github.com/sagernet/gvisor@v0.0.0-20240428053021-e691de28565f/pkg/tcpip/network/internal/ip/duplicate_address_detection.go (about) 1 // Copyright 2021 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 ip holds IPv4/IPv6 common utilities. 16 package ip 17 18 import ( 19 "bytes" 20 "fmt" 21 "io" 22 23 "github.com/sagernet/gvisor/pkg/sync" 24 "github.com/sagernet/gvisor/pkg/tcpip" 25 "github.com/sagernet/gvisor/pkg/tcpip/stack" 26 ) 27 28 type extendRequest int 29 30 const ( 31 notRequested extendRequest = iota 32 requested 33 extended 34 ) 35 36 type dadState struct { 37 nonce []byte 38 extendRequest extendRequest 39 40 done *bool 41 timer tcpip.Timer 42 43 completionHandlers []stack.DADCompletionHandler 44 } 45 46 // DADProtocol is a protocol whose core state machine can be represented by DAD. 47 type DADProtocol interface { 48 // SendDADMessage attempts to send a DAD probe message. 49 SendDADMessage(tcpip.Address, []byte) tcpip.Error 50 } 51 52 // DADOptions holds options for DAD. 53 type DADOptions struct { 54 Clock tcpip.Clock 55 SecureRNG io.Reader 56 NonceSize uint8 57 ExtendDADTransmits uint8 58 Protocol DADProtocol 59 NICID tcpip.NICID 60 } 61 62 // DAD performs duplicate address detection for addresses. 63 type DAD struct { 64 opts DADOptions 65 configs stack.DADConfigurations 66 67 protocolMU sync.Locker 68 addresses map[tcpip.Address]dadState 69 } 70 71 // Init initializes the DAD state. 72 // 73 // Must only be called once for the lifetime of d; Init will panic if it is 74 // called twice. 75 // 76 // The lock will only be taken when timers fire. 77 func (d *DAD) Init(protocolMU sync.Locker, configs stack.DADConfigurations, opts DADOptions) { 78 if d.addresses != nil { 79 panic("attempted to initialize DAD state twice") 80 } 81 82 if opts.NonceSize != 0 && opts.ExtendDADTransmits == 0 { 83 panic(fmt.Sprintf("given a non-zero value for NonceSize (%d) but zero for ExtendDADTransmits", opts.NonceSize)) 84 } 85 86 configs.Validate() 87 88 *d = DAD{ 89 opts: opts, 90 configs: configs, 91 protocolMU: protocolMU, 92 addresses: make(map[tcpip.Address]dadState), 93 } 94 } 95 96 // CheckDuplicateAddressLocked performs DAD for an address, calling the 97 // completion handler once DAD resolves. 98 // 99 // If DAD is already performing for the provided address, h will be called when 100 // the currently running process completes. 101 // 102 // Precondition: d.protocolMU must be locked. 103 func (d *DAD) CheckDuplicateAddressLocked(addr tcpip.Address, h stack.DADCompletionHandler) stack.DADCheckAddressDisposition { 104 if d.configs.DupAddrDetectTransmits == 0 { 105 return stack.DADDisabled 106 } 107 108 ret := stack.DADAlreadyRunning 109 s, ok := d.addresses[addr] 110 if !ok { 111 ret = stack.DADStarting 112 113 remaining := d.configs.DupAddrDetectTransmits 114 115 // Protected by d.protocolMU. 116 done := false 117 118 s = dadState{ 119 done: &done, 120 timer: d.opts.Clock.AfterFunc(0, func() { 121 dadDone := remaining == 0 122 123 nonce, earlyReturn := func() ([]byte, bool) { 124 d.protocolMU.Lock() 125 defer d.protocolMU.Unlock() 126 127 if done { 128 return nil, true 129 } 130 131 s, ok := d.addresses[addr] 132 if !ok { 133 panic(fmt.Sprintf("dad: timer fired but missing state for %s on NIC(%d)", addr, d.opts.NICID)) 134 } 135 136 // As per RFC 7527 section 4 137 // 138 // If any probe is looped back within RetransTimer milliseconds 139 // after having sent DupAddrDetectTransmits NS(DAD) messages, the 140 // interface continues with another MAX_MULTICAST_SOLICIT number of 141 // NS(DAD) messages transmitted RetransTimer milliseconds apart. 142 if dadDone && s.extendRequest == requested { 143 dadDone = false 144 remaining = d.opts.ExtendDADTransmits 145 s.extendRequest = extended 146 } 147 148 if !dadDone && d.opts.NonceSize != 0 { 149 if s.nonce == nil { 150 s.nonce = make([]byte, d.opts.NonceSize) 151 } 152 153 if n, err := io.ReadFull(d.opts.SecureRNG, s.nonce); err != nil { 154 panic(fmt.Sprintf("SecureRNG.Read(...): %s", err)) 155 } else if n != len(s.nonce) { 156 panic(fmt.Sprintf("expected to read %d bytes from secure RNG, only read %d bytes", len(s.nonce), n)) 157 } 158 } 159 160 d.addresses[addr] = s 161 return s.nonce, false 162 }() 163 if earlyReturn { 164 return 165 } 166 167 var err tcpip.Error 168 if !dadDone { 169 err = d.opts.Protocol.SendDADMessage(addr, nonce) 170 } 171 172 d.protocolMU.Lock() 173 defer d.protocolMU.Unlock() 174 175 if done { 176 return 177 } 178 179 s, ok := d.addresses[addr] 180 if !ok { 181 panic(fmt.Sprintf("dad: timer fired but missing state for %s on NIC(%d)", addr, d.opts.NICID)) 182 } 183 184 if !dadDone && err == nil { 185 remaining-- 186 s.timer.Reset(d.configs.RetransmitTimer) 187 return 188 } 189 190 // At this point we know that either DAD has resolved or we hit an error 191 // sending the last DAD message. Either way, clear the DAD state. 192 done = false 193 s.timer.Stop() 194 delete(d.addresses, addr) 195 196 var res stack.DADResult = &stack.DADSucceeded{} 197 if err != nil { 198 res = &stack.DADError{Err: err} 199 } 200 for _, h := range s.completionHandlers { 201 h(res) 202 } 203 }), 204 } 205 } 206 207 s.completionHandlers = append(s.completionHandlers, h) 208 d.addresses[addr] = s 209 return ret 210 } 211 212 // ExtendIfNonceEqualLockedDisposition enumerates the possible results from 213 // ExtendIfNonceEqualLocked. 214 type ExtendIfNonceEqualLockedDisposition int 215 216 const ( 217 // Extended indicates that the DAD process was extended. 218 Extended ExtendIfNonceEqualLockedDisposition = iota 219 220 // AlreadyExtended indicates that the DAD process was already extended. 221 AlreadyExtended 222 223 // NoDADStateFound indicates that DAD state was not found for the address. 224 NoDADStateFound 225 226 // NonceDisabled indicates that nonce values are not sent with DAD messages. 227 NonceDisabled 228 229 // NonceNotEqual indicates that the nonce value passed and the nonce in the 230 // last send DAD message are not equal. 231 NonceNotEqual 232 ) 233 234 // ExtendIfNonceEqualLocked extends the DAD process if the provided nonce is the 235 // same as the nonce sent in the last DAD message. 236 // 237 // Precondition: d.protocolMU must be locked. 238 func (d *DAD) ExtendIfNonceEqualLocked(addr tcpip.Address, nonce []byte) ExtendIfNonceEqualLockedDisposition { 239 s, ok := d.addresses[addr] 240 if !ok { 241 return NoDADStateFound 242 } 243 244 if d.opts.NonceSize == 0 { 245 return NonceDisabled 246 } 247 248 if s.extendRequest != notRequested { 249 return AlreadyExtended 250 } 251 252 // As per RFC 7527 section 4 253 // 254 // If any probe is looped back within RetransTimer milliseconds after having 255 // sent DupAddrDetectTransmits NS(DAD) messages, the interface continues 256 // with another MAX_MULTICAST_SOLICIT number of NS(DAD) messages transmitted 257 // RetransTimer milliseconds apart. 258 // 259 // If a DAD message has already been sent and the nonce value we observed is 260 // the same as the nonce value we last sent, then we assume our probe was 261 // looped back and request an extension to the DAD process. 262 // 263 // Note, the first DAD message is sent asynchronously so we need to make sure 264 // that we sent a DAD message by checking if we have a nonce value set. 265 if s.nonce != nil && bytes.Equal(s.nonce, nonce) { 266 s.extendRequest = requested 267 d.addresses[addr] = s 268 return Extended 269 } 270 271 return NonceNotEqual 272 } 273 274 // StopLocked stops a currently running DAD process. 275 // 276 // Precondition: d.protocolMU must be locked. 277 func (d *DAD) StopLocked(addr tcpip.Address, reason stack.DADResult) { 278 s, ok := d.addresses[addr] 279 if !ok { 280 return 281 } 282 283 *s.done = true 284 s.timer.Stop() 285 delete(d.addresses, addr) 286 287 for _, h := range s.completionHandlers { 288 h(reason) 289 } 290 } 291 292 // SetConfigsLocked sets the DAD configurations. 293 // 294 // Precondition: d.protocolMU must be locked. 295 func (d *DAD) SetConfigsLocked(c stack.DADConfigurations) { 296 c.Validate() 297 d.configs = c 298 }