-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add nlutil functions to retry on netlink EINTR
A recent change to the vishvananda/netlink package exposes NLM_F_DUMP_INTR in some netlink responses as an EINTR (with no data). Retry the requests when that happens, up to five times, before returning the error. The limit of five is arbitrary, on most systems a single retry will be rare but, there's no guarantee that a retry will succeed. So, on a very busy or misbehaving system the error may still be returned. In most cases, this will lead to failure of the operation being attempted (which may lead to daemon startup failure, network initialisation failure etc). Signed-off-by: Rob Murray <[email protected]>
- Loading branch information
Showing
25 changed files
with
263 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
// Package nlwrap 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 "nlwrap.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 nlwrap | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
|
||
"github.com/containerd/log" | ||
"github.com/vishvananda/netlink" | ||
"github.com/vishvananda/netns" | ||
"golang.org/x/sys/unix" | ||
) | ||
|
||
// 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 += 1 { | ||
if err := f(); !errors.Is(err, unix.EINTR) { | ||
return | ||
} | ||
} | ||
log.G(context.TODO()).Infof("netlink call interrupted after %d attempts", maxAttempts) | ||
} | ||
|
||
// AddrList calls nlh.LinkList, retrying if necessary. | ||
func (nlh Handle) AddrList(link netlink.Link, family int) (addrs []netlink.Addr, err error) { | ||
retryOnIntr(func() error { | ||
addrs, err = nlh.Handle.AddrList(link, family) | ||
return err | ||
}) | ||
return addrs, err | ||
} | ||
|
||
// AddrList calls netlink.LinkList, retrying if necessary. | ||
func AddrList(link netlink.Link, family int) (addrs []netlink.Addr, err error) { | ||
retryOnIntr(func() error { | ||
addrs, err = netlink.AddrList(link, family) | ||
return err | ||
}) | ||
return addrs, err | ||
} | ||
|
||
// ConntrackDeleteFilters calls nlh.ConntrackDeleteFilters, retrying if necessary. | ||
func (nlh Handle) ConntrackDeleteFilters( | ||
table netlink.ConntrackTableType, | ||
family netlink.InetFamily, | ||
filters ...netlink.CustomConntrackFilter, | ||
) (matched uint, err error) { | ||
retryOnIntr(func() error { | ||
matched, err = nlh.Handle.ConntrackDeleteFilters(table, family, filters...) | ||
return err | ||
}) | ||
return matched, err | ||
} | ||
|
||
// ConntrackTableList calls netlink.ConntrackTableList, retrying if necessary. | ||
func ConntrackTableList( | ||
table netlink.ConntrackTableType, | ||
family netlink.InetFamily, | ||
) (flows []*netlink.ConntrackFlow, err error) { | ||
retryOnIntr(func() error { | ||
flows, err = netlink.ConntrackTableList(table, family) | ||
return err | ||
}) | ||
return flows, err | ||
} | ||
|
||
// LinkByName calls nlh.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 (nlh Handle) LinkByName(name string) (link netlink.Link, err error) { | ||
retryOnIntr(func() error { | ||
link, err = nlh.Handle.LinkByName(name) | ||
return err | ||
}) | ||
return link, 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) (link netlink.Link, err error) { | ||
retryOnIntr(func() error { | ||
link, err = netlink.LinkByName(name) | ||
return err | ||
}) | ||
return link, err | ||
} | ||
|
||
// LinkList calls nlh.LinkList, retrying if necessary. | ||
func (nlh Handle) LinkList() (links []netlink.Link, err error) { | ||
retryOnIntr(func() error { | ||
links, err = nlh.Handle.LinkList() | ||
return err | ||
}) | ||
return links, err | ||
} | ||
|
||
// LinkList calls netlink.LinkList, retrying if necessary. | ||
func LinkList() (links []netlink.Link, err error) { | ||
retryOnIntr(func() error { | ||
links, err = netlink.LinkList() | ||
return err | ||
}) | ||
return links, err | ||
} | ||
|
||
// RouteList calls nlh.RouteList, retrying if necessary. | ||
func (nlh Handle) RouteList(link netlink.Link, family int) (routes []netlink.Route, err error) { | ||
retryOnIntr(func() error { | ||
routes, err = nlh.Handle.RouteList(link, family) | ||
return err | ||
}) | ||
return routes, err | ||
} | ||
|
||
func (nlh Handle) XfrmPolicyList(family int) (policies []netlink.XfrmPolicy, err error) { | ||
retryOnIntr(func() error { | ||
policies, err = nlh.Handle.XfrmPolicyList(family) | ||
return err | ||
}) | ||
return policies, err | ||
} | ||
|
||
func (nlh Handle) XfrmStateList(family int) (states []netlink.XfrmState, err error) { | ||
retryOnIntr(func() error { | ||
states, err = nlh.Handle.XfrmStateList(family) | ||
return err | ||
}) | ||
return states, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.