mirror of
https://github.com/soypat/lneto.git
synced 2026-08-22 07:29:04 +00:00
dhcp working!
This commit is contained in:
+25
-22
@@ -19,6 +19,7 @@ type Client struct {
|
||||
hostname []byte
|
||||
dns [][4]byte
|
||||
|
||||
svIPtos ipv4.ToS
|
||||
tRenew uint32
|
||||
tRebind uint32
|
||||
tIPLease uint32
|
||||
@@ -73,26 +74,16 @@ func (c *Client) setIP(b []byte, frameOffset int) {
|
||||
ifrm, _ := ipv4.NewFrame(b)
|
||||
ifrm.SetID((uint16(c.currentXID) ^ uint16(c.currentXID>>16)) + uint16(c.state))
|
||||
if c.state > StateInit {
|
||||
// TODO(soypat): Document why disabling ToS used by DHCP server may cause Request to fail.
|
||||
// Apparently server sets ToS=192. Uncommenting this line causes DHCP to fail on my setup.
|
||||
// If left fixed at 192, DHCP does not work.
|
||||
// If left fixed at 0, DHCP does not work.
|
||||
// Apparently ToS is a function of which state of DHCP one is in. Not sure why code below works.
|
||||
// Note: Not exactly needed for all servers.
|
||||
const ecnmask = 0b1100_0000
|
||||
ifrm.SetToS(ecnmask)
|
||||
// Match server ToS since some routers drop DHCP requests if no ToS set apparently?
|
||||
ifrm.SetToS(c.svIPtos)
|
||||
}
|
||||
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[:])
|
||||
for i := range dst {
|
||||
dst[i] = 255
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +94,8 @@ func (c *Client) Encapsulate(carrierFrame []byte, frameOffset int) (int, error)
|
||||
return 0, nil // No offer received yet.
|
||||
} else if c.state == StateBound {
|
||||
return 0, nil // Done!
|
||||
} else if c.state == StateRequesting {
|
||||
return 0, nil // Currently awaiting ACK.
|
||||
}
|
||||
dst := carrierFrame[frameOffset:]
|
||||
frm, err := NewFrame(dst)
|
||||
@@ -149,7 +142,7 @@ func (c *Client) Encapsulate(carrierFrame []byte, frameOffset int) (int, error)
|
||||
nextState = StateRequesting
|
||||
|
||||
default:
|
||||
return 0, errors.New("unhandled state")
|
||||
return 0, errors.New("unhandled state" + c.state.String())
|
||||
}
|
||||
n, _ := EncodeOption(opts[numOpts:], OptClientIdentifier, c.clientMAC[:]...)
|
||||
numOpts += n
|
||||
@@ -166,10 +159,11 @@ func (c *Client) Encapsulate(carrierFrame []byte, frameOffset int) (int, error)
|
||||
c.setHeader(frm)
|
||||
c.setIP(carrierFrame, frameOffset)
|
||||
c.state = nextState
|
||||
return optionsOffset + numOpts, nil
|
||||
return OptionsOffset + numOpts, nil
|
||||
}
|
||||
|
||||
func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
||||
fmt.Println("DEMUX DHCP")
|
||||
if c.isClosed() {
|
||||
return net.ErrClosed
|
||||
}
|
||||
@@ -212,13 +206,17 @@ func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
||||
default:
|
||||
return fmt.Errorf("dcpv4 unexpected state in recv %s", c.state.String())
|
||||
}
|
||||
if frameOffset > 28 && c.svIPtos == 0 {
|
||||
ifrm, _ := ipv4.NewFrame(carrierData)
|
||||
c.svIPtos = ifrm.ToS()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) getMessageType(frm Frame) MessageType {
|
||||
c.auxbuf[0] = 255
|
||||
ptrMsgType := &c.auxbuf[0]
|
||||
frm.ForEachOption(func(opt OptNum, data []byte) error {
|
||||
frm.ForEachOption(func(_ int, opt OptNum, data []byte) error {
|
||||
if len(data) == 1 {
|
||||
*ptrMsgType = data[0]
|
||||
return io.EOF
|
||||
@@ -229,7 +227,7 @@ func (c *Client) getMessageType(frm Frame) MessageType {
|
||||
}
|
||||
|
||||
func (c *Client) setOptions(frm Frame) error {
|
||||
return frm.ForEachOption(func(opt OptNum, data []byte) error {
|
||||
err := frm.ForEachOption(func(_ int, opt OptNum, data []byte) error {
|
||||
switch opt {
|
||||
case OptRenewTimeValue:
|
||||
c.tRenew = maybeU32(data)
|
||||
@@ -237,7 +235,6 @@ func (c *Client) setOptions(frm Frame) error {
|
||||
c.tIPLease = maybeU32(data)
|
||||
case OptRebindingTimeValue:
|
||||
c.tRebind = maybeU32(data)
|
||||
|
||||
case OptServerIdentification:
|
||||
c.svip = maybe4byte(data)
|
||||
case OptRouter:
|
||||
@@ -261,6 +258,7 @@ func (c *Client) setOptions(frm Frame) error {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) isClosed() bool { return c.state == 0 || c.currentXID == 0 }
|
||||
@@ -272,16 +270,21 @@ func (c *Client) setHeader(frm Frame) {
|
||||
frm.SetHardware(1, 6, 0)
|
||||
frm.SetSecs(1)
|
||||
if c.state.HasIP() {
|
||||
copy(frm.CIAddr()[:], c.offer[:])
|
||||
*frm.CIAddr() = c.offer
|
||||
}
|
||||
if c.state == StateInit {
|
||||
siaddr := frm.SIAddr()[:]
|
||||
for i := range siaddr {
|
||||
siaddr[i] = 255
|
||||
}
|
||||
} else {
|
||||
if c.siip == [4]byte{} {
|
||||
*frm.SIAddr() = c.svip
|
||||
} else {
|
||||
*frm.SIAddr() = c.siip
|
||||
}
|
||||
}
|
||||
|
||||
copy(frm.YIAddr()[:], c.offer[:])
|
||||
*frm.YIAddr() = c.offer
|
||||
copy(frm.CHAddrAs6()[:], c.clientMAC[:])
|
||||
frm.SetMagicCookie(MagicCookie)
|
||||
}
|
||||
|
||||
+27
-13
@@ -98,13 +98,18 @@ func TestExample(t *testing.T) {
|
||||
Hostname: clientHostname,
|
||||
})
|
||||
buf := make([]byte, 2048)
|
||||
buf2 := make([]byte, len(buf))
|
||||
n, err := cl.Encapsulate(buf, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if n <= 0 {
|
||||
t.Fatal("no data sent out by client after starting request")
|
||||
}
|
||||
|
||||
n, err = cl.Encapsulate(buf2, 0)
|
||||
if err != nil {
|
||||
t.Error("client encaps double tap after discover:", err)
|
||||
}
|
||||
// Fabricate server OFFER response.
|
||||
dfrm, _ := NewFrame(buf)
|
||||
dfrm.ClearHeader()
|
||||
dfrm.SetOp(OpReply)
|
||||
@@ -116,27 +121,36 @@ func TestExample(t *testing.T) {
|
||||
copy(dfrm.CHAddr()[:], clientHwaddr[:])
|
||||
dfrm.SetMagicCookie(MagicCookie)
|
||||
ntot := 0
|
||||
nopt, _ := EncodeOption(buf[optionsOffset+ntot:], OptMessageType, byte(MsgOffer))
|
||||
nopt, _ := EncodeOption(buf[OptionsOffset+ntot:], OptMessageType, byte(MsgOffer))
|
||||
ntot += nopt
|
||||
nopt, _ = EncodeOption(buf[optionsOffset+ntot:], OptServerIdentification, serverIP[:]...)
|
||||
nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptServerIdentification, serverIP[:]...)
|
||||
ntot += nopt
|
||||
nopt, _ = EncodeOption32(buf[optionsOffset+ntot:], OptServerIdentification, offerLease)
|
||||
nopt, _ = EncodeOption32(buf[OptionsOffset+ntot:], OptServerIdentification, offerLease)
|
||||
ntot += nopt
|
||||
nopt, _ = EncodeOption(buf[optionsOffset+ntot:], OptSubnetMask, subnetMask[:]...)
|
||||
nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptSubnetMask, subnetMask[:]...)
|
||||
ntot += nopt
|
||||
nopt, _ = EncodeOption(buf[optionsOffset+ntot:], OptRouter, routerAddr[:]...)
|
||||
nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptRouter, routerAddr[:]...)
|
||||
ntot += nopt
|
||||
nopt, _ = EncodeOption(buf[optionsOffset+ntot:], OptDNSServers, dnsAddr[:]...)
|
||||
nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptDNSServers, dnsAddr[:]...)
|
||||
ntot += nopt
|
||||
nopt, _ = EncodeOption(buf[optionsOffset+ntot:], OptEnd, dnsAddr[:]...)
|
||||
nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptEnd, dnsAddr[:]...)
|
||||
ntot += nopt
|
||||
|
||||
err = cl.Demux(buf[:optionsOffset+ntot], 0)
|
||||
err = cl.Demux(buf[:OptionsOffset+ntot], 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// frame := buf[:optionsOffset]
|
||||
// frame = AppendOption(frame, OptMessageType, byte(MsgOffer))
|
||||
// frame = AppendOption(frame, OptServerIdentification, serverIP[:]...)
|
||||
// frame = AppendOption(frame, OptIPAddressLeaseTime, serverIP[:]...)
|
||||
|
||||
n, err = cl.Encapsulate(buf[:], 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if n <= 0 {
|
||||
t.Fatal("no data written from client in response to offer")
|
||||
}
|
||||
n, err = cl.Encapsulate(buf[:], 0)
|
||||
if err != nil {
|
||||
t.Error("encapsulate double tap after request:", err)
|
||||
} else if n > 0 {
|
||||
t.Error("encapsulate double tap got data!", n)
|
||||
}
|
||||
}
|
||||
|
||||
+11
-7
@@ -17,7 +17,7 @@ const (
|
||||
// Expected Magic Cookie value.
|
||||
MagicCookie uint32 = 0x63825363
|
||||
// DHCP Options offset measured from the start of the UDP payload.
|
||||
optionsOffset = magicCookieOffset + 4
|
||||
OptionsOffset = magicCookieOffset + 4
|
||||
|
||||
DefaultClientPort = 68
|
||||
DefaultServerPort = 67
|
||||
@@ -26,12 +26,16 @@ const (
|
||||
// NewFrame returns a new DHCPv4 Frame with data set to buf.
|
||||
// An error is returned if the buffer size is smaller than 240.
|
||||
func NewFrame(buf []byte) (Frame, error) {
|
||||
if len(buf) < optionsOffset {
|
||||
if len(buf) < OptionsOffset {
|
||||
return Frame{}, errSmallFrame
|
||||
}
|
||||
return Frame{buf: buf}, nil
|
||||
}
|
||||
|
||||
func PayloadIsDHCPv4(payload []byte) bool {
|
||||
return len(payload) >= OptionsOffset && binary.BigEndian.Uint32(payload[magicCookieOffset:]) == MagicCookie
|
||||
}
|
||||
|
||||
// Frame encapsulates the raw data of a DHCP packet
|
||||
// and provides methods for manipulating, validating and
|
||||
// retrieving fields and payload data. See [RFC2131].
|
||||
@@ -43,7 +47,7 @@ type Frame struct {
|
||||
|
||||
// OptionsPayload returns the options portion of the DHCP frame. May be zero lengthed.
|
||||
func (frm Frame) OptionsPayload() []byte {
|
||||
return frm.buf[optionsOffset:]
|
||||
return frm.buf[OptionsOffset:]
|
||||
}
|
||||
|
||||
func (frm Frame) Op() Op { return Op(frm.buf[0]) }
|
||||
@@ -111,16 +115,16 @@ func (frm Frame) SetMagicCookie(cookie uint32) {
|
||||
|
||||
// ClearHeader zeros out the header contents.
|
||||
func (frm Frame) ClearHeader() {
|
||||
for i := range frm.buf[:optionsOffset] {
|
||||
for i := range frm.buf[:OptionsOffset] {
|
||||
frm.buf[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// ForEachOption iterates over all DHCPv4 options returning an error on a malformed option or when user provided callback returns an error.
|
||||
// If the user provided callback is nil then only option buffer validation is performed.
|
||||
func (frm Frame) ForEachOption(fn func(op OptNum, data []byte) error) error {
|
||||
func (frm Frame) ForEachOption(fn func(off int, op OptNum, data []byte) error) error {
|
||||
// Parse DHCP options.
|
||||
ptr := optionsOffset
|
||||
ptr := OptionsOffset
|
||||
if ptr > len(frm.buf) {
|
||||
return errSmallFrame
|
||||
} else if len(frm.buf[ptr:]) == 0 {
|
||||
@@ -141,7 +145,7 @@ func (frm Frame) ForEachOption(fn func(op OptNum, data []byte) error) error {
|
||||
optlen := frm.buf[ptr+1]
|
||||
if callback {
|
||||
optionData := frm.buf[ptr+2 : ptr+2+int(optlen)]
|
||||
if err := fn(optnum, optionData); err != nil {
|
||||
if err := fn(ptr, optnum, optionData); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -77,7 +77,7 @@ func (sv *Server) Demux(carrierData []byte, frameOffset int) error {
|
||||
var reqlist []byte
|
||||
var reqAddr []byte
|
||||
var hostname []byte
|
||||
err = dfrm.ForEachOption(func(op OptNum, data []byte) error {
|
||||
err = dfrm.ForEachOption(func(off int, op OptNum, data []byte) error {
|
||||
switch op {
|
||||
case OptMessageType:
|
||||
if len(data) == 1 {
|
||||
@@ -231,7 +231,7 @@ func (sv *Server) Encapsulate(carrierData []byte, frameOffset int) (int, error)
|
||||
// Set server state.
|
||||
sv.hosts[clientID] = client
|
||||
sv.pending--
|
||||
return optionsOffset + nopt, nil
|
||||
return OptionsOffset + nopt, nil
|
||||
}
|
||||
|
||||
func (sv *Server) getClient(clientID [36]byte) (serverEntry, bool) {
|
||||
|
||||
Reference in New Issue
Block a user