mirror of
https://github.com/soypat/lneto.git
synced 2026-08-06 16:03:42 +00:00
63f7870fe5
* add handler.encapsulateNode * implement round robin handler approach * simplify round robin implementation * leave TODO * internet.node touch up * fix conflict resolve f-up * replace certain ErrShortBuffer with ErrTruncatedFrame error
168 lines
5.7 KiB
Go
168 lines
5.7 KiB
Go
package arp
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"net"
|
|
"net/netip"
|
|
|
|
"github.com/soypat/lneto"
|
|
"github.com/soypat/lneto/ethernet"
|
|
)
|
|
|
|
// NewFrame returns a Frame with data set to buf.
|
|
// An error is returned if the buffer size is smaller than 28 (IPv4 min size).
|
|
// Users should still call [Frame.ValidateSize] before working
|
|
// with payload/options of frames to avoid panics.
|
|
func NewFrame(buf []byte) (Frame, error) {
|
|
if len(buf) < sizeHeaderv4 {
|
|
return Frame{buf: nil}, lneto.ErrTruncatedFrame
|
|
}
|
|
return Frame{buf: buf}, nil
|
|
}
|
|
|
|
// Frame encapsulates the raw data of an ARP packet
|
|
// and provides methods for manipulating, validating and
|
|
// retrieving fields and payload data. See [RFC826].
|
|
//
|
|
// [RFC826]: https://tools.ietf.org/html/rfc826
|
|
type Frame struct {
|
|
buf []byte
|
|
}
|
|
|
|
// RawData returns the underlying slice with which the frame was created.
|
|
func (afrm Frame) RawData() []byte { return afrm.buf }
|
|
|
|
// HardwareType specifies the network link protocol type. Example: Ethernet is 1.
|
|
func (afrm Frame) Hardware() (Type uint16, length uint8) {
|
|
Type = binary.BigEndian.Uint16(afrm.buf[0:2])
|
|
return Type, afrm.hwlen()
|
|
}
|
|
|
|
func (afrm Frame) hwlen() uint8 {
|
|
return afrm.buf[4]
|
|
}
|
|
|
|
// SetHardware sets the networl link protocol type. See [Frame.SetHardware].
|
|
func (afrm Frame) SetHardware(Type uint16, length uint8) {
|
|
binary.BigEndian.PutUint16(afrm.buf[0:2], Type)
|
|
afrm.buf[4] = length
|
|
}
|
|
|
|
// Protocol returns the internet protocol type and length. See [ethernet.Type].
|
|
func (afrm Frame) Protocol() (Type ethernet.Type, length uint8) {
|
|
Type = ethernet.Type(binary.BigEndian.Uint16(afrm.buf[2:4]))
|
|
return Type, afrm.protolen()
|
|
}
|
|
|
|
func (afrm Frame) protolen() uint8 { return afrm.buf[5] }
|
|
|
|
// SetProtocol sets the protocol type and length fields of the ARP frame. See [Frame.Protocol] and [ethernet.Type].
|
|
func (afrm Frame) SetProtocol(Type ethernet.Type, length uint8) {
|
|
binary.BigEndian.PutUint16(afrm.buf[2:4], uint16(Type))
|
|
afrm.buf[5] = length
|
|
}
|
|
|
|
// Operation returns the ARP header operation field. See [Operation].
|
|
func (afrm Frame) Operation() Operation { return Operation(binary.BigEndian.Uint16(afrm.buf[6:8])) }
|
|
|
|
// SetOperation sets the ARP header operation field. See [Operation].
|
|
func (afrm Frame) SetOperation(op Operation) { binary.BigEndian.PutUint16(afrm.buf[6:8], uint16(op)) }
|
|
|
|
// Sender returns the hardware (MAC) and protocol addresses of sender of ARP packet.
|
|
// In an ARP request MAC address is used to indicate
|
|
// the address of the host sending the request. In an ARP reply MAC address is
|
|
// used to indicate the address of the host that the request was looking for.
|
|
func (afrm Frame) Sender() (hardwareAddr []byte, proto []byte) {
|
|
_, hlen := afrm.Hardware()
|
|
_, ilen := afrm.Protocol()
|
|
return afrm.buf[8 : 8+hlen], afrm.buf[8+hlen : 8+hlen+ilen]
|
|
}
|
|
|
|
// Target returns the hardware (MAC) and protocol addresses of target of ARP packet.
|
|
// In an ARP request MAC target is ignored. In ARP reply MAC is used to indicate the address of host that originated request.
|
|
func (afrm Frame) Target() (hardwareAddr []byte, proto []byte) {
|
|
_, hlen := afrm.Hardware()
|
|
_, ilen := afrm.Protocol()
|
|
toff := 8 + hlen + ilen
|
|
return afrm.buf[toff : toff+hlen], afrm.buf[toff+hlen : toff+hlen+ilen]
|
|
}
|
|
|
|
// Sender4 returns the IPv4 sender addresses. See [Frame.Sender].
|
|
func (afrm Frame) Sender4() (hardwareAddr *[6]byte, proto *[4]byte) {
|
|
return (*[6]byte)(afrm.buf[8:14]), (*[4]byte)(afrm.buf[14:18])
|
|
}
|
|
|
|
// Target4 returns the IPv4 target addresses. See [Frame.Sender].
|
|
func (afrm Frame) Target4() (hardwareAddr *[6]byte, proto *[4]byte) {
|
|
return (*[6]byte)(afrm.buf[18:24]), (*[4]byte)(afrm.buf[24:28])
|
|
}
|
|
|
|
// Sender6 returns the IPv6 sender addresses. See [Frame.Sender].
|
|
func (afrm Frame) Sender16() (hardwareAddr *[6]byte, proto *[16]byte) {
|
|
return (*[6]byte)(afrm.buf[8:14]), (*[16]byte)(afrm.buf[14:30])
|
|
}
|
|
|
|
// Target6 returns the IPv6 target addresses. See [Frame.Sender].
|
|
func (afrm Frame) Target16() (hardwareAddr *[6]byte, proto *[16]byte) {
|
|
return (*[6]byte)(afrm.buf[30:36]), (*[16]byte)(afrm.buf[36:52])
|
|
}
|
|
|
|
// ClearHeader zeros out the fixed(non-variable) header contents.
|
|
func (afrm Frame) ClearHeader() {
|
|
for i := range afrm.buf[:8] {
|
|
afrm.buf[i] = 0
|
|
}
|
|
}
|
|
|
|
func (afrm Frame) Clip() Frame {
|
|
return Frame{buf: afrm.buf[:sizeHeader+2*int(afrm.hwlen())+2*int(afrm.protolen())]}
|
|
}
|
|
|
|
func (afrm Frame) SwapTargetSender() {
|
|
hwTarget, protoTarget := afrm.Target()
|
|
hwSender, protoSender := afrm.Sender()
|
|
for i := range hwTarget {
|
|
hwTarget[i], hwSender[i] = hwSender[i], hwTarget[i]
|
|
}
|
|
for i := range protoTarget {
|
|
protoTarget[i], protoSender[i] = protoSender[i], protoTarget[i]
|
|
}
|
|
}
|
|
|
|
// Validation API
|
|
//
|
|
// ValidateSize checks the frame's size fields and compares with the actual buffer
|
|
// the frame. It returns a non-nil error on finding an inconsistency.
|
|
func (afrm Frame) ValidateSize(v *lneto.Validator) {
|
|
_, hlen := afrm.Hardware()
|
|
_, ilen := afrm.Protocol()
|
|
minLen := 8 + 2*(int(hlen)+int(ilen))
|
|
if len(afrm.buf) < int(minLen) {
|
|
v.AddError(errShortARP)
|
|
} else if minLen > 255 {
|
|
v.AddError(errLargeSizes) // We don't want a uint8 overflow somewhere. This is probably a maliciously crafted packet.
|
|
}
|
|
}
|
|
|
|
func (afrm Frame) String() string {
|
|
opstr := afrm.Operation().String()
|
|
hwt, _ := afrm.Hardware()
|
|
ptt, _ := afrm.Protocol()
|
|
sndhw, sndpt := afrm.Sender()
|
|
tgthw, tgtpt := afrm.Target()
|
|
var sndstr, tgtstr string
|
|
if ptt == ethernet.TypeIPv4 || ptt == ethernet.TypeIPv6 {
|
|
sender, _ := netip.AddrFromSlice(sndpt)
|
|
target, _ := netip.AddrFromSlice(tgtpt)
|
|
sndstr = sender.String()
|
|
tgtstr = target.String()
|
|
} else {
|
|
sndstr = net.HardwareAddr(sndpt).String()
|
|
tgtstr = net.HardwareAddr(tgtpt).String()
|
|
}
|
|
return fmt.Sprintf("ARP %s HW=(%d,SENDER=%s,TARGET=%s) PROTO=(%s,SENDER=%s,TARGET=%s)",
|
|
opstr, hwt, net.HardwareAddr(sndhw).String(), net.HardwareAddr(tgthw).String(),
|
|
ptt.String(), sndstr, tgtstr)
|
|
}
|