diff options
Diffstat (limited to 'src/syscall/netlink_linux.go')
-rw-r--r-- | src/syscall/netlink_linux.go | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/src/syscall/netlink_linux.go b/src/syscall/netlink_linux.go new file mode 100644 index 0000000..a503a07 --- /dev/null +++ b/src/syscall/netlink_linux.go @@ -0,0 +1,188 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Netlink sockets and messages + +package syscall + +import ( + "sync" + "unsafe" +) + +// Round the length of a netlink message up to align it properly. +func nlmAlignOf(msglen int) int { + return (msglen + NLMSG_ALIGNTO - 1) & ^(NLMSG_ALIGNTO - 1) +} + +// Round the length of a netlink route attribute up to align it +// properly. +func rtaAlignOf(attrlen int) int { + return (attrlen + RTA_ALIGNTO - 1) & ^(RTA_ALIGNTO - 1) +} + +// NetlinkRouteRequest represents a request message to receive routing +// and link states from the kernel. +type NetlinkRouteRequest struct { + Header NlMsghdr + Data RtGenmsg +} + +func (rr *NetlinkRouteRequest) toWireFormat() []byte { + b := make([]byte, rr.Header.Len) + *(*uint32)(unsafe.Pointer(&b[0:4][0])) = rr.Header.Len + *(*uint16)(unsafe.Pointer(&b[4:6][0])) = rr.Header.Type + *(*uint16)(unsafe.Pointer(&b[6:8][0])) = rr.Header.Flags + *(*uint32)(unsafe.Pointer(&b[8:12][0])) = rr.Header.Seq + *(*uint32)(unsafe.Pointer(&b[12:16][0])) = rr.Header.Pid + b[16] = byte(rr.Data.Family) + return b +} + +func newNetlinkRouteRequest(proto, seq, family int) []byte { + rr := &NetlinkRouteRequest{} + rr.Header.Len = uint32(NLMSG_HDRLEN + SizeofRtGenmsg) + rr.Header.Type = uint16(proto) + rr.Header.Flags = NLM_F_DUMP | NLM_F_REQUEST + rr.Header.Seq = uint32(seq) + rr.Data.Family = uint8(family) + return rr.toWireFormat() +} + +var pageBufPool = &sync.Pool{New: func() any { + b := make([]byte, Getpagesize()) + return &b +}} + +// NetlinkRIB returns routing information base, as known as RIB, which +// consists of network facility information, states and parameters. +func NetlinkRIB(proto, family int) ([]byte, error) { + s, err := Socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE) + if err != nil { + return nil, err + } + defer Close(s) + sa := &SockaddrNetlink{Family: AF_NETLINK} + if err := Bind(s, sa); err != nil { + return nil, err + } + wb := newNetlinkRouteRequest(proto, 1, family) + if err := Sendto(s, wb, 0, sa); err != nil { + return nil, err + } + lsa, err := Getsockname(s) + if err != nil { + return nil, err + } + lsanl, ok := lsa.(*SockaddrNetlink) + if !ok { + return nil, EINVAL + } + var tab []byte + + rbNew := pageBufPool.Get().(*[]byte) + defer pageBufPool.Put(rbNew) +done: + for { + rb := *rbNew + nr, _, err := Recvfrom(s, rb, 0) + if err != nil { + return nil, err + } + if nr < NLMSG_HDRLEN { + return nil, EINVAL + } + rb = rb[:nr] + tab = append(tab, rb...) + msgs, err := ParseNetlinkMessage(rb) + if err != nil { + return nil, err + } + for _, m := range msgs { + if m.Header.Seq != 1 || m.Header.Pid != lsanl.Pid { + return nil, EINVAL + } + if m.Header.Type == NLMSG_DONE { + break done + } + if m.Header.Type == NLMSG_ERROR { + return nil, EINVAL + } + } + } + return tab, nil +} + +// NetlinkMessage represents a netlink message. +type NetlinkMessage struct { + Header NlMsghdr + Data []byte +} + +// ParseNetlinkMessage parses b as an array of netlink messages and +// returns the slice containing the NetlinkMessage structures. +func ParseNetlinkMessage(b []byte) ([]NetlinkMessage, error) { + var msgs []NetlinkMessage + for len(b) >= NLMSG_HDRLEN { + h, dbuf, dlen, err := netlinkMessageHeaderAndData(b) + if err != nil { + return nil, err + } + m := NetlinkMessage{Header: *h, Data: dbuf[:int(h.Len)-NLMSG_HDRLEN]} + msgs = append(msgs, m) + b = b[dlen:] + } + return msgs, nil +} + +func netlinkMessageHeaderAndData(b []byte) (*NlMsghdr, []byte, int, error) { + h := (*NlMsghdr)(unsafe.Pointer(&b[0])) + l := nlmAlignOf(int(h.Len)) + if int(h.Len) < NLMSG_HDRLEN || l > len(b) { + return nil, nil, 0, EINVAL + } + return h, b[NLMSG_HDRLEN:], l, nil +} + +// NetlinkRouteAttr represents a netlink route attribute. +type NetlinkRouteAttr struct { + Attr RtAttr + Value []byte +} + +// ParseNetlinkRouteAttr parses m's payload as an array of netlink +// route attributes and returns the slice containing the +// NetlinkRouteAttr structures. +func ParseNetlinkRouteAttr(m *NetlinkMessage) ([]NetlinkRouteAttr, error) { + var b []byte + switch m.Header.Type { + case RTM_NEWLINK, RTM_DELLINK: + b = m.Data[SizeofIfInfomsg:] + case RTM_NEWADDR, RTM_DELADDR: + b = m.Data[SizeofIfAddrmsg:] + case RTM_NEWROUTE, RTM_DELROUTE: + b = m.Data[SizeofRtMsg:] + default: + return nil, EINVAL + } + var attrs []NetlinkRouteAttr + for len(b) >= SizeofRtAttr { + a, vbuf, alen, err := netlinkRouteAttrAndValue(b) + if err != nil { + return nil, err + } + ra := NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-SizeofRtAttr]} + attrs = append(attrs, ra) + b = b[alen:] + } + return attrs, nil +} + +func netlinkRouteAttrAndValue(b []byte) (*RtAttr, []byte, int, error) { + a := (*RtAttr)(unsafe.Pointer(&b[0])) + if int(a.Len) < SizeofRtAttr || int(a.Len) > len(b) { + return nil, nil, 0, EINVAL + } + return a, b[SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil +} |