mirror of
https://github.com/moby/moby.git
synced 2026-08-07 16:41:50 +00:00
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 <rob.murray@docker.com>
108 lines
3.6 KiB
Go
108 lines
3.6 KiB
Go
//go:build linux
|
|
|
|
package iptables
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"syscall"
|
|
|
|
"github.com/containerd/log"
|
|
"github.com/docker/docker/internal/nlwrap"
|
|
"github.com/docker/docker/libnetwork/types"
|
|
"github.com/vishvananda/netlink"
|
|
)
|
|
|
|
// checkConntrackProgrammable checks if the handle supports the
|
|
// NETLINK_NETFILTER and the base modules are loaded.
|
|
func checkConntrackProgrammable(nlh nlwrap.Handle) error {
|
|
if !nlh.SupportsNetlinkFamily(syscall.NETLINK_NETFILTER) {
|
|
return errors.New("conntrack is not available")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DeleteConntrackEntries deletes all the conntrack connections on the host for the specified IP
|
|
// Returns the number of flows deleted for IPv4, IPv6 else error
|
|
func DeleteConntrackEntries(nlh nlwrap.Handle, ipv4List []net.IP, ipv6List []net.IP) error {
|
|
if err := checkConntrackProgrammable(nlh); err != nil {
|
|
return err
|
|
}
|
|
|
|
var totalIPv4FlowPurged uint
|
|
for _, ipAddress := range ipv4List {
|
|
flowPurged, err := purgeConntrackState(nlh, syscall.AF_INET, ipAddress)
|
|
if err != nil {
|
|
log.G(context.TODO()).Warnf("Failed to delete conntrack state for %s: %v", ipAddress, err)
|
|
continue
|
|
}
|
|
totalIPv4FlowPurged += flowPurged
|
|
}
|
|
|
|
var totalIPv6FlowPurged uint
|
|
for _, ipAddress := range ipv6List {
|
|
flowPurged, err := purgeConntrackState(nlh, syscall.AF_INET6, ipAddress)
|
|
if err != nil {
|
|
log.G(context.TODO()).Warnf("Failed to delete conntrack state for %s: %v", ipAddress, err)
|
|
continue
|
|
}
|
|
totalIPv6FlowPurged += flowPurged
|
|
}
|
|
|
|
if totalIPv4FlowPurged > 0 || totalIPv6FlowPurged > 0 {
|
|
log.G(context.TODO()).Debugf("DeleteConntrackEntries purged ipv4:%d, ipv6:%d", totalIPv4FlowPurged, totalIPv6FlowPurged)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func DeleteConntrackEntriesByPort(nlh nlwrap.Handle, proto types.Protocol, ports []uint16) error {
|
|
if err := checkConntrackProgrammable(nlh); err != nil {
|
|
return err
|
|
}
|
|
|
|
var totalIPv4FlowPurged uint
|
|
var totalIPv6FlowPurged uint
|
|
|
|
for _, port := range ports {
|
|
filter := &netlink.ConntrackFilter{}
|
|
if err := filter.AddProtocol(uint8(proto)); err != nil {
|
|
log.G(context.TODO()).Warnf("Failed to delete conntrack state for %s port %d: %v", proto.String(), port, err)
|
|
continue
|
|
}
|
|
if err := filter.AddPort(netlink.ConntrackOrigDstPort, port); err != nil {
|
|
log.G(context.TODO()).Warnf("Failed to delete conntrack state for %s port %d: %v", proto.String(), port, err)
|
|
continue
|
|
}
|
|
|
|
v4FlowPurged, err := nlh.ConntrackDeleteFilters(netlink.ConntrackTable, syscall.AF_INET, filter)
|
|
if err != nil {
|
|
log.G(context.TODO()).Warnf("Failed to delete conntrack state for IPv4 %s port %d: %v", proto.String(), port, err)
|
|
}
|
|
totalIPv4FlowPurged += v4FlowPurged
|
|
|
|
v6FlowPurged, err := nlh.ConntrackDeleteFilters(netlink.ConntrackTable, syscall.AF_INET6, filter)
|
|
if err != nil {
|
|
log.G(context.TODO()).Warnf("Failed to delete conntrack state for IPv6 %s port %d: %v", proto.String(), port, err)
|
|
}
|
|
totalIPv6FlowPurged += v6FlowPurged
|
|
}
|
|
|
|
if totalIPv4FlowPurged > 0 || totalIPv6FlowPurged > 0 {
|
|
log.G(context.TODO()).Debugf("DeleteConntrackEntriesByPort for %s ports purged ipv4:%d, ipv6:%d", proto.String(), totalIPv4FlowPurged, totalIPv6FlowPurged)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func purgeConntrackState(nlh nlwrap.Handle, family netlink.InetFamily, ipAddress net.IP) (uint, error) {
|
|
filter := &netlink.ConntrackFilter{}
|
|
// NOTE: doing the flush using the ipAddress is safe because today there cannot be multiple networks with the same subnet
|
|
// so it will not be possible to flush flows that are of other containers
|
|
if err := filter.AddIP(netlink.ConntrackNatAnyIP, ipAddress); err != nil {
|
|
return 0, err
|
|
}
|
|
return nlh.ConntrackDeleteFilters(netlink.ConntrackTable, family, filter)
|
|
}
|