diff --git a/dhcpv4/client.go b/dhcpv4/client.go index 3c730fe..c544ddf 100644 --- a/dhcpv4/client.go +++ b/dhcpv4/client.go @@ -25,7 +25,8 @@ type Client struct { currentXID uint32 state ClientState offer [4]byte - svip [4]byte + svip [4]byte // OptServerIdentification. + siip [4]byte // SIAddr. reqIP [4]byte router [4]byte subnet [4]byte @@ -80,6 +81,18 @@ func (c *Client) setIP(b []byte, frameOffset int) { const ecnmask = 0b1100_0000 ifrm.SetToS(ecnmask) } + src := ifrm.SourceAddr() + for i := range src { + src[i] = 0 + } + dst := ifrm.DestinationAddr()[:] + if c.svip == ([4]byte{}) { + for i := range dst { + dst[i] = 255 + } + } else { + copy(dst, c.svip[:]) + } } func (c *Client) Encapsulate(carrierFrame []byte, frameOffset int) (int, error) { @@ -177,7 +190,7 @@ func (c *Client) Demux(carrierData []byte, frameOffset int) error { // Lock in on this offer. c.gateway = *frm.GIAddr() c.offer = *frm.YIAddr() - c.svip = *frm.SIAddr() + c.siip = *frm.SIAddr() } case StateRequesting: @@ -246,10 +259,16 @@ func (c *Client) setHeader(frm Frame) { frm.SetXID(c.currentXID) frm.SetHardware(1, 6, 0) frm.SetSecs(1) - if c.state == StateRequesting || c.state == StateSelecting || c.state == StateBound || c.state == StateRenewing { + if c.state.HasIP() { copy(frm.CIAddr()[:], c.offer[:]) } - copy(frm.SIAddr()[:], c.svip[:]) + if c.state == StateInit { + siaddr := frm.SIAddr()[:] + for i := range siaddr { + siaddr[i] = 255 + } + } + copy(frm.YIAddr()[:], c.offer[:]) copy(frm.CHAddrAs6()[:], c.clientMAC[:]) frm.SetMagicCookie(MagicCookie) diff --git a/dhcpv4/definitions.go b/dhcpv4/definitions.go index 24f19ad..c7c239d 100644 --- a/dhcpv4/definitions.go +++ b/dhcpv4/definitions.go @@ -30,6 +30,11 @@ const ( StateRebooting // rebooting ) +// HasIP returns true if the state indicates the Client has an IP address assigned by server. +func (state ClientState) HasIP() bool { + return state == StateBound || state == StateRenewing || state == StateRebinding +} + func AppendOption(dst []byte, opt OptNum, data ...byte) []byte { if len(data) > 255 { panic("option data too long") diff --git a/dhcpv4/frame.go b/dhcpv4/frame.go index d5d3297..cdecd4f 100644 --- a/dhcpv4/frame.go +++ b/dhcpv4/frame.go @@ -57,9 +57,11 @@ func (frm Frame) SetHardware(Type, Len, Ops uint8) { frm.buf[1], frm.buf[2], frm.buf[3] = Type, Len, Ops } +// XID is the transaction ID. Is unique and constant for a DHCP request/response exchange of packets. func (frm Frame) XID() uint32 { return binary.BigEndian.Uint32(frm.buf[4:8]) } func (frm Frame) SetXID(xid uint32) { binary.BigEndian.PutUint32(frm.buf[4:8], xid) } +// Secs is seconds elapsed. func (frm Frame) Secs() uint16 { return binary.BigEndian.Uint16(frm.buf[8:10]) } func (frm Frame) SetSecs(secs uint16) { binary.BigEndian.PutUint16(frm.buf[8:10], secs) } @@ -72,7 +74,7 @@ func (frm Frame) CIAddr() *[4]byte { return (*[4]byte)(frm.buf[12:16]) } -// YIAddr is the IP address offered by the server to the client. +// YIAddr is the IP address offered by the server to the client. Your (client) IP Address. func (frm Frame) YIAddr() *[4]byte { return (*[4]byte)(frm.buf[16:20]) } @@ -83,7 +85,7 @@ func (frm Frame) SIAddr() *[4]byte { return (*[4]byte)(frm.buf[20:24]) } -// GIAddr is the gateway IP address. +// GIAddr is the gateway IP address. Is also known as the Relay Agent IP Address. func (frm Frame) GIAddr() *[4]byte { return (*[4]byte)(frm.buf[24:28]) }