Check that when a container has endpoints in an l3-ipvlan and
another network type (bridge), there's no longer any clash
betwen the ipvlan's connected default route and the bridge's
default gateway.
Signed-off-by: Rob Murray <rob.murray@docker.com>
| ... | ... |
@@ -18,6 +18,7 @@ import ( |
| 18 | 18 |
"github.com/docker/docker/integration/internal/container" |
| 19 | 19 |
"github.com/docker/docker/integration/internal/network" |
| 20 | 20 |
"github.com/docker/docker/internal/testutils/networking" |
| 21 |
+ "github.com/docker/docker/libnetwork/netlabel" |
|
| 21 | 22 |
"github.com/docker/docker/testutil" |
| 22 | 23 |
"github.com/docker/docker/testutil/daemon" |
| 23 | 24 |
"gotest.tools/v3/assert" |
| ... | ... |
@@ -387,9 +388,83 @@ func checkCtrRoutes(t *testing.T, ctx context.Context, apiClient client.APIClien |
| 387 | 387 |
return s == "" |
| 388 | 388 |
}) |
| 389 | 389 |
|
| 390 |
- assert.Equal(t, len(routes), expRoutes, "expected %d routes, got %d:\n%s", expRoutes, len(routes), strings.Join(routes, "\n")) |
|
| 390 |
+ assert.Check(t, is.Equal(len(routes), expRoutes), "expected %d routes, got %d:\n%s", expRoutes, len(routes), strings.Join(routes, "\n")) |
|
| 391 | 391 |
defFound := slices.ContainsFunc(routes, func(s string) bool {
|
| 392 | 392 |
return strings.Contains(s, expDefRoute) |
| 393 | 393 |
}) |
| 394 |
- assert.Assert(t, defFound, "default route %q not found:\n%s", expDefRoute, strings.Join(routes, "\n")) |
|
| 394 |
+ assert.Check(t, defFound, "default route %q not found:\n%s", expDefRoute, strings.Join(routes, "\n")) |
|
| 395 |
+} |
|
| 396 |
+ |
|
| 397 |
+// TestMixL3IPVlanAndBridge checks that a container can be connected to a layer-3 |
|
| 398 |
+// ipvlan network as well as a bridge ... the bridge network will set up a |
|
| 399 |
+// default gateway, if selected as the gateway endpoint. The ipvlan driver sets |
|
| 400 |
+// up a connected route to 0.0.0.0 or [::], a route via a specific interface with |
|
| 401 |
+// no next-hop address (because the next-hop address can't be ARP'd to determine |
|
| 402 |
+// the interface). These two types of route cannot be set up at the same time. |
|
| 403 |
+// So, the ipvlan's route must be treated like the default gateway and only get |
|
| 404 |
+// set up when the ipvlan is selected as the gateway endpoint. |
|
| 405 |
+// Regression test for https://github.com/moby/moby/issues/48576 |
|
| 406 |
+func TestMixL3IPVlanAndBridge(t *testing.T) {
|
|
| 407 |
+ skip.If(t, testEnv.DaemonInfo.OSType == "windows", "no ipvlan on Windows") |
|
| 408 |
+ skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.48"), "gw-priority requires API v1.48") |
|
| 409 |
+ skip.If(t, testEnv.IsRootless, "can't see the dummy parent interface from the rootless namespace") |
|
| 410 |
+ |
|
| 411 |
+ ctx := testutil.StartSpan(baseContext, t) |
|
| 412 |
+ d := daemon.New(t) |
|
| 413 |
+ d.StartWithBusybox(ctx, t) |
|
| 414 |
+ defer d.Stop(t) |
|
| 415 |
+ c := d.NewClientT(t) |
|
| 416 |
+ defer c.Close() |
|
| 417 |
+ |
|
| 418 |
+ const brNetName = "brnet" |
|
| 419 |
+ network.CreateNoError(ctx, t, c, brNetName, |
|
| 420 |
+ network.WithOption(netlabel.ContainerIfacePrefix, "bri"), |
|
| 421 |
+ network.WithIPv6(), |
|
| 422 |
+ network.WithIPAM("192.168.123.0/24", "192.168.123.1"),
|
|
| 423 |
+ network.WithIPAM("fd6f:36f8:3005::/64", "fd6f:36f8:3005::1"),
|
|
| 424 |
+ ) |
|
| 425 |
+ defer network.RemoveNoError(ctx, t, c, brNetName) |
|
| 426 |
+ |
|
| 427 |
+ // Create a dummy parent interface rather than letting the driver do it because, |
|
| 428 |
+ // when the driver creates its own, it becomes a '--internal' network and no |
|
| 429 |
+ // default route is configured. |
|
| 430 |
+ const parentIfName = "di-dummy0" |
|
| 431 |
+ CreateMasterDummy(ctx, t, parentIfName) |
|
| 432 |
+ defer DeleteInterface(ctx, t, parentIfName) |
|
| 433 |
+ |
|
| 434 |
+ const ipvNetName = "ipvnet" |
|
| 435 |
+ network.CreateNoError(ctx, t, c, ipvNetName, |
|
| 436 |
+ network.WithDriver("ipvlan"),
|
|
| 437 |
+ network.WithOption("ipvlan_mode", "l3"),
|
|
| 438 |
+ network.WithOption("parent", parentIfName),
|
|
| 439 |
+ network.WithIPv6(), |
|
| 440 |
+ network.WithIPAM("192.168.124.0/24", ""),
|
|
| 441 |
+ network.WithIPAM("fd7d:8755:51ba::/64", ""),
|
|
| 442 |
+ ) |
|
| 443 |
+ defer network.RemoveNoError(ctx, t, c, ipvNetName) |
|
| 444 |
+ |
|
| 445 |
+ // Create a container connected to both networks, bridge network acting as gateway. |
|
| 446 |
+ ctrId := container.Run(ctx, t, c, |
|
| 447 |
+ container.WithNetworkMode(brNetName), |
|
| 448 |
+ container.WithEndpointSettings(brNetName, |
|
| 449 |
+ &networktypes.EndpointSettings{GwPriority: 1},
|
|
| 450 |
+ ), |
|
| 451 |
+ container.WithEndpointSettings(ipvNetName, &networktypes.EndpointSettings{}),
|
|
| 452 |
+ ) |
|
| 453 |
+ defer container.Remove(ctx, t, c, ctrId, containertypes.RemoveOptions{Force: true})
|
|
| 454 |
+ // Expect three IPv4 routes: the default, plus one per network. |
|
| 455 |
+ checkCtrRoutes(t, ctx, c, ctrId, syscall.AF_INET, 3, "default via 192.168.123.1 dev bri") |
|
| 456 |
+ // Expect seven IPv6 routes: the default, plus UL, LL, and multicast routes per network. |
|
| 457 |
+ checkCtrRoutes(t, ctx, c, ctrId, syscall.AF_INET6, 7, "default via fd6f:36f8:3005::1 dev bri") |
|
| 458 |
+ |
|
| 459 |
+ // Disconnect the bridge network, expect the ipvlan's default route to be set up. |
|
| 460 |
+ c.NetworkDisconnect(ctx, brNetName, ctrId, false) |
|
| 461 |
+ checkCtrRoutes(t, ctx, c, ctrId, syscall.AF_INET, 2, "default dev eth") |
|
| 462 |
+ checkCtrRoutes(t, ctx, c, ctrId, syscall.AF_INET6, 4, "default dev eth") |
|
| 463 |
+ |
|
| 464 |
+ // Reconnect the bridge network, with gw-priority=1 so it gets the gateway back. |
|
| 465 |
+ // Expect the ipvlan's default route to be removed. |
|
| 466 |
+ c.NetworkConnect(ctx, brNetName, ctrId, &networktypes.EndpointSettings{GwPriority: 1})
|
|
| 467 |
+ checkCtrRoutes(t, ctx, c, ctrId, syscall.AF_INET, 3, "default via 192.168.123.1 dev bri") |
|
| 468 |
+ checkCtrRoutes(t, ctx, c, ctrId, syscall.AF_INET6, 7, "default via fd6f:36f8:3005::1 dev bri") |
|
| 395 | 469 |
} |