156 lines
4.4 KiB
Go
156 lines
4.4 KiB
Go
package arp
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"log"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/google/gopacket"
|
|
"github.com/google/gopacket/layers"
|
|
"github.com/google/gopacket/pcap"
|
|
)
|
|
|
|
// GoScan - scan iface
|
|
func GoScan() {
|
|
|
|
iface, _ := net.InterfaceByName("enp4s0")
|
|
err := scan(iface)
|
|
log.Println(err)
|
|
}
|
|
|
|
func scan(iface *net.Interface) error {
|
|
// We just look for IPv4 addresses, so try to find if the interface has one.
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Sanity-check that the interface has a good address.
|
|
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")
|
|
}
|
|
log.Printf("Using network range %v for interface %v", 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 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)
|
|
// for {
|
|
// 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
|
|
}
|
|
// 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(10 * time.Second)
|
|
// }
|
|
return err
|
|
}
|
|
|
|
// 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))
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|