-
Notifications
You must be signed in to change notification settings - Fork 38
/
arp.go
136 lines (112 loc) · 3.03 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
129
130
131
132
133
134
135
136
// Package arp provides utilities for looking up MAC/IP addresses on a LAN.
package arp
import (
"net"
"sync"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
var (
table = make(map[string]net.HardwareAddr)
tableMu sync.RWMutex
)
var defaultSerializeOpts = gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
// Add adds a IP-MAC map to a runtime ARP table.
func internalLookup(ip net.IP) (*net.HardwareAddr, bool) {
tableMu.Lock()
defer tableMu.Unlock()
if hwaddr, ok := table[ip.To4().String()]; ok {
return &hwaddr, true
}
return nil, false
}
// 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[ip.To4().String()] = hwaddr
}
// Delete removes an IP from the runtime ARP table.
func Delete(ip net.IP) {
tableMu.Lock()
defer tableMu.Unlock()
delete(table, ip.To4().String())
}
// List returns the current runtime ARP table.
func List() map[string]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 net.IP) (*Address, error) {
if hwaddr, ok := internalLookup(ip); ok {
return &Address{
IP: ip,
HardwareAddr: *hwaddr,
}, nil
}
addr, err := doARPLookup(ip.To4().String())
if err == nil {
Add(addr.IP, addr.HardwareAddr)
}
return addr, err
}
// 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
}