github.com/vishvananda/netlink@v1.3.1/neigh_linux.go (about)

     1  package netlink
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"syscall"
     8  	"unsafe"
     9  
    10  	"github.com/vishvananda/netlink/nl"
    11  	"github.com/vishvananda/netns"
    12  	"golang.org/x/sys/unix"
    13  )
    14  
    15  const (
    16  	NDA_UNSPEC = iota
    17  	NDA_DST
    18  	NDA_LLADDR
    19  	NDA_CACHEINFO
    20  	NDA_PROBES
    21  	NDA_VLAN
    22  	NDA_PORT
    23  	NDA_VNI
    24  	NDA_IFINDEX
    25  	NDA_MASTER
    26  	NDA_LINK_NETNSID
    27  	NDA_SRC_VNI
    28  	NDA_PROTOCOL
    29  	NDA_NH_ID
    30  	NDA_FDB_EXT_ATTRS
    31  	NDA_FLAGS_EXT
    32  	NDA_MAX = NDA_FLAGS_EXT
    33  )
    34  
    35  // Neighbor Cache Entry States.
    36  const (
    37  	NUD_NONE       = 0x00
    38  	NUD_INCOMPLETE = 0x01
    39  	NUD_REACHABLE  = 0x02
    40  	NUD_STALE      = 0x04
    41  	NUD_DELAY      = 0x08
    42  	NUD_PROBE      = 0x10
    43  	NUD_FAILED     = 0x20
    44  	NUD_NOARP      = 0x40
    45  	NUD_PERMANENT  = 0x80
    46  )
    47  
    48  // Neighbor Flags
    49  const (
    50  	NTF_USE         = 0x01
    51  	NTF_SELF        = 0x02
    52  	NTF_MASTER      = 0x04
    53  	NTF_PROXY       = 0x08
    54  	NTF_EXT_LEARNED = 0x10
    55  	NTF_OFFLOADED   = 0x20
    56  	NTF_STICKY      = 0x40
    57  	NTF_ROUTER      = 0x80
    58  )
    59  
    60  // Extended Neighbor Flags
    61  const (
    62  	NTF_EXT_MANAGED = 0x00000001
    63  )
    64  
    65  // Ndmsg is for adding, removing or receiving information about a neighbor table entry
    66  type Ndmsg struct {
    67  	Family uint8
    68  	Index  uint32
    69  	State  uint16
    70  	Flags  uint8
    71  	Type   uint8
    72  }
    73  
    74  func deserializeNdmsg(b []byte) *Ndmsg {
    75  	var dummy Ndmsg
    76  	return (*Ndmsg)(unsafe.Pointer(&b[0:unsafe.Sizeof(dummy)][0]))
    77  }
    78  
    79  func (msg *Ndmsg) Serialize() []byte {
    80  	return (*(*[unsafe.Sizeof(*msg)]byte)(unsafe.Pointer(msg)))[:]
    81  }
    82  
    83  func (msg *Ndmsg) Len() int {
    84  	return int(unsafe.Sizeof(*msg))
    85  }
    86  
    87  // NeighAdd will add an IP to MAC mapping to the ARP table
    88  // Equivalent to: `ip neigh add ....`
    89  func NeighAdd(neigh *Neigh) error {
    90  	return pkgHandle.NeighAdd(neigh)
    91  }
    92  
    93  // NeighAdd will add an IP to MAC mapping to the ARP table
    94  // Equivalent to: `ip neigh add ....`
    95  func (h *Handle) NeighAdd(neigh *Neigh) error {
    96  	return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
    97  }
    98  
    99  // NeighSet will add or replace an IP to MAC mapping to the ARP table
   100  // Equivalent to: `ip neigh replace....`
   101  func NeighSet(neigh *Neigh) error {
   102  	return pkgHandle.NeighSet(neigh)
   103  }
   104  
   105  // NeighSet will add or replace an IP to MAC mapping to the ARP table
   106  // Equivalent to: `ip neigh replace....`
   107  func (h *Handle) NeighSet(neigh *Neigh) error {
   108  	return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_REPLACE)
   109  }
   110  
   111  // NeighAppend will append an entry to FDB
   112  // Equivalent to: `bridge fdb append...`
   113  func NeighAppend(neigh *Neigh) error {
   114  	return pkgHandle.NeighAppend(neigh)
   115  }
   116  
   117  // NeighAppend will append an entry to FDB
   118  // Equivalent to: `bridge fdb append...`
   119  func (h *Handle) NeighAppend(neigh *Neigh) error {
   120  	return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_APPEND)
   121  }
   122  
   123  // NeighAppend will append an entry to FDB
   124  // Equivalent to: `bridge fdb append...`
   125  func neighAdd(neigh *Neigh, mode int) error {
   126  	return pkgHandle.neighAdd(neigh, mode)
   127  }
   128  
   129  // NeighAppend will append an entry to FDB
   130  // Equivalent to: `bridge fdb append...`
   131  func (h *Handle) neighAdd(neigh *Neigh, mode int) error {
   132  	req := h.newNetlinkRequest(unix.RTM_NEWNEIGH, mode|unix.NLM_F_ACK)
   133  	return neighHandle(neigh, req)
   134  }
   135  
   136  // NeighDel will delete an IP address from a link device.
   137  // Equivalent to: `ip addr del $addr dev $link`
   138  func NeighDel(neigh *Neigh) error {
   139  	return pkgHandle.NeighDel(neigh)
   140  }
   141  
   142  // NeighDel will delete an IP address from a link device.
   143  // Equivalent to: `ip addr del $addr dev $link`
   144  func (h *Handle) NeighDel(neigh *Neigh) error {
   145  	req := h.newNetlinkRequest(unix.RTM_DELNEIGH, unix.NLM_F_ACK)
   146  	return neighHandle(neigh, req)
   147  }
   148  
   149  func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
   150  	var family int
   151  
   152  	if neigh.Family > 0 {
   153  		family = neigh.Family
   154  	} else {
   155  		family = nl.GetIPFamily(neigh.IP)
   156  	}
   157  
   158  	msg := Ndmsg{
   159  		Family: uint8(family),
   160  		Index:  uint32(neigh.LinkIndex),
   161  		State:  uint16(neigh.State),
   162  		Type:   uint8(neigh.Type),
   163  		Flags:  uint8(neigh.Flags),
   164  	}
   165  	req.AddData(&msg)
   166  
   167  	ipData := neigh.IP.To4()
   168  	if ipData == nil {
   169  		ipData = neigh.IP.To16()
   170  	}
   171  
   172  	dstData := nl.NewRtAttr(NDA_DST, ipData)
   173  	req.AddData(dstData)
   174  
   175  	if neigh.LLIPAddr != nil {
   176  		llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4())
   177  		req.AddData(llIPData)
   178  	} else if neigh.HardwareAddr != nil {
   179  		hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
   180  		req.AddData(hwData)
   181  	}
   182  
   183  	if neigh.FlagsExt != 0 {
   184  		flagsExtData := nl.NewRtAttr(NDA_FLAGS_EXT, nl.Uint32Attr(uint32(neigh.FlagsExt)))
   185  		req.AddData(flagsExtData)
   186  	}
   187  
   188  	if neigh.Vlan != 0 {
   189  		vlanData := nl.NewRtAttr(NDA_VLAN, nl.Uint16Attr(uint16(neigh.Vlan)))
   190  		req.AddData(vlanData)
   191  	}
   192  
   193  	if neigh.VNI != 0 {
   194  		vniData := nl.NewRtAttr(NDA_VNI, nl.Uint32Attr(uint32(neigh.VNI)))
   195  		req.AddData(vniData)
   196  	}
   197  
   198  	if neigh.MasterIndex != 0 {
   199  		masterData := nl.NewRtAttr(NDA_MASTER, nl.Uint32Attr(uint32(neigh.MasterIndex)))
   200  		req.AddData(masterData)
   201  	}
   202  
   203  	_, err := req.Execute(unix.NETLINK_ROUTE, 0)
   204  	return err
   205  }
   206  
   207  // NeighList returns a list of IP-MAC mappings in the system (ARP table).
   208  // Equivalent to: `ip neighbor show`.
   209  // The list can be filtered by link and ip family.
   210  //
   211  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   212  // or incomplete.
   213  func NeighList(linkIndex, family int) ([]Neigh, error) {
   214  	return pkgHandle.NeighList(linkIndex, family)
   215  }
   216  
   217  // NeighProxyList returns a list of neighbor proxies in the system.
   218  // Equivalent to: `ip neighbor show proxy`.
   219  // The list can be filtered by link and ip family.
   220  //
   221  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   222  // or incomplete.
   223  func NeighProxyList(linkIndex, family int) ([]Neigh, error) {
   224  	return pkgHandle.NeighProxyList(linkIndex, family)
   225  }
   226  
   227  // NeighList returns a list of IP-MAC mappings in the system (ARP table).
   228  // Equivalent to: `ip neighbor show`.
   229  // The list can be filtered by link and ip family.
   230  //
   231  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   232  // or incomplete.
   233  func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
   234  	return h.NeighListExecute(Ndmsg{
   235  		Family: uint8(family),
   236  		Index:  uint32(linkIndex),
   237  	})
   238  }
   239  
   240  // NeighProxyList returns a list of neighbor proxies in the system.
   241  // Equivalent to: `ip neighbor show proxy`.
   242  // The list can be filtered by link, ip family.
   243  //
   244  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   245  // or incomplete.
   246  func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
   247  	return h.NeighListExecute(Ndmsg{
   248  		Family: uint8(family),
   249  		Index:  uint32(linkIndex),
   250  		Flags:  NTF_PROXY,
   251  	})
   252  }
   253  
   254  // NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
   255  //
   256  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   257  // or incomplete.
   258  func NeighListExecute(msg Ndmsg) ([]Neigh, error) {
   259  	return pkgHandle.NeighListExecute(msg)
   260  }
   261  
   262  // NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
   263  //
   264  // If the returned error is [ErrDumpInterrupted], results may be inconsistent
   265  // or incomplete.
   266  func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) {
   267  	req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
   268  	req.AddData(&msg)
   269  
   270  	msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH)
   271  	if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
   272  		return nil, executeErr
   273  	}
   274  
   275  	var res []Neigh
   276  	for _, m := range msgs {
   277  		ndm := deserializeNdmsg(m)
   278  		if msg.Index != 0 && ndm.Index != msg.Index {
   279  			// Ignore messages from other interfaces
   280  			continue
   281  		}
   282  		if msg.Family != 0 && ndm.Family != msg.Family {
   283  			continue
   284  		}
   285  		if msg.State != 0 && ndm.State != msg.State {
   286  			continue
   287  		}
   288  		if msg.Type != 0 && ndm.Type != msg.Type {
   289  			continue
   290  		}
   291  		if msg.Flags != 0 && ndm.Flags != msg.Flags {
   292  			continue
   293  		}
   294  
   295  		neigh, err := NeighDeserialize(m)
   296  		if err != nil {
   297  			continue
   298  		}
   299  
   300  		res = append(res, *neigh)
   301  	}
   302  
   303  	return res, executeErr
   304  }
   305  
   306  func NeighDeserialize(m []byte) (*Neigh, error) {
   307  	msg := deserializeNdmsg(m)
   308  
   309  	neigh := Neigh{
   310  		LinkIndex: int(msg.Index),
   311  		Family:    int(msg.Family),
   312  		State:     int(msg.State),
   313  		Type:      int(msg.Type),
   314  		Flags:     int(msg.Flags),
   315  	}
   316  
   317  	attrs, err := nl.ParseRouteAttr(m[msg.Len():])
   318  	if err != nil {
   319  		return nil, err
   320  	}
   321  
   322  	for _, attr := range attrs {
   323  		switch attr.Attr.Type {
   324  		case NDA_DST:
   325  			neigh.IP = net.IP(attr.Value)
   326  		case NDA_LLADDR:
   327  			// BUG: Is this a bug in the netlink library?
   328  			// #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
   329  			// #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
   330  			attrLen := attr.Attr.Len - unix.SizeofRtAttr
   331  			if attrLen == 4 {
   332  				neigh.LLIPAddr = net.IP(attr.Value)
   333  			} else if attrLen == 16 {
   334  				// Can be IPv6 or FireWire HWAddr
   335  				link, err := LinkByIndex(neigh.LinkIndex)
   336  				if err == nil && link.Attrs().EncapType == "tunnel6" {
   337  					neigh.IP = net.IP(attr.Value)
   338  				} else {
   339  					neigh.HardwareAddr = net.HardwareAddr(attr.Value)
   340  				}
   341  			} else {
   342  				neigh.HardwareAddr = net.HardwareAddr(attr.Value)
   343  			}
   344  		case NDA_FLAGS_EXT:
   345  			neigh.FlagsExt = int(native.Uint32(attr.Value[0:4]))
   346  		case NDA_VLAN:
   347  			neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
   348  		case NDA_VNI:
   349  			neigh.VNI = int(native.Uint32(attr.Value[0:4]))
   350  		case NDA_MASTER:
   351  			neigh.MasterIndex = int(native.Uint32(attr.Value[0:4]))
   352  		case NDA_CACHEINFO:
   353  			neigh.Confirmed = native.Uint32(attr.Value[0:4])
   354  			neigh.Used = native.Uint32(attr.Value[4:8])
   355  			neigh.Updated = native.Uint32(attr.Value[8:12])
   356  		}
   357  	}
   358  
   359  	return &neigh, nil
   360  }
   361  
   362  // NeighSubscribe takes a chan down which notifications will be sent
   363  // when neighbors are added or deleted. Close the 'done' chan to stop subscription.
   364  func NeighSubscribe(ch chan<- NeighUpdate, done <-chan struct{}) error {
   365  	return neighSubscribeAt(netns.None(), netns.None(), ch, done, nil, false, 0, nil, false)
   366  }
   367  
   368  // NeighSubscribeAt works like NeighSubscribe plus it allows the caller
   369  // to choose the network namespace in which to subscribe (ns).
   370  func NeighSubscribeAt(ns netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}) error {
   371  	return neighSubscribeAt(ns, netns.None(), ch, done, nil, false, 0, nil, false)
   372  }
   373  
   374  // NeighSubscribeOptions contains a set of options to use with
   375  // NeighSubscribeWithOptions.
   376  type NeighSubscribeOptions struct {
   377  	Namespace     *netns.NsHandle
   378  	ErrorCallback func(error)
   379  	ListExisting  bool
   380  
   381  	// max size is based on value of /proc/sys/net/core/rmem_max
   382  	ReceiveBufferSize      int
   383  	ReceiveBufferForceSize bool
   384  	ReceiveTimeout         *unix.Timeval
   385  }
   386  
   387  // NeighSubscribeWithOptions work like NeighSubscribe but enable to
   388  // provide additional options to modify the behavior. Currently, the
   389  // namespace can be provided as well as an error callback.
   390  //
   391  // When options.ListExisting is true, options.ErrorCallback may be
   392  // called with [ErrDumpInterrupted] to indicate that results from
   393  // the initial dump of links may be inconsistent or incomplete.
   394  func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error {
   395  	if options.Namespace == nil {
   396  		none := netns.None()
   397  		options.Namespace = &none
   398  	}
   399  	return neighSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting,
   400  		options.ReceiveBufferSize, options.ReceiveTimeout, options.ReceiveBufferForceSize)
   401  }
   402  
   403  func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}, cberr func(error), listExisting bool,
   404  	rcvbuf int, rcvTimeout *unix.Timeval, rcvbufForce bool) error {
   405  	s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH)
   406  	makeRequest := func(family int) error {
   407  		req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
   408  		ndmsg := &Ndmsg{Family: uint8(family)}
   409  		req.AddData(ndmsg)
   410  		if err := s.Send(req); err != nil {
   411  			return err
   412  		}
   413  		return nil
   414  	}
   415  	if err != nil {
   416  		return err
   417  	}
   418  	if rcvTimeout != nil {
   419  		if err := s.SetReceiveTimeout(rcvTimeout); err != nil {
   420  			return err
   421  		}
   422  	}
   423  	if rcvbuf != 0 {
   424  		err = s.SetReceiveBufferSize(rcvbuf, rcvbufForce)
   425  		if err != nil {
   426  			return err
   427  		}
   428  	}
   429  	if done != nil {
   430  		go func() {
   431  			<-done
   432  			s.Close()
   433  		}()
   434  	}
   435  	if listExisting {
   436  		if err := makeRequest(unix.AF_UNSPEC); err != nil {
   437  			return err
   438  		}
   439  		// We have to wait for NLMSG_DONE before making AF_BRIDGE request
   440  	}
   441  	go func() {
   442  		defer close(ch)
   443  		for {
   444  			msgs, from, err := s.Receive()
   445  			if err != nil {
   446  				if cberr != nil {
   447  					cberr(err)
   448  				}
   449  				return
   450  			}
   451  			if from.Pid != nl.PidKernel {
   452  				if cberr != nil {
   453  					cberr(fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel))
   454  				}
   455  				continue
   456  			}
   457  			for _, m := range msgs {
   458  				if m.Header.Flags&unix.NLM_F_DUMP_INTR != 0 && cberr != nil {
   459  					cberr(ErrDumpInterrupted)
   460  				}
   461  				if m.Header.Type == unix.NLMSG_DONE {
   462  					if listExisting {
   463  						// This will be called after handling AF_UNSPEC
   464  						// list request, we have to wait for NLMSG_DONE
   465  						// before making another request
   466  						if err := makeRequest(unix.AF_BRIDGE); err != nil {
   467  							if cberr != nil {
   468  								cberr(err)
   469  							}
   470  							return
   471  						}
   472  						listExisting = false
   473  					}
   474  					continue
   475  				}
   476  				if m.Header.Type == unix.NLMSG_ERROR {
   477  					nError := int32(native.Uint32(m.Data[0:4]))
   478  					if nError == 0 {
   479  						continue
   480  					}
   481  					if cberr != nil {
   482  						cberr(syscall.Errno(-nError))
   483  					}
   484  					return
   485  				}
   486  				neigh, err := NeighDeserialize(m.Data)
   487  				if err != nil {
   488  					if cberr != nil {
   489  						cberr(err)
   490  					}
   491  					return
   492  				}
   493  				ch <- NeighUpdate{Type: m.Header.Type, Neigh: *neigh}
   494  			}
   495  		}
   496  	}()
   497  
   498  	return nil
   499  }