mirror of
https://github.com/moby/moby.git
synced 2026-08-03 22:51:03 +00:00
Table.Close() had a value receiver, so setting t.t = nil only modified the callee's copy of the handle. The caller's Table was left looking valid: t, _ := nftables.NewTable(...) t.Close() t.IsValid() // true Worse, Close() only dropped the nftables handle, and nftApply() opens a new one whenever it finds none. So Apply() and Reload() on a closed table silently reopened a handle and carried on updating the ruleset. At the root of it, a Table looked like a plain value but behaved like a reference, and nothing stopped it from being copied. So embed table in Table by value and hand out *Table instead. The Table/table split is still needed - table's fields have to be exported for text/template - but reference semantics are now visible at every call site, and they're enforced: because table contains a sync.Mutex, "go vet" reports both a copy of a Table and a method or function that takes one by value, so the shape of this bug is no longer expressible. Close() therefore can't invalidate the table by clearing a pointer, it has to record the state. Add a closed flag, and refuse to open a new nftables handle for a table that's been closed. Apply() checked neither for a closed table nor for a nil *Table, which would have panicked. It now reports an error, checking the closed state with applyLock held so that it can't race with Close(), and before the in-memory table is touched so that a rejected update isn't recorded as applied. The invalid table is now a nil *Table rather than a zero-value Table, which also removes the need for consumers to return an empty Table alongside an error. That made it obvious that the nftabler was leaking the table it had just created when it gave up on setting up IPv6, so close it. Signed-off-by: Cory Snider <csnider@mirantis.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
150 lines
4.1 KiB
Go
150 lines
4.1 KiB
Go
//go:build linux
|
|
|
|
package overlay
|
|
|
|
//go:generate protoc -I=. -I=../../../../vendor/ --gogofaster_out=import_path=github.com/moby/moby/v2/daemon/libnetwork/drivers/overlay:. overlay.proto
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/netip"
|
|
"sync"
|
|
|
|
"github.com/moby/moby/v2/daemon/libnetwork/discoverapi"
|
|
"github.com/moby/moby/v2/daemon/libnetwork/driverapi"
|
|
"github.com/moby/moby/v2/daemon/libnetwork/internal/nftables"
|
|
"github.com/moby/moby/v2/daemon/libnetwork/scope"
|
|
)
|
|
|
|
const (
|
|
NetworkType = "overlay"
|
|
vethPrefix = "veth"
|
|
vethLen = len(vethPrefix) + 7
|
|
vxlanEncap = 50
|
|
secureOption = "encrypted"
|
|
)
|
|
|
|
var (
|
|
_ discoverapi.Discover = (*driver)(nil)
|
|
_ driverapi.TableWatcher = (*driver)(nil)
|
|
)
|
|
|
|
type driver struct {
|
|
// Immutable; mu does not need to be held when accessing these fields.
|
|
initOS sync.Once
|
|
|
|
// encrMu guards secMap and keys,
|
|
// and synchronizes the application of encryption parameters
|
|
// to the kernel.
|
|
//
|
|
// This mutex is above mu in the lock hierarchy.
|
|
// Do not lock any locks aside from mu while holding encrMu.
|
|
encrMu sync.Mutex
|
|
secMap encrMap
|
|
keys []*key
|
|
|
|
overlayEncNftInitMu sync.Mutex
|
|
overlayEncNftTable *nftables.Table
|
|
|
|
// mu must be held when accessing the fields which follow it
|
|
// in the struct definition.
|
|
//
|
|
// This mutex is at the bottom of the lock hierarchy:
|
|
// do not lock any other locks while holding it.
|
|
mu sync.Mutex
|
|
bindAddress netip.Addr
|
|
advertiseAddress netip.Addr
|
|
networks networkTable
|
|
}
|
|
|
|
// Register registers a new instance of the overlay driver.
|
|
func Register(r driverapi.Registerer) error {
|
|
d := &driver{
|
|
networks: networkTable{},
|
|
secMap: encrMap{},
|
|
}
|
|
return r.RegisterDriver(NetworkType, d, driverapi.Capability{
|
|
DataScope: scope.Global,
|
|
ConnectivityScope: scope.Global,
|
|
})
|
|
}
|
|
|
|
func (d *driver) configure() error {
|
|
// Apply OS specific kernel configs if needed
|
|
d.initOS.Do(func() {
|
|
applyOStweaks()
|
|
if !nftables.Enabled() {
|
|
d.cleanupNft(context.TODO())
|
|
}
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *driver) Type() string {
|
|
return NetworkType
|
|
}
|
|
|
|
func (d *driver) IsBuiltIn() bool {
|
|
return true
|
|
}
|
|
|
|
// isIPv6Transport reports whether the outer Layer-3 transport for VXLAN datagrams is IPv6.
|
|
func (d *driver) isIPv6Transport() (bool, error) {
|
|
// Infer whether remote peers' virtual tunnel endpoints will be IPv4 or IPv6
|
|
// from the address family of our own advertise address. This is a
|
|
// reasonable inference to make as Linux VXLAN links do not support
|
|
// mixed-address-family remote peers.
|
|
if !d.advertiseAddress.IsValid() {
|
|
return false, errors.New("overlay: cannot determine address family of transport: the local data-plane address is not currently known")
|
|
}
|
|
return d.advertiseAddress.Is6(), nil
|
|
}
|
|
|
|
func (d *driver) nodeJoin(data discoverapi.NodeDiscoveryData) error {
|
|
if data.Self {
|
|
advAddr, _ := netip.ParseAddr(data.Address)
|
|
bindAddr, _ := netip.ParseAddr(data.BindAddress)
|
|
if !advAddr.IsValid() {
|
|
return errors.New("invalid discovery data")
|
|
}
|
|
d.mu.Lock()
|
|
d.advertiseAddress = advAddr
|
|
d.bindAddress = bindAddr
|
|
d.mu.Unlock()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster
|
|
func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data any) error {
|
|
switch dType {
|
|
case discoverapi.NodeDiscovery:
|
|
nodeData, ok := data.(discoverapi.NodeDiscoveryData)
|
|
if !ok {
|
|
return fmt.Errorf("invalid discovery data type: %T", data)
|
|
}
|
|
return d.nodeJoin(nodeData)
|
|
case discoverapi.EncryptionKeysConfig:
|
|
encrData, ok := data.(discoverapi.DriverEncryptionConfig)
|
|
if !ok {
|
|
return fmt.Errorf("invalid encryption key notification data type: %T", data)
|
|
}
|
|
return d.setKeys(context.TODO(), encrData)
|
|
case discoverapi.EncryptionKeysUpdate:
|
|
encrData, ok := data.(discoverapi.DriverEncryptionUpdate)
|
|
if !ok {
|
|
return fmt.Errorf("invalid encryption key notification data type: %T", data)
|
|
}
|
|
return d.updateKeys(context.TODO(), encrData)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster
|
|
func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data any) error {
|
|
return nil
|
|
}
|