github.com/khulnasoft-lab/khulnasoft@v26.0.1-0.20240328202558-330a6f959fe0+incompatible/integration/networking/bridge_test.go (about) 1 package networking 2 3 import ( 4 "context" 5 "fmt" 6 "regexp" 7 "testing" 8 "time" 9 10 "github.com/docker/docker/api/types" 11 containertypes "github.com/docker/docker/api/types/container" 12 "github.com/docker/docker/integration/internal/container" 13 "github.com/docker/docker/integration/internal/network" 14 "github.com/docker/docker/testutil" 15 "github.com/docker/docker/testutil/daemon" 16 "github.com/google/go-cmp/cmp/cmpopts" 17 "gotest.tools/v3/assert" 18 is "gotest.tools/v3/assert/cmp" 19 "gotest.tools/v3/skip" 20 ) 21 22 // TestBridgeICC tries to ping container ctr1 from container ctr2 using its hostname. Thus, this test checks: 23 // 1. DNS resolution ; 2. ARP/NDP ; 3. whether containers can communicate with each other ; 4. kernel-assigned SLAAC 24 // addresses. 25 func TestBridgeICC(t *testing.T) { 26 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 27 28 ctx := setupTest(t) 29 30 d := daemon.New(t) 31 d.StartWithBusybox(ctx, t, "-D", "--experimental", "--ip6tables") 32 defer d.Stop(t) 33 34 c := d.NewClientT(t) 35 defer c.Close() 36 37 testcases := []struct { 38 name string 39 bridgeOpts []func(*types.NetworkCreate) 40 ctr1MacAddress string 41 isIPv6 bool 42 isLinkLocal bool 43 pingHost string 44 }{ 45 { 46 name: "IPv4 non-internal network", 47 bridgeOpts: []func(*types.NetworkCreate){}, 48 }, 49 { 50 name: "IPv4 internal network", 51 bridgeOpts: []func(*types.NetworkCreate){ 52 network.WithInternal(), 53 }, 54 }, 55 { 56 name: "IPv6 ULA on non-internal network", 57 bridgeOpts: []func(*types.NetworkCreate){ 58 network.WithIPv6(), 59 network.WithIPAM("fdf1:a844:380c:b200::/64", "fdf1:a844:380c:b200::1"), 60 }, 61 isIPv6: true, 62 }, 63 { 64 name: "IPv6 ULA on internal network", 65 bridgeOpts: []func(*types.NetworkCreate){ 66 network.WithIPv6(), 67 network.WithInternal(), 68 network.WithIPAM("fdf1:a844:380c:b247::/64", "fdf1:a844:380c:b247::1"), 69 }, 70 isIPv6: true, 71 }, 72 { 73 name: "IPv6 link-local address on non-internal network", 74 bridgeOpts: []func(*types.NetworkCreate){ 75 network.WithIPv6(), 76 // There's no real way to specify an IPv6 network is only used with SLAAC link-local IPv6 addresses. 77 // What we can do instead, is to tell the IPAM driver to assign addresses from the link-local prefix. 78 // Each container will have two link-local addresses: 1. a SLAAC address assigned by the kernel ; 79 // 2. the one dynamically assigned by the IPAM driver. 80 network.WithIPAM("fe80::/64", "fe80::1"), 81 }, 82 isLinkLocal: true, 83 isIPv6: true, 84 }, 85 { 86 name: "IPv6 link-local address on internal network", 87 bridgeOpts: []func(*types.NetworkCreate){ 88 network.WithIPv6(), 89 network.WithInternal(), 90 // See the note above about link-local addresses. 91 network.WithIPAM("fe80::/64", "fe80::1"), 92 }, 93 isLinkLocal: true, 94 isIPv6: true, 95 }, 96 { 97 // As for 'LL non-internal', ping the container by name instead of by address 98 // - the busybox test containers only have one interface with a link local 99 // address, so the zone index is not required: 100 // RFC-4007, section 6: "[...] for nodes with only a single non-loopback 101 // interface (e.g., a single Ethernet interface), the common case, link-local 102 // addresses need not be qualified with a zone index." 103 // So, for this common case, LL addresses should be included in DNS config. 104 name: "IPv6 link-local address on non-internal network ping by name", 105 bridgeOpts: []func(*types.NetworkCreate){ 106 network.WithIPv6(), 107 network.WithIPAM("fe80::/64", "fe80::1"), 108 }, 109 isIPv6: true, 110 }, 111 { 112 name: "IPv6 nonstandard link-local subnet on non-internal network ping by name", 113 // No interfaces apart from the one on the bridge network with this non-default 114 // subnet will be on this link local subnet (it's not currently possible to 115 // configure two networks with the same LL subnet, although perhaps it should 116 // be). So, again, no zone index is required and the LL address should be 117 // included in DNS config. 118 bridgeOpts: []func(*types.NetworkCreate){ 119 network.WithIPv6(), 120 network.WithIPAM("fe80:1234::/64", "fe80:1234::1"), 121 }, 122 isIPv6: true, 123 }, 124 { 125 name: "IPv6 non-internal network with SLAAC LL address", 126 bridgeOpts: []func(*types.NetworkCreate){ 127 network.WithIPv6(), 128 network.WithIPAM("fdf1:a844:380c:b247::/64", "fdf1:a844:380c:b247::1"), 129 }, 130 // Link-local address is derived from the MAC address, so we need to 131 // specify one here to hardcode the SLAAC LL address below. 132 ctr1MacAddress: "02:42:ac:11:00:02", 133 pingHost: "fe80::42:acff:fe11:2%eth0", 134 isIPv6: true, 135 }, 136 { 137 name: "IPv6 internal network with SLAAC LL address", 138 bridgeOpts: []func(*types.NetworkCreate){ 139 network.WithIPv6(), 140 network.WithIPAM("fdf1:a844:380c:b247::/64", "fdf1:a844:380c:b247::1"), 141 }, 142 // Link-local address is derived from the MAC address, so we need to 143 // specify one here to hardcode the SLAAC LL address below. 144 ctr1MacAddress: "02:42:ac:11:00:02", 145 pingHost: "fe80::42:acff:fe11:2%eth0", 146 isIPv6: true, 147 }, 148 } 149 150 for tcID, tc := range testcases { 151 t.Run(tc.name, func(t *testing.T) { 152 ctx := testutil.StartSpan(ctx, t) 153 154 bridgeName := fmt.Sprintf("testnet-icc-%d", tcID) 155 network.CreateNoError(ctx, t, c, bridgeName, append(tc.bridgeOpts, 156 network.WithDriver("bridge"), 157 network.WithOption("com.docker.network.bridge.name", bridgeName))...) 158 defer network.RemoveNoError(ctx, t, c, bridgeName) 159 160 ctr1Name := fmt.Sprintf("ctr-icc-%d-1", tcID) 161 var ctr1Opts []func(config *container.TestContainerConfig) 162 if tc.ctr1MacAddress != "" { 163 ctr1Opts = append(ctr1Opts, container.WithMacAddress(bridgeName, tc.ctr1MacAddress)) 164 } 165 id1 := container.Run(ctx, t, c, append(ctr1Opts, 166 container.WithName(ctr1Name), 167 container.WithImage("busybox:latest"), 168 container.WithCmd("top"), 169 container.WithNetworkMode(bridgeName))...) 170 defer c.ContainerRemove(ctx, id1, containertypes.RemoveOptions{ 171 Force: true, 172 }) 173 174 pingHost := tc.pingHost 175 if pingHost == "" { 176 if tc.isLinkLocal { 177 inspect := container.Inspect(ctx, t, c, id1) 178 pingHost = inspect.NetworkSettings.Networks[bridgeName].GlobalIPv6Address + "%eth0" 179 } else { 180 pingHost = ctr1Name 181 } 182 } 183 184 ipv := "-4" 185 if tc.isIPv6 { 186 ipv = "-6" 187 } 188 189 pingCmd := []string{"ping", "-c1", "-W3", ipv, pingHost} 190 191 ctr2Name := fmt.Sprintf("ctr-icc-%d-2", tcID) 192 attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second) 193 defer cancel() 194 res := container.RunAttach(attachCtx, t, c, 195 container.WithName(ctr2Name), 196 container.WithImage("busybox:latest"), 197 container.WithCmd(pingCmd...), 198 container.WithNetworkMode(bridgeName)) 199 defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{ 200 Force: true, 201 }) 202 203 assert.Check(t, is.Equal(res.ExitCode, 0)) 204 assert.Check(t, is.Equal(res.Stderr.Len(), 0)) 205 assert.Check(t, is.Contains(res.Stdout.String(), "1 packets transmitted, 1 packets received")) 206 }) 207 } 208 } 209 210 // TestBridgeICCWindows tries to ping container ctr1 from container ctr2 using its hostname. 211 // Checks DNS resolution, and whether containers can communicate with each other. 212 // Regression test for https://github.com/moby/moby/issues/47370 213 func TestBridgeICCWindows(t *testing.T) { 214 skip.If(t, testEnv.DaemonInfo.OSType != "windows") 215 216 ctx := setupTest(t) 217 c := testEnv.APIClient() 218 219 testcases := []struct { 220 name string 221 netName string 222 }{ 223 { 224 name: "Default nat network", 225 netName: "nat", 226 }, 227 { 228 name: "User defined nat network", 229 netName: "mynat", 230 }, 231 } 232 233 for _, tc := range testcases { 234 t.Run(tc.name, func(t *testing.T) { 235 ctx := testutil.StartSpan(ctx, t) 236 237 if tc.netName != "nat" { 238 network.CreateNoError(ctx, t, c, tc.netName, 239 network.WithDriver("nat"), 240 ) 241 defer network.RemoveNoError(ctx, t, c, tc.netName) 242 } 243 244 const ctr1Name = "ctr1" 245 id1 := container.Run(ctx, t, c, 246 container.WithName(ctr1Name), 247 container.WithNetworkMode(tc.netName), 248 ) 249 defer c.ContainerRemove(ctx, id1, containertypes.RemoveOptions{Force: true}) 250 251 pingCmd := []string{"ping", "-n", "1", "-w", "3000", ctr1Name} 252 253 const ctr2Name = "ctr2" 254 attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second) 255 defer cancel() 256 res := container.RunAttach(attachCtx, t, c, 257 container.WithName(ctr2Name), 258 container.WithCmd(pingCmd...), 259 container.WithNetworkMode(tc.netName), 260 ) 261 defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{Force: true}) 262 263 assert.Check(t, is.Equal(res.ExitCode, 0)) 264 assert.Check(t, is.Equal(res.Stderr.Len(), 0)) 265 assert.Check(t, is.Contains(res.Stdout.String(), "Sent = 1, Received = 1, Lost = 0")) 266 }) 267 } 268 } 269 270 // TestBridgeINC makes sure two containers on two different bridge networks can't communicate with each other. 271 func TestBridgeINC(t *testing.T) { 272 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 273 274 ctx := setupTest(t) 275 276 d := daemon.New(t) 277 d.StartWithBusybox(ctx, t, "-D", "--experimental", "--ip6tables") 278 defer d.Stop(t) 279 280 c := d.NewClientT(t) 281 defer c.Close() 282 283 type bridgesOpts struct { 284 bridge1Opts []func(*types.NetworkCreate) 285 bridge2Opts []func(*types.NetworkCreate) 286 } 287 288 testcases := []struct { 289 name string 290 bridges bridgesOpts 291 ipv6 bool 292 stdout string 293 stderr string 294 }{ 295 { 296 name: "IPv4 non-internal network", 297 bridges: bridgesOpts{ 298 bridge1Opts: []func(*types.NetworkCreate){}, 299 bridge2Opts: []func(*types.NetworkCreate){}, 300 }, 301 stdout: "1 packets transmitted, 0 packets received", 302 }, 303 { 304 name: "IPv4 internal network", 305 bridges: bridgesOpts{ 306 bridge1Opts: []func(*types.NetworkCreate){network.WithInternal()}, 307 bridge2Opts: []func(*types.NetworkCreate){network.WithInternal()}, 308 }, 309 stderr: "sendto: Network is unreachable", 310 }, 311 { 312 name: "IPv6 ULA on non-internal network", 313 bridges: bridgesOpts{ 314 bridge1Opts: []func(*types.NetworkCreate){ 315 network.WithIPv6(), 316 network.WithIPAM("fdf1:a844:380c:b200::/64", "fdf1:a844:380c:b200::1"), 317 }, 318 bridge2Opts: []func(*types.NetworkCreate){ 319 network.WithIPv6(), 320 network.WithIPAM("fdf1:a844:380c:b247::/64", "fdf1:a844:380c:b247::1"), 321 }, 322 }, 323 ipv6: true, 324 stdout: "1 packets transmitted, 0 packets received", 325 }, 326 { 327 name: "IPv6 ULA on internal network", 328 bridges: bridgesOpts{ 329 bridge1Opts: []func(*types.NetworkCreate){ 330 network.WithIPv6(), 331 network.WithInternal(), 332 network.WithIPAM("fdf1:a844:390c:b200::/64", "fdf1:a844:390c:b200::1"), 333 }, 334 bridge2Opts: []func(*types.NetworkCreate){ 335 network.WithIPv6(), 336 network.WithInternal(), 337 network.WithIPAM("fdf1:a844:390c:b247::/64", "fdf1:a844:390c:b247::1"), 338 }, 339 }, 340 ipv6: true, 341 stderr: "sendto: Network is unreachable", 342 }, 343 } 344 345 for tcID, tc := range testcases { 346 t.Run(tc.name, func(t *testing.T) { 347 ctx := testutil.StartSpan(ctx, t) 348 349 bridge1 := fmt.Sprintf("testnet-inc-%d-1", tcID) 350 bridge2 := fmt.Sprintf("testnet-inc-%d-2", tcID) 351 352 network.CreateNoError(ctx, t, c, bridge1, append(tc.bridges.bridge1Opts, 353 network.WithDriver("bridge"), 354 network.WithOption("com.docker.network.bridge.name", bridge1))...) 355 defer network.RemoveNoError(ctx, t, c, bridge1) 356 network.CreateNoError(ctx, t, c, bridge2, append(tc.bridges.bridge2Opts, 357 network.WithDriver("bridge"), 358 network.WithOption("com.docker.network.bridge.name", bridge2))...) 359 defer network.RemoveNoError(ctx, t, c, bridge2) 360 361 ctr1Name := sanitizeCtrName(t.Name() + "-ctr1") 362 id1 := container.Run(ctx, t, c, 363 container.WithName(ctr1Name), 364 container.WithImage("busybox:latest"), 365 container.WithCmd("top"), 366 container.WithNetworkMode(bridge1)) 367 defer c.ContainerRemove(ctx, id1, containertypes.RemoveOptions{ 368 Force: true, 369 }) 370 371 ctr1Info := container.Inspect(ctx, t, c, id1) 372 targetAddr := ctr1Info.NetworkSettings.Networks[bridge1].IPAddress 373 if tc.ipv6 { 374 targetAddr = ctr1Info.NetworkSettings.Networks[bridge1].GlobalIPv6Address 375 } 376 377 pingCmd := []string{"ping", "-c1", "-W3", targetAddr} 378 379 ctr2Name := sanitizeCtrName(t.Name() + "-ctr2") 380 attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second) 381 defer cancel() 382 res := container.RunAttach(attachCtx, t, c, 383 container.WithName(ctr2Name), 384 container.WithImage("busybox:latest"), 385 container.WithCmd(pingCmd...), 386 container.WithNetworkMode(bridge2)) 387 defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{ 388 Force: true, 389 }) 390 391 assert.Check(t, res.ExitCode != 0, "ping unexpectedly succeeded") 392 assert.Check(t, is.Contains(res.Stdout.String(), tc.stdout)) 393 assert.Check(t, is.Contains(res.Stderr.String(), tc.stderr)) 394 }) 395 } 396 } 397 398 func TestDefaultBridgeIPv6(t *testing.T) { 399 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 400 401 ctx := setupTest(t) 402 403 testcases := []struct { 404 name string 405 fixed_cidr_v6 string 406 }{ 407 { 408 name: "IPv6 ULA", 409 fixed_cidr_v6: "fd00:1234::/64", 410 }, 411 { 412 name: "IPv6 LLA only", 413 fixed_cidr_v6: "fe80::/64", 414 }, 415 { 416 name: "IPv6 nonstandard LLA only", 417 fixed_cidr_v6: "fe80:1234::/64", 418 }, 419 } 420 421 for _, tc := range testcases { 422 t.Run(tc.name, func(t *testing.T) { 423 ctx := testutil.StartSpan(ctx, t) 424 425 d := daemon.New(t) 426 d.StartWithBusybox(ctx, t, 427 "--experimental", 428 "--ip6tables", 429 "--ipv6", 430 "--fixed-cidr-v6", tc.fixed_cidr_v6, 431 ) 432 defer d.Stop(t) 433 434 c := d.NewClientT(t) 435 defer c.Close() 436 437 cID := container.Run(ctx, t, c, 438 container.WithImage("busybox:latest"), 439 container.WithCmd("top"), 440 ) 441 defer c.ContainerRemove(ctx, cID, containertypes.RemoveOptions{ 442 Force: true, 443 }) 444 445 networkName := "bridge" 446 inspect := container.Inspect(ctx, t, c, cID) 447 pingHost := inspect.NetworkSettings.Networks[networkName].GlobalIPv6Address 448 449 attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second) 450 defer cancel() 451 res := container.RunAttach(attachCtx, t, c, 452 container.WithImage("busybox:latest"), 453 container.WithCmd("ping", "-c1", "-W3", pingHost), 454 ) 455 defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{ 456 Force: true, 457 }) 458 459 assert.Check(t, is.Equal(res.ExitCode, 0)) 460 assert.Check(t, is.Equal(res.Stderr.String(), "")) 461 assert.Check(t, is.Contains(res.Stdout.String(), "1 packets transmitted, 1 packets received")) 462 }) 463 } 464 } 465 466 // Check that it's possible to change 'fixed-cidr-v6' and restart the daemon. 467 func TestDefaultBridgeAddresses(t *testing.T) { 468 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 469 470 ctx := setupTest(t) 471 d := daemon.New(t) 472 473 type testStep struct { 474 stepName string 475 fixedCIDRV6 string 476 expAddrs []string 477 } 478 479 testcases := []struct { 480 name string 481 steps []testStep 482 }{ 483 { 484 name: "Unique-Local Subnet Changes", 485 steps: []testStep{ 486 { 487 stepName: "Set up initial UL prefix", 488 fixedCIDRV6: "fd1c:f1a0:5d8d:aaaa::/64", 489 expAddrs: []string{"fd1c:f1a0:5d8d:aaaa::1/64", "fe80::1/64"}, 490 }, 491 { 492 // Modify that prefix, the default bridge's address must be deleted and re-added. 493 stepName: "Modify UL prefix - address change", 494 fixedCIDRV6: "fd1c:f1a0:5d8d:bbbb::/64", 495 expAddrs: []string{"fd1c:f1a0:5d8d:bbbb::1/64", "fe80::1/64"}, 496 }, 497 { 498 // Modify the prefix length, the default bridge's address should not change. 499 stepName: "Modify UL prefix - no address change", 500 fixedCIDRV6: "fd1c:f1a0:5d8d:bbbb::/80", 501 // The prefix length displayed by 'ip a' is not updated - it's informational, and 502 // can't be changed without unnecessarily deleting and re-adding the address. 503 expAddrs: []string{"fd1c:f1a0:5d8d:bbbb::1/64", "fe80::1/64"}, 504 }, 505 }, 506 }, 507 { 508 name: "Link-Local Subnet Changes", 509 steps: []testStep{ 510 { 511 stepName: "Standard LL subnet prefix", 512 fixedCIDRV6: "fe80::/64", 513 expAddrs: []string{"fe80::1/64"}, 514 }, 515 { 516 // Modify that prefix, the default bridge's address must be deleted and re-added. 517 // The bridge must still have an address in the required (standard) LL subnet. 518 stepName: "Nonstandard LL prefix - address change", 519 fixedCIDRV6: "fe80:1234::/32", 520 expAddrs: []string{"fe80:1234::1/32", "fe80::1/64"}, 521 }, 522 { 523 // Modify the prefix length, the addresses should not change. 524 stepName: "Modify LL prefix - no address change", 525 fixedCIDRV6: "fe80:1234::/64", 526 // The prefix length displayed by 'ip a' is not updated - it's informational, and 527 // can't be changed without unnecessarily deleting and re-adding the address. 528 expAddrs: []string{"fe80:1234::1/", "fe80::1/64"}, 529 }, 530 }, 531 }, 532 } 533 534 for _, tc := range testcases { 535 t.Run(tc.name, func(t *testing.T) { 536 for _, step := range tc.steps { 537 // Check that the daemon starts - regression test for: 538 // https://github.com/moby/moby/issues/46829 539 d.Start(t, "--experimental", "--ipv6", "--ip6tables", "--fixed-cidr-v6="+step.fixedCIDRV6) 540 d.Stop(t) 541 542 // Check that the expected addresses have been applied to the bridge. (Skip in 543 // rootless mode, because the bridge is in a different network namespace.) 544 if !testEnv.IsRootless() { 545 res := testutil.RunCommand(ctx, "ip", "-6", "addr", "show", "docker0") 546 assert.Equal(t, res.ExitCode, 0, step.stepName) 547 stdout := res.Stdout() 548 for _, expAddr := range step.expAddrs { 549 assert.Check(t, is.Contains(stdout, expAddr)) 550 } 551 } 552 } 553 }) 554 } 555 } 556 557 // Test that a container on an 'internal' network has IP connectivity with 558 // the host (on its own subnet, because the n/w bridge has an address on that 559 // subnet, and it's in the host's namespace). 560 // Regression test for https://github.com/moby/moby/issues/47329 561 func TestInternalNwConnectivity(t *testing.T) { 562 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 563 564 ctx := setupTest(t) 565 566 d := daemon.New(t) 567 d.StartWithBusybox(ctx, t, "-D", "--experimental", "--ip6tables") 568 defer d.Stop(t) 569 570 c := d.NewClientT(t) 571 defer c.Close() 572 573 const bridgeName = "intnw" 574 const gw4 = "172.30.0.1" 575 const gw6 = "fda9:4130:4715::1234" 576 network.CreateNoError(ctx, t, c, bridgeName, 577 network.WithInternal(), 578 network.WithIPv6(), 579 network.WithIPAM("172.30.0.0/24", gw4), 580 network.WithIPAM("fda9:4130:4715::/64", gw6), 581 network.WithDriver("bridge"), 582 network.WithOption("com.docker.network.bridge.name", bridgeName), 583 ) 584 defer network.RemoveNoError(ctx, t, c, bridgeName) 585 586 const ctrName = "intctr" 587 id := container.Run(ctx, t, c, 588 container.WithName(ctrName), 589 container.WithImage("busybox:latest"), 590 container.WithCmd("top"), 591 container.WithNetworkMode(bridgeName), 592 ) 593 defer c.ContainerRemove(ctx, id, containertypes.RemoveOptions{Force: true}) 594 595 execCtx, cancel := context.WithTimeout(ctx, 20*time.Second) 596 defer cancel() 597 598 res := container.ExecT(execCtx, t, c, id, []string{"ping", "-c1", "-W3", gw4}) 599 assert.Check(t, is.Equal(res.ExitCode, 0)) 600 assert.Check(t, is.Equal(res.Stderr(), "")) 601 assert.Check(t, is.Contains(res.Stdout(), "1 packets transmitted, 1 packets received")) 602 603 res = container.ExecT(execCtx, t, c, id, []string{"ping6", "-c1", "-W3", gw6}) 604 assert.Check(t, is.Equal(res.ExitCode, 0)) 605 assert.Check(t, is.Equal(res.Stderr(), "")) 606 assert.Check(t, is.Contains(res.Stdout(), "1 packets transmitted, 1 packets received")) 607 608 // Addresses outside the internal subnet must not be accessible. 609 res = container.ExecT(execCtx, t, c, id, []string{"ping", "-c1", "-W3", "8.8.8.8"}) 610 assert.Check(t, is.Equal(res.ExitCode, 1)) 611 assert.Check(t, is.Contains(res.Stderr(), "Network is unreachable")) 612 } 613 614 // Check that the container's interface has no IPv6 address when IPv6 is 615 // disabled in a container via sysctl. 616 func TestDisableIPv6Addrs(t *testing.T) { 617 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 618 619 ctx := setupTest(t) 620 d := daemon.New(t) 621 d.StartWithBusybox(ctx, t) 622 defer d.Stop(t) 623 624 c := d.NewClientT(t) 625 defer c.Close() 626 627 testcases := []struct { 628 name string 629 sysctls map[string]string 630 expIPv6 bool 631 }{ 632 { 633 name: "IPv6 enabled", 634 expIPv6: true, 635 }, 636 { 637 name: "IPv6 disabled", 638 sysctls: map[string]string{"net.ipv6.conf.all.disable_ipv6": "1"}, 639 }, 640 } 641 642 const netName = "testnet" 643 network.CreateNoError(ctx, t, c, netName, 644 network.WithIPv6(), 645 network.WithIPAM("fda0:ef3d:6430:abcd::/64", "fda0:ef3d:6430:abcd::1"), 646 ) 647 defer network.RemoveNoError(ctx, t, c, netName) 648 649 inet6RE := regexp.MustCompile(`inet6[ \t]+[0-9a-f:]*`) 650 651 for _, tc := range testcases { 652 t.Run(tc.name, func(t *testing.T) { 653 ctx := testutil.StartSpan(ctx, t) 654 655 opts := []func(config *container.TestContainerConfig){ 656 container.WithCmd("ip", "a"), 657 container.WithNetworkMode(netName), 658 } 659 if len(tc.sysctls) > 0 { 660 opts = append(opts, container.WithSysctls(tc.sysctls)) 661 } 662 663 runRes := container.RunAttach(ctx, t, c, opts...) 664 defer c.ContainerRemove(ctx, runRes.ContainerID, 665 containertypes.RemoveOptions{Force: true}, 666 ) 667 668 stdout := runRes.Stdout.String() 669 inet6 := inet6RE.FindAllString(stdout, -1) 670 if tc.expIPv6 { 671 assert.Check(t, len(inet6) > 0, "Expected IPv6 addresses but found none.") 672 } else { 673 assert.Check(t, is.DeepEqual(inet6, []string{}, cmpopts.EquateEmpty())) 674 } 675 }) 676 } 677 } 678 679 // Test that it's possible to set a sysctl on an interface in the container. 680 // Regression test for https://github.com/moby/moby/issues/47619 681 func TestSetInterfaceSysctl(t *testing.T) { 682 skip.If(t, testEnv.DaemonInfo.OSType == "windows", "no sysctl on Windows") 683 684 ctx := setupTest(t) 685 d := daemon.New(t) 686 d.StartWithBusybox(ctx, t) 687 defer d.Stop(t) 688 689 c := d.NewClientT(t) 690 defer c.Close() 691 692 const scName = "net.ipv4.conf.eth0.forwarding" 693 opts := []func(config *container.TestContainerConfig){ 694 container.WithCmd("sysctl", scName), 695 container.WithSysctls(map[string]string{scName: "1"}), 696 } 697 698 runRes := container.RunAttach(ctx, t, c, opts...) 699 defer c.ContainerRemove(ctx, runRes.ContainerID, 700 containertypes.RemoveOptions{Force: true}, 701 ) 702 703 stdout := runRes.Stdout.String() 704 assert.Check(t, is.Contains(stdout, scName)) 705 }