1+ // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
2+ //go:build go1.23
3+
14package bridge
25
36import (
69 "net"
710 "net/netip"
811 "os"
12+ "slices"
913 "strconv"
1014 "strings"
1115 "sync"
@@ -30,6 +34,7 @@ import (
3034 "github.com/docker/docker/libnetwork/options"
3135 "github.com/docker/docker/libnetwork/scope"
3236 "github.com/docker/docker/libnetwork/types"
37+ "github.com/docker/docker/pkg/stringid"
3338 "github.com/pkg/errors"
3439 "github.com/vishvananda/netlink"
3540 "github.com/vishvananda/netns"
@@ -1442,6 +1447,10 @@ func (d *driver) Join(ctx context.Context, nid, eid string, sboxKey string, jinf
14421447 if err != nil {
14431448 return err
14441449 }
1450+ endpoint .extConnConfig , err = parseConnectivityOptions (sbOpts )
1451+ if err != nil {
1452+ return err
1453+ }
14451454
14461455 iNames := jinfo .InterfaceName ()
14471456 containerVethPrefix := defaultContainerVethPrefix
@@ -1461,6 +1470,10 @@ func (d *driver) Join(ctx context.Context, nid, eid string, sboxKey string, jinf
14611470 }
14621471 }
14631472
1473+ if ! network .config .EnableICC {
1474+ return d .link (network , endpoint , true )
1475+ }
1476+
14641477 return nil
14651478}
14661479
@@ -1494,7 +1507,7 @@ type portBindingMode struct {
14941507 ipv6 bool
14951508}
14961509
1497- func (d * driver ) ProgramExternalConnectivity (ctx context.Context , nid , eid string , options map [ string ] interface {}, gw4Id , gw6Id string ) error {
1510+ func (d * driver ) ProgramExternalConnectivity (ctx context.Context , nid , eid string , gw4Id , gw6Id string ) ( retErr error ) {
14981511 ctx , span := otel .Tracer ("" ).Start (ctx , spanPrefix + ".ProgramExternalConnectivity" , trace .WithAttributes (
14991512 attribute .String ("nid" , nid ),
15001513 attribute .String ("eid" , eid ),
@@ -1521,11 +1534,6 @@ func (d *driver) ProgramExternalConnectivity(ctx context.Context, nid, eid strin
15211534 return endpointNotFoundError (eid )
15221535 }
15231536
1524- endpoint .extConnConfig , err = parseConnectivityOptions (options )
1525- if err != nil {
1526- return err
1527- }
1528-
15291537 var pbmReq portBindingMode
15301538 // Act as the IPv4 gateway if explicitly selected.
15311539 if gw4Id == eid {
@@ -1538,30 +1546,31 @@ func (d *driver) ProgramExternalConnectivity(ctx context.Context, nid, eid strin
15381546 pbmReq .ipv6 = true
15391547 }
15401548
1541- // Program any required port mapping and store them in the endpoint
1542- if endpoint .extConnConfig != nil && endpoint .extConnConfig .PortBindings != nil {
1543- endpoint .portMapping , err = network .addPortMappings (
1544- ctx ,
1545- endpoint ,
1546- endpoint .extConnConfig .PortBindings ,
1547- network .config .DefaultBindingIP ,
1548- pbmReq ,
1549- )
1550- if err != nil {
1551- return err
1552- }
1549+ // If no change is needed, return.
1550+ if endpoint .portBindingState == pbmReq {
1551+ return nil
15531552 }
15541553
1554+ // Remove port bindings that aren't needed due to a change in mode.
1555+ undoTrim , err := endpoint .trimPortBindings (ctx , network , pbmReq )
1556+ if err != nil {
1557+ return err
1558+ }
15551559 defer func () {
1556- if err != nil {
1557- if e := network .releasePorts (endpoint ); e != nil {
1558- log .G (ctx ).Errorf ("Failed to release ports allocated for the bridge endpoint %s on failure %v because of %v" ,
1559- eid , err , e )
1560- }
1561- endpoint .portMapping = nil
1560+ if retErr != nil && undoTrim != nil {
1561+ endpoint .portMapping = append (endpoint .portMapping , undoTrim ()... )
15621562 }
15631563 }()
15641564
1565+ // Set up new port bindings, and store them in the endpoint.
1566+ if (pbmReq .ipv4 || pbmReq .ipv6 ) && endpoint .extConnConfig != nil && endpoint .extConnConfig .PortBindings != nil {
1567+ newPMs , err := network .addPortMappings (ctx , endpoint , endpoint .extConnConfig .PortBindings , network .config .DefaultBindingIP , pbmReq )
1568+ if err != nil {
1569+ return err
1570+ }
1571+ endpoint .portMapping = append (endpoint .portMapping , newPMs ... )
1572+ }
1573+
15651574 // Remember the new port binding state.
15661575 endpoint .portBindingState = pbmReq
15671576
@@ -1573,50 +1582,62 @@ func (d *driver) ProgramExternalConnectivity(ctx context.Context, nid, eid strin
15731582 return fmt .Errorf ("failed to update bridge endpoint %.7s to store: %v" , endpoint .id , err )
15741583 }
15751584
1576- if ! network .config .EnableICC {
1577- return d .link (network , endpoint , true )
1578- }
1579-
15801585 return nil
15811586}
15821587
1583- func (d * driver ) RevokeExternalConnectivity (nid , eid string ) error {
1584- // Make sure this function isn't deleting iptables rules while handleFirewalldReloadNw
1585- // is restoring those same rules.
1586- d .configNetwork .Lock ()
1587- defer d .configNetwork .Unlock ()
1588-
1589- network , err := d .getNetwork (nid )
1590- if err != nil {
1591- return err
1592- }
1593-
1594- endpoint , err := network .getEndpoint (eid )
1595- if err != nil {
1596- return err
1588+ // trimPortBindings compares pbmReq with the current port bindings, and removes
1589+ // port bindings that are no longer required.
1590+ //
1591+ // ep.portMapping is updated when bindings are removed.
1592+ func (ep * bridgeEndpoint ) trimPortBindings (ctx context.Context , n * bridgeNetwork , pbmReq portBindingMode ) (func () []portBinding , error ) {
1593+ // If the endpoint is the gateway for IPv4 and IPv6, there's nothing to drop.
1594+ if pbmReq .ipv4 && pbmReq .ipv6 {
1595+ return nil , nil
15971596 }
15981597
1599- if endpoint == nil {
1600- return endpointNotFoundError (eid )
1598+ toDrop := make ([]portBinding , 0 , len (ep .portMapping ))
1599+ toKeep := slices .DeleteFunc (ep .portMapping , func (pb portBinding ) bool {
1600+ is4 := pb .HostIP .To4 () != nil
1601+ if (is4 && ! pbmReq .ipv4 ) || (! is4 && ! pbmReq .ipv6 ) {
1602+ toDrop = append (toDrop , pb )
1603+ return true
1604+ }
1605+ return false
1606+ })
1607+ if len (toDrop ) == 0 {
1608+ return nil , nil
16011609 }
16021610
1603- err = network .releasePorts (endpoint )
1604- if err != nil {
1605- log .G (context .TODO ()).Warn (err )
1611+ if err := releasePortBindings (toDrop , n .firewallerNetwork ); err != nil {
1612+ log .G (ctx ).WithFields (log.Fields {
1613+ "error" : err ,
1614+ "gw4" : pbmReq .ipv4 ,
1615+ "gw6" : pbmReq .ipv6 ,
1616+ "nid" : stringid .TruncateID (n .id ),
1617+ "eid" : stringid .TruncateID (ep .id ),
1618+ }).Error ("Failed to release port bindings" )
1619+ return nil , err
16061620 }
1621+ ep .portMapping = toKeep
16071622
1608- endpoint .portMapping = nil
1609-
1610- // Clean the connection tracker state of the host for the specific endpoint. This is a precautionary measure to
1611- // avoid new endpoints getting the same IP address to receive unexpected packets due to bad conntrack state leading
1612- // to bad NATing.
1613- clearConntrackEntries (d .nlh , endpoint )
1614-
1615- if err = d .storeUpdate (context .TODO (), endpoint ); err != nil {
1616- return fmt .Errorf ("failed to update bridge endpoint %.7s to store: %v" , endpoint .id , err )
1623+ undo := func () []portBinding {
1624+ pbReq := make ([]types.PortBinding , 0 , len (toDrop ))
1625+ for _ , pb := range toDrop {
1626+ pbReq = append (pbReq , pb .PortBinding )
1627+ }
1628+ pbs , err := n .addPortMappings (ctx , ep , pbReq , n .config .DefaultBindingIP , ep .portBindingState )
1629+ if err != nil {
1630+ log .G (ctx ).WithFields (log.Fields {
1631+ "error" : err ,
1632+ "nid" : stringid .TruncateID (n .id ),
1633+ "eid" : stringid .TruncateID (ep .id ),
1634+ }).Error ("Failed to restore port bindings following join failure" )
1635+ return nil
1636+ }
1637+ return pbs
16171638 }
16181639
1619- return nil
1640+ return undo , nil
16201641}
16211642
16221643// clearConntrackEntries flushes conntrack entries matching endpoint IP address
@@ -1677,8 +1698,8 @@ func (d *driver) handleFirewalldReloadNw(nid string) {
16771698 return
16781699 }
16791700
1680- // Make sure the network isn't being deleted, and ProgramExternalConnectivity/RevokeExternalConnectivity
1681- // aren 't modifying iptables rules, while restoring the rules.
1701+ // Make sure the network isn't being deleted, and ProgramExternalConnectivity
1702+ // isn 't modifying iptables rules, while restoring the rules.
16821703 d .configNetwork .Lock ()
16831704 defer d .configNetwork .Unlock ()
16841705
0 commit comments