Skip to content

Commit

Permalink
Add ARP packets parsing and socket support
Browse files Browse the repository at this point in the history
  • Loading branch information
NamelessOne91 committed Jul 23, 2024
1 parent a94d393 commit 3a67b99
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 2 deletions.
69 changes: 69 additions & 0 deletions protocols/arp.go
Original file line number Diff line number Diff line change
@@ -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())
}
1 change: 1 addition & 0 deletions protocols/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}
Expand Down
15 changes: 13 additions & 2 deletions sockets/raw_socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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) {
Expand Down

0 comments on commit 3a67b99

Please sign in to comment.