From 3a67b9965cd78a405bc97743c2f91e4f6870f3cf Mon Sep 17 00:00:00 2001 From: Paolo Invernizzi Date: Tue, 23 Jul 2024 18:05:26 +0200 Subject: [PATCH] Add ARP packets parsing and socket support --- protocols/arp.go | 69 +++++++++++++++++++++++++++++++++++++++++++ protocols/eth.go | 1 + sockets/raw_socket.go | 15 ++++++++-- 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 protocols/arp.go diff --git a/protocols/arp.go b/protocols/arp.go new file mode 100644 index 0000000..2bbf8be --- /dev/null +++ b/protocols/arp.go @@ -0,0 +1,69 @@ +package protocols + +import ( + "encoding/binary" + "errors" + "fmt" + "net" +) + +var HardwareTypeValues = map[uint16]string{ + 1: "Ethernet", + 6: "IEE 802 (Token Ring)", + 11: "ATM", + 12: "HDLC", +} + +type ARPPacket struct { + EthFrame EthernetFrame + HardwareType uint16 + ProtocolType uint16 + HardwareAddrLen uint8 + ProtocolAddrLen uint8 + Operation uint16 + SenderHWAddr net.HardwareAddr + SenderProtoAddr net.IP // only IPv4 - IPv6 should rely on NDP + TargetHWAddr net.HardwareAddr + TargetProtoAddr net.IP // only IPv4 - IPv6 should rely on NDP +} + +var errInvalidARPPacket = errors.New("ARP packet must be 28 bytes") + +// ARPPacketFromBytes parses an array of bytes to the corresponding ARP packet and returns a pointer to it. +// Returns an error if the number of bytes is less than 28 +func ARPPacketFromBytes(raw []byte) (*ARPPacket, error) { + frame, err := EthFrameFromBytes(raw) + if err != nil { + return nil, err + } + + if len(frame.Payload) < 28 { + return nil, errInvalidARPPacket + } + payload := frame.Payload + + return &ARPPacket{ + EthFrame: *frame, + HardwareType: binary.BigEndian.Uint16(payload[0:2]), + ProtocolType: binary.BigEndian.Uint16(payload[2:4]), + HardwareAddrLen: payload[4], + ProtocolAddrLen: payload[5], + Operation: binary.BigEndian.Uint16(payload[6:8]), + SenderHWAddr: payload[8:14], + SenderProtoAddr: payload[14:18], + TargetHWAddr: payload[18:24], + TargetProtoAddr: payload[24:28], + }, nil +} + +func (p ARPPacket) Destination() string { + return fmt.Sprintf("%s|%s", p.TargetHWAddr.String(), p.TargetProtoAddr.String()) +} + +func (p ARPPacket) Source() string { + return fmt.Sprintf("%s|%s", p.SenderHWAddr.String(), p.SenderProtoAddr.String()) +} + +func (p ARPPacket) Info() string { + return fmt.Sprintf("%s ARP packet from %s to %s", HardwareTypeValues[p.HardwareType], p.Source(), p.Destination()) +} diff --git a/protocols/eth.go b/protocols/eth.go index a21db81..5e121b5 100644 --- a/protocols/eth.go +++ b/protocols/eth.go @@ -12,6 +12,7 @@ var EtherTypesValues = map[uint16]string{ 0x0800: "IPv4", 0x0806: "ARP", 0x0842: "Wake-on-LAN", + 0x8535: "RARP", 0x86DD: "IPv6", 0x8808: "Ethernet flow control", } diff --git a/sockets/raw_socket.go b/sockets/raw_socket.go index 99ce225..388e117 100644 --- a/sockets/raw_socket.go +++ b/sockets/raw_socket.go @@ -89,12 +89,12 @@ func (rs *RawSocket) ReadToChan(dataChan chan<- NetworkPacket, errChan chan<- er switch ethFrame.Type() { case "ARP": - // TODO: ARP parsing + handleARPPacket(buf[:n], dataChan, errChan) case "IPv4", "IPv6": handleIPPacket(buf[:n], rs.layer4Filter, dataChan, errChan) } case syscall.ETH_P_ARP: - // TODO: ARP parsing + handleARPPacket(buf[:n], dataChan, errChan) case syscall.ETH_P_IP, syscall.ETH_P_IPV6: handleIPPacket(buf[:n], rs.layer4Filter, dataChan, errChan) } @@ -106,6 +106,17 @@ func (rs *RawSocket) Close() error { return syscall.Close(rs.fd) } +// handleARPPacket parses the provided bytes to an ARP packet's data and sends its representation, or +// an error, to the provided channels. +func handleARPPacket(raw []byte, dataChan chan<- NetworkPacket, errChan chan<- error) { + packet, err := protocols.ARPPacketFromBytes(raw) + if err != nil { + errChan <- err + return + } + dataChan <- packet +} + // handleIPPacket parses the provided bytes to an Ipv4 or Ipv6 packet's data and sends its representation, or // an error, to the provided channels. It is possible to apply a layer 4 filter to the packets. func handleIPPacket(raw []byte, filter string, dataChan chan<- NetworkPacket, errChan chan<- error) {