github.com/moby/docker@v26.1.3+incompatible/integration/network/ipvlan/ipvlan_test.go (about) 1 //go:build !windows 2 3 package ipvlan // import "github.com/docker/docker/integration/network/ipvlan" 4 5 import ( 6 "context" 7 "fmt" 8 "strings" 9 "testing" 10 11 "github.com/docker/docker/api/types" 12 containertypes "github.com/docker/docker/api/types/container" 13 dclient "github.com/docker/docker/client" 14 "github.com/docker/docker/integration/internal/container" 15 net "github.com/docker/docker/integration/internal/network" 16 n "github.com/docker/docker/integration/network" 17 "github.com/docker/docker/testutil" 18 "github.com/docker/docker/testutil/daemon" 19 "gotest.tools/v3/assert" 20 is "gotest.tools/v3/assert/cmp" 21 "gotest.tools/v3/skip" 22 ) 23 24 func TestDockerNetworkIpvlanPersistance(t *testing.T) { 25 // verify the driver automatically provisions the 802.1q link (di-dummy0.70) 26 skip.If(t, testEnv.IsRemoteDaemon) 27 skip.If(t, testEnv.IsRootless, "rootless mode has different view of network") 28 29 ctx := testutil.StartSpan(baseContext, t) 30 31 d := daemon.New(t) 32 d.StartWithBusybox(ctx, t) 33 defer d.Stop(t) 34 35 // master dummy interface 'di' notation represent 'docker ipvlan' 36 master := "di-dummy0" 37 n.CreateMasterDummy(ctx, t, master) 38 defer n.DeleteInterface(ctx, t, master) 39 40 c := d.NewClientT(t) 41 42 // create a network specifying the desired sub-interface name 43 netName := "di-persist" 44 net.CreateNoError(ctx, t, c, netName, 45 net.WithIPvlan("di-dummy0.70", ""), 46 ) 47 48 assert.Check(t, n.IsNetworkAvailable(ctx, c, netName)) 49 // Restart docker daemon to test the config has persisted to disk 50 d.Restart(t) 51 assert.Check(t, n.IsNetworkAvailable(ctx, c, netName)) 52 } 53 54 func TestDockerNetworkIpvlan(t *testing.T) { 55 skip.If(t, testEnv.IsRemoteDaemon) 56 skip.If(t, testEnv.IsRootless, "rootless mode has different view of network") 57 58 ctx := testutil.StartSpan(baseContext, t) 59 60 for _, tc := range []struct { 61 name string 62 test func(*testing.T, context.Context, dclient.APIClient) 63 }{ 64 { 65 name: "Subinterface", 66 test: testIpvlanSubinterface, 67 }, { 68 name: "OverlapParent", 69 test: testIpvlanOverlapParent, 70 }, { 71 name: "L2NilParent", 72 test: testIpvlanL2NilParent, 73 }, { 74 name: "L2InternalMode", 75 test: testIpvlanL2InternalMode, 76 }, { 77 name: "L3NilParent", 78 test: testIpvlanL3NilParent, 79 }, { 80 name: "L3InternalMode", 81 test: testIpvlanL3InternalMode, 82 }, { 83 name: "L2MultiSubnetWithParent", 84 test: testIpvlanL2MultiSubnetWithParent, 85 }, { 86 name: "L2MultiSubnetNoParent", 87 test: testIpvlanL2MultiSubnetNoParent, 88 }, { 89 name: "L3MultiSubnet", 90 test: testIpvlanL3MultiSubnet, 91 }, { 92 name: "L2Addressing", 93 test: testIpvlanL2Addressing, 94 }, { 95 name: "L3Addressing", 96 test: testIpvlanL3Addressing, 97 }, { 98 name: "NoIPv6", 99 test: testIpvlanNoIPv6, 100 }, 101 } { 102 103 t.Run(tc.name, func(t *testing.T) { 104 testutil.StartSpan(ctx, t) 105 d := daemon.New(t) 106 t.Cleanup(func() { d.Stop(t) }) 107 d.StartWithBusybox(ctx, t) 108 c := d.NewClientT(t) 109 tc.test(t, ctx, c) 110 }) 111 112 // FIXME(vdemeester) clean network 113 } 114 } 115 116 func testIpvlanSubinterface(t *testing.T, ctx context.Context, client dclient.APIClient) { 117 master := "di-dummy0" 118 n.CreateMasterDummy(ctx, t, master) 119 defer n.DeleteInterface(ctx, t, master) 120 121 netName := "di-subinterface" 122 net.CreateNoError(ctx, t, client, netName, 123 net.WithIPvlan("di-dummy0.60", ""), 124 ) 125 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 126 127 // delete the network while preserving the parent link 128 err := client.NetworkRemove(ctx, netName) 129 assert.NilError(t, err) 130 131 assert.Check(t, n.IsNetworkNotAvailable(ctx, client, netName)) 132 // verify the network delete did not delete the predefined link 133 n.LinkExists(ctx, t, "di-dummy0") 134 } 135 136 func testIpvlanOverlapParent(t *testing.T, ctx context.Context, client dclient.APIClient) { 137 // verify the same parent interface cannot be used if already in use by an existing network 138 master := "di-dummy0" 139 parent := master + ".30" 140 n.CreateMasterDummy(ctx, t, master) 141 defer n.DeleteInterface(ctx, t, master) 142 n.CreateVlanInterface(ctx, t, master, parent, "30") 143 144 netName := "di-subinterface" 145 net.CreateNoError(ctx, t, client, netName, 146 net.WithIPvlan(parent, ""), 147 ) 148 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 149 150 _, err := net.Create(ctx, client, netName, 151 net.WithIPvlan(parent, ""), 152 ) 153 // verify that the overlap returns an error 154 assert.Check(t, err != nil) 155 } 156 157 func testIpvlanL2NilParent(t *testing.T, ctx context.Context, client dclient.APIClient) { 158 // ipvlan l2 mode - dummy parent interface is provisioned dynamically 159 netName := "di-nil-parent" 160 net.CreateNoError(ctx, t, client, netName, 161 net.WithIPvlan("", ""), 162 ) 163 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 164 165 id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 166 id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 167 168 _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 169 assert.NilError(t, err) 170 } 171 172 func testIpvlanL2InternalMode(t *testing.T, ctx context.Context, client dclient.APIClient) { 173 netName := "di-internal" 174 net.CreateNoError(ctx, t, client, netName, 175 net.WithIPvlan("", ""), 176 net.WithInternal(), 177 ) 178 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 179 180 id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 181 id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 182 183 result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"}) 184 assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable")) 185 186 _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 187 assert.NilError(t, err) 188 } 189 190 func testIpvlanL3NilParent(t *testing.T, ctx context.Context, client dclient.APIClient) { 191 netName := "di-nil-parent-l3" 192 net.CreateNoError(ctx, t, client, netName, 193 net.WithIPvlan("", "l3"), 194 net.WithIPAM("172.28.230.0/24", ""), 195 net.WithIPAM("172.28.220.0/24", ""), 196 ) 197 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 198 199 id1 := container.Run(ctx, t, client, 200 container.WithNetworkMode(netName), 201 container.WithIPv4(netName, "172.28.220.10"), 202 ) 203 id2 := container.Run(ctx, t, client, 204 container.WithNetworkMode(netName), 205 container.WithIPv4(netName, "172.28.230.10"), 206 ) 207 208 _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 209 assert.NilError(t, err) 210 } 211 212 func testIpvlanL3InternalMode(t *testing.T, ctx context.Context, client dclient.APIClient) { 213 netName := "di-internal-l3" 214 net.CreateNoError(ctx, t, client, netName, 215 net.WithIPvlan("", "l3"), 216 net.WithInternal(), 217 net.WithIPAM("172.28.230.0/24", ""), 218 net.WithIPAM("172.28.220.0/24", ""), 219 ) 220 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 221 222 id1 := container.Run(ctx, t, client, 223 container.WithNetworkMode(netName), 224 container.WithIPv4(netName, "172.28.220.10"), 225 ) 226 id2 := container.Run(ctx, t, client, 227 container.WithNetworkMode(netName), 228 container.WithIPv4(netName, "172.28.230.10"), 229 ) 230 231 result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"}) 232 assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable")) 233 234 _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 235 assert.NilError(t, err) 236 } 237 238 func testIpvlanL2MultiSubnetWithParent(t *testing.T, ctx context.Context, client dclient.APIClient) { 239 const parentIfName = "di-dummy0" 240 n.CreateMasterDummy(ctx, t, parentIfName) 241 defer n.DeleteInterface(ctx, t, parentIfName) 242 testIpvlanL2MultiSubnet(t, ctx, client, parentIfName) 243 } 244 245 func testIpvlanL2MultiSubnetNoParent(t *testing.T, ctx context.Context, client dclient.APIClient) { 246 testIpvlanL2MultiSubnet(t, ctx, client, "") 247 } 248 249 func testIpvlanL2MultiSubnet(t *testing.T, ctx context.Context, client dclient.APIClient, parent string) { 250 netName := "dualstackl2" 251 net.CreateNoError(ctx, t, client, netName, 252 net.WithIPvlan(parent, ""), 253 net.WithIPv6(), 254 net.WithIPAM("172.28.200.0/24", ""), 255 net.WithIPAM("172.28.202.0/24", "172.28.202.254"), 256 net.WithIPAM("2001:db8:abc8::/64", ""), 257 net.WithIPAM("2001:db8:abc6::/64", "2001:db8:abc6::254"), 258 ) 259 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 260 261 // start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64 262 id1 := container.Run(ctx, t, client, 263 container.WithNetworkMode(netName), 264 container.WithIPv4(netName, "172.28.200.20"), 265 container.WithIPv6(netName, "2001:db8:abc8::20"), 266 ) 267 id2 := container.Run(ctx, t, client, 268 container.WithNetworkMode(netName), 269 container.WithIPv4(netName, "172.28.200.21"), 270 container.WithIPv6(netName, "2001:db8:abc8::21"), 271 ) 272 c1, err := client.ContainerInspect(ctx, id1) 273 assert.NilError(t, err) 274 if parent == "" { 275 // Inspect the v4 gateway to ensure no default GW was assigned 276 assert.Check(t, is.Equal(c1.NetworkSettings.Networks[netName].Gateway, "")) 277 // Inspect the v6 gateway to ensure no default GW was assigned 278 assert.Check(t, is.Equal(c1.NetworkSettings.Networks[netName].IPv6Gateway, "")) 279 } else { 280 // Inspect the v4 gateway to ensure the proper default GW was assigned 281 assert.Check(t, is.Equal(c1.NetworkSettings.Networks[netName].Gateway, "172.28.200.1")) 282 // Inspect the v6 gateway to ensure the proper default GW was assigned 283 assert.Check(t, is.Equal(c1.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc8::1")) 284 } 285 286 // verify ipv4 connectivity to the explicit --ip address second to first 287 _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress}) 288 assert.NilError(t, err) 289 // verify ipv6 connectivity to the explicit --ip6 address second to first 290 _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address}) 291 assert.NilError(t, err) 292 293 // start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64 294 id3 := container.Run(ctx, t, client, 295 container.WithNetworkMode(netName), 296 container.WithIPv4(netName, "172.28.202.20"), 297 container.WithIPv6(netName, "2001:db8:abc6::20"), 298 ) 299 id4 := container.Run(ctx, t, client, 300 container.WithNetworkMode(netName), 301 container.WithIPv4(netName, "172.28.202.21"), 302 container.WithIPv6(netName, "2001:db8:abc6::21"), 303 ) 304 c3, err := client.ContainerInspect(ctx, id3) 305 assert.NilError(t, err) 306 if parent == "" { 307 // Inspect the v4 gateway to ensure no default GW was assigned 308 assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].Gateway, "")) 309 // Inspect the v6 gateway to ensure no default GW was assigned 310 assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].IPv6Gateway, "")) 311 } else { 312 // Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned 313 assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].Gateway, "172.28.202.254")) 314 // Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned 315 assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc6::254")) 316 } 317 318 // verify ipv4 connectivity to the explicit --ip address from third to fourth 319 _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress}) 320 assert.NilError(t, err) 321 // verify ipv6 connectivity to the explicit --ip6 address from third to fourth 322 _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address}) 323 assert.NilError(t, err) 324 } 325 326 func testIpvlanL3MultiSubnet(t *testing.T, ctx context.Context, client dclient.APIClient) { 327 netName := "dualstackl3" 328 net.CreateNoError(ctx, t, client, netName, 329 net.WithIPvlan("", "l3"), 330 net.WithIPv6(), 331 net.WithIPAM("172.28.10.0/24", ""), 332 net.WithIPAM("172.28.12.0/24", "172.28.12.254"), 333 net.WithIPAM("2001:db8:abc9::/64", ""), 334 net.WithIPAM("2001:db8:abc7::/64", "2001:db8:abc7::254"), 335 ) 336 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 337 338 // start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64 339 id1 := container.Run(ctx, t, client, 340 container.WithNetworkMode(netName), 341 container.WithIPv4(netName, "172.28.10.20"), 342 container.WithIPv6(netName, "2001:db8:abc9::20"), 343 ) 344 id2 := container.Run(ctx, t, client, 345 container.WithNetworkMode(netName), 346 container.WithIPv4(netName, "172.28.10.21"), 347 container.WithIPv6(netName, "2001:db8:abc9::21"), 348 ) 349 c1, err := client.ContainerInspect(ctx, id1) 350 assert.NilError(t, err) 351 352 // verify ipv4 connectivity to the explicit --ipv address second to first 353 _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress}) 354 assert.NilError(t, err) 355 // verify ipv6 connectivity to the explicit --ipv6 address second to first 356 _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address}) 357 assert.NilError(t, err) 358 359 // start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64 360 id3 := container.Run(ctx, t, client, 361 container.WithNetworkMode(netName), 362 container.WithIPv4(netName, "172.28.12.20"), 363 container.WithIPv6(netName, "2001:db8:abc7::20"), 364 ) 365 id4 := container.Run(ctx, t, client, 366 container.WithNetworkMode(netName), 367 container.WithIPv4(netName, "172.28.12.21"), 368 container.WithIPv6(netName, "2001:db8:abc7::21"), 369 ) 370 c3, err := client.ContainerInspect(ctx, id3) 371 assert.NilError(t, err) 372 373 // verify ipv4 connectivity to the explicit --ipv address from third to fourth 374 _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress}) 375 assert.NilError(t, err) 376 // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth 377 _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address}) 378 assert.NilError(t, err) 379 380 // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode 381 assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "") 382 // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled 383 assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "") 384 // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode 385 assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "") 386 // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled 387 assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "") 388 } 389 390 // Verify ipvlan l2 mode sets the proper default gateway routes via netlink 391 // for either an explicitly set route by the user or inferred via default IPAM 392 func testIpvlanL2Addressing(t *testing.T, ctx context.Context, client dclient.APIClient) { 393 const parentIfName = "di-dummy0" 394 n.CreateMasterDummy(ctx, t, parentIfName) 395 defer n.DeleteInterface(ctx, t, parentIfName) 396 397 netNameL2 := "dualstackl2" 398 net.CreateNoError(ctx, t, client, netNameL2, 399 net.WithIPvlan(parentIfName, "l2"), 400 net.WithIPv6(), 401 net.WithIPAM("172.28.140.0/24", "172.28.140.254"), 402 net.WithIPAM("2001:db8:abcb::/64", ""), 403 ) 404 assert.Check(t, n.IsNetworkAvailable(ctx, client, netNameL2)) 405 406 id := container.Run(ctx, t, client, 407 container.WithNetworkMode(netNameL2), 408 ) 409 // Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet 410 result, err := container.Exec(ctx, client, id, []string{"ip", "route"}) 411 assert.NilError(t, err) 412 assert.Check(t, is.Contains(result.Combined(), "default via 172.28.140.254 dev eth0")) 413 // Validate ipvlan l2 mode sets the v6 gateway to the user specified default gateway/next-hop 414 result, err = container.Exec(ctx, client, id, []string{"ip", "-6", "route"}) 415 assert.NilError(t, err) 416 assert.Check(t, is.Contains(result.Combined(), "default via 2001:db8:abcb::1 dev eth0")) 417 } 418 419 // Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops 420 func testIpvlanL3Addressing(t *testing.T, ctx context.Context, client dclient.APIClient) { 421 const parentIfName = "di-dummy0" 422 n.CreateMasterDummy(ctx, t, parentIfName) 423 defer n.DeleteInterface(ctx, t, parentIfName) 424 425 netNameL3 := "dualstackl3" 426 net.CreateNoError(ctx, t, client, netNameL3, 427 net.WithIPvlan(parentIfName, "l3"), 428 net.WithIPv6(), 429 net.WithIPAM("172.28.160.0/24", "172.28.160.254"), 430 net.WithIPAM("2001:db8:abcd::/64", "2001:db8:abcd::254"), 431 ) 432 assert.Check(t, n.IsNetworkAvailable(ctx, client, netNameL3)) 433 434 id := container.Run(ctx, t, client, 435 container.WithNetworkMode(netNameL3), 436 ) 437 // Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops 438 result, err := container.Exec(ctx, client, id, []string{"ip", "route"}) 439 assert.NilError(t, err) 440 assert.Check(t, is.Contains(result.Combined(), "default dev eth0")) 441 // Validate ipvlan l3 mode sets the v6 gateway to dev eth0 and disregards any explicit or inferred next-hops 442 result, err = container.Exec(ctx, client, id, []string{"ip", "-6", "route"}) 443 assert.NilError(t, err) 444 assert.Check(t, is.Contains(result.Combined(), "default dev eth0")) 445 } 446 447 // Check that an ipvlan interface with '--ipv6=false' doesn't get kernel-assigned 448 // IPv6 addresses, but the loopback interface does still have an IPv6 address ('::1'). 449 func testIpvlanNoIPv6(t *testing.T, ctx context.Context, client dclient.APIClient) { 450 const netName = "ipvlannet" 451 net.CreateNoError(ctx, t, client, netName, net.WithIPvlan("", "l3")) 452 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 453 454 id := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 455 456 loRes := container.ExecT(ctx, t, client, id, []string{"ip", "a", "show", "dev", "lo"}) 457 assert.Check(t, is.Contains(loRes.Combined(), " inet ")) 458 assert.Check(t, is.Contains(loRes.Combined(), " inet6 ")) 459 460 eth0Res := container.ExecT(ctx, t, client, id, []string{"ip", "a", "show", "dev", "eth0"}) 461 assert.Check(t, is.Contains(eth0Res.Combined(), " inet ")) 462 assert.Check(t, !strings.Contains(eth0Res.Combined(), " inet6 "), 463 "result.Combined(): %s", eth0Res.Combined()) 464 465 sysctlRes := container.ExecT(ctx, t, client, id, []string{"sysctl", "-n", "net.ipv6.conf.eth0.disable_ipv6"}) 466 assert.Check(t, is.Equal(strings.TrimSpace(sysctlRes.Combined()), "1")) 467 } 468 469 // TestIPVlanDNS checks whether DNS is forwarded, for combinations of l2/l3 mode, 470 // with/without a parent interface, and with '--internal'. Note that, there's no 471 // attempt here to give the ipvlan network external connectivity - when this test 472 // supplies a parent interface, it's a dummy. External DNS lookups only work 473 // because the daemon is configured to see a host resolver on a loopback 474 // interface, so the external DNS lookup happens in the host's namespace. The 475 // test is checking that an automatically configured dummy interface causes the 476 // network to behave as if it was '--internal'. Regression test for 477 // https://github.com/moby/moby/issues/47662 478 func TestIPVlanDNS(t *testing.T) { 479 skip.If(t, testEnv.IsRootless, "rootless mode has different view of network") 480 ctx := testutil.StartSpan(baseContext, t) 481 482 net.StartDaftDNS(t, "127.0.0.1") 483 484 tmpFileName := net.WriteTempResolvConf(t, "127.0.0.1") 485 d := daemon.New(t, daemon.WithEnvVars("DOCKER_TEST_RESOLV_CONF_PATH="+tmpFileName)) 486 d.StartWithBusybox(ctx, t) 487 t.Cleanup(func() { d.Stop(t) }) 488 c := d.NewClientT(t) 489 490 const parentIfName = "di-dummy0" 491 n.CreateMasterDummy(ctx, t, parentIfName) 492 defer n.DeleteInterface(ctx, t, parentIfName) 493 494 const netName = "ipvlan-dns-net" 495 496 testcases := []struct { 497 name string 498 parent string 499 internal bool 500 expDNS bool 501 }{ 502 { 503 name: "with parent", 504 parent: parentIfName, 505 // External DNS should be used (even though the network has no external connectivity). 506 expDNS: true, 507 }, 508 { 509 name: "no parent", 510 // External DNS should not be used, equivalent to '--internal'. 511 }, 512 { 513 name: "with parent, internal", 514 parent: parentIfName, 515 internal: true, 516 // External DNS should not be used. 517 }, 518 } 519 520 for _, mode := range []string{"l2", "l3"} { 521 for _, tc := range testcases { 522 name := fmt.Sprintf("Mode=%v/HasParent=%v/Internal=%v", mode, tc.parent != "", tc.internal) 523 t.Run(name, func(t *testing.T) { 524 ctx := testutil.StartSpan(ctx, t) 525 createOpts := []func(*types.NetworkCreate){ 526 net.WithIPvlan(tc.parent, mode), 527 } 528 if tc.internal { 529 createOpts = append(createOpts, net.WithInternal()) 530 } 531 net.CreateNoError(ctx, t, c, netName, createOpts...) 532 defer c.NetworkRemove(ctx, netName) 533 534 ctrId := container.Run(ctx, t, c, container.WithNetworkMode(netName)) 535 defer c.ContainerRemove(ctx, ctrId, containertypes.RemoveOptions{Force: true}) 536 res, err := container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"}) 537 assert.NilError(t, err) 538 if tc.expDNS { 539 assert.Check(t, is.Equal(res.ExitCode, 0)) 540 assert.Check(t, is.Contains(res.Stdout(), net.DNSRespAddr)) 541 } else { 542 assert.Check(t, is.Equal(res.ExitCode, 1)) 543 assert.Check(t, is.Contains(res.Stdout(), "SERVFAIL")) 544 } 545 }) 546 } 547 } 548 }