453 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			453 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package netlink
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"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_MASTER
 | |
| 	NDA_LINK_NETNSID
 | |
| 	NDA_SRC_VNI
 | |
| 	NDA_PROTOCOL
 | |
| 	NDA_NH_ID
 | |
| 	NDA_FDB_EXT_ATTRS
 | |
| 	NDA_FLAGS_EXT
 | |
| 	NDA_MAX = NDA_FLAGS_EXT
 | |
| )
 | |
| 
 | |
| // 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_EXT_LEARNED = 0x10
 | |
| 	NTF_OFFLOADED   = 0x20
 | |
| 	NTF_STICKY      = 0x40
 | |
| 	NTF_ROUTER      = 0x80
 | |
| )
 | |
| 
 | |
| // Extended Neighbor Flags
 | |
| const (
 | |
| 	NTF_EXT_MANAGED = 0x00000001
 | |
| )
 | |
| 
 | |
| // Ndmsg is for adding, removing or receiving information about a neighbor table entry
 | |
| 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.HardwareAddr != nil {
 | |
| 		hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
 | |
| 		req.AddData(hwData)
 | |
| 	}
 | |
| 
 | |
| 	if neigh.FlagsExt != 0 {
 | |
| 		flagsExtData := nl.NewRtAttr(NDA_FLAGS_EXT, nl.Uint32Attr(uint32(neigh.FlagsExt)))
 | |
| 		req.AddData(flagsExtData)
 | |
| 	}
 | |
| 
 | |
| 	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)
 | |
| 	}
 | |
| 
 | |
| 	if neigh.MasterIndex != 0 {
 | |
| 		masterData := nl.NewRtAttr(NDA_MASTER, nl.Uint32Attr(uint32(neigh.MasterIndex)))
 | |
| 		req.AddData(masterData)
 | |
| 	}
 | |
| 
 | |
| 	_, err := req.Execute(unix.NETLINK_ROUTE, 0)
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // NeighList returns 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 returns 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 returns 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.NeighListExecute(Ndmsg{
 | |
| 		Family: uint8(family),
 | |
| 		Index:  uint32(linkIndex),
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // NeighProxyList returns 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.NeighListExecute(Ndmsg{
 | |
| 		Family: uint8(family),
 | |
| 		Index:  uint32(linkIndex),
 | |
| 		Flags:  NTF_PROXY,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
 | |
| func NeighListExecute(msg Ndmsg) ([]Neigh, error) {
 | |
| 	return pkgHandle.NeighListExecute(msg)
 | |
| }
 | |
| 
 | |
| // NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
 | |
| func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) {
 | |
| 	req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
 | |
| 	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 msg.Index != 0 && ndm.Index != msg.Index {
 | |
| 			// Ignore messages from other interfaces
 | |
| 			continue
 | |
| 		}
 | |
| 		if msg.Family != 0 && ndm.Family != msg.Family {
 | |
| 			continue
 | |
| 		}
 | |
| 		if msg.State != 0 && ndm.State != msg.State {
 | |
| 			continue
 | |
| 		}
 | |
| 		if msg.Type != 0 && ndm.Type != msg.Type {
 | |
| 			continue
 | |
| 		}
 | |
| 		if msg.Flags != 0 && ndm.Flags != msg.Flags {
 | |
| 			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
 | |
| 	}
 | |
| 
 | |
| 	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 {
 | |
| 				neigh.LLIPAddr = net.IP(attr.Value)
 | |
| 			} else if attrLen == 16 {
 | |
| 				// Can be IPv6 or FireWire HWAddr
 | |
| 				link, err := LinkByIndex(neigh.LinkIndex)
 | |
| 				if err == nil && link.Attrs().EncapType == "tunnel6" {
 | |
| 					neigh.IP = net.IP(attr.Value)
 | |
| 				} else {
 | |
| 					neigh.HardwareAddr = net.HardwareAddr(attr.Value)
 | |
| 				}
 | |
| 			} else {
 | |
| 				neigh.HardwareAddr = net.HardwareAddr(attr.Value)
 | |
| 			}
 | |
| 		case NDA_FLAGS_EXT:
 | |
| 			neigh.FlagsExt = int(native.Uint32(attr.Value[0:4]))
 | |
| 		case NDA_VLAN:
 | |
| 			neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
 | |
| 		case NDA_VNI:
 | |
| 			neigh.VNI = int(native.Uint32(attr.Value[0:4]))
 | |
| 		case NDA_MASTER:
 | |
| 			neigh.MasterIndex = 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)
 | |
| 	makeRequest := func(family int) error {
 | |
| 		req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH,
 | |
| 			unix.NLM_F_DUMP)
 | |
| 		infmsg := nl.NewIfInfomsg(family)
 | |
| 		req.AddData(infmsg)
 | |
| 		if err := s.Send(req); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if done != nil {
 | |
| 		go func() {
 | |
| 			<-done
 | |
| 			s.Close()
 | |
| 		}()
 | |
| 	}
 | |
| 	if listExisting {
 | |
| 		if err := makeRequest(unix.AF_UNSPEC); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		// We have to wait for NLMSG_DONE before making AF_BRIDGE request
 | |
| 	}
 | |
| 	go func() {
 | |
| 		defer close(ch)
 | |
| 		for {
 | |
| 			msgs, from, err := s.Receive()
 | |
| 			if err != nil {
 | |
| 				if cberr != nil {
 | |
| 					cberr(err)
 | |
| 				}
 | |
| 				return
 | |
| 			}
 | |
| 			if from.Pid != nl.PidKernel {
 | |
| 				if cberr != nil {
 | |
| 					cberr(fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel))
 | |
| 				}
 | |
| 				continue
 | |
| 			}
 | |
| 			for _, m := range msgs {
 | |
| 				if m.Header.Type == unix.NLMSG_DONE {
 | |
| 					if listExisting {
 | |
| 						// This will be called after handling AF_UNSPEC
 | |
| 						// list request, we have to wait for NLMSG_DONE
 | |
| 						// before making another request
 | |
| 						if err := makeRequest(unix.AF_BRIDGE); err != nil {
 | |
| 							if cberr != nil {
 | |
| 								cberr(err)
 | |
| 							}
 | |
| 							return
 | |
| 						}
 | |
| 						listExisting = false
 | |
| 					}
 | |
| 					continue
 | |
| 				}
 | |
| 				if m.Header.Type == unix.NLMSG_ERROR {
 | |
| 					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
 | |
| }
 | 
