github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/header/ipv6_extension_headers_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 header
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"io"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	"github.com/SagerNet/gvisor/pkg/tcpip"
    25  	"github.com/SagerNet/gvisor/pkg/tcpip/buffer"
    26  )
    27  
    28  // Equal returns true of a and b are equivalent.
    29  //
    30  // Note, Equal will return true if a and b hold the same Identifier value and
    31  // contain the same bytes in Buf, even if the bytes are split across views
    32  // differently.
    33  //
    34  // Needed to use cmp.Equal on IPv6RawPayloadHeader as it contains unexported
    35  // fields.
    36  func (a IPv6RawPayloadHeader) Equal(b IPv6RawPayloadHeader) bool {
    37  	return a.Identifier == b.Identifier && bytes.Equal(a.Buf.ToView(), b.Buf.ToView())
    38  }
    39  
    40  // Equal returns true of a and b are equivalent.
    41  //
    42  // Note, Equal will return true if a and b hold equivalent ipv6OptionsExtHdrs.
    43  //
    44  // Needed to use cmp.Equal on IPv6RawPayloadHeader as it contains unexported
    45  // fields.
    46  func (a IPv6HopByHopOptionsExtHdr) Equal(b IPv6HopByHopOptionsExtHdr) bool {
    47  	return bytes.Equal(a.ipv6OptionsExtHdr, b.ipv6OptionsExtHdr)
    48  }
    49  
    50  // Equal returns true of a and b are equivalent.
    51  //
    52  // Note, Equal will return true if a and b hold equivalent ipv6OptionsExtHdrs.
    53  //
    54  // Needed to use cmp.Equal on IPv6RawPayloadHeader as it contains unexported
    55  // fields.
    56  func (a IPv6DestinationOptionsExtHdr) Equal(b IPv6DestinationOptionsExtHdr) bool {
    57  	return bytes.Equal(a.ipv6OptionsExtHdr, b.ipv6OptionsExtHdr)
    58  }
    59  
    60  func TestIPv6UnknownExtHdrOption(t *testing.T) {
    61  	tests := []struct {
    62  		name                  string
    63  		identifier            IPv6ExtHdrOptionIdentifier
    64  		expectedUnknownAction IPv6OptionUnknownAction
    65  	}{
    66  		{
    67  			name:                  "Skip with zero LSBs",
    68  			identifier:            0,
    69  			expectedUnknownAction: IPv6OptionUnknownActionSkip,
    70  		},
    71  		{
    72  			name:                  "Discard with zero LSBs",
    73  			identifier:            64,
    74  			expectedUnknownAction: IPv6OptionUnknownActionDiscard,
    75  		},
    76  		{
    77  			name:                  "Discard and ICMP with zero LSBs",
    78  			identifier:            128,
    79  			expectedUnknownAction: IPv6OptionUnknownActionDiscardSendICMP,
    80  		},
    81  		{
    82  			name:                  "Discard and ICMP for non multicast destination with zero LSBs",
    83  			identifier:            192,
    84  			expectedUnknownAction: IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest,
    85  		},
    86  		{
    87  			name:                  "Skip with non-zero LSBs",
    88  			identifier:            63,
    89  			expectedUnknownAction: IPv6OptionUnknownActionSkip,
    90  		},
    91  		{
    92  			name:                  "Discard with non-zero LSBs",
    93  			identifier:            127,
    94  			expectedUnknownAction: IPv6OptionUnknownActionDiscard,
    95  		},
    96  		{
    97  			name:                  "Discard and ICMP with non-zero LSBs",
    98  			identifier:            191,
    99  			expectedUnknownAction: IPv6OptionUnknownActionDiscardSendICMP,
   100  		},
   101  		{
   102  			name:                  "Discard and ICMP for non multicast destination with non-zero LSBs",
   103  			identifier:            255,
   104  			expectedUnknownAction: IPv6OptionUnknownActionDiscardSendICMPNoMulticastDest,
   105  		},
   106  	}
   107  
   108  	for _, test := range tests {
   109  		t.Run(test.name, func(t *testing.T) {
   110  			opt := &IPv6UnknownExtHdrOption{Identifier: test.identifier, Data: []byte{1, 2, 3, 4}}
   111  			if a := opt.UnknownAction(); a != test.expectedUnknownAction {
   112  				t.Fatalf("got UnknownAction() = %d, want = %d", a, test.expectedUnknownAction)
   113  			}
   114  		})
   115  	}
   116  
   117  }
   118  
   119  func TestIPv6OptionsExtHdrIterErr(t *testing.T) {
   120  	tests := []struct {
   121  		name  string
   122  		bytes []byte
   123  		err   error
   124  	}{
   125  		{
   126  			name:  "Single unknown with zero length",
   127  			bytes: []byte{255, 0},
   128  		},
   129  		{
   130  			name:  "Single unknown with non-zero length",
   131  			bytes: []byte{255, 3, 1, 2, 3},
   132  		},
   133  		{
   134  			name: "Two options",
   135  			bytes: []byte{
   136  				255, 0,
   137  				254, 1, 1,
   138  			},
   139  		},
   140  		{
   141  			name: "Three options",
   142  			bytes: []byte{
   143  				255, 0,
   144  				254, 1, 1,
   145  				253, 4, 2, 3, 4, 5,
   146  			},
   147  		},
   148  		{
   149  			name:  "Single unknown only identifier",
   150  			bytes: []byte{255},
   151  			err:   io.ErrUnexpectedEOF,
   152  		},
   153  		{
   154  			name:  "Single unknown too small with length = 1",
   155  			bytes: []byte{255, 1},
   156  			err:   io.ErrUnexpectedEOF,
   157  		},
   158  		{
   159  			name:  "Single unknown too small with length = 2",
   160  			bytes: []byte{255, 2, 1},
   161  			err:   io.ErrUnexpectedEOF,
   162  		},
   163  		{
   164  			name: "Valid first with second unknown only identifier",
   165  			bytes: []byte{
   166  				255, 0,
   167  				254,
   168  			},
   169  			err: io.ErrUnexpectedEOF,
   170  		},
   171  		{
   172  			name: "Valid first with second unknown missing data",
   173  			bytes: []byte{
   174  				255, 0,
   175  				254, 1,
   176  			},
   177  			err: io.ErrUnexpectedEOF,
   178  		},
   179  		{
   180  			name: "Valid first with second unknown too small",
   181  			bytes: []byte{
   182  				255, 0,
   183  				254, 2, 1,
   184  			},
   185  			err: io.ErrUnexpectedEOF,
   186  		},
   187  		{
   188  			name:  "One Pad1",
   189  			bytes: []byte{0},
   190  		},
   191  		{
   192  			name:  "Multiple Pad1",
   193  			bytes: []byte{0, 0, 0},
   194  		},
   195  		{
   196  			name: "Multiple PadN",
   197  			bytes: []byte{
   198  				// Pad3
   199  				1, 1, 1,
   200  
   201  				// Pad5
   202  				1, 3, 1, 2, 3,
   203  			},
   204  		},
   205  		{
   206  			name:  "Pad5 too small middle of data buffer",
   207  			bytes: []byte{1, 3, 1, 2},
   208  			err:   io.ErrUnexpectedEOF,
   209  		},
   210  		{
   211  			name:  "Pad5 no data",
   212  			bytes: []byte{1, 3},
   213  			err:   io.ErrUnexpectedEOF,
   214  		},
   215  		{
   216  			name:  "Router alert without data",
   217  			bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 0},
   218  			err:   ErrMalformedIPv6ExtHdrOption,
   219  		},
   220  		{
   221  			name:  "Router alert with partial data",
   222  			bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 1, 1},
   223  			err:   ErrMalformedIPv6ExtHdrOption,
   224  		},
   225  		{
   226  			name:  "Router alert with partial data and Pad1",
   227  			bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 1, 1, 0},
   228  			err:   ErrMalformedIPv6ExtHdrOption,
   229  		},
   230  		{
   231  			name:  "Router alert with extra data",
   232  			bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 3, 1, 2, 3},
   233  			err:   ErrMalformedIPv6ExtHdrOption,
   234  		},
   235  		{
   236  			name:  "Router alert with missing data",
   237  			bytes: []byte{byte(ipv6RouterAlertHopByHopOptionIdentifier), 1},
   238  			err:   io.ErrUnexpectedEOF,
   239  		},
   240  	}
   241  
   242  	check := func(t *testing.T, it IPv6OptionsExtHdrOptionsIterator, expectedErr error) {
   243  		for i := 0; ; i++ {
   244  			_, done, err := it.Next()
   245  			if err != nil {
   246  				// If we encountered a non-nil error while iterating, make sure it is
   247  				// is the same error as expectedErr.
   248  				if !errors.Is(err, expectedErr) {
   249  					t.Fatalf("got %d-th Next() = %v, want = %v", i, err, expectedErr)
   250  				}
   251  
   252  				return
   253  			}
   254  			if done {
   255  				// If we are done (without an error), make sure that we did not expect
   256  				// an error.
   257  				if expectedErr != nil {
   258  					t.Fatalf("expected error when iterating; want = %s", expectedErr)
   259  				}
   260  
   261  				return
   262  			}
   263  		}
   264  	}
   265  
   266  	for _, test := range tests {
   267  		t.Run(test.name, func(t *testing.T) {
   268  			t.Run("Hop By Hop", func(t *testing.T) {
   269  				extHdr := IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr: test.bytes}
   270  				check(t, extHdr.Iter(), test.err)
   271  			})
   272  
   273  			t.Run("Destination", func(t *testing.T) {
   274  				extHdr := IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr: test.bytes}
   275  				check(t, extHdr.Iter(), test.err)
   276  			})
   277  		})
   278  	}
   279  }
   280  
   281  func TestIPv6OptionsExtHdrIter(t *testing.T) {
   282  	tests := []struct {
   283  		name     string
   284  		bytes    []byte
   285  		expected []IPv6ExtHdrOption
   286  	}{
   287  		{
   288  			name:  "Single unknown with zero length",
   289  			bytes: []byte{255, 0},
   290  			expected: []IPv6ExtHdrOption{
   291  				&IPv6UnknownExtHdrOption{Identifier: 255, Data: []byte{}},
   292  			},
   293  		},
   294  		{
   295  			name:  "Single unknown with non-zero length",
   296  			bytes: []byte{255, 3, 1, 2, 3},
   297  			expected: []IPv6ExtHdrOption{
   298  				&IPv6UnknownExtHdrOption{Identifier: 255, Data: []byte{1, 2, 3}},
   299  			},
   300  		},
   301  		{
   302  			name:  "Single Pad1",
   303  			bytes: []byte{0},
   304  		},
   305  		{
   306  			name:  "Two Pad1",
   307  			bytes: []byte{0, 0},
   308  		},
   309  		{
   310  			name:  "Single Pad3",
   311  			bytes: []byte{1, 1, 1},
   312  		},
   313  		{
   314  			name:  "Single Pad5",
   315  			bytes: []byte{1, 3, 1, 2, 3},
   316  		},
   317  		{
   318  			name: "Multiple Pad",
   319  			bytes: []byte{
   320  				// Pad1
   321  				0,
   322  
   323  				// Pad2
   324  				1, 0,
   325  
   326  				// Pad3
   327  				1, 1, 1,
   328  
   329  				// Pad4
   330  				1, 2, 1, 2,
   331  
   332  				// Pad5
   333  				1, 3, 1, 2, 3,
   334  			},
   335  		},
   336  		{
   337  			name: "Multiple options",
   338  			bytes: []byte{
   339  				// Pad1
   340  				0,
   341  
   342  				// Unknown
   343  				255, 0,
   344  
   345  				// Pad2
   346  				1, 0,
   347  
   348  				// Unknown
   349  				254, 1, 1,
   350  
   351  				// Pad3
   352  				1, 1, 1,
   353  
   354  				// Unknown
   355  				253, 4, 2, 3, 4, 5,
   356  
   357  				// Pad4
   358  				1, 2, 1, 2,
   359  			},
   360  			expected: []IPv6ExtHdrOption{
   361  				&IPv6UnknownExtHdrOption{Identifier: 255, Data: []byte{}},
   362  				&IPv6UnknownExtHdrOption{Identifier: 254, Data: []byte{1}},
   363  				&IPv6UnknownExtHdrOption{Identifier: 253, Data: []byte{2, 3, 4, 5}},
   364  			},
   365  		},
   366  	}
   367  
   368  	checkIter := func(t *testing.T, it IPv6OptionsExtHdrOptionsIterator, expected []IPv6ExtHdrOption) {
   369  		for i, e := range expected {
   370  			opt, done, err := it.Next()
   371  			if err != nil {
   372  				t.Errorf("(i=%d) Next(): %s", i, err)
   373  			}
   374  			if done {
   375  				t.Errorf("(i=%d) unexpectedly done iterating", i)
   376  			}
   377  			if diff := cmp.Diff(e, opt); diff != "" {
   378  				t.Errorf("(i=%d) got option mismatch (-want +got):\n%s", i, diff)
   379  			}
   380  
   381  			if t.Failed() {
   382  				t.FailNow()
   383  			}
   384  		}
   385  
   386  		opt, done, err := it.Next()
   387  		if err != nil {
   388  			t.Errorf("(last) Next(): %s", err)
   389  		}
   390  		if !done {
   391  			t.Errorf("(last) iterator unexpectedly not done")
   392  		}
   393  		if opt != nil {
   394  			t.Errorf("(last) got Next() = %T, want = nil", opt)
   395  		}
   396  	}
   397  
   398  	for _, test := range tests {
   399  		t.Run(test.name, func(t *testing.T) {
   400  			t.Run("Hop By Hop", func(t *testing.T) {
   401  				extHdr := IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr: test.bytes}
   402  				checkIter(t, extHdr.Iter(), test.expected)
   403  			})
   404  
   405  			t.Run("Destination", func(t *testing.T) {
   406  				extHdr := IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr: test.bytes}
   407  				checkIter(t, extHdr.Iter(), test.expected)
   408  			})
   409  		})
   410  	}
   411  }
   412  
   413  func TestIPv6RoutingExtHdr(t *testing.T) {
   414  	tests := []struct {
   415  		name         string
   416  		bytes        []byte
   417  		segmentsLeft uint8
   418  	}{
   419  		{
   420  			name:         "Zeroes",
   421  			bytes:        []byte{0, 0, 0, 0, 0, 0},
   422  			segmentsLeft: 0,
   423  		},
   424  		{
   425  			name:         "Ones",
   426  			bytes:        []byte{1, 1, 1, 1, 1, 1},
   427  			segmentsLeft: 1,
   428  		},
   429  		{
   430  			name:         "Mixed",
   431  			bytes:        []byte{1, 2, 3, 4, 5, 6},
   432  			segmentsLeft: 2,
   433  		},
   434  	}
   435  
   436  	for _, test := range tests {
   437  		t.Run(test.name, func(t *testing.T) {
   438  			extHdr := IPv6RoutingExtHdr(test.bytes)
   439  			if got := extHdr.SegmentsLeft(); got != test.segmentsLeft {
   440  				t.Errorf("got SegmentsLeft() = %d, want = %d", got, test.segmentsLeft)
   441  			}
   442  		})
   443  	}
   444  }
   445  
   446  func TestIPv6FragmentExtHdr(t *testing.T) {
   447  	tests := []struct {
   448  		name           string
   449  		bytes          [6]byte
   450  		fragmentOffset uint16
   451  		more           bool
   452  		id             uint32
   453  	}{
   454  		{
   455  			name:           "Zeroes",
   456  			bytes:          [6]byte{0, 0, 0, 0, 0, 0},
   457  			fragmentOffset: 0,
   458  			more:           false,
   459  			id:             0,
   460  		},
   461  		{
   462  			name:           "Ones",
   463  			bytes:          [6]byte{0, 9, 0, 0, 0, 1},
   464  			fragmentOffset: 1,
   465  			more:           true,
   466  			id:             1,
   467  		},
   468  		{
   469  			name:           "Mixed",
   470  			bytes:          [6]byte{68, 9, 128, 4, 2, 1},
   471  			fragmentOffset: 2177,
   472  			more:           true,
   473  			id:             2147746305,
   474  		},
   475  	}
   476  
   477  	for _, test := range tests {
   478  		t.Run(test.name, func(t *testing.T) {
   479  			extHdr := IPv6FragmentExtHdr(test.bytes)
   480  			if got := extHdr.FragmentOffset(); got != test.fragmentOffset {
   481  				t.Errorf("got FragmentOffset() = %d, want = %d", got, test.fragmentOffset)
   482  			}
   483  			if got := extHdr.More(); got != test.more {
   484  				t.Errorf("got More() = %t, want = %t", got, test.more)
   485  			}
   486  			if got := extHdr.ID(); got != test.id {
   487  				t.Errorf("got ID() = %d, want = %d", got, test.id)
   488  			}
   489  		})
   490  	}
   491  }
   492  
   493  func makeVectorisedViewFromByteBuffers(bs ...[]byte) buffer.VectorisedView {
   494  	size := 0
   495  	var vs []buffer.View
   496  
   497  	for _, b := range bs {
   498  		vs = append(vs, buffer.View(b))
   499  		size += len(b)
   500  	}
   501  
   502  	return buffer.NewVectorisedView(size, vs)
   503  }
   504  
   505  func TestIPv6ExtHdrIterErr(t *testing.T) {
   506  	tests := []struct {
   507  		name         string
   508  		firstNextHdr IPv6ExtensionHeaderIdentifier
   509  		payload      buffer.VectorisedView
   510  		err          error
   511  	}{
   512  		{
   513  			name:         "Upper layer only without data",
   514  			firstNextHdr: 255,
   515  		},
   516  		{
   517  			name:         "Upper layer only with data",
   518  			firstNextHdr: 255,
   519  			payload:      makeVectorisedViewFromByteBuffers([]byte{1, 2, 3, 4}),
   520  		},
   521  		{
   522  			name:         "No next header",
   523  			firstNextHdr: IPv6NoNextHeaderIdentifier,
   524  		},
   525  		{
   526  			name:         "No next header with data",
   527  			firstNextHdr: IPv6NoNextHeaderIdentifier,
   528  			payload:      makeVectorisedViewFromByteBuffers([]byte{1, 2, 3, 4}),
   529  		},
   530  		{
   531  			name:         "Valid single hop by hop",
   532  			firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier,
   533  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 4, 1, 2, 3, 4}),
   534  		},
   535  		{
   536  			name:         "Hop by hop too small",
   537  			firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier,
   538  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 4, 1, 2, 3}),
   539  			err:          io.ErrUnexpectedEOF,
   540  		},
   541  		{
   542  			name:         "Valid single fragment",
   543  			firstNextHdr: IPv6FragmentExtHdrIdentifier,
   544  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 0, 68, 9, 128, 4, 2, 1}),
   545  		},
   546  		{
   547  			name:         "Fragment too small",
   548  			firstNextHdr: IPv6FragmentExtHdrIdentifier,
   549  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 0, 68, 9, 128, 4, 2}),
   550  			err:          io.ErrUnexpectedEOF,
   551  		},
   552  		{
   553  			name:         "Valid single destination",
   554  			firstNextHdr: IPv6DestinationOptionsExtHdrIdentifier,
   555  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 4, 1, 2, 3, 4}),
   556  		},
   557  		{
   558  			name:         "Destination too small",
   559  			firstNextHdr: IPv6DestinationOptionsExtHdrIdentifier,
   560  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 4, 1, 2, 3}),
   561  			err:          io.ErrUnexpectedEOF,
   562  		},
   563  		{
   564  			name:         "Valid single routing",
   565  			firstNextHdr: IPv6RoutingExtHdrIdentifier,
   566  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 2, 3, 4, 5, 6}),
   567  		},
   568  		{
   569  			name:         "Valid single routing across views",
   570  			firstNextHdr: IPv6RoutingExtHdrIdentifier,
   571  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 2}, []byte{3, 4, 5, 6}),
   572  		},
   573  		{
   574  			name:         "Routing too small with zero length field",
   575  			firstNextHdr: IPv6RoutingExtHdrIdentifier,
   576  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 0, 1, 2, 3, 4, 5}),
   577  			err:          io.ErrUnexpectedEOF,
   578  		},
   579  		{
   580  			name:         "Valid routing with non-zero length field",
   581  			firstNextHdr: IPv6RoutingExtHdrIdentifier,
   582  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8}),
   583  		},
   584  		{
   585  			name:         "Valid routing with non-zero length field across views",
   586  			firstNextHdr: IPv6RoutingExtHdrIdentifier,
   587  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6}, []byte{1, 2, 3, 4, 5, 6, 7, 8}),
   588  		},
   589  		{
   590  			name:         "Routing too small with non-zero length field",
   591  			firstNextHdr: IPv6RoutingExtHdrIdentifier,
   592  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7}),
   593  			err:          io.ErrUnexpectedEOF,
   594  		},
   595  		{
   596  			name:         "Routing too small with non-zero length field across views",
   597  			firstNextHdr: IPv6RoutingExtHdrIdentifier,
   598  			payload:      makeVectorisedViewFromByteBuffers([]byte{255, 1, 1, 2, 3, 4, 5, 6}, []byte{1, 2, 3, 4, 5, 6, 7}),
   599  			err:          io.ErrUnexpectedEOF,
   600  		},
   601  		{
   602  			name:         "Mixed",
   603  			firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier,
   604  			payload: makeVectorisedViewFromByteBuffers([]byte{
   605  				// Hop By Hop Options extension header.
   606  				uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4,
   607  
   608  				// (Atomic) Fragment extension header.
   609  				//
   610  				// Reserved bits are 1 which should not affect anything.
   611  				uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1,
   612  
   613  				// Routing extension header.
   614  				uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6,
   615  
   616  				// Destination Options extension header.
   617  				255, 0, 255, 4, 1, 2, 3, 4,
   618  
   619  				// Upper layer data.
   620  				1, 2, 3, 4,
   621  			}),
   622  		},
   623  		{
   624  			name:         "Mixed without upper layer data",
   625  			firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier,
   626  			payload: makeVectorisedViewFromByteBuffers([]byte{
   627  				// Hop By Hop Options extension header.
   628  				uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4,
   629  
   630  				// (Atomic) Fragment extension header.
   631  				//
   632  				// Reserved bits are 1 which should not affect anything.
   633  				uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1,
   634  
   635  				// Routing extension header.
   636  				uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6,
   637  
   638  				// Destination Options extension header.
   639  				255, 0, 255, 4, 1, 2, 3, 4,
   640  			}),
   641  		},
   642  		{
   643  			name:         "Mixed without upper layer data but last ext hdr too small",
   644  			firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier,
   645  			payload: makeVectorisedViewFromByteBuffers([]byte{
   646  				// Hop By Hop Options extension header.
   647  				uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4,
   648  
   649  				// (Atomic) Fragment extension header.
   650  				//
   651  				// Reserved bits are 1 which should not affect anything.
   652  				uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1,
   653  
   654  				// Routing extension header.
   655  				uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6,
   656  
   657  				// Destination Options extension header.
   658  				255, 0, 255, 4, 1, 2, 3,
   659  			}),
   660  			err: io.ErrUnexpectedEOF,
   661  		},
   662  	}
   663  
   664  	for _, test := range tests {
   665  		t.Run(test.name, func(t *testing.T) {
   666  			it := MakeIPv6PayloadIterator(test.firstNextHdr, test.payload)
   667  
   668  			for i := 0; ; i++ {
   669  				_, done, err := it.Next()
   670  				if err != nil {
   671  					// If we encountered a non-nil error while iterating, make sure it is
   672  					// is the same error as test.err.
   673  					if !errors.Is(err, test.err) {
   674  						t.Fatalf("got %d-th Next() = %v, want = %v", i, err, test.err)
   675  					}
   676  
   677  					return
   678  				}
   679  				if done {
   680  					// If we are done (without an error), make sure that we did not expect
   681  					// an error.
   682  					if test.err != nil {
   683  						t.Fatalf("expected error when iterating; want = %s", test.err)
   684  					}
   685  
   686  					return
   687  				}
   688  			}
   689  		})
   690  	}
   691  }
   692  
   693  func TestIPv6ExtHdrIter(t *testing.T) {
   694  	routingExtHdrWithUpperLayerData := buffer.View([]byte{255, 0, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4})
   695  	upperLayerData := buffer.View([]byte{1, 2, 3, 4})
   696  	tests := []struct {
   697  		name         string
   698  		firstNextHdr IPv6ExtensionHeaderIdentifier
   699  		payload      buffer.VectorisedView
   700  		expected     []IPv6PayloadHeader
   701  	}{
   702  		// With a non-atomic fragment that is not the first fragment, the payload
   703  		// after the fragment will not be parsed because the payload is expected to
   704  		// only hold upper layer data.
   705  		{
   706  			name:         "hopbyhop - fragment (not first) - routing - upper",
   707  			firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier,
   708  			payload: makeVectorisedViewFromByteBuffers([]byte{
   709  				// Hop By Hop extension header.
   710  				uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4,
   711  
   712  				// Fragment extension header.
   713  				//
   714  				// More = 1, Fragment Offset = 2117, ID = 2147746305
   715  				uint8(IPv6RoutingExtHdrIdentifier), 0, 68, 9, 128, 4, 2, 1,
   716  
   717  				// Routing extension header.
   718  				//
   719  				// Even though we have a routing ext header here, it should be
   720  				// be interpretted as raw bytes as only the first fragment is expected
   721  				// to hold headers.
   722  				255, 0, 1, 2, 3, 4, 5, 6,
   723  
   724  				// Upper layer data.
   725  				1, 2, 3, 4,
   726  			}),
   727  			expected: []IPv6PayloadHeader{
   728  				IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr: []byte{1, 4, 1, 2, 3, 4}},
   729  				IPv6FragmentExtHdr([6]byte{68, 9, 128, 4, 2, 1}),
   730  				IPv6RawPayloadHeader{
   731  					Identifier: IPv6RoutingExtHdrIdentifier,
   732  					Buf:        routingExtHdrWithUpperLayerData.ToVectorisedView(),
   733  				},
   734  			},
   735  		},
   736  		{
   737  			name:         "hopbyhop - fragment (first) - routing - upper",
   738  			firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier,
   739  			payload: makeVectorisedViewFromByteBuffers([]byte{
   740  				// Hop By Hop extension header.
   741  				uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4,
   742  
   743  				// Fragment extension header.
   744  				//
   745  				// More = 1, Fragment Offset = 0, ID = 2147746305
   746  				uint8(IPv6RoutingExtHdrIdentifier), 0, 0, 1, 128, 4, 2, 1,
   747  
   748  				// Routing extension header.
   749  				255, 0, 1, 2, 3, 4, 5, 6,
   750  
   751  				// Upper layer data.
   752  				1, 2, 3, 4,
   753  			}),
   754  			expected: []IPv6PayloadHeader{
   755  				IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr: []byte{1, 4, 1, 2, 3, 4}},
   756  				IPv6FragmentExtHdr([6]byte{0, 1, 128, 4, 2, 1}),
   757  				IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}),
   758  				IPv6RawPayloadHeader{
   759  					Identifier: 255,
   760  					Buf:        upperLayerData.ToVectorisedView(),
   761  				},
   762  			},
   763  		},
   764  		{
   765  			name:         "fragment - routing - upper (across views)",
   766  			firstNextHdr: IPv6FragmentExtHdrIdentifier,
   767  			payload: makeVectorisedViewFromByteBuffers([]byte{
   768  				// Fragment extension header.
   769  				uint8(IPv6RoutingExtHdrIdentifier), 0, 68, 9, 128, 4, 2, 1,
   770  
   771  				// Routing extension header.
   772  				255, 0, 1, 2}, []byte{3, 4, 5, 6,
   773  
   774  				// Upper layer data.
   775  				1, 2, 3, 4,
   776  			}),
   777  			expected: []IPv6PayloadHeader{
   778  				IPv6FragmentExtHdr([6]byte{68, 9, 128, 4, 2, 1}),
   779  				IPv6RawPayloadHeader{
   780  					Identifier: IPv6RoutingExtHdrIdentifier,
   781  					Buf:        routingExtHdrWithUpperLayerData.ToVectorisedView(),
   782  				},
   783  			},
   784  		},
   785  
   786  		// If we have an atomic fragment, the payload following the fragment
   787  		// extension header should be parsed normally.
   788  		{
   789  			name:         "atomic fragment - routing - destination - upper",
   790  			firstNextHdr: IPv6FragmentExtHdrIdentifier,
   791  			payload: makeVectorisedViewFromByteBuffers([]byte{
   792  				// Fragment extension header.
   793  				//
   794  				// Reserved bits are 1 which should not affect anything.
   795  				uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6, 128, 4, 2, 1,
   796  
   797  				// Routing extension header.
   798  				uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6,
   799  
   800  				// Destination Options extension header.
   801  				255, 0, 1, 4, 1, 2, 3, 4,
   802  
   803  				// Upper layer data.
   804  				1, 2, 3, 4,
   805  			}),
   806  			expected: []IPv6PayloadHeader{
   807  				IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}),
   808  				IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}),
   809  				IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr: []byte{1, 4, 1, 2, 3, 4}},
   810  				IPv6RawPayloadHeader{
   811  					Identifier: 255,
   812  					Buf:        upperLayerData.ToVectorisedView(),
   813  				},
   814  			},
   815  		},
   816  		{
   817  			name:         "atomic fragment - routing - upper (across views)",
   818  			firstNextHdr: IPv6FragmentExtHdrIdentifier,
   819  			payload: makeVectorisedViewFromByteBuffers([]byte{
   820  				// Fragment extension header.
   821  				//
   822  				// Reserved bits are 1 which should not affect anything.
   823  				uint8(IPv6RoutingExtHdrIdentifier), 255, 0, 6}, []byte{128, 4, 2, 1,
   824  
   825  				// Routing extension header.
   826  				255, 0, 1, 2}, []byte{3, 4, 5, 6,
   827  
   828  				// Upper layer data.
   829  				1, 2}, []byte{3, 4}),
   830  			expected: []IPv6PayloadHeader{
   831  				IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}),
   832  				IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}),
   833  				IPv6RawPayloadHeader{
   834  					Identifier: 255,
   835  					Buf:        makeVectorisedViewFromByteBuffers(upperLayerData[:2], upperLayerData[2:]),
   836  				},
   837  			},
   838  		},
   839  		{
   840  			name:         "atomic fragment - destination - no next header",
   841  			firstNextHdr: IPv6FragmentExtHdrIdentifier,
   842  			payload: makeVectorisedViewFromByteBuffers([]byte{
   843  				// Fragment extension header.
   844  				//
   845  				// Res (Reserved) bits are 1 which should not affect anything.
   846  				uint8(IPv6DestinationOptionsExtHdrIdentifier), 0, 0, 6, 128, 4, 2, 1,
   847  
   848  				// Destination Options extension header.
   849  				uint8(IPv6NoNextHeaderIdentifier), 0, 1, 4, 1, 2, 3, 4,
   850  
   851  				// Random data.
   852  				1, 2, 3, 4,
   853  			}),
   854  			expected: []IPv6PayloadHeader{
   855  				IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}),
   856  				IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr: []byte{1, 4, 1, 2, 3, 4}},
   857  			},
   858  		},
   859  		{
   860  			name:         "routing - atomic fragment - no next header",
   861  			firstNextHdr: IPv6RoutingExtHdrIdentifier,
   862  			payload: makeVectorisedViewFromByteBuffers([]byte{
   863  				// Routing extension header.
   864  				uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6,
   865  
   866  				// Fragment extension header.
   867  				//
   868  				// Reserved bits are 1 which should not affect anything.
   869  				uint8(IPv6NoNextHeaderIdentifier), 0, 0, 6, 128, 4, 2, 1,
   870  
   871  				// Random data.
   872  				1, 2, 3, 4,
   873  			}),
   874  			expected: []IPv6PayloadHeader{
   875  				IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}),
   876  				IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}),
   877  			},
   878  		},
   879  		{
   880  			name:         "routing - atomic fragment - no next header (across views)",
   881  			firstNextHdr: IPv6RoutingExtHdrIdentifier,
   882  			payload: makeVectorisedViewFromByteBuffers([]byte{
   883  				// Routing extension header.
   884  				uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6,
   885  
   886  				// Fragment extension header.
   887  				//
   888  				// Reserved bits are 1 which should not affect anything.
   889  				uint8(IPv6NoNextHeaderIdentifier), 255, 0, 6}, []byte{128, 4, 2, 1,
   890  
   891  				// Random data.
   892  				1, 2, 3, 4,
   893  			}),
   894  			expected: []IPv6PayloadHeader{
   895  				IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}),
   896  				IPv6FragmentExtHdr([6]byte{0, 6, 128, 4, 2, 1}),
   897  			},
   898  		},
   899  		{
   900  			name:         "hopbyhop - routing - fragment - no next header",
   901  			firstNextHdr: IPv6HopByHopOptionsExtHdrIdentifier,
   902  			payload: makeVectorisedViewFromByteBuffers([]byte{
   903  				// Hop By Hop Options extension header.
   904  				uint8(IPv6RoutingExtHdrIdentifier), 0, 1, 4, 1, 2, 3, 4,
   905  
   906  				// Routing extension header.
   907  				uint8(IPv6FragmentExtHdrIdentifier), 0, 1, 2, 3, 4, 5, 6,
   908  
   909  				// Fragment extension header.
   910  				//
   911  				// Fragment Offset = 32; Res = 6.
   912  				uint8(IPv6NoNextHeaderIdentifier), 0, 1, 6, 128, 4, 2, 1,
   913  
   914  				// Random data.
   915  				1, 2, 3, 4,
   916  			}),
   917  			expected: []IPv6PayloadHeader{
   918  				IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr: []byte{1, 4, 1, 2, 3, 4}},
   919  				IPv6RoutingExtHdr([]byte{1, 2, 3, 4, 5, 6}),
   920  				IPv6FragmentExtHdr([6]byte{1, 6, 128, 4, 2, 1}),
   921  				IPv6RawPayloadHeader{
   922  					Identifier: IPv6NoNextHeaderIdentifier,
   923  					Buf:        upperLayerData.ToVectorisedView(),
   924  				},
   925  			},
   926  		},
   927  
   928  		// Test the raw payload for common transport layer protocol numbers.
   929  		{
   930  			name:         "TCP raw payload",
   931  			firstNextHdr: IPv6ExtensionHeaderIdentifier(TCPProtocolNumber),
   932  			payload:      makeVectorisedViewFromByteBuffers(upperLayerData),
   933  			expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{
   934  				Identifier: IPv6ExtensionHeaderIdentifier(TCPProtocolNumber),
   935  				Buf:        upperLayerData.ToVectorisedView(),
   936  			}},
   937  		},
   938  		{
   939  			name:         "UDP raw payload",
   940  			firstNextHdr: IPv6ExtensionHeaderIdentifier(UDPProtocolNumber),
   941  			payload:      makeVectorisedViewFromByteBuffers(upperLayerData),
   942  			expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{
   943  				Identifier: IPv6ExtensionHeaderIdentifier(UDPProtocolNumber),
   944  				Buf:        upperLayerData.ToVectorisedView(),
   945  			}},
   946  		},
   947  		{
   948  			name:         "ICMPv4 raw payload",
   949  			firstNextHdr: IPv6ExtensionHeaderIdentifier(ICMPv4ProtocolNumber),
   950  			payload:      makeVectorisedViewFromByteBuffers(upperLayerData),
   951  			expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{
   952  				Identifier: IPv6ExtensionHeaderIdentifier(ICMPv4ProtocolNumber),
   953  				Buf:        upperLayerData.ToVectorisedView(),
   954  			}},
   955  		},
   956  		{
   957  			name:         "ICMPv6 raw payload",
   958  			firstNextHdr: IPv6ExtensionHeaderIdentifier(ICMPv6ProtocolNumber),
   959  			payload:      makeVectorisedViewFromByteBuffers(upperLayerData),
   960  			expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{
   961  				Identifier: IPv6ExtensionHeaderIdentifier(ICMPv6ProtocolNumber),
   962  				Buf:        upperLayerData.ToVectorisedView(),
   963  			}},
   964  		},
   965  		{
   966  			name:         "Unknwon next header raw payload",
   967  			firstNextHdr: 255,
   968  			payload:      makeVectorisedViewFromByteBuffers(upperLayerData),
   969  			expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{
   970  				Identifier: 255,
   971  				Buf:        upperLayerData.ToVectorisedView(),
   972  			}},
   973  		},
   974  		{
   975  			name:         "Unknwon next header raw payload (across views)",
   976  			firstNextHdr: 255,
   977  			payload:      makeVectorisedViewFromByteBuffers(upperLayerData[:2], upperLayerData[2:]),
   978  			expected: []IPv6PayloadHeader{IPv6RawPayloadHeader{
   979  				Identifier: 255,
   980  				Buf:        makeVectorisedViewFromByteBuffers(upperLayerData[:2], upperLayerData[2:]),
   981  			}},
   982  		},
   983  	}
   984  
   985  	for _, test := range tests {
   986  		t.Run(test.name, func(t *testing.T) {
   987  			it := MakeIPv6PayloadIterator(test.firstNextHdr, test.payload)
   988  
   989  			for i, e := range test.expected {
   990  				extHdr, done, err := it.Next()
   991  				if err != nil {
   992  					t.Errorf("(i=%d) Next(): %s", i, err)
   993  				}
   994  				if done {
   995  					t.Errorf("(i=%d) unexpectedly done iterating", i)
   996  				}
   997  				if diff := cmp.Diff(e, extHdr); diff != "" {
   998  					t.Errorf("(i=%d) got ext hdr mismatch (-want +got):\n%s", i, diff)
   999  				}
  1000  
  1001  				if t.Failed() {
  1002  					t.FailNow()
  1003  				}
  1004  			}
  1005  
  1006  			extHdr, done, err := it.Next()
  1007  			if err != nil {
  1008  				t.Errorf("(last) Next(): %s", err)
  1009  			}
  1010  			if !done {
  1011  				t.Errorf("(last) iterator unexpectedly not done")
  1012  			}
  1013  			if extHdr != nil {
  1014  				t.Errorf("(last) got Next() = %T, want = nil", extHdr)
  1015  			}
  1016  		})
  1017  	}
  1018  }
  1019  
  1020  var _ IPv6SerializableHopByHopOption = (*dummyHbHOptionSerializer)(nil)
  1021  
  1022  // dummyHbHOptionSerializer provides a generic implementation of
  1023  // IPv6SerializableHopByHopOption for use in tests.
  1024  type dummyHbHOptionSerializer struct {
  1025  	id          IPv6ExtHdrOptionIdentifier
  1026  	payload     []byte
  1027  	align       int
  1028  	alignOffset int
  1029  }
  1030  
  1031  // identifier implements IPv6SerializableHopByHopOption.
  1032  func (s *dummyHbHOptionSerializer) identifier() IPv6ExtHdrOptionIdentifier {
  1033  	return s.id
  1034  }
  1035  
  1036  // length implements IPv6SerializableHopByHopOption.
  1037  func (s *dummyHbHOptionSerializer) length() uint8 {
  1038  	return uint8(len(s.payload))
  1039  }
  1040  
  1041  // alignment implements IPv6SerializableHopByHopOption.
  1042  func (s *dummyHbHOptionSerializer) alignment() (int, int) {
  1043  	align := 1
  1044  	if s.align != 0 {
  1045  		align = s.align
  1046  	}
  1047  	return align, s.alignOffset
  1048  }
  1049  
  1050  // serializeInto implements IPv6SerializableHopByHopOption.
  1051  func (s *dummyHbHOptionSerializer) serializeInto(b []byte) uint8 {
  1052  	return uint8(copy(b, s.payload))
  1053  }
  1054  
  1055  func TestIPv6HopByHopSerializer(t *testing.T) {
  1056  	validateDummies := func(t *testing.T, serializable IPv6SerializableHopByHopOption, deserialized IPv6ExtHdrOption) {
  1057  		t.Helper()
  1058  		dummy, ok := serializable.(*dummyHbHOptionSerializer)
  1059  		if !ok {
  1060  			t.Fatalf("got serializable = %T, want = *dummyHbHOptionSerializer", serializable)
  1061  		}
  1062  		unknown, ok := deserialized.(*IPv6UnknownExtHdrOption)
  1063  		if !ok {
  1064  			t.Fatalf("got deserialized = %T, want = %T", deserialized, &IPv6UnknownExtHdrOption{})
  1065  		}
  1066  		if dummy.id != unknown.Identifier {
  1067  			t.Errorf("got deserialized identifier = %d, want = %d", unknown.Identifier, dummy.id)
  1068  		}
  1069  		if diff := cmp.Diff(dummy.payload, unknown.Data); diff != "" {
  1070  			t.Errorf("option payload deserialization mismatch (-want +got):\n%s", diff)
  1071  		}
  1072  	}
  1073  	tests := []struct {
  1074  		name       string
  1075  		nextHeader uint8
  1076  		options    []IPv6SerializableHopByHopOption
  1077  		expect     []byte
  1078  		validate   func(*testing.T, IPv6SerializableHopByHopOption, IPv6ExtHdrOption)
  1079  	}{
  1080  		{
  1081  			name:       "single option",
  1082  			nextHeader: 13,
  1083  			options: []IPv6SerializableHopByHopOption{
  1084  				&dummyHbHOptionSerializer{
  1085  					id:      15,
  1086  					payload: []byte{9, 8, 7, 6},
  1087  				},
  1088  			},
  1089  			expect:   []byte{13, 0, 15, 4, 9, 8, 7, 6},
  1090  			validate: validateDummies,
  1091  		},
  1092  		{
  1093  			name:       "short option padN zero",
  1094  			nextHeader: 88,
  1095  			options: []IPv6SerializableHopByHopOption{
  1096  				&dummyHbHOptionSerializer{
  1097  					id:      22,
  1098  					payload: []byte{4, 5},
  1099  				},
  1100  			},
  1101  			expect:   []byte{88, 0, 22, 2, 4, 5, 1, 0},
  1102  			validate: validateDummies,
  1103  		},
  1104  		{
  1105  			name:       "short option pad1",
  1106  			nextHeader: 11,
  1107  			options: []IPv6SerializableHopByHopOption{
  1108  				&dummyHbHOptionSerializer{
  1109  					id:      33,
  1110  					payload: []byte{1, 2, 3},
  1111  				},
  1112  			},
  1113  			expect:   []byte{11, 0, 33, 3, 1, 2, 3, 0},
  1114  			validate: validateDummies,
  1115  		},
  1116  		{
  1117  			name:       "long option padN",
  1118  			nextHeader: 55,
  1119  			options: []IPv6SerializableHopByHopOption{
  1120  				&dummyHbHOptionSerializer{
  1121  					id:      77,
  1122  					payload: []byte{1, 2, 3, 4, 5, 6, 7, 8},
  1123  				},
  1124  			},
  1125  			expect:   []byte{55, 1, 77, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 0, 0},
  1126  			validate: validateDummies,
  1127  		},
  1128  		{
  1129  			name:       "two options",
  1130  			nextHeader: 33,
  1131  			options: []IPv6SerializableHopByHopOption{
  1132  				&dummyHbHOptionSerializer{
  1133  					id:      11,
  1134  					payload: []byte{1, 2, 3},
  1135  				},
  1136  				&dummyHbHOptionSerializer{
  1137  					id:      22,
  1138  					payload: []byte{4, 5, 6},
  1139  				},
  1140  			},
  1141  			expect:   []byte{33, 1, 11, 3, 1, 2, 3, 22, 3, 4, 5, 6, 1, 2, 0, 0},
  1142  			validate: validateDummies,
  1143  		},
  1144  		{
  1145  			name:       "two options align 2n",
  1146  			nextHeader: 33,
  1147  			options: []IPv6SerializableHopByHopOption{
  1148  				&dummyHbHOptionSerializer{
  1149  					id:      11,
  1150  					payload: []byte{1, 2, 3},
  1151  				},
  1152  				&dummyHbHOptionSerializer{
  1153  					id:      22,
  1154  					payload: []byte{4, 5, 6},
  1155  					align:   2,
  1156  				},
  1157  			},
  1158  			expect:   []byte{33, 1, 11, 3, 1, 2, 3, 0, 22, 3, 4, 5, 6, 1, 1, 0},
  1159  			validate: validateDummies,
  1160  		},
  1161  		{
  1162  			name:       "two options align 8n+1",
  1163  			nextHeader: 33,
  1164  			options: []IPv6SerializableHopByHopOption{
  1165  				&dummyHbHOptionSerializer{
  1166  					id:      11,
  1167  					payload: []byte{1, 2},
  1168  				},
  1169  				&dummyHbHOptionSerializer{
  1170  					id:          22,
  1171  					payload:     []byte{4, 5, 6},
  1172  					align:       8,
  1173  					alignOffset: 1,
  1174  				},
  1175  			},
  1176  			expect:   []byte{33, 1, 11, 2, 1, 2, 1, 1, 0, 22, 3, 4, 5, 6, 1, 0},
  1177  			validate: validateDummies,
  1178  		},
  1179  		{
  1180  			name:       "no options",
  1181  			nextHeader: 33,
  1182  			options:    []IPv6SerializableHopByHopOption{},
  1183  			expect:     []byte{33, 0, 1, 4, 0, 0, 0, 0},
  1184  		},
  1185  		{
  1186  			name:       "Router Alert",
  1187  			nextHeader: 33,
  1188  			options:    []IPv6SerializableHopByHopOption{&IPv6RouterAlertOption{Value: IPv6RouterAlertMLD}},
  1189  			expect:     []byte{33, 0, 5, 2, 0, 0, 1, 0},
  1190  			validate: func(t *testing.T, _ IPv6SerializableHopByHopOption, deserialized IPv6ExtHdrOption) {
  1191  				t.Helper()
  1192  				routerAlert, ok := deserialized.(*IPv6RouterAlertOption)
  1193  				if !ok {
  1194  					t.Fatalf("got deserialized = %T, want = *IPv6RouterAlertOption", deserialized)
  1195  				}
  1196  				if routerAlert.Value != IPv6RouterAlertMLD {
  1197  					t.Errorf("got routerAlert.Value = %d, want = %d", routerAlert.Value, IPv6RouterAlertMLD)
  1198  				}
  1199  			},
  1200  		},
  1201  	}
  1202  	for _, test := range tests {
  1203  		t.Run(test.name, func(t *testing.T) {
  1204  			s := IPv6SerializableHopByHopExtHdr(test.options)
  1205  			length := s.length()
  1206  			if length != len(test.expect) {
  1207  				t.Fatalf("got s.length() = %d, want = %d", length, len(test.expect))
  1208  			}
  1209  			b := make([]byte, length)
  1210  			for i := range b {
  1211  				// Fill the buffer with ones to ensure all padding is correctly set.
  1212  				b[i] = 0xFF
  1213  			}
  1214  			if got := s.serializeInto(test.nextHeader, b); got != length {
  1215  				t.Fatalf("got s.serializeInto(..) = %d, want = %d", got, length)
  1216  			}
  1217  			if diff := cmp.Diff(test.expect, b); diff != "" {
  1218  				t.Fatalf("serialization mismatch (-want +got):\n%s", diff)
  1219  			}
  1220  
  1221  			// Deserialize the options and verify them.
  1222  			optLen := (b[ipv6HopByHopExtHdrLengthOffset] + ipv6HopByHopExtHdrUnaccountedLenWords) * ipv6ExtHdrLenBytesPerUnit
  1223  			iter := ipv6OptionsExtHdr(b[ipv6HopByHopExtHdrOptionsOffset:optLen]).Iter()
  1224  			for _, testOpt := range test.options {
  1225  				opt, done, err := iter.Next()
  1226  				if err != nil {
  1227  					t.Fatalf("iter.Next(): %s", err)
  1228  				}
  1229  				if done {
  1230  					t.Fatalf("got iter.Next() = (%T, %t, _), want = (_, false, _)", opt, done)
  1231  				}
  1232  				test.validate(t, testOpt, opt)
  1233  			}
  1234  			opt, done, err := iter.Next()
  1235  			if err != nil {
  1236  				t.Fatalf("iter.Next(): %s", err)
  1237  			}
  1238  			if !done {
  1239  				t.Fatalf("got iter.Next() = (%T, %t, _), want = (_, true, _)", opt, done)
  1240  			}
  1241  		})
  1242  	}
  1243  }
  1244  
  1245  var _ IPv6SerializableExtHdr = (*dummyIPv6ExtHdrSerializer)(nil)
  1246  
  1247  // dummyIPv6ExtHdrSerializer provides a generic implementation of
  1248  // IPv6SerializableExtHdr for use in tests.
  1249  //
  1250  // The dummy header always carries the nextHeader value in the first byte.
  1251  type dummyIPv6ExtHdrSerializer struct {
  1252  	id             IPv6ExtensionHeaderIdentifier
  1253  	headerContents []byte
  1254  }
  1255  
  1256  // identifier implements IPv6SerializableExtHdr.
  1257  func (s *dummyIPv6ExtHdrSerializer) identifier() IPv6ExtensionHeaderIdentifier {
  1258  	return s.id
  1259  }
  1260  
  1261  // length implements IPv6SerializableExtHdr.
  1262  func (s *dummyIPv6ExtHdrSerializer) length() int {
  1263  	return len(s.headerContents) + 1
  1264  }
  1265  
  1266  // serializeInto implements IPv6SerializableExtHdr.
  1267  func (s *dummyIPv6ExtHdrSerializer) serializeInto(nextHeader uint8, b []byte) int {
  1268  	b[0] = nextHeader
  1269  	return copy(b[1:], s.headerContents) + 1
  1270  }
  1271  
  1272  func TestIPv6ExtHdrSerializer(t *testing.T) {
  1273  	tests := []struct {
  1274  		name             string
  1275  		headers          []IPv6SerializableExtHdr
  1276  		nextHeader       tcpip.TransportProtocolNumber
  1277  		expectSerialized []byte
  1278  		expectNextHeader uint8
  1279  	}{
  1280  		{
  1281  			name: "one header",
  1282  			headers: []IPv6SerializableExtHdr{
  1283  				&dummyIPv6ExtHdrSerializer{
  1284  					id:             15,
  1285  					headerContents: []byte{1, 2, 3, 4},
  1286  				},
  1287  			},
  1288  			nextHeader:       TCPProtocolNumber,
  1289  			expectSerialized: []byte{byte(TCPProtocolNumber), 1, 2, 3, 4},
  1290  			expectNextHeader: 15,
  1291  		},
  1292  		{
  1293  			name: "two headers",
  1294  			headers: []IPv6SerializableExtHdr{
  1295  				&dummyIPv6ExtHdrSerializer{
  1296  					id:             22,
  1297  					headerContents: []byte{1, 2, 3},
  1298  				},
  1299  				&dummyIPv6ExtHdrSerializer{
  1300  					id:             23,
  1301  					headerContents: []byte{4, 5, 6},
  1302  				},
  1303  			},
  1304  			nextHeader: ICMPv6ProtocolNumber,
  1305  			expectSerialized: []byte{
  1306  				23, 1, 2, 3,
  1307  				byte(ICMPv6ProtocolNumber), 4, 5, 6,
  1308  			},
  1309  			expectNextHeader: 22,
  1310  		},
  1311  		{
  1312  			name:             "no headers",
  1313  			headers:          []IPv6SerializableExtHdr{},
  1314  			nextHeader:       UDPProtocolNumber,
  1315  			expectSerialized: []byte{},
  1316  			expectNextHeader: byte(UDPProtocolNumber),
  1317  		},
  1318  	}
  1319  	for _, test := range tests {
  1320  		t.Run(test.name, func(t *testing.T) {
  1321  			s := IPv6ExtHdrSerializer(test.headers)
  1322  			l := s.Length()
  1323  			if got, want := l, len(test.expectSerialized); got != want {
  1324  				t.Fatalf("got serialized length = %d, want = %d", got, want)
  1325  			}
  1326  			b := make([]byte, l)
  1327  			for i := range b {
  1328  				// Fill the buffer with garbage to make sure we're writing to all bytes.
  1329  				b[i] = 0xFF
  1330  			}
  1331  			nextHeader, serializedLen := s.Serialize(test.nextHeader, b)
  1332  			if serializedLen != len(test.expectSerialized) || nextHeader != test.expectNextHeader {
  1333  				t.Errorf(
  1334  					"got s.Serialize(..) = (%d, %d), want = (%d, %d)",
  1335  					nextHeader,
  1336  					serializedLen,
  1337  					test.expectNextHeader,
  1338  					len(test.expectSerialized),
  1339  				)
  1340  			}
  1341  			if diff := cmp.Diff(test.expectSerialized, b); diff != "" {
  1342  				t.Errorf("serialization mismatch (-want +got):\n%s", diff)
  1343  			}
  1344  		})
  1345  	}
  1346  }