github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/network/ipv6/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 ipv6
    16  
    17  import (
    18  	"bytes"
    19  	"math/rand"
    20  	"strings"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	"github.com/SagerNet/gvisor/pkg/tcpip"
    26  	"github.com/SagerNet/gvisor/pkg/tcpip/buffer"
    27  	"github.com/SagerNet/gvisor/pkg/tcpip/checker"
    28  	"github.com/SagerNet/gvisor/pkg/tcpip/faketime"
    29  	"github.com/SagerNet/gvisor/pkg/tcpip/header"
    30  	"github.com/SagerNet/gvisor/pkg/tcpip/link/channel"
    31  	"github.com/SagerNet/gvisor/pkg/tcpip/stack"
    32  	"github.com/SagerNet/gvisor/pkg/tcpip/transport/icmp"
    33  )
    34  
    35  var _ NDPDispatcher = (*testNDPDispatcher)(nil)
    36  
    37  // testNDPDispatcher is an NDPDispatcher only allows default router discovery.
    38  type testNDPDispatcher struct {
    39  	addr tcpip.Address
    40  }
    41  
    42  func (*testNDPDispatcher) OnDuplicateAddressDetectionResult(tcpip.NICID, tcpip.Address, stack.DADResult) {
    43  }
    44  
    45  func (t *testNDPDispatcher) OnOffLinkRouteUpdated(_ tcpip.NICID, _ tcpip.Subnet, addr tcpip.Address, _ header.NDPRoutePreference) {
    46  	t.addr = addr
    47  }
    48  
    49  func (t *testNDPDispatcher) OnOffLinkRouteInvalidated(_ tcpip.NICID, _ tcpip.Subnet, addr tcpip.Address) {
    50  	t.addr = addr
    51  }
    52  
    53  func (*testNDPDispatcher) OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet) {
    54  }
    55  
    56  func (*testNDPDispatcher) OnOnLinkPrefixInvalidated(tcpip.NICID, tcpip.Subnet) {
    57  }
    58  
    59  func (*testNDPDispatcher) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) {
    60  }
    61  
    62  func (*testNDPDispatcher) OnAutoGenAddressDeprecated(tcpip.NICID, tcpip.AddressWithPrefix) {
    63  }
    64  
    65  func (*testNDPDispatcher) OnAutoGenAddressInvalidated(tcpip.NICID, tcpip.AddressWithPrefix) {
    66  }
    67  
    68  func (*testNDPDispatcher) OnRecursiveDNSServerOption(tcpip.NICID, []tcpip.Address, time.Duration) {
    69  }
    70  
    71  func (*testNDPDispatcher) OnDNSSearchListOption(tcpip.NICID, []string, time.Duration) {
    72  }
    73  
    74  func (*testNDPDispatcher) OnDHCPv6Configuration(tcpip.NICID, DHCPv6ConfigurationFromNDPRA) {
    75  }
    76  
    77  func TestStackNDPEndpointInvalidateDefaultRouter(t *testing.T) {
    78  	var ndpDisp testNDPDispatcher
    79  	s := stack.New(stack.Options{
    80  		NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocolWithOptions(Options{
    81  			NDPDisp: &ndpDisp,
    82  		})},
    83  	})
    84  
    85  	if err := s.CreateNIC(nicID, &stubLinkEndpoint{}); err != nil {
    86  		t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err)
    87  	}
    88  
    89  	ep, err := s.GetNetworkEndpoint(nicID, ProtocolNumber)
    90  	if err != nil {
    91  		t.Fatalf("s.GetNetworkEndpoint(%d, %d): %s", nicID, ProtocolNumber, err)
    92  	}
    93  
    94  	ipv6EP := ep.(*endpoint)
    95  	ipv6EP.mu.Lock()
    96  	ipv6EP.mu.ndp.handleOffLinkRouteDiscovery(offLinkRoute{dest: header.IPv6EmptySubnet, router: lladdr1}, time.Hour, header.MediumRoutePreference)
    97  	ipv6EP.mu.Unlock()
    98  
    99  	if ndpDisp.addr != lladdr1 {
   100  		t.Fatalf("got ndpDisp.addr = %s, want = %s", ndpDisp.addr, lladdr1)
   101  	}
   102  
   103  	ndpDisp.addr = ""
   104  	ndpEP := ep.(stack.NDPEndpoint)
   105  	ndpEP.InvalidateDefaultRouter(lladdr1)
   106  	if ndpDisp.addr != lladdr1 {
   107  		t.Fatalf("got ndpDisp.addr = %s, want = %s", ndpDisp.addr, lladdr1)
   108  	}
   109  }
   110  
   111  // TestNeighborSolicitationWithSourceLinkLayerOption tests that receiving a
   112  // valid NDP NS message with the Source Link Layer Address option results in a
   113  // new entry in the link address cache for the sender of the message.
   114  func TestNeighborSolicitationWithSourceLinkLayerOption(t *testing.T) {
   115  	const nicID = 1
   116  
   117  	tests := []struct {
   118  		name             string
   119  		optsBuf          []byte
   120  		expectedLinkAddr tcpip.LinkAddress
   121  	}{
   122  		{
   123  			name:             "Valid",
   124  			optsBuf:          []byte{1, 1, 2, 3, 4, 5, 6, 7},
   125  			expectedLinkAddr: "\x02\x03\x04\x05\x06\x07",
   126  		},
   127  		{
   128  			name:    "Too Small",
   129  			optsBuf: []byte{1, 1, 2, 3, 4, 5, 6},
   130  		},
   131  		{
   132  			name:    "Invalid Length",
   133  			optsBuf: []byte{1, 2, 2, 3, 4, 5, 6, 7},
   134  		},
   135  	}
   136  
   137  	for _, test := range tests {
   138  		t.Run(test.name, func(t *testing.T) {
   139  			s := stack.New(stack.Options{
   140  				NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
   141  			})
   142  			e := channel.New(0, 1280, linkAddr0)
   143  			e.LinkEPCapabilities |= stack.CapabilityResolutionRequired
   144  			if err := s.CreateNIC(nicID, e); err != nil {
   145  				t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
   146  			}
   147  			if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil {
   148  				t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, lladdr0, err)
   149  			}
   150  
   151  			ndpNSSize := header.ICMPv6NeighborSolicitMinimumSize + len(test.optsBuf)
   152  			hdr := buffer.NewPrependable(header.IPv6MinimumSize + ndpNSSize)
   153  			pkt := header.ICMPv6(hdr.Prepend(ndpNSSize))
   154  			pkt.SetType(header.ICMPv6NeighborSolicit)
   155  			ns := header.NDPNeighborSolicit(pkt.MessageBody())
   156  			ns.SetTargetAddress(lladdr0)
   157  			opts := ns.Options()
   158  			copy(opts, test.optsBuf)
   159  			pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
   160  				Header: pkt,
   161  				Src:    lladdr1,
   162  				Dst:    lladdr0,
   163  			}))
   164  			payloadLength := hdr.UsedLength()
   165  			ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
   166  			ip.Encode(&header.IPv6Fields{
   167  				PayloadLength:     uint16(payloadLength),
   168  				TransportProtocol: header.ICMPv6ProtocolNumber,
   169  				HopLimit:          255,
   170  				SrcAddr:           lladdr1,
   171  				DstAddr:           lladdr0,
   172  			})
   173  
   174  			invalid := s.Stats().ICMP.V6.PacketsReceived.Invalid
   175  
   176  			// Invalid count should initially be 0.
   177  			if got := invalid.Value(); got != 0 {
   178  				t.Fatalf("got invalid = %d, want = 0", got)
   179  			}
   180  
   181  			e.InjectInbound(ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{
   182  				Data: hdr.View().ToVectorisedView(),
   183  			}))
   184  
   185  			neighbors, err := s.Neighbors(nicID, ProtocolNumber)
   186  			if err != nil {
   187  				t.Fatalf("s.Neighbors(%d, %d): %s", nicID, ProtocolNumber, err)
   188  			}
   189  
   190  			neighborByAddr := make(map[tcpip.Address]stack.NeighborEntry)
   191  			for _, n := range neighbors {
   192  				if existing, ok := neighborByAddr[n.Addr]; ok {
   193  					if diff := cmp.Diff(existing, n); diff != "" {
   194  						t.Fatalf("s.Neighbors(%d, %d) returned unexpected duplicate neighbor entry (-existing +got):\n%s", nicID, ProtocolNumber, diff)
   195  					}
   196  					t.Fatalf("s.Neighbors(%d, %d) returned unexpected duplicate neighbor entry: %#v", nicID, ProtocolNumber, existing)
   197  				}
   198  				neighborByAddr[n.Addr] = n
   199  			}
   200  
   201  			if neigh, ok := neighborByAddr[lladdr1]; len(test.expectedLinkAddr) != 0 {
   202  				// Invalid count should not have increased.
   203  				if got := invalid.Value(); got != 0 {
   204  					t.Errorf("got invalid = %d, want = 0", got)
   205  				}
   206  
   207  				if !ok {
   208  					t.Fatalf("expected a neighbor entry for %q", lladdr1)
   209  				}
   210  				if neigh.LinkAddr != test.expectedLinkAddr {
   211  					t.Errorf("got link address = %s, want = %s", neigh.LinkAddr, test.expectedLinkAddr)
   212  				}
   213  				if neigh.State != stack.Stale {
   214  					t.Errorf("got NUD state = %s, want = %s", neigh.State, stack.Stale)
   215  				}
   216  			} else {
   217  				// Invalid count should have increased.
   218  				if got := invalid.Value(); got != 1 {
   219  					t.Errorf("got invalid = %d, want = 1", got)
   220  				}
   221  
   222  				if ok {
   223  					t.Fatalf("unexpectedly got neighbor entry: %#v", neigh)
   224  				}
   225  			}
   226  		})
   227  	}
   228  }
   229  
   230  func TestNeighborSolicitationResponse(t *testing.T) {
   231  	const nicID = 1
   232  	nicAddr := lladdr0
   233  	remoteAddr := lladdr1
   234  	nicAddrSNMC := header.SolicitedNodeAddr(nicAddr)
   235  	nicLinkAddr := linkAddr0
   236  	remoteLinkAddr0 := linkAddr1
   237  	remoteLinkAddr1 := linkAddr2
   238  
   239  	tests := []struct {
   240  		name                   string
   241  		nsOpts                 header.NDPOptionsSerializer
   242  		nsSrcLinkAddr          tcpip.LinkAddress
   243  		nsSrc                  tcpip.Address
   244  		nsDst                  tcpip.Address
   245  		nsInvalid              bool
   246  		naDstLinkAddr          tcpip.LinkAddress
   247  		naSolicited            bool
   248  		naSrc                  tcpip.Address
   249  		naDst                  tcpip.Address
   250  		performsLinkResolution bool
   251  	}{
   252  		{
   253  			name:          "Unspecified source to solicited-node multicast destination",
   254  			nsOpts:        nil,
   255  			nsSrcLinkAddr: remoteLinkAddr0,
   256  			nsSrc:         header.IPv6Any,
   257  			nsDst:         nicAddrSNMC,
   258  			nsInvalid:     false,
   259  			naDstLinkAddr: header.EthernetAddressFromMulticastIPv6Address(header.IPv6AllNodesMulticastAddress),
   260  			naSolicited:   false,
   261  			naSrc:         nicAddr,
   262  			naDst:         header.IPv6AllNodesMulticastAddress,
   263  		},
   264  		{
   265  			name: "Unspecified source with source ll option to multicast destination",
   266  			nsOpts: header.NDPOptionsSerializer{
   267  				header.NDPSourceLinkLayerAddressOption(remoteLinkAddr0[:]),
   268  			},
   269  			nsSrcLinkAddr: remoteLinkAddr0,
   270  			nsSrc:         header.IPv6Any,
   271  			nsDst:         nicAddrSNMC,
   272  			nsInvalid:     true,
   273  		},
   274  		{
   275  			name:          "Unspecified source to unicast destination",
   276  			nsOpts:        nil,
   277  			nsSrcLinkAddr: remoteLinkAddr0,
   278  			nsSrc:         header.IPv6Any,
   279  			nsDst:         nicAddr,
   280  			nsInvalid:     true,
   281  		},
   282  		{
   283  			name: "Unspecified source with source ll option to unicast destination",
   284  			nsOpts: header.NDPOptionsSerializer{
   285  				header.NDPSourceLinkLayerAddressOption(remoteLinkAddr0[:]),
   286  			},
   287  			nsSrcLinkAddr: remoteLinkAddr0,
   288  			nsSrc:         header.IPv6Any,
   289  			nsDst:         nicAddr,
   290  			nsInvalid:     true,
   291  		},
   292  		{
   293  			name: "Specified source with 1 source ll to multicast destination",
   294  			nsOpts: header.NDPOptionsSerializer{
   295  				header.NDPSourceLinkLayerAddressOption(remoteLinkAddr0[:]),
   296  			},
   297  			nsSrcLinkAddr: remoteLinkAddr0,
   298  			nsSrc:         remoteAddr,
   299  			nsDst:         nicAddrSNMC,
   300  			nsInvalid:     false,
   301  			naDstLinkAddr: remoteLinkAddr0,
   302  			naSolicited:   true,
   303  			naSrc:         nicAddr,
   304  			naDst:         remoteAddr,
   305  		},
   306  		{
   307  			name: "Specified source with 1 source ll different from route to multicast destination",
   308  			nsOpts: header.NDPOptionsSerializer{
   309  				header.NDPSourceLinkLayerAddressOption(remoteLinkAddr1[:]),
   310  			},
   311  			nsSrcLinkAddr: remoteLinkAddr0,
   312  			nsSrc:         remoteAddr,
   313  			nsDst:         nicAddrSNMC,
   314  			nsInvalid:     false,
   315  			naDstLinkAddr: remoteLinkAddr1,
   316  			naSolicited:   true,
   317  			naSrc:         nicAddr,
   318  			naDst:         remoteAddr,
   319  		},
   320  		{
   321  			name:          "Specified source to multicast destination",
   322  			nsOpts:        nil,
   323  			nsSrcLinkAddr: remoteLinkAddr0,
   324  			nsSrc:         remoteAddr,
   325  			nsDst:         nicAddrSNMC,
   326  			nsInvalid:     true,
   327  		},
   328  		{
   329  			name: "Specified source with 2 source ll to multicast destination",
   330  			nsOpts: header.NDPOptionsSerializer{
   331  				header.NDPSourceLinkLayerAddressOption(remoteLinkAddr0[:]),
   332  				header.NDPSourceLinkLayerAddressOption(remoteLinkAddr1[:]),
   333  			},
   334  			nsSrcLinkAddr: remoteLinkAddr0,
   335  			nsSrc:         remoteAddr,
   336  			nsDst:         nicAddrSNMC,
   337  			nsInvalid:     true,
   338  		},
   339  
   340  		{
   341  			name:          "Specified source to unicast destination",
   342  			nsOpts:        nil,
   343  			nsSrcLinkAddr: remoteLinkAddr0,
   344  			nsSrc:         remoteAddr,
   345  			nsDst:         nicAddr,
   346  			nsInvalid:     false,
   347  			naDstLinkAddr: remoteLinkAddr0,
   348  			naSolicited:   true,
   349  			naSrc:         nicAddr,
   350  			naDst:         remoteAddr,
   351  			// Since we send a unicast solicitations to a node without an entry for
   352  			// the remote, the node needs to perform neighbor discovery to get the
   353  			// remote's link address to send the advertisement response.
   354  			performsLinkResolution: true,
   355  		},
   356  		{
   357  			name: "Specified source with 1 source ll to unicast destination",
   358  			nsOpts: header.NDPOptionsSerializer{
   359  				header.NDPSourceLinkLayerAddressOption(remoteLinkAddr0[:]),
   360  			},
   361  			nsSrcLinkAddr: remoteLinkAddr0,
   362  			nsSrc:         remoteAddr,
   363  			nsDst:         nicAddr,
   364  			nsInvalid:     false,
   365  			naDstLinkAddr: remoteLinkAddr0,
   366  			naSolicited:   true,
   367  			naSrc:         nicAddr,
   368  			naDst:         remoteAddr,
   369  		},
   370  		{
   371  			name: "Specified source with 1 source ll different from route to unicast destination",
   372  			nsOpts: header.NDPOptionsSerializer{
   373  				header.NDPSourceLinkLayerAddressOption(remoteLinkAddr1[:]),
   374  			},
   375  			nsSrcLinkAddr: remoteLinkAddr0,
   376  			nsSrc:         remoteAddr,
   377  			nsDst:         nicAddr,
   378  			nsInvalid:     false,
   379  			naDstLinkAddr: remoteLinkAddr1,
   380  			naSolicited:   true,
   381  			naSrc:         nicAddr,
   382  			naDst:         remoteAddr,
   383  		},
   384  		{
   385  			name: "Specified source with 2 source ll to unicast destination",
   386  			nsOpts: header.NDPOptionsSerializer{
   387  				header.NDPSourceLinkLayerAddressOption(remoteLinkAddr0[:]),
   388  				header.NDPSourceLinkLayerAddressOption(remoteLinkAddr1[:]),
   389  			},
   390  			nsSrcLinkAddr: remoteLinkAddr0,
   391  			nsSrc:         remoteAddr,
   392  			nsDst:         nicAddr,
   393  			nsInvalid:     true,
   394  		},
   395  	}
   396  
   397  	for _, test := range tests {
   398  		t.Run(test.name, func(t *testing.T) {
   399  			clock := faketime.NewManualClock()
   400  			s := stack.New(stack.Options{
   401  				NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
   402  				Clock:            clock,
   403  			})
   404  			e := channel.New(1, 1280, nicLinkAddr)
   405  			e.LinkEPCapabilities |= stack.CapabilityResolutionRequired
   406  			if err := s.CreateNIC(nicID, e); err != nil {
   407  				t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
   408  			}
   409  			if err := s.AddAddress(nicID, ProtocolNumber, nicAddr); err != nil {
   410  				t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, nicAddr, err)
   411  			}
   412  
   413  			s.SetRouteTable([]tcpip.Route{
   414  				{
   415  					Destination: header.IPv6EmptySubnet,
   416  					NIC:         1,
   417  				},
   418  			})
   419  
   420  			ndpNSSize := header.ICMPv6NeighborSolicitMinimumSize + test.nsOpts.Length()
   421  			hdr := buffer.NewPrependable(header.IPv6MinimumSize + ndpNSSize)
   422  			pkt := header.ICMPv6(hdr.Prepend(ndpNSSize))
   423  			pkt.SetType(header.ICMPv6NeighborSolicit)
   424  			ns := header.NDPNeighborSolicit(pkt.MessageBody())
   425  			ns.SetTargetAddress(nicAddr)
   426  			opts := ns.Options()
   427  			opts.Serialize(test.nsOpts)
   428  			pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
   429  				Header: pkt,
   430  				Src:    test.nsSrc,
   431  				Dst:    test.nsDst,
   432  			}))
   433  			payloadLength := hdr.UsedLength()
   434  			ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
   435  			ip.Encode(&header.IPv6Fields{
   436  				PayloadLength:     uint16(payloadLength),
   437  				TransportProtocol: header.ICMPv6ProtocolNumber,
   438  				HopLimit:          255,
   439  				SrcAddr:           test.nsSrc,
   440  				DstAddr:           test.nsDst,
   441  			})
   442  
   443  			invalid := s.Stats().ICMP.V6.PacketsReceived.Invalid
   444  
   445  			// Invalid count should initially be 0.
   446  			if got := invalid.Value(); got != 0 {
   447  				t.Fatalf("got invalid = %d, want = 0", got)
   448  			}
   449  
   450  			e.InjectLinkAddr(ProtocolNumber, test.nsSrcLinkAddr, stack.NewPacketBuffer(stack.PacketBufferOptions{
   451  				Data: hdr.View().ToVectorisedView(),
   452  			}))
   453  
   454  			if test.nsInvalid {
   455  				if got := invalid.Value(); got != 1 {
   456  					t.Fatalf("got invalid = %d, want = 1", got)
   457  				}
   458  
   459  				if p, got := e.Read(); got {
   460  					t.Fatalf("unexpected response to an invalid NS = %+v", p.Pkt)
   461  				}
   462  
   463  				// If we expected the NS to be invalid, we have nothing else to check.
   464  				return
   465  			}
   466  
   467  			if got := invalid.Value(); got != 0 {
   468  				t.Fatalf("got invalid = %d, want = 0", got)
   469  			}
   470  
   471  			if test.performsLinkResolution {
   472  				clock.RunImmediatelyScheduledJobs()
   473  				p, got := e.Read()
   474  				if !got {
   475  					t.Fatal("expected an NDP NS response")
   476  				}
   477  
   478  				respNSDst := header.SolicitedNodeAddr(test.nsSrc)
   479  				var want stack.RouteInfo
   480  				want.NetProto = ProtocolNumber
   481  				want.RemoteLinkAddress = header.EthernetAddressFromMulticastIPv6Address(respNSDst)
   482  				if diff := cmp.Diff(want, p.Route, cmp.AllowUnexported(want)); diff != "" {
   483  					t.Errorf("route info mismatch (-want +got):\n%s", diff)
   484  				}
   485  
   486  				checker.IPv6(t, stack.PayloadSince(p.Pkt.NetworkHeader()),
   487  					checker.SrcAddr(nicAddr),
   488  					checker.DstAddr(respNSDst),
   489  					checker.TTL(header.NDPHopLimit),
   490  					checker.NDPNS(
   491  						checker.NDPNSTargetAddress(test.nsSrc),
   492  						checker.NDPNSOptions([]header.NDPOption{
   493  							header.NDPSourceLinkLayerAddressOption(nicLinkAddr),
   494  						}),
   495  					))
   496  
   497  				ser := header.NDPOptionsSerializer{
   498  					header.NDPTargetLinkLayerAddressOption(linkAddr1),
   499  				}
   500  				ndpNASize := header.ICMPv6NeighborAdvertMinimumSize + ser.Length()
   501  				hdr := buffer.NewPrependable(header.IPv6MinimumSize + ndpNASize)
   502  				pkt := header.ICMPv6(hdr.Prepend(ndpNASize))
   503  				pkt.SetType(header.ICMPv6NeighborAdvert)
   504  				na := header.NDPNeighborAdvert(pkt.MessageBody())
   505  				na.SetSolicitedFlag(true)
   506  				na.SetOverrideFlag(true)
   507  				na.SetTargetAddress(test.nsSrc)
   508  				na.Options().Serialize(ser)
   509  				pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
   510  					Header: pkt,
   511  					Src:    test.nsSrc,
   512  					Dst:    nicAddr,
   513  				}))
   514  				payloadLength := hdr.UsedLength()
   515  				ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
   516  				ip.Encode(&header.IPv6Fields{
   517  					PayloadLength:     uint16(payloadLength),
   518  					TransportProtocol: header.ICMPv6ProtocolNumber,
   519  					HopLimit:          header.NDPHopLimit,
   520  					SrcAddr:           test.nsSrc,
   521  					DstAddr:           nicAddr,
   522  				})
   523  				e.InjectLinkAddr(ProtocolNumber, "", stack.NewPacketBuffer(stack.PacketBufferOptions{
   524  					Data: hdr.View().ToVectorisedView(),
   525  				}))
   526  			}
   527  
   528  			clock.RunImmediatelyScheduledJobs()
   529  			p, got := e.Read()
   530  			if !got {
   531  				t.Fatal("expected an NDP NA response")
   532  			}
   533  
   534  			if p.Route.LocalAddress != test.naSrc {
   535  				t.Errorf("got p.Route.LocalAddress = %s, want = %s", p.Route.LocalAddress, test.naSrc)
   536  			}
   537  			if p.Route.LocalLinkAddress != nicLinkAddr {
   538  				t.Errorf("p.Route.LocalLinkAddress = %s, want = %s", p.Route.LocalLinkAddress, nicLinkAddr)
   539  			}
   540  			if p.Route.RemoteAddress != test.naDst {
   541  				t.Errorf("got p.Route.RemoteAddress = %s, want = %s", p.Route.RemoteAddress, test.naDst)
   542  			}
   543  			if p.Route.RemoteLinkAddress != test.naDstLinkAddr {
   544  				t.Errorf("got p.Route.RemoteLinkAddress = %s, want = %s", p.Route.RemoteLinkAddress, test.naDstLinkAddr)
   545  			}
   546  
   547  			checker.IPv6(t, stack.PayloadSince(p.Pkt.NetworkHeader()),
   548  				checker.SrcAddr(test.naSrc),
   549  				checker.DstAddr(test.naDst),
   550  				checker.TTL(header.NDPHopLimit),
   551  				checker.NDPNA(
   552  					checker.NDPNASolicitedFlag(test.naSolicited),
   553  					checker.NDPNATargetAddress(nicAddr),
   554  					checker.NDPNAOptions([]header.NDPOption{
   555  						header.NDPTargetLinkLayerAddressOption(nicLinkAddr[:]),
   556  					}),
   557  				))
   558  		})
   559  	}
   560  }
   561  
   562  // TestNeighborAdvertisementWithTargetLinkLayerOption tests that receiving a
   563  // valid NDP NA message with the Target Link Layer Address option does not
   564  // result in a new entry in the neighbor cache for the target of the message.
   565  func TestNeighborAdvertisementWithTargetLinkLayerOption(t *testing.T) {
   566  	const nicID = 1
   567  
   568  	tests := []struct {
   569  		name    string
   570  		optsBuf []byte
   571  		isValid bool
   572  	}{
   573  		{
   574  			name:    "Valid",
   575  			optsBuf: []byte{2, 1, 2, 3, 4, 5, 6, 7},
   576  			isValid: true,
   577  		},
   578  		{
   579  			name:    "Too Small",
   580  			optsBuf: []byte{2, 1, 2, 3, 4, 5, 6},
   581  		},
   582  		{
   583  			name:    "Invalid Length",
   584  			optsBuf: []byte{2, 2, 2, 3, 4, 5, 6, 7},
   585  		},
   586  		{
   587  			name: "Multiple",
   588  			optsBuf: []byte{
   589  				2, 1, 2, 3, 4, 5, 6, 7,
   590  				2, 1, 2, 3, 4, 5, 6, 8,
   591  			},
   592  		},
   593  	}
   594  
   595  	for _, test := range tests {
   596  		t.Run(test.name, func(t *testing.T) {
   597  			s := stack.New(stack.Options{
   598  				NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
   599  			})
   600  			e := channel.New(0, 1280, linkAddr0)
   601  			e.LinkEPCapabilities |= stack.CapabilityResolutionRequired
   602  			if err := s.CreateNIC(nicID, e); err != nil {
   603  				t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
   604  			}
   605  			if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil {
   606  				t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, lladdr0, err)
   607  			}
   608  
   609  			ndpNASize := header.ICMPv6NeighborAdvertMinimumSize + len(test.optsBuf)
   610  			hdr := buffer.NewPrependable(header.IPv6MinimumSize + ndpNASize)
   611  			pkt := header.ICMPv6(hdr.Prepend(ndpNASize))
   612  			pkt.SetType(header.ICMPv6NeighborAdvert)
   613  			ns := header.NDPNeighborAdvert(pkt.MessageBody())
   614  			ns.SetTargetAddress(lladdr1)
   615  			opts := ns.Options()
   616  			copy(opts, test.optsBuf)
   617  			pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
   618  				Header: pkt,
   619  				Src:    lladdr1,
   620  				Dst:    lladdr0,
   621  			}))
   622  			payloadLength := hdr.UsedLength()
   623  			ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
   624  			ip.Encode(&header.IPv6Fields{
   625  				PayloadLength:     uint16(payloadLength),
   626  				TransportProtocol: header.ICMPv6ProtocolNumber,
   627  				HopLimit:          255,
   628  				SrcAddr:           lladdr1,
   629  				DstAddr:           lladdr0,
   630  			})
   631  
   632  			invalid := s.Stats().ICMP.V6.PacketsReceived.Invalid
   633  
   634  			// Invalid count should initially be 0.
   635  			if got := invalid.Value(); got != 0 {
   636  				t.Fatalf("got invalid = %d, want = 0", got)
   637  			}
   638  
   639  			e.InjectInbound(ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{
   640  				Data: hdr.View().ToVectorisedView(),
   641  			}))
   642  
   643  			neighbors, err := s.Neighbors(nicID, ProtocolNumber)
   644  			if err != nil {
   645  				t.Fatalf("s.Neighbors(%d, %d): %s", nicID, ProtocolNumber, err)
   646  			}
   647  
   648  			neighborByAddr := make(map[tcpip.Address]stack.NeighborEntry)
   649  			for _, n := range neighbors {
   650  				if existing, ok := neighborByAddr[n.Addr]; ok {
   651  					if diff := cmp.Diff(existing, n); diff != "" {
   652  						t.Fatalf("s.Neighbors(%d, %d) returned unexpected duplicate neighbor entry (-existing +got):\n%s", nicID, ProtocolNumber, diff)
   653  					}
   654  					t.Fatalf("s.Neighbors(%d, %d) returned unexpected duplicate neighbor entry: %#v", nicID, ProtocolNumber, existing)
   655  				}
   656  				neighborByAddr[n.Addr] = n
   657  			}
   658  
   659  			if neigh, ok := neighborByAddr[lladdr1]; ok {
   660  				t.Fatalf("unexpectedly got neighbor entry: %#v", neigh)
   661  			}
   662  
   663  			if test.isValid {
   664  				// Invalid count should not have increased.
   665  				if got := invalid.Value(); got != 0 {
   666  					t.Errorf("got invalid = %d, want = 0", got)
   667  				}
   668  			} else {
   669  				// Invalid count should have increased.
   670  				if got := invalid.Value(); got != 1 {
   671  					t.Errorf("got invalid = %d, want = 1", got)
   672  				}
   673  			}
   674  		})
   675  	}
   676  }
   677  
   678  func TestNDPValidation(t *testing.T) {
   679  	const nicID = 1
   680  
   681  	handleIPv6Payload := func(payload buffer.View, hopLimit uint8, atomicFragment bool, ep stack.NetworkEndpoint) {
   682  		var extHdrs header.IPv6ExtHdrSerializer
   683  		if atomicFragment {
   684  			extHdrs = append(extHdrs, &header.IPv6SerializableFragmentExtHdr{})
   685  		}
   686  		extHdrsLen := extHdrs.Length()
   687  
   688  		ip := buffer.NewView(header.IPv6MinimumSize + extHdrsLen)
   689  		header.IPv6(ip).Encode(&header.IPv6Fields{
   690  			PayloadLength:     uint16(len(payload) + extHdrsLen),
   691  			TransportProtocol: header.ICMPv6ProtocolNumber,
   692  			HopLimit:          hopLimit,
   693  			SrcAddr:           lladdr1,
   694  			DstAddr:           lladdr0,
   695  			ExtensionHeaders:  extHdrs,
   696  		})
   697  		vv := ip.ToVectorisedView()
   698  		vv.AppendView(payload)
   699  		ep.HandlePacket(stack.NewPacketBuffer(stack.PacketBufferOptions{
   700  			Data: vv,
   701  		}))
   702  	}
   703  
   704  	var tllData [header.NDPLinkLayerAddressSize]byte
   705  	header.NDPOptions(tllData[:]).Serialize(header.NDPOptionsSerializer{
   706  		header.NDPTargetLinkLayerAddressOption(linkAddr1),
   707  	})
   708  
   709  	var sllData [header.NDPLinkLayerAddressSize]byte
   710  	header.NDPOptions(sllData[:]).Serialize(header.NDPOptionsSerializer{
   711  		header.NDPSourceLinkLayerAddressOption(linkAddr1),
   712  	})
   713  
   714  	types := []struct {
   715  		name        string
   716  		typ         header.ICMPv6Type
   717  		size        int
   718  		extraData   []byte
   719  		statCounter func(tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter
   720  		routerOnly  bool
   721  	}{
   722  		{
   723  			name: "RouterSolicit",
   724  			typ:  header.ICMPv6RouterSolicit,
   725  			size: header.ICMPv6MinimumSize,
   726  			statCounter: func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   727  				return stats.RouterSolicit
   728  			},
   729  			routerOnly: true,
   730  		},
   731  		{
   732  			name: "RouterAdvert",
   733  			typ:  header.ICMPv6RouterAdvert,
   734  			size: header.ICMPv6HeaderSize + header.NDPRAMinimumSize,
   735  			statCounter: func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   736  				return stats.RouterAdvert
   737  			},
   738  		},
   739  		{
   740  			name:      "NeighborSolicit",
   741  			typ:       header.ICMPv6NeighborSolicit,
   742  			size:      header.ICMPv6NeighborSolicitMinimumSize,
   743  			extraData: sllData[:],
   744  			statCounter: func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   745  				return stats.NeighborSolicit
   746  			},
   747  		},
   748  		{
   749  			name:      "NeighborAdvert",
   750  			typ:       header.ICMPv6NeighborAdvert,
   751  			size:      header.ICMPv6NeighborAdvertMinimumSize,
   752  			extraData: tllData[:],
   753  			statCounter: func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   754  				return stats.NeighborAdvert
   755  			},
   756  		},
   757  		{
   758  			name: "RedirectMsg",
   759  			typ:  header.ICMPv6RedirectMsg,
   760  			size: header.ICMPv6MinimumSize,
   761  			statCounter: func(stats tcpip.ICMPv6ReceivedPacketStats) *tcpip.StatCounter {
   762  				return stats.RedirectMsg
   763  			},
   764  		},
   765  	}
   766  
   767  	subTests := []struct {
   768  		name           string
   769  		atomicFragment bool
   770  		hopLimit       uint8
   771  		code           header.ICMPv6Code
   772  		valid          bool
   773  	}{
   774  		{
   775  			name:           "Valid",
   776  			atomicFragment: false,
   777  			hopLimit:       header.NDPHopLimit,
   778  			code:           0,
   779  			valid:          true,
   780  		},
   781  		{
   782  			name:           "Fragmented",
   783  			atomicFragment: true,
   784  			hopLimit:       header.NDPHopLimit,
   785  			code:           0,
   786  			valid:          false,
   787  		},
   788  		{
   789  			name:           "Invalid hop limit",
   790  			atomicFragment: false,
   791  			hopLimit:       header.NDPHopLimit - 1,
   792  			code:           0,
   793  			valid:          false,
   794  		},
   795  		{
   796  			name:           "Invalid ICMPv6 code",
   797  			atomicFragment: false,
   798  			hopLimit:       header.NDPHopLimit,
   799  			code:           1,
   800  			valid:          false,
   801  		},
   802  	}
   803  
   804  	subnet, err := tcpip.NewSubnet(lladdr1, tcpip.AddressMask(strings.Repeat("\xff", len(lladdr0))))
   805  	if err != nil {
   806  		t.Fatal(err)
   807  	}
   808  
   809  	for _, typ := range types {
   810  		for _, isRouter := range []bool{false, true} {
   811  			name := typ.name
   812  			if isRouter {
   813  				name += " (Router)"
   814  			}
   815  
   816  			t.Run(name, func(t *testing.T) {
   817  				for _, test := range subTests {
   818  					t.Run(test.name, func(t *testing.T) {
   819  						s := stack.New(stack.Options{
   820  							NetworkProtocols:   []stack.NetworkProtocolFactory{NewProtocol},
   821  							TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol6},
   822  						})
   823  
   824  						if isRouter {
   825  							if err := s.SetForwardingDefaultAndAllNICs(ProtocolNumber, true); err != nil {
   826  								t.Fatalf("SetForwardingDefaultAndAllNICs(%d, true): %s", ProtocolNumber, err)
   827  							}
   828  						}
   829  
   830  						if err := s.CreateNIC(nicID, &stubLinkEndpoint{}); err != nil {
   831  							t.Fatalf("CreateNIC(%d, _): %s", nicID, err)
   832  						}
   833  
   834  						if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil {
   835  							t.Fatalf("AddAddress(%d, %d, %s): %s", nicID, ProtocolNumber, lladdr0, err)
   836  						}
   837  
   838  						ep, err := s.GetNetworkEndpoint(nicID, ProtocolNumber)
   839  						if err != nil {
   840  							t.Fatal("cannot find network endpoint instance for IPv6")
   841  						}
   842  
   843  						s.SetRouteTable([]tcpip.Route{{
   844  							Destination: subnet,
   845  							NIC:         nicID,
   846  						}})
   847  
   848  						stats := s.Stats().ICMP.V6.PacketsReceived
   849  						invalid := stats.Invalid
   850  						routerOnly := stats.RouterOnlyPacketsDroppedByHost
   851  						typStat := typ.statCounter(stats)
   852  
   853  						icmpH := header.ICMPv6(buffer.NewView(typ.size + len(typ.extraData)))
   854  						copy(icmpH[typ.size:], typ.extraData)
   855  						icmpH.SetType(typ.typ)
   856  						icmpH.SetCode(test.code)
   857  						icmpH.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
   858  							Header:      icmpH[:typ.size],
   859  							Src:         lladdr0,
   860  							Dst:         lladdr1,
   861  							PayloadCsum: header.Checksum(typ.extraData /* initial */, 0),
   862  							PayloadLen:  len(typ.extraData),
   863  						}))
   864  
   865  						// Rx count of the NDP message should initially be 0.
   866  						if got := typStat.Value(); got != 0 {
   867  							t.Errorf("got %s = %d, want = 0", typ.name, got)
   868  						}
   869  
   870  						// Invalid count should initially be 0.
   871  						if got := invalid.Value(); got != 0 {
   872  							t.Errorf("got invalid.Value() = %d, want = 0", got)
   873  						}
   874  
   875  						// Should initially not have dropped any packets.
   876  						if got := routerOnly.Value(); got != 0 {
   877  							t.Errorf("got routerOnly.Value() = %d, want = 0", got)
   878  						}
   879  
   880  						if t.Failed() {
   881  							t.FailNow()
   882  						}
   883  
   884  						handleIPv6Payload(buffer.View(icmpH), test.hopLimit, test.atomicFragment, ep)
   885  
   886  						// Rx count of the NDP packet should have increased.
   887  						if got := typStat.Value(); got != 1 {
   888  							t.Errorf("got %s = %d, want = 1", typ.name, got)
   889  						}
   890  
   891  						want := uint64(0)
   892  						if !test.valid {
   893  							// Invalid count should have increased.
   894  							want = 1
   895  						}
   896  						if got := invalid.Value(); got != want {
   897  							t.Errorf("got invalid.Value() = %d, want = %d", got, want)
   898  						}
   899  
   900  						want = 0
   901  						if test.valid && !isRouter && typ.routerOnly {
   902  							// Router only packets are expected to be dropped when operating
   903  							// as a host.
   904  							want = 1
   905  						}
   906  						if got := routerOnly.Value(); got != want {
   907  							t.Errorf("got routerOnly.Value() = %d, want = %d", got, want)
   908  						}
   909  					})
   910  				}
   911  			})
   912  		}
   913  	}
   914  }
   915  
   916  // TestNeighborAdvertisementValidation tests that the NIC validates received
   917  // Neighbor Advertisements.
   918  //
   919  // In particular, if the IP Destination Address is a multicast address, and the
   920  // Solicited flag is not zero, the Neighbor Advertisement is invalid and should
   921  // be discarded.
   922  func TestNeighborAdvertisementValidation(t *testing.T) {
   923  	tests := []struct {
   924  		name          string
   925  		ipDstAddr     tcpip.Address
   926  		solicitedFlag bool
   927  		valid         bool
   928  	}{
   929  		{
   930  			name:          "Multicast IP destination address with Solicited flag set",
   931  			ipDstAddr:     header.IPv6AllNodesMulticastAddress,
   932  			solicitedFlag: true,
   933  			valid:         false,
   934  		},
   935  		{
   936  			name:          "Multicast IP destination address with Solicited flag unset",
   937  			ipDstAddr:     header.IPv6AllNodesMulticastAddress,
   938  			solicitedFlag: false,
   939  			valid:         true,
   940  		},
   941  		{
   942  			name:          "Unicast IP destination address with Solicited flag set",
   943  			ipDstAddr:     lladdr0,
   944  			solicitedFlag: true,
   945  			valid:         true,
   946  		},
   947  		{
   948  			name:          "Unicast IP destination address with Solicited flag unset",
   949  			ipDstAddr:     lladdr0,
   950  			solicitedFlag: false,
   951  			valid:         true,
   952  		},
   953  	}
   954  
   955  	for _, test := range tests {
   956  		t.Run(test.name, func(t *testing.T) {
   957  			s := stack.New(stack.Options{
   958  				NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
   959  			})
   960  			e := channel.New(0, header.IPv6MinimumMTU, linkAddr0)
   961  			e.LinkEPCapabilities |= stack.CapabilityResolutionRequired
   962  			if err := s.CreateNIC(nicID, e); err != nil {
   963  				t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
   964  			}
   965  			if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil {
   966  				t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, lladdr0, err)
   967  			}
   968  
   969  			ndpNASize := header.ICMPv6NeighborAdvertMinimumSize
   970  			hdr := buffer.NewPrependable(header.IPv6MinimumSize + ndpNASize)
   971  			pkt := header.ICMPv6(hdr.Prepend(ndpNASize))
   972  			pkt.SetType(header.ICMPv6NeighborAdvert)
   973  			na := header.NDPNeighborAdvert(pkt.MessageBody())
   974  			na.SetTargetAddress(lladdr1)
   975  			na.SetSolicitedFlag(test.solicitedFlag)
   976  			pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
   977  				Header: pkt,
   978  				Src:    lladdr1,
   979  				Dst:    test.ipDstAddr,
   980  			}))
   981  			payloadLength := hdr.UsedLength()
   982  			ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
   983  			ip.Encode(&header.IPv6Fields{
   984  				PayloadLength:     uint16(payloadLength),
   985  				TransportProtocol: header.ICMPv6ProtocolNumber,
   986  				HopLimit:          255,
   987  				SrcAddr:           lladdr1,
   988  				DstAddr:           test.ipDstAddr,
   989  			})
   990  
   991  			stats := s.Stats().ICMP.V6.PacketsReceived
   992  			invalid := stats.Invalid
   993  			rxNA := stats.NeighborAdvert
   994  
   995  			if got := rxNA.Value(); got != 0 {
   996  				t.Fatalf("got rxNA = %d, want = 0", got)
   997  			}
   998  			if got := invalid.Value(); got != 0 {
   999  				t.Fatalf("got invalid = %d, want = 0", got)
  1000  			}
  1001  
  1002  			e.InjectInbound(header.IPv6ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{
  1003  				Data: hdr.View().ToVectorisedView(),
  1004  			}))
  1005  
  1006  			if got := rxNA.Value(); got != 1 {
  1007  				t.Fatalf("got rxNA = %d, want = 1", got)
  1008  			}
  1009  			var wantInvalid uint64 = 1
  1010  			if test.valid {
  1011  				wantInvalid = 0
  1012  			}
  1013  			if got := invalid.Value(); got != wantInvalid {
  1014  				t.Fatalf("got invalid = %d, want = %d", got, wantInvalid)
  1015  			}
  1016  			// As per RFC 4861 section 7.2.5:
  1017  			//   When a valid Neighbor Advertisement is received ...
  1018  			//   If no entry exists, the advertisement SHOULD be silently discarded.
  1019  			//   There is no need to create an entry if none exists, since the
  1020  			//   recipient has apparently not initiated any communication with the
  1021  			//   target.
  1022  			if neighbors, err := s.Neighbors(nicID, ProtocolNumber); err != nil {
  1023  				t.Fatalf("s.Neighbors(%d, %d): %s", nicID, ProtocolNumber, err)
  1024  			} else if len(neighbors) != 0 {
  1025  				t.Fatalf("got len(neighbors) = %d, want = 0; neighbors = %#v", len(neighbors), neighbors)
  1026  			}
  1027  		})
  1028  	}
  1029  }
  1030  
  1031  // TestRouterAdvertValidation tests that when the NIC is configured to handle
  1032  // NDP Router Advertisement packets, it validates the Router Advertisement
  1033  // properly before handling them.
  1034  func TestRouterAdvertValidation(t *testing.T) {
  1035  	tests := []struct {
  1036  		name            string
  1037  		src             tcpip.Address
  1038  		hopLimit        uint8
  1039  		code            header.ICMPv6Code
  1040  		ndpPayload      []byte
  1041  		expectedSuccess bool
  1042  	}{
  1043  		{
  1044  			"OK",
  1045  			lladdr0,
  1046  			255,
  1047  			0,
  1048  			[]byte{
  1049  				0, 0, 0, 0,
  1050  				0, 0, 0, 0,
  1051  				0, 0, 0, 0,
  1052  			},
  1053  			true,
  1054  		},
  1055  		{
  1056  			"NonLinkLocalSourceAddr",
  1057  			addr1,
  1058  			255,
  1059  			0,
  1060  			[]byte{
  1061  				0, 0, 0, 0,
  1062  				0, 0, 0, 0,
  1063  				0, 0, 0, 0,
  1064  			},
  1065  			false,
  1066  		},
  1067  		{
  1068  			"HopLimitNot255",
  1069  			lladdr0,
  1070  			254,
  1071  			0,
  1072  			[]byte{
  1073  				0, 0, 0, 0,
  1074  				0, 0, 0, 0,
  1075  				0, 0, 0, 0,
  1076  			},
  1077  			false,
  1078  		},
  1079  		{
  1080  			"NonZeroCode",
  1081  			lladdr0,
  1082  			255,
  1083  			1,
  1084  			[]byte{
  1085  				0, 0, 0, 0,
  1086  				0, 0, 0, 0,
  1087  				0, 0, 0, 0,
  1088  			},
  1089  			false,
  1090  		},
  1091  		{
  1092  			"NDPPayloadTooSmall",
  1093  			lladdr0,
  1094  			255,
  1095  			0,
  1096  			[]byte{
  1097  				0, 0, 0, 0,
  1098  				0, 0, 0, 0,
  1099  				0, 0, 0,
  1100  			},
  1101  			false,
  1102  		},
  1103  		{
  1104  			"OKWithOptions",
  1105  			lladdr0,
  1106  			255,
  1107  			0,
  1108  			[]byte{
  1109  				// RA payload
  1110  				0, 0, 0, 0,
  1111  				0, 0, 0, 0,
  1112  				0, 0, 0, 0,
  1113  
  1114  				// Option #1 (TargetLinkLayerAddress)
  1115  				2, 1, 0, 0, 0, 0, 0, 0,
  1116  
  1117  				// Option #2 (unrecognized)
  1118  				255, 1, 0, 0, 0, 0, 0, 0,
  1119  
  1120  				// Option #3 (PrefixInformation)
  1121  				3, 4, 0, 0, 0, 0, 0, 0,
  1122  				0, 0, 0, 0, 0, 0, 0, 0,
  1123  				0, 0, 0, 0, 0, 0, 0, 0,
  1124  				0, 0, 0, 0, 0, 0, 0, 0,
  1125  			},
  1126  			true,
  1127  		},
  1128  		{
  1129  			"OptionWithZeroLength",
  1130  			lladdr0,
  1131  			255,
  1132  			0,
  1133  			[]byte{
  1134  				// RA payload
  1135  				0, 0, 0, 0,
  1136  				0, 0, 0, 0,
  1137  				0, 0, 0, 0,
  1138  
  1139  				// Option #1 (TargetLinkLayerAddress)
  1140  				// Invalid as it has 0 length.
  1141  				2, 0, 0, 0, 0, 0, 0, 0,
  1142  
  1143  				// Option #2 (unrecognized)
  1144  				255, 1, 0, 0, 0, 0, 0, 0,
  1145  
  1146  				// Option #3 (PrefixInformation)
  1147  				3, 4, 0, 0, 0, 0, 0, 0,
  1148  				0, 0, 0, 0, 0, 0, 0, 0,
  1149  				0, 0, 0, 0, 0, 0, 0, 0,
  1150  				0, 0, 0, 0, 0, 0, 0, 0,
  1151  			},
  1152  			false,
  1153  		},
  1154  	}
  1155  
  1156  	for _, test := range tests {
  1157  		t.Run(test.name, func(t *testing.T) {
  1158  			e := channel.New(10, 1280, linkAddr1)
  1159  			e.LinkEPCapabilities |= stack.CapabilityResolutionRequired
  1160  			s := stack.New(stack.Options{
  1161  				NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
  1162  			})
  1163  
  1164  			if err := s.CreateNIC(1, e); err != nil {
  1165  				t.Fatalf("CreateNIC(_) = %s", err)
  1166  			}
  1167  
  1168  			icmpSize := header.ICMPv6HeaderSize + len(test.ndpPayload)
  1169  			hdr := buffer.NewPrependable(header.IPv6MinimumSize + icmpSize)
  1170  			pkt := header.ICMPv6(hdr.Prepend(icmpSize))
  1171  			pkt.SetType(header.ICMPv6RouterAdvert)
  1172  			pkt.SetCode(test.code)
  1173  			copy(pkt.MessageBody(), test.ndpPayload)
  1174  			payloadLength := hdr.UsedLength()
  1175  			pkt.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
  1176  				Header: pkt,
  1177  				Src:    test.src,
  1178  				Dst:    header.IPv6AllNodesMulticastAddress,
  1179  			}))
  1180  			ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
  1181  			ip.Encode(&header.IPv6Fields{
  1182  				PayloadLength:     uint16(payloadLength),
  1183  				TransportProtocol: icmp.ProtocolNumber6,
  1184  				HopLimit:          test.hopLimit,
  1185  				SrcAddr:           test.src,
  1186  				DstAddr:           header.IPv6AllNodesMulticastAddress,
  1187  			})
  1188  
  1189  			stats := s.Stats().ICMP.V6.PacketsReceived
  1190  			invalid := stats.Invalid
  1191  			rxRA := stats.RouterAdvert
  1192  
  1193  			if got := invalid.Value(); got != 0 {
  1194  				t.Fatalf("got invalid = %d, want = 0", got)
  1195  			}
  1196  			if got := rxRA.Value(); got != 0 {
  1197  				t.Fatalf("got rxRA = %d, want = 0", got)
  1198  			}
  1199  
  1200  			e.InjectInbound(header.IPv6ProtocolNumber, stack.NewPacketBuffer(stack.PacketBufferOptions{
  1201  				Data: hdr.View().ToVectorisedView(),
  1202  			}))
  1203  
  1204  			if got := rxRA.Value(); got != 1 {
  1205  				t.Fatalf("got rxRA = %d, want = 1", got)
  1206  			}
  1207  
  1208  			if test.expectedSuccess {
  1209  				if got := invalid.Value(); got != 0 {
  1210  					t.Fatalf("got invalid = %d, want = 0", got)
  1211  				}
  1212  			} else {
  1213  				if got := invalid.Value(); got != 1 {
  1214  					t.Fatalf("got invalid = %d, want = 1", got)
  1215  				}
  1216  			}
  1217  		})
  1218  	}
  1219  }
  1220  
  1221  // TestCheckDuplicateAddress checks that calls to CheckDuplicateAddress and DAD
  1222  // performed when adding new addresses do not interfere with each other.
  1223  func TestCheckDuplicateAddress(t *testing.T) {
  1224  	const nicID = 1
  1225  
  1226  	clock := faketime.NewManualClock()
  1227  	dadConfigs := stack.DADConfigurations{
  1228  		DupAddrDetectTransmits: 1,
  1229  		RetransmitTimer:        time.Second,
  1230  	}
  1231  
  1232  	nonces := [...][]byte{
  1233  		{1, 2, 3, 4, 5, 6},
  1234  		{7, 8, 9, 10, 11, 12},
  1235  	}
  1236  
  1237  	var secureRNGBytes []byte
  1238  	for _, n := range nonces {
  1239  		secureRNGBytes = append(secureRNGBytes, n...)
  1240  	}
  1241  	var secureRNG bytes.Reader
  1242  	secureRNG.Reset(secureRNGBytes[:])
  1243  	s := stack.New(stack.Options{
  1244  		Clock:      clock,
  1245  		RandSource: rand.NewSource(time.Now().UnixNano()),
  1246  		SecureRNG:  &secureRNG,
  1247  		NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocolWithOptions(Options{
  1248  			DADConfigs: dadConfigs,
  1249  		})},
  1250  	})
  1251  	// This test is expected to send at max 2 DAD messages. We allow an extra
  1252  	// packet to be stored to catch unexpected packets.
  1253  	e := channel.New(3, header.IPv6MinimumMTU, linkAddr0)
  1254  	e.LinkEPCapabilities |= stack.CapabilityResolutionRequired
  1255  	if err := s.CreateNIC(nicID, e); err != nil {
  1256  		t.Fatalf("CreateNIC(%d, _) = %s", nicID, err)
  1257  	}
  1258  
  1259  	dadPacketsSent := 0
  1260  	snmc := header.SolicitedNodeAddr(lladdr0)
  1261  	remoteLinkAddr := header.EthernetAddressFromMulticastIPv6Address(snmc)
  1262  	checkDADMsg := func() {
  1263  		clock.RunImmediatelyScheduledJobs()
  1264  		p, ok := e.Read()
  1265  		if !ok {
  1266  			t.Fatalf("expected %d-th DAD message", dadPacketsSent)
  1267  		}
  1268  
  1269  		if p.Proto != header.IPv6ProtocolNumber {
  1270  			t.Errorf("(i=%d) got p.Proto = %d, want = %d", dadPacketsSent, p.Proto, header.IPv6ProtocolNumber)
  1271  		}
  1272  
  1273  		if p.Route.RemoteLinkAddress != remoteLinkAddr {
  1274  			t.Errorf("(i=%d) got p.Route.RemoteLinkAddress = %s, want = %s", dadPacketsSent, p.Route.RemoteLinkAddress, remoteLinkAddr)
  1275  		}
  1276  
  1277  		checker.IPv6(t, stack.PayloadSince(p.Pkt.NetworkHeader()),
  1278  			checker.SrcAddr(header.IPv6Any),
  1279  			checker.DstAddr(snmc),
  1280  			checker.TTL(header.NDPHopLimit),
  1281  			checker.NDPNS(
  1282  				checker.NDPNSTargetAddress(lladdr0),
  1283  				checker.NDPNSOptions([]header.NDPOption{header.NDPNonceOption(nonces[dadPacketsSent])}),
  1284  			))
  1285  	}
  1286  	if err := s.AddAddress(nicID, ProtocolNumber, lladdr0); err != nil {
  1287  		t.Fatalf("AddAddress(%d, %d, %s) = %s", nicID, ProtocolNumber, lladdr0, err)
  1288  	}
  1289  	checkDADMsg()
  1290  
  1291  	// Start DAD for the address we just added.
  1292  	//
  1293  	// Even though the stack will perform DAD before the added address transitions
  1294  	// from tentative to assigned, this DAD request should be independent of that.
  1295  	ch := make(chan stack.DADResult, 3)
  1296  	dadRequestsMade := 1
  1297  	dadPacketsSent++
  1298  	if res, err := s.CheckDuplicateAddress(nicID, ProtocolNumber, lladdr0, func(r stack.DADResult) {
  1299  		ch <- r
  1300  	}); err != nil {
  1301  		t.Fatalf("s.CheckDuplicateAddress(%d, %d, %s, _): %s", nicID, ProtocolNumber, lladdr0, err)
  1302  	} else if res != stack.DADStarting {
  1303  		t.Fatalf("got s.CheckDuplicateAddress(%d, %d, %s, _) = %d, want = %d", nicID, ProtocolNumber, lladdr0, res, stack.DADStarting)
  1304  	}
  1305  	checkDADMsg()
  1306  
  1307  	// Remove the address and make sure our DAD request was not stopped.
  1308  	if err := s.RemoveAddress(nicID, lladdr0); err != nil {
  1309  		t.Fatalf("RemoveAddress(%d, %s): %s", nicID, lladdr0, err)
  1310  	}
  1311  	// Should not restart DAD since we already requested DAD above - the handler
  1312  	// should be called when the original request completes so we should not send
  1313  	// an extra DAD message here.
  1314  	dadRequestsMade++
  1315  	if res, err := s.CheckDuplicateAddress(nicID, ProtocolNumber, lladdr0, func(r stack.DADResult) {
  1316  		ch <- r
  1317  	}); err != nil {
  1318  		t.Fatalf("s.CheckDuplicateAddress(%d, %d, %s, _): %s", nicID, ProtocolNumber, lladdr0, err)
  1319  	} else if res != stack.DADAlreadyRunning {
  1320  		t.Fatalf("got s.CheckDuplicateAddress(%d, %d, %s, _) = %d, want = %d", nicID, ProtocolNumber, lladdr0, res, stack.DADAlreadyRunning)
  1321  	}
  1322  
  1323  	// Wait for DAD to complete.
  1324  	clock.Advance(time.Duration(dadConfigs.DupAddrDetectTransmits) * dadConfigs.RetransmitTimer)
  1325  	for i := 0; i < dadRequestsMade; i++ {
  1326  		if diff := cmp.Diff(&stack.DADSucceeded{}, <-ch); diff != "" {
  1327  			t.Errorf("(i=%d) DAD result mismatch (-want +got):\n%s", i, diff)
  1328  		}
  1329  	}
  1330  	// Should have no more results.
  1331  	select {
  1332  	case r := <-ch:
  1333  		t.Errorf("unexpectedly got an extra DAD result; r = %#v", r)
  1334  	default:
  1335  	}
  1336  
  1337  	// Should have no more packets.
  1338  	if p, ok := e.Read(); ok {
  1339  		t.Errorf("got unexpected packet = %#v", p)
  1340  	}
  1341  }