mirror of
https://github.com/moby/moby.git
synced 2026-08-03 14:41:03 +00:00
Remove libnet's logic to track a driver's port mapping state
Change the semantics of ProgramExternalConnectivity, libnet can now call it whenever an endpoint is selected or deselected as a container's gateway endpoint. It's the driver's responsibility to remember what bindings it's set up, and to work out what needs to change. So, calling ProgramExternalConnectivity to tell the driver an endpoint is no longer a gateway has the same effect as RevokeExternalConnectivity - bindings need to be removed. That means libnet no longer needs to work out whether to Program/Revoke, it can just call ProgramExternalConnectivity. RevokeExternalConnectivity has been removed. Signed-off-by: Rob Murray <rob.murray@docker.com>
This commit is contained in:
@@ -81,13 +81,22 @@ type Driver interface {
|
||||
|
||||
// ExtConner is an optional interface for a network driver.
|
||||
type ExtConner interface {
|
||||
// ProgramExternalConnectivity invokes the driver method which does the necessary
|
||||
// programming to allow the external connectivity dictated by the passed options
|
||||
// ProgramExternalConnectivity tells the driver a container's options (including
|
||||
// port mapping options), so that it can configure the endpoint eid in network
|
||||
// nid.
|
||||
//
|
||||
// Ids of the endpoints currently acting as the container's default gateway for
|
||||
// IPv4 and IPv6 are passed as gw4Id/gw6Id. (Those endpoints may be managed by
|
||||
// different network drivers. If there is no gateway, the id will be the
|
||||
// empty string.)
|
||||
//
|
||||
// This method is called after Driver.Join, before Driver.Leave, and when eid
|
||||
// is or was equal to gw4Id or gw6Id, and there's a change.
|
||||
//
|
||||
// When an endpoint acting as a gateway is deleted, this function is called
|
||||
// with that endpoint's id in eid, and empty gateway ids (even if another
|
||||
// is present and will shortly be selected as the gateway).
|
||||
ProgramExternalConnectivity(ctx context.Context, nid, eid string, options map[string]interface{}, gw4Id, gw6Id string) error
|
||||
|
||||
// RevokeExternalConnectivity asks the driver to remove any external connectivity
|
||||
// programming that was done so far
|
||||
RevokeExternalConnectivity(nid, eid string) error
|
||||
}
|
||||
|
||||
// GwAllocChecker is an optional interface for a network driver.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.23
|
||||
|
||||
package bridge
|
||||
|
||||
import (
|
||||
@@ -6,6 +9,7 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -30,6 +34,7 @@ import (
|
||||
"github.com/docker/docker/libnetwork/options"
|
||||
"github.com/docker/docker/libnetwork/scope"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
@@ -1494,7 +1499,7 @@ type portBindingMode struct {
|
||||
ipv6 bool
|
||||
}
|
||||
|
||||
func (d *driver) ProgramExternalConnectivity(ctx context.Context, nid, eid string, options map[string]interface{}, gw4Id, gw6Id string) error {
|
||||
func (d *driver) ProgramExternalConnectivity(ctx context.Context, nid, eid string, options map[string]interface{}, gw4Id, gw6Id string) (retErr error) {
|
||||
ctx, span := otel.Tracer("").Start(ctx, spanPrefix+".ProgramExternalConnectivity", trace.WithAttributes(
|
||||
attribute.String("nid", nid),
|
||||
attribute.String("eid", eid),
|
||||
@@ -1521,9 +1526,13 @@ func (d *driver) ProgramExternalConnectivity(ctx context.Context, nid, eid strin
|
||||
return endpointNotFoundError(eid)
|
||||
}
|
||||
|
||||
endpoint.extConnConfig, err = parseConnectivityOptions(options)
|
||||
if err != nil {
|
||||
return err
|
||||
// Only parse options if given (options may be nil when revoking gateway-ness, but that
|
||||
// doesn't mean the endpoint's config has changed).
|
||||
if options != nil {
|
||||
endpoint.extConnConfig, err = parseConnectivityOptions(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var pbmReq portBindingMode
|
||||
@@ -1538,30 +1547,31 @@ func (d *driver) ProgramExternalConnectivity(ctx context.Context, nid, eid strin
|
||||
pbmReq.ipv6 = true
|
||||
}
|
||||
|
||||
// Program any required port mapping and store them in the endpoint
|
||||
if endpoint.extConnConfig != nil && endpoint.extConnConfig.PortBindings != nil {
|
||||
endpoint.portMapping, err = network.addPortMappings(
|
||||
ctx,
|
||||
endpoint,
|
||||
endpoint.extConnConfig.PortBindings,
|
||||
network.config.DefaultBindingIP,
|
||||
pbmReq,
|
||||
)
|
||||
// If no change is needed, return.
|
||||
if endpoint.portBindingState == pbmReq {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove port bindings that aren't needed due to a change in mode.
|
||||
undoTrim, err := endpoint.trimPortBindings(ctx, network, pbmReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil && undoTrim != nil {
|
||||
endpoint.portMapping = append(endpoint.portMapping, undoTrim()...)
|
||||
}
|
||||
}()
|
||||
|
||||
// Set up new port bindings, and store them in the endpoint.
|
||||
if (pbmReq.ipv4 || pbmReq.ipv6) && endpoint.extConnConfig != nil && endpoint.extConnConfig.PortBindings != nil {
|
||||
newPMs, err := network.addPortMappings(ctx, endpoint, endpoint.extConnConfig.PortBindings, network.config.DefaultBindingIP, pbmReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoint.portMapping = append(endpoint.portMapping, newPMs...)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if e := network.releasePorts(endpoint); e != nil {
|
||||
log.G(ctx).Errorf("Failed to release ports allocated for the bridge endpoint %s on failure %v because of %v",
|
||||
eid, err, e)
|
||||
}
|
||||
endpoint.portMapping = nil
|
||||
}
|
||||
}()
|
||||
|
||||
// Remember the new port binding state.
|
||||
endpoint.portBindingState = pbmReq
|
||||
|
||||
@@ -1580,43 +1590,59 @@ func (d *driver) ProgramExternalConnectivity(ctx context.Context, nid, eid strin
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
|
||||
// Make sure this function isn't deleting iptables rules while handleFirewalldReloadNw
|
||||
// is restoring those same rules.
|
||||
d.configNetwork.Lock()
|
||||
defer d.configNetwork.Unlock()
|
||||
|
||||
network, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return err
|
||||
// trimPortBindings compares pbmReq with the current port bindings, and removes
|
||||
// port bindings that are no longer required.
|
||||
//
|
||||
// ep.portMapping is updated when bindings are removed.
|
||||
func (ep *bridgeEndpoint) trimPortBindings(ctx context.Context, n *bridgeNetwork, pbmReq portBindingMode) (func() []portBinding, error) {
|
||||
// If the endpoint is the gateway for IPv4 and IPv6, there's nothing to drop.
|
||||
if pbmReq.ipv4 && pbmReq.ipv6 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
endpoint, err := network.getEndpoint(eid)
|
||||
if err != nil {
|
||||
return err
|
||||
toDrop := make([]portBinding, 0, len(ep.portMapping))
|
||||
toKeep := slices.DeleteFunc(ep.portMapping, func(pb portBinding) bool {
|
||||
is4 := pb.HostIP.To4() != nil
|
||||
if (is4 && !pbmReq.ipv4) || (!is4 && !pbmReq.ipv6) {
|
||||
toDrop = append(toDrop, pb)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
if len(toDrop) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if endpoint == nil {
|
||||
return endpointNotFoundError(eid)
|
||||
if err := releasePortBindings(toDrop, n.firewallerNetwork); err != nil {
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"error": err,
|
||||
"gw4": pbmReq.ipv4,
|
||||
"gw6": pbmReq.ipv6,
|
||||
"nid": stringid.TruncateID(n.id),
|
||||
"eid": stringid.TruncateID(ep.id),
|
||||
}).Error("Failed to release port bindings")
|
||||
return nil, err
|
||||
}
|
||||
ep.portMapping = toKeep
|
||||
|
||||
undo := func() []portBinding {
|
||||
pbReq := make([]types.PortBinding, 0, len(toDrop))
|
||||
for _, pb := range toDrop {
|
||||
pbReq = append(pbReq, pb.PortBinding)
|
||||
}
|
||||
pbs, err := n.addPortMappings(ctx, ep, pbReq, n.config.DefaultBindingIP, ep.portBindingState)
|
||||
if err != nil {
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"error": err,
|
||||
"nid": stringid.TruncateID(n.id),
|
||||
"eid": stringid.TruncateID(ep.id),
|
||||
}).Error("Failed to restore port bindings following join failure")
|
||||
return nil
|
||||
}
|
||||
return pbs
|
||||
}
|
||||
|
||||
err = network.releasePorts(endpoint)
|
||||
if err != nil {
|
||||
log.G(context.TODO()).Warn(err)
|
||||
}
|
||||
|
||||
endpoint.portMapping = nil
|
||||
|
||||
// Clean the connection tracker state of the host for the specific endpoint. This is a precautionary measure to
|
||||
// avoid new endpoints getting the same IP address to receive unexpected packets due to bad conntrack state leading
|
||||
// to bad NATing.
|
||||
clearConntrackEntries(d.nlh, endpoint)
|
||||
|
||||
if err = d.storeUpdate(context.TODO(), endpoint); err != nil {
|
||||
return fmt.Errorf("failed to update bridge endpoint %.7s to store: %v", endpoint.id, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return undo, nil
|
||||
}
|
||||
|
||||
// clearConntrackEntries flushes conntrack entries matching endpoint IP address
|
||||
@@ -1677,8 +1703,8 @@ func (d *driver) handleFirewalldReloadNw(nid string) {
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure the network isn't being deleted, and ProgramExternalConnectivity/RevokeExternalConnectivity
|
||||
// aren't modifying iptables rules, while restoring the rules.
|
||||
// Make sure the network isn't being deleted, and ProgramExternalConnectivity
|
||||
// isn't modifying iptables rules, while restoring the rules.
|
||||
d.configNetwork.Lock()
|
||||
defer d.configNetwork.Unlock()
|
||||
|
||||
|
||||
@@ -874,7 +874,7 @@ func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) {
|
||||
}
|
||||
}
|
||||
|
||||
err = d.RevokeExternalConnectivity("net1", "ep1")
|
||||
err = d.ProgramExternalConnectivity(context.Background(), "net1", "ep1", nil, "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -997,7 +997,7 @@ func TestLinkContainers(t *testing.T) {
|
||||
}
|
||||
checkLink(true)
|
||||
|
||||
err = d.RevokeExternalConnectivity("net1", "ep2")
|
||||
err = d.ProgramExternalConnectivity(context.Background(), "net1", "ep2", nil, "", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to revoke external connectivity: %v", err)
|
||||
}
|
||||
|
||||
@@ -477,4 +477,5 @@ func (n *bridgeNetwork) restorePortAllocations(ep *bridgeEndpoint) {
|
||||
if err != nil {
|
||||
log.G(context.TODO()).Warnf("Failed to reserve existing port mapping for endpoint %.7s:%v", ep.id, err)
|
||||
}
|
||||
ep.portBindingState = pbm
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func TestPortMappingConfig(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = d.RevokeExternalConnectivity("dummy", "ep1")
|
||||
err = d.ProgramExternalConnectivity(context.Background(), "dummy", "ep1", nil, "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -159,7 +159,7 @@ func TestPortMappingV6Config(t *testing.T) {
|
||||
t.Fatalf("Failed to join the endpoint: %v", err)
|
||||
}
|
||||
|
||||
if err = d.ProgramExternalConnectivity(context.Background(), "dummy", "ep1", sbOptions, "ep1", ""); err != nil {
|
||||
if err = d.ProgramExternalConnectivity(context.Background(), "dummy", "ep1", sbOptions, "ep1", "ep1"); err != nil {
|
||||
t.Fatalf("Failed to program external connectivity: %v", err)
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ func TestPortMappingV6Config(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = d.RevokeExternalConnectivity("dummy", "ep1")
|
||||
err = d.ProgramExternalConnectivity(context.Background(), "dummy", "ep1", nil, "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -384,7 +384,7 @@ func (d *driver) ProgramExternalConnectivity(_ context.Context, nid, eid string,
|
||||
return nil
|
||||
}
|
||||
if !isGw4 && !isGw6 {
|
||||
return nil
|
||||
return d.revokeExternalConnectivity(nid, eid)
|
||||
}
|
||||
ep.isGateway4, ep.isGateway6 = isGw4, isGw6
|
||||
if !isGw6 && gw6Id != "" {
|
||||
@@ -410,9 +410,8 @@ func (d *driver) ProgramExternalConnectivity(_ context.Context, nid, eid string,
|
||||
return err
|
||||
}
|
||||
|
||||
// RevokeExternalConnectivity method is invoked to remove any external connectivity programming related to the endpoint.
|
||||
func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
|
||||
d.nwEndpointsMu.Lock()
|
||||
// revokeExternalConnectivity method is invoked to remove any external connectivity programming related to the endpoint.
|
||||
func (d *driver) revokeExternalConnectivity(nid, eid string) error {
|
||||
ep, ok := d.nwEndpoints[eid]
|
||||
d.nwEndpointsMu.Unlock()
|
||||
if !ok {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/docker/docker/libnetwork/options"
|
||||
"github.com/docker/docker/libnetwork/scope"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"go.opentelemetry.io/otel"
|
||||
)
|
||||
|
||||
@@ -499,6 +500,10 @@ func epId(ep *Endpoint) string {
|
||||
return ep.id
|
||||
}
|
||||
|
||||
func epShortId(ep *Endpoint) string {
|
||||
return stringid.TruncateID(epId(ep))
|
||||
}
|
||||
|
||||
func (ep *Endpoint) sbJoin(ctx context.Context, sb *Sandbox, options ...EndpointOption) (retErr error) {
|
||||
ctx, span := otel.Tracer("").Start(ctx, "libnetwork.sbJoin")
|
||||
defer span.End()
|
||||
@@ -514,9 +519,9 @@ func (ep *Endpoint) sbJoin(ctx context.Context, sb *Sandbox, options ...Endpoint
|
||||
}
|
||||
|
||||
ctx = log.WithLogger(ctx, log.G(ctx).WithFields(log.Fields{
|
||||
"nid": n.ID(),
|
||||
"nid": stringid.TruncateID(n.ID()),
|
||||
"net": n.Name(),
|
||||
"eid": ep.ID(),
|
||||
"eid": stringid.TruncateID(ep.ID()),
|
||||
"ep": ep.Name(),
|
||||
}))
|
||||
|
||||
@@ -625,95 +630,50 @@ func (ep *Endpoint) sbJoin(ctx context.Context, sb *Sandbox, options ...Endpoint
|
||||
}
|
||||
|
||||
gwepAfter4, gwepAfter6 := sb.getGatewayEndpoint()
|
||||
if ep == gwepAfter4 || ep == gwepAfter6 {
|
||||
// When the driver programs external connectivity for a Sandbox to use an
|
||||
// IPv4-only Endpoint, it may choose to map ports from host IPv6 addresses (as
|
||||
// well as host IPv4) to the Endpoint's IPv4 address. But, it must not do that if
|
||||
// there is an IPv6-only Endpoint acting as the IPv6 gateway.
|
||||
//
|
||||
// So, for an IPv4-only Endpoint acting as a gateway, "noProxy6To4=true" must be
|
||||
// set if the Sandbox has a different Endpoint acting as IPv6 gateway. And, if ep
|
||||
// becoming the gateway changes noProxy6To4, the IPv4 gateway must be reset.
|
||||
//
|
||||
// This happens naturally in most cases. For example, if ep is dual-stack, sb had
|
||||
// an IPv4-only gateway and no IPv6 gateway - connectivity will be revoked from
|
||||
// the original IPv4-only gateway Endpoint and given to ep. So the old gateway
|
||||
// won't be mapping from the host-IPv6 address.
|
||||
//
|
||||
// But, if ep is IPv6 only, an existing IPv4 only gateway may be proxying 6To4.
|
||||
// So, its connectivity needs to be revoked and re-added with noProxy6To4 set.
|
||||
// Similarly, when an IPv6-only gateway is disconnected from the Sandbox,
|
||||
// gwepAfter6 will become nil and noProxy6To4 needs to be cleared in the
|
||||
// configuration of an IPv4-only gateway.
|
||||
//
|
||||
// Note that revoking/restoring external connectivity will result in the bridge
|
||||
// driver assigning new host ports for port mappings where the host port is not
|
||||
// specified.
|
||||
noProxy6To4Before := gwepBefore4 != nil && gwepBefore6 != nil && gwepBefore4 != gwepBefore6
|
||||
noProxy6To4After := gwepAfter4 != nil && gwepAfter6 != nil && gwepAfter4 != gwepAfter6
|
||||
restartGw4 := ep != gwepAfter4 && noProxy6To4Before != noProxy6To4After
|
||||
|
||||
// If ep is the new IPv4 gateway, remove the old IPv4 gateway.
|
||||
if gwepBefore4 != nil && (ep == gwepAfter4 || restartGw4) {
|
||||
role := "IPv4"
|
||||
if gwepAfter6 == gwepAfter4 {
|
||||
role = "dual-stack"
|
||||
}
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"noProxy6To4": noProxy6To4Before,
|
||||
}).Debug("Revoking external connectivity on endpoint")
|
||||
undoFunc, err := gwepBefore4.revokeExternalConnectivity()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if restartGw4 {
|
||||
// The IPv4 gateway hasn't changed, but its noProxy6To4 setting has. So,
|
||||
// restore it as the gateway with that new setting.
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"noProxy6To4": noProxy6To4After,
|
||||
"role": role,
|
||||
}).Debug("Programming IPv4 gateway endpoint")
|
||||
if err := undoFunc(ctx, sb.Labels(), epId(gwepAfter4), epId(gwepAfter6)); err != nil {
|
||||
log.G(ctx).WithError(err).Warn("Failed to restore IPv4 connectivity")
|
||||
}
|
||||
} else {
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
if err := undoFunc(ctx, sb.Labels(), epId(gwepBefore4), epId(gwepBefore6)); err != nil {
|
||||
log.G(ctx).WithError(err).Warn("Failed to restore connectivity during rollback")
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
// If ep is the new IPv6 gateway, there's an old IPv6 gateway to remove, and it
|
||||
// wasn't also the IPv4 gateway (removed already) - remove the old gateway.
|
||||
if ep == gwepAfter6 && gwepBefore6 != nil && gwepBefore6 != gwepBefore4 {
|
||||
log.G(ctx).Debug("Programming IPv6 gateway endpoint")
|
||||
undoFunc, err := gwepBefore6.revokeExternalConnectivity()
|
||||
if err != nil {
|
||||
return err
|
||||
log.G(ctx).Infof("sbJoin: gwep4 '%s'->'%s', gwep6 '%s'->'%s'",
|
||||
epShortId(gwepBefore4), epShortId(gwepAfter4),
|
||||
epShortId(gwepBefore6), epShortId(gwepAfter6))
|
||||
|
||||
// If ep has taken over as a gateway and there were gateways before, update them.
|
||||
if ep == gwepAfter4 || ep == gwepAfter6 {
|
||||
if gwepBefore4 != nil {
|
||||
if err := gwepBefore4.programExternalConnectivity(ctx, sb.Labels(), gwepAfter4, gwepAfter6); err != nil {
|
||||
return fmt.Errorf("updating external connectivity for IPv4 endpoint %s: %v", epShortId(gwepBefore4), err)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
if err := undoFunc(ctx, sb.Labels(), epId(gwepBefore4), epId(gwepBefore6)); err != nil {
|
||||
log.G(ctx).WithError(err).Warn("Failed to restore IPv6 connectivity during rollback")
|
||||
if err := gwepBefore4.programExternalConnectivity(ctx, sb.Labels(), gwepBefore4, gwepBefore6); err != nil {
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"error": err,
|
||||
"restoreEp": epShortId(gwepBefore4),
|
||||
}).Errorf("Failed to restore external IPv4 connectivity")
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
if !n.internal {
|
||||
if ecd, ok := d.(driverapi.ExtConner); ok {
|
||||
log.G(ctx).Debugf("Programming external connectivity on endpoint")
|
||||
if err := ecd.ProgramExternalConnectivity(ctx, n.ID(), ep.ID(), sb.Labels(), epId(gwepAfter4), epId(gwepAfter6)); err != nil {
|
||||
return errdefs.System(fmt.Errorf(
|
||||
"driver failed programming external connectivity on endpoint %s (%s): %v",
|
||||
ep.Name(), ep.ID(), err))
|
||||
}
|
||||
if gwepBefore6 != nil {
|
||||
if err := gwepBefore6.programExternalConnectivity(ctx, sb.Labels(), gwepAfter4, gwepAfter6); err != nil {
|
||||
return fmt.Errorf("updating external connectivity for IPv6 endpoint %s: %v", epShortId(gwepBefore6), err)
|
||||
}
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
if err := gwepBefore6.programExternalConnectivity(ctx, sb.Labels(), gwepBefore4, gwepBefore6); err != nil {
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"error": err,
|
||||
"restoreEp": epShortId(gwepBefore6),
|
||||
}).Errorf("Failed to restore external IPv6 connectivity")
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// Tell the new endpoint its port mappings, and whether it's a gateway.
|
||||
if err := ep.programExternalConnectivity(ctx, sb.Labels(), gwepAfter4, gwepAfter6); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !sb.needDefaultGW() {
|
||||
if e := sb.clearDefaultGW(); e != nil {
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
@@ -727,18 +687,23 @@ func (ep *Endpoint) sbJoin(ctx context.Context, sb *Sandbox, options ...Endpoint
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ep *Endpoint) programExternalConnectivity(ctx context.Context, labels map[string]any, gw4, gw6 string) error {
|
||||
log.G(ctx).Debugf("Programming external connectivity on endpoint %s (%s)", ep.Name(), ep.ID())
|
||||
extN, err := ep.getNetworkFromStore()
|
||||
func (ep *Endpoint) programExternalConnectivity(ctx context.Context, sbLabels map[string]any, gwep4, gwep6 *Endpoint) error {
|
||||
n, err := ep.getNetworkFromStore()
|
||||
if err != nil {
|
||||
return types.InternalErrorf("failed to get network from store for programming external connectivity: %v", err)
|
||||
}
|
||||
extD, err := extN.driver(true)
|
||||
d, err := n.driver(true)
|
||||
if err != nil {
|
||||
return types.InternalErrorf("failed to get driver for programming external connectivity: %v", err)
|
||||
}
|
||||
if ecd, ok := extD.(driverapi.ExtConner); ok {
|
||||
if err := ecd.ProgramExternalConnectivity(context.WithoutCancel(ctx), ep.network.ID(), ep.ID(), labels, gw4, gw6); err != nil {
|
||||
if ecd, ok := d.(driverapi.ExtConner); ok {
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"ep": ep.Name(),
|
||||
"epid": epShortId(ep),
|
||||
"gw4": epShortId(gwep4),
|
||||
"gw6": epShortId(gwep6),
|
||||
}).Debug("Programming external connectivity on endpoint")
|
||||
if err := ecd.ProgramExternalConnectivity(context.WithoutCancel(ctx), n.ID(), ep.ID(), sbLabels, epId(gwep4), epId(gwep6)); err != nil {
|
||||
return types.InternalErrorf("driver failed programming external connectivity on endpoint %s (%s): %v",
|
||||
ep.Name(), ep.ID(), err)
|
||||
}
|
||||
@@ -746,29 +711,6 @@ func (ep *Endpoint) programExternalConnectivity(ctx context.Context, labels map[
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ep *Endpoint) revokeExternalConnectivity() (func(context.Context, map[string]any, string, string) error, error) {
|
||||
extN, err := ep.getNetworkFromStore()
|
||||
if err != nil {
|
||||
return nil, types.InternalErrorf("failed to get network from store for revoking external connectivity: %v", err)
|
||||
}
|
||||
extD, err := extN.driver(true)
|
||||
if err != nil {
|
||||
return nil, types.InternalErrorf("failed to get driver for revoking external connectivity: %v", err)
|
||||
}
|
||||
ecd, ok := extD.(driverapi.ExtConner)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
if err = ecd.RevokeExternalConnectivity(ep.network.ID(), ep.ID()); err != nil {
|
||||
return nil, types.InternalErrorf(
|
||||
"driver failed revoking external connectivity on endpoint %s (%s): %v",
|
||||
ep.Name(), ep.ID(), err)
|
||||
}
|
||||
return func(ctx context.Context, labels map[string]any, gw4, gw6 string) error {
|
||||
return ecd.ProgramExternalConnectivity(context.WithoutCancel(ctx), ep.network.ID(), ep.ID(), labels, gw4, gw6)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ep *Endpoint) rename(name string) error {
|
||||
ep.mu.Lock()
|
||||
ep.name = name
|
||||
@@ -876,18 +818,10 @@ func (ep *Endpoint) sbLeave(ctx context.Context, sb *Sandbox, force bool) error
|
||||
ep.network = n
|
||||
ep.mu.Unlock()
|
||||
|
||||
// Current endpoint(s) providing external connectivity to the sandbox
|
||||
gwepBefore4, gwepBefore6 := sb.getGatewayEndpoint()
|
||||
moveExtConn4 := gwepBefore4 != nil && gwepBefore4.ID() == ep.ID()
|
||||
moveExtConn6 := gwepBefore6 != nil && gwepBefore6.ID() == ep.ID()
|
||||
|
||||
if d != nil {
|
||||
if moveExtConn4 || moveExtConn6 {
|
||||
if ecd, ok := d.(driverapi.ExtConner); ok {
|
||||
log.G(ctx).Debug("Revoking external connectivity on endpoint")
|
||||
if err := ecd.RevokeExternalConnectivity(n.id, ep.id); err != nil {
|
||||
log.G(ctx).WithError(err).Warn("driver failed revoking external connectivity on endpoint")
|
||||
}
|
||||
if ecd, ok := d.(driverapi.ExtConner); ok {
|
||||
if err := ecd.ProgramExternalConnectivity(context.WithoutCancel(ctx), n.ID(), ep.ID(), nil, "", ""); err != nil {
|
||||
log.G(ctx).WithError(err).Warn("driver failed revoking external connectivity on endpoint")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -958,45 +892,16 @@ func (ep *Endpoint) sbLeave(ctx context.Context, sb *Sandbox, force bool) error
|
||||
sb.resolver.SetForwardingPolicy(sb.hasExternalAccess())
|
||||
}
|
||||
|
||||
// New endpoint(s) providing external connectivity for the sandbox
|
||||
if moveExtConn4 || moveExtConn6 {
|
||||
gwepAfter4, gwepAfter6 := sb.getGatewayEndpoint()
|
||||
if gwepAfter4 != nil {
|
||||
// If the IPv4 gateway hasn't changed, and there was no IPv6 gateway before but
|
||||
// there is now, the driver for the IPv4 gateway must not proxy host-IPv6 to
|
||||
// container-IPv4 (6To4). Conversely, if there was an IPv6 gateway before but
|
||||
// there isn't one now, the driver must now be told it can proxy 6To4.
|
||||
//
|
||||
// Note that revoking/restoring external connectivity will result in the bridge
|
||||
// driver assigning new host ports for port mappings where the host port is not
|
||||
// specified.
|
||||
restartGw4 := gwepBefore4 == gwepAfter4 && ((gwepBefore6 == nil) != (gwepAfter6 == nil))
|
||||
noProxy6To4 := gwepAfter6 != nil && gwepAfter6 != gwepAfter4
|
||||
if restartGw4 {
|
||||
log.G(ctx).WithFields(log.Fields{"noProxy6To4": noProxy6To4}).Debug("Resetting IPv4 endpoint")
|
||||
if undoFunc, err := gwepBefore4.revokeExternalConnectivity(); err != nil {
|
||||
log.G(ctx).WithError(err).Error("Failed to restart IPv4 gateway")
|
||||
} else if err := undoFunc(ctx, sb.Labels(), epId(gwepAfter4), epId(gwepAfter6)); err != nil {
|
||||
log.G(ctx).WithError(err).Error("Failed to restore IPv4 gateway")
|
||||
}
|
||||
} else if moveExtConn4 {
|
||||
log.G(ctx).Debugf("Programming IPv6 gateway endpoint %s (%s)", ep.Name(), ep.ID())
|
||||
if err := gwepAfter4.programExternalConnectivity(ctx, sb.Labels(), epId(gwepAfter4), epId(gwepAfter6)); err != nil {
|
||||
role := "IPv4"
|
||||
if gwepAfter6 == gwepAfter4 {
|
||||
role = "dual-stack"
|
||||
}
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"role": role,
|
||||
"error": err,
|
||||
}).Error("Failed to set gateway")
|
||||
}
|
||||
}
|
||||
// Find new endpoint(s) to provide external connectivity for the sandbox.
|
||||
gwepAfter4, gwepAfter6 := sb.getGatewayEndpoint()
|
||||
if gwepAfter4 != nil {
|
||||
if err := gwepAfter4.programExternalConnectivity(ctx, sb.Labels(), gwepAfter4, gwepAfter6); err != nil {
|
||||
log.G(ctx).WithError(err).Error("Failed to set IPv4 gateway")
|
||||
}
|
||||
if gwepAfter6 != nil && moveExtConn6 && gwepAfter6 != gwepAfter4 {
|
||||
if err := gwepAfter6.programExternalConnectivity(ctx, sb.Labels(), epId(gwepAfter4), epId(gwepAfter6)); err != nil {
|
||||
log.G(ctx).WithError(err).Error("Failed to set IPv6 gateway")
|
||||
}
|
||||
}
|
||||
if gwepAfter6 != nil && gwepAfter6 != gwepAfter4 {
|
||||
if err := gwepAfter6.programExternalConnectivity(ctx, sb.Labels(), gwepAfter4, gwepAfter6); err != nil {
|
||||
log.G(ctx).WithError(err).Error("Failed to set IPv6 gateway")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user