k3s/vendor/github.com/vishvananda/netlink/nl/nl_linux.go

761 lines
18 KiB
Go
Raw Normal View History

2019-01-12 04:58:27 +00:00
// Package nl has low level primitives for making Netlink calls.
package nl
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"runtime"
"sync"
"sync/atomic"
"syscall"
"unsafe"
"github.com/vishvananda/netns"
2019-09-27 21:51:53 +00:00
"golang.org/x/sys/unix"
2019-01-12 04:58:27 +00:00
)
const (
// Family type definitions
2019-09-27 21:51:53 +00:00
FAMILY_ALL = unix.AF_UNSPEC
FAMILY_V4 = unix.AF_INET
FAMILY_V6 = unix.AF_INET6
2020-08-10 17:43:49 +00:00
FAMILY_MPLS = unix.AF_MPLS
// Arbitrary set value (greater than default 4k) to allow receiving
// from kernel more verbose messages e.g. for statistics,
// tc rules or filters, or other more memory requiring data.
RECEIVE_BUFFER_SIZE = 65536
// Kernel netlink pid
PidKernel uint32 = 0
2019-01-12 04:58:27 +00:00
)
// SupportedNlFamilies contains the list of netlink families this netlink package supports
2019-09-27 21:51:53 +00:00
var SupportedNlFamilies = []int{unix.NETLINK_ROUTE, unix.NETLINK_XFRM, unix.NETLINK_NETFILTER}
2019-01-12 04:58:27 +00:00
var nextSeqNr uint32
// GetIPFamily returns the family type of a net.IP.
func GetIPFamily(ip net.IP) int {
if len(ip) <= net.IPv4len {
return FAMILY_V4
}
if ip.To4() != nil {
return FAMILY_V4
}
return FAMILY_V6
}
var nativeEndian binary.ByteOrder
2020-08-10 17:43:49 +00:00
// NativeEndian gets native endianness for the system
2019-01-12 04:58:27 +00:00
func NativeEndian() binary.ByteOrder {
if nativeEndian == nil {
var x uint32 = 0x01020304
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
nativeEndian = binary.BigEndian
} else {
nativeEndian = binary.LittleEndian
}
}
return nativeEndian
}
// Byte swap a 16 bit value if we aren't big endian
func Swap16(i uint16) uint16 {
if NativeEndian() == binary.BigEndian {
return i
}
return (i&0xff00)>>8 | (i&0xff)<<8
}
// Byte swap a 32 bit value if aren't big endian
func Swap32(i uint32) uint32 {
if NativeEndian() == binary.BigEndian {
return i
}
return (i&0xff000000)>>24 | (i&0xff0000)>>8 | (i&0xff00)<<8 | (i&0xff)<<24
}
type NetlinkRequestData interface {
Len() int
Serialize() []byte
}
// IfInfomsg is related to links, but it is used for list requests as well
type IfInfomsg struct {
2019-09-27 21:51:53 +00:00
unix.IfInfomsg
2019-01-12 04:58:27 +00:00
}
// Create an IfInfomsg with family specified
func NewIfInfomsg(family int) *IfInfomsg {
return &IfInfomsg{
2019-09-27 21:51:53 +00:00
IfInfomsg: unix.IfInfomsg{
2019-01-12 04:58:27 +00:00
Family: uint8(family),
},
}
}
func DeserializeIfInfomsg(b []byte) *IfInfomsg {
2019-09-27 21:51:53 +00:00
return (*IfInfomsg)(unsafe.Pointer(&b[0:unix.SizeofIfInfomsg][0]))
2019-01-12 04:58:27 +00:00
}
func (msg *IfInfomsg) Serialize() []byte {
2019-09-27 21:51:53 +00:00
return (*(*[unix.SizeofIfInfomsg]byte)(unsafe.Pointer(msg)))[:]
2019-01-12 04:58:27 +00:00
}
func (msg *IfInfomsg) Len() int {
2019-09-27 21:51:53 +00:00
return unix.SizeofIfInfomsg
2019-01-12 04:58:27 +00:00
}
func (msg *IfInfomsg) EncapType() string {
switch msg.Type {
case 0:
return "generic"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_ETHER:
2019-01-12 04:58:27 +00:00
return "ether"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_EETHER:
2019-01-12 04:58:27 +00:00
return "eether"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_AX25:
2019-01-12 04:58:27 +00:00
return "ax25"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_PRONET:
2019-01-12 04:58:27 +00:00
return "pronet"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_CHAOS:
2019-01-12 04:58:27 +00:00
return "chaos"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_IEEE802:
2019-01-12 04:58:27 +00:00
return "ieee802"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_ARCNET:
2019-01-12 04:58:27 +00:00
return "arcnet"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_APPLETLK:
2019-01-12 04:58:27 +00:00
return "atalk"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_DLCI:
2019-01-12 04:58:27 +00:00
return "dlci"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_ATM:
2019-01-12 04:58:27 +00:00
return "atm"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_METRICOM:
2019-01-12 04:58:27 +00:00
return "metricom"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_IEEE1394:
2019-01-12 04:58:27 +00:00
return "ieee1394"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_INFINIBAND:
2019-01-12 04:58:27 +00:00
return "infiniband"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_SLIP:
2019-01-12 04:58:27 +00:00
return "slip"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_CSLIP:
2019-01-12 04:58:27 +00:00
return "cslip"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_SLIP6:
2019-01-12 04:58:27 +00:00
return "slip6"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_CSLIP6:
2019-01-12 04:58:27 +00:00
return "cslip6"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_RSRVD:
2019-01-12 04:58:27 +00:00
return "rsrvd"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_ADAPT:
2019-01-12 04:58:27 +00:00
return "adapt"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_ROSE:
2019-01-12 04:58:27 +00:00
return "rose"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_X25:
2019-01-12 04:58:27 +00:00
return "x25"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_HWX25:
2019-01-12 04:58:27 +00:00
return "hwx25"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_PPP:
2019-01-12 04:58:27 +00:00
return "ppp"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_HDLC:
2019-01-12 04:58:27 +00:00
return "hdlc"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_LAPB:
2019-01-12 04:58:27 +00:00
return "lapb"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_DDCMP:
2019-01-12 04:58:27 +00:00
return "ddcmp"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_RAWHDLC:
2019-01-12 04:58:27 +00:00
return "rawhdlc"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_TUNNEL:
2019-01-12 04:58:27 +00:00
return "ipip"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_TUNNEL6:
2019-01-12 04:58:27 +00:00
return "tunnel6"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FRAD:
2019-01-12 04:58:27 +00:00
return "frad"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_SKIP:
2019-01-12 04:58:27 +00:00
return "skip"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_LOOPBACK:
2019-01-12 04:58:27 +00:00
return "loopback"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_LOCALTLK:
2019-01-12 04:58:27 +00:00
return "ltalk"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FDDI:
2019-01-12 04:58:27 +00:00
return "fddi"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_BIF:
2019-01-12 04:58:27 +00:00
return "bif"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_SIT:
2019-01-12 04:58:27 +00:00
return "sit"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_IPDDP:
2019-01-12 04:58:27 +00:00
return "ip/ddp"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_IPGRE:
2019-01-12 04:58:27 +00:00
return "gre"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_PIMREG:
2019-01-12 04:58:27 +00:00
return "pimreg"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_HIPPI:
2019-01-12 04:58:27 +00:00
return "hippi"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_ASH:
2019-01-12 04:58:27 +00:00
return "ash"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_ECONET:
2019-01-12 04:58:27 +00:00
return "econet"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_IRDA:
2019-01-12 04:58:27 +00:00
return "irda"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCPP:
2019-01-12 04:58:27 +00:00
return "fcpp"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCAL:
2019-01-12 04:58:27 +00:00
return "fcal"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCPL:
2019-01-12 04:58:27 +00:00
return "fcpl"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC:
2019-01-12 04:58:27 +00:00
return "fcfb0"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC + 1:
2019-01-12 04:58:27 +00:00
return "fcfb1"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC + 2:
2019-01-12 04:58:27 +00:00
return "fcfb2"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC + 3:
2019-01-12 04:58:27 +00:00
return "fcfb3"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC + 4:
2019-01-12 04:58:27 +00:00
return "fcfb4"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC + 5:
2019-01-12 04:58:27 +00:00
return "fcfb5"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC + 6:
2019-01-12 04:58:27 +00:00
return "fcfb6"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC + 7:
2019-01-12 04:58:27 +00:00
return "fcfb7"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC + 8:
2019-01-12 04:58:27 +00:00
return "fcfb8"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC + 9:
2019-01-12 04:58:27 +00:00
return "fcfb9"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC + 10:
2019-01-12 04:58:27 +00:00
return "fcfb10"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC + 11:
2019-01-12 04:58:27 +00:00
return "fcfb11"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_FCFABRIC + 12:
2019-01-12 04:58:27 +00:00
return "fcfb12"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_IEEE802_TR:
2019-01-12 04:58:27 +00:00
return "tr"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_IEEE80211:
2019-01-12 04:58:27 +00:00
return "ieee802.11"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_IEEE80211_PRISM:
2019-01-12 04:58:27 +00:00
return "ieee802.11/prism"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_IEEE80211_RADIOTAP:
2019-01-12 04:58:27 +00:00
return "ieee802.11/radiotap"
2019-09-27 21:51:53 +00:00
case unix.ARPHRD_IEEE802154:
2019-01-12 04:58:27 +00:00
return "ieee802.15.4"
case 65534:
return "none"
case 65535:
return "void"
}
return fmt.Sprintf("unknown%d", msg.Type)
}
func rtaAlignOf(attrlen int) int {
2019-09-27 21:51:53 +00:00
return (attrlen + unix.RTA_ALIGNTO - 1) & ^(unix.RTA_ALIGNTO - 1)
2019-01-12 04:58:27 +00:00
}
func NewIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg {
msg := NewIfInfomsg(family)
parent.children = append(parent.children, msg)
return msg
}
// Extend RtAttr to handle data and children
type RtAttr struct {
2019-09-27 21:51:53 +00:00
unix.RtAttr
2019-01-12 04:58:27 +00:00
Data []byte
children []NetlinkRequestData
}
// Create a new Extended RtAttr object
func NewRtAttr(attrType int, data []byte) *RtAttr {
return &RtAttr{
2019-09-27 21:51:53 +00:00
RtAttr: unix.RtAttr{
2019-01-12 04:58:27 +00:00
Type: uint16(attrType),
},
children: []NetlinkRequestData{},
Data: data,
}
}
2020-08-10 17:43:49 +00:00
// NewRtAttrChild adds an RtAttr as a child to the parent and returns the new attribute
//
// Deprecated: Use AddRtAttr() on the parent object
2019-01-12 04:58:27 +00:00
func NewRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr {
2020-08-10 17:43:49 +00:00
return parent.AddRtAttr(attrType, data)
}
// AddRtAttr adds an RtAttr as a child and returns the new attribute
func (a *RtAttr) AddRtAttr(attrType int, data []byte) *RtAttr {
2019-01-12 04:58:27 +00:00
attr := NewRtAttr(attrType, data)
2020-08-10 17:43:49 +00:00
a.children = append(a.children, attr)
2019-01-12 04:58:27 +00:00
return attr
}
2020-08-10 17:43:49 +00:00
// AddChild adds an existing NetlinkRequestData as a child.
func (a *RtAttr) AddChild(attr NetlinkRequestData) {
2019-09-27 21:51:53 +00:00
a.children = append(a.children, attr)
}
2019-01-12 04:58:27 +00:00
func (a *RtAttr) Len() int {
if len(a.children) == 0 {
2019-09-27 21:51:53 +00:00
return (unix.SizeofRtAttr + len(a.Data))
2019-01-12 04:58:27 +00:00
}
l := 0
for _, child := range a.children {
l += rtaAlignOf(child.Len())
}
2019-09-27 21:51:53 +00:00
l += unix.SizeofRtAttr
2019-01-12 04:58:27 +00:00
return rtaAlignOf(l + len(a.Data))
}
// Serialize the RtAttr into a byte array
// This can't just unsafe.cast because it must iterate through children.
func (a *RtAttr) Serialize() []byte {
native := NativeEndian()
length := a.Len()
buf := make([]byte, rtaAlignOf(length))
next := 4
if a.Data != nil {
copy(buf[next:], a.Data)
next += rtaAlignOf(len(a.Data))
}
if len(a.children) > 0 {
for _, child := range a.children {
childBuf := child.Serialize()
copy(buf[next:], childBuf)
next += rtaAlignOf(len(childBuf))
}
}
if l := uint16(length); l != 0 {
native.PutUint16(buf[0:2], l)
}
native.PutUint16(buf[2:4], a.Type)
return buf
}
type NetlinkRequest struct {
2019-09-27 21:51:53 +00:00
unix.NlMsghdr
2019-01-12 04:58:27 +00:00
Data []NetlinkRequestData
RawData []byte
Sockets map[int]*SocketHandle
}
// Serialize the Netlink Request into a byte array
func (req *NetlinkRequest) Serialize() []byte {
2019-09-27 21:51:53 +00:00
length := unix.SizeofNlMsghdr
2019-01-12 04:58:27 +00:00
dataBytes := make([][]byte, len(req.Data))
for i, data := range req.Data {
dataBytes[i] = data.Serialize()
length = length + len(dataBytes[i])
}
length += len(req.RawData)
req.Len = uint32(length)
b := make([]byte, length)
2019-09-27 21:51:53 +00:00
hdr := (*(*[unix.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:]
next := unix.SizeofNlMsghdr
2019-01-12 04:58:27 +00:00
copy(b[0:next], hdr)
for _, data := range dataBytes {
for _, dataByte := range data {
b[next] = dataByte
next = next + 1
}
}
// Add the raw data if any
if len(req.RawData) > 0 {
copy(b[next:length], req.RawData)
}
return b
}
func (req *NetlinkRequest) AddData(data NetlinkRequestData) {
2020-08-10 17:43:49 +00:00
req.Data = append(req.Data, data)
2019-01-12 04:58:27 +00:00
}
// AddRawData adds raw bytes to the end of the NetlinkRequest object during serialization
func (req *NetlinkRequest) AddRawData(data []byte) {
2020-08-10 17:43:49 +00:00
req.RawData = append(req.RawData, data...)
2019-01-12 04:58:27 +00:00
}
// Execute the request against a the given sockType.
// Returns a list of netlink messages in serialized format, optionally filtered
// by resType.
func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, error) {
var (
s *NetlinkSocket
err error
)
if req.Sockets != nil {
if sh, ok := req.Sockets[sockType]; ok {
s = sh.Socket
req.Seq = atomic.AddUint32(&sh.Seq, 1)
}
}
sharedSocket := s != nil
if s == nil {
s, err = getNetlinkSocket(sockType)
if err != nil {
return nil, err
}
defer s.Close()
} else {
s.Lock()
defer s.Unlock()
}
if err := s.Send(req); err != nil {
return nil, err
}
pid, err := s.GetPid()
if err != nil {
return nil, err
}
var res [][]byte
done:
for {
2020-08-10 17:43:49 +00:00
msgs, from, err := s.Receive()
2019-01-12 04:58:27 +00:00
if err != nil {
return nil, err
}
2020-08-10 17:43:49 +00:00
if from.Pid != PidKernel {
return nil, fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, PidKernel)
}
2019-01-12 04:58:27 +00:00
for _, m := range msgs {
if m.Header.Seq != req.Seq {
if sharedSocket {
continue
}
return nil, fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, req.Seq)
}
if m.Header.Pid != pid {
2020-08-10 17:43:49 +00:00
continue
2019-01-12 04:58:27 +00:00
}
2019-09-27 21:51:53 +00:00
if m.Header.Type == unix.NLMSG_DONE {
2019-01-12 04:58:27 +00:00
break done
}
2019-09-27 21:51:53 +00:00
if m.Header.Type == unix.NLMSG_ERROR {
2019-01-12 04:58:27 +00:00
native := NativeEndian()
error := int32(native.Uint32(m.Data[0:4]))
if error == 0 {
break done
}
return nil, syscall.Errno(-error)
}
if resType != 0 && m.Header.Type != resType {
continue
}
res = append(res, m.Data)
2019-09-27 21:51:53 +00:00
if m.Header.Flags&unix.NLM_F_MULTI == 0 {
2019-01-12 04:58:27 +00:00
break done
}
}
}
return res, nil
}
// Create a new netlink request from proto and flags
// Note the Len value will be inaccurate once data is added until
// the message is serialized
func NewNetlinkRequest(proto, flags int) *NetlinkRequest {
return &NetlinkRequest{
2019-09-27 21:51:53 +00:00
NlMsghdr: unix.NlMsghdr{
Len: uint32(unix.SizeofNlMsghdr),
2019-01-12 04:58:27 +00:00
Type: uint16(proto),
2019-09-27 21:51:53 +00:00
Flags: unix.NLM_F_REQUEST | uint16(flags),
2019-01-12 04:58:27 +00:00
Seq: atomic.AddUint32(&nextSeqNr, 1),
},
}
}
type NetlinkSocket struct {
fd int32
2019-09-27 21:51:53 +00:00
lsa unix.SockaddrNetlink
2019-01-12 04:58:27 +00:00
sync.Mutex
}
func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
2019-09-27 21:51:53 +00:00
fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW|unix.SOCK_CLOEXEC, protocol)
2019-01-12 04:58:27 +00:00
if err != nil {
return nil, err
}
s := &NetlinkSocket{
fd: int32(fd),
}
2019-09-27 21:51:53 +00:00
s.lsa.Family = unix.AF_NETLINK
if err := unix.Bind(fd, &s.lsa); err != nil {
unix.Close(fd)
2019-01-12 04:58:27 +00:00
return nil, err
}
return s, nil
}
// GetNetlinkSocketAt opens a netlink socket in the network namespace newNs
// and positions the thread back into the network namespace specified by curNs,
// when done. If curNs is close, the function derives the current namespace and
// moves back into it when done. If newNs is close, the socket will be opened
// in the current network namespace.
func GetNetlinkSocketAt(newNs, curNs netns.NsHandle, protocol int) (*NetlinkSocket, error) {
c, err := executeInNetns(newNs, curNs)
if err != nil {
return nil, err
}
defer c()
return getNetlinkSocket(protocol)
}
// executeInNetns sets execution of the code following this call to the
// network namespace newNs, then moves the thread back to curNs if open,
// otherwise to the current netns at the time the function was invoked
// In case of success, the caller is expected to execute the returned function
// at the end of the code that needs to be executed in the network namespace.
// Example:
// func jobAt(...) error {
// d, err := executeInNetns(...)
// if err != nil { return err}
// defer d()
// < code which needs to be executed in specific netns>
// }
// TODO: his function probably belongs to netns pkg.
func executeInNetns(newNs, curNs netns.NsHandle) (func(), error) {
var (
err error
moveBack func(netns.NsHandle) error
closeNs func() error
unlockThd func()
)
restore := func() {
// order matters
if moveBack != nil {
moveBack(curNs)
}
if closeNs != nil {
closeNs()
}
if unlockThd != nil {
unlockThd()
}
}
if newNs.IsOpen() {
runtime.LockOSThread()
unlockThd = runtime.UnlockOSThread
if !curNs.IsOpen() {
if curNs, err = netns.Get(); err != nil {
restore()
return nil, fmt.Errorf("could not get current namespace while creating netlink socket: %v", err)
}
closeNs = curNs.Close
}
if err := netns.Set(newNs); err != nil {
restore()
return nil, fmt.Errorf("failed to set into network namespace %d while creating netlink socket: %v", newNs, err)
}
moveBack = netns.Set
}
return restore, nil
}
// Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE)
// and subscribe it to multicast groups passed in variable argument list.
// Returns the netlink socket on which Receive() method can be called
// to retrieve the messages from the kernel.
func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) {
2019-09-27 21:51:53 +00:00
fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, protocol)
2019-01-12 04:58:27 +00:00
if err != nil {
return nil, err
}
s := &NetlinkSocket{
fd: int32(fd),
}
2019-09-27 21:51:53 +00:00
s.lsa.Family = unix.AF_NETLINK
2019-01-12 04:58:27 +00:00
for _, g := range groups {
s.lsa.Groups |= (1 << (g - 1))
}
2019-09-27 21:51:53 +00:00
if err := unix.Bind(fd, &s.lsa); err != nil {
unix.Close(fd)
2019-01-12 04:58:27 +00:00
return nil, err
}
return s, nil
}
// SubscribeAt works like Subscribe plus let's the caller choose the network
// namespace in which the socket would be opened (newNs). Then control goes back
// to curNs if open, otherwise to the netns at the time this function was called.
func SubscribeAt(newNs, curNs netns.NsHandle, protocol int, groups ...uint) (*NetlinkSocket, error) {
c, err := executeInNetns(newNs, curNs)
if err != nil {
return nil, err
}
defer c()
return Subscribe(protocol, groups...)
}
func (s *NetlinkSocket) Close() {
fd := int(atomic.SwapInt32(&s.fd, -1))
2019-09-27 21:51:53 +00:00
unix.Close(fd)
2019-01-12 04:58:27 +00:00
}
func (s *NetlinkSocket) GetFd() int {
return int(atomic.LoadInt32(&s.fd))
}
func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
fd := int(atomic.LoadInt32(&s.fd))
if fd < 0 {
return fmt.Errorf("Send called on a closed socket")
}
2019-09-27 21:51:53 +00:00
if err := unix.Sendto(fd, request.Serialize(), 0, &s.lsa); err != nil {
2019-01-12 04:58:27 +00:00
return err
}
return nil
}
2020-08-10 17:43:49 +00:00
func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, *unix.SockaddrNetlink, error) {
2019-01-12 04:58:27 +00:00
fd := int(atomic.LoadInt32(&s.fd))
if fd < 0 {
2020-08-10 17:43:49 +00:00
return nil, nil, fmt.Errorf("Receive called on a closed socket")
2019-01-12 04:58:27 +00:00
}
2020-08-10 17:43:49 +00:00
var fromAddr *unix.SockaddrNetlink
var rb [RECEIVE_BUFFER_SIZE]byte
nr, from, err := unix.Recvfrom(fd, rb[:], 0)
2019-01-12 04:58:27 +00:00
if err != nil {
2020-08-10 17:43:49 +00:00
return nil, nil, err
}
fromAddr, ok := from.(*unix.SockaddrNetlink)
if !ok {
return nil, nil, fmt.Errorf("Error converting to netlink sockaddr")
2019-01-12 04:58:27 +00:00
}
2019-09-27 21:51:53 +00:00
if nr < unix.NLMSG_HDRLEN {
2020-08-10 17:43:49 +00:00
return nil, nil, fmt.Errorf("Got short response from netlink")
}
rb2 := make([]byte, nr)
copy(rb2, rb[:nr])
nl, err := syscall.ParseNetlinkMessage(rb2)
if err != nil {
return nil, nil, err
2019-01-12 04:58:27 +00:00
}
2020-08-10 17:43:49 +00:00
return nl, fromAddr, nil
2019-01-12 04:58:27 +00:00
}
// SetSendTimeout allows to set a send timeout on the socket
2019-09-27 21:51:53 +00:00
func (s *NetlinkSocket) SetSendTimeout(timeout *unix.Timeval) error {
2019-01-12 04:58:27 +00:00
// Set a send timeout of SOCKET_SEND_TIMEOUT, this will allow the Send to periodically unblock and avoid that a routine
// remains stuck on a send on a closed fd
2019-09-27 21:51:53 +00:00
return unix.SetsockoptTimeval(int(s.fd), unix.SOL_SOCKET, unix.SO_SNDTIMEO, timeout)
2019-01-12 04:58:27 +00:00
}
// SetReceiveTimeout allows to set a receive timeout on the socket
2019-09-27 21:51:53 +00:00
func (s *NetlinkSocket) SetReceiveTimeout(timeout *unix.Timeval) error {
2019-01-12 04:58:27 +00:00
// Set a read timeout of SOCKET_READ_TIMEOUT, this will allow the Read to periodically unblock and avoid that a routine
// remains stuck on a recvmsg on a closed fd
2019-09-27 21:51:53 +00:00
return unix.SetsockoptTimeval(int(s.fd), unix.SOL_SOCKET, unix.SO_RCVTIMEO, timeout)
2019-01-12 04:58:27 +00:00
}
func (s *NetlinkSocket) GetPid() (uint32, error) {
fd := int(atomic.LoadInt32(&s.fd))
2019-09-27 21:51:53 +00:00
lsa, err := unix.Getsockname(fd)
2019-01-12 04:58:27 +00:00
if err != nil {
return 0, err
}
switch v := lsa.(type) {
2019-09-27 21:51:53 +00:00
case *unix.SockaddrNetlink:
2019-01-12 04:58:27 +00:00
return v.Pid, nil
}
return 0, fmt.Errorf("Wrong socket type")
}
func ZeroTerminated(s string) []byte {
bytes := make([]byte, len(s)+1)
for i := 0; i < len(s); i++ {
bytes[i] = s[i]
}
bytes[len(s)] = 0
return bytes
}
func NonZeroTerminated(s string) []byte {
bytes := make([]byte, len(s))
for i := 0; i < len(s); i++ {
bytes[i] = s[i]
}
return bytes
}
func BytesToString(b []byte) string {
n := bytes.Index(b, []byte{0})
return string(b[:n])
}
func Uint8Attr(v uint8) []byte {
return []byte{byte(v)}
}
func Uint16Attr(v uint16) []byte {
native := NativeEndian()
bytes := make([]byte, 2)
native.PutUint16(bytes, v)
return bytes
}
func Uint32Attr(v uint32) []byte {
native := NativeEndian()
bytes := make([]byte, 4)
native.PutUint32(bytes, v)
return bytes
}
func Uint64Attr(v uint64) []byte {
native := NativeEndian()
bytes := make([]byte, 8)
native.PutUint64(bytes, v)
return bytes
}
func ParseRouteAttr(b []byte) ([]syscall.NetlinkRouteAttr, error) {
var attrs []syscall.NetlinkRouteAttr
2019-09-27 21:51:53 +00:00
for len(b) >= unix.SizeofRtAttr {
2019-01-12 04:58:27 +00:00
a, vbuf, alen, err := netlinkRouteAttrAndValue(b)
if err != nil {
return nil, err
}
2019-09-27 21:51:53 +00:00
ra := syscall.NetlinkRouteAttr{Attr: syscall.RtAttr(*a), Value: vbuf[:int(a.Len)-unix.SizeofRtAttr]}
2019-01-12 04:58:27 +00:00
attrs = append(attrs, ra)
b = b[alen:]
}
return attrs, nil
}
2019-09-27 21:51:53 +00:00
func netlinkRouteAttrAndValue(b []byte) (*unix.RtAttr, []byte, int, error) {
a := (*unix.RtAttr)(unsafe.Pointer(&b[0]))
if int(a.Len) < unix.SizeofRtAttr || int(a.Len) > len(b) {
return nil, nil, 0, unix.EINVAL
2019-01-12 04:58:27 +00:00
}
2019-09-27 21:51:53 +00:00
return a, b[unix.SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
2019-01-12 04:58:27 +00:00
}
// SocketHandle contains the netlink socket and the associated
// sequence counter for a specific netlink family
type SocketHandle struct {
Seq uint32
Socket *NetlinkSocket
}
// Close closes the netlink socket
func (sh *SocketHandle) Close() {
if sh.Socket != nil {
sh.Socket.Close()
}
}