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. // [TCP:flags:SA]-duplicate(tamper{TCP:flags:replace:R},tamper{TCP:flags:replace:S})-| \/
  124. //
  125. // is represented as follows (in JSON encoding):
  126. // [["TCP-flags R"], ["TCP-flags S"]]
  127. //
  128. //
  129. // Field and option values must be the expected length (see implementation).
  130. //
  131. // A Spec may produce invalid packets. For example, the total options length
  132. // can exceed 40 bytes and the DataOffset field may overflow.
  133. type Spec struct {
  134. Name string
  135. PacketSpecs [][]string
  136. }
  137. // Validate checks that the transformation spec is syntactically correct.
  138. func (s *Spec) Validate() error {
  139. _, err := compileSpec(s)
  140. return errors.Trace(err)
  141. }
  142. type compiledSpec struct {
  143. name string
  144. compiledPacketSpecs [][]transformation
  145. }
  146. func compileSpec(spec *Spec) (*compiledSpec, error) {
  147. compiledPacketSpecs := make([][]transformation, len(spec.PacketSpecs))
  148. for i, _ := range spec.PacketSpecs {
  149. compiledPacketSpecs[i] = make([]transformation, len(spec.PacketSpecs[i]))
  150. for j, transformationSpec := range spec.PacketSpecs[i] {
  151. transform, err := compileTransformation(transformationSpec)
  152. if err != nil {
  153. return nil, errors.Trace(err)
  154. }
  155. compiledPacketSpecs[i][j] = transform
  156. }
  157. }
  158. return &compiledSpec{
  159. name: spec.Name,
  160. compiledPacketSpecs: compiledPacketSpecs}, nil
  161. }
  162. func (spec *compiledSpec) apply(interceptedPacket gopacket.Packet) ([][]byte, error) {
  163. packets := make([][]byte, len(spec.compiledPacketSpecs))
  164. for i, packetTransformations := range spec.compiledPacketSpecs {
  165. var networkLayer gopacket.NetworkLayer
  166. var serializableNetworkLayer gopacket.SerializableLayer
  167. // Copy the network layer (IPv4 or IPv6) as modifications may be made to
  168. // checksums or lengths in that layer. Note this is not a deep copy of
  169. // fields such as the Options slice, as these are not modified.
  170. interceptedIPv4Layer := interceptedPacket.Layer(layers.LayerTypeIPv4)
  171. if interceptedIPv4Layer != nil {
  172. transformedIPv4 := *interceptedIPv4Layer.(*layers.IPv4)
  173. networkLayer = &transformedIPv4
  174. serializableNetworkLayer = &transformedIPv4
  175. } else {
  176. interceptedIPv6Layer := interceptedPacket.Layer(layers.LayerTypeIPv6)
  177. transformedIPv6 := *interceptedIPv6Layer.(*layers.IPv6)
  178. networkLayer = &transformedIPv6
  179. serializableNetworkLayer = &transformedIPv6
  180. }
  181. interceptedTCP := interceptedPacket.Layer(layers.LayerTypeTCP).(*layers.TCP)
  182. // Copy the TCP layer before transforming it. Again this is not a deep copy.
  183. // If a transformation modifies the Options slice, it will be copied at that
  184. // time.
  185. transformedTCP := *interceptedTCP
  186. var payload gopacket.Payload
  187. setCalculatedField := false
  188. fixLengths := true
  189. computeChecksums := true
  190. for _, transform := range packetTransformations {
  191. transform.apply(&transformedTCP, &payload)
  192. if t, ok := transform.(*transformationTCPField); ok && t.fieldName == tcpFieldDataOffset {
  193. fixLengths = false
  194. setCalculatedField = true
  195. }
  196. if t, ok := transform.(*transformationTCPField); ok && t.fieldName == tcpFieldChecksum {
  197. computeChecksums = false
  198. setCalculatedField = true
  199. }
  200. }
  201. err := transformedTCP.SetNetworkLayerForChecksum(networkLayer)
  202. if err != nil {
  203. return nil, errors.Trace(err)
  204. }
  205. buffer := gopacket.NewSerializeBuffer()
  206. options := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
  207. //warning: first SerializeLayers call may modify transformedTCP, we must copy the TCP DataOffset and Checksum
  208. tmpDataOffset := transformedTCP.DataOffset
  209. tmpChecksum := transformedTCP.Checksum
  210. gopacket.SerializeLayers(
  211. buffer,
  212. options,
  213. serializableNetworkLayer,
  214. &transformedTCP,
  215. payload)
  216. // In the first SerializeLayers call, all IP and TCP length and checksums
  217. // are recalculated and set to the correct values with transformations
  218. // applied.
  219. //
  220. // If the spec calls for setting the TCP DataOffset or Checksum, a second
  221. // SerializeLayers call is performed, which will repave these values without
  222. // recalculation; all other calculated lengths and checksums are retained
  223. // from the first round.
  224. if setCalculatedField {
  225. transformedTCP.DataOffset = tmpDataOffset
  226. transformedTCP.Checksum = tmpChecksum
  227. buffer.Clear()
  228. gopacket.SerializeLayers(
  229. buffer,
  230. gopacket.SerializeOptions{FixLengths: fixLengths, ComputeChecksums: computeChecksums},
  231. serializableNetworkLayer,
  232. &transformedTCP,
  233. payload)
  234. }
  235. packets[i] = buffer.Bytes()
  236. }
  237. return packets, nil
  238. }
  239. type transformation interface {
  240. apply(tcp *layers.TCP, payload *gopacket.Payload)
  241. }
  242. const (
  243. transformationTypeUnknown = iota
  244. transformationTypeOmit
  245. transformationTypeRandom
  246. transformationTypeValue
  247. )
  248. func compileTransformation(spec string) (transformation, error) {
  249. parts := strings.Split(spec, " ")
  250. if len(parts) != 2 {
  251. return nil, errors.Tracef("invalid spec: %s", spec)
  252. }
  253. fieldSpec := parts[0]
  254. valueSpec := parts[1]
  255. parts = strings.Split(fieldSpec, "-")
  256. if (len(parts) != 2 && len(parts) != 3) || parts[0] != "TCP" {
  257. return nil, errors.Tracef("invalid field spec: %s", fieldSpec)
  258. }
  259. var transformationType int
  260. if valueSpec == "omit" {
  261. transformationType = transformationTypeOmit
  262. } else if valueSpec == "random" {
  263. transformationType = transformationTypeRandom
  264. } else {
  265. transformationType = transformationTypeValue
  266. }
  267. var t transformation
  268. var err error
  269. if len(parts) == 3 {
  270. if parts[1] != "option" {
  271. return nil, errors.Tracef("invalid field spec: %s", fieldSpec)
  272. }
  273. t, err = newTransformationTCPOption(parts[2], transformationType, valueSpec)
  274. } else if parts[1] == "flags" {
  275. t, err = newTransformationTCPFlags(transformationType, valueSpec)
  276. } else if parts[1] == "payload" {
  277. t, err = newTransformationTCPPayload(transformationType, valueSpec)
  278. } else {
  279. t, err = newTransformationTCPField(parts[1], transformationType, valueSpec)
  280. }
  281. if err != nil {
  282. return nil, errors.Tracef("invalid field spec: %s: %v", fieldSpec, err)
  283. }
  284. return t, nil
  285. }
  286. type transformationTCPFlags struct {
  287. transformationType int
  288. flags string
  289. }
  290. func newTransformationTCPFlags(
  291. transformationType int, valueSpec string) (*transformationTCPFlags, error) {
  292. var flags string
  293. switch transformationType {
  294. case transformationTypeRandom:
  295. case transformationTypeValue:
  296. checkFlags := valueSpec
  297. for _, f := range "FSRPAUECN" {
  298. checkFlags = strings.ReplaceAll(checkFlags, string(f), "")
  299. }
  300. if checkFlags != "" {
  301. return nil, errors.Tracef("invalid value spec: %s", valueSpec)
  302. }
  303. flags = valueSpec
  304. default:
  305. return nil, errors.Tracef("invalid transformation type")
  306. }
  307. return &transformationTCPFlags{
  308. transformationType: transformationType,
  309. flags: flags,
  310. }, nil
  311. }
  312. func (t *transformationTCPFlags) apply(tcp *layers.TCP, _ *gopacket.Payload) {
  313. var flags string
  314. if t.transformationType == transformationTypeRandom {
  315. // Differs from Geneva, which often selects real flag combinations,
  316. // presumably to focus its search space:
  317. // https://github.com/Kkevsterrr/geneva/blob/de6823ba7723582054d2047083262cabffa85f36/layers/tcp_layer.py#L117-L121.
  318. for _, f := range "FSRPAUECN" {
  319. if prng.FlipCoin() {
  320. flags += string(f)
  321. }
  322. }
  323. } else {
  324. flags = t.flags
  325. }
  326. tcp.FIN = strings.Index(t.flags, "F") != -1
  327. tcp.SYN = strings.Index(t.flags, "S") != -1
  328. tcp.RST = strings.Index(t.flags, "R") != -1
  329. tcp.PSH = strings.Index(t.flags, "P") != -1
  330. tcp.ACK = strings.Index(t.flags, "A") != -1
  331. tcp.URG = strings.Index(t.flags, "U") != -1
  332. tcp.ECE = strings.Index(t.flags, "E") != -1
  333. tcp.CWR = strings.Index(t.flags, "C") != -1
  334. tcp.NS = strings.Index(t.flags, "N") != -1
  335. }
  336. type transformationTCPField struct {
  337. fieldName string
  338. transformationType int
  339. value []byte
  340. }
  341. const (
  342. tcpFieldSrcPort = "srcport"
  343. tcpFieldDstPort = "dstport"
  344. tcpFieldSeq = "seq"
  345. tcpFieldAck = "ack"
  346. tcpFieldDataOffset = "dataoffset"
  347. tcpFieldWindow = "window"
  348. tcpFieldChecksum = "checksum"
  349. tcpFieldUrgent = "urgent"
  350. )
  351. func newTransformationTCPField(
  352. fieldName string, transformationType int, valueSpec string) (*transformationTCPField, error) {
  353. length := 0
  354. switch fieldName {
  355. case tcpFieldSrcPort:
  356. length = 2
  357. case tcpFieldDstPort:
  358. length = 2
  359. case tcpFieldSeq:
  360. length = 4
  361. case tcpFieldAck:
  362. length = 4
  363. case tcpFieldDataOffset:
  364. length = 1
  365. case tcpFieldWindow:
  366. length = 2
  367. case tcpFieldChecksum:
  368. length = 2
  369. case tcpFieldUrgent:
  370. length = 2
  371. default:
  372. return nil, errors.Tracef("invalid field name: %s", fieldName)
  373. }
  374. var decodedValue []byte
  375. switch transformationType {
  376. case transformationTypeRandom:
  377. case transformationTypeValue:
  378. var err error
  379. decodedValue, err = hex.DecodeString(valueSpec)
  380. if err == nil && len(decodedValue) != length {
  381. err = fmt.Errorf("invalid value length: %d", len(decodedValue))
  382. }
  383. if err != nil {
  384. return nil, errors.Tracef("invalid value spec: %s: %v", valueSpec, err)
  385. }
  386. default:
  387. return nil, errors.Tracef("invalid transformation type")
  388. }
  389. return &transformationTCPField{
  390. fieldName: fieldName,
  391. transformationType: transformationType,
  392. value: decodedValue,
  393. }, nil
  394. }
  395. func (t *transformationTCPField) apply(tcp *layers.TCP, _ *gopacket.Payload) {
  396. var value [4]byte
  397. if t.transformationType == transformationTypeRandom {
  398. _, _ = prng.Read(value[:])
  399. } else {
  400. copy(value[:], t.value)
  401. }
  402. switch t.fieldName {
  403. case tcpFieldSrcPort:
  404. tcp.SrcPort = layers.TCPPort(binary.BigEndian.Uint16(value[:]))
  405. case tcpFieldDstPort:
  406. tcp.DstPort = layers.TCPPort(binary.BigEndian.Uint16(value[:]))
  407. case tcpFieldSeq:
  408. tcp.Seq = binary.BigEndian.Uint32(value[:])
  409. case tcpFieldAck:
  410. tcp.Ack = binary.BigEndian.Uint32(value[:])
  411. case tcpFieldDataOffset:
  412. tcp.DataOffset = value[0]
  413. // DataOffset is a 4-bit field; the most significant 4 bits are ignored
  414. tcp.DataOffset &= 0x0f
  415. case tcpFieldWindow:
  416. // Differs from Geneva: https://github.com/Kkevsterrr/geneva/blob/de6823ba7723582054d2047083262cabffa85f36/layers/tcp_layer.py#L117-L121
  417. tcp.Window = binary.BigEndian.Uint16(value[:])
  418. case tcpFieldChecksum:
  419. tcp.Checksum = binary.BigEndian.Uint16(value[:])
  420. case tcpFieldUrgent:
  421. tcp.Urgent = binary.BigEndian.Uint16(value[:])
  422. }
  423. }
  424. type transformationTCPOption struct {
  425. optionName string
  426. transformationType int
  427. value []byte
  428. }
  429. const (
  430. tcpOptionEOL = "eol"
  431. tcpOptionNOP = "nop"
  432. tcpOptionMSS = "mss"
  433. tcpOptionWindowScale = "windowscale"
  434. tcpOptionSACKPermitted = "sackpermitted"
  435. tcpOptionSACK = "sack"
  436. tcpOptionTimestamps = "timestamps"
  437. tcpOptionAltChecksum = "altchecksum"
  438. tcpOptionAltChecksumData = "altchecksumdata"
  439. tcpOptionMD5Header = "md5header"
  440. tcpOptionUserTimeout = "usertimeout"
  441. )
  442. func tcpOptionInfo(optionName string) (layers.TCPOptionKind, []int, bool) {
  443. var kind layers.TCPOptionKind
  444. var validLengths []int
  445. switch optionName {
  446. case tcpOptionEOL:
  447. kind = layers.TCPOptionKindEndList
  448. validLengths = nil // no option length field
  449. case tcpOptionNOP:
  450. kind = layers.TCPOptionKindNop
  451. validLengths = nil
  452. case tcpOptionMSS:
  453. kind = layers.TCPOptionKindMSS
  454. validLengths = []int{2}
  455. case tcpOptionWindowScale:
  456. kind = layers.TCPOptionKindWindowScale
  457. validLengths = []int{1}
  458. case tcpOptionSACKPermitted:
  459. kind = layers.TCPOptionKindSACKPermitted
  460. validLengths = []int{0}
  461. case tcpOptionSACK:
  462. // https://tools.ietf.org/html/rfc2018
  463. kind = layers.TCPOptionKindSACK
  464. validLengths = []int{8, 16, 24, 32}
  465. case tcpOptionTimestamps:
  466. kind = layers.TCPOptionKindTimestamps
  467. validLengths = []int{8}
  468. case tcpOptionAltChecksum:
  469. kind = layers.TCPOptionKindAltChecksum
  470. validLengths = []int{1}
  471. case tcpOptionAltChecksumData:
  472. // https://tools.ietf.org/html/rfc1145:
  473. // "this field is used only when the alternate checksum that is negotiated is longer than 16 bits"
  474. //
  475. // Geneva allows setting length 0.
  476. kind = layers.TCPOptionKindAltChecksumData
  477. validLengths = []int{0, 4}
  478. case tcpOptionMD5Header:
  479. // https://tools.ietf.org/html/rfc2385
  480. kind = layers.TCPOptionKind(19)
  481. validLengths = []int{16}
  482. case tcpOptionUserTimeout:
  483. // https://tools.ietf.org/html/rfc5482
  484. kind = layers.TCPOptionKind(28)
  485. validLengths = []int{2}
  486. default:
  487. return kind, nil, false
  488. }
  489. return kind, validLengths, true
  490. }
  491. func newTransformationTCPOption(
  492. optionName string, transformationType int, valueSpec string) (*transformationTCPOption, error) {
  493. _, validLengths, ok := tcpOptionInfo(optionName)
  494. if !ok {
  495. return nil, errors.Tracef("invalid option name: %s", optionName)
  496. }
  497. var decodedValue []byte
  498. switch transformationType {
  499. case transformationTypeOmit:
  500. case transformationTypeRandom:
  501. case transformationTypeValue:
  502. var err error
  503. decodedValue, err = hex.DecodeString(valueSpec)
  504. if err == nil {
  505. if validLengths == nil {
  506. validLengths = []int{0}
  507. }
  508. if !common.ContainsInt(validLengths, len(decodedValue)) {
  509. err = fmt.Errorf("invalid value length: %d", len(decodedValue))
  510. }
  511. }
  512. if err != nil {
  513. return nil, errors.Tracef("invalid value spec: %s: %v", valueSpec, err)
  514. }
  515. default:
  516. return nil, errors.Tracef("invalid transformation type")
  517. }
  518. return &transformationTCPOption{
  519. optionName: optionName,
  520. transformationType: transformationType,
  521. value: decodedValue,
  522. }, nil
  523. }
  524. func (t *transformationTCPOption) apply(tcp *layers.TCP, _ *gopacket.Payload) {
  525. // This transformation makes a copy of all existing TCPOption structs, so
  526. // transformed option slices are not shared between multiple packets.
  527. //
  528. // All existing options are retained in the existing order. Modified options
  529. // are overwritten in place. New options are appended to the end of the
  530. // option list.
  531. //
  532. // Total option set size is not tracked or validated and the DataOffset TCP
  533. // field can overflow.
  534. //
  535. // Limitations:
  536. // - Inserting an option at a specific position is not supported.
  537. // - OptionLengths cannot be set to arbitrary values.
  538. // - Each option transformation executes a full copy of the existing option
  539. // list, which is not efficient for a long list of option transformations.
  540. kind, validLengths, _ := tcpOptionInfo(t.optionName)
  541. var options []layers.TCPOption
  542. // The for loop iterates over all existing options plus one additional
  543. // iteration, copying or modifying existing options and then appending a new
  544. // option if required. This flag ensures that we don't both modify and append
  545. // a new option.
  546. applied := false
  547. for i := 0; i <= len(tcp.Options); i++ {
  548. if i < len(tcp.Options) {
  549. option := tcp.Options[i]
  550. if option.OptionType != kind {
  551. options = append(options, layers.TCPOption{
  552. OptionType: option.OptionType,
  553. OptionLength: option.OptionLength,
  554. OptionData: append([]byte(nil), option.OptionData...),
  555. })
  556. continue
  557. }
  558. } else if applied {
  559. // Skip the append iteration if we already applied the transformation to an
  560. // existing option.
  561. continue
  562. }
  563. // TCP options with validLengths == nil have only the "kind" byte and total
  564. // length 1. Options with validLengths have the "kind" byte, the "length"
  565. // byte, and 0 or more data bytes; in this case, "length" is 2 + the length
  566. // of the data.
  567. switch t.transformationType {
  568. case transformationTypeOmit:
  569. continue
  570. case transformationTypeRandom:
  571. if validLengths == nil {
  572. options = append(options, layers.TCPOption{
  573. OptionType: kind,
  574. OptionLength: 1,
  575. })
  576. } else {
  577. length := validLengths[prng.Range(0, len(validLengths)-1)]
  578. var data []byte
  579. if length > 0 {
  580. data = prng.Bytes(length)
  581. }
  582. options = append(options, layers.TCPOption{
  583. OptionType: kind,
  584. OptionLength: 2 + uint8(length),
  585. OptionData: data,
  586. })
  587. }
  588. applied = true
  589. case transformationTypeValue:
  590. if validLengths == nil {
  591. options = append(options, layers.TCPOption{
  592. OptionType: kind,
  593. OptionLength: 1,
  594. })
  595. } else {
  596. length := len(t.value)
  597. var data []byte
  598. if length > 0 {
  599. data = append([]byte(nil), t.value...)
  600. }
  601. options = append(options, layers.TCPOption{
  602. OptionType: kind,
  603. OptionLength: 2 + uint8(length),
  604. OptionData: data,
  605. })
  606. }
  607. applied = true
  608. }
  609. }
  610. tcp.Options = options
  611. }
  612. type transformationTCPPayload struct {
  613. transformationType int
  614. value []byte
  615. }
  616. func newTransformationTCPPayload(
  617. transformationType int, valueSpec string) (*transformationTCPPayload, error) {
  618. var decodedValue []byte
  619. switch transformationType {
  620. case transformationTypeOmit:
  621. case transformationTypeRandom:
  622. case transformationTypeValue:
  623. var err error
  624. decodedValue, err = hex.DecodeString(valueSpec)
  625. if err != nil {
  626. return nil, errors.Tracef("invalid value spec: %s: %v", valueSpec, err)
  627. }
  628. default:
  629. return nil, errors.Tracef("invalid transformation type")
  630. }
  631. return &transformationTCPPayload{
  632. transformationType: transformationType,
  633. value: decodedValue,
  634. }, nil
  635. }
  636. func (t *transformationTCPPayload) apply(tcp *layers.TCP, payload *gopacket.Payload) {
  637. var value []byte
  638. switch t.transformationType {
  639. case transformationTypeOmit:
  640. case transformationTypeRandom:
  641. // Differs from Geneva: https://github.com/Kkevsterrr/geneva/blob/de6823ba7723582054d2047083262cabffa85f36/layers/layer.py#L191-L197
  642. value = prng.Bytes(prng.Range(1, 200))
  643. case transformationTypeValue:
  644. value = t.value
  645. }
  646. if value == nil {
  647. // Omit the payload.
  648. *payload = nil
  649. } else {
  650. // Change the payload.
  651. *payload = append([]byte(nil), value...)
  652. }
  653. }
  654. func stripEOLOption(packet gopacket.Packet) {
  655. // gopacket.NewPacket appears to decode padding (0s) as an explicit EOL
  656. // option (value 0) at the end of the option list. This helper strips that
  657. // option, allowing append-option transformations to work as expected.
  658. // gopacket TCP serialization will re-add padding as required.
  659. tcpLayer := packet.Layer(layers.LayerTypeTCP).(*layers.TCP)
  660. if len(tcpLayer.Options) > 0 &&
  661. tcpLayer.Options[len(tcpLayer.Options)-1].OptionType == layers.TCPOptionKindEndList {
  662. tcpLayer.Options = tcpLayer.Options[:len(tcpLayer.Options)-1]
  663. }
  664. }