diff --git a/libnetwork/driverapi/driverapi.go b/libnetwork/driverapi/driverapi.go index 87303cc963..185d81c76a 100644 --- a/libnetwork/driverapi/driverapi.go +++ b/libnetwork/driverapi/driverapi.go @@ -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. diff --git a/libnetwork/drivers/bridge/bridge_linux.go b/libnetwork/drivers/bridge/bridge_linux.go index 9d965c475d..fe2b86339e 100644 --- a/libnetwork/drivers/bridge/bridge_linux.go +++ b/libnetwork/drivers/bridge/bridge_linux.go @@ -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() diff --git a/libnetwork/drivers/bridge/bridge_linux_test.go b/libnetwork/drivers/bridge/bridge_linux_test.go index a4b4ce6390..239786b540 100644 --- a/libnetwork/drivers/bridge/bridge_linux_test.go +++ b/libnetwork/drivers/bridge/bridge_linux_test.go @@ -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) } diff --git a/libnetwork/drivers/bridge/bridge_store.go b/libnetwork/drivers/bridge/bridge_store.go index 8d4fc54c33..a73e93006e 100644 --- a/libnetwork/drivers/bridge/bridge_store.go +++ b/libnetwork/drivers/bridge/bridge_store.go @@ -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 } diff --git a/libnetwork/drivers/bridge/port_mapping_linux_test.go b/libnetwork/drivers/bridge/port_mapping_linux_test.go index ef347a31d2..898774b819 100644 --- a/libnetwork/drivers/bridge/port_mapping_linux_test.go +++ b/libnetwork/drivers/bridge/port_mapping_linux_test.go @@ -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) } diff --git a/libnetwork/drivers/remote/driver.go b/libnetwork/drivers/remote/driver.go index 75cb792433..40c3d240f2 100644 --- a/libnetwork/drivers/remote/driver.go +++ b/libnetwork/drivers/remote/driver.go @@ -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 { diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index 7e2c4e02f7..02a77e366d 100644 --- a/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -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") } }