packetman.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. /*
  2. * Copyright (c) 2020, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. /*
  20. Package packetman implements low-level manipulation of TCP packets, enabling a
  21. variety of strategies to evade network censorship.
  22. This implementation is entirely based on and is a subset of Geneva:
  23. Come as You Are: Helping Unmodified Clients Bypass Censorship with
  24. Server-side Evasion
  25. Kevin Bock, George Hughey, Louis-Henri Merino, Tania Arya, Daniel Liscinsky,
  26. Regina Pogosian, Dave Levin
  27. ACM SIGCOMM 2020
  28. Geneva: Evolving Censorship Evasion Strategies
  29. Kevin Bock, George Hughey, Xiao Qiang, Dave Levin
  30. ACM CCS 2019 (Conference on Computer and Communications Security)
  31. https://github.com/Kkevsterrr/geneva
  32. This package implements the equivilent of the Geneva "engine", which can
  33. execute packet manipulation strategies. It does not implement the genetic
  34. algorithm component.
  35. Other notable differences:
  36. - We intercept, parse, and transform only server-side outbound SYN-ACK
  37. packets. Geneva supports client-side packet manipulation with a more diverse
  38. set of trigger packets, but in practise we cannot execute most low-level
  39. packet operations on client platforms such as Android and iOS.
  40. - For expediancy, we use a simplified strategy syntax (called transformation
  41. specs, to avoid confusion with the more general original). As we do not
  42. evolve strategies, we do not use a tree representation and some
  43. randomization tranformations are simplified.
  44. At this time, full functionality is limited to the Linux platform.
  45. Security: external parties can induce the server to emit a SYN-ACK, invoking
  46. the packet manipulation logic. External parties cannot set the transformation
  47. specs, and, as the input is the server-side generated SYN-ACK packet, cannot
  48. influence the packet manipulation with any external input parameters.
  49. */
  50. package packetman
  51. import (
  52. "encoding/binary"
  53. "encoding/hex"
  54. "fmt"
  55. "net"
  56. "strings"
  57. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  58. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
  59. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
  60. "github.com/google/gopacket"
  61. "github.com/google/gopacket/layers"
  62. )
  63. // Config specifies a packet manipulation configuration.
  64. type Config struct {
  65. // Logger is used for logging events and metrics.
  66. Logger common.Logger
  67. // ProtocolPorts specifies the set of TCP ports to which SYN-ACK packet
  68. // interception and manipulation is to be applied. To accommodate hosts with
  69. // multiple IP addresses, packet interception is applied to all interfaces.
  70. ProtocolPorts []int
  71. // On Linux, which uses NFQUEUE and raw sockets, QueueNumber is the NFQUEUE
  72. // queue-num parameter to be used.
  73. QueueNumber int
  74. // On Linux, which uses NFQUEUE and raw sockets, SocketMark is the SO_MARK
  75. // value to be used. When 0, a default value is used.
  76. SocketMark int
  77. // Specs is the list of packet transformation Spec value that are to be
  78. // available for packet manipulation. Spec names must be unique.
  79. Specs []*Spec
  80. // SelectSpecName is a callback invoked for each intercepted SYN-ACK packet.
  81. // SelectSpecName must return a name of a Spec, in Specs, to apply that
  82. // transformation spec, or "" to send the SYN-ACK packet unmodified.
  83. // The second return value is arbitrary extra data that is associated
  84. // with the packet's connection; see GetAppliedSpecName.
  85. //
  86. // The inputs protocolPort and clientIP allow the callback to select a Spec
  87. // based on the protocol running at the intercepted packet's port and/or
  88. // client GeoIP.
  89. SelectSpecName func(protocolPort int, clientIP net.IP) (string, interface{})
  90. // SudoNetworkConfigCommands specifies whether to use "sudo" when executing
  91. // network configuration commands. See comment for same parameter in
  92. // psiphon/common/tun.
  93. SudoNetworkConfigCommands bool
  94. // AllowNoIPv6NetworkConfiguration indicates that failures while configuring
  95. // tun interfaces and routing for IPv6 are to be logged as warnings only. See
  96. // comment for same parameter in psiphon/common/tun.
  97. AllowNoIPv6NetworkConfiguration bool
  98. }
  99. // Spec specifies a set of transformations to be applied to an intercepted
  100. // SYN-ACK packet to produce zero or more replacement packets to be sent in
  101. // its place.
  102. //
  103. // Each element in PacketSpecs specifies a new outgoing packet. Each element
  104. // in a packet specification specifies an individual transformation to be
  105. // applied, in turn, to a copy of the intercepted SYN-ACK packet, producing
  106. // the outgoing packet.
  107. //
  108. // Syntax of individual tranformations:
  109. //
  110. // "TCP-flags random|<flags>"
  111. // flags: FSRPAUECN
  112. //
  113. // "TCP-<field> random|<hex>"
  114. // field: srcport, dstport, seq, ack, dataoffset, window, checksum, urgent
  115. //
  116. // "TCP-option-<option> random|omit|<hex>"
  117. // option: eol, nop, mss, windowscale, sackpermitted, sack, timestamps,
  118. // altchecksum, altchecksumdata, md5header, usertimeout
  119. //
  120. // "TCP-payload random|<hex>"
  121. //
  122. // For example, this Geneva strategy:
  123. //
  124. // [TCP:flags:SA]-duplicate(tamper{TCP:flags:replace:R},tamper{TCP:flags:replace:S})-| \/
  125. //
  126. // is represented as follows (in JSON encoding):
  127. //
  128. // [["TCP-flags R"], ["TCP-flags S"]]
  129. //
  130. // Field and option values must be the expected length (see implementation).
  131. //
  132. // A Spec may produce invalid packets. For example, the total options length
  133. // can exceed 40 bytes and the DataOffset field may overflow.
  134. type Spec struct {
  135. Name string
  136. PacketSpecs [][]string
  137. }
  138. // Validate checks that the transformation spec is syntactically correct.
  139. func (s *Spec) Validate() error {
  140. _, err := compileSpec(s)
  141. return errors.Trace(err)
  142. }
  143. type compiledSpec struct {
  144. name string
  145. compiledPacketSpecs [][]transformation
  146. }
  147. func compileSpec(spec *Spec) (*compiledSpec, error) {
  148. compiledPacketSpecs := make([][]transformation, len(spec.PacketSpecs))
  149. for i := range spec.PacketSpecs {
  150. compiledPacketSpecs[i] = make([]transformation, len(spec.PacketSpecs[i]))
  151. for j, transformationSpec := range spec.PacketSpecs[i] {
  152. transform, err := compileTransformation(transformationSpec)
  153. if err != nil {
  154. return nil, errors.Trace(err)
  155. }
  156. compiledPacketSpecs[i][j] = transform
  157. }
  158. }
  159. return &compiledSpec{
  160. name: spec.Name,
  161. compiledPacketSpecs: compiledPacketSpecs}, nil
  162. }
  163. func (spec *compiledSpec) apply(interceptedPacket gopacket.Packet) ([][]byte, error) {
  164. packets := make([][]byte, len(spec.compiledPacketSpecs))
  165. for i, packetTransformations := range spec.compiledPacketSpecs {
  166. var networkLayer gopacket.NetworkLayer
  167. var serializableNetworkLayer gopacket.SerializableLayer
  168. // Copy the network layer (IPv4 or IPv6) as modifications may be made to
  169. // checksums or lengths in that layer. Note this is not a deep copy of
  170. // fields such as the Options slice, as these are not modified.
  171. interceptedIPv4Layer := interceptedPacket.Layer(layers.LayerTypeIPv4)
  172. if interceptedIPv4Layer != nil {
  173. transformedIPv4 := *interceptedIPv4Layer.(*layers.IPv4)
  174. networkLayer = &transformedIPv4
  175. serializableNetworkLayer = &transformedIPv4
  176. } else {
  177. interceptedIPv6Layer := interceptedPacket.Layer(layers.LayerTypeIPv6)
  178. transformedIPv6 := *interceptedIPv6Layer.(*layers.IPv6)
  179. networkLayer = &transformedIPv6
  180. serializableNetworkLayer = &transformedIPv6
  181. }
  182. interceptedTCP := interceptedPacket.Layer(layers.LayerTypeTCP).(*layers.TCP)
  183. // Copy the TCP layer before transforming it. Again this is not a deep copy.
  184. // If a transformation modifies the Options slice, it will be copied at that
  185. // time.
  186. transformedTCP := *interceptedTCP
  187. var payload gopacket.Payload
  188. setCalculatedField := false
  189. fixLengths := true
  190. computeChecksums := true
  191. for _, transform := range packetTransformations {
  192. transform.apply(&transformedTCP, &payload)
  193. if t, ok := transform.(*transformationTCPField); ok && t.fieldName == tcpFieldDataOffset {
  194. fixLengths = false
  195. setCalculatedField = true
  196. }
  197. if t, ok := transform.(*transformationTCPField); ok && t.fieldName == tcpFieldChecksum {
  198. computeChecksums = false
  199. setCalculatedField = true
  200. }
  201. }
  202. err := transformedTCP.SetNetworkLayerForChecksum(networkLayer)
  203. if err != nil {
  204. return nil, errors.Trace(err)
  205. }
  206. buffer := gopacket.NewSerializeBuffer()
  207. options := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
  208. //warning: first SerializeLayers call may modify transformedTCP, we must copy the TCP DataOffset and Checksum
  209. tmpDataOffset := transformedTCP.DataOffset
  210. tmpChecksum := transformedTCP.Checksum
  211. gopacket.SerializeLayers(
  212. buffer,
  213. options,
  214. serializableNetworkLayer,
  215. &transformedTCP,
  216. payload)
  217. // In the first SerializeLayers call, all IP and TCP length and checksums
  218. // are recalculated and set to the correct values with transformations
  219. // applied.
  220. //
  221. // If the spec calls for setting the TCP DataOffset or Checksum, a second
  222. // SerializeLayers call is performed, which will repave these values without
  223. // recalculation; all other calculated lengths and checksums are retained
  224. // from the first round.
  225. if setCalculatedField {
  226. transformedTCP.DataOffset = tmpDataOffset
  227. transformedTCP.Checksum = tmpChecksum
  228. buffer.Clear()
  229. gopacket.SerializeLayers(
  230. buffer,
  231. gopacket.SerializeOptions{FixLengths: fixLengths, ComputeChecksums: computeChecksums},
  232. serializableNetworkLayer,
  233. &transformedTCP,
  234. payload)
  235. }
  236. packets[i] = buffer.Bytes()
  237. }
  238. return packets, nil
  239. }
  240. type transformation interface {
  241. apply(tcp *layers.TCP, payload *gopacket.Payload)
  242. }
  243. const (
  244. transformationTypeUnknown = iota
  245. transformationTypeOmit
  246. transformationTypeRandom
  247. transformationTypeValue
  248. )
  249. func compileTransformation(spec string) (transformation, error) {
  250. parts := strings.Split(spec, " ")
  251. if len(parts) != 2 {
  252. return nil, errors.Tracef("invalid spec: %s", spec)
  253. }
  254. fieldSpec := parts[0]
  255. valueSpec := parts[1]
  256. parts = strings.Split(fieldSpec, "-")
  257. if (len(parts) != 2 && len(parts) != 3) || parts[0] != "TCP" {
  258. return nil, errors.Tracef("invalid field spec: %s", fieldSpec)
  259. }
  260. var transformationType int
  261. if valueSpec == "omit" {
  262. transformationType = transformationTypeOmit
  263. } else if valueSpec == "random" {
  264. transformationType = transformationTypeRandom
  265. } else {
  266. transformationType = transformationTypeValue
  267. }
  268. var t transformation
  269. var err error
  270. if len(parts) == 3 {
  271. if parts[1] != "option" {
  272. return nil, errors.Tracef("invalid field spec: %s", fieldSpec)
  273. }
  274. t, err = newTransformationTCPOption(parts[2], transformationType, valueSpec)
  275. } else if parts[1] == "flags" {
  276. t, err = newTransformationTCPFlags(transformationType, valueSpec)
  277. } else if parts[1] == "payload" {
  278. t, err = newTransformationTCPPayload(transformationType, valueSpec)
  279. } else {
  280. t, err = newTransformationTCPField(parts[1], transformationType, valueSpec)
  281. }
  282. if err != nil {
  283. return nil, errors.Tracef("invalid field spec: %s: %v", fieldSpec, err)
  284. }
  285. return t, nil
  286. }
  287. type transformationTCPFlags struct {
  288. transformationType int
  289. flags string
  290. }
  291. func newTransformationTCPFlags(
  292. transformationType int, valueSpec string) (*transformationTCPFlags, error) {
  293. var flags string
  294. switch transformationType {
  295. case transformationTypeRandom:
  296. case transformationTypeValue:
  297. checkFlags := valueSpec
  298. for _, f := range "FSRPAUECN" {
  299. checkFlags = strings.ReplaceAll(checkFlags, string(f), "")
  300. }
  301. if checkFlags != "" {
  302. return nil, errors.Tracef("invalid value spec: %s", valueSpec)
  303. }
  304. flags = valueSpec
  305. default:
  306. return nil, errors.Tracef("invalid transformation type")
  307. }
  308. return &transformationTCPFlags{
  309. transformationType: transformationType,
  310. flags: flags,
  311. }, nil
  312. }
  313. func (t *transformationTCPFlags) apply(tcp *layers.TCP, _ *gopacket.Payload) {
  314. var flags string
  315. if t.transformationType == transformationTypeRandom {
  316. // Differs from Geneva, which often selects real flag combinations,
  317. // presumably to focus its search space:
  318. // https://github.com/Kkevsterrr/geneva/blob/de6823ba7723582054d2047083262cabffa85f36/layers/tcp_layer.py#L117-L121.
  319. for _, f := range "FSRPAUECN" {
  320. if prng.FlipCoin() {
  321. flags += string(f)
  322. }
  323. }
  324. } else {
  325. flags = t.flags
  326. }
  327. tcp.FIN = strings.Index(t.flags, "F") != -1
  328. tcp.SYN = strings.Index(t.flags, "S") != -1
  329. tcp.RST = strings.Index(t.flags, "R") != -1
  330. tcp.PSH = strings.Index(t.flags, "P") != -1
  331. tcp.ACK = strings.Index(t.flags, "A") != -1
  332. tcp.URG = strings.Index(t.flags, "U") != -1
  333. tcp.ECE = strings.Index(t.flags, "E") != -1
  334. tcp.CWR = strings.Index(t.flags, "C") != -1
  335. tcp.NS = strings.Index(t.flags, "N") != -1
  336. }
  337. type transformationTCPField struct {
  338. fieldName string
  339. transformationType int
  340. value []byte
  341. }
  342. const (
  343. tcpFieldSrcPort = "srcport"
  344. tcpFieldDstPort = "dstport"
  345. tcpFieldSeq = "seq"
  346. tcpFieldAck = "ack"
  347. tcpFieldDataOffset = "dataoffset"
  348. tcpFieldWindow = "window"
  349. tcpFieldChecksum = "checksum"
  350. tcpFieldUrgent = "urgent"
  351. )
  352. func newTransformationTCPField(
  353. fieldName string, transformationType int, valueSpec string) (*transformationTCPField, error) {
  354. length := 0
  355. switch fieldName {
  356. case tcpFieldSrcPort:
  357. length = 2
  358. case tcpFieldDstPort:
  359. length = 2
  360. case tcpFieldSeq:
  361. length = 4
  362. case tcpFieldAck:
  363. length = 4
  364. case tcpFieldDataOffset:
  365. length = 1
  366. case tcpFieldWindow:
  367. length = 2
  368. case tcpFieldChecksum:
  369. length = 2
  370. case tcpFieldUrgent:
  371. length = 2
  372. default:
  373. return nil, errors.Tracef("invalid field name: %s", fieldName)
  374. }
  375. var decodedValue []byte
  376. switch transformationType {
  377. case transformationTypeRandom:
  378. case transformationTypeValue:
  379. var err error
  380. decodedValue, err = hex.DecodeString(valueSpec)
  381. if err == nil && len(decodedValue) != length {
  382. err = fmt.Errorf("invalid value length: %d", len(decodedValue))
  383. }
  384. if err != nil {
  385. return nil, errors.Tracef("invalid value spec: %s: %v", valueSpec, err)
  386. }
  387. default:
  388. return nil, errors.Tracef("invalid transformation type")
  389. }
  390. return &transformationTCPField{
  391. fieldName: fieldName,
  392. transformationType: transformationType,
  393. value: decodedValue,
  394. }, nil
  395. }
  396. func (t *transformationTCPField) apply(tcp *layers.TCP, _ *gopacket.Payload) {
  397. var value [4]byte
  398. if t.transformationType == transformationTypeRandom {
  399. _, _ = prng.Read(value[:])
  400. } else {
  401. copy(value[:], t.value)
  402. }
  403. switch t.fieldName {
  404. case tcpFieldSrcPort:
  405. tcp.SrcPort = layers.TCPPort(binary.BigEndian.Uint16(value[:]))
  406. case tcpFieldDstPort:
  407. tcp.DstPort = layers.TCPPort(binary.BigEndian.Uint16(value[:]))
  408. case tcpFieldSeq:
  409. tcp.Seq = binary.BigEndian.Uint32(value[:])
  410. case tcpFieldAck:
  411. tcp.Ack = binary.BigEndian.Uint32(value[:])
  412. case tcpFieldDataOffset:
  413. tcp.DataOffset = value[0]
  414. // DataOffset is a 4-bit field; the most significant 4 bits are ignored
  415. tcp.DataOffset &= 0x0f
  416. case tcpFieldWindow:
  417. // Differs from Geneva: https://github.com/Kkevsterrr/geneva/blob/de6823ba7723582054d2047083262cabffa85f36/layers/tcp_layer.py#L117-L121
  418. tcp.Window = binary.BigEndian.Uint16(value[:])
  419. case tcpFieldChecksum:
  420. tcp.Checksum = binary.BigEndian.Uint16(value[:])
  421. case tcpFieldUrgent:
  422. tcp.Urgent = binary.BigEndian.Uint16(value[:])
  423. }
  424. }
  425. type transformationTCPOption struct {
  426. optionName string
  427. transformationType int
  428. value []byte
  429. }
  430. const (
  431. tcpOptionEOL = "eol"
  432. tcpOptionNOP = "nop"
  433. tcpOptionMSS = "mss"
  434. tcpOptionWindowScale = "windowscale"
  435. tcpOptionSACKPermitted = "sackpermitted"
  436. tcpOptionSACK = "sack"
  437. tcpOptionTimestamps = "timestamps"
  438. tcpOptionAltChecksum = "altchecksum"
  439. tcpOptionAltChecksumData = "altchecksumdata"
  440. tcpOptionMD5Header = "md5header"
  441. tcpOptionUserTimeout = "usertimeout"
  442. )
  443. func tcpOptionInfo(optionName string) (layers.TCPOptionKind, []int, bool) {
  444. var kind layers.TCPOptionKind
  445. var validLengths []int
  446. switch optionName {
  447. case tcpOptionEOL:
  448. kind = layers.TCPOptionKindEndList
  449. validLengths = nil // no option length field
  450. case tcpOptionNOP:
  451. kind = layers.TCPOptionKindNop
  452. validLengths = nil
  453. case tcpOptionMSS:
  454. kind = layers.TCPOptionKindMSS
  455. validLengths = []int{2}
  456. case tcpOptionWindowScale:
  457. kind = layers.TCPOptionKindWindowScale
  458. validLengths = []int{1}
  459. case tcpOptionSACKPermitted:
  460. kind = layers.TCPOptionKindSACKPermitted
  461. validLengths = []int{0}
  462. case tcpOptionSACK:
  463. // https://tools.ietf.org/html/rfc2018
  464. kind = layers.TCPOptionKindSACK
  465. validLengths = []int{8, 16, 24, 32}
  466. case tcpOptionTimestamps:
  467. kind = layers.TCPOptionKindTimestamps
  468. validLengths = []int{8}
  469. case tcpOptionAltChecksum:
  470. kind = layers.TCPOptionKindAltChecksum
  471. validLengths = []int{1}
  472. case tcpOptionAltChecksumData:
  473. // https://tools.ietf.org/html/rfc1145:
  474. // "this field is used only when the alternate checksum that is negotiated is longer than 16 bits"
  475. //
  476. // Geneva allows setting length 0.
  477. kind = layers.TCPOptionKindAltChecksumData
  478. validLengths = []int{0, 4}
  479. case tcpOptionMD5Header:
  480. // https://tools.ietf.org/html/rfc2385
  481. kind = layers.TCPOptionKind(19)
  482. validLengths = []int{16}
  483. case tcpOptionUserTimeout:
  484. // https://tools.ietf.org/html/rfc5482
  485. kind = layers.TCPOptionKind(28)
  486. validLengths = []int{2}
  487. default:
  488. return kind, nil, false
  489. }
  490. return kind, validLengths, true
  491. }
  492. func newTransformationTCPOption(
  493. optionName string, transformationType int, valueSpec string) (*transformationTCPOption, error) {
  494. _, validLengths, ok := tcpOptionInfo(optionName)
  495. if !ok {
  496. return nil, errors.Tracef("invalid option name: %s", optionName)
  497. }
  498. var decodedValue []byte
  499. switch transformationType {
  500. case transformationTypeOmit:
  501. case transformationTypeRandom:
  502. case transformationTypeValue:
  503. var err error
  504. decodedValue, err = hex.DecodeString(valueSpec)
  505. if err == nil {
  506. if validLengths == nil {
  507. validLengths = []int{0}
  508. }
  509. if !common.ContainsInt(validLengths, len(decodedValue)) {
  510. err = fmt.Errorf("invalid value length: %d", len(decodedValue))
  511. }
  512. }
  513. if err != nil {
  514. return nil, errors.Tracef("invalid value spec: %s: %v", valueSpec, err)
  515. }
  516. default:
  517. return nil, errors.Tracef("invalid transformation type")
  518. }
  519. return &transformationTCPOption{
  520. optionName: optionName,
  521. transformationType: transformationType,
  522. value: decodedValue,
  523. }, nil
  524. }
  525. func (t *transformationTCPOption) apply(tcp *layers.TCP, _ *gopacket.Payload) {
  526. // This transformation makes a copy of all existing TCPOption structs, so
  527. // transformed option slices are not shared between multiple packets.
  528. //
  529. // All existing options are retained in the existing order. Modified options
  530. // are overwritten in place. New options are appended to the end of the
  531. // option list.
  532. //
  533. // Total option set size is not tracked or validated and the DataOffset TCP
  534. // field can overflow.
  535. //
  536. // Limitations:
  537. // - Inserting an option at a specific position is not supported.
  538. // - OptionLengths cannot be set to arbitrary values.
  539. // - Each option transformation executes a full copy of the existing option
  540. // list, which is not efficient for a long list of option transformations.
  541. kind, validLengths, _ := tcpOptionInfo(t.optionName)
  542. var options []layers.TCPOption
  543. // The for loop iterates over all existing options plus one additional
  544. // iteration, copying or modifying existing options and then appending a new
  545. // option if required. This flag ensures that we don't both modify and append
  546. // a new option.
  547. applied := false
  548. for i := 0; i <= len(tcp.Options); i++ {
  549. if i < len(tcp.Options) {
  550. option := tcp.Options[i]
  551. if option.OptionType != kind {
  552. options = append(options, layers.TCPOption{
  553. OptionType: option.OptionType,
  554. OptionLength: option.OptionLength,
  555. OptionData: append([]byte(nil), option.OptionData...),
  556. })
  557. continue
  558. }
  559. } else if applied {
  560. // Skip the append iteration if we already applied the transformation to an
  561. // existing option.
  562. continue
  563. }
  564. // TCP options with validLengths == nil have only the "kind" byte and total
  565. // length 1. Options with validLengths have the "kind" byte, the "length"
  566. // byte, and 0 or more data bytes; in this case, "length" is 2 + the length
  567. // of the data.
  568. switch t.transformationType {
  569. case transformationTypeOmit:
  570. continue
  571. case transformationTypeRandom:
  572. if validLengths == nil {
  573. options = append(options, layers.TCPOption{
  574. OptionType: kind,
  575. OptionLength: 1,
  576. })
  577. } else {
  578. length := validLengths[prng.Range(0, len(validLengths)-1)]
  579. var data []byte
  580. if length > 0 {
  581. data = prng.Bytes(length)
  582. }
  583. options = append(options, layers.TCPOption{
  584. OptionType: kind,
  585. OptionLength: 2 + uint8(length),
  586. OptionData: data,
  587. })
  588. }
  589. applied = true
  590. case transformationTypeValue:
  591. if validLengths == nil {
  592. options = append(options, layers.TCPOption{
  593. OptionType: kind,
  594. OptionLength: 1,
  595. })
  596. } else {
  597. length := len(t.value)
  598. var data []byte
  599. if length > 0 {
  600. data = append([]byte(nil), t.value...)
  601. }
  602. options = append(options, layers.TCPOption{
  603. OptionType: kind,
  604. OptionLength: 2 + uint8(length),
  605. OptionData: data,
  606. })
  607. }
  608. applied = true
  609. }
  610. }
  611. tcp.Options = options
  612. }
  613. type transformationTCPPayload struct {
  614. transformationType int
  615. value []byte
  616. }
  617. func newTransformationTCPPayload(
  618. transformationType int, valueSpec string) (*transformationTCPPayload, error) {
  619. var decodedValue []byte
  620. switch transformationType {
  621. case transformationTypeOmit:
  622. case transformationTypeRandom:
  623. case transformationTypeValue:
  624. var err error
  625. decodedValue, err = hex.DecodeString(valueSpec)
  626. if err != nil {
  627. return nil, errors.Tracef("invalid value spec: %s: %v", valueSpec, err)
  628. }
  629. default:
  630. return nil, errors.Tracef("invalid transformation type")
  631. }
  632. return &transformationTCPPayload{
  633. transformationType: transformationType,
  634. value: decodedValue,
  635. }, nil
  636. }
  637. func (t *transformationTCPPayload) apply(tcp *layers.TCP, payload *gopacket.Payload) {
  638. var value []byte
  639. switch t.transformationType {
  640. case transformationTypeOmit:
  641. case transformationTypeRandom:
  642. // Differs from Geneva: https://github.com/Kkevsterrr/geneva/blob/de6823ba7723582054d2047083262cabffa85f36/layers/layer.py#L191-L197
  643. value = prng.Bytes(prng.Range(1, 200))
  644. case transformationTypeValue:
  645. value = t.value
  646. }
  647. if value == nil {
  648. // Omit the payload.
  649. *payload = nil
  650. } else {
  651. // Change the payload.
  652. *payload = append([]byte(nil), value...)
  653. }
  654. }
  655. func stripEOLOption(packet gopacket.Packet) {
  656. // gopacket.NewPacket appears to decode padding (0s) as an explicit EOL
  657. // option (value 0) at the end of the option list. This helper strips that
  658. // option, allowing append-option transformations to work as expected.
  659. // gopacket TCP serialization will re-add padding as required.
  660. tcpLayer := packet.Layer(layers.LayerTypeTCP).(*layers.TCP)
  661. if len(tcpLayer.Options) > 0 &&
  662. tcpLayer.Options[len(tcpLayer.Options)-1].OptionType == layers.TCPOptionKindEndList {
  663. tcpLayer.Options = tcpLayer.Options[:len(tcpLayer.Options)-1]
  664. }
  665. }