2014-09-16 00:44:20 +00:00
|
|
|
package netlink
|
|
|
|
|
|
|
|
import (
|
Add ErrDumpInterrupted
Add a specific error to report that a netlink response had
NLM_F_DUMP_INTR set, indicating that the set of results may be
incomplete or inconsistent.
unix.EINTR was previously returned (with no results) when the
NLM_F_DUMP_INTR flag was set. Now, errors.Is(err, unix.EINTR) will
still work. But, this will be a breaking change for any code that's
checking for equality with unix.EINTR.
Return results with ErrDumpInterrupted. Results may be incomplete
or inconsistent, but give the caller the option of using them.
Look for NLM_F_DUMP_INTR in more places:
- linkSubscribeAt, neighSubscribeAt, routeSubscribeAt
- can do an initial dump, which may report inconsistent results
-> if there's an error callback, call it with ErrDumpInterrupted
- socketDiagXDPExecutor
- makes an NLM_F_DUMP request, without using Execute()
-> give it the same behaviour as functions that do use Execute()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-09-05 08:25:13 +00:00
|
|
|
"errors"
|
2019-07-15 17:54:20 +00:00
|
|
|
"fmt"
|
2014-09-16 00:44:20 +00:00
|
|
|
"net"
|
2018-09-21 03:03:18 +00:00
|
|
|
"syscall"
|
2014-09-16 00:44:20 +00:00
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
"github.com/vishvananda/netlink/nl"
|
2018-09-21 03:03:18 +00:00
|
|
|
"github.com/vishvananda/netns"
|
2017-10-20 20:38:07 +00:00
|
|
|
"golang.org/x/sys/unix"
|
2014-09-16 00:44:20 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2015-02-12 19:16:19 +00:00
|
|
|
NDA_UNSPEC = iota
|
|
|
|
NDA_DST
|
|
|
|
NDA_LLADDR
|
|
|
|
NDA_CACHEINFO
|
|
|
|
NDA_PROBES
|
|
|
|
NDA_VLAN
|
|
|
|
NDA_PORT
|
|
|
|
NDA_VNI
|
|
|
|
NDA_IFINDEX
|
2018-12-21 08:18:13 +00:00
|
|
|
NDA_MASTER
|
|
|
|
NDA_LINK_NETNSID
|
|
|
|
NDA_SRC_VNI
|
2021-10-26 10:04:16 +00:00
|
|
|
NDA_PROTOCOL
|
|
|
|
NDA_NH_ID
|
|
|
|
NDA_FDB_EXT_ATTRS
|
|
|
|
NDA_FLAGS_EXT
|
|
|
|
NDA_MAX = NDA_FLAGS_EXT
|
2014-09-16 00:44:20 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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 (
|
2021-05-15 13:46:42 +00:00
|
|
|
NTF_USE = 0x01
|
|
|
|
NTF_SELF = 0x02
|
|
|
|
NTF_MASTER = 0x04
|
|
|
|
NTF_PROXY = 0x08
|
|
|
|
NTF_EXT_LEARNED = 0x10
|
2021-10-26 10:04:16 +00:00
|
|
|
NTF_OFFLOADED = 0x20
|
|
|
|
NTF_STICKY = 0x40
|
2021-05-15 13:46:42 +00:00
|
|
|
NTF_ROUTER = 0x80
|
2014-09-16 00:44:20 +00:00
|
|
|
)
|
|
|
|
|
2021-10-26 10:04:16 +00:00
|
|
|
// Extended Neighbor Flags
|
|
|
|
const (
|
|
|
|
NTF_EXT_MANAGED = 0x00000001
|
|
|
|
)
|
|
|
|
|
2018-11-11 13:01:26 +00:00
|
|
|
// Ndmsg is for adding, removing or receiving information about a neighbor table entry
|
2014-09-16 00:44:20 +00:00
|
|
|
type Ndmsg struct {
|
|
|
|
Family uint8
|
|
|
|
Index uint32
|
|
|
|
State uint16
|
2014-10-29 00:22:52 +00:00
|
|
|
Flags uint8
|
2014-09-16 00:44:20 +00:00
|
|
|
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 {
|
2016-05-09 23:55:00 +00:00
|
|
|
return pkgHandle.NeighAdd(neigh)
|
2016-05-08 18:35:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NeighAdd will add an IP to MAC mapping to the ARP table
|
|
|
|
// Equivalent to: `ip neigh add ....`
|
|
|
|
func (h *Handle) NeighAdd(neigh *Neigh) error {
|
2017-10-20 20:38:07 +00:00
|
|
|
return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_EXCL)
|
2014-09-16 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
2015-11-10 13:38:50 +00:00
|
|
|
// NeighSet will add or replace an IP to MAC mapping to the ARP table
|
2014-09-16 00:44:20 +00:00
|
|
|
// Equivalent to: `ip neigh replace....`
|
|
|
|
func NeighSet(neigh *Neigh) error {
|
2016-05-09 23:55:00 +00:00
|
|
|
return pkgHandle.NeighSet(neigh)
|
2016-05-08 18:35:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2017-10-20 20:38:07 +00:00
|
|
|
return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_REPLACE)
|
2014-09-16 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NeighAppend will append an entry to FDB
|
|
|
|
// Equivalent to: `bridge fdb append...`
|
|
|
|
func NeighAppend(neigh *Neigh) error {
|
2016-05-09 23:55:00 +00:00
|
|
|
return pkgHandle.NeighAppend(neigh)
|
2016-05-08 18:35:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NeighAppend will append an entry to FDB
|
|
|
|
// Equivalent to: `bridge fdb append...`
|
|
|
|
func (h *Handle) NeighAppend(neigh *Neigh) error {
|
2017-10-20 20:38:07 +00:00
|
|
|
return h.neighAdd(neigh, unix.NLM_F_CREATE|unix.NLM_F_APPEND)
|
2014-09-16 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
2016-05-08 18:35:49 +00:00
|
|
|
// NeighAppend will append an entry to FDB
|
|
|
|
// Equivalent to: `bridge fdb append...`
|
2014-09-16 00:44:20 +00:00
|
|
|
func neighAdd(neigh *Neigh, mode int) error {
|
2016-05-09 23:55:00 +00:00
|
|
|
return pkgHandle.neighAdd(neigh, mode)
|
2016-05-08 18:35:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NeighAppend will append an entry to FDB
|
|
|
|
// Equivalent to: `bridge fdb append...`
|
|
|
|
func (h *Handle) neighAdd(neigh *Neigh, mode int) error {
|
2017-10-20 20:38:07 +00:00
|
|
|
req := h.newNetlinkRequest(unix.RTM_NEWNEIGH, mode|unix.NLM_F_ACK)
|
2014-09-16 00:44:20 +00:00
|
|
|
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 {
|
2016-05-09 23:55:00 +00:00
|
|
|
return pkgHandle.NeighDel(neigh)
|
2016-05-08 18:35:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2017-10-20 20:38:07 +00:00
|
|
|
req := h.newNetlinkRequest(unix.RTM_DELNEIGH, unix.NLM_F_ACK)
|
2014-09-16 00:44:20 +00:00
|
|
|
return neighHandle(neigh, req)
|
|
|
|
}
|
|
|
|
|
|
|
|
func neighHandle(neigh *Neigh, req *nl.NetlinkRequest) error {
|
|
|
|
var family int
|
2017-08-29 00:59:25 +00:00
|
|
|
|
2014-09-16 00:44:20 +00:00
|
|
|
if neigh.Family > 0 {
|
|
|
|
family = neigh.Family
|
|
|
|
} else {
|
|
|
|
family = nl.GetIPFamily(neigh.IP)
|
|
|
|
}
|
|
|
|
|
|
|
|
msg := Ndmsg{
|
|
|
|
Family: uint8(family),
|
2014-10-13 23:09:09 +00:00
|
|
|
Index: uint32(neigh.LinkIndex),
|
2014-09-16 00:44:20 +00:00
|
|
|
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)
|
|
|
|
|
2017-08-29 00:59:25 +00:00
|
|
|
if neigh.LLIPAddr != nil {
|
|
|
|
llIPData := nl.NewRtAttr(NDA_LLADDR, neigh.LLIPAddr.To4())
|
|
|
|
req.AddData(llIPData)
|
2021-10-29 13:36:25 +00:00
|
|
|
} else if neigh.HardwareAddr != nil {
|
2016-08-23 23:01:24 +00:00
|
|
|
hwData := nl.NewRtAttr(NDA_LLADDR, []byte(neigh.HardwareAddr))
|
|
|
|
req.AddData(hwData)
|
|
|
|
}
|
2014-09-16 00:44:20 +00:00
|
|
|
|
2021-10-26 10:04:16 +00:00
|
|
|
if neigh.FlagsExt != 0 {
|
|
|
|
flagsExtData := nl.NewRtAttr(NDA_FLAGS_EXT, nl.Uint32Attr(uint32(neigh.FlagsExt)))
|
|
|
|
req.AddData(flagsExtData)
|
|
|
|
}
|
|
|
|
|
2017-05-04 13:15:30 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2018-12-21 08:18:13 +00:00
|
|
|
if neigh.MasterIndex != 0 {
|
|
|
|
masterData := nl.NewRtAttr(NDA_MASTER, nl.Uint32Attr(uint32(neigh.MasterIndex)))
|
|
|
|
req.AddData(masterData)
|
|
|
|
}
|
|
|
|
|
2017-10-20 20:38:07 +00:00
|
|
|
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
|
2014-09-16 00:44:20 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-11-11 13:01:26 +00:00
|
|
|
// NeighList returns a list of IP-MAC mappings in the system (ARP table).
|
2014-09-16 00:44:20 +00:00
|
|
|
// Equivalent to: `ip neighbor show`.
|
|
|
|
// The list can be filtered by link and ip family.
|
Add ErrDumpInterrupted
Add a specific error to report that a netlink response had
NLM_F_DUMP_INTR set, indicating that the set of results may be
incomplete or inconsistent.
unix.EINTR was previously returned (with no results) when the
NLM_F_DUMP_INTR flag was set. Now, errors.Is(err, unix.EINTR) will
still work. But, this will be a breaking change for any code that's
checking for equality with unix.EINTR.
Return results with ErrDumpInterrupted. Results may be incomplete
or inconsistent, but give the caller the option of using them.
Look for NLM_F_DUMP_INTR in more places:
- linkSubscribeAt, neighSubscribeAt, routeSubscribeAt
- can do an initial dump, which may report inconsistent results
-> if there's an error callback, call it with ErrDumpInterrupted
- socketDiagXDPExecutor
- makes an NLM_F_DUMP request, without using Execute()
-> give it the same behaviour as functions that do use Execute()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-09-05 08:25:13 +00:00
|
|
|
//
|
|
|
|
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
|
|
|
|
// or incomplete.
|
2014-09-16 00:44:20 +00:00
|
|
|
func NeighList(linkIndex, family int) ([]Neigh, error) {
|
2016-05-09 23:55:00 +00:00
|
|
|
return pkgHandle.NeighList(linkIndex, family)
|
2016-05-08 18:35:49 +00:00
|
|
|
}
|
|
|
|
|
2018-11-11 13:01:26 +00:00
|
|
|
// NeighProxyList returns a list of neighbor proxies in the system.
|
2016-08-23 23:01:24 +00:00
|
|
|
// Equivalent to: `ip neighbor show proxy`.
|
|
|
|
// The list can be filtered by link and ip family.
|
Add ErrDumpInterrupted
Add a specific error to report that a netlink response had
NLM_F_DUMP_INTR set, indicating that the set of results may be
incomplete or inconsistent.
unix.EINTR was previously returned (with no results) when the
NLM_F_DUMP_INTR flag was set. Now, errors.Is(err, unix.EINTR) will
still work. But, this will be a breaking change for any code that's
checking for equality with unix.EINTR.
Return results with ErrDumpInterrupted. Results may be incomplete
or inconsistent, but give the caller the option of using them.
Look for NLM_F_DUMP_INTR in more places:
- linkSubscribeAt, neighSubscribeAt, routeSubscribeAt
- can do an initial dump, which may report inconsistent results
-> if there's an error callback, call it with ErrDumpInterrupted
- socketDiagXDPExecutor
- makes an NLM_F_DUMP request, without using Execute()
-> give it the same behaviour as functions that do use Execute()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-09-05 08:25:13 +00:00
|
|
|
//
|
|
|
|
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
|
|
|
|
// or incomplete.
|
2016-08-23 23:01:24 +00:00
|
|
|
func NeighProxyList(linkIndex, family int) ([]Neigh, error) {
|
|
|
|
return pkgHandle.NeighProxyList(linkIndex, family)
|
|
|
|
}
|
|
|
|
|
2018-11-11 13:01:26 +00:00
|
|
|
// NeighList returns a list of IP-MAC mappings in the system (ARP table).
|
2016-05-08 18:35:49 +00:00
|
|
|
// Equivalent to: `ip neighbor show`.
|
|
|
|
// The list can be filtered by link and ip family.
|
Add ErrDumpInterrupted
Add a specific error to report that a netlink response had
NLM_F_DUMP_INTR set, indicating that the set of results may be
incomplete or inconsistent.
unix.EINTR was previously returned (with no results) when the
NLM_F_DUMP_INTR flag was set. Now, errors.Is(err, unix.EINTR) will
still work. But, this will be a breaking change for any code that's
checking for equality with unix.EINTR.
Return results with ErrDumpInterrupted. Results may be incomplete
or inconsistent, but give the caller the option of using them.
Look for NLM_F_DUMP_INTR in more places:
- linkSubscribeAt, neighSubscribeAt, routeSubscribeAt
- can do an initial dump, which may report inconsistent results
-> if there's an error callback, call it with ErrDumpInterrupted
- socketDiagXDPExecutor
- makes an NLM_F_DUMP request, without using Execute()
-> give it the same behaviour as functions that do use Execute()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-09-05 08:25:13 +00:00
|
|
|
//
|
|
|
|
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
|
|
|
|
// or incomplete.
|
2016-05-08 18:35:49 +00:00
|
|
|
func (h *Handle) NeighList(linkIndex, family int) ([]Neigh, error) {
|
2018-11-11 13:01:26 +00:00
|
|
|
return h.NeighListExecute(Ndmsg{
|
|
|
|
Family: uint8(family),
|
|
|
|
Index: uint32(linkIndex),
|
|
|
|
})
|
2016-08-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
|
2018-11-11 13:01:26 +00:00
|
|
|
// NeighProxyList returns a list of neighbor proxies in the system.
|
2016-08-23 23:01:24 +00:00
|
|
|
// Equivalent to: `ip neighbor show proxy`.
|
|
|
|
// The list can be filtered by link, ip family.
|
Add ErrDumpInterrupted
Add a specific error to report that a netlink response had
NLM_F_DUMP_INTR set, indicating that the set of results may be
incomplete or inconsistent.
unix.EINTR was previously returned (with no results) when the
NLM_F_DUMP_INTR flag was set. Now, errors.Is(err, unix.EINTR) will
still work. But, this will be a breaking change for any code that's
checking for equality with unix.EINTR.
Return results with ErrDumpInterrupted. Results may be incomplete
or inconsistent, but give the caller the option of using them.
Look for NLM_F_DUMP_INTR in more places:
- linkSubscribeAt, neighSubscribeAt, routeSubscribeAt
- can do an initial dump, which may report inconsistent results
-> if there's an error callback, call it with ErrDumpInterrupted
- socketDiagXDPExecutor
- makes an NLM_F_DUMP request, without using Execute()
-> give it the same behaviour as functions that do use Execute()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-09-05 08:25:13 +00:00
|
|
|
//
|
|
|
|
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
|
|
|
|
// or incomplete.
|
2016-08-23 23:01:24 +00:00
|
|
|
func (h *Handle) NeighProxyList(linkIndex, family int) ([]Neigh, error) {
|
2018-11-11 13:01:26 +00:00
|
|
|
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.
|
Add ErrDumpInterrupted
Add a specific error to report that a netlink response had
NLM_F_DUMP_INTR set, indicating that the set of results may be
incomplete or inconsistent.
unix.EINTR was previously returned (with no results) when the
NLM_F_DUMP_INTR flag was set. Now, errors.Is(err, unix.EINTR) will
still work. But, this will be a breaking change for any code that's
checking for equality with unix.EINTR.
Return results with ErrDumpInterrupted. Results may be incomplete
or inconsistent, but give the caller the option of using them.
Look for NLM_F_DUMP_INTR in more places:
- linkSubscribeAt, neighSubscribeAt, routeSubscribeAt
- can do an initial dump, which may report inconsistent results
-> if there's an error callback, call it with ErrDumpInterrupted
- socketDiagXDPExecutor
- makes an NLM_F_DUMP request, without using Execute()
-> give it the same behaviour as functions that do use Execute()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-09-05 08:25:13 +00:00
|
|
|
//
|
|
|
|
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
|
|
|
|
// or incomplete.
|
2018-11-11 13:01:26 +00:00
|
|
|
func NeighListExecute(msg Ndmsg) ([]Neigh, error) {
|
|
|
|
return pkgHandle.NeighListExecute(msg)
|
2016-08-23 23:01:24 +00:00
|
|
|
}
|
|
|
|
|
2018-11-11 13:01:26 +00:00
|
|
|
// NeighListExecute returns a list of neighbour entries filtered by link, ip family, flag and state.
|
Add ErrDumpInterrupted
Add a specific error to report that a netlink response had
NLM_F_DUMP_INTR set, indicating that the set of results may be
incomplete or inconsistent.
unix.EINTR was previously returned (with no results) when the
NLM_F_DUMP_INTR flag was set. Now, errors.Is(err, unix.EINTR) will
still work. But, this will be a breaking change for any code that's
checking for equality with unix.EINTR.
Return results with ErrDumpInterrupted. Results may be incomplete
or inconsistent, but give the caller the option of using them.
Look for NLM_F_DUMP_INTR in more places:
- linkSubscribeAt, neighSubscribeAt, routeSubscribeAt
- can do an initial dump, which may report inconsistent results
-> if there's an error callback, call it with ErrDumpInterrupted
- socketDiagXDPExecutor
- makes an NLM_F_DUMP request, without using Execute()
-> give it the same behaviour as functions that do use Execute()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-09-05 08:25:13 +00:00
|
|
|
//
|
|
|
|
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
|
|
|
|
// or incomplete.
|
2018-11-11 13:01:26 +00:00
|
|
|
func (h *Handle) NeighListExecute(msg Ndmsg) ([]Neigh, error) {
|
2017-10-20 20:38:07 +00:00
|
|
|
req := h.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
|
2014-09-16 00:44:20 +00:00
|
|
|
req.AddData(&msg)
|
|
|
|
|
Add ErrDumpInterrupted
Add a specific error to report that a netlink response had
NLM_F_DUMP_INTR set, indicating that the set of results may be
incomplete or inconsistent.
unix.EINTR was previously returned (with no results) when the
NLM_F_DUMP_INTR flag was set. Now, errors.Is(err, unix.EINTR) will
still work. But, this will be a breaking change for any code that's
checking for equality with unix.EINTR.
Return results with ErrDumpInterrupted. Results may be incomplete
or inconsistent, but give the caller the option of using them.
Look for NLM_F_DUMP_INTR in more places:
- linkSubscribeAt, neighSubscribeAt, routeSubscribeAt
- can do an initial dump, which may report inconsistent results
-> if there's an error callback, call it with ErrDumpInterrupted
- socketDiagXDPExecutor
- makes an NLM_F_DUMP request, without using Execute()
-> give it the same behaviour as functions that do use Execute()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-09-05 08:25:13 +00:00
|
|
|
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNEIGH)
|
|
|
|
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
|
|
|
|
return nil, executeErr
|
2014-09-16 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
2015-06-19 00:41:46 +00:00
|
|
|
var res []Neigh
|
2014-09-16 00:44:20 +00:00
|
|
|
for _, m := range msgs {
|
|
|
|
ndm := deserializeNdmsg(m)
|
2018-11-11 13:01:26 +00:00
|
|
|
if msg.Index != 0 && ndm.Index != msg.Index {
|
2014-09-16 00:44:20 +00:00
|
|
|
// Ignore messages from other interfaces
|
|
|
|
continue
|
|
|
|
}
|
2020-03-23 21:13:19 +00:00
|
|
|
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
|
|
|
|
}
|
2014-09-16 00:44:20 +00:00
|
|
|
|
|
|
|
neigh, err := NeighDeserialize(m)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
res = append(res, *neigh)
|
|
|
|
}
|
|
|
|
|
Add ErrDumpInterrupted
Add a specific error to report that a netlink response had
NLM_F_DUMP_INTR set, indicating that the set of results may be
incomplete or inconsistent.
unix.EINTR was previously returned (with no results) when the
NLM_F_DUMP_INTR flag was set. Now, errors.Is(err, unix.EINTR) will
still work. But, this will be a breaking change for any code that's
checking for equality with unix.EINTR.
Return results with ErrDumpInterrupted. Results may be incomplete
or inconsistent, but give the caller the option of using them.
Look for NLM_F_DUMP_INTR in more places:
- linkSubscribeAt, neighSubscribeAt, routeSubscribeAt
- can do an initial dump, which may report inconsistent results
-> if there's an error callback, call it with ErrDumpInterrupted
- socketDiagXDPExecutor
- makes an NLM_F_DUMP request, without using Execute()
-> give it the same behaviour as functions that do use Execute()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-09-05 08:25:13 +00:00
|
|
|
return res, executeErr
|
2014-09-16 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NeighDeserialize(m []byte) (*Neigh, error) {
|
|
|
|
msg := deserializeNdmsg(m)
|
|
|
|
|
|
|
|
neigh := Neigh{
|
2014-10-13 23:09:09 +00:00
|
|
|
LinkIndex: int(msg.Index),
|
|
|
|
Family: int(msg.Family),
|
|
|
|
State: int(msg.State),
|
|
|
|
Type: int(msg.Type),
|
|
|
|
Flags: int(msg.Flags),
|
2014-09-16 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
2017-08-29 00:59:25 +00:00
|
|
|
// 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))
|
2017-10-20 20:38:07 +00:00
|
|
|
attrLen := attr.Attr.Len - unix.SizeofRtAttr
|
2018-12-18 09:10:34 +00:00
|
|
|
if attrLen == 4 {
|
2017-08-29 00:59:25 +00:00
|
|
|
neigh.LLIPAddr = net.IP(attr.Value)
|
2018-12-18 09:10:34 +00:00
|
|
|
} 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)
|
|
|
|
}
|
2017-08-29 00:59:25 +00:00
|
|
|
} else {
|
|
|
|
neigh.HardwareAddr = net.HardwareAddr(attr.Value)
|
|
|
|
}
|
2021-10-26 10:04:16 +00:00
|
|
|
case NDA_FLAGS_EXT:
|
|
|
|
neigh.FlagsExt = int(native.Uint32(attr.Value[0:4]))
|
2017-05-04 13:15:30 +00:00
|
|
|
case NDA_VLAN:
|
|
|
|
neigh.Vlan = int(native.Uint16(attr.Value[0:2]))
|
|
|
|
case NDA_VNI:
|
|
|
|
neigh.VNI = int(native.Uint32(attr.Value[0:4]))
|
2018-12-21 08:18:13 +00:00
|
|
|
case NDA_MASTER:
|
|
|
|
neigh.MasterIndex = int(native.Uint32(attr.Value[0:4]))
|
2014-09-16 00:44:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &neigh, nil
|
|
|
|
}
|
2018-09-21 03:03:18 +00:00
|
|
|
|
|
|
|
// 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 {
|
2022-05-05 22:56:08 +00:00
|
|
|
return neighSubscribeAt(netns.None(), netns.None(), ch, done, nil, false, 0, nil, false)
|
2018-09-21 03:03:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2022-05-05 22:56:08 +00:00
|
|
|
return neighSubscribeAt(ns, netns.None(), ch, done, nil, false, 0, nil, false)
|
2018-09-21 03:03:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NeighSubscribeOptions contains a set of options to use with
|
|
|
|
// NeighSubscribeWithOptions.
|
|
|
|
type NeighSubscribeOptions struct {
|
|
|
|
Namespace *netns.NsHandle
|
|
|
|
ErrorCallback func(error)
|
|
|
|
ListExisting bool
|
2022-05-05 22:56:08 +00:00
|
|
|
|
|
|
|
// max size is based on value of /proc/sys/net/core/rmem_max
|
|
|
|
ReceiveBufferSize int
|
|
|
|
ReceiveBufferForceSize bool
|
|
|
|
ReceiveTimeout *unix.Timeval
|
2018-09-21 03:03:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
Add ErrDumpInterrupted
Add a specific error to report that a netlink response had
NLM_F_DUMP_INTR set, indicating that the set of results may be
incomplete or inconsistent.
unix.EINTR was previously returned (with no results) when the
NLM_F_DUMP_INTR flag was set. Now, errors.Is(err, unix.EINTR) will
still work. But, this will be a breaking change for any code that's
checking for equality with unix.EINTR.
Return results with ErrDumpInterrupted. Results may be incomplete
or inconsistent, but give the caller the option of using them.
Look for NLM_F_DUMP_INTR in more places:
- linkSubscribeAt, neighSubscribeAt, routeSubscribeAt
- can do an initial dump, which may report inconsistent results
-> if there's an error callback, call it with ErrDumpInterrupted
- socketDiagXDPExecutor
- makes an NLM_F_DUMP request, without using Execute()
-> give it the same behaviour as functions that do use Execute()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-09-05 08:25:13 +00:00
|
|
|
//
|
|
|
|
// When options.ListExisting is true, options.ErrorCallback may be
|
|
|
|
// called with [ErrDumpInterrupted] to indicate that results from
|
|
|
|
// the initial dump of links may be inconsistent or incomplete.
|
2018-09-21 03:03:18 +00:00
|
|
|
func NeighSubscribeWithOptions(ch chan<- NeighUpdate, done <-chan struct{}, options NeighSubscribeOptions) error {
|
|
|
|
if options.Namespace == nil {
|
|
|
|
none := netns.None()
|
|
|
|
options.Namespace = &none
|
|
|
|
}
|
2022-05-05 22:56:08 +00:00
|
|
|
return neighSubscribeAt(*options.Namespace, netns.None(), ch, done, options.ErrorCallback, options.ListExisting,
|
|
|
|
options.ReceiveBufferSize, options.ReceiveTimeout, options.ReceiveBufferForceSize)
|
2018-09-21 03:03:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-05 22:56:08 +00:00
|
|
|
func neighSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- NeighUpdate, done <-chan struct{}, cberr func(error), listExisting bool,
|
|
|
|
rcvbuf int, rcvTimeout *unix.Timeval, rcvbufForce bool) error {
|
2018-09-21 03:03:18 +00:00
|
|
|
s, err := nl.SubscribeAt(newNs, curNs, unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH)
|
2018-12-21 08:18:13 +00:00
|
|
|
makeRequest := func(family int) error {
|
2022-05-07 16:08:58 +00:00
|
|
|
req := pkgHandle.newNetlinkRequest(unix.RTM_GETNEIGH, unix.NLM_F_DUMP)
|
|
|
|
ndmsg := &Ndmsg{Family: uint8(family)}
|
|
|
|
req.AddData(ndmsg)
|
2018-12-21 08:18:13 +00:00
|
|
|
if err := s.Send(req); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2018-09-21 03:03:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-05-05 22:56:08 +00:00
|
|
|
if rcvTimeout != nil {
|
|
|
|
if err := s.SetReceiveTimeout(rcvTimeout); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if rcvbuf != 0 {
|
|
|
|
err = s.SetReceiveBufferSize(rcvbuf, rcvbufForce)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2018-09-21 03:03:18 +00:00
|
|
|
if done != nil {
|
|
|
|
go func() {
|
|
|
|
<-done
|
|
|
|
s.Close()
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
if listExisting {
|
2018-12-21 08:18:13 +00:00
|
|
|
if err := makeRequest(unix.AF_UNSPEC); err != nil {
|
2018-09-21 03:03:18 +00:00
|
|
|
return err
|
|
|
|
}
|
2018-12-21 08:18:13 +00:00
|
|
|
// We have to wait for NLMSG_DONE before making AF_BRIDGE request
|
2018-09-21 03:03:18 +00:00
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
defer close(ch)
|
|
|
|
for {
|
2019-07-15 17:54:20 +00:00
|
|
|
msgs, from, err := s.Receive()
|
2018-09-21 03:03:18 +00:00
|
|
|
if err != nil {
|
|
|
|
if cberr != nil {
|
|
|
|
cberr(err)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2019-07-15 17:54:20 +00:00
|
|
|
if from.Pid != nl.PidKernel {
|
|
|
|
if cberr != nil {
|
|
|
|
cberr(fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel))
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2018-09-21 03:03:18 +00:00
|
|
|
for _, m := range msgs {
|
Add ErrDumpInterrupted
Add a specific error to report that a netlink response had
NLM_F_DUMP_INTR set, indicating that the set of results may be
incomplete or inconsistent.
unix.EINTR was previously returned (with no results) when the
NLM_F_DUMP_INTR flag was set. Now, errors.Is(err, unix.EINTR) will
still work. But, this will be a breaking change for any code that's
checking for equality with unix.EINTR.
Return results with ErrDumpInterrupted. Results may be incomplete
or inconsistent, but give the caller the option of using them.
Look for NLM_F_DUMP_INTR in more places:
- linkSubscribeAt, neighSubscribeAt, routeSubscribeAt
- can do an initial dump, which may report inconsistent results
-> if there's an error callback, call it with ErrDumpInterrupted
- socketDiagXDPExecutor
- makes an NLM_F_DUMP request, without using Execute()
-> give it the same behaviour as functions that do use Execute()
Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-09-05 08:25:13 +00:00
|
|
|
if m.Header.Flags&unix.NLM_F_DUMP_INTR != 0 && cberr != nil {
|
|
|
|
cberr(ErrDumpInterrupted)
|
|
|
|
}
|
2018-09-21 03:03:18 +00:00
|
|
|
if m.Header.Type == unix.NLMSG_DONE {
|
2018-12-21 08:18:13 +00:00
|
|
|
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
|
|
|
|
}
|
2018-09-21 03:03:18 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if m.Header.Type == unix.NLMSG_ERROR {
|
2022-05-07 16:08:58 +00:00
|
|
|
nError := int32(native.Uint32(m.Data[0:4]))
|
|
|
|
if nError == 0 {
|
2018-09-21 03:03:18 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if cberr != nil {
|
2022-05-07 16:08:58 +00:00
|
|
|
cberr(syscall.Errno(-nError))
|
2018-09-21 03:03:18 +00:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|