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