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  }