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  }