github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/packetman/packetman_test.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  package packetman
    21  
    22  import (
    23  	"bytes"
    24  	"encoding/json"
    25  	"net"
    26  	"testing"
    27  
    28  	"github.com/google/gopacket"
    29  	"github.com/google/gopacket/layers"
    30  )
    31  
    32  func TestTransformations(t *testing.T) {
    33  
    34  	// Test: apply various transformations to an original packet, then parse the
    35  	// resulting packets and check that flags/fields/options are as expected.
    36  
    37  	// Limitation: gopacket, used here in the test to verify transformations,
    38  	// will fail to parse some or all of certain packets that can be created by
    39  	// certain transformations. gopacket will fail to parse packets with too many
    40  	// option bytes or invalid DataOffset values. gopacket will stop
    41  	// deserializing TCP options as soon as it encounters the EOL option, even if
    42  	// the packet actually contains more options. Etc.
    43  
    44  	specJSON := []byte(`
    45      {
    46          "Name": "test-spec",
    47          "PacketSpecs": [
    48              ["TCP-flags SA",
    49               "TCP-flags S",
    50               "TCP-srcport ffff",
    51               "TCP-dstport ffff",
    52               "TCP-seq ffffffff",
    53               "TCP-ack ffffffff",
    54               "TCP-dataoffset 0f",
    55               "TCP-window ffff",
    56               "TCP-checksum ffff",
    57               "TCP-urgent ffff",
    58               "TCP-option-nop omit",
    59               "TCP-option-mss ffff",
    60               "TCP-option-windowscale ff",
    61               "TCP-option-sackpermitted ",
    62  	         "TCP-option-sack ffffffffffffffff",
    63  	         "TCP-option-timestamps ffffffffffffffff",
    64               "TCP-payload eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
    65               "TCP-payload ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"],
    66  
    67              ["TCP-flags random",
    68               "TCP-srcport random",
    69               "TCP-dstport random",
    70               "TCP-seq random",
    71               "TCP-ack random",
    72               "TCP-dataoffset random",
    73               "TCP-window random",
    74               "TCP-checksum random",
    75               "TCP-urgent random",
    76               "TCP-option-mss random",
    77               "TCP-option-windowscale random",
    78               "TCP-option-sackpermitted random",
    79               "TCP-option-timestamps random",
    80               "TCP-payload random"]
    81          ]
    82      }
    83  	`)
    84  
    85  	var spec *Spec
    86  	err := json.Unmarshal(specJSON, &spec)
    87  	if err != nil {
    88  		t.Fatalf("json.Unmarshal failed: %v", err)
    89  	}
    90  
    91  	c, err := compileSpec(spec)
    92  	if err != nil {
    93  		t.Fatalf("compileSpec failed: %v", err)
    94  	}
    95  
    96  	if c.name != spec.Name {
    97  		t.Fatalf("unexpected compiled spec name: %s", c.name)
    98  	}
    99  
   100  	originalIPv4 := &layers.IPv4{
   101  		Version:  0x04,
   102  		IHL:      0x05,
   103  		Protocol: 0x06,
   104  		SrcIP:    net.IPv4(192, 168, 0, 1),
   105  		DstIP:    net.IPv4(192, 168, 0, 2),
   106  	}
   107  
   108  	originalTCP := &layers.TCP{
   109  		SYN: true,
   110  		ACK: true,
   111  		Options: []layers.TCPOption{
   112  			layers.TCPOption{OptionType: layers.TCPOptionKindNop, OptionLength: 1},
   113  			layers.TCPOption{OptionType: layers.TCPOptionKindSACKPermitted, OptionLength: 2},
   114  			layers.TCPOption{OptionType: layers.TCPOptionKindSACK, OptionLength: 10, OptionData: bytes.Repeat([]byte{0}, 8)},
   115  			layers.TCPOption{OptionType: layers.TCPOptionKindTimestamps, OptionLength: 10, OptionData: bytes.Repeat([]byte{0}, 8)},
   116  		},
   117  	}
   118  
   119  	originalTCP.SetNetworkLayerForChecksum(originalIPv4)
   120  
   121  	originalPayload := gopacket.Payload([]byte{0, 0, 0, 0})
   122  
   123  	buffer := gopacket.NewSerializeBuffer()
   124  	gopacket.SerializeLayers(
   125  		buffer,
   126  		gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true},
   127  		originalIPv4,
   128  		originalTCP,
   129  		originalPayload)
   130  	originalPacketData := buffer.Bytes()
   131  
   132  	originalPacket := gopacket.NewPacket(originalPacketData, layers.LayerTypeIPv4, gopacket.Default)
   133  	errLayer := originalPacket.ErrorLayer()
   134  	if errLayer != nil {
   135  		t.Fatalf("gopacket.NewPacket failed: %v", errLayer.Error())
   136  	}
   137  
   138  	stripEOLOption(originalPacket)
   139  
   140  	repeats := 1000
   141  repeatLoop:
   142  	for i := 0; i < repeats; i++ {
   143  
   144  		lastRepeat := i == repeats-1
   145  
   146  		injectPackets, err := c.apply(originalPacket)
   147  		if err != nil {
   148  			t.Fatalf("apply failed: %v", err)
   149  		}
   150  
   151  		if len(injectPackets) != 2 {
   152  			t.Fatalf("unexpected injectPackets count: %d", len(injectPackets))
   153  		}
   154  
   155  		for packetNum, packetData := range injectPackets {
   156  
   157  			packet := gopacket.NewPacket(packetData, layers.LayerTypeIPv4, gopacket.Default)
   158  
   159  			errLayer := packet.ErrorLayer()
   160  			if errLayer != nil {
   161  				t.Fatalf("gopacket.NewPacket failed: %v", errLayer.Error())
   162  			}
   163  
   164  			tcpLayer := packet.Layer(layers.LayerTypeTCP)
   165  			if tcpLayer == nil {
   166  				t.Fatalf("missing TCP layer")
   167  			}
   168  
   169  			tcp := tcpLayer.(*layers.TCP)
   170  
   171  			payloadLayer := packet.Layer(gopacket.LayerTypePayload)
   172  			if payloadLayer == nil {
   173  				t.Fatalf("missing payload layer")
   174  			}
   175  
   176  			payload := payloadLayer.(*gopacket.Payload)
   177  
   178  			optionsEqual := func(a, b layers.TCPOption) bool {
   179  				if a.OptionType != b.OptionType ||
   180  					a.OptionLength != b.OptionLength ||
   181  					!bytes.Equal(a.OptionData, b.OptionData) {
   182  					return false
   183  				}
   184  				return true
   185  			}
   186  
   187  			optionsListEqual := func(a, b []layers.TCPOption) bool {
   188  				if len(a) != len(b) {
   189  					return false
   190  				}
   191  				for i, o := range a {
   192  					if !optionsEqual(o, b[i]) {
   193  						return false
   194  					}
   195  				}
   196  				return true
   197  			}
   198  
   199  			if packetNum == 0 {
   200  
   201  				// With multiple, redundant value specs (TCP-flags in the test case) the
   202  				// _last_ value spec should be applied. Values should be truncated to
   203  				// protocol lengths. The NOP option in the original packet should be
   204  				// omitted.
   205  
   206  				expectedOptions := []layers.TCPOption{
   207  					layers.TCPOption{OptionType: layers.TCPOptionKindSACKPermitted, OptionLength: 2},
   208  					layers.TCPOption{OptionType: layers.TCPOptionKindSACK, OptionLength: 10, OptionData: bytes.Repeat([]byte{0xff}, 8)},
   209  					layers.TCPOption{OptionType: layers.TCPOptionKindTimestamps, OptionLength: 10, OptionData: bytes.Repeat([]byte{0xff}, 8)},
   210  					layers.TCPOption{OptionType: layers.TCPOptionKindMSS, OptionLength: 4, OptionData: bytes.Repeat([]byte{0xff}, 2)},
   211  					layers.TCPOption{OptionType: layers.TCPOptionKindWindowScale, OptionLength: 3, OptionData: bytes.Repeat([]byte{0xff}, 1)},
   212  					layers.TCPOption{OptionType: layers.TCPOptionKindEndList, OptionLength: 1},
   213  				}
   214  
   215  				if tcp.SrcPort != 0xffff ||
   216  					tcp.DstPort != 0xffff ||
   217  					tcp.Seq != 0xffffffff ||
   218  					tcp.Ack != 0xffffffff ||
   219  					tcp.FIN || !tcp.SYN || tcp.RST || tcp.PSH || tcp.ACK ||
   220  					tcp.URG || tcp.ECE || tcp.CWR || tcp.NS ||
   221  					tcp.Window != 0xffff ||
   222  					tcp.Urgent != 0xffff ||
   223  					!optionsListEqual(tcp.Options, expectedOptions) {
   224  					t.Fatalf("unexpected TCP layer: %+v", tcp)
   225  				}
   226  
   227  				expectedPayload := bytes.Repeat([]byte{0xff}, 32)
   228  				if !bytes.Equal(expectedPayload, *payload) {
   229  					t.Fatalf("unexpected payload: %x", *payload)
   230  				}
   231  
   232  			} else {
   233  
   234  				// In at least one repeat, randomized fields fully differ from original,
   235  				// including zero-values; original NOP and SACK options retained; random
   236  				// options have correct protocol lengths.
   237  
   238  				if tcp.SrcPort == originalTCP.SrcPort ||
   239  					tcp.DstPort == originalTCP.DstPort ||
   240  					tcp.Seq == originalTCP.Seq ||
   241  					tcp.Ack == originalTCP.Ack ||
   242  					(tcp.FIN == originalTCP.FIN &&
   243  						tcp.SYN == originalTCP.SYN &&
   244  						tcp.RST == originalTCP.RST &&
   245  						tcp.PSH == originalTCP.PSH &&
   246  						tcp.ACK == originalTCP.ACK &&
   247  						tcp.URG == originalTCP.URG &&
   248  						tcp.ECE == originalTCP.ECE &&
   249  						tcp.CWR == originalTCP.CWR &&
   250  						tcp.NS == originalTCP.NS) ||
   251  					tcp.Window == originalTCP.Window ||
   252  					tcp.Checksum == originalTCP.Checksum ||
   253  					tcp.Urgent == originalTCP.Urgent ||
   254  					len(tcp.Options) != 7 ||
   255  					!optionsEqual(tcp.Options[0], originalTCP.Options[0]) ||
   256  					!optionsEqual(tcp.Options[1], originalTCP.Options[1]) ||
   257  					!optionsEqual(tcp.Options[2], originalTCP.Options[2]) ||
   258  					tcp.Options[3].OptionType != layers.TCPOptionKindTimestamps ||
   259  					tcp.Options[3].OptionLength != 10 ||
   260  					optionsEqual(tcp.Options[3], originalTCP.Options[3]) ||
   261  					tcp.Options[4].OptionType != layers.TCPOptionKindMSS ||
   262  					tcp.Options[4].OptionLength != 4 ||
   263  					tcp.Options[5].OptionType != layers.TCPOptionKindWindowScale ||
   264  					tcp.Options[5].OptionLength != 3 ||
   265  					tcp.Options[6].OptionType != layers.TCPOptionKindEndList ||
   266  					bytes.Equal(originalPayload, *payload) {
   267  
   268  					if lastRepeat {
   269  						t.Fatalf("unexpected TCP layer: %+v", tcp)
   270  					}
   271  				} else {
   272  					break repeatLoop
   273  				}
   274  			}
   275  		}
   276  	}
   277  }