github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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  	"github.com/SagerNet/gvisor/pkg/tcpip"
    30  	"github.com/SagerNet/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.Address(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.Address(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.Address(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.Address(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.Address(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.Address(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.Address(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.Address(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); 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); 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  				"\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  				"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
   879  				"\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  				"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
   894  				"\x11\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x10",
   895  				"\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  			opt, ok := next.(NDPRecursiveDNSServer)
   921  			if !ok {
   922  				t.Fatalf("next (type = %T) cannot be casted to an NDPRecursiveDNSServer", next)
   923  			}
   924  			if got := opt.Lifetime(); got != test.lifetime {
   925  				t.Errorf("got Lifetime = %d, want = %d", got, test.lifetime)
   926  			}
   927  			addrs, err := opt.Addresses()
   928  			if err != nil {
   929  				t.Errorf("opt.Addresses() = %s", err)
   930  			}
   931  			if diff := cmp.Diff(addrs, test.addrs); diff != "" {
   932  				t.Errorf("mismatched addresses (-want +got):\n%s", diff)
   933  			}
   934  
   935  			// Iterator should not return anything else.
   936  			next, done, err = it.Next()
   937  			if err != nil {
   938  				t.Errorf("got Next = (_, _, %s), want = (_, _, nil)", err)
   939  			}
   940  			if !done {
   941  				t.Error("got Next = (_, false, _), want = (_, true, _)")
   942  			}
   943  			if next != nil {
   944  				t.Errorf("got Next = (%x, _, _), want = (nil, _, _)", next)
   945  			}
   946  		})
   947  	}
   948  }
   949  
   950  // TestNDPDNSSearchListOption tests the getters of NDPDNSSearchList.
   951  func TestNDPDNSSearchListOption(t *testing.T) {
   952  	tests := []struct {
   953  		name        string
   954  		buf         []byte
   955  		lifetime    time.Duration
   956  		domainNames []string
   957  		err         error
   958  	}{
   959  		{
   960  			name: "Valid1Label",
   961  			buf: []byte{
   962  				0, 0,
   963  				0, 0, 0, 1,
   964  				3, 'a', 'b', 'c',
   965  				0,
   966  				0, 0, 0,
   967  			},
   968  			lifetime: time.Second,
   969  			domainNames: []string{
   970  				"abc",
   971  			},
   972  			err: nil,
   973  		},
   974  		{
   975  			name: "Valid2Label",
   976  			buf: []byte{
   977  				0, 0,
   978  				0, 0, 0, 5,
   979  				3, 'a', 'b', 'c',
   980  				4, 'a', 'b', 'c', 'd',
   981  				0,
   982  				0, 0, 0, 0, 0, 0,
   983  			},
   984  			lifetime: 5 * time.Second,
   985  			domainNames: []string{
   986  				"abc.abcd",
   987  			},
   988  			err: nil,
   989  		},
   990  		{
   991  			name: "Valid3Label",
   992  			buf: []byte{
   993  				0, 0,
   994  				1, 0, 0, 0,
   995  				3, 'a', 'b', 'c',
   996  				4, 'a', 'b', 'c', 'd',
   997  				1, 'e',
   998  				0,
   999  				0, 0, 0, 0,
  1000  			},
  1001  			lifetime: 16777216 * time.Second,
  1002  			domainNames: []string{
  1003  				"abc.abcd.e",
  1004  			},
  1005  			err: nil,
  1006  		},
  1007  		{
  1008  			name: "Valid2Domains",
  1009  			buf: []byte{
  1010  				0, 0,
  1011  				1, 2, 3, 4,
  1012  				3, 'a', 'b', 'c',
  1013  				0,
  1014  				2, 'd', 'e',
  1015  				3, 'x', 'y', 'z',
  1016  				0,
  1017  				0, 0, 0,
  1018  			},
  1019  			lifetime: 16909060 * time.Second,
  1020  			domainNames: []string{
  1021  				"abc",
  1022  				"de.xyz",
  1023  			},
  1024  			err: nil,
  1025  		},
  1026  		{
  1027  			name: "Valid3DomainsMixedCase",
  1028  			buf: []byte{
  1029  				0, 0,
  1030  				0, 0, 0, 0,
  1031  				3, 'a', 'B', 'c',
  1032  				0,
  1033  				2, 'd', 'E',
  1034  				3, 'X', 'y', 'z',
  1035  				0,
  1036  				1, 'J',
  1037  				0,
  1038  			},
  1039  			lifetime: 0,
  1040  			domainNames: []string{
  1041  				"abc",
  1042  				"de.xyz",
  1043  				"j",
  1044  			},
  1045  			err: nil,
  1046  		},
  1047  		{
  1048  			name: "ValidDomainAfterNULL",
  1049  			buf: []byte{
  1050  				0, 0,
  1051  				0, 0, 0, 0,
  1052  				3, 'a', 'B', 'c',
  1053  				0, 0, 0, 0,
  1054  				2, 'd', 'E',
  1055  				3, 'X', 'y', 'z',
  1056  				0,
  1057  			},
  1058  			lifetime: 0,
  1059  			domainNames: []string{
  1060  				"abc",
  1061  				"de.xyz",
  1062  			},
  1063  			err: nil,
  1064  		},
  1065  		{
  1066  			name: "Valid0Domains",
  1067  			buf: []byte{
  1068  				0, 0,
  1069  				0, 0, 0, 0,
  1070  				0,
  1071  				0, 0, 0, 0, 0, 0, 0,
  1072  			},
  1073  			lifetime:    0,
  1074  			domainNames: nil,
  1075  			err:         nil,
  1076  		},
  1077  		{
  1078  			name: "NoTrailingNull",
  1079  			buf: []byte{
  1080  				0, 0,
  1081  				0, 0, 0, 0,
  1082  				7, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
  1083  			},
  1084  			lifetime:    0,
  1085  			domainNames: nil,
  1086  			err:         io.ErrUnexpectedEOF,
  1087  		},
  1088  		{
  1089  			name: "IncorrectLength",
  1090  			buf: []byte{
  1091  				0, 0,
  1092  				0, 0, 0, 0,
  1093  				8, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
  1094  			},
  1095  			lifetime:    0,
  1096  			domainNames: nil,
  1097  			err:         io.ErrUnexpectedEOF,
  1098  		},
  1099  		{
  1100  			name: "IncorrectLengthWithNULL",
  1101  			buf: []byte{
  1102  				0, 0,
  1103  				0, 0, 0, 0,
  1104  				7, 'a', 'b', 'c', 'd', 'e', 'f',
  1105  				0,
  1106  			},
  1107  			lifetime:    0,
  1108  			domainNames: nil,
  1109  			err:         ErrNDPOptMalformedBody,
  1110  		},
  1111  		{
  1112  			name: "LabelOfLength63",
  1113  			buf: []byte{
  1114  				0, 0,
  1115  				0, 0, 0, 0,
  1116  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1117  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1118  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1119  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1120  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1121  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1122  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1123  				'i', 'j', 'k',
  1124  				0,
  1125  			},
  1126  			lifetime: 0,
  1127  			domainNames: []string{
  1128  				"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk",
  1129  			},
  1130  			err: nil,
  1131  		},
  1132  		{
  1133  			name: "LabelOfLength64",
  1134  			buf: []byte{
  1135  				0, 0,
  1136  				0, 0, 0, 0,
  1137  				64, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1138  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1139  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1140  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1141  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1142  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1143  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1144  				'i', 'j', 'k', 'l',
  1145  				0,
  1146  			},
  1147  			lifetime:    0,
  1148  			domainNames: nil,
  1149  			err:         ErrNDPOptMalformedBody,
  1150  		},
  1151  		{
  1152  			name: "DomainNameOfLength255",
  1153  			buf: []byte{
  1154  				0, 0,
  1155  				0, 0, 0, 0,
  1156  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1157  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1158  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1159  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1160  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1161  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1162  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1163  				'i', 'j', 'k',
  1164  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1165  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1166  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1167  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1168  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1169  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1170  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1171  				'i', 'j', 'k',
  1172  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1173  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1174  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1175  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1176  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1177  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1178  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1179  				'i', 'j', 'k',
  1180  				62, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1181  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1182  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1183  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1184  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1185  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1186  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1187  				'i', 'j',
  1188  				0,
  1189  			},
  1190  			lifetime: 0,
  1191  			domainNames: []string{
  1192  				"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij",
  1193  			},
  1194  			err: nil,
  1195  		},
  1196  		{
  1197  			name: "DomainNameOfLength256",
  1198  			buf: []byte{
  1199  				0, 0,
  1200  				0, 0, 0, 0,
  1201  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1202  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1203  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1204  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1205  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1206  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1207  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1208  				'i', 'j', 'k',
  1209  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1210  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1211  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1212  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1213  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1214  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1215  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1216  				'i', 'j', 'k',
  1217  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1218  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1219  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1220  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1221  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1222  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1223  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1224  				'i', 'j', 'k',
  1225  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1226  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1227  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1228  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1229  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1230  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1231  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1232  				'i', 'j', 'k',
  1233  				0,
  1234  			},
  1235  			lifetime:    0,
  1236  			domainNames: nil,
  1237  			err:         ErrNDPOptMalformedBody,
  1238  		},
  1239  		{
  1240  			name: "StartingDigitForLabel",
  1241  			buf: []byte{
  1242  				0, 0,
  1243  				0, 0, 0, 1,
  1244  				3, '9', 'b', 'c',
  1245  				0,
  1246  				0, 0, 0,
  1247  			},
  1248  			lifetime:    time.Second,
  1249  			domainNames: nil,
  1250  			err:         ErrNDPOptMalformedBody,
  1251  		},
  1252  		{
  1253  			name: "StartingHyphenForLabel",
  1254  			buf: []byte{
  1255  				0, 0,
  1256  				0, 0, 0, 1,
  1257  				3, '-', 'b', 'c',
  1258  				0,
  1259  				0, 0, 0,
  1260  			},
  1261  			lifetime:    time.Second,
  1262  			domainNames: nil,
  1263  			err:         ErrNDPOptMalformedBody,
  1264  		},
  1265  		{
  1266  			name: "EndingHyphenForLabel",
  1267  			buf: []byte{
  1268  				0, 0,
  1269  				0, 0, 0, 1,
  1270  				3, 'a', 'b', '-',
  1271  				0,
  1272  				0, 0, 0,
  1273  			},
  1274  			lifetime:    time.Second,
  1275  			domainNames: nil,
  1276  			err:         ErrNDPOptMalformedBody,
  1277  		},
  1278  		{
  1279  			name: "EndingDigitForLabel",
  1280  			buf: []byte{
  1281  				0, 0,
  1282  				0, 0, 0, 1,
  1283  				3, 'a', 'b', '9',
  1284  				0,
  1285  				0, 0, 0,
  1286  			},
  1287  			lifetime: time.Second,
  1288  			domainNames: []string{
  1289  				"ab9",
  1290  			},
  1291  			err: nil,
  1292  		},
  1293  	}
  1294  
  1295  	for _, test := range tests {
  1296  		t.Run(test.name, func(t *testing.T) {
  1297  			opt := NDPDNSSearchList(test.buf)
  1298  
  1299  			if got := opt.Lifetime(); got != test.lifetime {
  1300  				t.Errorf("got Lifetime = %d, want = %d", got, test.lifetime)
  1301  			}
  1302  			domainNames, err := opt.DomainNames()
  1303  			if !errors.Is(err, test.err) {
  1304  				t.Errorf("opt.DomainNames() = %s", err)
  1305  			}
  1306  			if diff := cmp.Diff(domainNames, test.domainNames); diff != "" {
  1307  				t.Errorf("mismatched domain names (-want +got):\n%s", diff)
  1308  			}
  1309  		})
  1310  	}
  1311  }
  1312  
  1313  func TestNDPSearchListOptionDomainNameLabelInvalidSymbols(t *testing.T) {
  1314  	for r := rune(0); r <= 255; r++ {
  1315  		t.Run(fmt.Sprintf("RuneVal=%d", r), func(t *testing.T) {
  1316  			buf := []byte{
  1317  				0, 0,
  1318  				0, 0, 0, 0,
  1319  				3, 'a', 0 /* will be replaced */, 'c',
  1320  				0,
  1321  				0, 0, 0,
  1322  			}
  1323  			buf[8] = uint8(r)
  1324  			opt := NDPDNSSearchList(buf)
  1325  
  1326  			// As per RFC 1035 section 2.3.1, the label must only include ASCII
  1327  			// letters, digits and hyphens (a-z, A-Z, 0-9, -).
  1328  			var expectedErr error
  1329  			re := regexp.MustCompile(`[a-zA-Z0-9-]`)
  1330  			if !re.Match([]byte{byte(r)}) {
  1331  				expectedErr = ErrNDPOptMalformedBody
  1332  			}
  1333  
  1334  			if domainNames, err := opt.DomainNames(); !errors.Is(err, expectedErr) {
  1335  				t.Errorf("got opt.DomainNames() = (%s, %v), want = (_, %v)", domainNames, err, ErrNDPOptMalformedBody)
  1336  			}
  1337  		})
  1338  	}
  1339  }
  1340  
  1341  // TestNDPOptionsIterCheck tests that Iter will return false if the NDPOptions
  1342  // the iterator was returned for is malformed.
  1343  func TestNDPOptionsIterCheck(t *testing.T) {
  1344  	tests := []struct {
  1345  		name        string
  1346  		buf         []byte
  1347  		expectedErr error
  1348  	}{
  1349  		{
  1350  			name:        "ZeroLengthField",
  1351  			buf:         []byte{0, 0, 0, 0, 0, 0, 0, 0},
  1352  			expectedErr: ErrNDPOptMalformedHeader,
  1353  		},
  1354  		{
  1355  			name:        "ValidSourceLinkLayerAddressOption",
  1356  			buf:         []byte{1, 1, 1, 2, 3, 4, 5, 6},
  1357  			expectedErr: nil,
  1358  		},
  1359  		{
  1360  			name:        "TooSmallSourceLinkLayerAddressOption",
  1361  			buf:         []byte{1, 1, 1, 2, 3, 4, 5},
  1362  			expectedErr: io.ErrUnexpectedEOF,
  1363  		},
  1364  		{
  1365  			name:        "ValidTargetLinkLayerAddressOption",
  1366  			buf:         []byte{2, 1, 1, 2, 3, 4, 5, 6},
  1367  			expectedErr: nil,
  1368  		},
  1369  		{
  1370  			name:        "TooSmallTargetLinkLayerAddressOption",
  1371  			buf:         []byte{2, 1, 1, 2, 3, 4, 5},
  1372  			expectedErr: io.ErrUnexpectedEOF,
  1373  		},
  1374  		{
  1375  			name: "ValidPrefixInformation",
  1376  			buf: []byte{
  1377  				3, 4, 43, 64,
  1378  				1, 2, 3, 4,
  1379  				5, 6, 7, 8,
  1380  				0, 0, 0, 0,
  1381  				9, 10, 11, 12,
  1382  				13, 14, 15, 16,
  1383  				17, 18, 19, 20,
  1384  				21, 22, 23, 24,
  1385  			},
  1386  			expectedErr: nil,
  1387  		},
  1388  		{
  1389  			name: "TooSmallPrefixInformation",
  1390  			buf: []byte{
  1391  				3, 4, 43, 64,
  1392  				1, 2, 3, 4,
  1393  				5, 6, 7, 8,
  1394  				0, 0, 0, 0,
  1395  				9, 10, 11, 12,
  1396  				13, 14, 15, 16,
  1397  				17, 18, 19, 20,
  1398  				21, 22, 23,
  1399  			},
  1400  			expectedErr: io.ErrUnexpectedEOF,
  1401  		},
  1402  		{
  1403  			name: "InvalidPrefixInformationLength",
  1404  			buf: []byte{
  1405  				3, 3, 43, 64,
  1406  				1, 2, 3, 4,
  1407  				5, 6, 7, 8,
  1408  				0, 0, 0, 0,
  1409  				9, 10, 11, 12,
  1410  				13, 14, 15, 16,
  1411  			},
  1412  			expectedErr: ErrNDPOptMalformedBody,
  1413  		},
  1414  		{
  1415  			name: "ValidSourceAndTargetLinkLayerAddressWithPrefixInformation",
  1416  			buf: []byte{
  1417  				// Source Link-Layer Address.
  1418  				1, 1, 1, 2, 3, 4, 5, 6,
  1419  
  1420  				// Target Link-Layer Address.
  1421  				2, 1, 7, 8, 9, 10, 11, 12,
  1422  
  1423  				// Prefix information.
  1424  				3, 4, 43, 64,
  1425  				1, 2, 3, 4,
  1426  				5, 6, 7, 8,
  1427  				0, 0, 0, 0,
  1428  				9, 10, 11, 12,
  1429  				13, 14, 15, 16,
  1430  				17, 18, 19, 20,
  1431  				21, 22, 23, 24,
  1432  			},
  1433  			expectedErr: nil,
  1434  		},
  1435  		{
  1436  			name: "ValidSourceAndTargetLinkLayerAddressWithPrefixInformationWithUnrecognized",
  1437  			buf: []byte{
  1438  				// Source Link-Layer Address.
  1439  				1, 1, 1, 2, 3, 4, 5, 6,
  1440  
  1441  				// Target Link-Layer Address.
  1442  				2, 1, 7, 8, 9, 10, 11, 12,
  1443  
  1444  				// 255 is an unrecognized type. If 255 ends up
  1445  				// being the type for some recognized type,
  1446  				// update 255 to some other unrecognized value.
  1447  				255, 2, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8,
  1448  
  1449  				// Prefix information.
  1450  				3, 4, 43, 64,
  1451  				1, 2, 3, 4,
  1452  				5, 6, 7, 8,
  1453  				0, 0, 0, 0,
  1454  				9, 10, 11, 12,
  1455  				13, 14, 15, 16,
  1456  				17, 18, 19, 20,
  1457  				21, 22, 23, 24,
  1458  			},
  1459  			expectedErr: nil,
  1460  		},
  1461  		{
  1462  			name: "InvalidRecursiveDNSServerCutsOffAddress",
  1463  			buf: []byte{
  1464  				25, 4, 0, 0,
  1465  				0, 0, 0, 0,
  1466  				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
  1467  				0, 1, 2, 3, 4, 5, 6, 7,
  1468  			},
  1469  			expectedErr: ErrNDPOptMalformedBody,
  1470  		},
  1471  		{
  1472  			name: "InvalidRecursiveDNSServerInvalidLengthField",
  1473  			buf: []byte{
  1474  				25, 2, 0, 0,
  1475  				0, 0, 0, 0,
  1476  				0, 1, 2, 3, 4, 5, 6, 7, 8,
  1477  			},
  1478  			expectedErr: io.ErrUnexpectedEOF,
  1479  		},
  1480  		{
  1481  			name: "RecursiveDNSServerTooSmall",
  1482  			buf: []byte{
  1483  				25, 1, 0, 0,
  1484  				0, 0, 0,
  1485  			},
  1486  			expectedErr: io.ErrUnexpectedEOF,
  1487  		},
  1488  		{
  1489  			name: "RecursiveDNSServerMulticast",
  1490  			buf: []byte{
  1491  				25, 3, 0, 0,
  1492  				0, 0, 0, 0,
  1493  				255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
  1494  			},
  1495  			expectedErr: ErrNDPOptMalformedBody,
  1496  		},
  1497  		{
  1498  			name: "RecursiveDNSServerUnspecified",
  1499  			buf: []byte{
  1500  				25, 3, 0, 0,
  1501  				0, 0, 0, 0,
  1502  				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1503  			},
  1504  			expectedErr: ErrNDPOptMalformedBody,
  1505  		},
  1506  		{
  1507  			name: "DNSSearchListLargeCompliantRFC1035",
  1508  			buf: []byte{
  1509  				31, 33, 0, 0,
  1510  				0, 0, 0, 0,
  1511  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1512  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1513  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1514  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1515  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1516  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1517  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1518  				'i', 'j', 'k',
  1519  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1520  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1521  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1522  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1523  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1524  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1525  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1526  				'i', 'j', 'k',
  1527  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1528  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1529  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1530  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1531  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1532  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1533  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1534  				'i', 'j', 'k',
  1535  				62, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1536  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1537  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1538  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1539  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1540  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1541  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1542  				'i', 'j',
  1543  				0,
  1544  			},
  1545  			expectedErr: nil,
  1546  		},
  1547  		{
  1548  			name: "DNSSearchListNonCompliantRFC1035",
  1549  			buf: []byte{
  1550  				31, 33, 0, 0,
  1551  				0, 0, 0, 0,
  1552  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1553  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1554  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1555  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1556  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1557  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1558  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1559  				'i', 'j', 'k',
  1560  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1561  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1562  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1563  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1564  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1565  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1566  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1567  				'i', 'j', 'k',
  1568  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1569  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1570  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1571  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1572  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1573  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1574  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1575  				'i', 'j', 'k',
  1576  				63, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1577  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1578  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1579  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1580  				'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
  1581  				'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  1582  				'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1583  				'i', 'j', 'k',
  1584  				0,
  1585  				0, 0, 0, 0, 0, 0, 0, 0,
  1586  			},
  1587  			expectedErr: ErrNDPOptMalformedBody,
  1588  		},
  1589  		{
  1590  			name: "DNSSearchListValidSmall",
  1591  			buf: []byte{
  1592  				31, 2, 0, 0,
  1593  				0, 0, 0, 0,
  1594  				6, 'a', 'b', 'c', 'd', 'e', 'f',
  1595  				0,
  1596  			},
  1597  			expectedErr: nil,
  1598  		},
  1599  		{
  1600  			name: "DNSSearchListTooSmall",
  1601  			buf: []byte{
  1602  				31, 1, 0, 0,
  1603  				0, 0, 0,
  1604  			},
  1605  			expectedErr: io.ErrUnexpectedEOF,
  1606  		},
  1607  	}
  1608  
  1609  	for _, test := range tests {
  1610  		t.Run(test.name, func(t *testing.T) {
  1611  			opts := NDPOptions(test.buf)
  1612  
  1613  			if _, err := opts.Iter(true); !errors.Is(err, test.expectedErr) {
  1614  				t.Fatalf("got Iter(true) = (_, %v), want = (_, %v)", err, test.expectedErr)
  1615  			}
  1616  
  1617  			// test.buf may be malformed but we chose not to check
  1618  			// the iterator so it must return true.
  1619  			if _, err := opts.Iter(false); err != nil {
  1620  				t.Fatalf("got Iter(false) = (_, %s), want = (_, nil)", err)
  1621  			}
  1622  		})
  1623  	}
  1624  }
  1625  
  1626  // TestNDPOptionsIter tests that we can iterator over a valid NDPOptions. Note,
  1627  // this test does not actually check any of the option's getters, it simply
  1628  // checks the option Type and Body. We have other tests that tests the option
  1629  // field gettings given an option body and don't need to duplicate those tests
  1630  // here.
  1631  func TestNDPOptionsIter(t *testing.T) {
  1632  	buf := []byte{
  1633  		// Source Link-Layer Address.
  1634  		1, 1, 1, 2, 3, 4, 5, 6,
  1635  
  1636  		// Target Link-Layer Address.
  1637  		2, 1, 7, 8, 9, 10, 11, 12,
  1638  
  1639  		// 255 is an unrecognized type. If 255 ends up being the type
  1640  		// for some recognized type, update 255 to some other
  1641  		// unrecognized value. Note, this option should be skipped when
  1642  		// iterating.
  1643  		255, 2, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 8,
  1644  
  1645  		// Prefix information.
  1646  		3, 4, 43, 64,
  1647  		1, 2, 3, 4,
  1648  		5, 6, 7, 8,
  1649  		0, 0, 0, 0,
  1650  		9, 10, 11, 12,
  1651  		13, 14, 15, 16,
  1652  		17, 18, 19, 20,
  1653  		21, 22, 23, 24,
  1654  	}
  1655  
  1656  	opts := NDPOptions(buf)
  1657  	it, err := opts.Iter(true)
  1658  	if err != nil {
  1659  		t.Fatalf("got Iter = (_, %s), want = (_, nil)", err)
  1660  	}
  1661  
  1662  	// Test the first (Source Link-Layer) option.
  1663  	next, done, err := it.Next()
  1664  	if err != nil {
  1665  		t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err)
  1666  	}
  1667  	if done {
  1668  		t.Fatal("got Next = (_, true, _), want = (_, false, _)")
  1669  	}
  1670  	if got, want := []byte(next.(NDPSourceLinkLayerAddressOption)), buf[2:][:6]; !bytes.Equal(got, want) {
  1671  		t.Errorf("got Next = (%x, _, _), want = (%x, _, _)", got, want)
  1672  	}
  1673  	if got := next.kind(); got != ndpSourceLinkLayerAddressOptionType {
  1674  		t.Errorf("got Type = %d, want = %d", got, ndpSourceLinkLayerAddressOptionType)
  1675  	}
  1676  
  1677  	// Test the next (Target Link-Layer) option.
  1678  	next, done, err = it.Next()
  1679  	if err != nil {
  1680  		t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err)
  1681  	}
  1682  	if done {
  1683  		t.Fatal("got Next = (_, true, _), want = (_, false, _)")
  1684  	}
  1685  	if got, want := []byte(next.(NDPTargetLinkLayerAddressOption)), buf[10:][:6]; !bytes.Equal(got, want) {
  1686  		t.Errorf("got Next = (%x, _, _), want = (%x, _, _)", got, want)
  1687  	}
  1688  	if got := next.kind(); got != ndpTargetLinkLayerAddressOptionType {
  1689  		t.Errorf("got Type = %d, want = %d", got, ndpTargetLinkLayerAddressOptionType)
  1690  	}
  1691  
  1692  	// Test the next (Prefix Information) option.
  1693  	// Note, the unrecognized option should be skipped.
  1694  	next, done, err = it.Next()
  1695  	if err != nil {
  1696  		t.Fatalf("got Next = (_, _, %s), want = (_, _, nil)", err)
  1697  	}
  1698  	if done {
  1699  		t.Fatal("got Next = (_, true, _), want = (_, false, _)")
  1700  	}
  1701  	if got, want := next.(NDPPrefixInformation), buf[34:][:30]; !bytes.Equal(got, want) {
  1702  		t.Errorf("got Next = (%x, _, _), want = (%x, _, _)", got, want)
  1703  	}
  1704  	if got := next.kind(); got != ndpPrefixInformationType {
  1705  		t.Errorf("got Type = %d, want = %d", got, ndpPrefixInformationType)
  1706  	}
  1707  
  1708  	// Iterator should not return anything else.
  1709  	next, done, err = it.Next()
  1710  	if err != nil {
  1711  		t.Errorf("got Next = (_, _, %s), want = (_, _, nil)", err)
  1712  	}
  1713  	if !done {
  1714  		t.Error("got Next = (_, false, _), want = (_, true, _)")
  1715  	}
  1716  	if next != nil {
  1717  		t.Errorf("got Next = (%x, _, _), want = (nil, _, _)", next)
  1718  	}
  1719  }
  1720  
  1721  func TestNDPRoutePreferenceStringer(t *testing.T) {
  1722  	p := NDPRoutePreference(0)
  1723  	for {
  1724  		var wantStr string
  1725  		switch p {
  1726  		case 0b01:
  1727  			wantStr = "HighRoutePreference"
  1728  		case 0b00:
  1729  			wantStr = "MediumRoutePreference"
  1730  		case 0b11:
  1731  			wantStr = "LowRoutePreference"
  1732  		case 0b10:
  1733  			wantStr = "ReservedRoutePreference"
  1734  		default:
  1735  			wantStr = fmt.Sprintf("NDPRoutePreference(%d)", p)
  1736  		}
  1737  
  1738  		if gotStr := p.String(); gotStr != wantStr {
  1739  			t.Errorf("got NDPRoutePreference(%d).String() = %s, want = %s", p, gotStr, wantStr)
  1740  		}
  1741  
  1742  		p++
  1743  		if p == 0 {
  1744  			// Overflowed, we hit all values.
  1745  			break
  1746  		}
  1747  	}
  1748  }