Files
cloudpods/vendor/github.com/mdlayher/arp/packet.go
wanyaoqi 7371f1ca55 dhcp use raw socket
vendor update
update golang/x/sys
2019-06-21 13:42:37 +08:00

251 lines
6.6 KiB
Go

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
}