package odp import ( "fmt" "reflect" "sync/atomic" "syscall" ) func align(n int, a int) int { return (n + a - 1) & -a } type NetlinkSocket struct { fd int addr *syscall.SockaddrNetlink buf []byte } func OpenNetlinkSocket(protocol int) (*NetlinkSocket, error) { fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol) if err != nil { return nil, err } success := false defer func() { if !success { syscall.Close(fd) } }() // It's fairly easy to provoke ENOBUFS from a netlink socket // receiving miss upcalls when every packet misses. The // default socket buffer size is relatively small at 200KB, // and the default of /proc/sys/net/core/rmem_max means we // can't easily increase it. if err := syscall.SetsockoptInt(fd, SOL_NETLINK, syscall.NETLINK_NO_ENOBUFS, 1); err != nil { return nil, err } addr := syscall.SockaddrNetlink{Family: syscall.AF_NETLINK} if err := syscall.Bind(fd, &addr); err != nil { return nil, err } localaddr, err := syscall.Getsockname(fd) if err != nil { return nil, err } nladdr, ok := localaddr.(*syscall.SockaddrNetlink) if !ok { return nil, fmt.Errorf("Expected netlink sockaddr, got %s", reflect.TypeOf(localaddr)) } success = true return &NetlinkSocket{ fd: fd, addr: nladdr, // netlink messages can be bigger than this, but it // seems unlikely in practice, and this is similar to // the limit that the OVS userspace imposes. buf: make([]byte, 65536), }, nil } func (s *NetlinkSocket) PortId() uint32 { return s.addr.Pid } func (s *NetlinkSocket) Close() error { if s.fd < 0 { return nil } err := syscall.Close(s.fd) s.fd = -1 return err } type NlMsgBuilder struct { buf []byte } func NewNlMsgBuilder(flags uint16, typ uint16) *NlMsgBuilder { buf := MakeAlignedByteSlice(syscall.NLMSG_HDRLEN) nlmsg := &NlMsgBuilder{buf: buf} h := nlMsghdrAt(buf, 0) h.Flags = flags h.Type = typ return nlmsg } // Expand the array underlying a slice to have capacity of at least l func expand(buf []byte, l int) []byte { c := (cap(buf) + 1) * 3 / 2 for l > c { c = (c + 1) * 3 / 2 } new := MakeAlignedByteSliceCap(len(buf), c) copy(new, buf) return new } func (nlmsg *NlMsgBuilder) Align(a int) { l := align(len(nlmsg.buf), a) if l > cap(nlmsg.buf) { nlmsg.buf = expand(nlmsg.buf, l) } nlmsg.buf = nlmsg.buf[:l] } func (nlmsg *NlMsgBuilder) Grow(size uintptr) int { pos := len(nlmsg.buf) l := pos + int(size) if l > cap(nlmsg.buf) { nlmsg.buf = expand(nlmsg.buf, l) } nlmsg.buf = nlmsg.buf[:l] return pos } func (nlmsg *NlMsgBuilder) AlignGrow(a int, size uintptr) int { apos := align(len(nlmsg.buf), a) l := apos + int(size) if l > cap(nlmsg.buf) { nlmsg.buf = expand(nlmsg.buf, l) } nlmsg.buf = nlmsg.buf[:l] return apos } var nextSeqNo uint32 func (nlmsg *NlMsgBuilder) Finish() (res []byte, seq uint32) { h := nlMsghdrAt(nlmsg.buf, 0) h.Len = uint32(len(nlmsg.buf)) seq = atomic.AddUint32(&nextSeqNo, 1) h.Seq = seq res = nlmsg.buf nlmsg.buf = nil return } func (nlmsg *NlMsgBuilder) PutAttr(typ uint16, gen func()) { pos := nlmsg.AlignGrow(syscall.NLA_ALIGNTO, syscall.SizeofNlAttr) gen() nla := nlAttrAt(nlmsg.buf, pos) nla.Type = typ nla.Len = uint16(len(nlmsg.buf) - pos) } func (nlmsg *NlMsgBuilder) PutNestedAttrs(typ uint16, gen func()) { nlmsg.PutAttr(typ, func() { gen() // The kernel nlattr parser expects the alignment // padding at the end of a nested attributes value to // be included in the length of the enclosing // attribute nlmsg.Align(syscall.NLA_ALIGNTO) }) } func (nlmsg *NlMsgBuilder) PutEmptyAttr(typ uint16) { nlmsg.PutAttr(typ, func() {}) } func (nlmsg *NlMsgBuilder) PutUint8Attr(typ uint16, val uint8) { nlmsg.PutAttr(typ, func() { pos := nlmsg.Grow(1) nlmsg.buf[pos] = val }) } func (nlmsg *NlMsgBuilder) PutUint16Attr(typ uint16, val uint16) { nlmsg.PutAttr(typ, func() { pos := nlmsg.Grow(2) *uint16At(nlmsg.buf, pos) = val }) } func (nlmsg *NlMsgBuilder) PutUint32Attr(typ uint16, val uint32) { nlmsg.PutAttr(typ, func() { pos := nlmsg.Grow(4) *uint32At(nlmsg.buf, pos) = val }) } func (nlmsg *NlMsgBuilder) putStringZ(str string) { l := len(str) pos := nlmsg.Grow(uintptr(l) + 1) copy(nlmsg.buf[pos:], str) nlmsg.buf[pos+l] = 0 } func (nlmsg *NlMsgBuilder) PutStringAttr(typ uint16, str string) { nlmsg.PutAttr(typ, func() { nlmsg.putStringZ(str) }) } func (nlmsg *NlMsgBuilder) PutSliceAttr(typ uint16, data []byte) { nlmsg.PutAttr(typ, func() { pos := nlmsg.Grow(uintptr(len(data))) copy(nlmsg.buf[pos:], data) }) } type NetlinkError syscall.Errno func (err NetlinkError) Error() string { return fmt.Sprintf("netlink error response: %s", syscall.Errno(err)) } type NlMsgParser struct { data []byte pos int } func (nlmsg *NlMsgParser) Advance(size uintptr) error { if err := nlmsg.CheckAvailable(size); err != nil { return err } nlmsg.pos += int(size) return nil } func (nlmsg *NlMsgParser) AlignAdvance(a int, size uintptr) (int, error) { pos := align(nlmsg.pos, a) nlmsg.pos = pos if err := nlmsg.Advance(size); err != nil { return 0, err } return pos, nil } func (nlmsg *NlMsgParser) NlMsghdr() *syscall.NlMsghdr { return nlMsghdrAt(nlmsg.data, nlmsg.pos) } func (msg *NlMsgParser) nextNlMsg() (*NlMsgParser, error) { pos := msg.pos avail := len(msg.data) - pos if avail <= 0 { return nil, nil } if avail < syscall.SizeofNlMsghdr { return nil, fmt.Errorf("netlink message header truncated") } h := msg.NlMsghdr() if avail < int(h.Len) { return nil, fmt.Errorf("netlink message truncated (%d bytes available, %d expected)", avail, h.Len) } end := pos + int(h.Len) msg.pos = align(end, syscall.NLMSG_ALIGNTO) return &NlMsgParser{data: msg.data[:end], pos: pos}, nil } func (nlmsg *NlMsgParser) CheckAvailable(size uintptr) error { if nlmsg.pos+int(size) > len(nlmsg.data) { return fmt.Errorf("netlink message truncated") } return nil } func (nlmsg *NlMsgParser) checkHeader() error { // nextNlMsg ensures that there is an nlmsghdr-worth of data // present h := nlmsg.NlMsghdr() if h.Type == syscall.NLMSG_ERROR { nlerr := nlMsgerrAt(nlmsg.data, nlmsg.pos+syscall.NLMSG_HDRLEN) if nlerr.Error != 0 { return NetlinkError(-nlerr.Error) } // an error code of 0 means the error is an ack, so // return normally. } return nil } func (nlmsg *NlMsgParser) checkResponseHeader(expectedPortId uint32, expectedSeq uint32) (relevant bool, err error) { // nextNlMsg ensures that there is an nlmsghdr-worth of data // present h := nlmsg.NlMsghdr() if h.Pid != expectedPortId { return true, fmt.Errorf("netlink reply port id mismatch (got %d, expected %d)", h.Pid, expectedPortId) } if h.Seq != expectedSeq { // This doesn't necessarily indicate an error. For // example, if an early requestMulti was interrupted // due to an error, we might still be getting its // response messages back that, and we should discard // them. On the other hand, sequence number // mismatches might indicate bugs, so it is sometimes // nice to see them in development. fmt.Printf("netlink reply sequence number mismatch (got %d, expected %d)\n", h.Seq, expectedSeq) return false, nil } return true, nlmsg.checkHeader() } func (nlmsg *NlMsgParser) ExpectNlMsghdr(typ uint16) (*syscall.NlMsghdr, error) { h := nlmsg.NlMsghdr() if err := nlmsg.Advance(syscall.SizeofNlMsghdr); err != nil { return nil, err } if h.Type != typ { return nil, fmt.Errorf("netlink response has wrong type (got %d, expected %d)", h.Type, typ) } return h, nil } type Attrs map[uint16][]byte func (attrs Attrs) Get(typ uint16, optional bool) ([]byte, error) { val, ok := attrs[typ] if !ok && !optional { return nil, fmt.Errorf("missing netlink attribute %d", typ) } return val, nil } func (attrs Attrs) GetFixedBytes(typ uint16, expect int, optional bool) ([]byte, error) { val, err := attrs.Get(typ, optional) if err != nil || val == nil { return nil, err } if len(val) != expect { return nil, fmt.Errorf("attribute %d has wrong length (got %d bytes, expected %d bytes)", typ, len(val), expect) } return val, nil } func (attrs Attrs) GetOptionalBytes(typ uint16, dest []byte) (bool, error) { val, err := attrs.GetFixedBytes(typ, len(dest), true) if err != nil || val == nil { return false, err } copy(dest, val) return true, nil } func (attrs Attrs) GetEmpty(typ uint16) (bool, error) { val, err := attrs.Get(typ, true) if err != nil || val == nil { return false, err } if len(val) != 0 { return false, fmt.Errorf("empty attribute %d has wrong length (%d bytes)", typ, len(val)) } return true, nil } func (attrs Attrs) GetOptionalUint8(typ uint16) (uint8, bool, error) { val, err := attrs.Get(typ, true) if err != nil || val == nil { return 0, false, err } if len(val) != 1 { return 0, false, fmt.Errorf("uint8 attribute %d has wrong length (%d bytes)", typ, len(val)) } return val[0], true, nil } func (attrs Attrs) getUint16(typ uint16, optional bool) (uint16, bool, error) { val, err := attrs.Get(typ, optional) if err != nil || val == nil { return 0, false, err } if len(val) != 2 { return 0, false, fmt.Errorf("uint16 attribute %d has wrong length (%d bytes)", typ, len(val)) } return *uint16At(val, 0), true, nil } func (attrs Attrs) GetUint16(typ uint16) (uint16, error) { res, _, err := attrs.getUint16(typ, false) return res, err } func (attrs Attrs) GetOptionalUint16(typ uint16) (uint16, bool, error) { return attrs.getUint16(typ, true) } func (attrs Attrs) GetUint32(typ uint16) (uint32, error) { val, err := attrs.Get(typ, false) if err != nil { return 0, err } if len(val) != 4 { return 0, fmt.Errorf("uint32 attribute %d has wrong length (%d bytes)", typ, len(val)) } return *uint32At(val, 0), nil } func (attrs Attrs) getUint64(typ uint16, optional bool) (uint64, bool, error) { val, err := attrs.Get(typ, optional) if err != nil || val == nil { return 0, false, err } if len(val) != 8 { return 0, false, fmt.Errorf("uint64 attribute %d has wrong length (%d bytes)", typ, len(val)) } return *uint64At(val, 0), true, nil } func (attrs Attrs) GetUint64(typ uint16) (uint64, error) { res, _, err := attrs.getUint64(typ, false) return res, err } func (attrs Attrs) GetOptionalUint64(typ uint16) (uint64, bool, error) { return attrs.getUint64(typ, true) } func (attrs Attrs) GetString(typ uint16) (string, error) { val, err := attrs.Get(typ, false) if err != nil { return "", err } if len(val) == 0 { return "", fmt.Errorf("string attribute %d has zero length", typ) } if val[len(val)-1] != 0 { return "", fmt.Errorf("string attribute %d does not end with nul byte", typ) } return string(val[0 : len(val)-1]), nil } func (nlmsg *NlMsgParser) checkData(l uintptr, obj string) error { if nlmsg.pos+int(l) <= len(nlmsg.data) { return nil } else { return fmt.Errorf("truncated %s (have %d bytes, expected %d)", obj, len(nlmsg.data)-nlmsg.pos, l) } } func (nlmsg *NlMsgParser) parseAttrs(consumer func(uint16, []byte)) error { for { apos := align(nlmsg.pos, syscall.NLA_ALIGNTO) if len(nlmsg.data) <= apos { break } nlmsg.pos = apos if err := nlmsg.checkData(syscall.SizeofNlAttr, "netlink attribute"); err != nil { return err } nla := nlAttrAt(nlmsg.data, nlmsg.pos) if err := nlmsg.checkData(uintptr(nla.Len), "netlink attribute"); err != nil { return err } valpos := align(nlmsg.pos+syscall.SizeofNlAttr, syscall.NLA_ALIGNTO) consumer(nla.Type, nlmsg.data[valpos:nlmsg.pos+int(nla.Len)]) nlmsg.pos += int(nla.Len) } return nil } func (nlmsg *NlMsgParser) TakeAttrs() (Attrs, error) { res := make(Attrs) err := nlmsg.parseAttrs(func(typ uint16, val []byte) { res[typ] = val }) return res, err } func ParseNestedAttrs(data []byte) (Attrs, error) { parser := NlMsgParser{data: data, pos: 0} return parser.TakeAttrs() } func (attrs Attrs) GetNestedAttrs(typ uint16, optional bool) (Attrs, error) { val, err := attrs.Get(typ, optional) if val == nil { return nil, err } return ParseNestedAttrs(val) } // Usually we parse attributes into a map, but there are cases where // attribute order matters. type Attr struct { typ uint16 val []byte } func (attrs Attrs) GetOrderedAttrs(typ uint16) ([]Attr, error) { val, err := attrs.Get(typ, false) if val == nil { return nil, err } parser := NlMsgParser{data: val, pos: 0} res := make([]Attr, 0) err = parser.parseAttrs(func(typ uint16, val []byte) { res = append(res, Attr{typ, val}) }) return res, err } func (s *NetlinkSocket) send(msg *NlMsgBuilder) (uint32, error) { sa := syscall.SockaddrNetlink{ Family: syscall.AF_NETLINK, Pid: 0, Groups: 0, } data, seq := msg.Finish() return seq, syscall.Sendto(s.fd, data, 0, &sa) } func (s *NetlinkSocket) recv(peer uint32) (*NlMsgParser, error) { nr, from, err := syscall.Recvfrom(s.fd, s.buf, 0) if err != nil { return nil, err } buf := MakeAlignedByteSlice(nr) copy(buf, s.buf) switch nlfrom := from.(type) { case *syscall.SockaddrNetlink: if nlfrom.Pid != peer { return nil, fmt.Errorf("wrong netlink peer pid (expected %d, got %d)", peer, nlfrom.Pid) } return &NlMsgParser{data: buf, pos: 0}, nil default: return nil, fmt.Errorf("Expected netlink sockaddr, got %s", reflect.TypeOf(from)) } } func (s *NetlinkSocket) Receive(consumer func(*NlMsgParser) (bool, error)) error { for { resp, err := s.recv(0) if err != nil { return err } msg, err := resp.nextNlMsg() if err != nil { return err } if msg == nil { return fmt.Errorf("netlink response message missing") } for { done, err := consumer(msg) if done || err != nil { return err } msg, err = resp.nextNlMsg() if err != nil { return err } if msg == nil { break } } } } // Some generic netlink operations always return a reply message (e.g // *_GET), others don't by default (e.g. *_NEW). In the latter case, // NLM_F_ECHO forces a reply. This is undocumented AFAICT. const RequestFlags = syscall.NLM_F_REQUEST | syscall.NLM_F_ECHO // Do a netlink request that yields a single response message. func (s *NetlinkSocket) Request(req *NlMsgBuilder) (resp *NlMsgParser, err error) { seq, err := s.send(req) if err != nil { return nil, err } err = s.Receive(func(msg *NlMsgParser) (bool, error) { relevant, err := msg.checkResponseHeader(s.PortId(), seq) if relevant && err == nil { resp = msg } return true, err }) return } const DumpFlags = syscall.NLM_F_DUMP | syscall.NLM_F_REQUEST // Do a netlink request that yield multiple response messages. func (s *NetlinkSocket) RequestMulti(req *NlMsgBuilder, consumer func(*NlMsgParser) error) error { seq, err := s.send(req) if err != nil { return err } return s.Receive(func(msg *NlMsgParser) (bool, error) { relevant, err := msg.checkResponseHeader(s.PortId(), seq) if !relevant || err != nil { return false, err } if msg.NlMsghdr().Type == syscall.NLMSG_DONE { return true, processNlMsgDone(msg) } err = consumer(msg) if err != nil { return true, err } return false, nil }) } func processNlMsgDone(msg *NlMsgParser) error { err := msg.Advance(syscall.SizeofNlMsghdr) if err != nil { return err } err = msg.checkData(4, "NLMSG_DONE error code") if err != nil { return err } errno := *int32At(msg.data, msg.pos) if errno == 0 { return nil } else { return NetlinkError(-errno) } } type Consumer interface { Error(err error, stopped bool) } func (s *NetlinkSocket) consume(consumer Consumer, handler func(*NlMsgParser) error) { for { err := s.Receive(func(msg *NlMsgParser) (bool, error) { err := msg.checkHeader() if err == nil { err = handler(msg) if err == nil { return false, nil } } consumer.Error(err, false) return false, nil }) if err != nil { consumer.Error(err, true) break } } }