github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/neigh_linux.go (about)

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