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
|
package arp
import (
"errors"
"net"
"net/netip"
"time"
"github.com/mdlayher/ethernet"
"github.com/mdlayher/packet"
)
// errNoIPv4Addr is returned when an interface does not have an IPv4
// address.
var errNoIPv4Addr = errors.New("no IPv4 address available for interface")
// protocolARP is the uint16 EtherType representation of ARP (Address
// Resolution Protocol, RFC 826).
const protocolARP = 0x0806
// A Client is an ARP client, which can be used to send and receive
// ARP packets.
type Client struct {
ifi *net.Interface
ip netip.Addr
p net.PacketConn
}
// Dial creates a new Client using the specified network interface.
// Dial retrieves the IPv4 address of the interface and binds a raw socket
// to send and receive ARP packets.
func Dial(ifi *net.Interface) (*Client, error) {
// Open raw socket to send and receive ARP packets using ethernet frames
// we build ourselves.
p, err := packet.Listen(ifi, packet.Raw, protocolARP, nil)
if err != nil {
return nil, err
}
return New(ifi, p)
}
// New creates a new Client using the specified network interface
// and net.PacketConn. This allows the caller to define exactly how they bind to the
// net.PacketConn. This is most useful to define what protocol to pass to socket(7).
//
// In most cases, callers would be better off calling Dial.
func New(ifi *net.Interface, p net.PacketConn) (*Client, error) {
// Check for usable IPv4 addresses for the Client
addrs, err := ifi.Addrs()
if err != nil {
return nil, err
}
ipaddrs := make([]netip.Addr, len(addrs))
for i, a := range addrs {
ipPrefix, err := netip.ParsePrefix(a.String())
if err != nil {
return nil, err
}
ipaddrs[i] = ipPrefix.Addr()
}
return newClient(ifi, p, ipaddrs)
}
// newClient is the internal, generic implementation of newClient. It is used
// to allow an arbitrary net.PacketConn to be used in a Client, so testing
// is easier to accomplish.
func newClient(ifi *net.Interface, p net.PacketConn, addrs []netip.Addr) (*Client, error) {
ip, err := firstIPv4Addr(addrs)
if err != nil {
return nil, err
}
return &Client{
ifi: ifi,
ip: ip,
p: p,
}, nil
}
// Close closes the Client's raw socket and stops sending and receiving
// ARP packets.
func (c *Client) Close() error {
return c.p.Close()
}
// Request sends an ARP request, asking for the hardware address
// associated with an IPv4 address. The response, if any, can be read
// with the Read method.
//
// Unlike Resolve, which provides an easier interface for getting the
// hardware address, Request allows sending many requests in a row,
// retrieving the responses afterwards.
func (c *Client) Request(ip netip.Addr) error {
if !c.ip.IsValid() {
return errNoIPv4Addr
}
// Create ARP packet for broadcast address to attempt to find the
// hardware address of the input IP address
arp, err := NewPacket(OperationRequest, c.ifi.HardwareAddr, c.ip, ethernet.Broadcast, ip)
if err != nil {
return err
}
return c.WriteTo(arp, ethernet.Broadcast)
}
// Resolve performs an ARP request, attempting to retrieve the
// hardware address of a machine using its IPv4 address. Resolve must not
// be used concurrently with Read. If you're using Read (usually in a
// loop), you need to use Request instead. Resolve may read more than
// one message if it receives messages unrelated to the request.
func (c *Client) Resolve(ip netip.Addr) (net.HardwareAddr, error) {
err := c.Request(ip)
if err != nil {
return nil, err
}
// Loop and wait for replies
for {
arp, _, err := c.Read()
if err != nil {
return nil, err
}
if arp.Operation != OperationReply || arp.SenderIP != ip {
continue
}
return arp.SenderHardwareAddr, nil
}
}
// Read reads a single ARP packet and returns it, together with its
// ethernet frame.
func (c *Client) Read() (*Packet, *ethernet.Frame, error) {
buf := make([]byte, 128)
for {
n, _, err := c.p.ReadFrom(buf)
if err != nil {
return nil, nil, err
}
p, eth, err := parsePacket(buf[:n])
if err != nil {
if err == errInvalidARPPacket {
continue
}
return nil, nil, err
}
return p, eth, nil
}
}
// WriteTo writes a single ARP packet to addr. Note that addr should,
// but doesn't have to, match the target hardware address of the ARP
// packet.
func (c *Client) WriteTo(p *Packet, addr net.HardwareAddr) error {
pb, err := p.MarshalBinary()
if err != nil {
return err
}
f := ðernet.Frame{
Destination: addr,
Source: p.SenderHardwareAddr,
EtherType: ethernet.EtherTypeARP,
Payload: pb,
}
fb, err := f.MarshalBinary()
if err != nil {
return err
}
_, err = c.p.WriteTo(fb, &packet.Addr{HardwareAddr: addr})
return err
}
// Reply constructs and sends a reply to an ARP request. On the ARP
// layer, it will be addressed to the sender address of the packet. On
// the ethernet layer, it will be sent to the actual remote address
// from which the request was received.
//
// For more fine-grained control, use WriteTo to write a custom
// response.
func (c *Client) Reply(req *Packet, hwAddr net.HardwareAddr, ip netip.Addr) error {
p, err := NewPacket(OperationReply, hwAddr, ip, req.SenderHardwareAddr, req.SenderIP)
if err != nil {
return err
}
return c.WriteTo(p, req.SenderHardwareAddr)
}
// Copyright (c) 2012 The Go Authors. All rights reserved.
// Source code in this file is based on src/net/interface_linux.go,
// from the Go standard library. The Go license can be found here:
// https://golang.org/LICENSE.
// Documentation taken from net.PacketConn interface. Thanks:
// http://golang.org/pkg/net/#PacketConn.
// SetDeadline sets the read and write deadlines associated with the
// connection.
func (c *Client) SetDeadline(t time.Time) error {
return c.p.SetDeadline(t)
}
// SetReadDeadline sets the deadline for future raw socket read calls.
// If the deadline is reached, a raw socket read will fail with a timeout
// (see type net.Error) instead of blocking.
// A zero value for t means a raw socket read will not time out.
func (c *Client) SetReadDeadline(t time.Time) error {
return c.p.SetReadDeadline(t)
}
// SetWriteDeadline sets the deadline for future raw socket write calls.
// If the deadline is reached, a raw socket write will fail with a timeout
// (see type net.Error) instead of blocking.
// A zero value for t means a raw socket write will not time out.
// Even if a write times out, it may return n > 0, indicating that
// some of the data was successfully written.
func (c *Client) SetWriteDeadline(t time.Time) error {
return c.p.SetWriteDeadline(t)
}
// HardwareAddr fetches the hardware address for the interface associated
// with the connection.
func (c Client) HardwareAddr() net.HardwareAddr {
return c.ifi.HardwareAddr
}
// firstIPv4Addr attempts to retrieve the first detected IPv4 address from an
// input slice of network addresses.
func firstIPv4Addr(addrs []netip.Addr) (netip.Addr, error) {
for _, a := range addrs {
if a.Is4() {
return a, nil
}
}
return netip.Addr{}, errNoIPv4Addr
}
|