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