mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-07-05 21:04:23 +08:00
251 lines
6.6 KiB
Go
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
|
|
}
|