github.com/transparency-dev/armored-witness-applet@v0.1.1/third_party/dhcp/client.go (about) 1 // Copyright 2019 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 //go:build !lint 6 // +build !lint 7 8 package dhcp 9 10 import ( 11 "bytes" 12 "context" 13 "fmt" 14 "math/rand" 15 "net" 16 "sync/atomic" 17 "time" 18 19 "gvisor.dev/gvisor/pkg/tcpip" 20 "gvisor.dev/gvisor/pkg/tcpip/header" 21 "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" 22 "gvisor.dev/gvisor/pkg/tcpip/stack" 23 "gvisor.dev/gvisor/pkg/waiter" 24 "k8s.io/klog/v2" 25 ) 26 27 const ( 28 defaultLeaseLength Seconds = 12 * 3600 29 ) 30 31 type AcquiredFunc func(oldAddr, newAddr tcpip.AddressWithPrefix, cfg Config) 32 33 // Client is a DHCP client. 34 type Client struct { 35 stack *stack.Stack 36 37 // info holds the Client's state as type Info. 38 info atomic.Value 39 40 clientID string 41 hostname string 42 acquiredFunc AcquiredFunc 43 44 wq waiter.Queue 45 46 // Used to ensure that only one Run goroutine per interface may be 47 // permitted to run at a time. In certain cases, rapidly flapping the 48 // DHCP client on and off can cause a second instance of Run to start 49 // before the existing one has finished, which can violate invariants. 50 // At the time of writing, TestDhcpConfiguration was creating this 51 // scenario and causing panics. 52 sem chan struct{} 53 54 stats Stats 55 56 // Stubbable in test. 57 rand *rand.Rand 58 retransTimeout func(time.Duration) <-chan time.Time 59 acquire func(ctx context.Context, c *Client, info *Info) (Config, error) 60 now func() time.Time 61 } 62 63 type dhcpClientState uint8 64 65 const ( 66 initSelecting dhcpClientState = iota 67 bound 68 renewing 69 rebinding 70 ) 71 72 // Stats collects DHCP statistics per client. 73 type Stats struct { 74 InitAcquire tcpip.StatCounter 75 RenewAcquire tcpip.StatCounter 76 RebindAcquire tcpip.StatCounter 77 SendDiscovers tcpip.StatCounter 78 RecvOffers tcpip.StatCounter 79 SendRequests tcpip.StatCounter 80 RecvAcks tcpip.StatCounter 81 RecvNaks tcpip.StatCounter 82 SendDiscoverErrors tcpip.StatCounter 83 SendRequestErrors tcpip.StatCounter 84 RecvOfferErrors tcpip.StatCounter 85 RecvOfferUnexpectedType tcpip.StatCounter 86 RecvOfferOptsDecodeErrors tcpip.StatCounter 87 RecvOfferTimeout tcpip.StatCounter 88 RecvOfferAcquisitionTimeout tcpip.StatCounter 89 RecvAckErrors tcpip.StatCounter 90 RecvNakErrors tcpip.StatCounter 91 RecvAckOptsDecodeErrors tcpip.StatCounter 92 RecvAckAddrErrors tcpip.StatCounter 93 RecvAckUnexpectedType tcpip.StatCounter 94 RecvAckTimeout tcpip.StatCounter 95 RecvAckAcquisitionTimeout tcpip.StatCounter 96 } 97 98 type Info struct { 99 // NICID is the identifer to the associated NIC. 100 NICID tcpip.NICID 101 // LinkAddr is the link-address of the associated NIC. 102 LinkAddr tcpip.LinkAddress 103 // Acquisition is the duration within which a complete DHCP transaction must 104 // complete before timing out. 105 Acquisition time.Duration 106 // Backoff is the duration for which the client must wait before starting a 107 // new DHCP transaction after a failed transaction. 108 Backoff time.Duration 109 // Retransmission is the duration to wait before resending a DISCOVER or 110 // REQUEST within an active transaction. 111 Retransmission time.Duration 112 // Addr is the acquired network address. 113 Addr tcpip.AddressWithPrefix 114 // Server is the network address of the DHCP server. 115 Server tcpip.Address 116 // State is the DHCP client state. 117 State dhcpClientState 118 // OldAddr is the address reported in the last call to acquiredFunc. 119 OldAddr tcpip.AddressWithPrefix 120 } 121 122 // NewClient creates a DHCP client. 123 // 124 // acquiredFunc will be called after each DHCP acquisition, and is responsible 125 // for making necessary modifications to the stack state. 126 // 127 // TODO: use (*stack.Stack).NICInfo()[nicid].LinkAddress instead of passing 128 // linkAddr when broadcasting on multiple interfaces works. 129 func NewClient(s *stack.Stack, nicid tcpip.NICID, linkAddr tcpip.LinkAddress, clientID, hostname string, acquisition, backoff, retransmission time.Duration, acquiredFunc AcquiredFunc) *Client { 130 c := &Client{ 131 stack: s, 132 clientID: clientID, 133 hostname: hostname, 134 acquiredFunc: acquiredFunc, 135 sem: make(chan struct{}, 1), 136 rand: rand.New(rand.NewSource(time.Now().UnixNano())), 137 retransTimeout: time.After, 138 acquire: acquire, 139 now: time.Now, 140 } 141 c.info.Store(Info{ 142 NICID: nicid, 143 LinkAddr: linkAddr, 144 Acquisition: acquisition, 145 Retransmission: retransmission, 146 Backoff: backoff, 147 }) 148 return c 149 } 150 151 // Info returns a copy of the synchronized state of the Info. 152 func (c *Client) Info() Info { 153 return c.info.Load().(Info) 154 } 155 156 // Stats returns a reference to the Client`s stats. 157 func (c *Client) Stats() *Stats { 158 return &c.stats 159 } 160 161 // Run runs the DHCP client. 162 // 163 // The function periodically searches for a new IP address. 164 func (c *Client) Run(ctx context.Context) { 165 info := c.Info() 166 // For the initial iteration of the acquisition loop, the client should 167 // be in the initSelecting state, corresponding to the 168 // INIT->SELECTING->REQUESTING->BOUND state transition: 169 // https://tools.ietf.org/html/rfc2131#section-4.4 170 info.State = initSelecting 171 172 c.sem <- struct{}{} 173 defer func() { <-c.sem }() 174 defer func() { 175 klog.Warning("client is stopping, cleaning up") 176 c.cleanup(&info) 177 // cleanup mutates info. 178 c.info.Store(info) 179 }() 180 181 var leaseExpirationTime, renewTime, rebindTime time.Time 182 var timer *time.Timer 183 184 for { 185 if err := func() error { 186 acquisitionTimeout := info.Acquisition 187 188 // Adjust the timeout to make sure client is not stuck in retransmission 189 // when it should transition to the next state. This can only happen for 190 // two time-driven transitions: RENEW->REBIND, REBIND->INIT. 191 // 192 // Another time-driven transition BOUND->RENEW is not handled here because 193 // the client does not have to send out any request during BOUND. 194 switch s := info.State; s { 195 case initSelecting: 196 // Nothing to do. The client is initializing, no leases have been acquired. 197 // Thus no times are set for renew, rebind, and lease expiration. 198 c.stats.InitAcquire.Increment() 199 case renewing: 200 c.stats.RenewAcquire.Increment() 201 if tilRebind := time.Until(rebindTime); tilRebind < acquisitionTimeout { 202 acquisitionTimeout = tilRebind 203 } 204 case rebinding: 205 c.stats.RebindAcquire.Increment() 206 if tilLeaseExpire := time.Until(leaseExpirationTime); tilLeaseExpire < acquisitionTimeout { 207 acquisitionTimeout = tilLeaseExpire 208 } 209 default: 210 panic(fmt.Sprintf("unexpected state before acquire: %s", s)) 211 } 212 213 ctx, cancel := context.WithTimeout(ctx, acquisitionTimeout) 214 defer cancel() 215 216 cfg, err := c.acquire(ctx, c, &info) 217 if err != nil { 218 return err 219 } 220 221 { 222 leaseLength, renewTime, rebindTime := cfg.LeaseLength, cfg.RenewTime, cfg.RebindTime 223 if cfg.LeaseLength == 0 { 224 klog.Warningf("unspecified lease length, setting default=%s", defaultLeaseLength) 225 leaseLength = defaultLeaseLength 226 } 227 switch { 228 case cfg.LeaseLength != 0 && cfg.RenewTime >= cfg.LeaseLength: 229 klog.Warningf("invalid renewal time: renewing=%s, lease=%s", cfg.RenewTime, cfg.LeaseLength) 230 fallthrough 231 case cfg.RenewTime == 0: 232 // Based on RFC 2131 Sec. 4.4.5, this defaults to (0.5 * duration_of_lease). 233 renewTime = leaseLength / 2 234 } 235 switch { 236 case cfg.RenewTime != 0 && cfg.RebindTime <= cfg.RenewTime: 237 klog.Warningf("invalid rebinding time: rebinding=%s, renewing=%s", cfg.RebindTime, cfg.RenewTime) 238 fallthrough 239 case cfg.RebindTime == 0: 240 // Based on RFC 2131 Sec. 4.4.5, this defaults to (0.875 * duration_of_lease). 241 rebindTime = leaseLength * 875 / 1000 242 } 243 cfg.LeaseLength, cfg.RenewTime, cfg.RebindTime = leaseLength, renewTime, rebindTime 244 } 245 246 now := c.now() 247 leaseExpirationTime = now.Add(cfg.LeaseLength.Duration()) 248 renewTime = now.Add(cfg.RenewTime.Duration()) 249 rebindTime = now.Add(cfg.RebindTime.Duration()) 250 251 if fn := c.acquiredFunc; fn != nil { 252 fn(info.OldAddr, info.Addr, cfg) 253 } 254 info.OldAddr = info.Addr 255 info.State = bound 256 257 return nil 258 }(); err != nil { 259 if ctx.Err() != nil { 260 return 261 } 262 klog.V(1).Infof("%s; retrying", err) 263 } 264 265 // Synchronize info after attempt to acquire is complete. 266 c.info.Store(info) 267 268 // RFC 2131 Section 4.4.5 269 // https://tools.ietf.org/html/rfc2131#section-4.4.5 270 // 271 // T1 MUST be earlier than T2, which, in turn, MUST be earlier than 272 // the time at which the client's lease will expire. 273 var next dhcpClientState 274 var waitDuration time.Duration 275 switch now := c.now(); { 276 case !now.Before(leaseExpirationTime): 277 next = initSelecting 278 case !now.Before(rebindTime): 279 next = rebinding 280 case !now.Before(renewTime): 281 next = renewing 282 default: 283 switch s := info.State; s { 284 case renewing, rebinding: 285 // This means the client is stuck in a bad state, because if 286 // the timers are correctly set, previous cases should have matched. 287 panic(fmt.Sprintf("invalid client state %s, now=%s, leaseExpirationTime=%s, renewTime=%s, rebindTime=%s", s, now, leaseExpirationTime, renewTime, rebindTime)) 288 } 289 waitDuration = renewTime.Sub(now) 290 next = renewing 291 } 292 293 // No state transition occurred, the client is retrying. 294 if info.State == next { 295 waitDuration = info.Backoff 296 } 297 298 if timer == nil { 299 timer = time.NewTimer(waitDuration) 300 } else if waitDuration != 0 { 301 timer.Reset(waitDuration) 302 } 303 304 if info.State != next && next != renewing { 305 // Transition immediately for RENEW->REBIND, REBIND->INIT. 306 if ctx.Err() != nil { 307 return 308 } 309 } else { 310 select { 311 case <-ctx.Done(): 312 return 313 case <-timer.C: 314 } 315 } 316 317 if info.State != initSelecting && next == initSelecting { 318 klog.Warning("lease time expired, cleaning up") 319 c.cleanup(&info) 320 } 321 322 info.State = next 323 324 // Synchronize info after any state updates. 325 c.info.Store(info) 326 } 327 } 328 329 func (c *Client) cleanup(info *Info) { 330 if info.OldAddr == (tcpip.AddressWithPrefix{}) { 331 return 332 } 333 334 // Remove the old address and configuration. 335 if fn := c.acquiredFunc; fn != nil { 336 fn(info.OldAddr, tcpip.AddressWithPrefix{}, Config{}) 337 } 338 info.OldAddr = tcpip.AddressWithPrefix{} 339 } 340 341 const maxBackoff = 64 * time.Second 342 343 // Exponential backoff calculates the backoff delay for this iteration (0-indexed) of retransmission. 344 // 345 // RFC 2131 section 4.1 346 // https://tools.ietf.org/html/rfc2131#section-4.1 347 // 348 // The delay between retransmissions SHOULD be 349 // chosen to allow sufficient time for replies from the server to be 350 // delivered based on the characteristics of the internetwork between 351 // the client and the server. For example, in a 10Mb/sec Ethernet 352 // internetwork, the delay before the first retransmission SHOULD be 4 353 // seconds randomized by the value of a uniform random number chosen 354 // from the range -1 to +1. Clients with clocks that provide resolution 355 // granularity of less than one second may choose a non-integer 356 // randomization value. The delay before the next retransmission SHOULD 357 // be 8 seconds randomized by the value of a uniform number chosen from 358 // the range -1 to +1. The retransmission delay SHOULD be doubled with 359 // subsequent retransmissions up to a maximum of 64 seconds. 360 func (c *Client) exponentialBackoff(iteration uint) time.Duration { 361 jitter := time.Duration(c.rand.Int63n(int64(2*time.Second+1))) - time.Second // [-1s, +1s] 362 backoff := maxBackoff 363 // Guards against overflow. 364 if retransmission := c.Info().Retransmission; (maxBackoff/retransmission)>>iteration != 0 { 365 backoff = retransmission * (1 << iteration) 366 } 367 backoff += jitter 368 if backoff < 0 { 369 return 0 370 } 371 return backoff 372 } 373 374 func acquire(ctx context.Context, c *Client, info *Info) (Config, error) { 375 // https://tools.ietf.org/html/rfc2131#section-4.3.6 Client messages: 376 // 377 // --------------------------------------------------------------------- 378 // | |INIT-REBOOT |SELECTING |RENEWING |REBINDING | 379 // --------------------------------------------------------------------- 380 // |broad/unicast |broadcast |broadcast |unicast |broadcast | 381 // |server-ip |MUST NOT |MUST |MUST NOT |MUST NOT | 382 // |requested-ip |MUST |MUST |MUST NOT |MUST NOT | 383 // |ciaddr |zero |zero |IP address |IP address| 384 // --------------------------------------------------------------------- 385 bindAddress := tcpip.FullAddress{ 386 Addr: info.Addr.Address, 387 Port: ClientPort, 388 NIC: info.NICID, 389 } 390 writeOpts := tcpip.WriteOptions{ 391 To: &tcpip.FullAddress{ 392 Addr: header.IPv4Broadcast, 393 Port: ServerPort, 394 NIC: info.NICID, 395 }, 396 } 397 398 var sendEP tcpip.Endpoint 399 switch info.State { 400 case initSelecting: 401 bindAddress.Addr = header.IPv4Broadcast 402 403 protocolAddress := tcpip.ProtocolAddress{ 404 Protocol: ipv4.ProtocolNumber, 405 AddressWithPrefix: tcpip.AddressWithPrefix{ 406 Address: header.IPv4Any, 407 PrefixLen: 0, 408 }, 409 } 410 // The IPv4 unspecified/any address should never be used as a primary endpoint. 411 if err := c.stack.AddProtocolAddress(info.NICID, protocolAddress, stack.AddressProperties{PEB: stack.NeverPrimaryEndpoint}); err != nil { 412 panic(fmt.Sprintf("AddProtocolAddress(%d, %+v, NeverPrimaryEndpoint): %s", info.NICID, protocolAddress, err)) 413 } 414 defer func() { 415 if err := c.stack.RemoveAddress(info.NICID, protocolAddress.AddressWithPrefix.Address); err != nil { 416 panic(fmt.Sprintf("RemoveAddress(%d, %s): %s", info.NICID, protocolAddress.AddressWithPrefix.Address, err)) 417 } 418 }() 419 420 // Create a dedicated endpoint for writes with an unspecified source address 421 // so it can explicitly bind to the IPv4 unspecified address. We do this so 422 // ep (the endpoint we receive on) can bind to the IPv4 broadcast address 423 // which is where replies will be sent in response to packets sent from this 424 // endpoint. The write endpoint needs to explicitly bind to the unspecified 425 // address because it is marked as NeverPrimaryEndpoint and will not be used 426 // unless explicitly bound to. 427 var err tcpip.Error 428 sendEP, err = c.stack.NewEndpoint(header.UDPProtocolNumber, header.IPv4ProtocolNumber, &waiter.Queue{}) 429 if err != nil { 430 return Config{}, fmt.Errorf("stack.NewEndpoint(%d, %d, _): %s", header.UDPProtocolNumber, header.IPv4ProtocolNumber, err) 431 } 432 defer sendEP.Close() 433 sendEP.SocketOptions().SetBindToDevice(int32(info.NICID)) 434 sendBindAddress := bindAddress 435 sendBindAddress.Addr = header.IPv4Any 436 if err := sendEP.Bind(sendBindAddress); err != nil { 437 return Config{}, fmt.Errorf("send ep Bind(%+v): %s", sendBindAddress, err) 438 } 439 440 case renewing: 441 writeOpts.To.Addr = info.Server 442 case rebinding: 443 default: 444 panic(fmt.Sprintf("unknown client state: c.State=%s", info.State)) 445 } 446 ep, err := c.stack.NewEndpoint(header.UDPProtocolNumber, header.IPv4ProtocolNumber, &c.wq) 447 if err != nil { 448 return Config{}, fmt.Errorf("stack.NewEndpoint(): %s", err) 449 } 450 defer ep.Close() 451 452 // If we don't have a dedicated send endpoint, use ep. 453 if sendEP == nil { 454 sendEP = ep 455 } 456 457 // BindToDevice allows us to have multiple DHCP clients listening to the same 458 // IP address and port at the same time so long as the nic is unique. 459 sendEP.SocketOptions().SetBindToDevice(int32(info.NICID)) 460 if writeOpts.To.Addr == header.IPv4Broadcast { 461 sendEP.SocketOptions().SetBroadcast(true) 462 } 463 if err := ep.Bind(bindAddress); err != nil { 464 return Config{}, fmt.Errorf("Bind(%+v): %s", bindAddress, err) 465 } 466 467 we, ch := waiter.NewChannelEntry(waiter.EventIn) 468 c.wq.EventRegister(&we) 469 defer c.wq.EventUnregister(&we) 470 471 var xid [4]byte 472 if _, err := c.rand.Read(xid[:]); err != nil { 473 return Config{}, fmt.Errorf("c.rand.Read(): %w", err) 474 } 475 476 commonOpts := options{ 477 {optParamReq, []byte{ 478 1, // request subnet mask 479 3, // request router 480 15, // domain name 481 6, // domain name server 482 }}, 483 } 484 if c.clientID != "" { 485 commonOpts = append(commonOpts, option{optClientID, []byte(c.clientID)}) 486 } 487 if c.hostname != "" { 488 commonOpts = append(commonOpts, option{optHostname, []byte(c.hostname)}) 489 } 490 requestedAddr := info.Addr 491 if info.State == initSelecting { 492 discOpts := append(options{ 493 {optDHCPMsgType, []byte{byte(dhcpDISCOVER)}}, 494 }, commonOpts...) 495 if requestedAddr.Address.Len() != 0 { 496 discOpts = append(discOpts, option{optReqIPAddr, []byte(requestedAddr.Address.AsSlice())}) 497 } 498 // TODO(fxbug.dev/38166): Refactor retransmitDiscover and retransmitRequest 499 500 retransmitDiscover: 501 for i := uint(0); ; i++ { 502 if err := c.send( 503 ctx, 504 info, 505 sendEP, 506 discOpts, 507 writeOpts, 508 xid[:], 509 // DHCPDISCOVER is only performed when the client cannot receive unicast 510 // (i.e. it does not have an allocated IP address), so a broadcast reply 511 // is always requested, and the client's address is never supplied. 512 true, /* broadcast */ 513 false, /* ciaddr */ 514 ); err != nil { 515 c.stats.SendDiscoverErrors.Increment() 516 return Config{}, fmt.Errorf("%s: %w", dhcpDISCOVER, err) 517 } 518 c.stats.SendDiscovers.Increment() 519 520 // Receive a DHCPOFFER message from a responding DHCP server. 521 timeoutCh := c.retransTimeout(c.exponentialBackoff(i)) 522 for { 523 srcAddr, addr, opts, typ, timedOut, err := c.recv(ctx, ep, ch, xid[:], timeoutCh) 524 if err != nil { 525 if timedOut { 526 c.stats.RecvOfferAcquisitionTimeout.Increment() 527 } else { 528 c.stats.RecvOfferErrors.Increment() 529 } 530 return Config{}, fmt.Errorf("recv %s: %w", dhcpOFFER, err) 531 } 532 if timedOut { 533 c.stats.RecvOfferTimeout.Increment() 534 klog.V(1).Infof("recv timeout waiting for %s, retransmitting %s", dhcpOFFER, dhcpDISCOVER) 535 continue retransmitDiscover 536 } 537 538 if typ != dhcpOFFER { 539 c.stats.RecvOfferUnexpectedType.Increment() 540 klog.V(1).Infof("got DHCP type = %s, want = %s", typ, dhcpOFFER) 541 continue 542 } 543 c.stats.RecvOffers.Increment() 544 545 var cfg Config 546 if err := cfg.decode(opts); err != nil { 547 c.stats.RecvOfferOptsDecodeErrors.Increment() 548 return Config{}, fmt.Errorf("%s decode: %w", typ, err) 549 } 550 551 // We can overwrite the client's server notion, since there's no 552 // atomicity required for correctness. 553 // 554 // We do not perform sophisticated offer selection and instead merely 555 // select the first valid offer we receive. 556 info.Server = cfg.ServerAddress 557 558 if cfg.SubnetMask.Len() == 0 { 559 sn := info.Addr.Subnet() 560 cfg.SubnetMask = sn.Mask() 561 } 562 563 prefixLen, _ := net.IPMask(cfg.SubnetMask.AsSlice()).Size() 564 requestedAddr = tcpip.AddressWithPrefix{ 565 Address: addr, 566 PrefixLen: prefixLen, 567 } 568 569 klog.V(1).Infof("got %s from %s: Address=%s, server=%s, leaseLength=%s, renewTime=%s, rebindTime=%s", typ, srcAddr.Addr, requestedAddr, info.Server, cfg.LeaseLength, cfg.RenewTime, cfg.RebindTime) 570 571 break retransmitDiscover 572 } 573 } 574 } 575 576 reqOpts := append(options{ 577 {optDHCPMsgType, []byte{byte(dhcpREQUEST)}}, 578 }, commonOpts...) 579 if info.State == initSelecting { 580 reqOpts = append(reqOpts, 581 options{ 582 {optDHCPServer, info.Server.AsSlice()}, 583 {optReqIPAddr, requestedAddr.Address.AsSlice()}, 584 }...) 585 } 586 587 retransmitRequest: 588 for i := uint(0); ; i++ { 589 if err := c.send( 590 ctx, 591 info, 592 sendEP, 593 reqOpts, 594 writeOpts, 595 xid[:], 596 info.State == initSelecting, /* broadcast */ 597 info.State != initSelecting, /* ciaddr */ 598 ); err != nil { 599 c.stats.SendRequestErrors.Increment() 600 return Config{}, fmt.Errorf("%s: %w", dhcpREQUEST, err) 601 } 602 c.stats.SendRequests.Increment() 603 604 // Receive a DHCPACK/DHCPNAK from the server. 605 timeoutCh := c.retransTimeout(c.exponentialBackoff(i)) 606 for { 607 fromAddr, addr, opts, typ, timedOut, err := c.recv(ctx, ep, ch, xid[:], timeoutCh) 608 if err != nil { 609 if timedOut { 610 c.stats.RecvAckAcquisitionTimeout.Increment() 611 } else { 612 c.stats.RecvAckErrors.Increment() 613 } 614 return Config{}, fmt.Errorf("recv %s: %w", dhcpACK, err) 615 } 616 if timedOut { 617 c.stats.RecvAckTimeout.Increment() 618 klog.V(1).Infof("recv timeout waiting for %s, retransmitting %s", dhcpACK, dhcpREQUEST) 619 continue retransmitRequest 620 } 621 622 switch typ { 623 case dhcpACK: 624 var cfg Config 625 if err := cfg.decode(opts); err != nil { 626 c.stats.RecvAckOptsDecodeErrors.Increment() 627 return Config{}, fmt.Errorf("%s decode: %w", typ, err) 628 } 629 prefixLen, _ := net.IPMask(cfg.SubnetMask.AsSlice()).Size() 630 addr := tcpip.AddressWithPrefix{ 631 Address: addr, 632 PrefixLen: prefixLen, 633 } 634 if addr != requestedAddr { 635 c.stats.RecvAckAddrErrors.Increment() 636 return Config{}, fmt.Errorf("%s with unexpected address=%s expected=%s", typ, addr, requestedAddr) 637 } 638 c.stats.RecvAcks.Increment() 639 640 // Now that we've successfully acquired the address, update the client state. 641 info.Addr = requestedAddr 642 klog.V(1).Infof("got %s from %s with leaseLength=%s", typ, fromAddr.Addr, cfg.LeaseLength) 643 return cfg, nil 644 case dhcpNAK: 645 if msg := opts.message(); len(msg) != 0 { 646 c.stats.RecvNakErrors.Increment() 647 return Config{}, fmt.Errorf("%s: %x", typ, msg) 648 } 649 c.stats.RecvNaks.Increment() 650 return Config{}, fmt.Errorf("empty %s", typ) 651 default: 652 c.stats.RecvAckUnexpectedType.Increment() 653 klog.V(1).Infof("got DHCP type = %s from %s, want = %s or %s", typ, fromAddr.Addr, dhcpACK, dhcpNAK) 654 continue 655 } 656 } 657 } 658 } 659 660 func (c *Client) send(ctx context.Context, info *Info, ep tcpip.Endpoint, opts options, writeOpts tcpip.WriteOptions, xid []byte, broadcast, ciaddr bool) error { 661 h := make(hdr, headerBaseSize+opts.len()+1) 662 h.init() 663 h.setOp(opRequest) 664 copy(h.xidbytes(), xid) 665 if broadcast { 666 h.setBroadcast() 667 } 668 if ciaddr { 669 copy(h.ciaddr(), info.Addr.Address.AsSlice()) 670 } 671 672 copy(h.chaddr(), info.LinkAddr) 673 h.setOptions(opts) 674 675 typ, err := opts.dhcpMsgType() 676 if err != nil { 677 panic(err) 678 } 679 680 klog.V(1).Infof("send %s to %s:%d on NIC:%d (bcast=%t ciaddr=%t)", typ, writeOpts.To.Addr, writeOpts.To.Port, writeOpts.To.NIC, broadcast, ciaddr) 681 682 for { 683 payload := bytes.NewBuffer(h) 684 n, err := ep.Write(payload, writeOpts) 685 /* 686 if resCh != nil { 687 if err != tcpip.ErrNoLinkAddress { 688 panic(fmt.Sprintf("err=%v inconsistent with presence of resCh", err)) 689 } 690 select { 691 case <-resCh: 692 continue 693 case <-ctx.Done(): 694 return fmt.Errorf("client address resolution: %w", ctx.Err()) 695 } 696 } 697 */ 698 if _, ok := err.(*tcpip.ErrWouldBlock); ok { 699 panic(fmt.Sprintf("UDP writes are nonblocking; saw %d/%d", n, payload.Len())) 700 } 701 if err != nil { 702 return fmt.Errorf("client write: %s", err) 703 } 704 return nil 705 } 706 } 707 708 func (c *Client) recv(ctx context.Context, ep tcpip.Endpoint, ch <-chan struct{}, xid []byte, timeoutCh <-chan time.Time) (tcpip.FullAddress, tcpip.Address, options, dhcpMsgType, bool, error) { 709 for { 710 var buf bytes.Buffer 711 rRes, err := ep.Read(&buf, tcpip.ReadOptions{NeedRemoteAddr: true}) 712 if _, ok := err.(*tcpip.ErrWouldBlock); ok { 713 select { 714 case <-ch: 715 continue 716 case <-timeoutCh: 717 return tcpip.FullAddress{}, tcpip.Address{}, nil, 0, true, nil 718 case <-ctx.Done(): 719 return tcpip.FullAddress{}, tcpip.Address{}, nil, 0, true, fmt.Errorf("read: %w", ctx.Err()) 720 } 721 } 722 if err != nil { 723 return tcpip.FullAddress{}, tcpip.Address{}, nil, 0, false, fmt.Errorf("read: %s", err) 724 } 725 726 h := hdr(buf.Bytes()) 727 728 if !h.isValid() { 729 return tcpip.FullAddress{}, tcpip.Address{}, nil, 0, false, fmt.Errorf("invalid hdr: %x", h) 730 } 731 732 if op := h.op(); op != opReply { 733 return tcpip.FullAddress{}, tcpip.Address{}, nil, 0, false, fmt.Errorf("op-code=%s, want=%s", h, opReply) 734 } 735 736 if !bytes.Equal(h.xidbytes(), xid[:]) { 737 // This message is for another client, ignore silently. 738 continue 739 } 740 741 { 742 opts, err := h.options() 743 if err != nil { 744 return tcpip.FullAddress{}, tcpip.Address{}, nil, 0, false, fmt.Errorf("invalid options: %w", err) 745 } 746 747 typ, err := opts.dhcpMsgType() 748 if err != nil { 749 return tcpip.FullAddress{}, tcpip.Address{}, nil, 0, false, fmt.Errorf("invalid type: %w", err) 750 } 751 752 return rRes.RemoteAddr, tcpip.AddrFromSlice(h.yiaddr()), opts, typ, false, nil 753 } 754 } 755 }