diff --git a/dhcpv4/client.go b/dhcpv4/client.go index 7d14553..65e91be 100644 --- a/dhcpv4/client.go +++ b/dhcpv4/client.go @@ -26,19 +26,41 @@ type Client struct { tIPLease uint32 currentXID uint32 state ClientState - offer [4]byte - svip [4]byte // OptServerIdentification. - siip [4]byte // SIAddr. - reqIP [4]byte - router [4]byte - subnet [4]byte - broadcast [4]byte - gateway [4]byte + offer addr4 + svip addr4 // OptServerIdentification. + siip addr4 // SIAddr. + reqIP addr4 + router addr4 + subnet addr4 + broadcast addr4 + gateway addr4 clientMAC [6]byte auxbuf [64]byte } +type addr4 struct { + addr [4]byte + valid bool +} + +func (a *addr4) unpack() ([4]byte, bool) { + return a.addr, a.valid +} + +func (a *addr4) setmaybe(data []byte) { + if len(data) == 4 { + a.set4([4]byte(data[:])) + } else { + a.valid = false + } +} + +func (a *addr4) set4(addr [4]byte) { + a.valid = true + a.addr = addr +} + type RequestConfig struct { RequestedAddr [4]byte ClientHardwareAddr [6]byte @@ -59,7 +81,7 @@ func (c *Client) BeginRequest(xid uint32, cfg RequestConfig) error { c.state = StateInit c.currentXID = xid c.reqHostname = cfg.Hostname - c.reqIP = cfg.RequestedAddr + c.reqIP = addr4{addr: cfg.RequestedAddr, valid: true} c.clientMAC = cfg.ClientHardwareAddr return nil } @@ -91,7 +113,7 @@ func (c *Client) setIP(b []byte, frameOffset int) { func (c *Client) Encapsulate(carrierFrame []byte, frameOffset int) (int, error) { if c.isClosed() { return 0, net.ErrClosed - } else if c.state == StateSelecting && c.offer == [4]byte{} { + } else if c.state == StateSelecting && !c.offer.valid { return 0, nil // No offer received yet. } else if c.state == StateBound { return 0, nil // Done! @@ -123,22 +145,22 @@ func (c *Client) Encapsulate(carrierFrame []byte, frameOffset int) (int, error) } n, _ = EncodeOption16(opts[numOpts:], OptMaximumMessageSize, uint16(maxlen)) numOpts += n - if c.reqIP != [4]byte{} { - n, _ = EncodeOption(opts[numOpts:], OptRequestedIPaddress, c.reqIP[:]...) + if !c.reqIP.valid { + n, _ = EncodeOption(opts[numOpts:], OptRequestedIPaddress, c.reqIP.addr[:]...) numOpts += n } nextState = StateSelecting case StateSelecting: - if c.offer == ([4]byte{}) { + if !c.offer.valid { return 0, nil // Offer not yet received. } // Send out request, we know we've received an offer by now. n, _ := EncodeOption(opts[numOpts:], OptMessageType, byte(MsgRequest)) numOpts += n - n, _ = EncodeOption(opts[numOpts:], OptRequestedIPaddress, c.offer[:]...) + n, _ = EncodeOption(opts[numOpts:], OptRequestedIPaddress, c.offer.addr[:]...) numOpts += n - n, _ = EncodeOption(opts[numOpts:], OptServerIdentification, c.svip[:]...) + n, _ = EncodeOption(opts[numOpts:], OptServerIdentification, c.svip.addr[:]...) numOpts += n nextState = StateRequesting @@ -193,11 +215,11 @@ func (c *Client) Demux(carrierData []byte, frameOffset int) error { switch c.state { case StateSelecting: - if msgType == MsgOffer && c.offer == [4]byte{} { + if msgType == MsgOffer && !c.offer.valid { // Lock in on this offer. - c.gateway = *frm.GIAddr() - c.offer = *frm.YIAddr() - c.siip = *frm.SIAddr() + c.gateway.set4(*frm.GIAddr()) + c.offer.set4(*frm.YIAddr()) + c.siip.set4(*frm.SIAddr()) } case StateRequesting: @@ -237,13 +259,13 @@ func (c *Client) setOptions(frm Frame) error { case OptRebindingTimeValue: c.tRebind = maybeU32(data) case OptServerIdentification: - c.svip = maybe4byte(data) + c.svip.setmaybe(data) case OptRouter: - c.router = maybe4byte(data) + c.router.setmaybe(data) case OptBroadcastAddress: - c.broadcast = maybe4byte(data) + c.broadcast.setmaybe(data) case OptSubnetMask: - c.subnet = maybe4byte(data) + c.subnet.setmaybe(data) case OptHostName: if len(data) < maxHostSize { @@ -271,7 +293,7 @@ func (c *Client) setHeader(frm Frame) { frm.SetHardware(1, 6, 0) frm.SetSecs(1) if c.state.HasIP() { - *frm.CIAddr() = c.offer + *frm.CIAddr() = c.offer.addr } if c.state == StateInit { siaddr := frm.SIAddr()[:] @@ -279,13 +301,13 @@ func (c *Client) setHeader(frm Frame) { siaddr[i] = 255 } } else { - if c.siip == [4]byte{} { - *frm.SIAddr() = c.svip + if !c.siip.valid { + *frm.SIAddr() = c.svip.addr } else { - *frm.SIAddr() = c.siip + *frm.SIAddr() = c.siip.addr } } - *frm.YIAddr() = c.offer + *frm.YIAddr() = c.offer.addr copy(frm.CHAddrAs6()[:], c.clientMAC[:]) frm.SetMagicCookie(MagicCookie) } @@ -302,11 +324,12 @@ func (c *Client) reset(xid uint32) { func (d *Client) State() ClientState { return d.state } -func (d *Client) BroadcastAddr() [4]byte { return d.broadcast } -func (d *Client) AssignedAddr() [4]byte { return d.offer } -func (d *Client) ServerAddr() [4]byte { return d.svip } -func (d *Client) RouterAddr() [4]byte { return d.router } -func (d *Client) GatewayAddr() [4]byte { return d.gateway } +func (d *Client) BroadcastAddr() ([4]byte, bool) { return d.broadcast.unpack() } +func (d *Client) AssignedAddr() ([4]byte, bool) { return d.offer.unpack() } +func (d *Client) ServerAddr() ([4]byte, bool) { return d.svip.unpack() } +func (d *Client) RouterAddr() ([4]byte, bool) { return d.router.unpack() } +func (d *Client) GatewayAddr() ([4]byte, bool) { return d.gateway.unpack() } +func (d *Client) Subnet() ([4]byte, bool) { return d.subnet.unpack() } func (d *Client) RebindingSeconds() uint32 { return d.tRebind } func (d *Client) RenewalSeconds() uint32 { return d.tRenew } func (d *Client) IPLeaseSeconds() uint32 { return d.tIPLease } @@ -318,11 +341,12 @@ func (d *Client) DNSServerFirst() netip.Addr { } return d.dns[0] } + func (d *Client) CIDRBits() uint8 { - if d.subnet == [4]byte{} { + if !d.subnet.valid { return 0 } - v := binary.BigEndian.Uint32(d.subnet[:]) + v := binary.BigEndian.Uint32(d.subnet.addr[:]) return 32 - uint8(bits.TrailingZeros32(v)) } diff --git a/examples/bridge/main.go b/examples/bridge/main.go index 63e31bf..3b00d6e 100644 --- a/examples/bridge/main.go +++ b/examples/bridge/main.go @@ -121,18 +121,29 @@ func run() (err error) { dhcpIsDone := stack.dhcp.State() == dhcpv4.StateBound if dhcpIsDone { state = stateInitARP - err = stack.ip.SetAddr(netip.AddrFrom4(stack.dhcp.AssignedAddr())) + assigned4, ok := stack.dhcp.AssignedAddr() + if !ok { + return errors.New("DHCP client address not assigned") + } + err = stack.ip.SetAddr(netip.AddrFrom4(assigned4)) if err != nil { return err } - err = stack.StartResolveHardwareAddress6(netip.AddrFrom4(stack.dhcp.RouterAddr())) + routeraddr, ok := stack.dhcp.RouterAddr() + if !ok { + return errors.New("DHCP router address not assigned") + } + err = stack.StartResolveHardwareAddress6(netip.AddrFrom4(routeraddr)) if err != nil { return err } } case stateInitARP: - router := stack.dhcp.RouterAddr() + router, ok := stack.dhcp.RouterAddr() + if !ok { + return errors.New("DHCP router address not assigned") + } hw, err := stack.ResultResolveHardwareAddress6(netip.AddrFrom4(router)) if err == nil { stack.link.SetGateway6(hw)