...

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

Documentation: github.com/mdlayher/arp

     1  package arp
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"io"
     8  	"net"
     9  	"net/netip"
    10  
    11  	"github.com/mdlayher/ethernet"
    12  )
    13  
    14  var (
    15  	// ErrInvalidHardwareAddr is returned when one or more invalid hardware
    16  	// addresses are passed to NewPacket.
    17  	ErrInvalidHardwareAddr = errors.New("invalid hardware address")
    18  
    19  	// ErrInvalidIP is returned when one or more invalid IPv4 addresses are
    20  	// passed to NewPacket.
    21  	ErrInvalidIP = errors.New("invalid IPv4 address")
    22  
    23  	// errInvalidARPPacket is returned when an ethernet frame does not
    24  	// indicate that an ARP packet is contained in its payload.
    25  	errInvalidARPPacket = errors.New("invalid ARP packet")
    26  )
    27  
    28  //go:generate stringer -output=string.go -type=Operation
    29  
    30  // An Operation is an ARP operation, such as request or reply.
    31  type Operation uint16
    32  
    33  // Operation constants which indicate an ARP request or reply.
    34  const (
    35  	OperationRequest Operation = 1
    36  	OperationReply   Operation = 2
    37  )
    38  
    39  // A Packet is a raw ARP packet, as described in RFC 826.
    40  type Packet struct {
    41  	// HardwareType specifies an IANA-assigned hardware type, as described
    42  	// in RFC 826.
    43  	HardwareType uint16
    44  
    45  	// ProtocolType specifies the internetwork protocol for which the ARP
    46  	// request is intended.  Typically, this is the IPv4 EtherType.
    47  	ProtocolType uint16
    48  
    49  	// HardwareAddrLength specifies the length of the sender and target
    50  	// hardware addresses included in a Packet.
    51  	HardwareAddrLength uint8
    52  
    53  	// IPLength specifies the length of the sender and target IPv4 addresses
    54  	// included in a Packet.
    55  	IPLength uint8
    56  
    57  	// Operation specifies the ARP operation being performed, such as request
    58  	// or reply.
    59  	Operation Operation
    60  
    61  	// SenderHardwareAddr specifies the hardware address of the sender of this
    62  	// Packet.
    63  	SenderHardwareAddr net.HardwareAddr
    64  
    65  	// SenderIP specifies the IPv4 address of the sender of this Packet.
    66  	SenderIP netip.Addr
    67  
    68  	// TargetHardwareAddr specifies the hardware address of the target of this
    69  	// Packet.
    70  	TargetHardwareAddr net.HardwareAddr
    71  
    72  	// TargetIP specifies the IPv4 address of the target of this Packet.
    73  	TargetIP netip.Addr
    74  }
    75  
    76  // NewPacket creates a new Packet from an input Operation and hardware/IPv4
    77  // address values for both a sender and target.
    78  //
    79  // If either hardware address is less than 6 bytes in length, or there is a
    80  // length mismatch between the two, ErrInvalidHardwareAddr is returned.
    81  //
    82  // If either IP address is not an IPv4 address, or there is a length mismatch
    83  // between the two, ErrInvalidIP is returned.
    84  func NewPacket(op Operation, srcHW net.HardwareAddr, srcIP netip.Addr, dstHW net.HardwareAddr, dstIP netip.Addr) (*Packet, error) {
    85  	// Validate hardware addresses for minimum length, and matching length
    86  	if len(srcHW) < 6 {
    87  		return nil, ErrInvalidHardwareAddr
    88  	}
    89  	if len(dstHW) < 6 {
    90  		return nil, ErrInvalidHardwareAddr
    91  	}
    92  	if !bytes.Equal(ethernet.Broadcast, dstHW) && len(srcHW) != len(dstHW) {
    93  		return nil, ErrInvalidHardwareAddr
    94  	}
    95  
    96  	// Validate IP addresses to ensure they are IPv4 addresses, and
    97  	// correct length
    98  	var invalidIP netip.Addr
    99  	if !srcIP.IsValid() || !srcIP.Is4() {
   100  		return nil, ErrInvalidIP
   101  	}
   102  	if !dstIP.Is4() || dstIP == invalidIP {
   103  		return nil, ErrInvalidIP
   104  	}
   105  
   106  	return &Packet{
   107  		// There is no Go-native way to detect hardware type of a network
   108  		// interface, so default to 1 (ethernet 10Mb) for now
   109  		HardwareType: 1,
   110  
   111  		// Default to EtherType for IPv4
   112  		ProtocolType: uint16(ethernet.EtherTypeIPv4),
   113  
   114  		// Populate other fields using input data
   115  		HardwareAddrLength: uint8(len(srcHW)),
   116  		IPLength:           uint8(4),
   117  		Operation:          op,
   118  		SenderHardwareAddr: srcHW,
   119  		SenderIP:           srcIP,
   120  		TargetHardwareAddr: dstHW,
   121  		TargetIP:           dstIP,
   122  	}, nil
   123  }
   124  
   125  // MarshalBinary allocates a byte slice containing the data from a Packet.
   126  //
   127  // MarshalBinary never returns an error.
   128  func (p *Packet) MarshalBinary() ([]byte, error) {
   129  	// 2 bytes: hardware type
   130  	// 2 bytes: protocol type
   131  	// 1 byte : hardware address length
   132  	// 1 byte : protocol length
   133  	// 2 bytes: operation
   134  	// N bytes: source hardware address
   135  	// N bytes: source protocol address
   136  	// N bytes: target hardware address
   137  	// N bytes: target protocol address
   138  
   139  	// Though an IPv4 address should always 4 bytes, go-fuzz
   140  	// very quickly created several crasher scenarios which
   141  	// indicated that these values can lie.
   142  	b := make([]byte, 2+2+1+1+2+(p.IPLength*2)+(p.HardwareAddrLength*2))
   143  
   144  	// Marshal fixed length data
   145  
   146  	binary.BigEndian.PutUint16(b[0:2], p.HardwareType)
   147  	binary.BigEndian.PutUint16(b[2:4], p.ProtocolType)
   148  
   149  	b[4] = p.HardwareAddrLength
   150  	b[5] = p.IPLength
   151  
   152  	binary.BigEndian.PutUint16(b[6:8], uint16(p.Operation))
   153  
   154  	// Marshal variable length data at correct offset using lengths
   155  	// defined in p
   156  
   157  	n := 8
   158  	hal := int(p.HardwareAddrLength)
   159  	pl := int(p.IPLength)
   160  
   161  	copy(b[n:n+hal], p.SenderHardwareAddr)
   162  	n += hal
   163  
   164  	sender4 := p.SenderIP.As4()
   165  	copy(b[n:n+pl], sender4[:])
   166  	n += pl
   167  
   168  	copy(b[n:n+hal], p.TargetHardwareAddr)
   169  	n += hal
   170  
   171  	target4 := p.TargetIP.As4()
   172  	copy(b[n:n+pl], target4[:])
   173  
   174  	return b, nil
   175  }
   176  
   177  // UnmarshalBinary unmarshals a raw byte slice into a Packet.
   178  func (p *Packet) UnmarshalBinary(b []byte) error {
   179  	// Must have enough room to retrieve hardware address and IP lengths
   180  	if len(b) < 8 {
   181  		return io.ErrUnexpectedEOF
   182  	}
   183  
   184  	// Retrieve fixed length data
   185  
   186  	p.HardwareType = binary.BigEndian.Uint16(b[0:2])
   187  	p.ProtocolType = binary.BigEndian.Uint16(b[2:4])
   188  
   189  	p.HardwareAddrLength = b[4]
   190  	p.IPLength = b[5]
   191  
   192  	p.Operation = Operation(binary.BigEndian.Uint16(b[6:8]))
   193  
   194  	// Unmarshal variable length data at correct offset using lengths
   195  	// defined by ml and il
   196  	//
   197  	// These variables are meant to improve readability of offset calculations
   198  	// for the code below
   199  	n := 8
   200  	ml := int(p.HardwareAddrLength)
   201  	ml2 := ml * 2
   202  	il := int(p.IPLength)
   203  	il2 := il * 2
   204  
   205  	// Must have enough room to retrieve both hardware address and IP addresses
   206  	addrl := n + ml2 + il2
   207  	if len(b) < addrl {
   208  		return io.ErrUnexpectedEOF
   209  	}
   210  
   211  	// Allocate single byte slice to store address information, which
   212  	// is resliced into fields
   213  	bb := make([]byte, addrl-n)
   214  
   215  	// Sender hardware address
   216  	copy(bb[0:ml], b[n:n+ml])
   217  	p.SenderHardwareAddr = bb[0:ml]
   218  	n += ml
   219  
   220  	// Sender IP address
   221  	copy(bb[ml:ml+il], b[n:n+il])
   222  	senderIP, ok := netip.AddrFromSlice(bb[ml : ml+il])
   223  	if !ok {
   224  		return errors.New("Invalid Sender IP address")
   225  	}
   226  	p.SenderIP = senderIP
   227  	n += il
   228  
   229  	// Target hardware address
   230  	copy(bb[ml+il:ml2+il], b[n:n+ml])
   231  	p.TargetHardwareAddr = bb[ml+il : ml2+il]
   232  	n += ml
   233  
   234  	// Target IP address
   235  	copy(bb[ml2+il:ml2+il2], b[n:n+il])
   236  	targetIP, ok := netip.AddrFromSlice(bb[ml2+il : ml2+il2])
   237  	if !ok {
   238  		return errors.New("Invalid Target IP address")
   239  	}
   240  	p.TargetIP = targetIP
   241  
   242  	return nil
   243  }
   244  
   245  func parsePacket(buf []byte) (*Packet, *ethernet.Frame, error) {
   246  	f := new(ethernet.Frame)
   247  	if err := f.UnmarshalBinary(buf); err != nil {
   248  		return nil, nil, err
   249  	}
   250  
   251  	// Ignore frames which do not have ARP EtherType
   252  	if f.EtherType != ethernet.EtherTypeARP {
   253  		return nil, nil, errInvalidARPPacket
   254  	}
   255  
   256  	p := new(Packet)
   257  	if err := p.UnmarshalBinary(f.Payload); err != nil {
   258  		return nil, nil, err
   259  	}
   260  	return p, f, nil
   261  }
   262  

View as plain text