github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/integration/network/ipvlan/ipvlan_test.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package ipvlan // import "github.com/docker/docker/integration/network/ipvlan" 5 6 import ( 7 "context" 8 "os" 9 "os/exec" 10 "strings" 11 "sync" 12 "testing" 13 14 dclient "github.com/docker/docker/client" 15 "github.com/docker/docker/integration/internal/container" 16 net "github.com/docker/docker/integration/internal/network" 17 n "github.com/docker/docker/integration/network" 18 "github.com/docker/docker/testutil/daemon" 19 "gotest.tools/v3/assert" 20 "gotest.tools/v3/skip" 21 ) 22 23 func TestDockerNetworkIpvlanPersistance(t *testing.T) { 24 // verify the driver automatically provisions the 802.1q link (di-dummy0.70) 25 skip.If(t, testEnv.IsRemoteDaemon) 26 skip.If(t, !ipvlanKernelSupport(t), "Kernel doesn't support ipvlan") 27 28 d := daemon.New(t) 29 d.StartWithBusybox(t) 30 defer d.Stop(t) 31 32 // master dummy interface 'di' notation represent 'docker ipvlan' 33 master := "di-dummy0" 34 n.CreateMasterDummy(t, master) 35 defer n.DeleteInterface(t, master) 36 37 c := d.NewClientT(t) 38 39 // create a network specifying the desired sub-interface name 40 netName := "di-persist" 41 net.CreateNoError(context.Background(), t, c, netName, 42 net.WithIPvlan("di-dummy0.70", ""), 43 ) 44 45 assert.Check(t, n.IsNetworkAvailable(c, netName)) 46 // Restart docker daemon to test the config has persisted to disk 47 d.Restart(t) 48 assert.Check(t, n.IsNetworkAvailable(c, netName)) 49 } 50 51 func TestDockerNetworkIpvlan(t *testing.T) { 52 skip.If(t, testEnv.IsRemoteDaemon) 53 skip.If(t, !ipvlanKernelSupport(t), "Kernel doesn't support ipvlan") 54 55 for _, tc := range []struct { 56 name string 57 test func(dclient.APIClient) func(*testing.T) 58 }{ 59 { 60 name: "Subinterface", 61 test: testIpvlanSubinterface, 62 }, { 63 name: "OverlapParent", 64 test: testIpvlanOverlapParent, 65 }, { 66 name: "L2NilParent", 67 test: testIpvlanL2NilParent, 68 }, { 69 name: "L2InternalMode", 70 test: testIpvlanL2InternalMode, 71 }, { 72 name: "L3NilParent", 73 test: testIpvlanL3NilParent, 74 }, { 75 name: "L3InternalMode", 76 test: testIpvlanL3InternalMode, 77 }, { 78 name: "L2MultiSubnet", 79 test: testIpvlanL2MultiSubnet, 80 }, { 81 name: "L3MultiSubnet", 82 test: testIpvlanL3MultiSubnet, 83 }, { 84 name: "Addressing", 85 test: testIpvlanAddressing, 86 }, 87 } { 88 d := daemon.New(t) 89 d.StartWithBusybox(t) 90 c := d.NewClientT(t) 91 92 t.Run(tc.name, tc.test(c)) 93 94 d.Stop(t) 95 // FIXME(vdemeester) clean network 96 } 97 } 98 99 func testIpvlanSubinterface(client dclient.APIClient) func(*testing.T) { 100 return func(t *testing.T) { 101 master := "di-dummy0" 102 n.CreateMasterDummy(t, master) 103 defer n.DeleteInterface(t, master) 104 105 netName := "di-subinterface" 106 net.CreateNoError(context.Background(), t, client, netName, 107 net.WithIPvlan("di-dummy0.60", ""), 108 ) 109 assert.Check(t, n.IsNetworkAvailable(client, netName)) 110 111 // delete the network while preserving the parent link 112 err := client.NetworkRemove(context.Background(), netName) 113 assert.NilError(t, err) 114 115 assert.Check(t, n.IsNetworkNotAvailable(client, netName)) 116 // verify the network delete did not delete the predefined link 117 n.LinkExists(t, "di-dummy0") 118 } 119 } 120 121 func testIpvlanOverlapParent(client dclient.APIClient) func(*testing.T) { 122 return func(t *testing.T) { 123 // verify the same parent interface cannot be used if already in use by an existing network 124 master := "di-dummy0" 125 parent := master + ".30" 126 n.CreateMasterDummy(t, master) 127 defer n.DeleteInterface(t, master) 128 n.CreateVlanInterface(t, master, parent, "30") 129 130 netName := "di-subinterface" 131 net.CreateNoError(context.Background(), t, client, netName, 132 net.WithIPvlan(parent, ""), 133 ) 134 assert.Check(t, n.IsNetworkAvailable(client, netName)) 135 136 _, err := net.Create(context.Background(), client, netName, 137 net.WithIPvlan(parent, ""), 138 ) 139 // verify that the overlap returns an error 140 assert.Check(t, err != nil) 141 } 142 } 143 144 func testIpvlanL2NilParent(client dclient.APIClient) func(*testing.T) { 145 return func(t *testing.T) { 146 // ipvlan l2 mode - dummy parent interface is provisioned dynamically 147 netName := "di-nil-parent" 148 net.CreateNoError(context.Background(), t, client, netName, 149 net.WithIPvlan("", ""), 150 ) 151 assert.Check(t, n.IsNetworkAvailable(client, netName)) 152 153 ctx := context.Background() 154 id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 155 id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 156 157 _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 158 assert.NilError(t, err) 159 } 160 } 161 162 func testIpvlanL2InternalMode(client dclient.APIClient) func(*testing.T) { 163 return func(t *testing.T) { 164 netName := "di-internal" 165 net.CreateNoError(context.Background(), t, client, netName, 166 net.WithIPvlan("", ""), 167 net.WithInternal(), 168 ) 169 assert.Check(t, n.IsNetworkAvailable(client, netName)) 170 171 ctx := context.Background() 172 id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 173 id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 174 175 result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"}) 176 assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable")) 177 178 _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 179 assert.NilError(t, err) 180 } 181 } 182 183 func testIpvlanL3NilParent(client dclient.APIClient) func(*testing.T) { 184 return func(t *testing.T) { 185 netName := "di-nil-parent-l3" 186 net.CreateNoError(context.Background(), t, client, netName, 187 net.WithIPvlan("", "l3"), 188 net.WithIPAM("172.28.230.0/24", ""), 189 net.WithIPAM("172.28.220.0/24", ""), 190 ) 191 assert.Check(t, n.IsNetworkAvailable(client, netName)) 192 193 ctx := context.Background() 194 id1 := container.Run(ctx, t, client, 195 container.WithNetworkMode(netName), 196 container.WithIPv4(netName, "172.28.220.10"), 197 ) 198 id2 := container.Run(ctx, t, client, 199 container.WithNetworkMode(netName), 200 container.WithIPv4(netName, "172.28.230.10"), 201 ) 202 203 _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 204 assert.NilError(t, err) 205 } 206 } 207 208 func testIpvlanL3InternalMode(client dclient.APIClient) func(*testing.T) { 209 return func(t *testing.T) { 210 netName := "di-internal-l3" 211 net.CreateNoError(context.Background(), t, client, netName, 212 net.WithIPvlan("", "l3"), 213 net.WithInternal(), 214 net.WithIPAM("172.28.230.0/24", ""), 215 net.WithIPAM("172.28.220.0/24", ""), 216 ) 217 assert.Check(t, n.IsNetworkAvailable(client, netName)) 218 219 ctx := context.Background() 220 id1 := container.Run(ctx, t, client, 221 container.WithNetworkMode(netName), 222 container.WithIPv4(netName, "172.28.220.10"), 223 ) 224 id2 := container.Run(ctx, t, client, 225 container.WithNetworkMode(netName), 226 container.WithIPv4(netName, "172.28.230.10"), 227 ) 228 229 result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"}) 230 assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable")) 231 232 _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 233 assert.NilError(t, err) 234 } 235 } 236 237 func testIpvlanL2MultiSubnet(client dclient.APIClient) func(*testing.T) { 238 return func(t *testing.T) { 239 netName := "dualstackl2" 240 net.CreateNoError(context.Background(), t, client, netName, 241 net.WithIPvlan("", ""), 242 net.WithIPv6(), 243 net.WithIPAM("172.28.200.0/24", ""), 244 net.WithIPAM("172.28.202.0/24", "172.28.202.254"), 245 net.WithIPAM("2001:db8:abc8::/64", ""), 246 net.WithIPAM("2001:db8:abc6::/64", "2001:db8:abc6::254"), 247 ) 248 assert.Check(t, n.IsNetworkAvailable(client, netName)) 249 250 // 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 251 ctx := context.Background() 252 id1 := container.Run(ctx, t, client, 253 container.WithNetworkMode(netName), 254 container.WithIPv4(netName, "172.28.200.20"), 255 container.WithIPv6(netName, "2001:db8:abc8::20"), 256 ) 257 id2 := container.Run(ctx, t, client, 258 container.WithNetworkMode(netName), 259 container.WithIPv4(netName, "172.28.200.21"), 260 container.WithIPv6(netName, "2001:db8:abc8::21"), 261 ) 262 c1, err := client.ContainerInspect(ctx, id1) 263 assert.NilError(t, err) 264 265 // verify ipv4 connectivity to the explicit --ipv address second to first 266 _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress}) 267 assert.NilError(t, err) 268 // verify ipv6 connectivity to the explicit --ipv6 address second to first 269 _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address}) 270 assert.NilError(t, err) 271 272 // 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 273 id3 := container.Run(ctx, t, client, 274 container.WithNetworkMode(netName), 275 container.WithIPv4(netName, "172.28.202.20"), 276 container.WithIPv6(netName, "2001:db8:abc6::20"), 277 ) 278 id4 := container.Run(ctx, t, client, 279 container.WithNetworkMode(netName), 280 container.WithIPv4(netName, "172.28.202.21"), 281 container.WithIPv6(netName, "2001:db8:abc6::21"), 282 ) 283 c3, err := client.ContainerInspect(ctx, id3) 284 assert.NilError(t, err) 285 286 // verify ipv4 connectivity to the explicit --ipv address from third to fourth 287 _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress}) 288 assert.NilError(t, err) 289 // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth 290 _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address}) 291 assert.NilError(t, err) 292 293 // Inspect the v4 gateway to ensure the proper default GW was assigned 294 assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "172.28.200.1") 295 // Inspect the v6 gateway to ensure the proper default GW was assigned 296 assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc8::1") 297 // Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned 298 assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "172.28.202.254") 299 // Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned 300 assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc6::254") 301 } 302 } 303 304 func testIpvlanL3MultiSubnet(client dclient.APIClient) func(*testing.T) { 305 return func(t *testing.T) { 306 netName := "dualstackl3" 307 net.CreateNoError(context.Background(), t, client, netName, 308 net.WithIPvlan("", "l3"), 309 net.WithIPv6(), 310 net.WithIPAM("172.28.10.0/24", ""), 311 net.WithIPAM("172.28.12.0/24", "172.28.12.254"), 312 net.WithIPAM("2001:db8:abc9::/64", ""), 313 net.WithIPAM("2001:db8:abc7::/64", "2001:db8:abc7::254"), 314 ) 315 assert.Check(t, n.IsNetworkAvailable(client, netName)) 316 317 // 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 318 ctx := context.Background() 319 id1 := container.Run(ctx, t, client, 320 container.WithNetworkMode(netName), 321 container.WithIPv4(netName, "172.28.10.20"), 322 container.WithIPv6(netName, "2001:db8:abc9::20"), 323 ) 324 id2 := container.Run(ctx, t, client, 325 container.WithNetworkMode(netName), 326 container.WithIPv4(netName, "172.28.10.21"), 327 container.WithIPv6(netName, "2001:db8:abc9::21"), 328 ) 329 c1, err := client.ContainerInspect(ctx, id1) 330 assert.NilError(t, err) 331 332 // verify ipv4 connectivity to the explicit --ipv address second to first 333 _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress}) 334 assert.NilError(t, err) 335 // verify ipv6 connectivity to the explicit --ipv6 address second to first 336 _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address}) 337 assert.NilError(t, err) 338 339 // 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 340 id3 := container.Run(ctx, t, client, 341 container.WithNetworkMode(netName), 342 container.WithIPv4(netName, "172.28.12.20"), 343 container.WithIPv6(netName, "2001:db8:abc7::20"), 344 ) 345 id4 := container.Run(ctx, t, client, 346 container.WithNetworkMode(netName), 347 container.WithIPv4(netName, "172.28.12.21"), 348 container.WithIPv6(netName, "2001:db8:abc7::21"), 349 ) 350 c3, err := client.ContainerInspect(ctx, id3) 351 assert.NilError(t, err) 352 353 // verify ipv4 connectivity to the explicit --ipv address from third to fourth 354 _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress}) 355 assert.NilError(t, err) 356 // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth 357 _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address}) 358 assert.NilError(t, err) 359 360 // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode 361 assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "") 362 // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled 363 assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "") 364 // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode 365 assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "") 366 // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled 367 assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "") 368 } 369 } 370 371 func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) { 372 return func(t *testing.T) { 373 // Verify ipvlan l2 mode sets the proper default gateway routes via netlink 374 // for either an explicitly set route by the user or inferred via default IPAM 375 netNameL2 := "dualstackl2" 376 net.CreateNoError(context.Background(), t, client, netNameL2, 377 net.WithIPvlan("", "l2"), 378 net.WithIPv6(), 379 net.WithIPAM("172.28.140.0/24", "172.28.140.254"), 380 net.WithIPAM("2001:db8:abcb::/64", ""), 381 ) 382 assert.Check(t, n.IsNetworkAvailable(client, netNameL2)) 383 384 ctx := context.Background() 385 id1 := container.Run(ctx, t, client, 386 container.WithNetworkMode(netNameL2), 387 ) 388 // Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet 389 result, err := container.Exec(ctx, client, id1, []string{"ip", "route"}) 390 assert.NilError(t, err) 391 assert.Check(t, strings.Contains(result.Combined(), "default via 172.28.140.254 dev eth0")) 392 // Validate ipvlan l2 mode sets the v6 gateway to the user specified default gateway/next-hop 393 result, err = container.Exec(ctx, client, id1, []string{"ip", "-6", "route"}) 394 assert.NilError(t, err) 395 assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abcb::1 dev eth0")) 396 397 // Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops 398 netNameL3 := "dualstackl3" 399 net.CreateNoError(context.Background(), t, client, netNameL3, 400 net.WithIPvlan("", "l3"), 401 net.WithIPv6(), 402 net.WithIPAM("172.28.160.0/24", "172.28.160.254"), 403 net.WithIPAM("2001:db8:abcd::/64", "2001:db8:abcd::254"), 404 ) 405 assert.Check(t, n.IsNetworkAvailable(client, netNameL3)) 406 407 id2 := container.Run(ctx, t, client, 408 container.WithNetworkMode(netNameL3), 409 ) 410 // Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops 411 result, err = container.Exec(ctx, client, id2, []string{"ip", "route"}) 412 assert.NilError(t, err) 413 assert.Check(t, strings.Contains(result.Combined(), "default dev eth0")) 414 // Validate ipvlan l3 mode sets the v6 gateway to dev eth0 and disregards any explicit or inferred next-hops 415 result, err = container.Exec(ctx, client, id2, []string{"ip", "-6", "route"}) 416 assert.NilError(t, err) 417 assert.Check(t, strings.Contains(result.Combined(), "default dev eth0")) 418 } 419 } 420 421 var ( 422 once sync.Once 423 ipvlanSupported bool 424 ) 425 426 // figure out if ipvlan is supported by the kernel 427 func ipvlanKernelSupport(t *testing.T) bool { 428 once.Do(func() { 429 // this may have the side effect of enabling the ipvlan module 430 exec.Command("modprobe", "ipvlan").Run() 431 _, err := os.Stat("/sys/module/ipvlan") 432 if err == nil { 433 ipvlanSupported = true 434 } else if !os.IsNotExist(err) { 435 t.Logf("WARNING: ipvlanKernelSupport: stat failed: %v\n", err) 436 } 437 }) 438 439 return ipvlanSupported 440 }