Skip to content

Commit 3a67b99

Browse files
committed
Add ARP packets parsing and socket support
1 parent a94d393 commit 3a67b99

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

protocols/arp.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package protocols
2+
3+
import (
4+
"encoding/binary"
5+
"errors"
6+
"fmt"
7+
"net"
8+
)
9+
10+
var HardwareTypeValues = map[uint16]string{
11+
1: "Ethernet",
12+
6: "IEE 802 (Token Ring)",
13+
11: "ATM",
14+
12: "HDLC",
15+
}
16+
17+
type ARPPacket struct {
18+
EthFrame EthernetFrame
19+
HardwareType uint16
20+
ProtocolType uint16
21+
HardwareAddrLen uint8
22+
ProtocolAddrLen uint8
23+
Operation uint16
24+
SenderHWAddr net.HardwareAddr
25+
SenderProtoAddr net.IP // only IPv4 - IPv6 should rely on NDP
26+
TargetHWAddr net.HardwareAddr
27+
TargetProtoAddr net.IP // only IPv4 - IPv6 should rely on NDP
28+
}
29+
30+
var errInvalidARPPacket = errors.New("ARP packet must be 28 bytes")
31+
32+
// ARPPacketFromBytes parses an array of bytes to the corresponding ARP packet and returns a pointer to it.
33+
// Returns an error if the number of bytes is less than 28
34+
func ARPPacketFromBytes(raw []byte) (*ARPPacket, error) {
35+
frame, err := EthFrameFromBytes(raw)
36+
if err != nil {
37+
return nil, err
38+
}
39+
40+
if len(frame.Payload) < 28 {
41+
return nil, errInvalidARPPacket
42+
}
43+
payload := frame.Payload
44+
45+
return &ARPPacket{
46+
EthFrame: *frame,
47+
HardwareType: binary.BigEndian.Uint16(payload[0:2]),
48+
ProtocolType: binary.BigEndian.Uint16(payload[2:4]),
49+
HardwareAddrLen: payload[4],
50+
ProtocolAddrLen: payload[5],
51+
Operation: binary.BigEndian.Uint16(payload[6:8]),
52+
SenderHWAddr: payload[8:14],
53+
SenderProtoAddr: payload[14:18],
54+
TargetHWAddr: payload[18:24],
55+
TargetProtoAddr: payload[24:28],
56+
}, nil
57+
}
58+
59+
func (p ARPPacket) Destination() string {
60+
return fmt.Sprintf("%s|%s", p.TargetHWAddr.String(), p.TargetProtoAddr.String())
61+
}
62+
63+
func (p ARPPacket) Source() string {
64+
return fmt.Sprintf("%s|%s", p.SenderHWAddr.String(), p.SenderProtoAddr.String())
65+
}
66+
67+
func (p ARPPacket) Info() string {
68+
return fmt.Sprintf("%s ARP packet from %s to %s", HardwareTypeValues[p.HardwareType], p.Source(), p.Destination())
69+
}

protocols/eth.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ var EtherTypesValues = map[uint16]string{
1212
0x0800: "IPv4",
1313
0x0806: "ARP",
1414
0x0842: "Wake-on-LAN",
15+
0x8535: "RARP",
1516
0x86DD: "IPv6",
1617
0x8808: "Ethernet flow control",
1718
}

sockets/raw_socket.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,12 @@ func (rs *RawSocket) ReadToChan(dataChan chan<- NetworkPacket, errChan chan<- er
8989

9090
switch ethFrame.Type() {
9191
case "ARP":
92-
// TODO: ARP parsing
92+
handleARPPacket(buf[:n], dataChan, errChan)
9393
case "IPv4", "IPv6":
9494
handleIPPacket(buf[:n], rs.layer4Filter, dataChan, errChan)
9595
}
9696
case syscall.ETH_P_ARP:
97-
// TODO: ARP parsing
97+
handleARPPacket(buf[:n], dataChan, errChan)
9898
case syscall.ETH_P_IP, syscall.ETH_P_IPV6:
9999
handleIPPacket(buf[:n], rs.layer4Filter, dataChan, errChan)
100100
}
@@ -106,6 +106,17 @@ func (rs *RawSocket) Close() error {
106106
return syscall.Close(rs.fd)
107107
}
108108

109+
// handleARPPacket parses the provided bytes to an ARP packet's data and sends its representation, or
110+
// an error, to the provided channels.
111+
func handleARPPacket(raw []byte, dataChan chan<- NetworkPacket, errChan chan<- error) {
112+
packet, err := protocols.ARPPacketFromBytes(raw)
113+
if err != nil {
114+
errChan <- err
115+
return
116+
}
117+
dataChan <- packet
118+
}
119+
109120
// handleIPPacket parses the provided bytes to an Ipv4 or Ipv6 packet's data and sends its representation, or
110121
// an error, to the provided channels. It is possible to apply a layer 4 filter to the packets.
111122
func handleIPPacket(raw []byte, filter string, dataChan chan<- NetworkPacket, errChan chan<- error) {

0 commit comments

Comments
 (0)