123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- package netlink
- import (
- "net"
- "syscall"
- "unsafe"
- "github.com/vishvananda/netlink/nl"
- "github.com/vishvananda/netns"
- "golang.org/x/sys/unix"
- )
- const (
- NDA_UNSPEC = iota
- NDA_DST
- NDA_LLADDR
- NDA_CACHEINFO
- NDA_PROBES
- NDA_VLAN
- NDA_PORT
- NDA_VNI
- NDA_IFINDEX
- NDA_MAX = NDA_IFINDEX
- )
- // Neighbor Cache Entry States.
- const (
- NUD_NONE = 0x00
- NUD_INCOMPLETE = 0x01
- NUD_REACHABLE = 0x02
- NUD_STALE = 0x04
- NUD_DELAY = 0x08
- NUD_PROBE = 0x10
- NUD_FAILED = 0x20
- NUD_NOARP = 0x40
- NUD_PERMANENT = 0x80
- )
- // Neighbor Flags
- const (
- NTF_USE = 0x01
- NTF_SELF = 0x02
- NTF_MASTER = 0x04
- NTF_PROXY = 0x08
- NTF_ROUTER = 0x80
- )
- type Ndmsg struct {
- Family uint8
- Index uint32
- State uint16
- Flags uint8
- Type uint8
- }
- func deserializeNdmsg(b []byte) *Ndmsg {
- var dummy Ndmsg
- return (*Ndmsg)(unsafe.Pointer(&b[0:unsafe.Sizeof(dummy)][0]))
- }
- func (msg *Ndmsg) Serialize() []byte {
- return (*(*[unsafe.Sizeof(*msg)]byte)(unsafe.Pointer(msg)))[:]
- }
- func (msg *Ndmsg) Len() int {
- return int(unsafe.Sizeof(*msg))
- }
- // NeighAdd will add an IP to MAC mapping to the ARP table
- // Equivalent to: `ip neigh add ....`
- func NeighAdd(neigh *Neigh) error {
- return pkgHandle.NeighAdd(neigh)
- }
- // NeighAdd will add an IP to MAC mapping to the ARP table
- // Equivalent to: `ip neigh add ....`
- func (h *Handle) NeighAdd(neigh *Neigh) error {
- return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
- }
- // NeighSet will add or replace an IP to MAC mapping to the ARP table
- // Equivalent to: `ip neigh replace....`
- func NeighSet(neigh *Neigh) error {
- return pkgHandle.NeighSet(neigh)
- }
- // NeighSet will add or replace an IP to MAC mapping to the ARP table
- // Equivalent to: `ip neigh replace....`
- func (h *Handle) NeighSet(neigh *Neigh) error {
- return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_REPLACE)
- }
- // NeighAppend will append an entry to FDB
- // Equivalent to: `bridge fdb append...`
- func NeighAppend(neigh *Neigh) error {
- return pkgHandle.NeighAppend(neigh)
- }
- // NeighAppend will append an entry to FDB
- // Equivalent to: `bridge fdb append...`
- func (h *Handle) NeighAppend(neigh *Neigh) error {
- return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_APPEND)
- }
- // NeighAppend will append an entry to FDB
- // Equivalent to: `bridge fdb append...`
- func neighAdd(neigh *Neigh, mode int) error {
- return pkgHandle.neighAdd(neigh, mode)
- }
- // NeighAppend will append an entry to FDB
- // Equivalent to: `bridge fdb append...`
- func (h *Handle) neighAdd(neigh *Neigh, mode int) error {
- req := h.newNetlinkRequest(unix.RTM_NEWNEIGH, mode|unix.NLM_F_ACK)
- return neighHandle(neigh, req)
- }
- // NeighDel will delete an IP address from a link device.
- // Equivalent to: `ip addr del $addr dev $link`
- func NeighDel(neigh *Neigh) error {
- return pkgHandle.NeighDel(neigh)
- }
- // NeighDel will delete an IP address from a link device.
- // Equivalent to: `ip addr del $addr dev $link`
- func (h *Handle) NeighDel(neigh *Neigh) error {
- req := h.newNetlinkRequest(unix.RTM_DELNEIGH, unix.NLM_F_ACK)
- return neighHandle(neigh, req)
- }
- func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
- var family int
- if neigh.Family > 0 {
- family = neigh.Family
- } else {
- family = nl.GetIPFamily(neigh.IP)
- }
- msg := Ndmsg{
- Family: uint8(family),
- Index: uint32(neigh.LinkIndex),
- State: uint16(neigh.State),
- Type: uint8(neigh.Type),
- Flags: uint8(neigh.Flags),
- }
- req.AddData(&msg)
- ipData := neigh.IP.To4()
- if ipData == nil {
- ipData = neigh.IP.To16()
- }
- dstData := nl.NewRtAttr(NDA_DST, ipData)
- req.AddData(dstData)
- if neigh.LLIPAddr != nil {
- llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4())
- req.AddData(llIPData)
- } else if neigh.Flags != NTF_PROXY || neigh.HardwareAddr != nil {
- hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
- req.AddData(hwData)
- }
- if neigh.Vlan != 0 {
- vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan)))
- req.AddData(vlanData)
- }
- if neigh.VNI != 0 {
- vniData := nl.NewRtAttr(NDA_VNI, nl.Uint32Attr(uint32(neigh.VNI)))
- req.AddData(vniData)
- }
- _, err := req.Execute(unix.NETLINK_ROUTE, 0)
- return err
- }
- // NeighList gets a list of IP-MAC mappings in the system (ARP table).
- // Equivalent to: `ip neighbor show`.
- // The list can be filtered by link and ip family.
- func NeighList(linkIndex, family int) ([]Neigh, error) {
- return pkgHandle.NeighList(linkIndex, family)
- }
- // NeighProxyList gets a list of neighbor proxies in the system.
- // Equivalent to: `ip neighbor show proxy`.
- // The list can be filtered by link and ip family.
- func NeighProxyList(linkIndex, family int) ([]Neigh, error) {
- return pkgHandle.NeighProxyList(linkIndex, family)
- }
- // NeighList gets a list of IP-MAC mappings in the system (ARP table).
- // Equivalent to: `ip neighbor show`.
- // The list can be filtered by link and ip family.
- func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
- return h.neighList(linkIndex, family, 0)
- }
- // NeighProxyList gets a list of neighbor proxies in the system.
- // Equivalent to: `ip neighbor show proxy`.
- // The list can be filtered by link, ip family.
- func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
- return h.neighList(linkIndex, family, NTF_PROXY)
- }
- func (h *Handle) neighList(linkIndex, family, flags int) ([]Neigh, error) {
- req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
- msg := Ndmsg{
- Family: uint8(family),
- Index: uint32(linkIndex),
- Flags: uint8(flags),
- }
- req.AddData(&msg)
- msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH)
- if err != nil {
- return nil, err
- }
- var res []Neigh
- for _, m := range msgs {
- ndm := deserializeNdmsg(m)
- if linkIndex != 0 && int(ndm.Index) != linkIndex {
- // Ignore messages from other interfaces
- continue
- }
- neigh, err := NeighDeserialize(m)
- if err != nil {
- continue
- }
- res = append(res, *neigh)
- }
- return res, nil
- }
- func NeighDeserialize(m []byte) (*Neigh, error) {
- msg := deserializeNdmsg(m)
- neigh := Neigh{
- LinkIndex: int(msg.Index),
- Family: int(msg.Family),
- State: int(msg.State),
- Type: int(msg.Type),
- Flags: int(msg.Flags),
- }
- attrs, err := nl.ParseRouteAttr(m[msg.Len():])
- if err != nil {
- return nil, err
- }
- // This should be cached for perfomance
- // once per table dump
- link, err := LinkByIndex(neigh.LinkIndex)
- if err != nil {
- return nil, err
- }
- encapType := link.Attrs().EncapType
- for _, attr := range attrs {
- switch attr.Attr.Type {
- case NDA_DST:
- neigh.IP = net.IP(attr.Value)
- case NDA_LLADDR:
- // BUG: Is this a bug in the netlink library?
- // #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
- // #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
- attrLen := attr.Attr.Len - unix.SizeofRtAttr
- if attrLen == 4 && (encapType == "ipip" ||
- encapType == "sit" ||
- encapType == "gre") {
- neigh.LLIPAddr = net.IP(attr.Value)
- } else if attrLen == 16 &&
- encapType == "tunnel6" {
- neigh.IP = net.IP(attr.Value)
- } else {
- neigh.HardwareAddr = net.HardwareAddr(attr.Value)
- }
- case NDA_VLAN:
- neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
- case NDA_VNI:
- neigh.VNI = int(native.Uint32(attr.Value[0:4]))
- }
- }
- return &neigh, nil
- }
- // NeighSubscribe takes a chan down which notifications will be sent
- // when neighbors are added or deleted. Close the 'done' chan to stop subscription.
- func NeighSubscribe(ch chan<- NeighUpdate, done <-chan struct{}) error {
- return neighSubscribeAt(netns.None(), netns.None(), ch, done, nil, false)
- }
- // NeighSubscribeAt works like NeighSubscribe plus it allows the caller
- // to choose the network namespace in which to subscribe (ns).
- func NeighSubscribeAt(ns netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}) error {
- return neighSubscribeAt(ns, netns.None(), ch, done, nil, false)
- }
- // NeighSubscribeOptions contains a set of options to use with
- // NeighSubscribeWithOptions.
- type NeighSubscribeOptions struct {
- Namespace *netns.NsHandle
- ErrorCallback func(error)
- ListExisting bool
- }
- // NeighSubscribeWithOptions work like NeighSubscribe but enable to
- // provide additional options to modify the behavior. Currently, the
- // namespace can be provided as well as an error callback.
- func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error {
- if options.Namespace == nil {
- none := netns.None()
- options.Namespace = &none
- }
- return neighSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting)
- }
- func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}, cberr func(error), listExisting bool) error {
- s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH)
- if err != nil {
- return err
- }
- if done != nil {
- go func() {
- <-done
- s.Close()
- }()
- }
- if listExisting {
- req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH,
- unix.NLM_F_DUMP)
- infmsg := nl.NewIfInfomsg(unix.AF_UNSPEC)
- req.AddData(infmsg)
- if err := s.Send(req); err != nil {
- return err
- }
- }
- go func() {
- defer close(ch)
- for {
- msgs, err := s.Receive()
- if err != nil {
- if cberr != nil {
- cberr(err)
- }
- return
- }
- for _, m := range msgs {
- if m.Header.Type == unix.NLMSG_DONE {
- continue
- }
- if m.Header.Type == unix.NLMSG_ERROR {
- native := nl.NativeEndian()
- error := int32(native.Uint32(m.Data[0:4]))
- if error == 0 {
- continue
- }
- if cberr != nil {
- cberr(syscall.Errno(-error))
- }
- return
- }
- neigh, err := NeighDeserialize(m.Data)
- if err != nil {
- if cberr != nil {
- cberr(err)
- }
- return
- }
- ch <- NeighUpdate{Type: m.Header.Type, Neigh: *neigh}
- }
- }
- }()
- return nil
- }
|