gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/header/ndp_test.go (about)

     1  // Copyright 2019 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  	"encoding/binary"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"regexp"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/google/go-cmp/cmp"
    29  	"gvisor.dev/gvisor/pkg/tcpip"
    30  	"gvisor.dev/gvisor/pkg/tcpip/testutil"
    31  )
    32  
    33  // TestNDPNeighborSolicit tests the functions of NDPNeighborSolicit.
    34  func TestNDPNeighborSolicit(t *testing.T) {
    35  	b := []byte{
    36  		0, 0, 0, 0,
    37  		1, 2, 3, 4,
    38  		5, 6, 7, 8,
    39  		9, 10, 11, 12,
    40  		13, 14, 15, 16,
    41  	}
    42  
    43  	// Test getting the Target Address.
    44  	ns := NDPNeighborSolicit(b)
    45  	addr := testutil.MustParse6("102:304:506:708:90a:b0c:d0e:f10")
    46  	if got := ns.TargetAddress(); got != addr {
    47  		t.Errorf("got ns.TargetAddress = %s, want %s", got, addr)
    48  	}
    49  
    50  	// Test updating the Target Address.
    51  	addr2 := testutil.MustParse6("1112:1314:1516:1718:191a:1b1c:1d1e:1f11")
    52  	ns.SetTargetAddress(addr2)
    53  	if got := ns.TargetAddress(); got != addr2 {
    54  		t.Errorf("got ns.TargetAddress = %s, want %s", got, addr2)
    55  	}
    56  	// Make sure the address got updated in the backing buffer.
    57  	if got := tcpip.AddrFrom16Slice(b[ndpNSTargetAddessOffset:][:IPv6AddressSize]); got != addr2 {
    58  		t.Errorf("got targetaddress buffer = %s, want %s", got, addr2)
    59  	}
    60  }
    61  
    62  func TestNDPRouteInformationOption(t *testing.T) {
    63  	tests := []struct {
    64  		name string
    65  
    66  		length         uint8
    67  		prefixLength   uint8
    68  		prf            NDPRoutePreference
    69  		lifetimeS      uint32
    70  		prefixBytes    []byte
    71  		expectedPrefix tcpip.Subnet
    72  
    73  		expectedErr error
    74  	}{
    75  		{
    76  			name:           "Length=1 with Prefix Length = 0",
    77  			length:         1,
    78  			prefixLength:   0,
    79  			prf:            MediumRoutePreference,
    80  			lifetimeS:      1,
    81  			prefixBytes:    nil,
    82  			expectedPrefix: IPv6EmptySubnet,
    83  		},
    84  		{
    85  			name:         "Length=1 but Prefix Length > 0",
    86  			length:       1,
    87  			prefixLength: 1,
    88  			prf:          MediumRoutePreference,
    89  			lifetimeS:    1,
    90  			prefixBytes:  nil,
    91  			expectedErr:  ErrNDPOptMalformedBody,
    92  		},
    93  		{
    94  			name:           "Length=2 with Prefix Length = 0",
    95  			length:         2,
    96  			prefixLength:   0,
    97  			prf:            MediumRoutePreference,
    98  			lifetimeS:      1,
    99  			prefixBytes:    nil,
   100  			expectedPrefix: IPv6EmptySubnet,
   101  		},
   102  		{
   103  			name:         "Length=2 with Prefix Length in [1, 64] (1)",
   104  			length:       2,
   105  			prefixLength: 1,
   106  			prf:          LowRoutePreference,
   107  			lifetimeS:    1,
   108  			prefixBytes:  nil,
   109  			expectedPrefix: tcpip.AddressWithPrefix{
   110  				Address:   tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))),
   111  				PrefixLen: 1,
   112  			}.Subnet(),
   113  		},
   114  		{
   115  			name:         "Length=2 with Prefix Length in [1, 64] (64)",
   116  			length:       2,
   117  			prefixLength: 64,
   118  			prf:          HighRoutePreference,
   119  			lifetimeS:    1,
   120  			prefixBytes:  nil,
   121  			expectedPrefix: tcpip.AddressWithPrefix{
   122  				Address:   tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))),
   123  				PrefixLen: 64,
   124  			}.Subnet(),
   125  		},
   126  		{
   127  			name:         "Length=2 with Prefix Length > 64",
   128  			length:       2,
   129  			prefixLength: 65,
   130  			prf:          HighRoutePreference,
   131  			lifetimeS:    1,
   132  			prefixBytes:  nil,
   133  			expectedErr:  ErrNDPOptMalformedBody,
   134  		},
   135  		{
   136  			name:           "Length=3 with Prefix Length = 0",
   137  			length:         3,
   138  			prefixLength:   0,
   139  			prf:            MediumRoutePreference,
   140  			lifetimeS:      1,
   141  			prefixBytes:    nil,
   142  			expectedPrefix: IPv6EmptySubnet,
   143  		},
   144  		{
   145  			name:         "Length=3 with Prefix Length in [1, 64] (1)",
   146  			length:       3,
   147  			prefixLength: 1,
   148  			prf:          LowRoutePreference,
   149  			lifetimeS:    1,
   150  			prefixBytes:  nil,
   151  			expectedPrefix: tcpip.AddressWithPrefix{
   152  				Address:   tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))),
   153  				PrefixLen: 1,
   154  			}.Subnet(),
   155  		},
   156  		{
   157  			name:         "Length=3 with Prefix Length in [1, 64] (64)",
   158  			length:       3,
   159  			prefixLength: 64,
   160  			prf:          HighRoutePreference,
   161  			lifetimeS:    1,
   162  			prefixBytes:  nil,
   163  			expectedPrefix: tcpip.AddressWithPrefix{
   164  				Address:   tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))),
   165  				PrefixLen: 64,
   166  			}.Subnet(),
   167  		},
   168  		{
   169  			name:         "Length=3 with Prefix Length in [65, 128] (65)",
   170  			length:       3,
   171  			prefixLength: 65,
   172  			prf:          HighRoutePreference,
   173  			lifetimeS:    1,
   174  			prefixBytes:  nil,
   175  			expectedPrefix: tcpip.AddressWithPrefix{
   176  				Address:   tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))),
   177  				PrefixLen: 65,
   178  			}.Subnet(),
   179  		},
   180  		{
   181  			name:         "Length=3 with Prefix Length in [65, 128] (128)",
   182  			length:       3,
   183  			prefixLength: 128,
   184  			prf:          HighRoutePreference,
   185  			lifetimeS:    1,
   186  			prefixBytes:  nil,
   187  			expectedPrefix: tcpip.AddressWithPrefix{
   188  				Address:   tcpip.AddrFrom16Slice([]byte(strings.Repeat("\x00", IPv6AddressSize))),
   189  				PrefixLen: 128,
   190  			}.Subnet(),
   191  		},
   192  		{
   193  			name:         "Length=3 with (invalid) Prefix Length > 128",
   194  			length:       3,
   195  			prefixLength: 129,
   196  			prf:          HighRoutePreference,
   197  			lifetimeS:    1,
   198  			prefixBytes:  nil,
   199  			expectedErr:  ErrNDPOptMalformedBody,
   200  		},
   201  	}
   202  
   203  	for _, test := range tests {
   204  		t.Run(test.name, func(t *testing.T) {
   205  			expectedRouteInformationBytes := [...]byte{
   206  				// Type, Length
   207  				24, test.length,
   208  
   209  				// Prefix Length, Prf
   210  				uint8(test.prefixLength), uint8(test.prf) << 3,
   211  
   212  				// Route Lifetime
   213  				0, 0, 0, 0,
   214  
   215  				0, 0, 0, 0,
   216  				0, 0, 0, 0,
   217  				0, 0, 0, 0,
   218  				0, 0, 0, 0,
   219  			}
   220  			binary.BigEndian.PutUint32(expectedRouteInformationBytes[4:], test.lifetimeS)
   221  			_ = copy(expectedRouteInformationBytes[8:], test.prefixBytes)
   222  
   223  			opts := NDPOptions(expectedRouteInformationBytes[:test.length*lengthByteUnits])
   224  			it, err := opts.Iter(false)
   225  			if err != nil {
   226  				t.Fatalf("got Iter(false) = (_, %s), want = (_, nil)", err)
   227  			}
   228  			opt, done, err := it.Next()
   229  			if !errors.Is(err, test.expectedErr) {
   230  				t.Fatalf("got Next() = (_, _, %s), want = (_, _, %s)", err, test.expectedErr)
   231  			}
   232  			if want := test.expectedErr != nil; done != want {
   233  				t.Fatalf("got Next() = (_, %t, _), want = (_, %t, _)", done, want)
   234  			}
   235  			if test.expectedErr != nil {
   236  				return
   237  			}
   238  
   239  			if got := opt.kind(); got != ndpRouteInformationType {
   240  				t.Errorf("got kind() = %d, want = %d", got, ndpRouteInformationType)
   241  			}
   242  
   243  			ri, ok := opt.(NDPRouteInformation)
   244  			if !ok {
   245  				t.Fatalf("got opt = %T, want = NDPRouteInformation", opt)
   246  			}
   247  
   248  			if got := ri.PrefixLength(); got != test.prefixLength {
   249  				t.Errorf("got PrefixLength() = %d, want = %d", got, test.prefixLength)
   250  			}
   251  			if got := ri.RoutePreference(); got != test.prf {
   252  				t.Errorf("got RoutePreference() = %d, want = %d", got, test.prf)
   253  			}
   254  			if got, want := ri.RouteLifetime(), time.Duration(test.lifetimeS)*time.Second; got != want {
   255  				t.Errorf("got RouteLifetime() = %s, want = %s", got, want)
   256  			}
   257  			if got, err := ri.Prefix(); err != nil {
   258  				t.Errorf("Prefix(): %s", err)
   259  			} else if got != test.expectedPrefix {
   260  				t.Errorf("got Prefix() = %s, want = %s", got, test.expectedPrefix)
   261  			}
   262  
   263  			// Iterator should not return anything else.
   264  			{
   265  				next, done, err := it.Next()
   266  				if err != nil {
   267  					t.Errorf("got Next() = (_, _, %s), want = (_, _, nil)", err)
   268  				}
   269  				if !done {
   270  					t.Error("got Next() = (_, false, _), want = (_, true, _)")
   271  				}
   272  				if next != nil {
   273  					t.Errorf("got Next() = (%x, _, _), want = (nil, _, _)", next)
   274  				}
   275  			}
   276  		})
   277  	}
   278  }
   279  
   280  // TestNDPNeighborAdvert tests the functions of NDPNeighborAdvert.
   281  func TestNDPNeighborAdvert(t *testing.T) {
   282  	b := []byte{
   283  		160, 0, 0, 0,
   284  		1, 2, 3, 4,
   285  		5, 6, 7, 8,
   286  		9, 10, 11, 12,
   287  		13, 14, 15, 16,
   288  	}
   289  
   290  	// Test getting the Target Address.
   291  	na := NDPNeighborAdvert(b)
   292  	addr := testutil.MustParse6("102:304:506:708:90a:b0c:d0e:f10")
   293  	if got := na.TargetAddress(); got != addr {
   294  		t.Errorf("got TargetAddress = %s, want %s", got, addr)
   295  	}
   296  
   297  	// Test getting the Router Flag.
   298  	if got := na.RouterFlag(); !got {
   299  		t.Errorf("got RouterFlag = false, want = true")
   300  	}
   301  
   302  	// Test getting the Solicited Flag.
   303  	if got := na.SolicitedFlag(); got {
   304  		t.Errorf("got SolicitedFlag = true, want = false")
   305  	}
   306  
   307  	// Test getting the Override Flag.
   308  	if got := na.OverrideFlag(); !got {
   309  		t.Errorf("got OverrideFlag = false, want = true")
   310  	}
   311  
   312  	// Test updating the Target Address.
   313  	addr2 := testutil.MustParse6("1112:1314:1516:1718:191a:1b1c:1d1e:1f11")
   314  	na.SetTargetAddress(addr2)
   315  	if got := na.TargetAddress(); got != addr2 {
   316  		t.Errorf("got TargetAddress = %s, want %s", got, addr2)
   317  	}
   318  	// Make sure the address got updated in the backing buffer.
   319  	if got := tcpip.AddrFrom16Slice(b[ndpNATargetAddressOffset:][:IPv6AddressSize]); got != addr2 {
   320  		t.Errorf("got targetaddress buffer = %s, want %s", got, addr2)
   321  	}
   322  
   323  	// Test updating the Router Flag.
   324  	na.SetRouterFlag(false)
   325  	if got := na.RouterFlag(); got {
   326  		t.Errorf("got RouterFlag = true, want = false")
   327  	}
   328  
   329  	// Test updating the Solicited Flag.
   330  	na.SetSolicitedFlag(true)
   331  	if got := na.SolicitedFlag(); !got {
   332  		t.Errorf("got SolicitedFlag = false, want = true")
   333  	}
   334  
   335  	// Test updating the Override Flag.
   336  	na.SetOverrideFlag(false)
   337  	if got := na.OverrideFlag(); got {
   338  		t.Errorf("got OverrideFlag = true, want = false")
   339  	}
   340  
   341  	// Make sure flags got updated in the backing buffer.
   342  	if got := b[ndpNAFlagsOffset]; got != 64 {
   343  		t.Errorf("got flags byte = %d, want = 64", got)
   344  	}
   345  }
   346  
   347  func TestNDPRouterAdvert(t *testing.T) {
   348  	tests := []struct {
   349  		hopLimit                        uint8
   350  		managedFlag, otherConfFlag      bool
   351  		prf                             NDPRoutePreference
   352  		routerLifetimeS                 uint16
   353  		reachableTimeMS, retransTimerMS uint32
   354  	}{
   355  		{
   356  			hopLimit:        1,
   357  			managedFlag:     false,
   358  			otherConfFlag:   true,
   359  			prf:             HighRoutePreference,
   360  			routerLifetimeS: 2,
   361  			reachableTimeMS: 3,
   362  			retransTimerMS:  4,
   363  		},
   364  		{
   365  			hopLimit:        64,
   366  			managedFlag:     true,
   367  			otherConfFlag:   false,
   368  			prf:             LowRoutePreference,
   369  			routerLifetimeS: 258,
   370  			reachableTimeMS: 78492,
   371  			retransTimerMS:  13213,
   372  		},
   373  	}
   374  
   375  	for i, test := range tests {
   376  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
   377  			flags := uint8(0)
   378  			if test.managedFlag {
   379  				flags |= 1 << 7
   380  			}
   381  			if test.otherConfFlag {
   382  				flags |= 1 << 6
   383  			}
   384  			flags |= uint8(test.prf) << 3
   385  
   386  			b := []byte{
   387  				test.hopLimit, flags, 1, 2,
   388  				3, 4, 5, 6,
   389  				7, 8, 9, 10,
   390  			}
   391  			binary.BigEndian.PutUint16(b[2:], test.routerLifetimeS)
   392  			binary.BigEndian.PutUint32(b[4:], test.reachableTimeMS)
   393  			binary.BigEndian.PutUint32(b[8:], test.retransTimerMS)
   394  
   395  			ra := NDPRouterAdvert(b)
   396  
   397  			if got := ra.CurrHopLimit(); got != test.hopLimit {
   398  				t.Errorf("got ra.CurrHopLimit() = %d, want = %d", got, test.hopLimit)
   399  			}
   400  
   401  			if got := ra.ManagedAddrConfFlag(); got != test.managedFlag {
   402  				t.Errorf("got ManagedAddrConfFlag() = %t, want = %t", got, test.managedFlag)
   403  			}
   404  
   405  			if got := ra.OtherConfFlag(); got != test.otherConfFlag {
   406  				t.Errorf("got OtherConfFlag() = %t, want = %t", got, test.otherConfFlag)
   407  			}
   408  
   409  			if got := ra.DefaultRouterPreference(); got != test.prf {
   410  				t.Errorf("got DefaultRouterPreference() = %d, want = %d", got, test.prf)
   411  			}
   412  
   413  			if got, want := ra.RouterLifetime(), time.Second*time.Duration(test.routerLifetimeS); got != want {
   414  				t.Errorf("got ra.RouterLifetime() = %d, want = %d", got, want)
   415  			}
   416  
   417  			if got, want := ra.ReachableTime(), time.Millisecond*time.Duration(test.reachableTimeMS); got != want {
   418  				t.Errorf("got ra.ReachableTime() = %d, want = %d", got, want)
   419  			}
   420  
   421  			if got, want := ra.RetransTimer(), time.Millisecond*time.Duration(test.retransTimerMS); got != want {
   422  				t.Errorf("got ra.RetransTimer() = %d, want = %d", got, want)
   423  			}
   424  		})
   425  	}
   426  }
   427  
   428  // TestNDPSourceLinkLayerAddressOptionEthernetAddress tests getting the
   429  // Ethernet address from an NDPSourceLinkLayerAddressOption.
   430  func TestNDPSourceLinkLayerAddressOptionEthernetAddress(t *testing.T) {
   431  	tests := []struct {
   432  		name     string
   433  		buf      []byte
   434  		expected tcpip.LinkAddress
   435  	}{
   436  		{
   437  			"ValidMAC",
   438  			[]byte{1, 2, 3, 4, 5, 6},
   439  			tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"),
   440  		},
   441  		{
   442  			"SLLBodyTooShort",
   443  			[]byte{1, 2, 3, 4, 5},
   444  			tcpip.LinkAddress([]byte(nil)),
   445  		},
   446  		{
   447  			"SLLBodyLargerThanNeeded",
   448  			[]byte{1, 2, 3, 4, 5, 6, 7, 8},
   449  			tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"),
   450  		},
   451  	}
   452  
   453  	for _, test := range tests {
   454  		t.Run(test.name, func(t *testing.T) {
   455  			sll := NDPSourceLinkLayerAddressOption(test.buf)
   456  			if got := sll.EthernetAddress(); got != test.expected {
   457  				t.Errorf("got sll.EthernetAddress = %s, want = %s", got, test.expected)
   458  			}
   459  		})
   460  	}
   461  }
   462  
   463  // TestNDPTargetLinkLayerAddressOptionEthernetAddress tests getting the
   464  // Ethernet address from an NDPTargetLinkLayerAddressOption.
   465  func TestNDPTargetLinkLayerAddressOptionEthernetAddress(t *testing.T) {
   466  	tests := []struct {
   467  		name     string
   468  		buf      []byte
   469  		expected tcpip.LinkAddress
   470  	}{
   471  		{
   472  			"ValidMAC",
   473  			[]byte{1, 2, 3, 4, 5, 6},
   474  			tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"),
   475  		},
   476  		{
   477  			"TLLBodyTooShort",
   478  			[]byte{1, 2, 3, 4, 5},
   479  			tcpip.LinkAddress([]byte(nil)),
   480  		},
   481  		{
   482  			"TLLBodyLargerThanNeeded",
   483  			[]byte{1, 2, 3, 4, 5, 6, 7, 8},
   484  			tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06"),
   485  		},
   486  	}
   487  
   488  	for _, test := range tests {
   489  		t.Run(test.name, func(t *testing.T) {
   490  			tll := NDPTargetLinkLayerAddressOption(test.buf)
   491  			if got := tll.EthernetAddress(); got != test.expected {
   492  				t.Errorf("got tll.EthernetAddress = %s, want = %s", got, test.expected)
   493  			}
   494  		})
   495  	}
   496  }
   497  
   498  func TestOpts(t *testing.T) {
   499  	const optionHeaderLen = 2
   500  
   501  	checkNonce := func(expectedNonce []byte) func(*testing.T, NDPOption) {
   502  		return func(t *testing.T, opt NDPOption) {
   503  			if got := opt.kind(); got != ndpNonceOptionType {
   504  				t.Errorf("got kind() = %d, want = %d", got, ndpNonceOptionType)
   505  			}
   506  			nonce, ok := opt.(NDPNonceOption)
   507  			if !ok {
   508  				t.Fatalf("got nonce = %T, want = NDPNonceOption", opt)
   509  			}
   510  			if diff := cmp.Diff(expectedNonce, nonce.Nonce()); diff != "" {
   511  				t.Errorf("nonce mismatch (-want +got):\n%s", diff)
   512  			}
   513  		}
   514  	}
   515  
   516  	checkTLL := func(expectedAddr tcpip.LinkAddress) func(*testing.T, NDPOption) {
   517  		return func(t *testing.T, opt NDPOption) {
   518  			if got := opt.kind(); got != ndpTargetLinkLayerAddressOptionType {
   519  				t.Errorf("got kind() = %d, want = %d", got, ndpTargetLinkLayerAddressOptionType)
   520  			}
   521  			tll, ok := opt.(NDPTargetLinkLayerAddressOption)
   522  			if !ok {
   523  				t.Fatalf("got tll = %T, want = NDPTargetLinkLayerAddressOption", opt)
   524  			}
   525  			if got, want := tll.EthernetAddress(), expectedAddr; got != want {
   526  				t.Errorf("got tll.EthernetAddress = %s, want = %s", got, want)
   527  			}
   528  		}
   529  	}
   530  
   531  	checkSLL := func(expectedAddr tcpip.LinkAddress) func(*testing.T, NDPOption) {
   532  		return func(t *testing.T, opt NDPOption) {
   533  			if got := opt.kind(); got != ndpSourceLinkLayerAddressOptionType {
   534  				t.Errorf("got kind() = %d, want = %d", got, ndpSourceLinkLayerAddressOptionType)
   535  			}
   536  			sll, ok := opt.(NDPSourceLinkLayerAddressOption)
   537  			if !ok {
   538  				t.Fatalf("got sll = %T, want = NDPSourceLinkLayerAddressOption", opt)
   539  			}
   540  			if got, want := sll.EthernetAddress(), expectedAddr; got != want {
   541  				t.Errorf("got sll.EthernetAddress = %s, want = %s", got, want)
   542  			}
   543  		}
   544  	}
   545  
   546  	const validLifetimeSeconds = 16909060
   547  	address := testutil.MustParse6("90a:b0c:d0e:f10:1112:1314:1516:1718")
   548  
   549  	expectedRDNSSBytes := [...]byte{
   550  		// Type, Length
   551  		25, 3,
   552  
   553  		// Reserved
   554  		0, 0,
   555  
   556  		// Lifetime
   557  		1, 2, 4, 8,
   558  
   559  		// Address
   560  		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
   561  	}
   562  	binary.BigEndian.PutUint32(expectedRDNSSBytes[4:], validLifetimeSeconds)
   563  	if n := copy(expectedRDNSSBytes[8:], address.AsSlice()); n != IPv6AddressSize {
   564  		t.Fatalf("got copy(...) = %d, want = %d", n, IPv6AddressSize)
   565  	}
   566  	// Update reserved fields to non zero values to make sure serializing sets
   567  	// them to zero.
   568  	rdnssBytes := expectedRDNSSBytes
   569  	rdnssBytes[1] = 1
   570  	rdnssBytes[2] = 2
   571  
   572  	const searchListPaddingBytes = 3
   573  	const domainName = "abc.abcd.e"
   574  	expectedSearchListBytes := [...]byte{
   575  		// Type, Length
   576  		31, 3,
   577  
   578  		// Reserved
   579  		0, 0,
   580  
   581  		// Lifetime
   582  		1, 0, 0, 0,
   583  
   584  		// Domain names
   585  		3, 'a', 'b', 'c',
   586  		4, 'a', 'b', 'c', 'd',
   587  		1, 'e',
   588  		0,
   589  		0, 0, 0, 0,
   590  	}
   591  	binary.BigEndian.PutUint32(expectedSearchListBytes[4:], validLifetimeSeconds)
   592  	// Update reserved fields to non zero values to make sure serializing sets
   593  	// them to zero.
   594  	searchListBytes := expectedSearchListBytes
   595  	searchListBytes[2] = 1
   596  	searchListBytes[3] = 2
   597  
   598  	const prefixLength = 43
   599  	const onLinkFlag = false
   600  	const slaacFlag = true
   601  	const preferredLifetimeSeconds = 84281096
   602  	const onLinkFlagBit = 7
   603  	const slaacFlagBit = 6
   604  	boolToByte := func(v bool) byte {
   605  		if v {
   606  			return 1
   607  		}
   608  		return 0
   609  	}
   610  	flags := boolToByte(onLinkFlag)<<onLinkFlagBit | boolToByte(slaacFlag)<<slaacFlagBit
   611  	expectedPrefixInformationBytes := [...]byte{
   612  		// Type, Length
   613  		3, 4,
   614  
   615  		prefixLength, flags,
   616  
   617  		// Valid Lifetime
   618  		1, 2, 3, 4,
   619  
   620  		// Preferred Lifetime
   621  		5, 6, 7, 8,
   622  
   623  		// Reserved2
   624  		0, 0, 0, 0,
   625  
   626  		// Address
   627  		9, 10, 11, 12,
   628  		13, 14, 15, 16,
   629  		17, 18, 19, 20,
   630  		21, 22, 23, 24,
   631  	}
   632  	binary.BigEndian.PutUint32(expectedPrefixInformationBytes[4:], validLifetimeSeconds)
   633  	binary.BigEndian.PutUint32(expectedPrefixInformationBytes[8:], preferredLifetimeSeconds)
   634  	if n := copy(expectedPrefixInformationBytes[16:], address.AsSlice()); n != IPv6AddressSize {
   635  		t.Fatalf("got copy(...) = %d, want = %d", n, IPv6AddressSize)
   636  	}
   637  	// Update reserved fields to non zero values to make sure serializing sets
   638  	// them to zero.
   639  	prefixInformationBytes := expectedPrefixInformationBytes
   640  	prefixInformationBytes[3] |= (1 << slaacFlagBit) - 1
   641  	binary.BigEndian.PutUint32(prefixInformationBytes[12:], validLifetimeSeconds+1)
   642  	tests := []struct {
   643  		name        string
   644  		buf         []byte
   645  		opt         NDPOption
   646  		expectedBuf []byte
   647  		check       func(*testing.T, NDPOption)
   648  	}{
   649  		{
   650  			name:        "Nonce",
   651  			buf:         make([]byte, 8),
   652  			opt:         NDPNonceOption([]byte{1, 2, 3, 4, 5, 6}),
   653  			expectedBuf: []byte{14, 1, 1, 2, 3, 4, 5, 6},
   654  			check:       checkNonce([]byte{1, 2, 3, 4, 5, 6}),
   655  		},
   656  		{
   657  			name:        "Nonce with padding",
   658  			buf:         []byte{1, 1, 1, 1, 1, 1, 1, 1},
   659  			opt:         NDPNonceOption([]byte{1, 2, 3, 4, 5}),
   660  			expectedBuf: []byte{14, 1, 1, 2, 3, 4, 5, 0},
   661  			check:       checkNonce([]byte{1, 2, 3, 4, 5, 0}),
   662  		},
   663  
   664  		{
   665  			name:        "TLL Ethernet",
   666  			buf:         make([]byte, 8),
   667  			opt:         NDPTargetLinkLayerAddressOption("\x01\x02\x03\x04\x05\x06"),
   668  			expectedBuf: []byte{2, 1, 1, 2, 3, 4, 5, 6},
   669  			check:       checkTLL("\x01\x02\x03\x04\x05\x06"),
   670  		},
   671  		{
   672  			name:        "TLL Padding",
   673  			buf:         []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
   674  			opt:         NDPTargetLinkLayerAddressOption("\x01\x02\x03\x04\x05\x06\x07\x08"),
   675  			expectedBuf: []byte{2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0},
   676  			check:       checkTLL("\x01\x02\x03\x04\x05\x06"),
   677  		},
   678  		{
   679  			name:        "TLL Empty",
   680  			buf:         nil,
   681  			opt:         NDPTargetLinkLayerAddressOption(""),
   682  			expectedBuf: nil,
   683  		},
   684  
   685  		{
   686  			name:        "SLL Ethernet",
   687  			buf:         make([]byte, 8),
   688  			opt:         NDPSourceLinkLayerAddressOption("\x01\x02\x03\x04\x05\x06"),
   689  			expectedBuf: []byte{1, 1, 1, 2, 3, 4, 5, 6},
   690  			check:       checkSLL("\x01\x02\x03\x04\x05\x06"),
   691  		},
   692  		{
   693  			name:        "SLL Padding",
   694  			buf:         []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
   695  			opt:         NDPSourceLinkLayerAddressOption("\x01\x02\x03\x04\x05\x06\x07\x08"),
   696  			expectedBuf: []byte{1, 2, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0},
   697  			check:       checkSLL("\x01\x02\x03\x04\x05\x06"),
   698  		},
   699  		{
   700  			name:        "SLL Empty",
   701  			buf:         nil,
   702  			opt:         NDPSourceLinkLayerAddressOption(""),
   703  			expectedBuf: nil,
   704  		},
   705  
   706  		{
   707  			name: "RDNSS",
   708  			buf:  []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
   709  			// NDPRecursiveDNSServer holds the option after the header bytes.
   710  			opt:         NDPRecursiveDNSServer(rdnssBytes[optionHeaderLen:]),
   711  			expectedBuf: expectedRDNSSBytes[:],
   712  			check: func(t *testing.T, opt NDPOption) {
   713  				if got := opt.kind(); got != ndpRecursiveDNSServerOptionType {
   714  					t.Errorf("got kind() = %d, want = %d", got, ndpRecursiveDNSServerOptionType)
   715  				}
   716  				rdnss, ok := opt.(NDPRecursiveDNSServer)
   717  				if !ok {
   718  					t.Fatalf("got opt = %T, want = NDPRecursiveDNSServer", opt)
   719  				}
   720  				if got, want := rdnss.length(), len(expectedRDNSSBytes[optionHeaderLen:]); got != want {
   721  					t.Errorf("got length() = %d, want = %d", got, want)
   722  				}
   723  				if got, want := rdnss.Lifetime(), validLifetimeSeconds*time.Second; got != want {
   724  					t.Errorf("got Lifetime() = %s, want = %s", got, want)
   725  				}
   726  				if addrs, err := rdnss.Addresses(); err != nil {
   727  					t.Errorf("Addresses(): %s", err)
   728  				} else if diff := cmp.Diff([]tcpip.Address{address}, addrs); diff != "" {
   729  					t.Errorf("mismatched addresses (-want +got):\n%s", diff)
   730  				}
   731  			},
   732  		},
   733  
   734  		{
   735  			name:        "Search list",
   736  			buf:         []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
   737  			opt:         NDPDNSSearchList(searchListBytes[optionHeaderLen:]),
   738  			expectedBuf: expectedSearchListBytes[:],
   739  			check: func(t *testing.T, opt NDPOption) {
   740  				if got := opt.kind(); got != ndpDNSSearchListOptionType {
   741  					t.Errorf("got kind() = %d, want = %d", got, ndpDNSSearchListOptionType)
   742  				}
   743  
   744  				dnssl, ok := opt.(NDPDNSSearchList)
   745  				if !ok {
   746  					t.Fatalf("got opt = %T, want = NDPDNSSearchList", opt)
   747  				}
   748  				if got, want := dnssl.length(), len(expectedRDNSSBytes[optionHeaderLen:]); got != want {
   749  					t.Errorf("got length() = %d, want = %d", got, want)
   750  				}
   751  				if got, want := dnssl.Lifetime(), validLifetimeSeconds*time.Second; got != want {
   752  					t.Errorf("got Lifetime() = %s, want = %s", got, want)
   753  				}
   754  
   755  				if domainNames, err := dnssl.DomainNames(); err != nil {
   756  					t.Errorf("DomainNames(): %s", err)
   757  				} else if diff := cmp.Diff([]string{domainName}, domainNames); diff != "" {
   758  					t.Errorf("domain names mismatch (-want +got):\n%s", diff)
   759  				}
   760  			},
   761  		},
   762  
   763  		{
   764  			name: "Prefix Information",
   765  			buf:  []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
   766  			// NDPPrefixInformation holds the option after the header bytes.
   767  			opt:         NDPPrefixInformation(prefixInformationBytes[optionHeaderLen:]),
   768  			expectedBuf: expectedPrefixInformationBytes[:],
   769  			check: func(t *testing.T, opt NDPOption) {
   770  				if got := opt.kind(); got != ndpPrefixInformationType {
   771  					t.Errorf("got kind() = %d, want = %d", got, ndpPrefixInformationType)
   772  				}
   773  
   774  				pi, ok := opt.(NDPPrefixInformation)
   775  				if !ok {
   776  					t.Fatalf("got opt = %T, want = NDPPrefixInformation", opt)
   777  				}
   778  
   779  				if got, want := pi.length(), len(expectedPrefixInformationBytes[optionHeaderLen:]); got != want {
   780  					t.Errorf("got length() = %d, want = %d", got, want)
   781  				}
   782  				if got := pi.PrefixLength(); got != prefixLength {
   783  					t.Errorf("got PrefixLength() = %d, want = %d", got, prefixLength)
   784  				}
   785  				if got := pi.OnLinkFlag(); got != onLinkFlag {
   786  					t.Errorf("got OnLinkFlag() = %t, want = %t", got, onLinkFlag)
   787  				}
   788  				if got := pi.AutonomousAddressConfigurationFlag(); got != slaacFlag {
   789  					t.Errorf("got AutonomousAddressConfigurationFlag() = %t, want = %t", got, slaacFlag)
   790  				}
   791  				if got, want := pi.ValidLifetime(), validLifetimeSeconds*time.Second; got != want {
   792  					t.Errorf("got ValidLifetime() = %s, want = %s", got, want)
   793  				}
   794  				if got, want := pi.PreferredLifetime(), preferredLifetimeSeconds*time.Second; got != want {
   795  					t.Errorf("got PreferredLifetime() = %s, want = %s", got, want)
   796  				}
   797  				if got := pi.Prefix(); got != address {
   798  					t.Errorf("got Prefix() = %s, want = %s", got, address)
   799  				}
   800  			},
   801  		},
   802  	}
   803  
   804  	for _, test := range tests {
   805  		t.Run(test.name, func(t *testing.T) {
   806  			opts := NDPOptions(test.buf)
   807  			serializer := NDPOptionsSerializer{
   808  				test.opt,
   809  			}
   810  			if got, want := int(serializer.Length()), len(test.expectedBuf); got != want {
   811  				t.Fatalf("got Length() = %d, want = %d", got, want)
   812  			}
   813  			opts.Serialize(serializer)
   814  			if diff := cmp.Diff(test.expectedBuf, test.buf); diff != "" {
   815  				t.Fatalf("serialized buffer mismatch (-want +got):\n%s", diff)
   816  			}
   817  
   818  			it, err := opts.Iter(true)
   819  			if err != nil {
   820  				t.Fatalf("got Iter(true) = (_, %s), want = (_, nil)", err)
   821  			}
   822  
   823  			if len(test.expectedBuf) > 0 {
   824  				next, done, err := it.Next()
   825  				if err != nil {
   826  					t.Fatalf("got Next() = (_, _, %s), want = (_, _, nil)", err)
   827  				}
   828  				if done {
   829  					t.Fatal("got Next() = (_, true, _), want = (_, false, _)")
   830  				}
   831  				test.check(t, next)
   832  			}
   833  
   834  			// Iterator should not return anything else.
   835  			next, done, err := it.Next()
   836  			if err != nil {
   837  				t.Errorf("got Next() = (_, _, %s), want = (_, _, nil)", err)
   838  			}
   839  			if !done {
   840  				t.Error("got Next() = (_, false, _), want = (_, true, _)")
   841  			}
   842  			if next != nil {
   843  				t.Errorf("got Next() = (%x, _, _), want = (nil, _, _)", next)
   844  			}
   845  		})
   846  	}
   847  }
   848  
   849  func TestNDPRecursiveDNSServerOption(t *testing.T) {
   850  	tests := []struct {
   851  		name     string
   852  		buf      []byte
   853  		lifetime time.Duration
   854  		addrs    []tcpip.Address
   855  	}{
   856  		{
   857  			"Valid1Addr",
   858  			[]byte{
   859  				25, 3, 0, 0,
   860  				0, 0, 0, 0,
   861  				0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
   862  			},
   863  			0,
   864  			[]tcpip.Address{
   865  				tcpip.AddrFrom16Slice([]byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f")),
   866  			},
   867  		},
   868  		{
   869  			"Valid2Addr",
   870  			[]byte{
   871  				25, 5, 0, 0,
   872  				0, 0, 0, 0,
   873  				0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
   874  				17, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16,
   875  			},
   876  			0,
   877  			[]tcpip.Address{
   878  				tcpip.AddrFrom16Slice([]byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f")),
   879  				tcpip.AddrFrom16Slice([]byte("\x11\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x10")),
   880  			},
   881  		},
   882  		{
   883  			"Valid3Addr",
   884  			[]byte{
   885  				25, 7, 0, 0,
   886  				0, 0, 0, 0,
   887  				0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
   888  				17, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16,
   889  				17, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17,
   890  			},
   891  			0,
   892  			[]tcpip.Address{
   893  				tcpip.AddrFrom16Slice([]byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f")),
   894  				tcpip.AddrFrom16Slice([]byte("\x11\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x10")),
   895  				tcpip.AddrFrom16Slice([]byte("\x11\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x11")),
   896  			},
   897  		},
   898  	}
   899  
   900  	for _, test := range tests {
   901  		t.Run(test.name, func(t *testing.T) {
   902  			opts := NDPOptions(test.buf)
   903  			it, err := opts.Iter(true)
   904  			if err != nil {
   905  				t.Fatalf("got Iter = (_, %s), want = (_, nil)", err)
   906  			}
   907  
   908  			// Iterator should get our option.
   909  			next, done, err := it.Next()
   910  			if err != nil {
   911  				t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err)
   912  			}
   913  			if done {
   914  				t.Fatal("got Next = (_, true, _), want = (_, false, _)")
   915  			}
   916  			if got := next.kind(); got != ndpRecursiveDNSServerOptionType {
   917  				t.Fatalf("got Type = %d, want = %d", got, ndpRecursiveDNSServerOptionType)
   918  			}
   919  
   920  			comparer := cmp.Comparer(func(addrA, addrB tcpip.Address) bool {
   921  				return addrA == addrB
   922  			})
   923  
   924  			opt, ok := next.(NDPRecursiveDNSServer)
   925  			if !ok {
   926  				t.Fatalf("next (type = %T) cannot be casted to an NDPRecursiveDNSServer", next)
   927  			}
   928  			if got := opt.Lifetime(); got != test.lifetime {
   929  				t.Errorf("got Lifetime = %d, want = %d", got, test.lifetime)
   930  			}
   931  			addrs, err := opt.Addresses()
   932  			if err != nil {
   933  				t.Errorf("opt.Addresses() = %s", err)
   934  			}
   935  			if diff := cmp.Diff(addrs, test.addrs, comparer); diff != "" {
   936  				t.Errorf("mismatched addresses (-want +got):\n%s", diff)
   937  			}
   938  
   939  			// Iterator should not return anything else.
   940  			next, done, err = it.Next()
   941  			if err != nil {
   942  				t.Errorf("got Next = (_, _, %s), want = (_, _, nil)", err)
   943  			}
   944  			if !done {
   945  				t.Error("got Next = (_, false, _), want = (_, true, _)")
   946  			}
   947  			if next != nil {
   948  				t.Errorf("got Next = (%x, _, _), want = (nil, _, _)", next)
   949  			}
   950  		})
   951  	}
   952  }
   953  
   954  // TestNDPDNSSearchListOption tests the getters of NDPDNSSearchList.
   955  func TestNDPDNSSearchListOption(t *testing.T) {
   956  	tests := []struct {
   957  		name        string
   958  		buf         []byte
   959  		lifetime    time.Duration
   960  		domainNames []string
   961  		err         error
   962  	}{
   963  		{
   964  			name: "Valid1Label",
   965  			buf: []byte{
   966  				0, 0,
   967  				0, 0, 0, 1,
   968  				3, 'a', 'b', 'c',
   969  				0,
   970  				0, 0, 0,
   971  			},
   972  			lifetime: time.Second,
   973  			domainNames: []string{
   974  				"abc",
   975  			},
   976  			err: nil,
   977  		},
   978  		{
   979  			name: "Valid2Label",
   980  			buf: []byte{
   981  				0, 0,
   982  				0, 0, 0, 5,
   983  				3, 'a', 'b', 'c',
   984  				4, 'a', 'b', 'c', 'd',
   985  				0,
   986  				0, 0, 0, 0, 0, 0,
   987  			},
   988  			lifetime: 5 * time.Second,
   989  			domainNames: []string{
   990  				"abc.abcd",
   991  			},
   992  			err: nil,
   993  		},
   994  		{
   995  			name: "Valid3Label",
   996  			buf: []byte{
   997  				0, 0,
   998  				1, 0, 0, 0,
   999  				3, 'a', 'b', 'c',
  1000  				4, 'a', 'b', 'c', 'd',
  1001  				1, 'e',
  1002  				0,
  1003  				0, 0, 0, 0,
  1004  			},
  1005  			lifetime: 16777216 * time.Second,
  1006  			domainNames: []string{
  1007  				"abc.abcd.e",
  1008  			},
  1009  			err: nil,
  1010  		},
  1011  		{
  1012  			name: "Valid2Domains",
  1013  			buf: []byte{
  1014  				0, 0,
  1015  				1, 2, 3, 4,
  1016  				3, 'a', 'b', 'c',
  1017  				0,
  1018  				2, 'd', 'e',
  1019  				3, 'x', 'y', 'z',
  1020  				0,
  1021  				0, 0, 0,
  1022  			},
  1023  			lifetime: 16909060 * time.Second,
  1024  			domainNames: []string{
  1025  				"abc",
  1026  				"de.xyz",
  1027  			},
  1028  			err: nil,
  1029  		},
  1030  		{
  1031  			name: "Valid3DomainsMixedCase",
  1032  			buf: []byte{
  1033  				0, 0,
  1034  				0, 0, 0, 0,
  1035  				3, 'a', 'B', 'c',
  1036  				0,
  1037  				2, 'd', 'E',
  1038  				3, 'X', 'y', 'z',
  1039  				0,
  1040  				1, 'J',
  1041  				0,
  1042  			},
  1043  			lifetime: 0,
  1044  			domainNames: []string{
  1045  				"abc",
  1046  				"de.xyz",
  1047  				"j",
  1048  			},
  1049  			err: nil,
  1050  		},
  1051  		{
  1052  			name: "ValidDomainAfterNULL",
  1053  			buf: []byte{
  1054  				0, 0,
  1055  				0, 0, 0, 0,
  1056  				3, 'a', 'B', 'c',
  1057  				0, 0, 0, 0,
  1058  				2, 'd', 'E',
  1059  				3, 'X', 'y', 'z',
  1060  				0,
  1061  			},
  1062  			lifetime: 0,
  1063  			domainNames: []string{
  1064  				"abc",
  1065  				"de.xyz",
  1066  			},
  1067  			err: nil,
  1068  		},
  1069  		{
  1070  			name: "Valid0Domains",
  1071  			buf: []byte{
  1072  				0, 0,
  1073  				0, 0, 0, 0,
  1074  				0,
  1075  				0, 0, 0, 0, 0, 0, 0,
  1076  			},
  1077  			lifetime:    0,
  1078  			domainNames: nil,
  1079  			err:         nil,
  1080  		},
  1081  		{
  1082  			name: "NoTrailingNull",
  1083  			buf: []byte{
  1084  				0, 0,
  1085  				0, 0, 0, 0,
  1086  				7, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
  1087  			},
  1088  			lifetime:    0,
  1089  			domainNames: nil,
  1090  			err:         io.ErrUnexpectedEOF,
  1091  		},
  1092  		{
  1093  			name: "IncorrectLength",
  1094  			buf: []byte{
  1095  				0, 0,
  1096  				0, 0, 0, 0,
  1097  				8, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
  1098  			},
  1099  			lifetime:    0,
  1100  			domainNames: nil,
  1101  			err:         io.ErrUnexpectedEOF,
  1102  		},
  1103  		{
  1104  			name: "IncorrectLengthWithNULL",
  1105  			buf: []byte{
  1106  				0, 0,
  1107  				0, 0, 0, 0,
  1108  				7, 'a', 'b', 'c', 'd', 'e', 'f',
  1109  				0,
  1110  			},
  1111  			lifetime:    0,
  1112  			domainNames: nil,
  1113  			err:         ErrNDPOptMalformedBody,
  1114  		},
  1115  		{
  1116  			name: "LabelOfLength63",
  1117  			buf: []byte{
  1118  				0, 0,
  1119  				0, 0, 0, 0,
  1120  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1121  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1122  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1123  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1124  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1125  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1126  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1127  				'i', 'j', 'k',
  1128  				0,
  1129  			},
  1130  			lifetime: 0,
  1131  			domainNames: []string{
  1132  				"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk",
  1133  			},
  1134  			err: nil,
  1135  		},
  1136  		{
  1137  			name: "LabelOfLength64",
  1138  			buf: []byte{
  1139  				0, 0,
  1140  				0, 0, 0, 0,
  1141  				64, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1142  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1143  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1144  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1145  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1146  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1147  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1148  				'i', 'j', 'k', 'l',
  1149  				0,
  1150  			},
  1151  			lifetime:    0,
  1152  			domainNames: nil,
  1153  			err:         ErrNDPOptMalformedBody,
  1154  		},
  1155  		{
  1156  			name: "DomainNameOfLength255",
  1157  			buf: []byte{
  1158  				0, 0,
  1159  				0, 0, 0, 0,
  1160  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1161  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1162  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1163  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1164  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1165  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1166  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1167  				'i', 'j', 'k',
  1168  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1169  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1170  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1171  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1172  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1173  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1174  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1175  				'i', 'j', 'k',
  1176  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1177  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1178  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1179  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1180  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1181  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1182  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1183  				'i', 'j', 'k',
  1184  				62, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1185  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1186  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1187  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1188  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1189  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1190  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1191  				'i', 'j',
  1192  				0,
  1193  			},
  1194  			lifetime: 0,
  1195  			domainNames: []string{
  1196  				"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij",
  1197  			},
  1198  			err: nil,
  1199  		},
  1200  		{
  1201  			name: "DomainNameOfLength256",
  1202  			buf: []byte{
  1203  				0, 0,
  1204  				0, 0, 0, 0,
  1205  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1206  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1207  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1208  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1209  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1210  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1211  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1212  				'i', 'j', 'k',
  1213  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1214  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1215  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1216  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1217  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1218  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1219  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1220  				'i', 'j', 'k',
  1221  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1222  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1223  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1224  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1225  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1226  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1227  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1228  				'i', 'j', 'k',
  1229  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1230  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1231  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1232  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1233  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1234  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1235  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1236  				'i', 'j', 'k',
  1237  				0,
  1238  			},
  1239  			lifetime:    0,
  1240  			domainNames: nil,
  1241  			err:         ErrNDPOptMalformedBody,
  1242  		},
  1243  		{
  1244  			name: "StartingDigitForLabel",
  1245  			buf: []byte{
  1246  				0, 0,
  1247  				0, 0, 0, 1,
  1248  				3, '9', 'b', 'c',
  1249  				0,
  1250  				0, 0, 0,
  1251  			},
  1252  			lifetime:    time.Second,
  1253  			domainNames: nil,
  1254  			err:         ErrNDPOptMalformedBody,
  1255  		},
  1256  		{
  1257  			name: "StartingHyphenForLabel",
  1258  			buf: []byte{
  1259  				0, 0,
  1260  				0, 0, 0, 1,
  1261  				3, '-', 'b', 'c',
  1262  				0,
  1263  				0, 0, 0,
  1264  			},
  1265  			lifetime:    time.Second,
  1266  			domainNames: nil,
  1267  			err:         ErrNDPOptMalformedBody,
  1268  		},
  1269  		{
  1270  			name: "EndingHyphenForLabel",
  1271  			buf: []byte{
  1272  				0, 0,
  1273  				0, 0, 0, 1,
  1274  				3, 'a', 'b', '-',
  1275  				0,
  1276  				0, 0, 0,
  1277  			},
  1278  			lifetime:    time.Second,
  1279  			domainNames: nil,
  1280  			err:         ErrNDPOptMalformedBody,
  1281  		},
  1282  		{
  1283  			name: "EndingDigitForLabel",
  1284  			buf: []byte{
  1285  				0, 0,
  1286  				0, 0, 0, 1,
  1287  				3, 'a', 'b', '9',
  1288  				0,
  1289  				0, 0, 0,
  1290  			},
  1291  			lifetime: time.Second,
  1292  			domainNames: []string{
  1293  				"ab9",
  1294  			},
  1295  			err: nil,
  1296  		},
  1297  	}
  1298  
  1299  	for _, test := range tests {
  1300  		t.Run(test.name, func(t *testing.T) {
  1301  			opt := NDPDNSSearchList(test.buf)
  1302  
  1303  			if got := opt.Lifetime(); got != test.lifetime {
  1304  				t.Errorf("got Lifetime = %d, want = %d", got, test.lifetime)
  1305  			}
  1306  			domainNames, err := opt.DomainNames()
  1307  			if !errors.Is(err, test.err) {
  1308  				t.Errorf("opt.DomainNames() = %s", err)
  1309  			}
  1310  			if diff := cmp.Diff(domainNames, test.domainNames); diff != "" {
  1311  				t.Errorf("mismatched domain names (-want +got):\n%s", diff)
  1312  			}
  1313  		})
  1314  	}
  1315  }
  1316  
  1317  func TestNDPSearchListOptionDomainNameLabelInvalidSymbols(t *testing.T) {
  1318  	for r := rune(0); r <= 255; r++ {
  1319  		t.Run(fmt.Sprintf("RuneVal=%d", r), func(t *testing.T) {
  1320  			buf := []byte{
  1321  				0, 0,
  1322  				0, 0, 0, 0,
  1323  				3, 'a', 0 /* will be replaced */, 'c',
  1324  				0,
  1325  				0, 0, 0,
  1326  			}
  1327  			buf[8] = uint8(r)
  1328  			opt := NDPDNSSearchList(buf)
  1329  
  1330  			// As per RFC 1035 section 2.3.1, the label must only include ASCII
  1331  			// letters, digits and hyphens (a-z, A-Z, 0-9, -).
  1332  			var expectedErr error
  1333  			re := regexp.MustCompile(`[a-zA-Z0-9-]`)
  1334  			if !re.Match([]byte{byte(r)}) {
  1335  				expectedErr = ErrNDPOptMalformedBody
  1336  			}
  1337  
  1338  			if domainNames, err := opt.DomainNames(); !errors.Is(err, expectedErr) {
  1339  				t.Errorf("got opt.DomainNames() = (%s, %v), want = (_, %v)", domainNames, err, ErrNDPOptMalformedBody)
  1340  			}
  1341  		})
  1342  	}
  1343  }
  1344  
  1345  // TestNDPOptionsIterCheck tests that Iter will return false if the NDPOptions
  1346  // the iterator was returned for is malformed.
  1347  func TestNDPOptionsIterCheck(t *testing.T) {
  1348  	tests := []struct {
  1349  		name        string
  1350  		buf         []byte
  1351  		expectedErr error
  1352  	}{
  1353  		{
  1354  			name:        "ZeroLengthField",
  1355  			buf:         []byte{0, 0, 0, 0, 0, 0, 0, 0},
  1356  			expectedErr: ErrNDPOptMalformedHeader,
  1357  		},
  1358  		{
  1359  			name:        "ValidSourceLinkLayerAddressOption",
  1360  			buf:         []byte{1, 1, 1, 2, 3, 4, 5, 6},
  1361  			expectedErr: nil,
  1362  		},
  1363  		{
  1364  			name:        "TooSmallSourceLinkLayerAddressOption",
  1365  			buf:         []byte{1, 1, 1, 2, 3, 4, 5},
  1366  			expectedErr: io.ErrUnexpectedEOF,
  1367  		},
  1368  		{
  1369  			name:        "ValidTargetLinkLayerAddressOption",
  1370  			buf:         []byte{2, 1, 1, 2, 3, 4, 5, 6},
  1371  			expectedErr: nil,
  1372  		},
  1373  		{
  1374  			name:        "TooSmallTargetLinkLayerAddressOption",
  1375  			buf:         []byte{2, 1, 1, 2, 3, 4, 5},
  1376  			expectedErr: io.ErrUnexpectedEOF,
  1377  		},
  1378  		{
  1379  			name: "ValidPrefixInformation",
  1380  			buf: []byte{
  1381  				3, 4, 43, 64,
  1382  				1, 2, 3, 4,
  1383  				5, 6, 7, 8,
  1384  				0, 0, 0, 0,
  1385  				9, 10, 11, 12,
  1386  				13, 14, 15, 16,
  1387  				17, 18, 19, 20,
  1388  				21, 22, 23, 24,
  1389  			},
  1390  			expectedErr: nil,
  1391  		},
  1392  		{
  1393  			name: "TooSmallPrefixInformation",
  1394  			buf: []byte{
  1395  				3, 4, 43, 64,
  1396  				1, 2, 3, 4,
  1397  				5, 6, 7, 8,
  1398  				0, 0, 0, 0,
  1399  				9, 10, 11, 12,
  1400  				13, 14, 15, 16,
  1401  				17, 18, 19, 20,
  1402  				21, 22, 23,
  1403  			},
  1404  			expectedErr: io.ErrUnexpectedEOF,
  1405  		},
  1406  		{
  1407  			name: "InvalidPrefixInformationLength",
  1408  			buf: []byte{
  1409  				3, 3, 43, 64,
  1410  				1, 2, 3, 4,
  1411  				5, 6, 7, 8,
  1412  				0, 0, 0, 0,
  1413  				9, 10, 11, 12,
  1414  				13, 14, 15, 16,
  1415  			},
  1416  			expectedErr: ErrNDPOptMalformedBody,
  1417  		},
  1418  		{
  1419  			name: "ValidSourceAndTargetLinkLayerAddressWithPrefixInformation",
  1420  			buf: []byte{
  1421  				// Source Link-Layer Address.
  1422  				1, 1, 1, 2, 3, 4, 5, 6,
  1423  
  1424  				// Target Link-Layer Address.
  1425  				2, 1, 7, 8, 9, 10, 11, 12,
  1426  
  1427  				// Prefix information.
  1428  				3, 4, 43, 64,
  1429  				1, 2, 3, 4,
  1430  				5, 6, 7, 8,
  1431  				0, 0, 0, 0,
  1432  				9, 10, 11, 12,
  1433  				13, 14, 15, 16,
  1434  				17, 18, 19, 20,
  1435  				21, 22, 23, 24,
  1436  			},
  1437  			expectedErr: nil,
  1438  		},
  1439  		{
  1440  			name: "ValidSourceAndTargetLinkLayerAddressWithPrefixInformationWithUnrecognized",
  1441  			buf: []byte{
  1442  				// Source Link-Layer Address.
  1443  				1, 1, 1, 2, 3, 4, 5, 6,
  1444  
  1445  				// Target Link-Layer Address.
  1446  				2, 1, 7, 8, 9, 10, 11, 12,
  1447  
  1448  				// 255 is an unrecognized type. If 255 ends up
  1449  				// being the type for some recognized type,
  1450  				// update 255 to some other unrecognized value.
  1451  				255, 2, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8,
  1452  
  1453  				// Prefix information.
  1454  				3, 4, 43, 64,
  1455  				1, 2, 3, 4,
  1456  				5, 6, 7, 8,
  1457  				0, 0, 0, 0,
  1458  				9, 10, 11, 12,
  1459  				13, 14, 15, 16,
  1460  				17, 18, 19, 20,
  1461  				21, 22, 23, 24,
  1462  			},
  1463  			expectedErr: nil,
  1464  		},
  1465  		{
  1466  			name: "InvalidRecursiveDNSServerCutsOffAddress",
  1467  			buf: []byte{
  1468  				25, 4, 0, 0,
  1469  				0, 0, 0, 0,
  1470  				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
  1471  				0, 1, 2, 3, 4, 5, 6, 7,
  1472  			},
  1473  			expectedErr: ErrNDPOptMalformedBody,
  1474  		},
  1475  		{
  1476  			name: "InvalidRecursiveDNSServerInvalidLengthField",
  1477  			buf: []byte{
  1478  				25, 2, 0, 0,
  1479  				0, 0, 0, 0,
  1480  				0, 1, 2, 3, 4, 5, 6, 7, 8,
  1481  			},
  1482  			expectedErr: io.ErrUnexpectedEOF,
  1483  		},
  1484  		{
  1485  			name: "RecursiveDNSServerTooSmall",
  1486  			buf: []byte{
  1487  				25, 1, 0, 0,
  1488  				0, 0, 0,
  1489  			},
  1490  			expectedErr: io.ErrUnexpectedEOF,
  1491  		},
  1492  		{
  1493  			name: "RecursiveDNSServerMulticast",
  1494  			buf: []byte{
  1495  				25, 3, 0, 0,
  1496  				0, 0, 0, 0,
  1497  				255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
  1498  			},
  1499  			expectedErr: ErrNDPOptMalformedBody,
  1500  		},
  1501  		{
  1502  			name: "RecursiveDNSServerUnspecified",
  1503  			buf: []byte{
  1504  				25, 3, 0, 0,
  1505  				0, 0, 0, 0,
  1506  				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1507  			},
  1508  			expectedErr: ErrNDPOptMalformedBody,
  1509  		},
  1510  		{
  1511  			name: "DNSSearchListLargeCompliantRFC1035",
  1512  			buf: []byte{
  1513  				31, 33, 0, 0,
  1514  				0, 0, 0, 0,
  1515  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1516  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1517  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1518  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1519  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1520  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1521  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1522  				'i', 'j', 'k',
  1523  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1524  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1525  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1526  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1527  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1528  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1529  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1530  				'i', 'j', 'k',
  1531  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1532  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1533  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1534  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1535  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1536  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1537  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1538  				'i', 'j', 'k',
  1539  				62, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1540  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1541  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1542  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1543  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1544  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1545  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1546  				'i', 'j',
  1547  				0,
  1548  			},
  1549  			expectedErr: nil,
  1550  		},
  1551  		{
  1552  			name: "DNSSearchListNonCompliantRFC1035",
  1553  			buf: []byte{
  1554  				31, 33, 0, 0,
  1555  				0, 0, 0, 0,
  1556  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1557  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1558  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1559  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1560  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1561  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1562  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1563  				'i', 'j', 'k',
  1564  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1565  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1566  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1567  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1568  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1569  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1570  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1571  				'i', 'j', 'k',
  1572  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1573  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1574  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1575  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1576  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1577  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1578  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1579  				'i', 'j', 'k',
  1580  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1581  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1582  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1583  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1584  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1585  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1586  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1587  				'i', 'j', 'k',
  1588  				0,
  1589  				0, 0, 0, 0, 0, 0, 0, 0,
  1590  			},
  1591  			expectedErr: ErrNDPOptMalformedBody,
  1592  		},
  1593  		{
  1594  			name: "DNSSearchListValidSmall",
  1595  			buf: []byte{
  1596  				31, 2, 0, 0,
  1597  				0, 0, 0, 0,
  1598  				6, 'a', 'b', 'c', 'd', 'e', 'f',
  1599  				0,
  1600  			},
  1601  			expectedErr: nil,
  1602  		},
  1603  		{
  1604  			name: "DNSSearchListTooSmall",
  1605  			buf: []byte{
  1606  				31, 1, 0, 0,
  1607  				0, 0, 0,
  1608  			},
  1609  			expectedErr: io.ErrUnexpectedEOF,
  1610  		},
  1611  	}
  1612  
  1613  	for _, test := range tests {
  1614  		t.Run(test.name, func(t *testing.T) {
  1615  			opts := NDPOptions(test.buf)
  1616  
  1617  			if _, err := opts.Iter(true); !errors.Is(err, test.expectedErr) {
  1618  				t.Fatalf("got Iter(true) = (_, %v), want = (_, %v)", err, test.expectedErr)
  1619  			}
  1620  
  1621  			// test.buf may be malformed but we chose not to check
  1622  			// the iterator so it must return true.
  1623  			if _, err := opts.Iter(false); err != nil {
  1624  				t.Fatalf("got Iter(false) = (_, %s), want = (_, nil)", err)
  1625  			}
  1626  		})
  1627  	}
  1628  }
  1629  
  1630  // TestNDPOptionsIter tests that we can iterator over a valid NDPOptions. Note,
  1631  // this test does not actually check any of the option's getters, it simply
  1632  // checks the option Type and Body. We have other tests that tests the option
  1633  // field gettings given an option body and don't need to duplicate those tests
  1634  // here.
  1635  func TestNDPOptionsIter(t *testing.T) {
  1636  	buf := []byte{
  1637  		// Source Link-Layer Address.
  1638  		1, 1, 1, 2, 3, 4, 5, 6,
  1639  
  1640  		// Target Link-Layer Address.
  1641  		2, 1, 7, 8, 9, 10, 11, 12,
  1642  
  1643  		// 255 is an unrecognized type. If 255 ends up being the type
  1644  		// for some recognized type, update 255 to some other
  1645  		// unrecognized value. Note, this option should be skipped when
  1646  		// iterating.
  1647  		255, 2, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8,
  1648  
  1649  		// Prefix information.
  1650  		3, 4, 43, 64,
  1651  		1, 2, 3, 4,
  1652  		5, 6, 7, 8,
  1653  		0, 0, 0, 0,
  1654  		9, 10, 11, 12,
  1655  		13, 14, 15, 16,
  1656  		17, 18, 19, 20,
  1657  		21, 22, 23, 24,
  1658  	}
  1659  
  1660  	opts := NDPOptions(buf)
  1661  	it, err := opts.Iter(true)
  1662  	if err != nil {
  1663  		t.Fatalf("got Iter = (_, %s), want = (_, nil)", err)
  1664  	}
  1665  
  1666  	// Test the first (Source Link-Layer) option.
  1667  	next, done, err := it.Next()
  1668  	if err != nil {
  1669  		t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err)
  1670  	}
  1671  	if done {
  1672  		t.Fatal("got Next = (_, true, _), want = (_, false, _)")
  1673  	}
  1674  	if got, want := []byte(next.(NDPSourceLinkLayerAddressOption)), buf[2:][:6]; !bytes.Equal(got, want) {
  1675  		t.Errorf("got Next = (%x, _, _), want = (%x, _, _)", got, want)
  1676  	}
  1677  	if got := next.kind(); got != ndpSourceLinkLayerAddressOptionType {
  1678  		t.Errorf("got Type = %d, want = %d", got, ndpSourceLinkLayerAddressOptionType)
  1679  	}
  1680  
  1681  	// Test the next (Target Link-Layer) option.
  1682  	next, done, err = it.Next()
  1683  	if err != nil {
  1684  		t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err)
  1685  	}
  1686  	if done {
  1687  		t.Fatal("got Next = (_, true, _), want = (_, false, _)")
  1688  	}
  1689  	if got, want := []byte(next.(NDPTargetLinkLayerAddressOption)), buf[10:][:6]; !bytes.Equal(got, want) {
  1690  		t.Errorf("got Next = (%x, _, _), want = (%x, _, _)", got, want)
  1691  	}
  1692  	if got := next.kind(); got != ndpTargetLinkLayerAddressOptionType {
  1693  		t.Errorf("got Type = %d, want = %d", got, ndpTargetLinkLayerAddressOptionType)
  1694  	}
  1695  
  1696  	// Test the next (Prefix Information) option.
  1697  	// Note, the unrecognized option should be skipped.
  1698  	next, done, err = it.Next()
  1699  	if err != nil {
  1700  		t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err)
  1701  	}
  1702  	if done {
  1703  		t.Fatal("got Next = (_, true, _), want = (_, false, _)")
  1704  	}
  1705  	if got, want := next.(NDPPrefixInformation), buf[34:][:30]; !bytes.Equal(got, want) {
  1706  		t.Errorf("got Next = (%x, _, _), want = (%x, _, _)", got, want)
  1707  	}
  1708  	if got := next.kind(); got != ndpPrefixInformationType {
  1709  		t.Errorf("got Type = %d, want = %d", got, ndpPrefixInformationType)
  1710  	}
  1711  
  1712  	// Iterator should not return anything else.
  1713  	next, done, err = it.Next()
  1714  	if err != nil {
  1715  		t.Errorf("got Next = (_, _, %s), want = (_, _, nil)", err)
  1716  	}
  1717  	if !done {
  1718  		t.Error("got Next = (_, false, _), want = (_, true, _)")
  1719  	}
  1720  	if next != nil {
  1721  		t.Errorf("got Next = (%x, _, _), want = (nil, _, _)", next)
  1722  	}
  1723  }
  1724  
  1725  func TestNDPRoutePreferenceStringer(t *testing.T) {
  1726  	p := NDPRoutePreference(0)
  1727  	for {
  1728  		var wantStr string
  1729  		switch p {
  1730  		case 0b01:
  1731  			wantStr = "HighRoutePreference"
  1732  		case 0b00:
  1733  			wantStr = "MediumRoutePreference"
  1734  		case 0b11:
  1735  			wantStr = "LowRoutePreference"
  1736  		case 0b10:
  1737  			wantStr = "ReservedRoutePreference"
  1738  		default:
  1739  			wantStr = fmt.Sprintf("NDPRoutePreference(%d)", p)
  1740  		}
  1741  
  1742  		if gotStr := p.String(); gotStr != wantStr {
  1743  			t.Errorf("got NDPRoutePreference(%d).String() = %s, want = %s", p, gotStr, wantStr)
  1744  		}
  1745  
  1746  		p++
  1747  		if p == 0 {
  1748  			// Overflowed, we hit all values.
  1749  			break
  1750  		}
  1751  	}
  1752  }