github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/tcpip/stack/nud.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package stack
    16  
    17  import (
    18  	"math"
    19  	"math/rand"
    20  	"sync"
    21  	"time"
    22  
    23  	"github.com/metacubex/gvisor/pkg/tcpip"
    24  )
    25  
    26  const (
    27  	// defaultBaseReachableTime is the default base duration for computing the
    28  	// random reachable time.
    29  	//
    30  	// Reachable time is the duration for which a neighbor is considered
    31  	// reachable after a positive reachability confirmation is received. It is a
    32  	// function of a uniformly distributed random value between the minimum and
    33  	// maximum random factors, multiplied by the base reachable time. Using a
    34  	// random component eliminates the possibility that Neighbor Unreachability
    35  	// Detection messages will synchronize with each other.
    36  	//
    37  	// Default taken from REACHABLE_TIME of RFC 4861 section 10.
    38  	defaultBaseReachableTime = 30 * time.Second
    39  
    40  	// minimumBaseReachableTime is the minimum base duration for computing the
    41  	// random reachable time.
    42  	//
    43  	// Minimum = 1ms
    44  	minimumBaseReachableTime = time.Millisecond
    45  
    46  	// defaultMinRandomFactor is the default minimum value of the random factor
    47  	// used for computing reachable time.
    48  	//
    49  	// Default taken from MIN_RANDOM_FACTOR of RFC 4861 section 10.
    50  	defaultMinRandomFactor = 0.5
    51  
    52  	// defaultMaxRandomFactor is the default maximum value of the random factor
    53  	// used for computing reachable time.
    54  	//
    55  	// The default value depends on the value of MinRandomFactor.
    56  	// If MinRandomFactor is less than MAX_RANDOM_FACTOR of RFC 4861 section 10,
    57  	// the value from the RFC will be used; otherwise, the default is
    58  	// MinRandomFactor multiplied by three.
    59  	defaultMaxRandomFactor = 1.5
    60  
    61  	// defaultRetransmitTimer is the default amount of time to wait between
    62  	// sending reachability probes.
    63  	//
    64  	// Default taken from RETRANS_TIMER of RFC 4861 section 10.
    65  	defaultRetransmitTimer = time.Second
    66  
    67  	// minimumRetransmitTimer is the minimum amount of time to wait between
    68  	// sending reachability probes.
    69  	//
    70  	// Note, RFC 4861 does not impose a minimum Retransmit Timer, but we do here
    71  	// to make sure the messages are not sent all at once. We also come to this
    72  	// value because in the RetransmitTimer field of a Router Advertisement, a
    73  	// value of 0 means unspecified, so the smallest valid value is 1. Note, the
    74  	// unit of the RetransmitTimer field in the Router Advertisement is
    75  	// milliseconds.
    76  	minimumRetransmitTimer = time.Millisecond
    77  
    78  	// defaultDelayFirstProbeTime is the default duration to wait for a
    79  	// non-Neighbor-Discovery related protocol to reconfirm reachability after
    80  	// entering the DELAY state. After this time, a reachability probe will be
    81  	// sent and the entry will transition to the PROBE state.
    82  	//
    83  	// Default taken from DELAY_FIRST_PROBE_TIME of RFC 4861 section 10.
    84  	defaultDelayFirstProbeTime = 5 * time.Second
    85  
    86  	// defaultMaxMulticastProbes is the default number of reachabililty probes
    87  	// to send before concluding negative reachability and deleting the neighbor
    88  	// entry from the INCOMPLETE state.
    89  	//
    90  	// Default taken from MAX_MULTICAST_SOLICIT of RFC 4861 section 10.
    91  	defaultMaxMulticastProbes = 3
    92  
    93  	// defaultMaxUnicastProbes is the default number of reachability probes to
    94  	// send before concluding retransmission from within the PROBE state should
    95  	// cease and the entry SHOULD be deleted.
    96  	//
    97  	// Default taken from MAX_UNICASE_SOLICIT of RFC 4861 section 10.
    98  	defaultMaxUnicastProbes = 3
    99  
   100  	// defaultMaxAnycastDelayTime is the default time in which the stack SHOULD
   101  	// delay sending a response for a random time between 0 and this time, if the
   102  	// target address is an anycast address.
   103  	//
   104  	// Default taken from MAX_ANYCAST_DELAY_TIME of RFC 4861 section 10.
   105  	defaultMaxAnycastDelayTime = time.Second
   106  
   107  	// defaultMaxReachbilityConfirmations is the default amount of unsolicited
   108  	// reachability confirmation messages a node MAY send to all-node multicast
   109  	// address when it determines its link-layer address has changed.
   110  	//
   111  	// Default taken from MAX_NEIGHBOR_ADVERTISEMENT of RFC 4861 section 10.
   112  	defaultMaxReachbilityConfirmations = 3
   113  )
   114  
   115  // NUDDispatcher is the interface integrators of netstack must implement to
   116  // receive and handle NUD related events.
   117  type NUDDispatcher interface {
   118  	// OnNeighborAdded will be called when a new entry is added to a NIC's (with
   119  	// ID nicID) neighbor table.
   120  	//
   121  	// This function is permitted to block indefinitely without interfering with
   122  	// the stack's operation.
   123  	//
   124  	// May be called concurrently.
   125  	OnNeighborAdded(tcpip.NICID, NeighborEntry)
   126  
   127  	// OnNeighborChanged will be called when an entry in a NIC's (with ID nicID)
   128  	// neighbor table changes state and/or link address.
   129  	//
   130  	// This function is permitted to block indefinitely without interfering with
   131  	// the stack's operation.
   132  	//
   133  	// May be called concurrently.
   134  	OnNeighborChanged(tcpip.NICID, NeighborEntry)
   135  
   136  	// OnNeighborRemoved will be called when an entry is removed from a NIC's
   137  	// (with ID nicID) neighbor table.
   138  	//
   139  	// This function is permitted to block indefinitely without interfering with
   140  	// the stack's operation.
   141  	//
   142  	// May be called concurrently.
   143  	OnNeighborRemoved(tcpip.NICID, NeighborEntry)
   144  }
   145  
   146  // ReachabilityConfirmationFlags describes the flags used within a reachability
   147  // confirmation (e.g. ARP reply or Neighbor Advertisement for ARP or NDP,
   148  // respectively).
   149  type ReachabilityConfirmationFlags struct {
   150  	// Solicited indicates that the advertisement was sent in response to a
   151  	// reachability probe.
   152  	Solicited bool
   153  
   154  	// Override indicates that the reachability confirmation should override an
   155  	// existing neighbor cache entry and update the cached link-layer address.
   156  	// When Override is not set the confirmation will not update a cached
   157  	// link-layer address, but will update an existing neighbor cache entry for
   158  	// which no link-layer address is known.
   159  	Override bool
   160  
   161  	// IsRouter indicates that the sender is a router.
   162  	IsRouter bool
   163  }
   164  
   165  // NUDConfigurations is the NUD configurations for the netstack. This is used
   166  // by the neighbor cache to operate the NUD state machine on each device in the
   167  // local network.
   168  type NUDConfigurations struct {
   169  	// BaseReachableTime is the base duration for computing the random reachable
   170  	// time.
   171  	//
   172  	// Reachable time is the duration for which a neighbor is considered
   173  	// reachable after a positive reachability confirmation is received. It is a
   174  	// function of uniformly distributed random value between minRandomFactor and
   175  	// maxRandomFactor multiplied by baseReachableTime. Using a random component
   176  	// eliminates the possibility that Neighbor Unreachability Detection messages
   177  	// will synchronize with each other.
   178  	//
   179  	// After this time, a neighbor entry will transition from REACHABLE to STALE
   180  	// state.
   181  	//
   182  	// Must be greater than 0.
   183  	BaseReachableTime time.Duration
   184  
   185  	// LearnBaseReachableTime enables learning BaseReachableTime during runtime
   186  	// from the neighbor discovery protocol, if supported.
   187  	//
   188  	// TODO(gvisor.dev/issue/2240): Implement this NUD configuration option.
   189  	LearnBaseReachableTime bool
   190  
   191  	// MinRandomFactor is the minimum value of the random factor used for
   192  	// computing reachable time.
   193  	//
   194  	// See BaseReachbleTime for more information on computing the reachable time.
   195  	//
   196  	// Must be greater than 0.
   197  	MinRandomFactor float32
   198  
   199  	// MaxRandomFactor is the maximum value of the random factor used for
   200  	// computing reachabile time.
   201  	//
   202  	// See BaseReachbleTime for more information on computing the reachable time.
   203  	//
   204  	// Must be great than or equal to MinRandomFactor.
   205  	MaxRandomFactor float32
   206  
   207  	// RetransmitTimer is the duration between retransmission of reachability
   208  	// probes in the PROBE state.
   209  	RetransmitTimer time.Duration
   210  
   211  	// LearnRetransmitTimer enables learning RetransmitTimer during runtime from
   212  	// the neighbor discovery protocol, if supported.
   213  	//
   214  	// TODO(gvisor.dev/issue/2241): Implement this NUD configuration option.
   215  	LearnRetransmitTimer bool
   216  
   217  	// DelayFirstProbeTime is the duration to wait for a non-Neighbor-Discovery
   218  	// related protocol to reconfirm reachability after entering the DELAY state.
   219  	// After this time, a reachability probe will be sent and the entry will
   220  	// transition to the PROBE state.
   221  	//
   222  	// Must be greater than 0.
   223  	DelayFirstProbeTime time.Duration
   224  
   225  	// MaxMulticastProbes is the number of reachability probes to send before
   226  	// concluding negative reachability and deleting the neighbor entry from the
   227  	// INCOMPLETE state.
   228  	//
   229  	// Must be greater than 0.
   230  	MaxMulticastProbes uint32
   231  
   232  	// MaxUnicastProbes is the number of reachability probes to send before
   233  	// concluding retransmission from within the PROBE state should cease and
   234  	// entry SHOULD be deleted.
   235  	//
   236  	// Must be greater than 0.
   237  	MaxUnicastProbes uint32
   238  
   239  	// MaxAnycastDelayTime is the time in which the stack SHOULD delay sending a
   240  	// response for a random time between 0 and this time, if the target address
   241  	// is an anycast address.
   242  	//
   243  	// TODO(gvisor.dev/issue/2242): Use this option when sending solicited
   244  	// neighbor confirmations to anycast addresses and proxying neighbor
   245  	// confirmations.
   246  	MaxAnycastDelayTime time.Duration
   247  
   248  	// MaxReachabilityConfirmations is the number of unsolicited reachability
   249  	// confirmation messages a node MAY send to all-node multicast address when
   250  	// it determines its link-layer address has changed.
   251  	//
   252  	// TODO(gvisor.dev/issue/2246): Discuss if implementation of this NUD
   253  	// configuration option is necessary.
   254  	MaxReachabilityConfirmations uint32
   255  }
   256  
   257  // DefaultNUDConfigurations returns a NUDConfigurations populated with default
   258  // values defined by RFC 4861 section 10.
   259  func DefaultNUDConfigurations() NUDConfigurations {
   260  	return NUDConfigurations{
   261  		BaseReachableTime:            defaultBaseReachableTime,
   262  		LearnBaseReachableTime:       true,
   263  		MinRandomFactor:              defaultMinRandomFactor,
   264  		MaxRandomFactor:              defaultMaxRandomFactor,
   265  		RetransmitTimer:              defaultRetransmitTimer,
   266  		LearnRetransmitTimer:         true,
   267  		DelayFirstProbeTime:          defaultDelayFirstProbeTime,
   268  		MaxMulticastProbes:           defaultMaxMulticastProbes,
   269  		MaxUnicastProbes:             defaultMaxUnicastProbes,
   270  		MaxAnycastDelayTime:          defaultMaxAnycastDelayTime,
   271  		MaxReachabilityConfirmations: defaultMaxReachbilityConfirmations,
   272  	}
   273  }
   274  
   275  // resetInvalidFields modifies an invalid NDPConfigurations with valid values.
   276  // If invalid values are present in c, the corresponding default values will be
   277  // used instead. This is needed to check, and conditionally fix, user-specified
   278  // NUDConfigurations.
   279  func (c *NUDConfigurations) resetInvalidFields() {
   280  	if c.BaseReachableTime < minimumBaseReachableTime {
   281  		c.BaseReachableTime = defaultBaseReachableTime
   282  	}
   283  	if c.MinRandomFactor <= 0 {
   284  		c.MinRandomFactor = defaultMinRandomFactor
   285  	}
   286  	if c.MaxRandomFactor < c.MinRandomFactor {
   287  		c.MaxRandomFactor = calcMaxRandomFactor(c.MinRandomFactor)
   288  	}
   289  	if c.RetransmitTimer < minimumRetransmitTimer {
   290  		c.RetransmitTimer = defaultRetransmitTimer
   291  	}
   292  	if c.DelayFirstProbeTime == 0 {
   293  		c.DelayFirstProbeTime = defaultDelayFirstProbeTime
   294  	}
   295  	if c.MaxMulticastProbes == 0 {
   296  		c.MaxMulticastProbes = defaultMaxMulticastProbes
   297  	}
   298  	if c.MaxUnicastProbes == 0 {
   299  		c.MaxUnicastProbes = defaultMaxUnicastProbes
   300  	}
   301  }
   302  
   303  // calcMaxRandomFactor calculates the maximum value of the random factor used
   304  // for computing reachable time. This function is necessary for when the
   305  // default specified in RFC 4861 section 10 is less than the current
   306  // MinRandomFactor.
   307  //
   308  // Assumes minRandomFactor is positive since validation of the minimum value
   309  // should come before the validation of the maximum.
   310  func calcMaxRandomFactor(minRandomFactor float32) float32 {
   311  	if minRandomFactor > defaultMaxRandomFactor {
   312  		return minRandomFactor * 3
   313  	}
   314  	return defaultMaxRandomFactor
   315  }
   316  
   317  // NUDState stores states needed for calculating reachable time.
   318  type NUDState struct {
   319  	clock tcpip.Clock
   320  	rng   *rand.Rand
   321  
   322  	mu struct {
   323  		sync.RWMutex
   324  
   325  		config NUDConfigurations
   326  
   327  		// reachableTime is the duration to wait for a REACHABLE entry to
   328  		// transition into STALE after inactivity. This value is calculated with
   329  		// the algorithm defined in RFC 4861 section 6.3.2.
   330  		reachableTime time.Duration
   331  
   332  		expiration            tcpip.MonotonicTime
   333  		prevBaseReachableTime time.Duration
   334  		prevMinRandomFactor   float32
   335  		prevMaxRandomFactor   float32
   336  	}
   337  }
   338  
   339  // NewNUDState returns new NUDState using c as configuration and the specified
   340  // random number generator for use in recomputing ReachableTime.
   341  func NewNUDState(c NUDConfigurations, clock tcpip.Clock, rng *rand.Rand) *NUDState {
   342  	s := &NUDState{
   343  		clock: clock,
   344  		rng:   rng,
   345  	}
   346  	s.mu.config = c
   347  	return s
   348  }
   349  
   350  // Config returns the NUD configuration.
   351  func (s *NUDState) Config() NUDConfigurations {
   352  	s.mu.RLock()
   353  	defer s.mu.RUnlock()
   354  	return s.mu.config
   355  }
   356  
   357  // SetConfig replaces the existing NUD configurations with c.
   358  func (s *NUDState) SetConfig(c NUDConfigurations) {
   359  	s.mu.Lock()
   360  	defer s.mu.Unlock()
   361  	s.mu.config = c
   362  }
   363  
   364  // ReachableTime returns the duration to wait for a REACHABLE entry to
   365  // transition into STALE after inactivity. This value is recalculated for new
   366  // values of BaseReachableTime, MinRandomFactor, and MaxRandomFactor using the
   367  // algorithm defined in RFC 4861 section 6.3.2.
   368  func (s *NUDState) ReachableTime() time.Duration {
   369  	s.mu.Lock()
   370  	defer s.mu.Unlock()
   371  
   372  	if s.clock.NowMonotonic().After(s.mu.expiration) ||
   373  		s.mu.config.BaseReachableTime != s.mu.prevBaseReachableTime ||
   374  		s.mu.config.MinRandomFactor != s.mu.prevMinRandomFactor ||
   375  		s.mu.config.MaxRandomFactor != s.mu.prevMaxRandomFactor {
   376  		s.recomputeReachableTimeLocked()
   377  	}
   378  	return s.mu.reachableTime
   379  }
   380  
   381  // recomputeReachableTimeLocked forces a recalculation of ReachableTime using
   382  // the algorithm defined in RFC 4861 section 6.3.2.
   383  //
   384  // This SHOULD automatically be invoked during certain situations, as per
   385  // RFC 4861 section 6.3.4:
   386  //
   387  //	If the received Reachable Time value is non-zero, the host SHOULD set its
   388  //	BaseReachableTime variable to the received value.  If the new value
   389  //	differs from the previous value, the host SHOULD re-compute a new random
   390  //	ReachableTime value.  ReachableTime is computed as a uniformly
   391  //	distributed random value between MIN_RANDOM_FACTOR and MAX_RANDOM_FACTOR
   392  //	times the BaseReachableTime.  Using a random component eliminates the
   393  //	possibility that Neighbor Unreachability Detection messages will
   394  //	synchronize with each other.
   395  //
   396  //	In most cases, the advertised Reachable Time value will be the same in
   397  //	consecutive Router Advertisements, and a host's BaseReachableTime rarely
   398  //	changes.  In such cases, an implementation SHOULD ensure that a new
   399  //	random value gets re-computed at least once every few hours.
   400  //
   401  // s.mu MUST be locked for writing.
   402  func (s *NUDState) recomputeReachableTimeLocked() {
   403  	s.mu.prevBaseReachableTime = s.mu.config.BaseReachableTime
   404  	s.mu.prevMinRandomFactor = s.mu.config.MinRandomFactor
   405  	s.mu.prevMaxRandomFactor = s.mu.config.MaxRandomFactor
   406  
   407  	randomFactor := s.mu.config.MinRandomFactor + s.rng.Float32()*(s.mu.config.MaxRandomFactor-s.mu.config.MinRandomFactor)
   408  
   409  	// Check for overflow, given that minRandomFactor and maxRandomFactor are
   410  	// guaranteed to be positive numbers.
   411  	if math.MaxInt64/randomFactor < float32(s.mu.config.BaseReachableTime) {
   412  		s.mu.reachableTime = time.Duration(math.MaxInt64)
   413  	} else if randomFactor == 1 {
   414  		// Avoid loss of precision when a large base reachable time is used.
   415  		s.mu.reachableTime = s.mu.config.BaseReachableTime
   416  	} else {
   417  		reachableTime := int64(float32(s.mu.config.BaseReachableTime) * randomFactor)
   418  		s.mu.reachableTime = time.Duration(reachableTime)
   419  	}
   420  
   421  	s.mu.expiration = s.clock.NowMonotonic().Add(2 * time.Hour)
   422  }