-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathipfrommac.go
166 lines (151 loc) · 4.41 KB
/
ipfrommac.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
// 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.
package ipfrommac
import (
"bytes"
"encoding/binary"
"errors"
"log"
"net"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
)
const arpTimeout = 5 * time.Second
func IpFromMac(ifaceName string, mac string) (string, error) {
iface := getInterface(ifaceName)
var addr *net.IPNet
if addrs, err := iface.Addrs(); err != nil {
return "", 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
}
}
}
}
if addr == nil {
return "", errors.New("no good IP network found")
} else if addr.IP[0] == 127 {
return "", errors.New("skipping localhost")
} else if addr.Mask[0] != 0xff || addr.Mask[1] != 0xff {
return "", errors.New("mask means network is too large")
}
// Open up a pcap handle for packet reads/writes.
handle, err := pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever)
if err != nil {
return "", err
}
defer handle.Close()
// Start up a goroutine to read in packet data.
stop := make(chan bool)
ipFound := make(chan string)
go findIpFromArpRead(handle, iface, stop, mac, ipFound)
defer close(stop)
// Write our scan packets out to the handle.
if err := writeARP(handle, iface, addr); err != nil {
log.Printf("error writing packets on %v: %v", iface.Name, err)
return "", err
}
select {
case res := <-ipFound:
return res, nil
case <-time.After(arpTimeout):
return "", errors.New("not found")
}
}
func getInterface(interName string) *net.Interface {
interfaces, _ := net.Interfaces()
for _, inter := range interfaces {
if inter.Name == interName {
return &inter
}
}
return &net.Interface{}
}
// readARP watches a handle for incoming ARP responses we might care about, and prints them.
//
// readARP loops until 'stop' is closed.
func findIpFromArpRead(handle *pcap.Handle, iface *net.Interface, stop chan bool, mac string, ipFound chan string) {
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) {
continue
}
if net.HardwareAddr(arp.SourceHwAddress).String() == mac {
ipFound <- net.IP(arp.SourceProtAddress).String()
}
}
}
}
// 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) 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.
for _, ip := range ips(addr) {
arp.DstProtAddress = []byte(ip)
gopacket.SerializeLayers(buf, opts, ð, &arp)
if err := handle.WritePacketData(buf.Bytes()); err != nil {
return err
}
}
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))
network := num & mask
broadcast := network | ^mask
for network++; network < broadcast; network++ {
var buf [4]byte
binary.BigEndian.PutUint32(buf[:], network)
out = append(out, net.IP(buf[:]))
}
return
}