227 lines
5.1 KiB
Go
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
|
|
}
|