gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/packetimpact/testbench/layers_test.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package testbench
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/hex"
    20  	"net"
    21  	"testing"
    22  
    23  	"github.com/mohae/deepcopy"
    24  	"gvisor.dev/gvisor/pkg/tcpip"
    25  	"gvisor.dev/gvisor/pkg/tcpip/header"
    26  )
    27  
    28  func TestLayerMatch(t *testing.T) {
    29  	var nilPayload *Payload
    30  	noPayload := &Payload{}
    31  	emptyPayload := &Payload{Bytes: []byte{}}
    32  	fullPayload := &Payload{Bytes: []byte{1, 2, 3}}
    33  	emptyTCP := &TCP{SrcPort: Uint16(1234), LayerBase: LayerBase{nextLayer: emptyPayload}}
    34  	fullTCP := &TCP{SrcPort: Uint16(1234), LayerBase: LayerBase{nextLayer: fullPayload}}
    35  	for _, tt := range []struct {
    36  		a, b Layer
    37  		want bool
    38  	}{
    39  		{nilPayload, nilPayload, true},
    40  		{nilPayload, noPayload, true},
    41  		{nilPayload, emptyPayload, true},
    42  		{nilPayload, fullPayload, true},
    43  		{noPayload, noPayload, true},
    44  		{noPayload, emptyPayload, true},
    45  		{noPayload, fullPayload, true},
    46  		{emptyPayload, emptyPayload, true},
    47  		{emptyPayload, fullPayload, false},
    48  		{fullPayload, fullPayload, true},
    49  		{emptyTCP, fullTCP, true},
    50  	} {
    51  		if got := tt.a.match(tt.b); got != tt.want {
    52  			t.Errorf("%s.match(%s) = %t, want %t", tt.a, tt.b, got, tt.want)
    53  		}
    54  		if got := tt.b.match(tt.a); got != tt.want {
    55  			t.Errorf("%s.match(%s) = %t, want %t", tt.b, tt.a, got, tt.want)
    56  		}
    57  	}
    58  }
    59  
    60  func TestLayerMergeMismatch(t *testing.T) {
    61  	tcp := &TCP{}
    62  	otherTCP := &TCP{}
    63  	ipv4 := &IPv4{}
    64  	ether := &Ether{}
    65  	for _, tt := range []struct {
    66  		a, b    Layer
    67  		success bool
    68  	}{
    69  		{tcp, tcp, true},
    70  		{tcp, otherTCP, true},
    71  		{tcp, ipv4, false},
    72  		{tcp, ether, false},
    73  		{tcp, nil, true},
    74  
    75  		{otherTCP, otherTCP, true},
    76  		{otherTCP, ipv4, false},
    77  		{otherTCP, ether, false},
    78  		{otherTCP, nil, true},
    79  
    80  		{ipv4, ipv4, true},
    81  		{ipv4, ether, false},
    82  		{ipv4, nil, true},
    83  
    84  		{ether, ether, true},
    85  		{ether, nil, true},
    86  	} {
    87  		if err := tt.a.merge(tt.b); (err == nil) != tt.success {
    88  			t.Errorf("%s.merge(%s) got %s, wanted the opposite", tt.a, tt.b, err)
    89  		}
    90  		if tt.b != nil {
    91  			if err := tt.b.merge(tt.a); (err == nil) != tt.success {
    92  				t.Errorf("%s.merge(%s) got %s, wanted the opposite", tt.b, tt.a, err)
    93  			}
    94  		}
    95  	}
    96  }
    97  
    98  func TestLayerMerge(t *testing.T) {
    99  	zero := Uint32(0)
   100  	one := Uint32(1)
   101  	two := Uint32(2)
   102  	empty := []byte{}
   103  	foo := []byte("foo")
   104  	bar := []byte("bar")
   105  	for _, tt := range []struct {
   106  		a, b Layer
   107  		want Layer
   108  	}{
   109  		{&TCP{AckNum: nil}, &TCP{AckNum: nil}, &TCP{AckNum: nil}},
   110  		{&TCP{AckNum: nil}, &TCP{AckNum: zero}, &TCP{AckNum: zero}},
   111  		{&TCP{AckNum: nil}, &TCP{AckNum: one}, &TCP{AckNum: one}},
   112  		{&TCP{AckNum: nil}, &TCP{AckNum: two}, &TCP{AckNum: two}},
   113  		{&TCP{AckNum: nil}, nil, &TCP{AckNum: nil}},
   114  
   115  		{&TCP{AckNum: zero}, &TCP{AckNum: nil}, &TCP{AckNum: zero}},
   116  		{&TCP{AckNum: zero}, &TCP{AckNum: zero}, &TCP{AckNum: zero}},
   117  		{&TCP{AckNum: zero}, &TCP{AckNum: one}, &TCP{AckNum: one}},
   118  		{&TCP{AckNum: zero}, &TCP{AckNum: two}, &TCP{AckNum: two}},
   119  		{&TCP{AckNum: zero}, nil, &TCP{AckNum: zero}},
   120  
   121  		{&TCP{AckNum: one}, &TCP{AckNum: nil}, &TCP{AckNum: one}},
   122  		{&TCP{AckNum: one}, &TCP{AckNum: zero}, &TCP{AckNum: zero}},
   123  		{&TCP{AckNum: one}, &TCP{AckNum: one}, &TCP{AckNum: one}},
   124  		{&TCP{AckNum: one}, &TCP{AckNum: two}, &TCP{AckNum: two}},
   125  		{&TCP{AckNum: one}, nil, &TCP{AckNum: one}},
   126  
   127  		{&TCP{AckNum: two}, &TCP{AckNum: nil}, &TCP{AckNum: two}},
   128  		{&TCP{AckNum: two}, &TCP{AckNum: zero}, &TCP{AckNum: zero}},
   129  		{&TCP{AckNum: two}, &TCP{AckNum: one}, &TCP{AckNum: one}},
   130  		{&TCP{AckNum: two}, &TCP{AckNum: two}, &TCP{AckNum: two}},
   131  		{&TCP{AckNum: two}, nil, &TCP{AckNum: two}},
   132  
   133  		{&Payload{Bytes: nil}, &Payload{Bytes: nil}, &Payload{Bytes: nil}},
   134  		{&Payload{Bytes: nil}, &Payload{Bytes: empty}, &Payload{Bytes: empty}},
   135  		{&Payload{Bytes: nil}, &Payload{Bytes: foo}, &Payload{Bytes: foo}},
   136  		{&Payload{Bytes: nil}, &Payload{Bytes: bar}, &Payload{Bytes: bar}},
   137  		{&Payload{Bytes: nil}, nil, &Payload{Bytes: nil}},
   138  
   139  		{&Payload{Bytes: empty}, &Payload{Bytes: nil}, &Payload{Bytes: empty}},
   140  		{&Payload{Bytes: empty}, &Payload{Bytes: empty}, &Payload{Bytes: empty}},
   141  		{&Payload{Bytes: empty}, &Payload{Bytes: foo}, &Payload{Bytes: foo}},
   142  		{&Payload{Bytes: empty}, &Payload{Bytes: bar}, &Payload{Bytes: bar}},
   143  		{&Payload{Bytes: empty}, nil, &Payload{Bytes: empty}},
   144  
   145  		{&Payload{Bytes: foo}, &Payload{Bytes: nil}, &Payload{Bytes: foo}},
   146  		{&Payload{Bytes: foo}, &Payload{Bytes: empty}, &Payload{Bytes: empty}},
   147  		{&Payload{Bytes: foo}, &Payload{Bytes: foo}, &Payload{Bytes: foo}},
   148  		{&Payload{Bytes: foo}, &Payload{Bytes: bar}, &Payload{Bytes: bar}},
   149  		{&Payload{Bytes: foo}, nil, &Payload{Bytes: foo}},
   150  
   151  		{&Payload{Bytes: bar}, &Payload{Bytes: nil}, &Payload{Bytes: bar}},
   152  		{&Payload{Bytes: bar}, &Payload{Bytes: empty}, &Payload{Bytes: empty}},
   153  		{&Payload{Bytes: bar}, &Payload{Bytes: foo}, &Payload{Bytes: foo}},
   154  		{&Payload{Bytes: bar}, &Payload{Bytes: bar}, &Payload{Bytes: bar}},
   155  		{&Payload{Bytes: bar}, nil, &Payload{Bytes: bar}},
   156  	} {
   157  		a := deepcopy.Copy(tt.a).(Layer)
   158  		if err := a.merge(tt.b); err != nil {
   159  			t.Errorf("%s.merge(%s) = %s, wanted nil", tt.a, tt.b, err)
   160  			continue
   161  		}
   162  		if a.String() != tt.want.String() {
   163  			t.Errorf("%s.merge(%s) merge result got %s, want %s", tt.a, tt.b, a, tt.want)
   164  		}
   165  	}
   166  }
   167  
   168  func TestLayerStringFormat(t *testing.T) {
   169  	for _, tt := range []struct {
   170  		name string
   171  		l    Layer
   172  		want string
   173  	}{
   174  		{
   175  			name: "TCP",
   176  			l: &TCP{
   177  				SrcPort:    Uint16(34785),
   178  				DstPort:    Uint16(47767),
   179  				SeqNum:     Uint32(3452155723),
   180  				AckNum:     Uint32(2596996163),
   181  				DataOffset: Uint8(5),
   182  				Flags:      TCPFlags(header.TCPFlagRst | header.TCPFlagAck),
   183  				WindowSize: Uint16(64240),
   184  				Checksum:   Uint16(0x2e2b),
   185  			},
   186  			want: "&testbench.TCP{" +
   187  				"SrcPort:34785 " +
   188  				"DstPort:47767 " +
   189  				"SeqNum:3452155723 " +
   190  				"AckNum:2596996163 " +
   191  				"DataOffset:5 " +
   192  				"Flags:  R A    " +
   193  				"WindowSize:64240 " +
   194  				"Checksum:11819" +
   195  				"}",
   196  		},
   197  		{
   198  			name: "UDP",
   199  			l: &UDP{
   200  				SrcPort: Uint16(34785),
   201  				DstPort: Uint16(47767),
   202  				Length:  Uint16(12),
   203  			},
   204  			want: "&testbench.UDP{" +
   205  				"SrcPort:34785 " +
   206  				"DstPort:47767 " +
   207  				"Length:12" +
   208  				"}",
   209  		},
   210  		{
   211  			name: "IPv4",
   212  			l: &IPv4{
   213  				IHL:            Uint8(5),
   214  				TOS:            Uint8(0),
   215  				TotalLength:    Uint16(44),
   216  				ID:             Uint16(0),
   217  				Flags:          Uint8(2),
   218  				FragmentOffset: Uint16(0),
   219  				TTL:            Uint8(64),
   220  				Protocol:       Uint8(6),
   221  				Checksum:       Uint16(0x2e2b),
   222  				SrcAddr:        Address(tcpip.AddrFrom4Slice([]byte{197, 34, 63, 10})),
   223  				DstAddr:        Address(tcpip.AddrFrom4Slice([]byte{197, 34, 63, 20})),
   224  			},
   225  			want: "&testbench.IPv4{" +
   226  				"IHL:5 " +
   227  				"TOS:0 " +
   228  				"TotalLength:44 " +
   229  				"ID:0 " +
   230  				"Flags:2 " +
   231  				"FragmentOffset:0 " +
   232  				"TTL:64 " +
   233  				"Protocol:6 " +
   234  				"Checksum:11819 " +
   235  				"SrcAddr:197.34.63.10 " +
   236  				"DstAddr:197.34.63.20" +
   237  				"}",
   238  		},
   239  		{
   240  			name: "Ether",
   241  			l: &Ether{
   242  				SrcAddr: LinkAddress(tcpip.LinkAddress([]byte{0x02, 0x42, 0xc5, 0x22, 0x3f, 0x0a})),
   243  				DstAddr: LinkAddress(tcpip.LinkAddress([]byte{0x02, 0x42, 0xc5, 0x22, 0x3f, 0x14})),
   244  				Type:    NetworkProtocolNumber(4),
   245  			},
   246  			want: "&testbench.Ether{" +
   247  				"SrcAddr:02:42:c5:22:3f:0a " +
   248  				"DstAddr:02:42:c5:22:3f:14 " +
   249  				"Type:4" +
   250  				"}",
   251  		},
   252  		{
   253  			name: "Payload",
   254  			l: &Payload{
   255  				Bytes: []byte("Hooray for packetimpact."),
   256  			},
   257  			want: "&testbench.Payload{Bytes:\n" +
   258  				"00000000  48 6f 6f 72 61 79 20 66  6f 72 20 70 61 63 6b 65  |Hooray for packe|\n" +
   259  				"00000010  74 69 6d 70 61 63 74 2e                           |timpact.|\n" +
   260  				"}",
   261  		},
   262  	} {
   263  		t.Run(tt.name, func(t *testing.T) {
   264  			if got := tt.l.String(); got != tt.want {
   265  				t.Errorf("%s.String() = %s, want: %s", tt.name, got, tt.want)
   266  			}
   267  		})
   268  	}
   269  }
   270  
   271  func TestConnectionMatch(t *testing.T) {
   272  	conn := Connection{
   273  		layerStates: []layerState{&etherState{}},
   274  	}
   275  	protoNum0 := tcpip.NetworkProtocolNumber(0)
   276  	protoNum1 := tcpip.NetworkProtocolNumber(1)
   277  	for _, tt := range []struct {
   278  		description        string
   279  		override, received Layers
   280  		wantMatch          bool
   281  	}{
   282  		{
   283  			description: "shorter override",
   284  			override:    []Layer{&Ether{}},
   285  			received:    []Layer{&Ether{}, &Payload{Bytes: []byte("hello")}},
   286  			wantMatch:   true,
   287  		},
   288  		{
   289  			description: "longer override",
   290  			override:    []Layer{&Ether{}, &Payload{Bytes: []byte("hello")}},
   291  			received:    []Layer{&Ether{}},
   292  			wantMatch:   false,
   293  		},
   294  		{
   295  			description: "ether layer mismatch",
   296  			override:    []Layer{&Ether{Type: &protoNum0}},
   297  			received:    []Layer{&Ether{Type: &protoNum1}},
   298  			wantMatch:   false,
   299  		},
   300  		{
   301  			description: "both nil",
   302  			override:    nil,
   303  			received:    nil,
   304  			wantMatch:   false,
   305  		},
   306  		{
   307  			description: "nil override",
   308  			override:    nil,
   309  			received:    []Layer{&Ether{}},
   310  			wantMatch:   true,
   311  		},
   312  	} {
   313  		t.Run(tt.description, func(t *testing.T) {
   314  			if gotMatch := conn.match(tt.override, tt.received); gotMatch != tt.wantMatch {
   315  				t.Fatalf("conn.match(%s, %s) = %t, want %t", tt.override, tt.received, gotMatch, tt.wantMatch)
   316  			}
   317  		})
   318  	}
   319  }
   320  
   321  func TestLayersDiff(t *testing.T) {
   322  	for _, tt := range []struct {
   323  		x, y Layers
   324  		want string
   325  	}{
   326  		{
   327  			Layers{&Ether{Type: NetworkProtocolNumber(12)}, &TCP{DataOffset: Uint8(5), SeqNum: Uint32(5)}},
   328  			Layers{&Ether{Type: NetworkProtocolNumber(13)}, &TCP{DataOffset: Uint8(7), SeqNum: Uint32(6)}},
   329  			"Ether:       Type: 12 13\n" +
   330  				"  TCP:     SeqNum:  5  6\n" +
   331  				"       DataOffset:  5  7\n",
   332  		},
   333  		{
   334  			Layers{&Ether{Type: NetworkProtocolNumber(12)}, &UDP{SrcPort: Uint16(123)}},
   335  			Layers{&Ether{Type: NetworkProtocolNumber(13)}, &TCP{DataOffset: Uint8(7), SeqNum: Uint32(6)}},
   336  			"Ether:       Type:  12 13\n" +
   337  				"(UDP doesn't match TCP)\n" +
   338  				"  UDP:    SrcPort: 123   \n" +
   339  				"  TCP:     SeqNum:      6\n" +
   340  				"       DataOffset:      7\n",
   341  		},
   342  		{
   343  			Layers{&UDP{SrcPort: Uint16(123)}},
   344  			Layers{&Ether{Type: NetworkProtocolNumber(13)}, &TCP{DataOffset: Uint8(7), SeqNum: Uint32(6)}},
   345  			"(UDP doesn't match Ether)\n" +
   346  				"  UDP: SrcPort: 123   \n" +
   347  				"Ether:    Type:     13\n" +
   348  				"(missing matches TCP)\n",
   349  		},
   350  		{
   351  			Layers{nil, &UDP{SrcPort: Uint16(123)}},
   352  			Layers{&Ether{Type: NetworkProtocolNumber(13)}, &TCP{DataOffset: Uint8(7), SeqNum: Uint32(6)}},
   353  			"(nil matches Ether)\n" +
   354  				"(UDP doesn't match TCP)\n" +
   355  				"UDP:    SrcPort: 123  \n" +
   356  				"TCP:     SeqNum:     6\n" +
   357  				"     DataOffset:     7\n",
   358  		},
   359  		{
   360  			Layers{&Ether{Type: NetworkProtocolNumber(13)}, &IPv4{IHL: Uint8(4)}, &TCP{DataOffset: Uint8(7), SeqNum: Uint32(6)}},
   361  			Layers{&Ether{Type: NetworkProtocolNumber(13)}, &IPv4{IHL: Uint8(6)}, &TCP{DataOffset: Uint8(7), SeqNum: Uint32(6)}},
   362  			"(Ether matches Ether)\n" +
   363  				"IPv4: IHL: 4 6\n" +
   364  				"(TCP matches TCP)\n",
   365  		},
   366  		{
   367  			Layers{&Payload{Bytes: []byte("foo")}},
   368  			Layers{&Payload{Bytes: []byte("bar")}},
   369  			"Payload: Bytes: [102 111 111] [98 97 114]\n",
   370  		},
   371  		{
   372  			Layers{&Payload{Bytes: []byte("")}},
   373  			Layers{&Payload{}},
   374  			"",
   375  		},
   376  		{
   377  			Layers{&Payload{Bytes: []byte("")}},
   378  			Layers{&Payload{Bytes: []byte("")}},
   379  			"",
   380  		},
   381  		{
   382  			Layers{&UDP{}},
   383  			Layers{&TCP{}},
   384  			"(UDP doesn't match TCP)\n" +
   385  				"(UDP)\n" +
   386  				"(TCP)\n",
   387  		},
   388  	} {
   389  		if got := tt.x.diff(tt.y); got != tt.want {
   390  			t.Errorf("%s.diff(%s) = %q, want %q", tt.x, tt.y, got, tt.want)
   391  		}
   392  		if tt.x.match(tt.y) != (tt.x.diff(tt.y) == "") {
   393  			t.Errorf("match and diff of %s and %s disagree", tt.x, tt.y)
   394  		}
   395  		if tt.y.match(tt.x) != (tt.y.diff(tt.x) == "") {
   396  			t.Errorf("match and diff of %s and %s disagree", tt.y, tt.x)
   397  		}
   398  	}
   399  }
   400  
   401  func TestTCPOptions(t *testing.T) {
   402  	for _, tt := range []struct {
   403  		description string
   404  		wantBytes   []byte
   405  		wantLayers  Layers
   406  	}{
   407  		{
   408  			description: "without payload",
   409  			wantBytes: []byte{
   410  				// IPv4 Header
   411  				0x45, 0x00, 0x00, 0x2c, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06,
   412  				0xf9, 0x77, 0xc0, 0xa8, 0x00, 0x02, 0xc0, 0xa8, 0x00, 0x01,
   413  				// TCP Header
   414  				0x30, 0x39, 0xd4, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   415  				0x00, 0x00, 0x60, 0x02, 0x20, 0x00, 0xf5, 0x1c, 0x00, 0x00,
   416  				// WindowScale Option
   417  				0x03, 0x03, 0x02,
   418  				// NOP Option
   419  				0x00,
   420  			},
   421  			wantLayers: []Layer{
   422  				&IPv4{
   423  					IHL:            Uint8(20),
   424  					TOS:            Uint8(0),
   425  					TotalLength:    Uint16(44),
   426  					ID:             Uint16(1),
   427  					Flags:          Uint8(0),
   428  					FragmentOffset: Uint16(0),
   429  					TTL:            Uint8(64),
   430  					Protocol:       Uint8(uint8(header.TCPProtocolNumber)),
   431  					Checksum:       Uint16(0xf977),
   432  					SrcAddr:        Address(tcpip.AddrFrom4Slice(net.ParseIP("192.168.0.2").To4())),
   433  					DstAddr:        Address(tcpip.AddrFrom4Slice(net.ParseIP("192.168.0.1").To4())),
   434  				},
   435  				&TCP{
   436  					SrcPort:       Uint16(12345),
   437  					DstPort:       Uint16(54321),
   438  					SeqNum:        Uint32(0),
   439  					AckNum:        Uint32(0),
   440  					Flags:         TCPFlags(header.TCPFlagSyn),
   441  					WindowSize:    Uint16(8192),
   442  					Checksum:      Uint16(0xf51c),
   443  					UrgentPointer: Uint16(0),
   444  					Options:       []byte{3, 3, 2, 0},
   445  				},
   446  				&Payload{Bytes: nil},
   447  			},
   448  		},
   449  		{
   450  			description: "with payload",
   451  			wantBytes: []byte{
   452  				// IPv4 header
   453  				0x45, 0x00, 0x00, 0x37, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06,
   454  				0xf9, 0x6c, 0xc0, 0xa8, 0x00, 0x02, 0xc0, 0xa8, 0x00, 0x01,
   455  				// TCP header
   456  				0x30, 0x39, 0xd4, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   457  				0x00, 0x00, 0x60, 0x02, 0x20, 0x00, 0xe5, 0x21, 0x00, 0x00,
   458  				// WindowScale Option
   459  				0x03, 0x03, 0x02,
   460  				// NOP Option
   461  				0x00,
   462  				// Payload: "Sample Data"
   463  				0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61,
   464  			},
   465  			wantLayers: []Layer{
   466  				&IPv4{
   467  					IHL:            Uint8(20),
   468  					TOS:            Uint8(0),
   469  					TotalLength:    Uint16(55),
   470  					ID:             Uint16(1),
   471  					Flags:          Uint8(0),
   472  					FragmentOffset: Uint16(0),
   473  					TTL:            Uint8(64),
   474  					Protocol:       Uint8(uint8(header.TCPProtocolNumber)),
   475  					Checksum:       Uint16(0xf96c),
   476  					SrcAddr:        Address(tcpip.AddrFrom4Slice(net.ParseIP("192.168.0.2").To4())),
   477  					DstAddr:        Address(tcpip.AddrFrom4Slice(net.ParseIP("192.168.0.1").To4())),
   478  				},
   479  				&TCP{
   480  					SrcPort:       Uint16(12345),
   481  					DstPort:       Uint16(54321),
   482  					SeqNum:        Uint32(0),
   483  					AckNum:        Uint32(0),
   484  					Flags:         TCPFlags(header.TCPFlagSyn),
   485  					WindowSize:    Uint16(8192),
   486  					Checksum:      Uint16(0xe521),
   487  					UrgentPointer: Uint16(0),
   488  					Options:       []byte{3, 3, 2, 0},
   489  				},
   490  				&Payload{Bytes: []byte("Sample Data")},
   491  			},
   492  		},
   493  	} {
   494  		t.Run(tt.description, func(t *testing.T) {
   495  			layers := parse(parseIPv4, tt.wantBytes)
   496  			if !layers.match(tt.wantLayers) {
   497  				t.Fatalf("match failed with diff: %s", layers.diff(tt.wantLayers))
   498  			}
   499  			gotBytes, err := layers.ToBytes()
   500  			if err != nil {
   501  				t.Fatalf("ToBytes() failed on %s: %s", &layers, err)
   502  			}
   503  			if !bytes.Equal(tt.wantBytes, gotBytes) {
   504  				t.Fatalf("mismatching bytes, gotBytes: %x, wantBytes: %x", gotBytes, tt.wantBytes)
   505  			}
   506  		})
   507  	}
   508  }
   509  
   510  func TestIPv6ExtHdrOptions(t *testing.T) {
   511  	for _, tt := range []struct {
   512  		description string
   513  		wantBytes   []byte
   514  		wantLayers  Layers
   515  	}{
   516  		{
   517  			description: "IPv6/HopByHop",
   518  			wantBytes: []byte{
   519  				// IPv6 Header
   520  				0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00,
   521  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   522  				0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
   523  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
   524  				// HopByHop Options
   525  				0x3b, 0x00, 0x05, 0x02, 0x00, 0x00, 0x01, 0x00,
   526  			},
   527  			wantLayers: []Layer{
   528  				&IPv6{
   529  					SrcAddr: Address(tcpip.AddrFrom16Slice(net.ParseIP("::1"))),
   530  					DstAddr: Address(tcpip.AddrFrom16Slice(net.ParseIP("fe80::dead:beef"))),
   531  				},
   532  				&IPv6HopByHopOptionsExtHdr{
   533  					NextHeader: IPv6ExtHdrIdent(header.IPv6NoNextHeaderIdentifier),
   534  					Options:    []byte{0x05, 0x02, 0x00, 0x00, 0x01, 0x00},
   535  				},
   536  				&Payload{
   537  					Bytes: nil,
   538  				},
   539  			},
   540  		},
   541  		{
   542  			description: "IPv6/HopByHop/Payload",
   543  			wantBytes: []byte{
   544  				// IPv6 Header
   545  				0x60, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x40, 0x00, 0x00,
   546  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   547  				0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
   548  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
   549  				// HopByHop Options
   550  				0x3b, 0x00, 0x05, 0x02, 0x00, 0x00, 0x01, 0x00,
   551  				// Sample Data
   552  				0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61,
   553  			},
   554  			wantLayers: []Layer{
   555  				&IPv6{
   556  					SrcAddr: Address(tcpip.AddrFromSlice(net.ParseIP("::1"))),
   557  					DstAddr: Address(tcpip.AddrFromSlice(net.ParseIP("fe80::dead:beef"))),
   558  				},
   559  				&IPv6HopByHopOptionsExtHdr{
   560  					NextHeader: IPv6ExtHdrIdent(header.IPv6NoNextHeaderIdentifier),
   561  					Options:    []byte{0x05, 0x02, 0x00, 0x00, 0x01, 0x00},
   562  				},
   563  				&Payload{
   564  					Bytes: []byte("Sample Data"),
   565  				},
   566  			},
   567  		},
   568  		{
   569  			description: "IPv6/HopByHop/Destination/ICMPv6",
   570  			wantBytes: []byte{
   571  				// IPv6 Header
   572  				0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x40, 0x00, 0x00,
   573  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   574  				0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
   575  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
   576  				// HopByHop Options
   577  				0x3c, 0x00, 0x05, 0x02, 0x00, 0x00, 0x01, 0x00,
   578  				// Destination Options
   579  				0x3a, 0x00, 0x05, 0x02, 0x00, 0x00, 0x01, 0x00,
   580  				// ICMPv6 Param Problem
   581  				0x04, 0x00, 0x5f, 0x98, 0x00, 0x00, 0x00, 0x06,
   582  			},
   583  			wantLayers: []Layer{
   584  				&IPv6{
   585  					SrcAddr: Address(tcpip.AddrFromSlice(net.ParseIP("::1"))),
   586  					DstAddr: Address(tcpip.AddrFromSlice(net.ParseIP("fe80::dead:beef"))),
   587  				},
   588  				&IPv6HopByHopOptionsExtHdr{
   589  					NextHeader: IPv6ExtHdrIdent(header.IPv6DestinationOptionsExtHdrIdentifier),
   590  					Options:    []byte{0x05, 0x02, 0x00, 0x00, 0x01, 0x00},
   591  				},
   592  				&IPv6DestinationOptionsExtHdr{
   593  					NextHeader: IPv6ExtHdrIdent(header.IPv6ExtensionHeaderIdentifier(header.ICMPv6ProtocolNumber)),
   594  					Options:    []byte{0x05, 0x02, 0x00, 0x00, 0x01, 0x00},
   595  				},
   596  				&ICMPv6{
   597  					Type:     ICMPv6Type(header.ICMPv6ParamProblem),
   598  					Code:     ICMPv6Code(header.ICMPv6ErroneousHeader),
   599  					Checksum: Uint16(0x5f98),
   600  					Pointer:  Uint32(6),
   601  				},
   602  			},
   603  		},
   604  		{
   605  			description: "IPv6/HopByHop/Fragment",
   606  			wantBytes: []byte{
   607  				// IPv6 Header
   608  				0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x40, 0x00, 0x00,
   609  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   610  				0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
   611  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
   612  				// HopByHop Options
   613  				0x2c, 0x00, 0x05, 0x02, 0x00, 0x00, 0x01, 0x00,
   614  				// Fragment ExtHdr
   615  				0x3b, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x2a,
   616  			},
   617  			wantLayers: []Layer{
   618  				&IPv6{
   619  					SrcAddr: Address(tcpip.AddrFromSlice(net.ParseIP("::1"))),
   620  					DstAddr: Address(tcpip.AddrFromSlice(net.ParseIP("fe80::dead:beef"))),
   621  				},
   622  				&IPv6HopByHopOptionsExtHdr{
   623  					NextHeader: IPv6ExtHdrIdent(header.IPv6FragmentExtHdrIdentifier),
   624  					Options:    []byte{0x05, 0x02, 0x00, 0x00, 0x01, 0x00},
   625  				},
   626  				&IPv6FragmentExtHdr{
   627  					NextHeader:     IPv6ExtHdrIdent(header.IPv6NoNextHeaderIdentifier),
   628  					FragmentOffset: Uint16(100),
   629  					MoreFragments:  Bool(false),
   630  					Identification: Uint32(42),
   631  				},
   632  				&Payload{
   633  					Bytes: nil,
   634  				},
   635  			},
   636  		},
   637  		{
   638  			description: "IPv6/DestOpt/Fragment/Payload",
   639  			wantBytes: []byte{
   640  				// IPv6 Header
   641  				0x60, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x3c, 0x40, 0x00, 0x00,
   642  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   643  				0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
   644  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
   645  				// Destination Options
   646  				0x2c, 0x00, 0x05, 0x02, 0x00, 0x00, 0x01, 0x00,
   647  				// Fragment ExtHdr
   648  				0x3b, 0x00, 0x03, 0x21, 0x00, 0x00, 0x00, 0x2a,
   649  				// Sample Data
   650  				0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61,
   651  			},
   652  			wantLayers: []Layer{
   653  				&IPv6{
   654  					SrcAddr: Address(tcpip.AddrFromSlice(net.ParseIP("::1"))),
   655  					DstAddr: Address(tcpip.AddrFromSlice(net.ParseIP("fe80::dead:beef"))),
   656  				},
   657  				&IPv6DestinationOptionsExtHdr{
   658  					NextHeader: IPv6ExtHdrIdent(header.IPv6FragmentExtHdrIdentifier),
   659  					Options:    []byte{0x05, 0x02, 0x00, 0x00, 0x01, 0x00},
   660  				},
   661  				&IPv6FragmentExtHdr{
   662  					NextHeader:     IPv6ExtHdrIdent(header.IPv6NoNextHeaderIdentifier),
   663  					FragmentOffset: Uint16(100),
   664  					MoreFragments:  Bool(true),
   665  					Identification: Uint32(42),
   666  				},
   667  				&Payload{
   668  					Bytes: []byte("Sample Data"),
   669  				},
   670  			},
   671  		},
   672  		{
   673  			description: "IPv6/Fragment/Payload",
   674  			wantBytes: []byte{
   675  				// IPv6 Header
   676  				0x60, 0x00, 0x00, 0x00, 0x00, 0x13, 0x2c, 0x40, 0x00, 0x00,
   677  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   678  				0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
   679  				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
   680  				// Fragment ExtHdr
   681  				0x3b, 0x00, 0x03, 0x21, 0x00, 0x00, 0x00, 0x2a,
   682  				// Sample Data
   683  				0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61,
   684  			},
   685  			wantLayers: []Layer{
   686  				&IPv6{
   687  					SrcAddr: Address(tcpip.AddrFromSlice(net.ParseIP("::1"))),
   688  					DstAddr: Address(tcpip.AddrFromSlice(net.ParseIP("fe80::dead:beef"))),
   689  				},
   690  				&IPv6FragmentExtHdr{
   691  					NextHeader:     IPv6ExtHdrIdent(header.IPv6NoNextHeaderIdentifier),
   692  					FragmentOffset: Uint16(100),
   693  					MoreFragments:  Bool(true),
   694  					Identification: Uint32(42),
   695  				},
   696  				&Payload{
   697  					Bytes: []byte("Sample Data"),
   698  				},
   699  			},
   700  		},
   701  	} {
   702  		t.Run(tt.description, func(t *testing.T) {
   703  			layers := parse(parseIPv6, tt.wantBytes)
   704  			if !layers.match(tt.wantLayers) {
   705  				t.Fatalf("match failed with diff: %s", layers.diff(tt.wantLayers))
   706  			}
   707  			// Make sure we can generate correct next header values and checksums
   708  			for _, layer := range layers {
   709  				switch layer := layer.(type) {
   710  				case *IPv6HopByHopOptionsExtHdr:
   711  					layer.NextHeader = nil
   712  				case *IPv6DestinationOptionsExtHdr:
   713  					layer.NextHeader = nil
   714  				case *IPv6FragmentExtHdr:
   715  					layer.NextHeader = nil
   716  				case *ICMPv6:
   717  					layer.Checksum = nil
   718  				}
   719  			}
   720  			gotBytes, err := layers.ToBytes()
   721  			if err != nil {
   722  				t.Fatalf("ToBytes() failed on %s: %s", &layers, err)
   723  			}
   724  			if !bytes.Equal(tt.wantBytes, gotBytes) {
   725  				t.Fatalf("mismatching bytes, gotBytes: %x, wantBytes: %x", gotBytes, tt.wantBytes)
   726  			}
   727  		})
   728  	}
   729  }
   730  
   731  func TestEthernetPadding(t *testing.T) {
   732  	packet := []byte{
   733  		0x3a, 0xd6, 0x90, 0x36, 0x18, 0xce, 0x64, 0x4f, 0x16, 0x3f,
   734  		0x5f, 0x0f, 0x08, 0x00, 0x45, 0x00, 0x00, 0x2c, 0xf5, 0x0e,
   735  		0x00, 0x00, 0x40, 0x06, 0x2d, 0xba, 0xac, 0x00, 0x00, 0x02,
   736  		0xac, 0x00, 0x00, 0x01, 0x7c, 0x3e, 0xe3, 0x91, 0x2b, 0xe4,
   737  		0xb0, 0xe7, 0x9a, 0xcb, 0x04, 0x43, 0x60, 0x12, 0x72, 0x00,
   738  		0xf2, 0x67, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x00, 0x00,
   739  	}
   740  	parsed := parse(parseEther, packet)
   741  	wanted := Layers{
   742  		&Ether{
   743  			SrcAddr: LinkAddress(tcpip.LinkAddress("\x64\x4f\x16\x3f\x5f\x0f")),
   744  			DstAddr: LinkAddress(tcpip.LinkAddress("\x3a\xd6\x90\x36\x18\xce")),
   745  			Type:    NetworkProtocolNumber(header.IPv4ProtocolNumber),
   746  		},
   747  		&IPv4{
   748  			IHL:            Uint8(20),
   749  			TOS:            Uint8(0),
   750  			TotalLength:    Uint16(44),
   751  			ID:             Uint16(0xf50e),
   752  			Flags:          Uint8(0),
   753  			FragmentOffset: Uint16(0),
   754  			TTL:            Uint8(64),
   755  			Protocol:       Uint8(uint8(header.TCPProtocolNumber)),
   756  			Checksum:       Uint16(0x2dba),
   757  			SrcAddr:        Address(tcpip.AddrFromSlice([]byte("\xac\x00\x00\x02"))),
   758  			DstAddr:        Address(tcpip.AddrFromSlice([]byte("\xac\x00\x00\x01"))),
   759  		},
   760  		&TCP{
   761  			SrcPort:       Uint16(31806),
   762  			DstPort:       Uint16(58257),
   763  			SeqNum:        Uint32(736407783),
   764  			AckNum:        Uint32(2596996163),
   765  			DataOffset:    Uint8(24),
   766  			Flags:         TCPFlags(header.TCPFlagSyn | header.TCPFlagAck),
   767  			WindowSize:    Uint16(29184),
   768  			Checksum:      Uint16(0xf267),
   769  			UrgentPointer: Uint16(0),
   770  		},
   771  		&Payload{
   772  			Bytes: []byte{},
   773  		},
   774  	}
   775  	if !parsed.match(wanted) {
   776  		t.Fatalf("parse(parseEther, %s) = %s, want %s)", hex.Dump(packet), parsed, wanted)
   777  	}
   778  }