Skip to content

Commit 4f09af6

Browse files
committed
Allocate same port for IPv4/IPv6 for 'any interface' mappings.
The bridge driver now does its own port-mapping, rather than using the portmapper module (which ran as two completely separate instances, for IPv4 and IPv6). When asked for a mapping from any host address (0.0.0.0/0) with a range of host ports, the same port will be allocated for IPv4 and IPv6, or the mapping will fail with an error if that's not possible. The bridge driver now manages its own port mappings. So, remove linux-specific PortMapper code and make what's left Windows-only. Also, replace the portmapper.userlandProxy interface with StartProxy(). Signed-off-by: Rob Murray <[email protected]>
1 parent 0357788 commit 4f09af6

17 files changed

Lines changed: 782 additions & 615 deletions

libnetwork/drivers/bridge/bridge_linux.go

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"github.com/docker/docker/libnetwork/ns"
2222
"github.com/docker/docker/libnetwork/options"
2323
"github.com/docker/docker/libnetwork/portallocator"
24-
"github.com/docker/docker/libnetwork/portmapper"
2524
"github.com/docker/docker/libnetwork/scope"
2625
"github.com/docker/docker/libnetwork/types"
2726
"github.com/pkg/errors"
@@ -113,7 +112,7 @@ type bridgeEndpoint struct {
113112
macAddress net.HardwareAddr
114113
containerConfig *containerConfiguration
115114
extConnConfig *connectivityConfiguration
116-
portMapping []types.PortBinding // Operation port bindings
115+
portMapping []portBinding // Operational port bindings
117116
dbIndex uint64
118117
dbExists bool
119118
}
@@ -123,9 +122,7 @@ type bridgeNetwork struct {
123122
bridge *bridgeInterface // The bridge's L3 interface
124123
config *networkConfiguration
125124
endpoints map[string]*bridgeEndpoint // key: endpoint id
126-
portMapper *portmapper.PortMapper
127-
portMapperV6 *portmapper.PortMapper
128-
driver *driver // The network's driver
125+
driver *driver // The network's driver
129126
iptCleanFuncs iptablesCleanFuncs
130127
sync.Mutex
131128
}
@@ -355,6 +352,15 @@ func (n *bridgeNetwork) getNetworkBridgeName() string {
355352
return config.BridgeName
356353
}
357354

355+
func (n *bridgeNetwork) userlandProxyPath() string {
356+
n.Lock()
357+
defer n.Unlock()
358+
if n.driver == nil {
359+
return ""
360+
}
361+
return n.driver.userlandProxyPath()
362+
}
363+
358364
func (n *bridgeNetwork) getEndpoint(eid string) (*bridgeEndpoint, error) {
359365
if eid == "" {
360366
return nil, InvalidEndpointIDError(eid)
@@ -508,6 +514,16 @@ func (d *driver) getNetwork(id string) (*bridgeNetwork, error) {
508514
return nil, types.NotFoundErrorf("network not found: %s", id)
509515
}
510516

517+
func (d *driver) userlandProxyPath() string {
518+
d.Lock()
519+
defer d.Unlock()
520+
521+
if d.config.EnableUserlandProxy {
522+
return d.config.UserlandProxyPath
523+
}
524+
return ""
525+
}
526+
511527
func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error) {
512528
var (
513529
err error
@@ -714,13 +730,11 @@ func (d *driver) createNetwork(config *networkConfiguration) (err error) {
714730

715731
// Create and set network handler in driver
716732
network := &bridgeNetwork{
717-
id: config.ID,
718-
endpoints: make(map[string]*bridgeEndpoint),
719-
config: config,
720-
portMapper: portmapper.NewWithPortAllocator(d.portAllocator, d.config.UserlandProxyPath),
721-
portMapperV6: portmapper.NewWithPortAllocator(d.portAllocator, d.config.UserlandProxyPath),
722-
bridge: bridgeIface,
723-
driver: d,
733+
id: config.ID,
734+
endpoints: make(map[string]*bridgeEndpoint),
735+
config: config,
736+
bridge: bridgeIface,
737+
driver: d,
724738
}
725739

726740
d.Lock()
@@ -1221,7 +1235,7 @@ func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, erro
12211235
// Return a copy of the operational data
12221236
pmc := make([]types.PortBinding, 0, len(ep.portMapping))
12231237
for _, pm := range ep.portMapping {
1224-
pmc = append(pmc, pm.GetCopy())
1238+
pmc = append(pmc, pm.PortBinding.GetCopy())
12251239
}
12261240
m[netlabel.PortMap] = pmc
12271241
}
@@ -1321,9 +1335,16 @@ func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string
13211335
}
13221336

13231337
// Program any required port mapping and store them in the endpoint
1324-
endpoint.portMapping, err = network.allocatePorts(endpoint, network.config.DefaultBindingIP, d.config.EnableUserlandProxy)
1325-
if err != nil {
1326-
return err
1338+
if endpoint.extConnConfig != nil && endpoint.extConnConfig.PortBindings != nil {
1339+
endpoint.portMapping, err = network.addPortMappings(
1340+
endpoint.addr,
1341+
endpoint.addrv6,
1342+
endpoint.extConnConfig.PortBindings,
1343+
network.config.DefaultBindingIP,
1344+
)
1345+
if err != nil {
1346+
return err
1347+
}
13271348
}
13281349

13291350
defer func() {

libnetwork/drivers/bridge/bridge_linux_test.go

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -59,22 +59,26 @@ func TestEndpointMarshalling(t *testing.T) {
5959
},
6060
},
6161
},
62-
portMapping: []types.PortBinding{
62+
portMapping: []portBinding{
6363
{
64-
Proto: 17,
65-
IP: net.ParseIP("172.33.9.56"),
66-
Port: uint16(99),
67-
HostIP: net.ParseIP("10.10.100.2"),
68-
HostPort: uint16(9900),
69-
HostPortEnd: uint16(10000),
64+
PortBinding: types.PortBinding{
65+
Proto: 17,
66+
IP: net.ParseIP("172.33.9.56"),
67+
Port: uint16(99),
68+
HostIP: net.ParseIP("10.10.100.2"),
69+
HostPort: uint16(9900),
70+
HostPortEnd: uint16(10000),
71+
},
7072
},
7173
{
72-
Proto: 6,
73-
IP: net.ParseIP("171.33.9.56"),
74-
Port: uint16(55),
75-
HostIP: net.ParseIP("10.11.100.2"),
76-
HostPort: uint16(5500),
77-
HostPortEnd: uint16(55000),
74+
PortBinding: types.PortBinding{
75+
Proto: 6,
76+
IP: net.ParseIP("171.33.9.56"),
77+
Port: uint16(55),
78+
HostIP: net.ParseIP("10.11.100.2"),
79+
HostPort: uint16(5500),
80+
HostPortEnd: uint16(55000),
81+
},
7882
},
7983
},
8084
}
@@ -101,9 +105,9 @@ func TestEndpointMarshalling(t *testing.T) {
101105
// a different port cannot be selected on live-restore if the original is
102106
// already in-use). So, fix up portMapping in the original before running
103107
// the comparison.
104-
epms := make([]types.PortBinding, len(e.portMapping))
105-
for i, pb := range e.portMapping {
106-
epms[i] = pb
108+
epms := make([]portBinding, len(e.portMapping))
109+
for i, p := range e.portMapping {
110+
epms[i] = p
107111
epms[i].HostPortEnd = epms[i].HostPort
108112
}
109113
if !compareBindings(epms, ee.portMapping) {
@@ -197,12 +201,12 @@ func comparePortBinding(p *types.PortBinding, o *types.PortBinding) bool {
197201
return true
198202
}
199203

200-
func compareBindings(a, b []types.PortBinding) bool {
204+
func compareBindings(a, b []portBinding) bool {
201205
if len(a) != len(b) {
202206
return false
203207
}
204208
for i := 0; i < len(a); i++ {
205-
if !comparePortBinding(&a[i], &b[i]) {
209+
if !comparePortBinding(&a[i].PortBinding, &b[i].PortBinding) {
206210
return false
207211
}
208212
}
@@ -732,7 +736,7 @@ func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) {
732736
t.Fatal("Incomplete data for port mapping in endpoint operational data")
733737
}
734738
for i, pb := range ep.portMapping {
735-
if !comparePortBinding(&pb, &pm[i]) {
739+
if !comparePortBinding(&pb.PortBinding, &pm[i]) {
736740
t.Fatal("Unexpected data for port mapping in endpoint operational data")
737741
}
738742
}

libnetwork/drivers/bridge/bridge_store.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -374,17 +374,30 @@ func (ep *bridgeEndpoint) CopyTo(o datastore.KVObject) error {
374374
return nil
375375
}
376376

377+
// restorePortAllocations is used during live-restore. It re-creates iptables
378+
// forwarding/NAT rules, and restarts docker-proxy, as needed.
379+
//
380+
// TODO(robmry) - if any previously-mapped host ports are no longer available, all
381+
// iptables forwarding/NAT rules get removed and there will be no docker-proxy
382+
// processes. So, the container will be left running, but inaccessible.
377383
func (n *bridgeNetwork) restorePortAllocations(ep *bridgeEndpoint) {
378384
if ep.extConnConfig == nil ||
379385
ep.extConnConfig.ExposedPorts == nil ||
380386
ep.extConnConfig.PortBindings == nil {
381387
return
382388
}
383-
tmp := ep.extConnConfig.PortBindings
384-
ep.extConnConfig.PortBindings = ep.portMapping
385-
_, err := n.allocatePorts(ep, n.config.DefaultBindingIP, n.driver.config.EnableUserlandProxy)
389+
390+
// ep.portMapping has HostPort=HostPortEnd, the host port allocated last
391+
// time around ... use that in place of ep.extConnConfig.PortBindings, which
392+
// may specify host port ranges.
393+
cfg := make([]types.PortBinding, len(ep.portMapping))
394+
for i, b := range ep.portMapping {
395+
cfg[i] = b.PortBinding
396+
}
397+
398+
var err error
399+
ep.portMapping, err = n.addPortMappings(ep.addr, ep.addrv6, cfg, n.config.DefaultBindingIP)
386400
if err != nil {
387401
log.G(context.TODO()).Warnf("Failed to reserve existing port mapping for endpoint %.7s:%v", ep.id, err)
388402
}
389-
ep.extConnConfig.PortBindings = tmp
390403
}

0 commit comments

Comments
 (0)