diff --git a/daemon/libnetwork/default_gateway.go b/daemon/libnetwork/default_gateway.go index 6f49c34d3f..e746338626 100644 --- a/daemon/libnetwork/default_gateway.go +++ b/daemon/libnetwork/default_gateway.go @@ -190,26 +190,19 @@ func (sb *Sandbox) getGatewayEndpoint() (ep4, ep6 *Endpoint) { continue } gw4, gw6 := ep.hasGatewayOrDefaultRoute() - if gw4 && gw6 { - // The first dual-stack endpoint is the gateway, no need to search further. - // - // FIXME(robmry) - this means a dual-stack gateway is preferred over single-stack - // gateways with higher gateway-priorities. A dual-stack network should probably - // be preferred over two single-stack networks, if they all have equal priorities. - // It'd probably also be better to use a dual-stack endpoint as the gateway for - // a single address family, if there's a higher-priority single-stack gateway for - // the other address family. (But, priority is currently a Sandbox property, not - // an Endpoint property. So, this function doesn't have access to priorities.) - return ep, ep - } if gw4 && ep4 == nil { - // Found the best IPv4-only gateway, keep searching for an IPv6 or dual-stack gateway. + // Endpoints are already sorted according to Endpoint.Less(). The + // first endpoint with IPv4 connectivity is the IPv4 gateway. ep4 = ep } if gw6 && ep6 == nil { - // Found the best IPv6-only gateway, keep searching for an IPv4 or dual-stack gateway. + // The first endpoint with IPv6 connectivity is the IPv6 gateway. ep6 = ep } + if ep4 != nil && ep6 != nil { + // Found both; we're done. + break + } } return ep4, ep6 } diff --git a/daemon/libnetwork/sandbox_unix_test.go b/daemon/libnetwork/sandbox_unix_test.go index 5ad7923234..4b8fa05d6a 100644 --- a/daemon/libnetwork/sandbox_unix_test.go +++ b/daemon/libnetwork/sandbox_unix_test.go @@ -206,6 +206,51 @@ func TestSandboxAddMultiPrio(t *testing.T) { } } +func TestGatewayEndpointRespectsPriorityPerAddressFamily(t *testing.T) { + defer netnsutils.SetupTestOSContext(t)() + ctx := t.Context() + + opts := [][]NetworkOption{ + { + NetworkOptionEnableIPv4(true), + NetworkOptionEnableIPv6(true), + NetworkOptionIpam(defaultipam.DriverName, "", + []*IpamConf{{PreferredPool: "172.30.0.0/24", Gateway: "172.30.0.1"}}, + []*IpamConf{{PreferredPool: "fe90::/64", Gateway: "fe90::1"}}, nil), + }, + { + NetworkOptionEnableIPv4(true), + NetworkOptionIpam(defaultipam.DriverName, "", + []*IpamConf{{PreferredPool: "172.31.0.0/24", Gateway: "172.31.0.1"}}, + nil, nil), + }, + } + + ctrlr, nws := getTestEnv(t, opts...) + + sbx, err := ctrlr.NewSandbox(ctx, "sandbox-prio-per-family") + assert.NilError(t, err) + + epDual, err := nws[0].CreateEndpoint(ctx, "ep-dual") + assert.NilError(t, err) + epV4, err := nws[1].CreateEndpoint(ctx, "ep-v4") + assert.NilError(t, err) + + err = epDual.Join(ctx, sbx) + assert.NilError(t, err) + err = epV4.Join(ctx, sbx, JoinOptionPriority(1000)) + assert.NilError(t, err) + + gwep4, gwep6 := sbx.getGatewayEndpoint() + assert.Assert(t, gwep4 != nil) + assert.Assert(t, gwep6 != nil) + assert.Check(t, is.Equal(gwep4.ID(), epV4.ID())) + assert.Check(t, is.Equal(gwep6.ID(), epDual.ID())) + + err = sbx.Delete(ctx) + assert.NilError(t, err) +} + func TestSandboxAddSamePrio(t *testing.T) { defer netnsutils.SetupTestOSContext(t)()