Skip to content

Commit 1e1711d

Browse files
authored
Merge pull request moby#50283 from robmry/gatewayness_visible_to_libnet_drivers
Tell libnet's drivers which endpoints have been selected as gateways
2 parents c1e6edb + a881e9e commit 1e1711d

File tree

8 files changed

+169
-67
lines changed

8 files changed

+169
-67
lines changed

libnetwork/driverapi/driverapi.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ type Driver interface {
8383
type ExtConner interface {
8484
// ProgramExternalConnectivity invokes the driver method which does the necessary
8585
// programming to allow the external connectivity dictated by the passed options
86-
ProgramExternalConnectivity(ctx context.Context, nid, eid string, options map[string]interface{}) error
86+
ProgramExternalConnectivity(ctx context.Context, nid, eid string, options map[string]interface{}, gw4Id, gw6Id string) error
8787

8888
// RevokeExternalConnectivity asks the driver to remove any external connectivity
8989
// programming that was done so far

libnetwork/drivers/bridge/bridge_linux.go

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -125,17 +125,18 @@ type connectivityConfiguration struct {
125125
}
126126

127127
type bridgeEndpoint struct {
128-
id string
129-
nid string
130-
srcName string
131-
addr *net.IPNet
132-
addrv6 *net.IPNet
133-
macAddress net.HardwareAddr
134-
containerConfig *containerConfiguration
135-
extConnConfig *connectivityConfiguration
136-
portMapping []portBinding // Operational port bindings
137-
dbIndex uint64
138-
dbExists bool
128+
id string
129+
nid string
130+
srcName string
131+
addr *net.IPNet
132+
addrv6 *net.IPNet
133+
macAddress net.HardwareAddr
134+
containerConfig *containerConfiguration
135+
extConnConfig *connectivityConfiguration
136+
portMapping []portBinding // Operational port bindings
137+
portBindingState portBindingMode // Not persisted, even on live-restore port mappings are re-created.
138+
dbIndex uint64
139+
dbExists bool
139140
}
140141

141142
type bridgeNetwork struct {
@@ -1488,10 +1489,17 @@ func (d *driver) Leave(nid, eid string) error {
14881489
return nil
14891490
}
14901491

1491-
func (d *driver) ProgramExternalConnectivity(ctx context.Context, nid, eid string, options map[string]interface{}) error {
1492+
type portBindingMode struct {
1493+
ipv4 bool
1494+
ipv6 bool
1495+
}
1496+
1497+
func (d *driver) ProgramExternalConnectivity(ctx context.Context, nid, eid string, options map[string]interface{}, gw4Id, gw6Id string) error {
14921498
ctx, span := otel.Tracer("").Start(ctx, spanPrefix+".ProgramExternalConnectivity", trace.WithAttributes(
14931499
attribute.String("nid", nid),
1494-
attribute.String("eid", eid)))
1500+
attribute.String("eid", eid),
1501+
attribute.String("gw4", gw4Id),
1502+
attribute.String("gw6", gw6Id)))
14951503
defer span.End()
14961504

14971505
// Make sure the network isn't deleted, or in the middle of a firewalld reload, while
@@ -1518,15 +1526,26 @@ func (d *driver) ProgramExternalConnectivity(ctx context.Context, nid, eid strin
15181526
return err
15191527
}
15201528

1529+
var pbmReq portBindingMode
1530+
// Act as the IPv4 gateway if explicitly selected.
1531+
if gw4Id == eid {
1532+
pbmReq.ipv4 = true
1533+
}
1534+
// Act as the IPv6 gateway if explicitly selected - or if there's no IPv6
1535+
// gateway, but this endpoint is the IPv4 gateway (in which case, the userland
1536+
// proxy may proxy between host v6 and container v4 addresses.)
1537+
if gw6Id == eid || (gw6Id == "" && gw4Id == eid) {
1538+
pbmReq.ipv6 = true
1539+
}
1540+
15211541
// Program any required port mapping and store them in the endpoint
15221542
if endpoint.extConnConfig != nil && endpoint.extConnConfig.PortBindings != nil {
15231543
endpoint.portMapping, err = network.addPortMappings(
15241544
ctx,
1525-
endpoint.addr,
1526-
endpoint.addrv6,
1545+
endpoint,
15271546
endpoint.extConnConfig.PortBindings,
15281547
network.config.DefaultBindingIP,
1529-
endpoint.extConnConfig.NoProxy6To4,
1548+
pbmReq,
15301549
)
15311550
if err != nil {
15321551
return err
@@ -1543,6 +1562,9 @@ func (d *driver) ProgramExternalConnectivity(ctx context.Context, nid, eid strin
15431562
}
15441563
}()
15451564

1565+
// Remember the new port binding state.
1566+
endpoint.portBindingState = pbmReq
1567+
15461568
// Clean the connection tracker state of the host for the specific endpoint. This is needed because some flows may
15471569
// be bound to the local proxy, or to the host (for UDP packets), and won't be redirected to the new endpoints.
15481570
clearConntrackEntries(d.nlh, endpoint)
@@ -1836,13 +1858,5 @@ func parseConnectivityOptions(cOptions map[string]interface{}) (*connectivityCon
18361858
}
18371859
}
18381860

1839-
if opt, ok := cOptions[netlabel.NoProxy6To4]; ok {
1840-
if noProxy6To4, ok := opt.(bool); ok {
1841-
cc.NoProxy6To4 = noProxy6To4
1842-
} else {
1843-
return nil, types.InvalidParameterErrorf("invalid "+netlabel.NoProxy6To4+" in connectivity configuration: %v", opt)
1844-
}
1845-
}
1846-
18471861
return cc, nil
18481862
}

libnetwork/drivers/bridge/bridge_linux_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,7 @@ func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) {
843843
t.Fatalf("Failed to join the endpoint: %v", err)
844844
}
845845

846-
err = d.ProgramExternalConnectivity(context.Background(), "net1", "ep1", sbOptions)
846+
err = d.ProgramExternalConnectivity(context.Background(), "net1", "ep1", sbOptions, "ep1", "")
847847
if err != nil {
848848
t.Fatalf("Failed to program external connectivity: %v", err)
849849
}
@@ -947,7 +947,7 @@ func TestLinkContainers(t *testing.T) {
947947
t.Fatalf("Failed to join the endpoint: %v", err)
948948
}
949949

950-
err = d.ProgramExternalConnectivity(context.Background(), "net1", "ep1", sbOptions)
950+
err = d.ProgramExternalConnectivity(context.Background(), "net1", "ep1", sbOptions, "ep1", "")
951951
if err != nil {
952952
t.Fatalf("Failed to program external connectivity: %v", err)
953953
}
@@ -978,7 +978,7 @@ func TestLinkContainers(t *testing.T) {
978978
t.Fatal("Failed to link ep1 and ep2")
979979
}
980980

981-
err = d.ProgramExternalConnectivity(context.Background(), "net1", "ep2", sbOptions)
981+
err = d.ProgramExternalConnectivity(context.Background(), "net1", "ep2", sbOptions, "ep2", "ep2")
982982
if err != nil {
983983
t.Fatalf("Failed to program external connectivity: %v", err)
984984
}
@@ -1017,7 +1017,7 @@ func TestLinkContainers(t *testing.T) {
10171017
if err != nil {
10181018
t.Fatal(err)
10191019
}
1020-
err = d.ProgramExternalConnectivity(context.Background(), "net1", "ep2", sbOptions)
1020+
err = d.ProgramExternalConnectivity(context.Background(), "net1", "ep2", sbOptions, "ep2", "ep2")
10211021
assert.Check(t, err != nil, "Expected Join to fail given link conditions are not satisfied")
10221022
checkLink(false)
10231023
}

libnetwork/drivers/bridge/bridge_store.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,22 @@ func (n *bridgeNetwork) restorePortAllocations(ep *bridgeEndpoint) {
458458
cfg[i] = b.PortBinding
459459
}
460460

461+
// Calculate a portBindingMode - it need not be accurate but, if there were
462+
// IPv4/IPv6 bindings before, ensure they are re-created. (If, for example,
463+
// there are no IPv6 bindings, it doesn't matter whether that was because this
464+
// endpoint is not an IPv6 gateway and "pbmIPv6" was not set in the port
465+
// binding state, or there were just no IPv6 port bindings configured.)
466+
var pbm portBindingMode
467+
for _, b := range ep.portMapping {
468+
if b.HostIP.To4() == nil {
469+
pbm.ipv6 = true
470+
} else {
471+
pbm.ipv4 = true
472+
}
473+
}
474+
461475
var err error
462-
ep.portMapping, err = n.addPortMappings(context.TODO(), ep.addr, ep.addrv6, cfg, n.config.DefaultBindingIP, ep.extConnConfig.NoProxy6To4)
476+
ep.portMapping, err = n.addPortMappings(context.TODO(), ep, cfg, n.config.DefaultBindingIP, pbm)
463477
if err != nil {
464478
log.G(context.TODO()).Warnf("Failed to reserve existing port mapping for endpoint %.7s:%v", ep.id, err)
465479
}

libnetwork/drivers/bridge/port_mapping_linux.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ var startProxy = portmapper.StartProxy
8080
// each returned portBinding are set to the selected and reserved port.
8181
func (n *bridgeNetwork) addPortMappings(
8282
ctx context.Context,
83-
epAddrV4, epAddrV6 *net.IPNet,
83+
ep *bridgeEndpoint,
8484
cfg []types.PortBinding,
8585
defHostIP net.IP,
86-
noProxy6To4 bool,
86+
pbmReq portBindingMode,
8787
) (_ []portBinding, retErr error) {
8888
if len(defHostIP) == 0 {
8989
defHostIP = net.IPv4zero
@@ -93,11 +93,11 @@ func (n *bridgeNetwork) addPortMappings(
9393
}
9494

9595
var containerIPv4, containerIPv6 net.IP
96-
if epAddrV4 != nil {
97-
containerIPv4 = epAddrV4.IP
96+
if ep.addr != nil {
97+
containerIPv4 = ep.addr.IP
9898
}
99-
if epAddrV6 != nil {
100-
containerIPv6 = epAddrV6.IP
99+
if ep.addrv6 != nil {
100+
containerIPv6 = ep.addrv6.IP
101101
}
102102

103103
bindings := make([]portBinding, 0, len(cfg)*2)
@@ -117,6 +117,9 @@ func (n *bridgeNetwork) addPortMappings(
117117
pdc := n.getPortDriverClient()
118118
disableNAT4, disableNAT6 := n.getNATDisabled()
119119

120+
add4 := !ep.portBindingState.ipv4 && pbmReq.ipv4
121+
add6 := !ep.portBindingState.ipv6 && pbmReq.ipv6
122+
120123
// toBind accumulates port bindings that should be allocated the same host port
121124
// (if required by NAT config). If the host address is unspecified, and defHostIP
122125
// is 0.0.0.0, one iteration of the loop may generate bindings for v4 and v6. If
@@ -128,15 +131,17 @@ func (n *bridgeNetwork) addPortMappings(
128131
// bindings to collect, they're applied and toBind is reset.
129132
var toBind []portBindingReq
130133
for i, c := range sortedCfg {
131-
if bindingIPv4, ok := configurePortBindingIPv4(ctx, pdc, disableNAT4, c, containerIPv4, defHostIP); ok {
132-
toBind = append(toBind, bindingIPv4)
134+
if add4 {
135+
if bindingIPv4, ok := configurePortBindingIPv4(ctx, pdc, disableNAT4, c, containerIPv4, defHostIP); ok {
136+
toBind = append(toBind, bindingIPv4)
137+
}
133138
}
134139

135140
// If the container has no IPv6 address, allow proxying host IPv6 traffic to it
136141
// by setting up the binding with the IPv4 interface if the userland proxy is enabled
137142
// This change was added to keep backward compatibility
138143
containerIP := containerIPv6
139-
if containerIPv6 == nil && !noProxy6To4 {
144+
if containerIPv6 == nil && pbmReq.ipv4 && add6 {
140145
if proxyPath == "" {
141146
// There's no way to map from host-IPv6 to container-IPv4 with the userland proxy
142147
// disabled.
@@ -157,8 +162,10 @@ func (n *bridgeNetwork) addPortMappings(
157162
containerIP = containerIPv4
158163
}
159164
}
160-
if bindingIPv6, ok := configurePortBindingIPv6(ctx, pdc, disableNAT6, c, containerIP, defHostIP); ok {
161-
toBind = append(toBind, bindingIPv6)
165+
if add6 {
166+
if bindingIPv6, ok := configurePortBindingIPv6(ctx, pdc, disableNAT6, c, containerIP, defHostIP); ok {
167+
toBind = append(toBind, bindingIPv6)
168+
}
162169
}
163170

164171
if i < len(sortedCfg)-1 && needSamePort(c, sortedCfg[i+1]) {
@@ -754,6 +761,7 @@ func (n *bridgeNetwork) releasePorts(ep *bridgeEndpoint) error {
754761
n.Lock()
755762
pbs := ep.portMapping
756763
ep.portMapping = nil
764+
ep.portBindingState = portBindingMode{}
757765
n.Unlock()
758766

759767
return releasePortBindings(pbs, n.firewallerNetwork)

libnetwork/drivers/bridge/port_mapping_linux_test.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func TestPortMappingConfig(t *testing.T) {
7373
t.Fatalf("Failed to join the endpoint: %v", err)
7474
}
7575

76-
if err = d.ProgramExternalConnectivity(context.Background(), "dummy", "ep1", sbOptions); err != nil {
76+
if err = d.ProgramExternalConnectivity(context.Background(), "dummy", "ep1", sbOptions, "ep1", ""); err != nil {
7777
t.Fatalf("Failed to program external connectivity: %v", err)
7878
}
7979

@@ -159,7 +159,7 @@ func TestPortMappingV6Config(t *testing.T) {
159159
t.Fatalf("Failed to join the endpoint: %v", err)
160160
}
161161

162-
if err = d.ProgramExternalConnectivity(context.Background(), "dummy", "ep1", sbOptions); err != nil {
162+
if err = d.ProgramExternalConnectivity(context.Background(), "dummy", "ep1", sbOptions, "ep1", ""); err != nil {
163163
t.Fatalf("Failed to program external connectivity: %v", err)
164164
}
165165

@@ -876,7 +876,20 @@ func TestAddPortMappings(t *testing.T) {
876876
}
877877
})
878878

879-
pbs, err := n.addPortMappings(ctx, tc.epAddrV4, tc.epAddrV6, tc.cfg, tc.defHostIP, tc.noProxy6To4)
879+
ep := &bridgeEndpoint{
880+
id: "dummyep",
881+
nid: "dummynetwork",
882+
addr: tc.epAddrV4,
883+
addrv6: tc.epAddrV6,
884+
}
885+
var pbm portBindingMode
886+
if ep.addr != nil {
887+
pbm.ipv4 = true
888+
}
889+
if ep.addrv6 != nil || (!tc.noProxy6To4 && ep.addr != nil) {
890+
pbm.ipv6 = true
891+
}
892+
pbs, err := n.addPortMappings(ctx, ep, tc.cfg, tc.defHostIP, pbm)
880893
if tc.expErr != "" {
881894
assert.ErrorContains(t, err, tc.expErr)
882895
return

0 commit comments

Comments
 (0)