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