mirror of
https://github.com/soypat/lneto.git
synced 2026-08-30 11:29:06 +00:00
dhcp working!
This commit is contained in:
+25
-22
@@ -19,6 +19,7 @@ type Client struct {
|
|||||||
hostname []byte
|
hostname []byte
|
||||||
dns [][4]byte
|
dns [][4]byte
|
||||||
|
|
||||||
|
svIPtos ipv4.ToS
|
||||||
tRenew uint32
|
tRenew uint32
|
||||||
tRebind uint32
|
tRebind uint32
|
||||||
tIPLease uint32
|
tIPLease uint32
|
||||||
@@ -73,26 +74,16 @@ func (c *Client) setIP(b []byte, frameOffset int) {
|
|||||||
ifrm, _ := ipv4.NewFrame(b)
|
ifrm, _ := ipv4.NewFrame(b)
|
||||||
ifrm.SetID((uint16(c.currentXID) ^ uint16(c.currentXID>>16)) + uint16(c.state))
|
ifrm.SetID((uint16(c.currentXID) ^ uint16(c.currentXID>>16)) + uint16(c.state))
|
||||||
if c.state > StateInit {
|
if c.state > StateInit {
|
||||||
// TODO(soypat): Document why disabling ToS used by DHCP server may cause Request to fail.
|
// Match server ToS since some routers drop DHCP requests if no ToS set apparently?
|
||||||
// Apparently server sets ToS=192. Uncommenting this line causes DHCP to fail on my setup.
|
ifrm.SetToS(c.svIPtos)
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
src := ifrm.SourceAddr()
|
src := ifrm.SourceAddr()
|
||||||
for i := range src {
|
for i := range src {
|
||||||
src[i] = 0
|
src[i] = 0
|
||||||
}
|
}
|
||||||
dst := ifrm.DestinationAddr()[:]
|
dst := ifrm.DestinationAddr()[:]
|
||||||
if c.svip == ([4]byte{}) {
|
for i := range dst {
|
||||||
for i := range dst {
|
dst[i] = 255
|
||||||
dst[i] = 255
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
copy(dst, c.svip[:])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +94,8 @@ func (c *Client) Encapsulate(carrierFrame []byte, frameOffset int) (int, error)
|
|||||||
return 0, nil // No offer received yet.
|
return 0, nil // No offer received yet.
|
||||||
} else if c.state == StateBound {
|
} else if c.state == StateBound {
|
||||||
return 0, nil // Done!
|
return 0, nil // Done!
|
||||||
|
} else if c.state == StateRequesting {
|
||||||
|
return 0, nil // Currently awaiting ACK.
|
||||||
}
|
}
|
||||||
dst := carrierFrame[frameOffset:]
|
dst := carrierFrame[frameOffset:]
|
||||||
frm, err := NewFrame(dst)
|
frm, err := NewFrame(dst)
|
||||||
@@ -149,7 +142,7 @@ func (c *Client) Encapsulate(carrierFrame []byte, frameOffset int) (int, error)
|
|||||||
nextState = StateRequesting
|
nextState = StateRequesting
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0, errors.New("unhandled state")
|
return 0, errors.New("unhandled state" + c.state.String())
|
||||||
}
|
}
|
||||||
n, _ := EncodeOption(opts[numOpts:], OptClientIdentifier, c.clientMAC[:]...)
|
n, _ := EncodeOption(opts[numOpts:], OptClientIdentifier, c.clientMAC[:]...)
|
||||||
numOpts += n
|
numOpts += n
|
||||||
@@ -166,10 +159,11 @@ func (c *Client) Encapsulate(carrierFrame []byte, frameOffset int) (int, error)
|
|||||||
c.setHeader(frm)
|
c.setHeader(frm)
|
||||||
c.setIP(carrierFrame, frameOffset)
|
c.setIP(carrierFrame, frameOffset)
|
||||||
c.state = nextState
|
c.state = nextState
|
||||||
return optionsOffset + numOpts, nil
|
return OptionsOffset + numOpts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
||||||
|
fmt.Println("DEMUX DHCP")
|
||||||
if c.isClosed() {
|
if c.isClosed() {
|
||||||
return net.ErrClosed
|
return net.ErrClosed
|
||||||
}
|
}
|
||||||
@@ -212,13 +206,17 @@ func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
|||||||
default:
|
default:
|
||||||
return fmt.Errorf("dcpv4 unexpected state in recv %s", c.state.String())
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) getMessageType(frm Frame) MessageType {
|
func (c *Client) getMessageType(frm Frame) MessageType {
|
||||||
c.auxbuf[0] = 255
|
c.auxbuf[0] = 255
|
||||||
ptrMsgType := &c.auxbuf[0]
|
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 {
|
if len(data) == 1 {
|
||||||
*ptrMsgType = data[0]
|
*ptrMsgType = data[0]
|
||||||
return io.EOF
|
return io.EOF
|
||||||
@@ -229,7 +227,7 @@ func (c *Client) getMessageType(frm Frame) MessageType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) setOptions(frm Frame) error {
|
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 {
|
switch opt {
|
||||||
case OptRenewTimeValue:
|
case OptRenewTimeValue:
|
||||||
c.tRenew = maybeU32(data)
|
c.tRenew = maybeU32(data)
|
||||||
@@ -237,7 +235,6 @@ func (c *Client) setOptions(frm Frame) error {
|
|||||||
c.tIPLease = maybeU32(data)
|
c.tIPLease = maybeU32(data)
|
||||||
case OptRebindingTimeValue:
|
case OptRebindingTimeValue:
|
||||||
c.tRebind = maybeU32(data)
|
c.tRebind = maybeU32(data)
|
||||||
|
|
||||||
case OptServerIdentification:
|
case OptServerIdentification:
|
||||||
c.svip = maybe4byte(data)
|
c.svip = maybe4byte(data)
|
||||||
case OptRouter:
|
case OptRouter:
|
||||||
@@ -261,6 +258,7 @@ func (c *Client) setOptions(frm Frame) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) isClosed() bool { return c.state == 0 || c.currentXID == 0 }
|
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.SetHardware(1, 6, 0)
|
||||||
frm.SetSecs(1)
|
frm.SetSecs(1)
|
||||||
if c.state.HasIP() {
|
if c.state.HasIP() {
|
||||||
copy(frm.CIAddr()[:], c.offer[:])
|
*frm.CIAddr() = c.offer
|
||||||
}
|
}
|
||||||
if c.state == StateInit {
|
if c.state == StateInit {
|
||||||
siaddr := frm.SIAddr()[:]
|
siaddr := frm.SIAddr()[:]
|
||||||
for i := range siaddr {
|
for i := range siaddr {
|
||||||
siaddr[i] = 255
|
siaddr[i] = 255
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if c.siip == [4]byte{} {
|
||||||
|
*frm.SIAddr() = c.svip
|
||||||
|
} else {
|
||||||
|
*frm.SIAddr() = c.siip
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
*frm.YIAddr() = c.offer
|
||||||
copy(frm.YIAddr()[:], c.offer[:])
|
|
||||||
copy(frm.CHAddrAs6()[:], c.clientMAC[:])
|
copy(frm.CHAddrAs6()[:], c.clientMAC[:])
|
||||||
frm.SetMagicCookie(MagicCookie)
|
frm.SetMagicCookie(MagicCookie)
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-13
@@ -98,13 +98,18 @@ func TestExample(t *testing.T) {
|
|||||||
Hostname: clientHostname,
|
Hostname: clientHostname,
|
||||||
})
|
})
|
||||||
buf := make([]byte, 2048)
|
buf := make([]byte, 2048)
|
||||||
|
buf2 := make([]byte, len(buf))
|
||||||
n, err := cl.Encapsulate(buf, 0)
|
n, err := cl.Encapsulate(buf, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n <= 0 {
|
} else if n <= 0 {
|
||||||
t.Fatal("no data sent out by client after starting request")
|
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, _ := NewFrame(buf)
|
||||||
dfrm.ClearHeader()
|
dfrm.ClearHeader()
|
||||||
dfrm.SetOp(OpReply)
|
dfrm.SetOp(OpReply)
|
||||||
@@ -116,27 +121,36 @@ func TestExample(t *testing.T) {
|
|||||||
copy(dfrm.CHAddr()[:], clientHwaddr[:])
|
copy(dfrm.CHAddr()[:], clientHwaddr[:])
|
||||||
dfrm.SetMagicCookie(MagicCookie)
|
dfrm.SetMagicCookie(MagicCookie)
|
||||||
ntot := 0
|
ntot := 0
|
||||||
nopt, _ := EncodeOption(buf[optionsOffset+ntot:], OptMessageType, byte(MsgOffer))
|
nopt, _ := EncodeOption(buf[OptionsOffset+ntot:], OptMessageType, byte(MsgOffer))
|
||||||
ntot += nopt
|
ntot += nopt
|
||||||
nopt, _ = EncodeOption(buf[optionsOffset+ntot:], OptServerIdentification, serverIP[:]...)
|
nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptServerIdentification, serverIP[:]...)
|
||||||
ntot += nopt
|
ntot += nopt
|
||||||
nopt, _ = EncodeOption32(buf[optionsOffset+ntot:], OptServerIdentification, offerLease)
|
nopt, _ = EncodeOption32(buf[OptionsOffset+ntot:], OptServerIdentification, offerLease)
|
||||||
ntot += nopt
|
ntot += nopt
|
||||||
nopt, _ = EncodeOption(buf[optionsOffset+ntot:], OptSubnetMask, subnetMask[:]...)
|
nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptSubnetMask, subnetMask[:]...)
|
||||||
ntot += nopt
|
ntot += nopt
|
||||||
nopt, _ = EncodeOption(buf[optionsOffset+ntot:], OptRouter, routerAddr[:]...)
|
nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptRouter, routerAddr[:]...)
|
||||||
ntot += nopt
|
ntot += nopt
|
||||||
nopt, _ = EncodeOption(buf[optionsOffset+ntot:], OptDNSServers, dnsAddr[:]...)
|
nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptDNSServers, dnsAddr[:]...)
|
||||||
ntot += nopt
|
ntot += nopt
|
||||||
nopt, _ = EncodeOption(buf[optionsOffset+ntot:], OptEnd, dnsAddr[:]...)
|
nopt, _ = EncodeOption(buf[OptionsOffset+ntot:], OptEnd, dnsAddr[:]...)
|
||||||
ntot += nopt
|
ntot += nopt
|
||||||
|
|
||||||
err = cl.Demux(buf[:optionsOffset+ntot], 0)
|
err = cl.Demux(buf[:OptionsOffset+ntot], 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// frame := buf[:optionsOffset]
|
|
||||||
// frame = AppendOption(frame, OptMessageType, byte(MsgOffer))
|
n, err = cl.Encapsulate(buf[:], 0)
|
||||||
// frame = AppendOption(frame, OptServerIdentification, serverIP[:]...)
|
if err != nil {
|
||||||
// frame = AppendOption(frame, OptIPAddressLeaseTime, serverIP[:]...)
|
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.
|
// Expected Magic Cookie value.
|
||||||
MagicCookie uint32 = 0x63825363
|
MagicCookie uint32 = 0x63825363
|
||||||
// DHCP Options offset measured from the start of the UDP payload.
|
// DHCP Options offset measured from the start of the UDP payload.
|
||||||
optionsOffset = magicCookieOffset + 4
|
OptionsOffset = magicCookieOffset + 4
|
||||||
|
|
||||||
DefaultClientPort = 68
|
DefaultClientPort = 68
|
||||||
DefaultServerPort = 67
|
DefaultServerPort = 67
|
||||||
@@ -26,12 +26,16 @@ const (
|
|||||||
// NewFrame returns a new DHCPv4 Frame with data set to buf.
|
// NewFrame returns a new DHCPv4 Frame with data set to buf.
|
||||||
// An error is returned if the buffer size is smaller than 240.
|
// An error is returned if the buffer size is smaller than 240.
|
||||||
func NewFrame(buf []byte) (Frame, error) {
|
func NewFrame(buf []byte) (Frame, error) {
|
||||||
if len(buf) < optionsOffset {
|
if len(buf) < OptionsOffset {
|
||||||
return Frame{}, errSmallFrame
|
return Frame{}, errSmallFrame
|
||||||
}
|
}
|
||||||
return Frame{buf: buf}, nil
|
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
|
// Frame encapsulates the raw data of a DHCP packet
|
||||||
// and provides methods for manipulating, validating and
|
// and provides methods for manipulating, validating and
|
||||||
// retrieving fields and payload data. See [RFC2131].
|
// 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.
|
// OptionsPayload returns the options portion of the DHCP frame. May be zero lengthed.
|
||||||
func (frm Frame) OptionsPayload() []byte {
|
func (frm Frame) OptionsPayload() []byte {
|
||||||
return frm.buf[optionsOffset:]
|
return frm.buf[OptionsOffset:]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (frm Frame) Op() Op { return Op(frm.buf[0]) }
|
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.
|
// ClearHeader zeros out the header contents.
|
||||||
func (frm Frame) ClearHeader() {
|
func (frm Frame) ClearHeader() {
|
||||||
for i := range frm.buf[:optionsOffset] {
|
for i := range frm.buf[:OptionsOffset] {
|
||||||
frm.buf[i] = 0
|
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.
|
// 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.
|
// 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.
|
// Parse DHCP options.
|
||||||
ptr := optionsOffset
|
ptr := OptionsOffset
|
||||||
if ptr > len(frm.buf) {
|
if ptr > len(frm.buf) {
|
||||||
return errSmallFrame
|
return errSmallFrame
|
||||||
} else if len(frm.buf[ptr:]) == 0 {
|
} 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]
|
optlen := frm.buf[ptr+1]
|
||||||
if callback {
|
if callback {
|
||||||
optionData := frm.buf[ptr+2 : ptr+2+int(optlen)]
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -77,7 +77,7 @@ func (sv *Server) Demux(carrierData []byte, frameOffset int) error {
|
|||||||
var reqlist []byte
|
var reqlist []byte
|
||||||
var reqAddr []byte
|
var reqAddr []byte
|
||||||
var hostname []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 {
|
switch op {
|
||||||
case OptMessageType:
|
case OptMessageType:
|
||||||
if len(data) == 1 {
|
if len(data) == 1 {
|
||||||
@@ -231,7 +231,7 @@ func (sv *Server) Encapsulate(carrierData []byte, frameOffset int) (int, error)
|
|||||||
// Set server state.
|
// Set server state.
|
||||||
sv.hosts[clientID] = client
|
sv.hosts[clientID] = client
|
||||||
sv.pending--
|
sv.pending--
|
||||||
return optionsOffset + nopt, nil
|
return OptionsOffset + nopt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sv *Server) getClient(clientID [36]byte) (serverEntry, bool) {
|
func (sv *Server) getClient(clientID [36]byte) (serverEntry, bool) {
|
||||||
|
|||||||
+27
-17
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
@@ -89,7 +90,7 @@ func run() (err error) {
|
|||||||
}
|
}
|
||||||
fmt.Println("NIC hardware address:", net.HardwareAddr(nicHW[:]).String(), "bridgeHW:", net.HardwareAddr(brHW[:]).String(), "mtu:", mtu, "addr:", nicAddr.String())
|
fmt.Println("NIC hardware address:", net.HardwareAddr(nicHW[:]).String(), "bridgeHW:", net.HardwareAddr(brHW[:]).String(), "mtu:", mtu, "addr:", nicAddr.String())
|
||||||
var stack Stack
|
var stack Stack
|
||||||
err = stack.Reset(brHW, nicAddr.Addr().Next(), uint16(mtu))
|
err = stack.Reset(brHW, netip.AddrFrom4([4]byte{}), uint16(mtu))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -97,9 +98,7 @@ func run() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var shark pcap.PacketBreakdown
|
|
||||||
buf := make([]byte, mtu)
|
buf := make([]byte, mtu)
|
||||||
var iframes []pcap.Frame
|
|
||||||
lastAction := time.Now()
|
lastAction := time.Now()
|
||||||
dnsOngoing := false
|
dnsOngoing := false
|
||||||
for {
|
for {
|
||||||
@@ -126,12 +125,6 @@ func run() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("ERR:ENCAPSULATE", err)
|
fmt.Println("ERR:ENCAPSULATE", err)
|
||||||
} else if nwrite > 0 {
|
} else if nwrite > 0 {
|
||||||
iframes, err = shark.CaptureEthernet(iframes[:0], buf[:nwrite], 0)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("OU", iframes, err.Error())
|
|
||||||
} else {
|
|
||||||
fmt.Println("OU", iframes)
|
|
||||||
}
|
|
||||||
n, err := iface.Write(buf[:nwrite])
|
n, err := iface.Write(buf[:nwrite])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -145,12 +138,6 @@ func run() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if nread > 0 {
|
} else if nread > 0 {
|
||||||
iframes, err = shark.CaptureEthernet(iframes[:0], buf[:nread], 0)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("IN", iframes, err.Error())
|
|
||||||
} else {
|
|
||||||
fmt.Println("IN", iframes)
|
|
||||||
}
|
|
||||||
err = stack.Demux(buf[:nread], 0)
|
err = stack.Demux(buf[:nread], 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("ERR:DEMUX", err)
|
fmt.Println("ERR:DEMUX", err)
|
||||||
@@ -174,14 +161,36 @@ type Stack struct {
|
|||||||
dhcp dhcpv4.Client
|
dhcp dhcpv4.Client
|
||||||
dns dns.Client
|
dns dns.Client
|
||||||
lookup dns.Message
|
lookup dns.Message
|
||||||
|
|
||||||
|
// Packet capture and top level filtering.
|
||||||
|
shark pcap.PacketBreakdown
|
||||||
|
aux []pcap.Frame
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stack) Demux(b []byte, _ int) error {
|
func (s *Stack) Demux(b []byte, _ int) (err error) {
|
||||||
|
s.aux, err = s.shark.CaptureEthernet(s.aux[:0], b, 0)
|
||||||
|
if s.aux[len(s.aux)-1].Protocol != "DHCPv4" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("IN", s.aux, err.Error())
|
||||||
|
} else {
|
||||||
|
fmt.Println("IN", s.aux)
|
||||||
|
}
|
||||||
return s.link.Demux(b, 0)
|
return s.link.Demux(b, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stack) Encapsulate(b []byte, _ int) (int, error) {
|
func (s *Stack) Encapsulate(b []byte, _ int) (int, error) {
|
||||||
return s.link.Encapsulate(b, 0)
|
n, err := s.link.Encapsulate(b, 0)
|
||||||
|
if n > 0 {
|
||||||
|
iframes, errpcap := s.shark.CaptureEthernet(s.aux[:0], b[:n], 0)
|
||||||
|
if errpcap != nil {
|
||||||
|
fmt.Println("OU", iframes, errpcap.Error())
|
||||||
|
} else {
|
||||||
|
fmt.Println("OU", iframes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stack) Reset(mac [6]byte, addr netip.Addr, mtu uint16) error {
|
func (s *Stack) Reset(mac [6]byte, addr netip.Addr, mtu uint16) error {
|
||||||
@@ -227,6 +236,7 @@ func (s *Stack) Reset(mac [6]byte, addr netip.Addr, mtu uint16) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
s.ip.SetLogger(slog.Default())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+126
-2
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/soypat/lneto"
|
"github.com/soypat/lneto"
|
||||||
"github.com/soypat/lneto/arp"
|
"github.com/soypat/lneto/arp"
|
||||||
|
"github.com/soypat/lneto/dhcpv4"
|
||||||
"github.com/soypat/lneto/ethernet"
|
"github.com/soypat/lneto/ethernet"
|
||||||
"github.com/soypat/lneto/http/httpraw"
|
"github.com/soypat/lneto/http/httpraw"
|
||||||
"github.com/soypat/lneto/ipv4"
|
"github.com/soypat/lneto/ipv4"
|
||||||
@@ -238,7 +239,6 @@ func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pc.captureIPProto(proto, dst, pkt, end, protoErrs...)
|
return pc.captureIPProto(proto, dst, pkt, end, protoErrs...)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc *PacketBreakdown) captureIPProto(proto lneto.IPProto, dst []Frame, pkt []byte, bitOffset int, ipProtoErrs ...error) (_ []Frame, err error) {
|
func (pc *PacketBreakdown) captureIPProto(proto lneto.IPProto, dst []Frame, pkt []byte, bitOffset int, ipProtoErrs ...error) (_ []Frame, err error) {
|
||||||
@@ -321,7 +321,49 @@ func (pc *PacketBreakdown) CaptureUDP(dst []Frame, pkt []byte, bitOffset int) ([
|
|||||||
finfo.Fields = append(finfo.Fields, baseUDPFields[:]...)
|
finfo.Fields = append(finfo.Fields, baseUDPFields[:]...)
|
||||||
dst = append(dst, finfo)
|
dst = append(dst, finfo)
|
||||||
end := bitOffset + 8*octet
|
end := bitOffset + 8*octet
|
||||||
dst = append(dst, remainingFrameInfo(unknownPayloadProto, FieldClassPayload, end, octet*len(pkt)))
|
payload := ufrm.Payload()
|
||||||
|
if dhcpv4.PayloadIsDHCPv4(payload) {
|
||||||
|
return pc.CaptureDHCPv4(dst, pkt, end)
|
||||||
|
} else {
|
||||||
|
dst = append(dst, remainingFrameInfo(unknownPayloadProto, FieldClassPayload, end, octet*len(pkt)))
|
||||||
|
}
|
||||||
|
return dst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||||
|
if bitOffset%8 != 0 {
|
||||||
|
return nil, errors.New("DHCP must be parsed at byte boundary")
|
||||||
|
}
|
||||||
|
dhcpData := pkt[bitOffset/8:]
|
||||||
|
dfrm, err := dhcpv4.NewFrame(dhcpData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
finfo := Frame{
|
||||||
|
Protocol: "DHCPv4",
|
||||||
|
PacketBitOffset: bitOffset,
|
||||||
|
}
|
||||||
|
magic := dfrm.MagicCookie()
|
||||||
|
if magic != dhcpv4.MagicCookie {
|
||||||
|
finfo.Errors = append(finfo.Errors, errors.New("incorrect DHCPv4 magic cookie"))
|
||||||
|
}
|
||||||
|
finfo.Fields = append(finfo.Fields, baseDHCPv4Fields[:]...)
|
||||||
|
options := dfrm.OptionsPayload()
|
||||||
|
if len(options) > 0 {
|
||||||
|
err = dfrm.ForEachOption(func(optoff int, op dhcpv4.OptNum, data []byte) error {
|
||||||
|
finfo.Fields = append(finfo.Fields, FrameField{
|
||||||
|
Name: op.String(),
|
||||||
|
Class: FieldClassOptions,
|
||||||
|
FrameBitOffset: optoff * octet,
|
||||||
|
BitLength: (2 + len(data)) * octet,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
finfo.Errors = append(finfo.Errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dst = append(dst, finfo)
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,6 +589,7 @@ const (
|
|||||||
FieldClassOptions // options
|
FieldClassOptions // options
|
||||||
FieldClassPayload // payload
|
FieldClassPayload // payload
|
||||||
FieldClassText // text
|
FieldClassText // text
|
||||||
|
FieldClassAddress // address
|
||||||
)
|
)
|
||||||
|
|
||||||
const octet = 8
|
const octet = 8
|
||||||
@@ -799,6 +842,87 @@ var baseUDPFields = [...]FrameField{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var baseDHCPv4Fields = [...]FrameField{
|
||||||
|
{
|
||||||
|
Name: "Opcode",
|
||||||
|
Class: FieldClassType,
|
||||||
|
FrameBitOffset: 0,
|
||||||
|
BitLength: 1 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Hardware Address Type",
|
||||||
|
Class: FieldClassProto,
|
||||||
|
FrameBitOffset: 1 * octet,
|
||||||
|
BitLength: 1 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Hardware Address Length",
|
||||||
|
Class: FieldClassSize,
|
||||||
|
FrameBitOffset: 2 * octet,
|
||||||
|
BitLength: 1 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Hops",
|
||||||
|
Class: fieldClassUndefined,
|
||||||
|
FrameBitOffset: 3 * octet,
|
||||||
|
BitLength: 1 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Transaction ID",
|
||||||
|
Class: FieldClassID,
|
||||||
|
FrameBitOffset: 4 * octet,
|
||||||
|
BitLength: 4 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Start Time",
|
||||||
|
Class: fieldClassUndefined,
|
||||||
|
FrameBitOffset: 8 * octet,
|
||||||
|
BitLength: 2 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Flags",
|
||||||
|
Class: FieldClassFlags,
|
||||||
|
FrameBitOffset: 10 * octet,
|
||||||
|
BitLength: 2 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Client Address",
|
||||||
|
Class: FieldClassAddress,
|
||||||
|
FrameBitOffset: 12 * octet,
|
||||||
|
BitLength: 4 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Offered Address",
|
||||||
|
Class: FieldClassAddress,
|
||||||
|
FrameBitOffset: 16 * octet,
|
||||||
|
BitLength: 4 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Server Next Address",
|
||||||
|
Class: FieldClassAddress,
|
||||||
|
FrameBitOffset: 20 * octet,
|
||||||
|
BitLength: 4 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Relay Agent Address",
|
||||||
|
Class: FieldClassAddress,
|
||||||
|
FrameBitOffset: 24 * octet,
|
||||||
|
BitLength: 4 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Client Hardware Address",
|
||||||
|
Class: FieldClassAddress,
|
||||||
|
FrameBitOffset: 28 * octet,
|
||||||
|
BitLength: 16 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "BOOTP",
|
||||||
|
Class: FieldClassAddress,
|
||||||
|
FrameBitOffset: (28 + 16) * octet,
|
||||||
|
BitLength: (dhcpv4.OptionsOffset - (28 + 16)) * octet,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func remainingFrameInfo(proto any, class FieldClass, pktBitOffset, pktBitLen int) Frame {
|
func remainingFrameInfo(proto any, class FieldClass, pktBitOffset, pktBitLen int) Frame {
|
||||||
return Frame{
|
return Frame{
|
||||||
Protocol: proto,
|
Protocol: proto,
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ func (ls *StackEthernet) Demux(carrierData []byte, frameOffset int) (err error)
|
|||||||
}
|
}
|
||||||
DROP:
|
DROP:
|
||||||
ls.info("LinkStack:drop-packet", slog.String("dsthw", net.HardwareAddr(dstaddr[:]).String()), slog.String("ethertype", efrm.EtherTypeOrSize().String()))
|
ls.info("LinkStack:drop-packet", slog.String("dsthw", net.HardwareAddr(dstaddr[:]).String()), slog.String("ethertype", efrm.EtherTypeOrSize().String()))
|
||||||
return nil
|
return errors.New("drop packet")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *StackEthernet) Encapsulate(carrierData []byte, frameOffset int) (n int, err error) {
|
func (ls *StackEthernet) Encapsulate(carrierData []byte, frameOffset int) (n int, err error) {
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ func (sb *StackIP) Addr() netip.Addr {
|
|||||||
return netip.AddrFrom4(sb.ip)
|
return netip.AddrFrom4(sb.ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sb *StackIP) SetLogger(logger *slog.Logger) {
|
||||||
|
sb.logger.log = logger
|
||||||
|
}
|
||||||
|
|
||||||
func (sb *StackIP) Demux(carrierData []byte, offset int) error {
|
func (sb *StackIP) Demux(carrierData []byte, offset int) error {
|
||||||
sb.info("StackIP.Demux:start")
|
sb.info("StackIP.Demux:start")
|
||||||
frame := carrierData[offset:] // we don't care about carrier data in IP.
|
frame := carrierData[offset:] // we don't care about carrier data in IP.
|
||||||
@@ -72,8 +76,8 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dst := ifrm.DestinationAddr()
|
dst := ifrm.DestinationAddr()
|
||||||
if *dst != sb.ip {
|
if sb.ip != ([4]byte{}) && *dst != sb.ip {
|
||||||
return nil // Not meant for us.
|
return errors.New("not meant for us") // Not meant for us.
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.validator.ResetErr()
|
sb.validator.ResetErr()
|
||||||
@@ -155,7 +159,7 @@ func (sb *StackIP) Encapsulate(carrierData []byte, frameOffset int) (int, error)
|
|||||||
println("NODE REMOVED", proto.String(), h.port)
|
println("NODE REMOVED", proto.String(), h.port)
|
||||||
h.destroy()
|
h.destroy()
|
||||||
}
|
}
|
||||||
sb.error("StackIP:handle", slog.String("proto", proto.String()), slog.String("err", err.Error()))
|
sb.error("StackIP:encapsulate", slog.String("proto", proto.String()), slog.String("err", err.Error()))
|
||||||
continue
|
continue
|
||||||
} else if n == 0 {
|
} else if n == 0 {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func (sudp *StackUDPPort) Encapsulate(carrierData []byte, frameOffset int) (int,
|
|||||||
n, err := sudp.h.encapsulate(carrierData, frameOffset+8)
|
n, err := sudp.h.encapsulate(carrierData, frameOffset+8)
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("stackudp:demux", slog.String("err", err.Error()))
|
slog.Error("stackudp:encapsulate", slog.String("err", err.Error()))
|
||||||
}
|
}
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func expectExchange(t *testing.T, from, to *StackIP, buf []byte) {
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
n, err := from.Encapsulate(buf, 0)
|
n, err := from.Encapsulate(buf, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("expectExchange:Handle:", err)
|
t.Error("expectExchange:encapsulate:", err)
|
||||||
} else if n == 0 {
|
} else if n == 0 {
|
||||||
t.Error("expected data exchange")
|
t.Error("expected data exchange")
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user