/
arpscan.go
316 lines (293 loc) · 9.61 KB
/
arpscan.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
package netdog
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
log "github.com/sirupsen/logrus"
"github.com/xkortex/vprint"
"net"
"sync"
"time"
)
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
// arpscan implements ARP scanning of all interfaces' local networks using
// gopacket and its subpackages. This example shows, among other things:
// * Generating and sending packet data
// * Reading in packet data and interpreting it
// * Use of the 'pcap' subpackage for reading/writing
func ScanAllInterfaces(timeout float64, delay float64) (results interface{}, err error) {
// Get a list of all interfaces.
ifaces, err := net.Interfaces()
if err != nil {
panic(err)
}
var wg sync.WaitGroup
for _, iface := range ifaces {
wg.Add(1)
// Start up a Scan on each interface.
go func(iface net.Interface) {
defer wg.Done()
if _, err := Scan(&iface, timeout, delay); err != nil {
vprint.Printf("interface %v: %v", iface.Name, err)
}
}(iface)
}
// Wait for all interfaces' scans to complete. They'll try to run
// forever, but will stop on an error, so if we get past this Wait
// it means all attempts to write have failed.
wg.Wait()
return "data (work in progress)", err
}
func ScanInterface(name string, timeout float64, delay float64) (results interface{}, err error) {
// Get a list of all interfaces.
ifaces, err := net.Interfaces()
if err != nil {
panic(err)
}
ifaceMap := map[string]net.Interface{}
for _, iface := range ifaces {
ifaceMap[iface.Name] = iface
}
var wg sync.WaitGroup
if iface, ok := ifaceMap[name]; !ok {
return nil, fmt.Errorf("Could not find interface: %s", name)
} else {
// Start up a Scan on each interface.
wg.Add(1)
go func(iface net.Interface) {
vprint.Printf("Scanning:", iface.Name)
defer wg.Done()
if _, err := Scan(&iface, timeout, delay); err != nil {
log.Errorf("interface %v: %v", iface.Name, err)
}
}(iface)
}
wg.Wait()
return "data (work in progress)", err
}
// Scan scans an individual interface's local network for machines using ARP requests/replies.
//
// Scan loops forever, sending packets out regularly. It returns an error if
// it's ever unable to write a packet.
func Scan(iface *net.Interface, timeout float64, delay float64) (arps []layers.ARP, err error) {
// We just look for IPv4 addresses, so try to find if the interface has one.
//ctx, cancel := context.WithTimeout(context.Background(), SecsToDuration(timeout))
//defer cancel()
var addr *net.IPNet
if addrs, err := iface.Addrs(); err != nil {
return nil, err
} else {
for _, a := range addrs {
if ipnet, ok := a.(*net.IPNet); ok {
if ip4 := ipnet.IP.To4(); ip4 != nil {
addr = &net.IPNet{
IP: ip4,
Mask: ipnet.Mask[len(ipnet.Mask)-4:],
}
break
}
}
}
}
// Sanity-check that the interface has a good address.
if addr == nil {
return nil, errors.New("no good IP network found")
} else if addr.IP[0] == 127 {
return nil, errors.New("skipping localhost")
} else if addr.Mask[0] != 0xff || addr.Mask[1] != 0xff {
return nil, errors.New("mask means network is too large")
}
vprint.Printf("Using network range %v for interface %v\n", addr, iface.Name)
// Open up a pcap handle for packet reads/writes.
handle, err := pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever)
if err != nil {
return nil, err
}
defer handle.Close()
// Start up a goroutine to read in packet data.
stop := make(chan struct{})
go readARP(handle, iface, stop)
defer close(stop)
// Write our Scan packets out to the handle.
if err := writeARP(handle, iface, addr, delay); err != nil {
log.Printf("error writing packets on %v: %v", iface.Name, err)
return nil, err
}
// We don't know exactly how long it'll take for packets to be
// sent back to us, but 10 seconds should be more than enough
// time ;)
time.Sleep(SecsToDuration(timeout))
return nil, nil
}
func AggroScan(iface *net.Interface, timeout float64) (arps []layers.ARP, err error) {
// We just look for IPv4 addresses, so try to find if the interface has one.
//ctx, cancel := context.WithTimeout(context.Background(), SecsToDuration(timeout))
//defer cancel()
var addr *net.IPNet
if addrs, err := iface.Addrs(); err != nil {
return nil, err
} else {
for _, a := range addrs {
if ipnet, ok := a.(*net.IPNet); ok {
if ip4 := ipnet.IP.To4(); ip4 != nil {
addr = &net.IPNet{
IP: ip4,
Mask: ipnet.Mask[len(ipnet.Mask)-4:],
}
break
}
}
}
}
// Sanity-check that the interface has a good address.
if addr == nil {
return nil, errors.New("no good IP network found")
} else if addr.IP[0] == 127 {
return nil, errors.New("skipping localhost")
} else if addr.Mask[0] != 0xff || addr.Mask[1] != 0xff {
return nil, errors.New("mask means network is too large")
}
vprint.Printf("Using network range %v for interface %v\n", addr, iface.Name)
// Open up a pcap handle for packet reads/writes.
handle, err := pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever)
if err != nil {
return nil, err
}
defer handle.Close()
// Start up a goroutine to read in packet data.
stop := make(chan struct{})
go ReadInterface(handle, iface, stop)
defer close(stop)
// Write our Scan packets out to the handle.
// We don't know exactly how long it'll take for packets to be
// sent back to us, but 10 seconds should be more than enough
// time ;)
time.Sleep(SecsToDuration(timeout))
return nil, nil
}
func ReadInterface(handle *pcap.Handle, iface *net.Interface, stop chan struct{}) {
src := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet)
in := src.Packets()
for {
var packet gopacket.Packet
select {
case <-stop:
return
case packet = <-in:
arpLayer := packet.Layer(layers.LayerTypeARP)
if arpLayer == nil {
continue
}
arp := arpLayer.(*layers.ARP)
if arp.Operation != layers.ARPReply || bytes.Equal([]byte(iface.HardwareAddr), arp.SourceHwAddress) {
// This is a packet I sent.
continue
}
// Note: we might get some packets here that aren't responses to ones we've sent,
// if for example someone else sends US an ARP request. Doesn't much matter, though...
// all information is good information :)
//log.Printf("IP %v is at %v", net.IP(arp.SourceProtAddress), net.HardwareAddr(arp.SourceHwAddress))
log.WithFields(log.Fields{
"ip": net.IP(arp.SourceProtAddress),
"mac": fmt.Sprintf("%v", net.HardwareAddr(arp.SourceHwAddress)),
"iface": iface.Name,
}).Info()
}
}
}
// readARP watches a handle for incoming ARP responses we might care about, and prints them.
//
// readARP loops until 'stop' is closed.
func readARP(handle *pcap.Handle, iface *net.Interface, stop chan struct{}) {
src := gopacket.NewPacketSource(handle, layers.LayerTypeEthernet)
in := src.Packets()
for {
var packet gopacket.Packet
select {
case <-stop:
return
case packet = <-in:
arpLayer := packet.Layer(layers.LayerTypeARP)
if arpLayer == nil {
continue
}
arp := arpLayer.(*layers.ARP)
if arp.Operation != layers.ARPReply || bytes.Equal([]byte(iface.HardwareAddr), arp.SourceHwAddress) {
// This is a packet I sent.
continue
}
// Note: we might get some packets here that aren't responses to ones we've sent,
// if for example someone else sends US an ARP request. Doesn't much matter, though...
// all information is good information :)
//log.Printf("IP %v is at %v", net.IP(arp.SourceProtAddress), net.HardwareAddr(arp.SourceHwAddress))
log.WithFields(log.Fields{
"ip": net.IP(arp.SourceProtAddress),
"mac": fmt.Sprintf("%v", net.HardwareAddr(arp.SourceHwAddress)),
"iface": iface.Name,
}).Info()
}
}
}
// writeARP writes an ARP request for each address on our local network to the
// pcap handle.
func writeARP(handle *pcap.Handle, iface *net.Interface, addr *net.IPNet, delay float64) error {
// Set up all the layers' fields we can.
eth := layers.Ethernet{
SrcMAC: iface.HardwareAddr,
DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
EthernetType: layers.EthernetTypeARP,
}
arp := layers.ARP{
AddrType: layers.LinkTypeEthernet,
Protocol: layers.EthernetTypeIPv4,
HwAddressSize: 6,
ProtAddressSize: 4,
Operation: layers.ARPRequest,
SourceHwAddress: []byte(iface.HardwareAddr),
SourceProtAddress: []byte(addr.IP),
DstHwAddress: []byte{0, 0, 0, 0, 0, 0},
}
// Set up buffer and options for serialization.
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
// Send one packet for every address.
//fmt.Println(addr)
for _, ip := range ips(addr) {
arp.DstProtAddress = []byte(ip)
gopacket.SerializeLayers(buf, opts, ð, &arp)
// Might want to trim to 42 because the camera does not respond to the padded one
if err := handle.WritePacketData(buf.Bytes()); err != nil {
return err
}
time.Sleep(SecsToDuration(delay))
}
return nil
}
// ips is a simple and not very good method for getting all IPv4 addresses from a
// net.IPNet. It returns all IPs it can over the channel it sends back, closing
// the channel when done.
func ips(n *net.IPNet) (out []net.IP) {
num := binary.BigEndian.Uint32([]byte(n.IP))
mask := binary.BigEndian.Uint32([]byte(n.Mask))
num &= mask
//fmt.Printf("num: %x mask: %x", num, mask)
for mask < 0xffffffff {
var buf [4]byte
binary.BigEndian.PutUint32(buf[:], num)
out = append(out, net.IP(buf[:]))
mask++
num++
}
return
}