...

Source file src/github.com/mdlayher/arp/client.go

Documentation: github.com/mdlayher/arp

     1  package arp
     2  
     3  import (
     4  	"errors"
     5  	"net"
     6  	"net/netip"
     7  	"time"
     8  
     9  	"github.com/mdlayher/ethernet"
    10  	"github.com/mdlayher/packet"
    11  )
    12  
    13  // errNoIPv4Addr is returned when an interface does not have an IPv4
    14  // address.
    15  var errNoIPv4Addr = errors.New("no IPv4 address available for interface")
    16  
    17  // protocolARP is the uint16 EtherType representation of ARP (Address
    18  // Resolution Protocol, RFC 826).
    19  const protocolARP = 0x0806
    20  
    21  // A Client is an ARP client, which can be used to send and receive
    22  // ARP packets.
    23  type Client struct {
    24  	ifi *net.Interface
    25  	ip  netip.Addr
    26  	p   net.PacketConn
    27  }
    28  
    29  // Dial creates a new Client using the specified network interface.
    30  // Dial retrieves the IPv4 address of the interface and binds a raw socket
    31  // to send and receive ARP packets.
    32  func Dial(ifi *net.Interface) (*Client, error) {
    33  	// Open raw socket to send and receive ARP packets using ethernet frames
    34  	// we build ourselves.
    35  	p, err := packet.Listen(ifi, packet.Raw, protocolARP, nil)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	return New(ifi, p)
    40  }
    41  
    42  // New creates a new Client using the specified network interface
    43  // and net.PacketConn. This allows the caller to define exactly how they bind to the
    44  // net.PacketConn. This is most useful to define what protocol to pass to socket(7).
    45  //
    46  // In most cases, callers would be better off calling Dial.
    47  func New(ifi *net.Interface, p net.PacketConn) (*Client, error) {
    48  	// Check for usable IPv4 addresses for the Client
    49  	addrs, err := ifi.Addrs()
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	ipaddrs := make([]netip.Addr, len(addrs))
    55  	for i, a := range addrs {
    56  		ipPrefix, err := netip.ParsePrefix(a.String())
    57  		if err != nil {
    58  			return nil, err
    59  		}
    60  		ipaddrs[i] = ipPrefix.Addr()
    61  	}
    62  
    63  	return newClient(ifi, p, ipaddrs)
    64  }
    65  
    66  // newClient is the internal, generic implementation of newClient.  It is used
    67  // to allow an arbitrary net.PacketConn to be used in a Client, so testing
    68  // is easier to accomplish.
    69  func newClient(ifi *net.Interface, p net.PacketConn, addrs []netip.Addr) (*Client, error) {
    70  	ip, err := firstIPv4Addr(addrs)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return &Client{
    76  		ifi: ifi,
    77  		ip:  ip,
    78  		p:   p,
    79  	}, nil
    80  }
    81  
    82  // Close closes the Client's raw socket and stops sending and receiving
    83  // ARP packets.
    84  func (c *Client) Close() error {
    85  	return c.p.Close()
    86  }
    87  
    88  // Request sends an ARP request, asking for the hardware address
    89  // associated with an IPv4 address. The response, if any, can be read
    90  // with the Read method.
    91  //
    92  // Unlike Resolve, which provides an easier interface for getting the
    93  // hardware address, Request allows sending many requests in a row,
    94  // retrieving the responses afterwards.
    95  func (c *Client) Request(ip netip.Addr) error {
    96  	if !c.ip.IsValid() {
    97  		return errNoIPv4Addr
    98  	}
    99  
   100  	// Create ARP packet for broadcast address to attempt to find the
   101  	// hardware address of the input IP address
   102  	arp, err := NewPacket(OperationRequest, c.ifi.HardwareAddr, c.ip, ethernet.Broadcast, ip)
   103  	if err != nil {
   104  		return err
   105  	}
   106  	return c.WriteTo(arp, ethernet.Broadcast)
   107  }
   108  
   109  // Resolve performs an ARP request, attempting to retrieve the
   110  // hardware address of a machine using its IPv4 address. Resolve must not
   111  // be used concurrently with Read. If you're using Read (usually in a
   112  // loop), you need to use Request instead. Resolve may read more than
   113  // one message if it receives messages unrelated to the request.
   114  func (c *Client) Resolve(ip netip.Addr) (net.HardwareAddr, error) {
   115  	err := c.Request(ip)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	// Loop and wait for replies
   121  	for {
   122  		arp, _, err := c.Read()
   123  		if err != nil {
   124  			return nil, err
   125  		}
   126  
   127  		if arp.Operation != OperationReply || arp.SenderIP != ip {
   128  			continue
   129  		}
   130  
   131  		return arp.SenderHardwareAddr, nil
   132  	}
   133  }
   134  
   135  // Read reads a single ARP packet and returns it, together with its
   136  // ethernet frame.
   137  func (c *Client) Read() (*Packet, *ethernet.Frame, error) {
   138  	buf := make([]byte, 128)
   139  	for {
   140  		n, _, err := c.p.ReadFrom(buf)
   141  		if err != nil {
   142  			return nil, nil, err
   143  		}
   144  
   145  		p, eth, err := parsePacket(buf[:n])
   146  		if err != nil {
   147  			if err == errInvalidARPPacket {
   148  				continue
   149  			}
   150  			return nil, nil, err
   151  		}
   152  		return p, eth, nil
   153  	}
   154  }
   155  
   156  // WriteTo writes a single ARP packet to addr. Note that addr should,
   157  // but doesn't have to, match the target hardware address of the ARP
   158  // packet.
   159  func (c *Client) WriteTo(p *Packet, addr net.HardwareAddr) error {
   160  	pb, err := p.MarshalBinary()
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	f := &ethernet.Frame{
   166  		Destination: addr,
   167  		Source:      p.SenderHardwareAddr,
   168  		EtherType:   ethernet.EtherTypeARP,
   169  		Payload:     pb,
   170  	}
   171  
   172  	fb, err := f.MarshalBinary()
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	_, err = c.p.WriteTo(fb, &packet.Addr{HardwareAddr: addr})
   178  	return err
   179  }
   180  
   181  // Reply constructs and sends a reply to an ARP request. On the ARP
   182  // layer, it will be addressed to the sender address of the packet. On
   183  // the ethernet layer, it will be sent to the actual remote address
   184  // from which the request was received.
   185  //
   186  // For more fine-grained control, use WriteTo to write a custom
   187  // response.
   188  func (c *Client) Reply(req *Packet, hwAddr net.HardwareAddr, ip netip.Addr) error {
   189  	p, err := NewPacket(OperationReply, hwAddr, ip, req.SenderHardwareAddr, req.SenderIP)
   190  	if err != nil {
   191  		return err
   192  	}
   193  	return c.WriteTo(p, req.SenderHardwareAddr)
   194  }
   195  
   196  // Copyright (c) 2012 The Go Authors. All rights reserved.
   197  // Source code in this file is based on src/net/interface_linux.go,
   198  // from the Go standard library.  The Go license can be found here:
   199  // https://golang.org/LICENSE.
   200  
   201  // Documentation taken from net.PacketConn interface.  Thanks:
   202  // http://golang.org/pkg/net/#PacketConn.
   203  
   204  // SetDeadline sets the read and write deadlines associated with the
   205  // connection.
   206  func (c *Client) SetDeadline(t time.Time) error {
   207  	return c.p.SetDeadline(t)
   208  }
   209  
   210  // SetReadDeadline sets the deadline for future raw socket read calls.
   211  // If the deadline is reached, a raw socket read will fail with a timeout
   212  // (see type net.Error) instead of blocking.
   213  // A zero value for t means a raw socket read will not time out.
   214  func (c *Client) SetReadDeadline(t time.Time) error {
   215  	return c.p.SetReadDeadline(t)
   216  }
   217  
   218  // SetWriteDeadline sets the deadline for future raw socket write calls.
   219  // If the deadline is reached, a raw socket write will fail with a timeout
   220  // (see type net.Error) instead of blocking.
   221  // A zero value for t means a raw socket write will not time out.
   222  // Even if a write times out, it may return n > 0, indicating that
   223  // some of the data was successfully written.
   224  func (c *Client) SetWriteDeadline(t time.Time) error {
   225  	return c.p.SetWriteDeadline(t)
   226  }
   227  
   228  // HardwareAddr fetches the hardware address for the interface associated
   229  // with the connection.
   230  func (c Client) HardwareAddr() net.HardwareAddr {
   231  	return c.ifi.HardwareAddr
   232  }
   233  
   234  // firstIPv4Addr attempts to retrieve the first detected IPv4 address from an
   235  // input slice of network addresses.
   236  func firstIPv4Addr(addrs []netip.Addr) (netip.Addr, error) {
   237  	for _, a := range addrs {
   238  		if a.Is4() {
   239  			return a, nil
   240  		}
   241  	}
   242  	return netip.Addr{}, errNoIPv4Addr
   243  }
   244  

View as plain text