| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- package arp
- import (
- "encoding/binary"
- "errors"
- "io"
- "net"
- "github.com/mdlayher/ethernet"
- )
- var (
- // ErrInvalidHardwareAddr is returned when one or more invalid hardware
- // addresses are passed to NewPacket.
- ErrInvalidHardwareAddr = errors.New("invalid hardware address")
- // ErrInvalidIP is returned when one or more invalid IPv4 addresses are
- // passed to NewPacket.
- ErrInvalidIP = errors.New("invalid IPv4 address")
- // errInvalidARPPacket is returned when an ethernet frame does not
- // indicate that an ARP packet is contained in its payload.
- errInvalidARPPacket = errors.New("invalid ARP packet")
- )
- //go:generate stringer -output=string.go -type=Operation
- // An Operation is an ARP operation, such as request or reply.
- type Operation uint16
- // Operation constants which indicate an ARP request or reply.
- const (
- OperationRequest Operation = 1
- OperationReply Operation = 2
- )
- // A Packet is a raw ARP packet, as described in RFC 826.
- type Packet struct {
- // HardwareType specifies an IANA-assigned hardware type, as described
- // in RFC 826.
- HardwareType uint16
- // ProtocolType specifies the internetwork protocol for which the ARP
- // request is intended. Typically, this is the IPv4 EtherType.
- ProtocolType uint16
- // HardwareAddrLength specifies the length of the sender and target
- // hardware addresses included in a Packet.
- HardwareAddrLength uint8
- // IPLength specifies the length of the sender and target IPv4 addresses
- // included in a Packet.
- IPLength uint8
- // Operation specifies the ARP operation being performed, such as request
- // or reply.
- Operation Operation
- // SenderHardwareAddr specifies the hardware address of the sender of this
- // Packet.
- SenderHardwareAddr net.HardwareAddr
- // SenderIP specifies the IPv4 address of the sender of this Packet.
- SenderIP net.IP
- // TargetHardwareAddr specifies the hardware address of the target of this
- // Packet.
- TargetHardwareAddr net.HardwareAddr
- // TargetIP specifies the IPv4 address of the target of this Packet.
- TargetIP net.IP
- }
- // NewPacket creates a new Packet from an input Operation and hardware/IPv4
- // address values for both a sender and target.
- //
- // If either hardware address is less than 6 bytes in length, or there is a
- // length mismatch between the two, ErrInvalidHardwareAddr is returned.
- //
- // If either IP address is not an IPv4 address, or there is a length mismatch
- // between the two, ErrInvalidIP is returned.
- func NewPacket(op Operation, srcHW net.HardwareAddr, srcIP net.IP, dstHW net.HardwareAddr, dstIP net.IP) (*Packet, error) {
- // Validate hardware addresses for minimum length, and matching length
- if len(srcHW) < 6 {
- return nil, ErrInvalidHardwareAddr
- }
- if len(dstHW) < 6 {
- return nil, ErrInvalidHardwareAddr
- }
- if len(srcHW) != len(dstHW) {
- return nil, ErrInvalidHardwareAddr
- }
- // Validate IP addresses to ensure they are IPv4 addresses, and
- // correct length
- srcIP = srcIP.To4()
- if srcIP == nil {
- return nil, ErrInvalidIP
- }
- dstIP = dstIP.To4()
- if dstIP == nil {
- return nil, ErrInvalidIP
- }
- return &Packet{
- // There is no Go-native way to detect hardware type of a network
- // interface, so default to 1 (ethernet 10Mb) for now
- HardwareType: 1,
- // Default to EtherType for IPv4
- ProtocolType: uint16(ethernet.EtherTypeIPv4),
- // Populate other fields using input data
- HardwareAddrLength: uint8(len(srcHW)),
- IPLength: uint8(len(srcIP)),
- Operation: op,
- SenderHardwareAddr: srcHW,
- SenderIP: srcIP,
- TargetHardwareAddr: dstHW,
- TargetIP: dstIP,
- }, nil
- }
- // MarshalBinary allocates a byte slice containing the data from a Packet.
- //
- // MarshalBinary never returns an error.
- func (p *Packet) MarshalBinary() ([]byte, error) {
- // 2 bytes: hardware type
- // 2 bytes: protocol type
- // 1 byte : hardware address length
- // 1 byte : protocol length
- // 2 bytes: operation
- // N bytes: source hardware address
- // N bytes: source protocol address
- // N bytes: target hardware address
- // N bytes: target protocol address
- // Though an IPv4 address should always 4 bytes, go-fuzz
- // very quickly created several crasher scenarios which
- // indicated that these values can lie.
- b := make([]byte, 2+2+1+1+2+(p.IPLength*2)+(p.HardwareAddrLength*2))
- // Marshal fixed length data
- binary.BigEndian.PutUint16(b[0:2], p.HardwareType)
- binary.BigEndian.PutUint16(b[2:4], p.ProtocolType)
- b[4] = p.HardwareAddrLength
- b[5] = p.IPLength
- binary.BigEndian.PutUint16(b[6:8], uint16(p.Operation))
- // Marshal variable length data at correct offset using lengths
- // defined in p
- n := 8
- hal := int(p.HardwareAddrLength)
- pl := int(p.IPLength)
- copy(b[n:n+hal], p.SenderHardwareAddr)
- n += hal
- copy(b[n:n+pl], p.SenderIP)
- n += pl
- copy(b[n:n+hal], p.TargetHardwareAddr)
- n += hal
- copy(b[n:n+pl], p.TargetIP)
- return b, nil
- }
- // UnmarshalBinary unmarshals a raw byte slice into a Packet.
- func (p *Packet) UnmarshalBinary(b []byte) error {
- // Must have enough room to retrieve hardware address and IP lengths
- if len(b) < 8 {
- return io.ErrUnexpectedEOF
- }
- // Retrieve fixed length data
- p.HardwareType = binary.BigEndian.Uint16(b[0:2])
- p.ProtocolType = binary.BigEndian.Uint16(b[2:4])
- p.HardwareAddrLength = b[4]
- p.IPLength = b[5]
- p.Operation = Operation(binary.BigEndian.Uint16(b[6:8]))
- // Unmarshal variable length data at correct offset using lengths
- // defined by ml and il
- //
- // These variables are meant to improve readability of offset calculations
- // for the code below
- n := 8
- ml := int(p.HardwareAddrLength)
- ml2 := ml * 2
- il := int(p.IPLength)
- il2 := il * 2
- // Must have enough room to retrieve both hardware address and IP addresses
- addrl := n + ml2 + il2
- if len(b) < addrl {
- return io.ErrUnexpectedEOF
- }
- // Allocate single byte slice to store address information, which
- // is resliced into fields
- bb := make([]byte, addrl-n)
- // Sender hardware address
- copy(bb[0:ml], b[n:n+ml])
- p.SenderHardwareAddr = bb[0:ml]
- n += ml
- // Sender IP address
- copy(bb[ml:ml+il], b[n:n+il])
- p.SenderIP = bb[ml : ml+il]
- n += il
- // Target hardware address
- copy(bb[ml+il:ml2+il], b[n:n+ml])
- p.TargetHardwareAddr = bb[ml+il : ml2+il]
- n += ml
- // Target IP address
- copy(bb[ml2+il:ml2+il2], b[n:n+il])
- p.TargetIP = bb[ml2+il : ml2+il2]
- return nil
- }
- func parsePacket(buf []byte) (*Packet, *ethernet.Frame, error) {
- f := new(ethernet.Frame)
- if err := f.UnmarshalBinary(buf); err != nil {
- return nil, nil, err
- }
- // Ignore frames which do not have ARP EtherType
- if f.EtherType != ethernet.EtherTypeARP {
- return nil, nil, errInvalidARPPacket
- }
- p := new(Packet)
- if err := p.UnmarshalBinary(f.Payload); err != nil {
- return nil, nil, err
- }
- return p, f, nil
- }
|