Skip to content

Commit

Permalink
Add ErrDumpInterrupted
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
robmry authored and aboch committed Sep 22, 2024
1 parent a018296 commit 084abd9
Show file tree
Hide file tree
Showing 24 changed files with 465 additions and 146 deletions.
15 changes: 11 additions & 4 deletions addr_linux.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package netlink

import (
"errors"
"fmt"
"net"
"strings"
Expand Down Expand Up @@ -169,21 +170,27 @@ func (h *Handle) addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error
// AddrList gets a list of IP addresses in the system.
// Equivalent to: `ip addr show`.
// The list can be filtered by link and ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func AddrList(link Link, family int) ([]Addr, error) {
return pkgHandle.AddrList(link, family)
}

// AddrList gets a list of IP addresses in the system.
// Equivalent to: `ip addr show`.
// The list can be filtered by link and ip family.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) AddrList(link Link, family int) ([]Addr, error) {
req := h.newNetlinkRequest(unix.RTM_GETADDR, unix.NLM_F_DUMP)
msg := nl.NewIfAddrmsg(family)
req.AddData(msg)

msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWADDR)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}

indexFilter := 0
Expand Down Expand Up @@ -212,7 +219,7 @@ func (h *Handle) AddrList(link Link, family int) ([]Addr, error) {
res = append(res, addr)
}

return res, nil
return res, executeErr
}

func parseAddr(m []byte) (addr Addr, family int, err error) {
Expand Down
15 changes: 11 additions & 4 deletions bridge_linux.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package netlink

import (
"errors"
"fmt"

"github.com/vishvananda/netlink/nl"
Expand All @@ -9,21 +10,27 @@ import (

// BridgeVlanList gets a map of device id to bridge vlan infos.
// Equivalent to: `bridge vlan show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
return pkgHandle.BridgeVlanList()
}

// BridgeVlanList gets a map of device id to bridge vlan infos.
// Equivalent to: `bridge vlan show`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_DUMP)
msg := nl.NewIfInfomsg(unix.AF_BRIDGE)
req.AddData(msg)
req.AddData(nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(uint32(nl.RTEXT_FILTER_BRVLAN))))

msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
ret := make(map[int32][]*nl.BridgeVlanInfo)
for _, m := range msgs {
Expand Down Expand Up @@ -51,7 +58,7 @@ func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
}
}
}
return ret, nil
return ret, executeErr
}

// BridgeVlanAdd adds a new vlan filter entry
Expand Down
16 changes: 12 additions & 4 deletions chain_linux.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package netlink

import (
"errors"

"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
Expand Down Expand Up @@ -56,13 +58,19 @@ func (h *Handle) chainModify(cmd, flags int, link Link, chain Chain) error {
// ChainList gets a list of chains in the system.
// Equivalent to: `tc chain list`.
// The list can be filtered by link.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func ChainList(link Link, parent uint32) ([]Chain, error) {
return pkgHandle.ChainList(link, parent)
}

// ChainList gets a list of chains in the system.
// Equivalent to: `tc chain list`.
// The list can be filtered by link.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) ChainList(link Link, parent uint32) ([]Chain, error) {
req := h.newNetlinkRequest(unix.RTM_GETCHAIN, unix.NLM_F_DUMP)
index := int32(0)
Expand All @@ -78,9 +86,9 @@ func (h *Handle) ChainList(link Link, parent uint32) ([]Chain, error) {
}
req.AddData(msg)

msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWCHAIN)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWCHAIN)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}

var res []Chain
Expand Down Expand Up @@ -108,5 +116,5 @@ func (h *Handle) ChainList(link Link, parent uint32) ([]Chain, error) {
res = append(res, chain)
}

return res, nil
return res, executeErr
}
14 changes: 10 additions & 4 deletions class_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,20 @@ func classPayload(req *nl.NetlinkRequest, class Class) error {

// ClassList gets a list of classes in the system.
// Equivalent to: `tc class show`.
//
// Generally returns nothing if link and parent are not specified.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func ClassList(link Link, parent uint32) ([]Class, error) {
return pkgHandle.ClassList(link, parent)
}

// ClassList gets a list of classes in the system.
// Equivalent to: `tc class show`.
//
// Generally returns nothing if link and parent are not specified.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
req := h.newNetlinkRequest(unix.RTM_GETTCLASS, unix.NLM_F_DUMP)
msg := &nl.TcMsg{
Expand All @@ -222,9 +228,9 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
}
req.AddData(msg)

msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTCLASS)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}

var res []Class
Expand Down Expand Up @@ -295,7 +301,7 @@ func (h *Handle) ClassList(link Link, parent uint32) ([]Class, error) {
res = append(res, class)
}

return res, nil
return res, executeErr
}

func parseHtbClassData(class Class, data []syscall.NetlinkRouteAttr) (bool, error) {
Expand Down
14 changes: 10 additions & 4 deletions conntrack_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ type InetFamily uint8

// ConntrackTableList returns the flow list of a table of a specific family
// conntrack -L [table] [options] List conntrack or expectation table
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
return pkgHandle.ConntrackTableList(table, family)
}
Expand Down Expand Up @@ -84,10 +87,13 @@ func ConntrackDeleteFilters(table ConntrackTableType, family InetFamily, filters

// ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed
// conntrack -L [table] [options] List conntrack or expectation table
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
res, err := h.dumpConntrackTable(table, family)
if err != nil {
return nil, err
res, executeErr := h.dumpConntrackTable(table, family)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}

// Deserialize all the flows
Expand All @@ -96,7 +102,7 @@ func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily)
result = append(result, parseRawData(dataRaw))
}

return result, nil
return result, executeErr
}

// ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed
Expand Down
40 changes: 28 additions & 12 deletions devlink_linux.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package netlink

import (
"errors"
"fmt"
"net"
"strings"
Expand Down Expand Up @@ -466,6 +467,8 @@ func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) {

// DevLinkGetDeviceList provides a pointer to devlink devices and nil error,
// otherwise returns an error code.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
if err != nil {
Expand All @@ -478,9 +481,9 @@ func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
req := h.newNetlinkRequest(int(f.ID),
unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP)
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
devices, err := parseDevLinkDeviceList(msgs)
if err != nil {
Expand All @@ -489,11 +492,14 @@ func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
for _, d := range devices {
h.getEswitchAttrs(f, d)
}
return devices, nil
return devices, executeErr
}

// DevLinkGetDeviceList provides a pointer to devlink devices and nil error,
// otherwise returns an error code.
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
return pkgHandle.DevLinkGetDeviceList()
}
Expand Down Expand Up @@ -646,6 +652,8 @@ func parseDevLinkAllPortList(msgs [][]byte) ([]*DevlinkPort, error) {

// DevLinkGetPortList provides a pointer to devlink ports and nil error,
// otherwise returns an error code.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) DevLinkGetAllPortList() ([]*DevlinkPort, error) {
f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
if err != nil {
Expand All @@ -658,19 +666,21 @@ func (h *Handle) DevLinkGetAllPortList() ([]*DevlinkPort, error) {
req := h.newNetlinkRequest(int(f.ID),
unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP)
req.AddData(msg)
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
ports, err := parseDevLinkAllPortList(msgs)
if err != nil {
return nil, err
}
return ports, nil
return ports, executeErr
}

// DevLinkGetPortList provides a pointer to devlink ports and nil error,
// otherwise returns an error code.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func DevLinkGetAllPortList() ([]*DevlinkPort, error) {
return pkgHandle.DevLinkGetAllPortList()
}
Expand Down Expand Up @@ -738,15 +748,18 @@ func (h *Handle) DevlinkGetDeviceResources(bus string, device string) (*DevlinkR

// DevlinkGetDeviceParams returns parameters for devlink device
// Equivalent to: `devlink dev param show <bus>/<device>`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) DevlinkGetDeviceParams(bus string, device string) ([]*DevlinkParam, error) {
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PARAM_GET, bus, device)
if err != nil {
return nil, err
}
req.Flags |= unix.NLM_F_DUMP
respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
respmsg, executeErr := req.Execute(unix.NETLINK_GENERIC, 0)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
var params []*DevlinkParam
for _, m := range respmsg {
Expand All @@ -761,11 +774,14 @@ func (h *Handle) DevlinkGetDeviceParams(bus string, device string) ([]*DevlinkPa
params = append(params, p)
}

return params, nil
return params, executeErr
}

// DevlinkGetDeviceParams returns parameters for devlink device
// Equivalent to: `devlink dev param show <bus>/<device>`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func DevlinkGetDeviceParams(bus string, device string) ([]*DevlinkParam, error) {
return pkgHandle.DevlinkGetDeviceParams(bus, device)
}
Expand Down
14 changes: 10 additions & 4 deletions filter_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,14 +405,20 @@ func (h *Handle) filterModify(filter Filter, proto, flags int) error {

// FilterList gets a list of filters in the system.
// Equivalent to: `tc filter show`.
//
// Generally returns nothing if link and parent are not specified.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func FilterList(link Link, parent uint32) ([]Filter, error) {
return pkgHandle.FilterList(link, parent)
}

// FilterList gets a list of filters in the system.
// Equivalent to: `tc filter show`.
//
// Generally returns nothing if link and parent are not specified.
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
req := h.newNetlinkRequest(unix.RTM_GETTFILTER, unix.NLM_F_DUMP)
msg := &nl.TcMsg{
Expand All @@ -426,9 +432,9 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
}
req.AddData(msg)

msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTFILTER)
if err != nil {
return nil, err
msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWTFILTER)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}

var res []Filter
Expand Down Expand Up @@ -516,7 +522,7 @@ func (h *Handle) FilterList(link Link, parent uint32) ([]Filter, error) {
}
}

return res, nil
return res, executeErr
}

func toTcGen(attrs *ActionAttrs, tcgen *nl.TcGen) {
Expand Down
Loading

0 comments on commit 084abd9

Please sign in to comment.