mirror of
https://github.com/rancher/plugins.git
synced 2025-09-12 16:42:19 +00:00
Add netlinksafe
A wrapper to add retry on for netlink when it receives a ErrDumpInterrupted Signed-off-by: Adrian Moisey <adrian@changeover.za.net>
This commit is contained in:
committed by
Casey Callendrello
parent
0464017aff
commit
5f88af10a0
321
pkg/netlinksafe/netlink.go
Normal file
321
pkg/netlinksafe/netlink.go
Normal file
@@ -0,0 +1,321 @@
|
||||
// Package netlinksafe wraps vishvandanda/netlink functions that may return EINTR.
|
||||
//
|
||||
// A Handle instantiated using [NewHandle] or [NewHandleAt] can be used in place
|
||||
// of a netlink.Handle, it's a wrapper that replaces methods that need to be
|
||||
// wrapped. Functions that use the package handle need to be called as "netlinksafe.X"
|
||||
// instead of "netlink.X".
|
||||
//
|
||||
// The wrapped functions currently return EINTR when NLM_F_DUMP_INTR flagged
|
||||
// in a netlink response, meaning something changed during the dump so results
|
||||
// may be incomplete or inconsistent.
|
||||
//
|
||||
// At present, the possibly incomplete/inconsistent results are not returned
|
||||
// by netlink functions along with the EINTR. So, it's not possible to do
|
||||
// anything but retry. After maxAttempts the EINTR will be returned to the
|
||||
// caller.
|
||||
package netlinksafe
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
// Arbitrary limit on max attempts at netlink calls if they are repeatedly interrupted.
|
||||
const maxAttempts = 5
|
||||
|
||||
type Handle struct {
|
||||
*netlink.Handle
|
||||
}
|
||||
|
||||
func NewHandle(nlFamilies ...int) (Handle, error) {
|
||||
nlh, err := netlink.NewHandle(nlFamilies...)
|
||||
if err != nil {
|
||||
return Handle{}, err
|
||||
}
|
||||
return Handle{nlh}, nil
|
||||
}
|
||||
|
||||
func NewHandleAt(ns netns.NsHandle, nlFamilies ...int) (Handle, error) {
|
||||
nlh, err := netlink.NewHandleAt(ns, nlFamilies...)
|
||||
if err != nil {
|
||||
return Handle{}, err
|
||||
}
|
||||
return Handle{nlh}, nil
|
||||
}
|
||||
|
||||
func (h Handle) Close() {
|
||||
if h.Handle != nil {
|
||||
h.Handle.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func retryOnIntr(f func() error) {
|
||||
for attempt := 0; attempt < maxAttempts; attempt++ {
|
||||
if err := f(); !errors.Is(err, netlink.ErrDumpInterrupted) {
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Printf("netlink call interrupted after %d attempts", maxAttempts)
|
||||
}
|
||||
|
||||
func discardErrDumpInterrupted(err error) error {
|
||||
if errors.Is(err, netlink.ErrDumpInterrupted) {
|
||||
// The netlink function has returned possibly-inconsistent data along with the
|
||||
// error. Discard the error and return the data. This restores the behaviour of
|
||||
// the netlink package prior to v1.2.1, in which NLM_F_DUMP_INTR was ignored in
|
||||
// the netlink response.
|
||||
log.Printf("discarding ErrDumpInterrupted: %+v", errors.WithStack(err))
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// AddrList calls netlink.AddrList, retrying if necessary.
|
||||
func AddrList(link netlink.Link, family int) ([]netlink.Addr, error) {
|
||||
var addrs []netlink.Addr
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
addrs, err = netlink.AddrList(link, family) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return addrs, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// LinkByName calls h.Handle.LinkByName, retrying if necessary. The netlink function
|
||||
// doesn't normally ask the kernel for a dump of links. But, on an old kernel, it
|
||||
// will do as a fallback and that dump may get inconsistent results.
|
||||
func (h Handle) LinkByName(name string) (netlink.Link, error) {
|
||||
var link netlink.Link
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
link, err = h.Handle.LinkByName(name) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return link, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// LinkByName calls netlink.LinkByName, retrying if necessary. The netlink
|
||||
// function doesn't normally ask the kernel for a dump of links. But, on an old
|
||||
// kernel, it will do as a fallback and that dump may get inconsistent results.
|
||||
func LinkByName(name string) (netlink.Link, error) {
|
||||
var link netlink.Link
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
link, err = netlink.LinkByName(name) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return link, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// LinkList calls h.Handle.LinkList, retrying if necessary.
|
||||
func (h Handle) LinkList() ([]netlink.Link, error) {
|
||||
var links []netlink.Link
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
links, err = h.Handle.LinkList() //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return links, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// LinkList calls netlink.Handle.LinkList, retrying if necessary.
|
||||
func LinkList() ([]netlink.Link, error) {
|
||||
var links []netlink.Link
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
links, err = netlink.LinkList() //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return links, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// RouteList calls h.Handle.RouteList, retrying if necessary.
|
||||
func (h Handle) RouteList(link netlink.Link, family int) ([]netlink.Route, error) {
|
||||
var routes []netlink.Route
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
routes, err = h.Handle.RouteList(link, family) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return routes, err
|
||||
}
|
||||
|
||||
// RouteList calls netlink.RouteList, retrying if necessary.
|
||||
func RouteList(link netlink.Link, family int) ([]netlink.Route, error) {
|
||||
var route []netlink.Route
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
route, err = netlink.RouteList(link, family) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return route, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// BridgeVlanList calls netlink.BridgeVlanList, retrying if necessary.
|
||||
func BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
|
||||
var err error
|
||||
var info map[int32][]*nl.BridgeVlanInfo
|
||||
retryOnIntr(func() error {
|
||||
info, err = netlink.BridgeVlanList() //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return info, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// RouteListFiltered calls h.Handle.RouteListFiltered, retrying if necessary.
|
||||
func (h Handle) RouteListFiltered(family int, filter *netlink.Route, filterMask uint64) ([]netlink.Route, error) {
|
||||
var routes []netlink.Route
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
routes, err = h.Handle.RouteListFiltered(family, filter, filterMask) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return routes, err
|
||||
}
|
||||
|
||||
// RouteListFiltered calls netlink.RouteListFiltered, retrying if necessary.
|
||||
func RouteListFiltered(family int, filter *netlink.Route, filterMask uint64) ([]netlink.Route, error) {
|
||||
var route []netlink.Route
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
route, err = netlink.RouteListFiltered(family, filter, filterMask) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return route, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// QdiscList calls netlink.QdiscList, retrying if necessary.
|
||||
func QdiscList(link netlink.Link) ([]netlink.Qdisc, error) {
|
||||
var qdisc []netlink.Qdisc
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
qdisc, err = netlink.QdiscList(link) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return qdisc, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// QdiscList calls h.Handle.QdiscList, retrying if necessary.
|
||||
func (h *Handle) QdiscList(link netlink.Link) ([]netlink.Qdisc, error) {
|
||||
var qdisc []netlink.Qdisc
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
qdisc, err = h.Handle.QdiscList(link) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return qdisc, err
|
||||
}
|
||||
|
||||
// LinkGetProtinfo calls netlink.LinkGetProtinfo, retrying if necessary.
|
||||
func LinkGetProtinfo(link netlink.Link) (netlink.Protinfo, error) {
|
||||
var protinfo netlink.Protinfo
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
protinfo, err = netlink.LinkGetProtinfo(link) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return protinfo, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// LinkGetProtinfo calls h.Handle.LinkGetProtinfo, retrying if necessary.
|
||||
func (h *Handle) LinkGetProtinfo(link netlink.Link) (netlink.Protinfo, error) {
|
||||
var protinfo netlink.Protinfo
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
protinfo, err = h.Handle.LinkGetProtinfo(link) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return protinfo, err
|
||||
}
|
||||
|
||||
// RuleListFiltered calls netlink.RuleListFiltered, retrying if necessary.
|
||||
func RuleListFiltered(family int, filter *netlink.Rule, filterMask uint64) ([]netlink.Rule, error) {
|
||||
var rules []netlink.Rule
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
rules, err = netlink.RuleListFiltered(family, filter, filterMask) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return rules, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// RuleListFiltered calls h.Handle.RuleListFiltered, retrying if necessary.
|
||||
func (h *Handle) RuleListFiltered(family int, filter *netlink.Rule, filterMask uint64) ([]netlink.Rule, error) {
|
||||
var rules []netlink.Rule
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
rules, err = h.Handle.RuleListFiltered(family, filter, filterMask) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return rules, err
|
||||
}
|
||||
|
||||
// FilterList calls netlink.FilterList, retrying if necessary.
|
||||
func FilterList(link netlink.Link, parent uint32) ([]netlink.Filter, error) {
|
||||
var filters []netlink.Filter
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
filters, err = netlink.FilterList(link, parent) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return filters, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// FilterList calls h.Handle.FilterList, retrying if necessary.
|
||||
func (h *Handle) FilterList(link netlink.Link, parent uint32) ([]netlink.Filter, error) {
|
||||
var filters []netlink.Filter
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
filters, err = h.Handle.FilterList(link, parent) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return filters, err
|
||||
}
|
||||
|
||||
// RuleList calls netlink.RuleList, retrying if necessary.
|
||||
func RuleList(family int) ([]netlink.Rule, error) {
|
||||
var rules []netlink.Rule
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
rules, err = netlink.RuleList(family) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return rules, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// RuleList calls h.Handle.RuleList, retrying if necessary.
|
||||
func (h *Handle) RuleList(family int) ([]netlink.Rule, error) {
|
||||
var rules []netlink.Rule
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
rules, err = h.Handle.RuleList(family) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return rules, err
|
||||
}
|
||||
|
||||
// ConntrackDeleteFilters calls netlink.ConntrackDeleteFilters, retrying if necessary.
|
||||
func ConntrackDeleteFilters(table netlink.ConntrackTableType, family netlink.InetFamily, filters ...netlink.CustomConntrackFilter) (uint, error) {
|
||||
var deleted uint
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
deleted, err = netlink.ConntrackDeleteFilters(table, family, filters...) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return deleted, discardErrDumpInterrupted(err)
|
||||
}
|
||||
|
||||
// ConntrackDeleteFilters calls h.Handle.ConntrackDeleteFilters, retrying if necessary.
|
||||
func (h *Handle) ConntrackDeleteFilters(table netlink.ConntrackTableType, family netlink.InetFamily, filters ...netlink.CustomConntrackFilter) (uint, error) {
|
||||
var deleted uint
|
||||
var err error
|
||||
retryOnIntr(func() error {
|
||||
deleted, err = h.Handle.ConntrackDeleteFilters(table, family, filters...) //nolint:forbidigo
|
||||
return err
|
||||
})
|
||||
return deleted, err
|
||||
}
|
Reference in New Issue
Block a user