Files
tsnet-proxy/vendor/github.com/jsimonetti/rtnetlink/neigh.go
2024-11-01 17:43:06 +00:00

227 lines
5.1 KiB
Go

package rtnetlink
import (
"errors"
"fmt"
"net"
"github.com/jsimonetti/rtnetlink/internal/unix"
"github.com/mdlayher/netlink"
)
var (
// errInvalidNeighMessage is returned when a LinkMessage is malformed.
errInvalidNeighMessage = errors.New("rtnetlink NeighMessage is invalid or too short")
// errInvalidNeighMessageAttr is returned when neigh attributes are malformed.
errInvalidNeighMessageAttr = errors.New("rtnetlink NeighMessage has a wrong attribute data length")
)
var _ Message = &NeighMessage{}
// A NeighMessage is a route netlink neighbor message.
type NeighMessage struct {
// Always set to AF_UNSPEC (0)
Family uint16
// Unique interface index
Index uint32
// Neighbor State is a bitmask of neighbor states (see rtnetlink(7))
State uint16
// Neighbor flags
Flags uint8
// Neighbor type
Type uint8
// Attributes List
Attributes *NeighAttributes
}
// MarshalBinary marshals a NeighMessage into a byte slice.
func (m *NeighMessage) MarshalBinary() ([]byte, error) {
b := make([]byte, unix.SizeofNdMsg)
b[0] = uint8(m.Family)
// bytes 2-4 are padding
nativeEndian.PutUint32(b[4:8], m.Index)
nativeEndian.PutUint16(b[8:10], m.State)
b[10] = m.Flags
b[11] = m.Type
if m.Attributes != nil {
ae := netlink.NewAttributeEncoder()
ae.ByteOrder = nativeEndian
err := m.Attributes.encode(ae)
if err != nil {
return nil, err
}
a, err := ae.Encode()
if err != nil {
return nil, err
}
return append(b, a...), nil
}
return b, nil
}
// UnmarshalBinary unmarshals the contents of a byte slice into a NeighMessage.
func (m *NeighMessage) UnmarshalBinary(b []byte) error {
l := len(b)
if l < unix.SizeofNdMsg {
return errInvalidNeighMessage
}
m.Family = uint16(b[0])
m.Index = nativeEndian.Uint32(b[4:8])
m.State = nativeEndian.Uint16(b[8:10])
m.Flags = b[10]
m.Type = b[11]
if l > unix.SizeofNdMsg {
m.Attributes = &NeighAttributes{}
ad, err := netlink.NewAttributeDecoder(b[unix.SizeofNdMsg:])
if err != nil {
return err
}
ad.ByteOrder = nativeEndian
err = m.Attributes.decode(ad)
if err != nil {
return err
}
}
return nil
}
// rtMessage is an empty method to sattisfy the Message interface.
func (*NeighMessage) rtMessage() {}
// NeighService is used to retrieve rtnetlink family information.
type NeighService struct {
c *Conn
}
// New creates a new interface using the LinkMessage information.
func (l *NeighService) New(req *NeighMessage) error {
flags := netlink.Request | netlink.Create | netlink.Acknowledge | netlink.Excl
_, err := l.c.Execute(req, unix.RTM_NEWNEIGH, flags)
if err != nil {
return err
}
return nil
}
// Delete removes an neighbor entry by index.
func (l *NeighService) Delete(index uint32) error {
req := &NeighMessage{}
flags := netlink.Request | netlink.Acknowledge
_, err := l.c.Execute(req, unix.RTM_DELNEIGH, flags)
if err != nil {
return err
}
return nil
}
// List retrieves all neighbors.
func (l *NeighService) List() ([]NeighMessage, error) {
req := NeighMessage{}
flags := netlink.Request | netlink.Dump
msgs, err := l.c.Execute(&req, unix.RTM_GETNEIGH, flags)
if err != nil {
return nil, err
}
neighs := make([]NeighMessage, len(msgs))
for i := range msgs {
neighs[i] = *msgs[i].(*NeighMessage)
}
return neighs, nil
}
// NeighCacheInfo contains neigh information
type NeighCacheInfo struct {
Confirmed uint32
Used uint32
Updated uint32
RefCount uint32
}
// UnmarshalBinary unmarshals the contents of a byte slice into a NeighMessage.
func (n *NeighCacheInfo) unmarshalBinary(b []byte) error {
if len(b) != 16 {
return fmt.Errorf("incorrect size, want: 16, got: %d", len(b))
}
n.Confirmed = nativeEndian.Uint32(b[0:4])
n.Used = nativeEndian.Uint32(b[4:8])
n.Updated = nativeEndian.Uint32(b[8:12])
n.RefCount = nativeEndian.Uint32(b[12:16])
return nil
}
// NeighAttributes contains all attributes for a neighbor.
type NeighAttributes struct {
Address net.IP // a neighbor cache n/w layer destination address
LLAddress net.HardwareAddr // a neighbor cache link layer address
CacheInfo *NeighCacheInfo // cache statistics
IfIndex uint32
}
func (a *NeighAttributes) decode(ad *netlink.AttributeDecoder) error {
for ad.Next() {
switch ad.Type() {
case unix.NDA_UNSPEC:
// unused attribute
case unix.NDA_DST:
l := len(ad.Bytes())
if l != 4 && l != 16 {
return errInvalidNeighMessageAttr
}
a.Address = ad.Bytes()
case unix.NDA_LLADDR:
// Allow IEEE 802 MAC-48, EUI-48, EUI-64, or 20-octet
// IP over InfiniBand link-layer addresses
l := len(ad.Bytes())
if l == 0 {
// Ignore empty addresses.
continue
}
if l != 6 && l != 8 && l != 20 {
return errInvalidNeighMessageAttr
}
a.LLAddress = ad.Bytes()
case unix.NDA_CACHEINFO:
a.CacheInfo = &NeighCacheInfo{}
err := a.CacheInfo.unmarshalBinary(ad.Bytes())
if err != nil {
return err
}
case unix.NDA_IFINDEX:
a.IfIndex = ad.Uint32()
}
}
return nil
}
func (a *NeighAttributes) encode(ae *netlink.AttributeEncoder) error {
ae.Uint16(unix.NDA_UNSPEC, 0)
ae.Bytes(unix.NDA_DST, a.Address)
ae.Bytes(unix.NDA_LLADDR, a.LLAddress)
ae.Uint32(unix.NDA_IFINDEX, a.IfIndex)
return nil
}