func linkSubscribe(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error { s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK) if err != nil { return err } if done != nil { go func() { <-done s.Close() }() } go func() { defer close(ch) for { msgs, err := s.Receive() if err != nil { return } for _, m := range msgs { ifmsg := nl.DeserializeIfInfomsg(m.Data) link, err := LinkDeserialize(m.Data) if err != nil { return } ch <- LinkUpdate{IfInfomsg: *ifmsg, Header: m.Header, Link: link} } } }() return nil }
func LinkGetProtinfo(link Link) (Protinfo, error) { base := link.Attrs() ensureIndex(base) var pi Protinfo req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP) msg := nl.NewIfInfomsg(syscall.AF_BRIDGE) req.AddData(msg) msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0) if err != nil { return pi, err } for _, m := range msgs { ans := nl.DeserializeIfInfomsg(m) if int(ans.Index) != base.Index { continue } attrs, err := nl.ParseRouteAttr(m[ans.Len():]) if err != nil { return pi, err } for _, attr := range attrs { if attr.Attr.Type != syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED { continue } infos, err := nl.ParseRouteAttr(attr.Value) if err != nil { return pi, err } var pi Protinfo for _, info := range infos { switch info.Attr.Type { case nl.IFLA_BRPORT_MODE: pi.Hairpin = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_GUARD: pi.Guard = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_FAST_LEAVE: pi.FastLeave = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_PROTECT: pi.RootBlock = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_LEARNING: pi.Learning = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_UNICAST_FLOOD: pi.Flood = byteToBool(info.Value[0]) } } return pi, nil } } return pi, fmt.Errorf("Device with index %d not found", base.Index) }
func matchIfName(ifaceName string) func(m syscall.NetlinkMessage) (bool, error) { return func(m syscall.NetlinkMessage) (bool, error) { switch m.Header.Type { case syscall.RTM_NEWLINK: // receive this type for link 'up' ifmsg := nl.DeserializeIfInfomsg(m.Data) attrs, err := syscall.ParseNetlinkRouteAttr(&m) if err != nil { return true, err } name := "" for _, attr := range attrs { if attr.Attr.Type == syscall.IFA_LABEL { name = string(attr.Value[:len(attr.Value)-1]) } } if ifaceName == name && ifmsg.Flags&syscall.IFF_UP != 0 { return true, nil } } return false, nil } }
func (h *Handle) LinkGetProtinfo(link Link) (Protinfo, error) { base := link.Attrs() h.ensureIndex(base) var pi Protinfo req := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP) msg := nl.NewIfInfomsg(syscall.AF_BRIDGE) req.AddData(msg) msgs, err := req.Execute(syscall.NETLINK_ROUTE, 0) if err != nil { return pi, err } for _, m := range msgs { ans := nl.DeserializeIfInfomsg(m) if int(ans.Index) != base.Index { continue } attrs, err := nl.ParseRouteAttr(m[ans.Len():]) if err != nil { return pi, err } for _, attr := range attrs { if attr.Attr.Type != syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED { continue } infos, err := nl.ParseRouteAttr(attr.Value) if err != nil { return pi, err } pi = *parseProtinfo(infos) return pi, nil } } return pi, fmt.Errorf("Device with index %d not found", base.Index) }
// linkDeserialize deserializes a raw message received from netlink into // a link object. func LinkDeserialize(m []byte) (Link, error) { msg := nl.DeserializeIfInfomsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return nil, err } base := LinkAttrs{Index: int(msg.Index), RawFlags: msg.Flags, Flags: linkFlags(msg.Flags), EncapType: msg.EncapType()} if msg.Flags&syscall.IFF_PROMISC != 0 { base.Promisc = 1 } var link Link linkType := "" for _, attr := range attrs { switch attr.Attr.Type { case syscall.IFLA_LINKINFO: infos, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } for _, info := range infos { switch info.Attr.Type { case nl.IFLA_INFO_KIND: linkType = string(info.Value[:len(info.Value)-1]) switch linkType { case "dummy": link = &Dummy{} case "ifb": link = &Ifb{} case "bridge": link = &Bridge{} case "vlan": link = &Vlan{} case "veth": link = &Veth{} case "vxlan": link = &Vxlan{} case "bond": link = &Bond{} case "ipvlan": link = &IPVlan{} case "macvlan": link = &Macvlan{} case "macvtap": link = &Macvtap{} case "gretap": link = &Gretap{} case "ipip": link = &Iptun{} case "vti": link = &Vti{} default: link = &GenericLink{LinkType: linkType} } case nl.IFLA_INFO_DATA: data, err := nl.ParseRouteAttr(info.Value) if err != nil { return nil, err } switch linkType { case "vlan": parseVlanData(link, data) case "vxlan": parseVxlanData(link, data) case "bond": parseBondData(link, data) case "ipvlan": parseIPVlanData(link, data) case "macvlan": parseMacvlanData(link, data) case "macvtap": parseMacvtapData(link, data) case "gretap": parseGretapData(link, data) case "ipip": parseIptunData(link, data) case "vti": parseVtiData(link, data) } } } case syscall.IFLA_ADDRESS: var nonzero bool for _, b := range attr.Value { if b != 0 { nonzero = true } } if nonzero { base.HardwareAddr = attr.Value[:] } case syscall.IFLA_IFNAME: base.Name = string(attr.Value[:len(attr.Value)-1]) case syscall.IFLA_MTU: base.MTU = int(native.Uint32(attr.Value[0:4])) case syscall.IFLA_LINK: base.ParentIndex = int(native.Uint32(attr.Value[0:4])) case syscall.IFLA_MASTER: base.MasterIndex = int(native.Uint32(attr.Value[0:4])) case syscall.IFLA_TXQLEN: base.TxQLen = int(native.Uint32(attr.Value[0:4])) case syscall.IFLA_IFALIAS: base.Alias = string(attr.Value[:len(attr.Value)-1]) case syscall.IFLA_STATS: base.Statistics = parseLinkStats(attr.Value[:]) case nl.IFLA_XDP: xdp, err := parseLinkXdp(attr.Value[:]) if err != nil { return nil, err } base.Xdp = xdp } } // Links that don't have IFLA_INFO_KIND are hardware devices if link == nil { link = &Device{} } *link.Attrs() = base return link, nil }
// linkDeserialize deserializes a raw message received from netlink into // a link object. func linkDeserialize(m []byte) (Link, error) { msg := nl.DeserializeIfInfomsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { return nil, err } base := LinkAttrs{Index: int(msg.Index), Flags: linkFlags(msg.Flags)} var link Link linkType := "" for _, attr := range attrs { switch attr.Attr.Type { case syscall.IFLA_LINKINFO: infos, err := nl.ParseRouteAttr(attr.Value) if err != nil { return nil, err } for _, info := range infos { switch info.Attr.Type { case nl.IFLA_INFO_KIND: linkType = string(info.Value[:len(info.Value)-1]) switch linkType { case "dummy": link = &Dummy{} case "bridge": link = &Bridge{} case "vlan": link = &Vlan{} case "veth": link = &Veth{} case "vxlan": link = &Vxlan{} case "ipvlan": link = &IPVlan{} case "macvlan": link = &Macvlan{} default: link = &Generic{LinkType: linkType} } case nl.IFLA_INFO_DATA: data, err := nl.ParseRouteAttr(info.Value) if err != nil { return nil, err } switch linkType { case "vlan": parseVlanData(link, data) case "vxlan": parseVxlanData(link, data) case "ipvlan": parseIPVlanData(link, data) case "macvlan": parseMacvlanData(link, data) } } } case syscall.IFLA_ADDRESS: var nonzero bool for _, b := range attr.Value { if b != 0 { nonzero = true } } if nonzero { base.HardwareAddr = attr.Value[:] } case syscall.IFLA_IFNAME: base.Name = string(attr.Value[:len(attr.Value)-1]) case syscall.IFLA_MTU: base.MTU = int(native.Uint32(attr.Value[0:4])) case syscall.IFLA_LINK: base.ParentIndex = int(native.Uint32(attr.Value[0:4])) case syscall.IFLA_MASTER: base.MasterIndex = int(native.Uint32(attr.Value[0:4])) case syscall.IFLA_TXQLEN: base.TxQLen = int(native.Uint32(attr.Value[0:4])) } } // Links that don't have IFLA_INFO_KIND are hardware devices if link == nil { link = &Device{} } *link.Attrs() = base return link, nil }
func (u *NetLinkProbe) start() { s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK) if err != nil { logging.GetLogger().Errorf("Failed to subscribe to netlink RTNLGRP_LINK messages: %s", err.Error()) return } u.nlSocket = s defer u.nlSocket.Close() fd := u.nlSocket.GetFd() err = syscall.SetNonblock(fd, true) if err != nil { logging.GetLogger().Errorf("Failed to set the netlink fd as non-blocking: %s", err.Error()) return } epfd, e := syscall.EpollCreate1(0) if e != nil { logging.GetLogger().Errorf("Failed to create epoll: %s", err.Error()) return } defer syscall.Close(epfd) u.initialize() event := syscall.EpollEvent{Events: syscall.EPOLLIN, Fd: int32(fd)} if e = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, fd, &event); e != nil { logging.GetLogger().Errorf("Failed to control epoll: %s", err.Error()) return } events := make([]syscall.EpollEvent, maxEpollEvents) for u.running.Load() == true { n, err := syscall.EpollWait(epfd, events[:], 1000) if err != nil { errno, ok := err.(syscall.Errno) if ok && errno != syscall.EINTR { logging.GetLogger().Errorf("Failed to receive from events from netlink: %s", err.Error()) } continue } if n == 0 { continue } msgs, err := s.Receive() if err != nil { logging.GetLogger().Errorf("Failed to receive from netlink messages: %s", err.Error()) time.Sleep(1 * time.Second) continue } for _, msg := range msgs { switch msg.Header.Type { case syscall.RTM_NEWLINK: ifmsg := nl.DeserializeIfInfomsg(msg.Data) u.onLinkAdded(int(ifmsg.Index)) case syscall.RTM_DELLINK: ifmsg := nl.DeserializeIfInfomsg(msg.Data) u.onLinkDeleted(int(ifmsg.Index)) } } } }