github.com/vpnishe/netstack@v1.10.6/tcpip/stack/ndp.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 stack
    16  
    17  import (
    18  	"fmt"
    19  	"log"
    20  	"time"
    21  
    22  	"github.com/vpnishe/netstack/tcpip"
    23  	"github.com/vpnishe/netstack/tcpip/buffer"
    24  	"github.com/vpnishe/netstack/tcpip/header"
    25  )
    26  
    27  const (
    28  	// defaultDupAddrDetectTransmits is the default number of NDP Neighbor
    29  	// Solicitation messages to send when doing Duplicate Address Detection
    30  	// for a tentative address.
    31  	//
    32  	// Default = 1 (from RFC 4862 section 5.1)
    33  	defaultDupAddrDetectTransmits = 1
    34  
    35  	// defaultRetransmitTimer is the default amount of time to wait between
    36  	// sending NDP Neighbor solicitation messages.
    37  	//
    38  	// Default = 1s (from RFC 4861 section 10).
    39  	defaultRetransmitTimer = time.Second
    40  
    41  	// defaultHandleRAs is the default configuration for whether or not to
    42  	// handle incoming Router Advertisements as a host.
    43  	//
    44  	// Default = true.
    45  	defaultHandleRAs = true
    46  
    47  	// defaultDiscoverDefaultRouters is the default configuration for
    48  	// whether or not to discover default routers from incoming Router
    49  	// Advertisements, as a host.
    50  	//
    51  	// Default = true.
    52  	defaultDiscoverDefaultRouters = true
    53  
    54  	// defaultDiscoverOnLinkPrefixes is the default configuration for
    55  	// whether or not to discover on-link prefixes from incoming Router
    56  	// Advertisements' Prefix Information option, as a host.
    57  	//
    58  	// Default = true.
    59  	defaultDiscoverOnLinkPrefixes = true
    60  
    61  	// minimumRetransmitTimer is the minimum amount of time to wait between
    62  	// sending NDP Neighbor solicitation messages. Note, RFC 4861 does
    63  	// not impose a minimum Retransmit Timer, but we do here to make sure
    64  	// the messages are not sent all at once. We also come to this value
    65  	// because in the RetransmitTimer field of a Router Advertisement, a
    66  	// value of 0 means unspecified, so the smallest valid value is 1.
    67  	// Note, the unit of the RetransmitTimer field in the Router
    68  	// Advertisement is milliseconds.
    69  	//
    70  	// Min = 1ms.
    71  	minimumRetransmitTimer = time.Millisecond
    72  
    73  	// MaxDiscoveredDefaultRouters is the maximum number of discovered
    74  	// default routers. The stack should stop discovering new routers after
    75  	// discovering MaxDiscoveredDefaultRouters routers.
    76  	//
    77  	// This value MUST be at minimum 2 as per RFC 4861 section 6.3.4, and
    78  	// SHOULD be more.
    79  	//
    80  	// Max = 10.
    81  	MaxDiscoveredDefaultRouters = 10
    82  
    83  	// MaxDiscoveredOnLinkPrefixes is the maximum number of discovered
    84  	// on-link prefixes. The stack should stop discovering new on-link
    85  	// prefixes after discovering MaxDiscoveredOnLinkPrefixes on-link
    86  	// prefixes.
    87  	//
    88  	// Max = 10.
    89  	MaxDiscoveredOnLinkPrefixes = 10
    90  )
    91  
    92  // NDPDispatcher is the interface integrators of netstack must implement to
    93  // receive and handle NDP related events.
    94  type NDPDispatcher interface {
    95  	// OnDuplicateAddressDetectionStatus will be called when the DAD process
    96  	// for an address (addr) on a NIC (with ID nicID) completes. resolved
    97  	// will be set to true if DAD completed successfully (no duplicate addr
    98  	// detected); false otherwise (addr was detected to be a duplicate on
    99  	// the link the NIC is a part of, or it was stopped for some other
   100  	// reason, such as the address being removed). If an error occured
   101  	// during DAD, err will be set and resolved must be ignored.
   102  	//
   103  	// This function is permitted to block indefinitely without interfering
   104  	// with the stack's operation.
   105  	OnDuplicateAddressDetectionStatus(nicID tcpip.NICID, addr tcpip.Address, resolved bool, err *tcpip.Error)
   106  
   107  	// OnDefaultRouterDiscovered will be called when a new default router is
   108  	// discovered. Implementations must return true along with a new valid
   109  	// route table if the newly discovered router should be remembered. If
   110  	// an implementation returns false, the second return value will be
   111  	// ignored.
   112  	//
   113  	// This function is not permitted to block indefinitely. This function
   114  	// is also not permitted to call into the stack.
   115  	OnDefaultRouterDiscovered(nicID tcpip.NICID, addr tcpip.Address) (bool, []tcpip.Route)
   116  
   117  	// OnDefaultRouterInvalidated will be called when a discovered default
   118  	// router is invalidated. Implementers must return a new valid route
   119  	// table.
   120  	//
   121  	// This function is not permitted to block indefinitely. This function
   122  	// is also not permitted to call into the stack.
   123  	OnDefaultRouterInvalidated(nicID tcpip.NICID, addr tcpip.Address) []tcpip.Route
   124  
   125  	// OnOnLinkPrefixDiscovered will be called when a new on-link prefix is
   126  	// discovered. Implementations must return true along with a new valid
   127  	// route table if the newly discovered on-link prefix should be
   128  	// remembered. If an implementation returns false, the second return
   129  	// value will be ignored.
   130  	//
   131  	// This function is not permitted to block indefinitely. This function
   132  	// is also not permitted to call into the stack.
   133  	OnOnLinkPrefixDiscovered(nicID tcpip.NICID, prefix tcpip.Subnet) (bool, []tcpip.Route)
   134  
   135  	// OnOnLinkPrefixInvalidated will be called when a discovered on-link
   136  	// prefix is invalidated. Implementers must return a new valid route
   137  	// table.
   138  	//
   139  	// This function is not permitted to block indefinitely. This function
   140  	// is also not permitted to call into the stack.
   141  	OnOnLinkPrefixInvalidated(nicID tcpip.NICID, prefix tcpip.Subnet) []tcpip.Route
   142  }
   143  
   144  // NDPConfigurations is the NDP configurations for the netstack.
   145  type NDPConfigurations struct {
   146  	// The number of Neighbor Solicitation messages to send when doing
   147  	// Duplicate Address Detection for a tentative address.
   148  	//
   149  	// Note, a value of zero effectively disables DAD.
   150  	DupAddrDetectTransmits uint8
   151  
   152  	// The amount of time to wait between sending Neighbor solicitation
   153  	// messages.
   154  	//
   155  	// Must be greater than 0.5s.
   156  	RetransmitTimer time.Duration
   157  
   158  	// HandleRAs determines whether or not Router Advertisements will be
   159  	// processed.
   160  	HandleRAs bool
   161  
   162  	// DiscoverDefaultRouters determines whether or not default routers will
   163  	// be discovered from Router Advertisements. This configuration is
   164  	// ignored if HandleRAs is false.
   165  	DiscoverDefaultRouters bool
   166  
   167  	// DiscoverOnLinkPrefixes determines whether or not on-link prefixes
   168  	// will be discovered from Router Advertisements' Prefix Information
   169  	// option. This configuration is ignored if HandleRAs is false.
   170  	DiscoverOnLinkPrefixes bool
   171  }
   172  
   173  // DefaultNDPConfigurations returns an NDPConfigurations populated with
   174  // default values.
   175  func DefaultNDPConfigurations() NDPConfigurations {
   176  	return NDPConfigurations{
   177  		DupAddrDetectTransmits: defaultDupAddrDetectTransmits,
   178  		RetransmitTimer:        defaultRetransmitTimer,
   179  		HandleRAs:              defaultHandleRAs,
   180  		DiscoverDefaultRouters: defaultDiscoverDefaultRouters,
   181  		DiscoverOnLinkPrefixes: defaultDiscoverOnLinkPrefixes,
   182  	}
   183  }
   184  
   185  // validate modifies an NDPConfigurations with valid values. If invalid values
   186  // are present in c, the corresponding default values will be used instead.
   187  //
   188  // If RetransmitTimer is less than minimumRetransmitTimer, then a value of
   189  // defaultRetransmitTimer will be used.
   190  func (c *NDPConfigurations) validate() {
   191  	if c.RetransmitTimer < minimumRetransmitTimer {
   192  		c.RetransmitTimer = defaultRetransmitTimer
   193  	}
   194  }
   195  
   196  // ndpState is the per-interface NDP state.
   197  type ndpState struct {
   198  	// The NIC this ndpState is for.
   199  	nic *NIC
   200  
   201  	// configs is the per-interface NDP configurations.
   202  	configs NDPConfigurations
   203  
   204  	// The DAD state to send the next NS message, or resolve the address.
   205  	dad map[tcpip.Address]dadState
   206  
   207  	// The default routers discovered through Router Advertisements.
   208  	defaultRouters map[tcpip.Address]defaultRouterState
   209  
   210  	// The on-link prefixes discovered through Router Advertisements' Prefix
   211  	// Information option.
   212  	onLinkPrefixes map[tcpip.Subnet]onLinkPrefixState
   213  }
   214  
   215  // dadState holds the Duplicate Address Detection timer and channel to signal
   216  // to the DAD goroutine that DAD should stop.
   217  type dadState struct {
   218  	// The DAD timer to send the next NS message, or resolve the address.
   219  	timer *time.Timer
   220  
   221  	// Used to let the DAD timer know that it has been stopped.
   222  	//
   223  	// Must only be read from or written to while protected by the lock of
   224  	// the NIC this dadState is associated with.
   225  	done *bool
   226  }
   227  
   228  // defaultRouterState holds data associated with a default router discovered by
   229  // a Router Advertisement (RA).
   230  type defaultRouterState struct {
   231  	invalidationTimer *time.Timer
   232  
   233  	// Used to inform the timer not to invalidate the default router (R) in
   234  	// a race condition (T1 is a goroutine that handles an RA from R and T2
   235  	// is the goroutine that handles R's invalidation timer firing):
   236  	//   T1: Receive a new RA from R
   237  	//   T1: Obtain the NIC's lock before processing the RA
   238  	//   T2: R's invalidation timer fires, and gets blocked on obtaining the
   239  	//       NIC's lock
   240  	//   T1: Refreshes/extends R's lifetime & releases NIC's lock
   241  	//   T2: Obtains NIC's lock & invalidates R immediately
   242  	//
   243  	// To resolve this, T1 will check to see if the timer already fired, and
   244  	// inform the timer using doNotInvalidate to not invalidate R, so that
   245  	// once T2 obtains the lock, it will see that it is set to true and do
   246  	// nothing further.
   247  	doNotInvalidate *bool
   248  }
   249  
   250  // onLinkPrefixState holds data associated with an on-link prefix discovered by
   251  // a Router Advertisement's Prefix Information option (PI) when the NDP
   252  // configurations was configured to do so.
   253  type onLinkPrefixState struct {
   254  	invalidationTimer *time.Timer
   255  
   256  	// Used to signal the timer not to invalidate the on-link prefix (P) in
   257  	// a race condition (T1 is a goroutine that handles a PI for P and T2
   258  	// is the goroutine that handles P's invalidation timer firing):
   259  	//   T1: Receive a new PI for P
   260  	//   T1: Obtain the NIC's lock before processing the PI
   261  	//   T2: P's invalidation timer fires, and gets blocked on obtaining the
   262  	//       NIC's lock
   263  	//   T1: Refreshes/extends P's lifetime & releases NIC's lock
   264  	//   T2: Obtains NIC's lock & invalidates P immediately
   265  	//
   266  	// To resolve this, T1 will check to see if the timer already fired, and
   267  	// inform the timer using doNotInvalidate to not invalidate P, so that
   268  	// once T2 obtains the lock, it will see that it is set to true and do
   269  	// nothing further.
   270  	doNotInvalidate *bool
   271  }
   272  
   273  // startDuplicateAddressDetection performs Duplicate Address Detection.
   274  //
   275  // This function must only be called by IPv6 addresses that are currently
   276  // tentative.
   277  //
   278  // The NIC that ndp belongs to MUST be locked.
   279  func (ndp *ndpState) startDuplicateAddressDetection(addr tcpip.Address, ref *referencedNetworkEndpoint) *tcpip.Error {
   280  	// addr must be a valid unicast IPv6 address.
   281  	if !header.IsV6UnicastAddress(addr) {
   282  		return tcpip.ErrAddressFamilyNotSupported
   283  	}
   284  
   285  	// Should not attempt to perform DAD on an address that is currently in
   286  	// the DAD process.
   287  	if _, ok := ndp.dad[addr]; ok {
   288  		// Should never happen because we should only ever call this
   289  		// function for newly created addresses. If we attemped to
   290  		// "add" an address that already existed, we would returned an
   291  		// error since we attempted to add a duplicate address, or its
   292  		// reference count would have been increased without doing the
   293  		// work that would have been done for an address that was brand
   294  		// new. See NIC.addPermanentAddressLocked.
   295  		panic(fmt.Sprintf("ndpdad: already performing DAD for addr %s on NIC(%d)", addr, ndp.nic.ID()))
   296  	}
   297  
   298  	remaining := ndp.configs.DupAddrDetectTransmits
   299  
   300  	{
   301  		done, err := ndp.doDuplicateAddressDetection(addr, remaining, ref)
   302  		if err != nil {
   303  			return err
   304  		}
   305  		if done {
   306  			return nil
   307  		}
   308  	}
   309  
   310  	remaining--
   311  
   312  	var done bool
   313  	var timer *time.Timer
   314  	timer = time.AfterFunc(ndp.configs.RetransmitTimer, func() {
   315  		var d bool
   316  		var err *tcpip.Error
   317  
   318  		// doDadIteration does a single iteration of the DAD loop.
   319  		//
   320  		// Returns true if the integrator needs to be informed of DAD
   321  		// completing.
   322  		doDadIteration := func() bool {
   323  			ndp.nic.mu.Lock()
   324  			defer ndp.nic.mu.Unlock()
   325  
   326  			if done {
   327  				// If we reach this point, it means that the DAD
   328  				// timer fired after another goroutine already
   329  				// obtained the NIC lock and stopped DAD before
   330  				// this function obtained the NIC lock. Simply
   331  				// return here and do nothing further.
   332  				return false
   333  			}
   334  
   335  			ref, ok := ndp.nic.endpoints[NetworkEndpointID{addr}]
   336  			if !ok {
   337  				// This should never happen.
   338  				// We should have an endpoint for addr since we
   339  				// are still performing DAD on it. If the
   340  				// endpoint does not exist, but we are doing DAD
   341  				// on it, then we started DAD at some point, but
   342  				// forgot to stop it when the endpoint was
   343  				// deleted.
   344  				panic(fmt.Sprintf("ndpdad: unrecognized addr %s for NIC(%d)", addr, ndp.nic.ID()))
   345  			}
   346  
   347  			d, err = ndp.doDuplicateAddressDetection(addr, remaining, ref)
   348  			if err != nil || d {
   349  				delete(ndp.dad, addr)
   350  
   351  				if err != nil {
   352  					log.Printf("ndpdad: Error occured during DAD iteration for addr (%s) on NIC(%d); err = %s", addr, ndp.nic.ID(), err)
   353  				}
   354  
   355  				// Let the integrator know DAD has completed.
   356  				return true
   357  			}
   358  
   359  			remaining--
   360  			timer.Reset(ndp.nic.stack.ndpConfigs.RetransmitTimer)
   361  			return false
   362  		}
   363  
   364  		if doDadIteration() && ndp.nic.stack.ndpDisp != nil {
   365  			ndp.nic.stack.ndpDisp.OnDuplicateAddressDetectionStatus(ndp.nic.ID(), addr, d, err)
   366  		}
   367  	})
   368  
   369  	ndp.dad[addr] = dadState{
   370  		timer: timer,
   371  		done:  &done,
   372  	}
   373  
   374  	return nil
   375  }
   376  
   377  // doDuplicateAddressDetection is called on every iteration of the timer, and
   378  // when DAD starts.
   379  //
   380  // It handles resolving the address (if there are no more NS to send), or
   381  // sending the next NS if there are more NS to send.
   382  //
   383  // This function must only be called by IPv6 addresses that are currently
   384  // tentative.
   385  //
   386  // The NIC that ndp belongs to (n) MUST be locked.
   387  //
   388  // Returns true if DAD has resolved; false if DAD is still ongoing.
   389  func (ndp *ndpState) doDuplicateAddressDetection(addr tcpip.Address, remaining uint8, ref *referencedNetworkEndpoint) (bool, *tcpip.Error) {
   390  	if ref.getKind() != permanentTentative {
   391  		// The endpoint should still be marked as tentative
   392  		// since we are still performing DAD on it.
   393  		panic(fmt.Sprintf("ndpdad: addr %s is not tentative on NIC(%d)", addr, ndp.nic.ID()))
   394  	}
   395  
   396  	if remaining == 0 {
   397  		// DAD has resolved.
   398  		ref.setKind(permanent)
   399  		return true, nil
   400  	}
   401  
   402  	// Send a new NS.
   403  	snmc := header.SolicitedNodeAddr(addr)
   404  	snmcRef, ok := ndp.nic.endpoints[NetworkEndpointID{snmc}]
   405  	if !ok {
   406  		// This should never happen as if we have the
   407  		// address, we should have the solicited-node
   408  		// address.
   409  		panic(fmt.Sprintf("ndpdad: NIC(%d) is not in the solicited-node multicast group (%s) but it has addr %s", ndp.nic.ID(), snmc, addr))
   410  	}
   411  
   412  	// Use the unspecified address as the source address when performing
   413  	// DAD.
   414  	r := makeRoute(header.IPv6ProtocolNumber, header.IPv6Any, snmc, ndp.nic.linkEP.LinkAddress(), snmcRef, false, false)
   415  
   416  	hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.ICMPv6NeighborSolicitMinimumSize)
   417  	pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborSolicitMinimumSize))
   418  	pkt.SetType(header.ICMPv6NeighborSolicit)
   419  	ns := header.NDPNeighborSolicit(pkt.NDPPayload())
   420  	ns.SetTargetAddress(addr)
   421  	pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
   422  
   423  	sent := r.Stats().ICMP.V6PacketsSent
   424  	if err := r.WritePacket(nil, NetworkHeaderParams{Protocol: header.ICMPv6ProtocolNumber, TTL: header.NDPHopLimit, TOS: DefaultTOS}, tcpip.PacketBuffer{
   425  		Header: hdr,
   426  	}); err != nil {
   427  		sent.Dropped.Increment()
   428  		return false, err
   429  	}
   430  	sent.NeighborSolicit.Increment()
   431  
   432  	return false, nil
   433  }
   434  
   435  // stopDuplicateAddressDetection ends a running Duplicate Address Detection
   436  // process. Note, this may leave the DAD process for a tentative address in
   437  // such a state forever, unless some other external event resolves the DAD
   438  // process (receiving an NA from the true owner of addr, or an NS for addr
   439  // (implying another node is attempting to use addr)). It is up to the caller
   440  // of this function to handle such a scenario. Normally, addr will be removed
   441  // from n right after this function returns or the address successfully
   442  // resolved.
   443  //
   444  // The NIC that ndp belongs to MUST be locked.
   445  func (ndp *ndpState) stopDuplicateAddressDetection(addr tcpip.Address) {
   446  	dad, ok := ndp.dad[addr]
   447  	if !ok {
   448  		// Not currently performing DAD on addr, just return.
   449  		return
   450  	}
   451  
   452  	if dad.timer != nil {
   453  		dad.timer.Stop()
   454  		dad.timer = nil
   455  
   456  		*dad.done = true
   457  		dad.done = nil
   458  	}
   459  
   460  	delete(ndp.dad, addr)
   461  
   462  	// Let the integrator know DAD did not resolve.
   463  	if ndp.nic.stack.ndpDisp != nil {
   464  		go ndp.nic.stack.ndpDisp.OnDuplicateAddressDetectionStatus(ndp.nic.ID(), addr, false, nil)
   465  	}
   466  }
   467  
   468  // handleRA handles a Router Advertisement message that arrived on the NIC
   469  // this ndp is for. Does nothing if the NIC is configured to not handle RAs.
   470  //
   471  // The NIC that ndp belongs to and its associated stack MUST be locked.
   472  func (ndp *ndpState) handleRA(ip tcpip.Address, ra header.NDPRouterAdvert) {
   473  	// Is the NIC configured to handle RAs at all?
   474  	//
   475  	// Currently, the stack does not determine router interface status on a
   476  	// per-interface basis; it is a stack-wide configuration, so we check
   477  	// stack's forwarding flag to determine if the NIC is a routing
   478  	// interface.
   479  	if !ndp.configs.HandleRAs || ndp.nic.stack.forwarding {
   480  		return
   481  	}
   482  
   483  	// Is the NIC configured to discover default routers?
   484  	if ndp.configs.DiscoverDefaultRouters {
   485  		rtr, ok := ndp.defaultRouters[ip]
   486  		rl := ra.RouterLifetime()
   487  		switch {
   488  		case !ok && rl != 0:
   489  			// This is a new default router we are discovering.
   490  			//
   491  			// Only remember it if we currently know about less than
   492  			// MaxDiscoveredDefaultRouters routers.
   493  			if len(ndp.defaultRouters) < MaxDiscoveredDefaultRouters {
   494  				ndp.rememberDefaultRouter(ip, rl)
   495  			}
   496  
   497  		case ok && rl != 0:
   498  			// This is an already discovered default router. Update
   499  			// the invalidation timer.
   500  			timer := rtr.invalidationTimer
   501  
   502  			// We should ALWAYS have an invalidation timer for a
   503  			// discovered router.
   504  			if timer == nil {
   505  				panic("ndphandlera: RA invalidation timer should not be nil")
   506  			}
   507  
   508  			if !timer.Stop() {
   509  				// If we reach this point, then we know the
   510  				// timer fired after we already took the NIC
   511  				// lock. Inform the timer not to invalidate the
   512  				// router when it obtains the lock as we just
   513  				// got a new RA that refreshes its lifetime to a
   514  				// non-zero value. See
   515  				// defaultRouterState.doNotInvalidate for more
   516  				// details.
   517  				*rtr.doNotInvalidate = true
   518  			}
   519  
   520  			timer.Reset(rl)
   521  
   522  		case ok && rl == 0:
   523  			// We know about the router but it is no longer to be
   524  			// used as a default router so invalidate it.
   525  			ndp.invalidateDefaultRouter(ip)
   526  		}
   527  	}
   528  
   529  	// TODO(b/141556115): Do (RetransTimer, ReachableTime)) Parameter
   530  	//                    Discovery.
   531  
   532  	// We know the options is valid as far as wire format is concerned since
   533  	// we got the Router Advertisement, as documented by this fn. Given this
   534  	// we do not check the iterator for errors on calls to Next.
   535  	it, _ := ra.Options().Iter(false)
   536  	for opt, done, _ := it.Next(); !done; opt, done, _ = it.Next() {
   537  		switch opt.Type() {
   538  		case header.NDPPrefixInformationType:
   539  			if !ndp.configs.DiscoverOnLinkPrefixes {
   540  				continue
   541  			}
   542  
   543  			pi := opt.(header.NDPPrefixInformation)
   544  
   545  			prefix := pi.Subnet()
   546  
   547  			// Is the prefix a link-local?
   548  			if header.IsV6LinkLocalAddress(prefix.ID()) {
   549  				// ...Yes, skip as per RFC 4861 section 6.3.4.
   550  				continue
   551  			}
   552  
   553  			// Is the Prefix Length 0?
   554  			if prefix.Prefix() == 0 {
   555  				// ...Yes, skip as this is an invalid prefix
   556  				// as all IPv6 addresses cannot be on-link.
   557  				continue
   558  			}
   559  
   560  			if !pi.OnLinkFlag() {
   561  				// Not on-link so don't "discover" it as an
   562  				// on-link prefix.
   563  				continue
   564  			}
   565  
   566  			prefixState, ok := ndp.onLinkPrefixes[prefix]
   567  			vl := pi.ValidLifetime()
   568  			switch {
   569  			case !ok && vl == 0:
   570  				// Don't know about this prefix but has a zero
   571  				// valid lifetime, so just ignore.
   572  				continue
   573  
   574  			case !ok && vl != 0:
   575  				// This is a new on-link prefix we are
   576  				// discovering.
   577  				//
   578  				// Only remember it if we currently know about
   579  				// less than MaxDiscoveredOnLinkPrefixes on-link
   580  				// prefixes.
   581  				if len(ndp.onLinkPrefixes) < MaxDiscoveredOnLinkPrefixes {
   582  					ndp.rememberOnLinkPrefix(prefix, vl)
   583  				}
   584  				continue
   585  
   586  			case ok && vl == 0:
   587  				// We know about the on-link prefix, but it is
   588  				// no longer to be considered on-link, so
   589  				// invalidate it.
   590  				ndp.invalidateOnLinkPrefix(prefix)
   591  				continue
   592  			}
   593  
   594  			// This is an already discovered on-link prefix with a
   595  			// new non-zero valid lifetime.
   596  			// Update the invalidation timer.
   597  			timer := prefixState.invalidationTimer
   598  
   599  			if timer == nil && vl >= header.NDPPrefixInformationInfiniteLifetime {
   600  				// Had infinite valid lifetime before and
   601  				// continues to have an invalid lifetime. Do
   602  				// nothing further.
   603  				continue
   604  			}
   605  
   606  			if timer != nil && !timer.Stop() {
   607  				// If we reach this point, then we know the
   608  				// timer already fired after we took the NIC
   609  				// lock. Inform the timer to not invalidate
   610  				// the prefix once it obtains the lock as we
   611  				// just got a new PI that refeshes its lifetime
   612  				// to a non-zero value. See
   613  				// onLinkPrefixState.doNotInvalidate for more
   614  				// details.
   615  				*prefixState.doNotInvalidate = true
   616  			}
   617  
   618  			if vl >= header.NDPPrefixInformationInfiniteLifetime {
   619  				// Prefix is now valid forever so we don't need
   620  				// an invalidation timer.
   621  				prefixState.invalidationTimer = nil
   622  				ndp.onLinkPrefixes[prefix] = prefixState
   623  				continue
   624  			}
   625  
   626  			if timer != nil {
   627  				// We already have a timer so just reset it to
   628  				// expire after the new valid lifetime.
   629  				timer.Reset(vl)
   630  				continue
   631  			}
   632  
   633  			// We do not have a timer so just create a new one.
   634  			prefixState.invalidationTimer = ndp.prefixInvalidationCallback(prefix, vl, prefixState.doNotInvalidate)
   635  			ndp.onLinkPrefixes[prefix] = prefixState
   636  		}
   637  
   638  		// TODO(b/141556115): Do (MTU) Parameter Discovery.
   639  	}
   640  }
   641  
   642  // invalidateDefaultRouter invalidates a discovered default router.
   643  //
   644  // The NIC that ndp belongs to and its associated stack MUST be locked.
   645  func (ndp *ndpState) invalidateDefaultRouter(ip tcpip.Address) {
   646  	rtr, ok := ndp.defaultRouters[ip]
   647  
   648  	// Is the router still discovered?
   649  	if !ok {
   650  		// ...Nope, do nothing further.
   651  		return
   652  	}
   653  
   654  	rtr.invalidationTimer.Stop()
   655  	rtr.invalidationTimer = nil
   656  	*rtr.doNotInvalidate = true
   657  	rtr.doNotInvalidate = nil
   658  
   659  	delete(ndp.defaultRouters, ip)
   660  
   661  	// Let the integrator know a discovered default router is invalidated.
   662  	if ndp.nic.stack.ndpDisp != nil {
   663  		ndp.nic.stack.routeTable = ndp.nic.stack.ndpDisp.OnDefaultRouterInvalidated(ndp.nic.ID(), ip)
   664  	}
   665  }
   666  
   667  // rememberDefaultRouter remembers a newly discovered default router with IPv6
   668  // link-local address ip with lifetime rl.
   669  //
   670  // The router identified by ip MUST NOT already be known by the NIC.
   671  //
   672  // The NIC that ndp belongs to and its associated stack MUST be locked.
   673  func (ndp *ndpState) rememberDefaultRouter(ip tcpip.Address, rl time.Duration) {
   674  	if ndp.nic.stack.ndpDisp == nil {
   675  		return
   676  	}
   677  
   678  	// Inform the integrator when we discovered a default router.
   679  	remember, routeTable := ndp.nic.stack.ndpDisp.OnDefaultRouterDiscovered(ndp.nic.ID(), ip)
   680  	if !remember {
   681  		// Informed by the integrator to not remember the router, do
   682  		// nothing further.
   683  		return
   684  	}
   685  
   686  	// Used to signal the timer not to invalidate the default router (R) in
   687  	// a race condition. See defaultRouterState.doNotInvalidate for more
   688  	// details.
   689  	var doNotInvalidate bool
   690  
   691  	ndp.defaultRouters[ip] = defaultRouterState{
   692  		invalidationTimer: time.AfterFunc(rl, func() {
   693  			ndp.nic.stack.mu.Lock()
   694  			defer ndp.nic.stack.mu.Unlock()
   695  			ndp.nic.mu.Lock()
   696  			defer ndp.nic.mu.Unlock()
   697  
   698  			if doNotInvalidate {
   699  				doNotInvalidate = false
   700  				return
   701  			}
   702  
   703  			ndp.invalidateDefaultRouter(ip)
   704  		}),
   705  		doNotInvalidate: &doNotInvalidate,
   706  	}
   707  
   708  	ndp.nic.stack.routeTable = routeTable
   709  }
   710  
   711  // rememberOnLinkPrefix remembers a newly discovered on-link prefix with IPv6
   712  // address with prefix prefix with lifetime l.
   713  //
   714  // The prefix identified by prefix MUST NOT already be known.
   715  //
   716  // The NIC that ndp belongs to and its associated stack MUST be locked.
   717  func (ndp *ndpState) rememberOnLinkPrefix(prefix tcpip.Subnet, l time.Duration) {
   718  	if ndp.nic.stack.ndpDisp == nil {
   719  		return
   720  	}
   721  
   722  	// Inform the integrator when we discovered an on-link prefix.
   723  	remember, routeTable := ndp.nic.stack.ndpDisp.OnOnLinkPrefixDiscovered(ndp.nic.ID(), prefix)
   724  	if !remember {
   725  		// Informed by the integrator to not remember the prefix, do
   726  		// nothing further.
   727  		return
   728  	}
   729  
   730  	// Used to signal the timer not to invalidate the on-link prefix (P) in
   731  	// a race condition. See onLinkPrefixState.doNotInvalidate for more
   732  	// details.
   733  	var doNotInvalidate bool
   734  	var timer *time.Timer
   735  
   736  	// Only create a timer if the lifetime is not infinite.
   737  	if l < header.NDPPrefixInformationInfiniteLifetime {
   738  		timer = ndp.prefixInvalidationCallback(prefix, l, &doNotInvalidate)
   739  	}
   740  
   741  	ndp.onLinkPrefixes[prefix] = onLinkPrefixState{
   742  		invalidationTimer: timer,
   743  		doNotInvalidate:   &doNotInvalidate,
   744  	}
   745  
   746  	ndp.nic.stack.routeTable = routeTable
   747  }
   748  
   749  // invalidateOnLinkPrefix invalidates a discovered on-link prefix.
   750  //
   751  // The NIC that ndp belongs to and its associated stack MUST be locked.
   752  func (ndp *ndpState) invalidateOnLinkPrefix(prefix tcpip.Subnet) {
   753  	s, ok := ndp.onLinkPrefixes[prefix]
   754  
   755  	// Is the on-link prefix still discovered?
   756  	if !ok {
   757  		// ...Nope, do nothing further.
   758  		return
   759  	}
   760  
   761  	if s.invalidationTimer != nil {
   762  		s.invalidationTimer.Stop()
   763  		s.invalidationTimer = nil
   764  		*s.doNotInvalidate = true
   765  	}
   766  
   767  	s.doNotInvalidate = nil
   768  
   769  	delete(ndp.onLinkPrefixes, prefix)
   770  
   771  	// Let the integrator know a discovered on-link prefix is invalidated.
   772  	if ndp.nic.stack.ndpDisp != nil {
   773  		ndp.nic.stack.routeTable = ndp.nic.stack.ndpDisp.OnOnLinkPrefixInvalidated(ndp.nic.ID(), prefix)
   774  	}
   775  }
   776  
   777  // prefixInvalidationCallback returns a new on-link prefix invalidation timer
   778  // for prefix that fires after vl.
   779  //
   780  // doNotInvalidate is used to signal the timer when it fires at the same time
   781  // that a prefix's valid lifetime gets refreshed. See
   782  // onLinkPrefixState.doNotInvalidate for more details.
   783  func (ndp *ndpState) prefixInvalidationCallback(prefix tcpip.Subnet, vl time.Duration, doNotInvalidate *bool) *time.Timer {
   784  	return time.AfterFunc(vl, func() {
   785  		ndp.nic.stack.mu.Lock()
   786  		defer ndp.nic.stack.mu.Unlock()
   787  		ndp.nic.mu.Lock()
   788  		defer ndp.nic.mu.Unlock()
   789  
   790  		if *doNotInvalidate {
   791  			*doNotInvalidate = false
   792  			return
   793  		}
   794  
   795  		ndp.invalidateOnLinkPrefix(prefix)
   796  	})
   797  }