forked from malfunkt/arpfox
-
Notifications
You must be signed in to change notification settings - Fork 3
/
arp.go
128 lines (105 loc) · 2.86 KB
/
arp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Package arp provides utilities for looking up MAC/IP addresses on a LAN.
package arp
import (
"encoding/binary"
"net"
"sync"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
var (
table map[uint32]net.HardwareAddr
tableMu sync.RWMutex
)
var defaultSerializeOpts = gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
func init() {
table = make(map[uint32]net.HardwareAddr)
}
// Add adds a IP-MAC map to a runtime ARP table.
func Add(ip net.IP, hwaddr net.HardwareAddr) {
tableMu.Lock()
defer tableMu.Unlock()
table[binary.BigEndian.Uint32(ip)] = hwaddr
}
// Delete removes an IP from the runtime ARP table.
func Delete(ip net.IP) {
tableMu.Lock()
defer tableMu.Unlock()
delete(table, binary.BigEndian.Uint32(ip))
}
// List returns the current runtime ARP table.
func List() map[uint32]net.HardwareAddr {
tableMu.RLock()
defer tableMu.RUnlock()
return table
}
// Address represents an ARP address.
type Address struct {
IP net.IP
HardwareAddr net.HardwareAddr
Interface net.Interface
}
// Lookup returns the Address given an IP, if the IP is not found within the
// table, a lookup is attempted.
func Lookup(ip uint32) (*Address, error) {
b := make([]byte, 4)
binary.BigEndian.PutUint32(b, ip)
if hwaddr, ok := table[ip]; ok {
return &Address{
IP: net.IP(b),
HardwareAddr: hwaddr,
}, nil
}
return doARPLookup(net.IP(b).To4().String())
}
// NewARPRequest creates a bew ARP packet of type "request.
func NewARPRequest(src *Address, dst *Address) ([]byte, error) {
ether, arp, err := buildPacket(src, dst)
if err != nil {
return nil, err
}
arp.Operation = layers.ARPRequest
buf := gopacket.NewSerializeBuffer()
err = gopacket.SerializeLayers(buf, defaultSerializeOpts, ðer, &arp)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// NewARPReply creates a new ARP packet of type "reply".
func NewARPReply(src *Address, dst *Address) ([]byte, error) {
ether, arp, err := buildPacket(src, dst)
if err != nil {
return nil, err
}
arp.Operation = layers.ARPReply
buf := gopacket.NewSerializeBuffer()
err = gopacket.SerializeLayers(buf, defaultSerializeOpts, ðer, &arp)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// buildPacket creates an template ARP packet with the given source and
// destination.
func buildPacket(src *Address, dst *Address) (layers.Ethernet, layers.ARP, error) {
ether := layers.Ethernet{
EthernetType: layers.EthernetTypeARP,
SrcMAC: src.HardwareAddr,
DstMAC: dst.HardwareAddr,
}
arp := layers.ARP{
AddrType: layers.LinkTypeEthernet,
Protocol: layers.EthernetTypeIPv4,
HwAddressSize: 6,
ProtAddressSize: 4,
SourceHwAddress: []byte(src.HardwareAddr),
SourceProtAddress: []byte(src.IP.To4()),
DstHwAddress: []byte(dst.HardwareAddr),
DstProtAddress: []byte(dst.IP.To4()),
}
return ether, arp, nil
}