github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/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/nicocha30/gvisor-ligolo/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 }