github.com/devdivbcp/moby@v17.12.0-ce-rc1.0.20200726071732-2d4bfdc789ad+incompatible/integration/network/ipvlan/ipvlan_test.go (about) 1 // +build !windows 2 3 package ipvlan // import "github.com/docker/docker/integration/network/ipvlan" 4 5 import ( 6 "context" 7 "os" 8 "os/exec" 9 "strings" 10 "sync" 11 "testing" 12 "time" 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/internal/test/daemon" 19 "gotest.tools/assert" 20 "gotest.tools/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 timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 176 defer cancel() 177 _, err := container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"}) 178 // FIXME(vdemeester) check the time of error ? 179 assert.Check(t, err != nil) 180 assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded) 181 182 _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 183 assert.NilError(t, err) 184 } 185 } 186 187 func testIpvlanL3NilParent(client dclient.APIClient) func(*testing.T) { 188 return func(t *testing.T) { 189 netName := "di-nil-parent-l3" 190 net.CreateNoError(context.Background(), t, client, netName, 191 net.WithIPvlan("", "l3"), 192 net.WithIPAM("172.28.230.0/24", ""), 193 net.WithIPAM("172.28.220.0/24", ""), 194 ) 195 assert.Check(t, n.IsNetworkAvailable(client, netName)) 196 197 ctx := context.Background() 198 id1 := container.Run(ctx, t, client, 199 container.WithNetworkMode(netName), 200 container.WithIPv4(netName, "172.28.220.10"), 201 ) 202 id2 := container.Run(ctx, t, client, 203 container.WithNetworkMode(netName), 204 container.WithIPv4(netName, "172.28.230.10"), 205 ) 206 207 _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 208 assert.NilError(t, err) 209 } 210 } 211 212 func testIpvlanL3InternalMode(client dclient.APIClient) func(*testing.T) { 213 return func(t *testing.T) { 214 netName := "di-internal-l3" 215 net.CreateNoError(context.Background(), t, client, netName, 216 net.WithIPvlan("", "l3"), 217 net.WithInternal(), 218 net.WithIPAM("172.28.230.0/24", ""), 219 net.WithIPAM("172.28.220.0/24", ""), 220 ) 221 assert.Check(t, n.IsNetworkAvailable(client, netName)) 222 223 ctx := context.Background() 224 id1 := container.Run(ctx, t, client, 225 container.WithNetworkMode(netName), 226 container.WithIPv4(netName, "172.28.220.10"), 227 ) 228 id2 := container.Run(ctx, t, client, 229 container.WithNetworkMode(netName), 230 container.WithIPv4(netName, "172.28.230.10"), 231 ) 232 233 timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 234 defer cancel() 235 _, err := container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"}) 236 // FIXME(vdemeester) check the time of error ? 237 assert.Check(t, err != nil) 238 assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded) 239 240 _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 241 assert.NilError(t, err) 242 } 243 } 244 245 func testIpvlanL2MultiSubnet(client dclient.APIClient) func(*testing.T) { 246 return func(t *testing.T) { 247 netName := "dualstackl2" 248 net.CreateNoError(context.Background(), t, client, netName, 249 net.WithIPvlan("", ""), 250 net.WithIPv6(), 251 net.WithIPAM("172.28.200.0/24", ""), 252 net.WithIPAM("172.28.202.0/24", "172.28.202.254"), 253 net.WithIPAM("2001:db8:abc8::/64", ""), 254 net.WithIPAM("2001:db8:abc6::/64", "2001:db8:abc6::254"), 255 ) 256 assert.Check(t, n.IsNetworkAvailable(client, netName)) 257 258 // 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 259 ctx := context.Background() 260 id1 := container.Run(ctx, t, client, 261 container.WithNetworkMode(netName), 262 container.WithIPv4(netName, "172.28.200.20"), 263 container.WithIPv6(netName, "2001:db8:abc8::20"), 264 ) 265 id2 := container.Run(ctx, t, client, 266 container.WithNetworkMode(netName), 267 container.WithIPv4(netName, "172.28.200.21"), 268 container.WithIPv6(netName, "2001:db8:abc8::21"), 269 ) 270 c1, err := client.ContainerInspect(ctx, id1) 271 assert.NilError(t, err) 272 273 // verify ipv4 connectivity to the explicit --ipv address second to first 274 _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress}) 275 assert.NilError(t, err) 276 // verify ipv6 connectivity to the explicit --ipv6 address second to first 277 _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address}) 278 assert.NilError(t, err) 279 280 // 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 281 id3 := container.Run(ctx, t, client, 282 container.WithNetworkMode(netName), 283 container.WithIPv4(netName, "172.28.202.20"), 284 container.WithIPv6(netName, "2001:db8:abc6::20"), 285 ) 286 id4 := container.Run(ctx, t, client, 287 container.WithNetworkMode(netName), 288 container.WithIPv4(netName, "172.28.202.21"), 289 container.WithIPv6(netName, "2001:db8:abc6::21"), 290 ) 291 c3, err := client.ContainerInspect(ctx, id3) 292 assert.NilError(t, err) 293 294 // verify ipv4 connectivity to the explicit --ipv address from third to fourth 295 _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress}) 296 assert.NilError(t, err) 297 // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth 298 _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address}) 299 assert.NilError(t, err) 300 301 // Inspect the v4 gateway to ensure the proper default GW was assigned 302 assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "172.28.200.1") 303 // Inspect the v6 gateway to ensure the proper default GW was assigned 304 assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc8::1") 305 // Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned 306 assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "172.28.202.254") 307 // Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned 308 assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc6::254") 309 } 310 } 311 312 func testIpvlanL3MultiSubnet(client dclient.APIClient) func(*testing.T) { 313 return func(t *testing.T) { 314 netName := "dualstackl3" 315 net.CreateNoError(context.Background(), t, client, netName, 316 net.WithIPvlan("", "l3"), 317 net.WithIPv6(), 318 net.WithIPAM("172.28.10.0/24", ""), 319 net.WithIPAM("172.28.12.0/24", "172.28.12.254"), 320 net.WithIPAM("2001:db8:abc9::/64", ""), 321 net.WithIPAM("2001:db8:abc7::/64", "2001:db8:abc7::254"), 322 ) 323 assert.Check(t, n.IsNetworkAvailable(client, netName)) 324 325 // 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 326 ctx := context.Background() 327 id1 := container.Run(ctx, t, client, 328 container.WithNetworkMode(netName), 329 container.WithIPv4(netName, "172.28.10.20"), 330 container.WithIPv6(netName, "2001:db8:abc9::20"), 331 ) 332 id2 := container.Run(ctx, t, client, 333 container.WithNetworkMode(netName), 334 container.WithIPv4(netName, "172.28.10.21"), 335 container.WithIPv6(netName, "2001:db8:abc9::21"), 336 ) 337 c1, err := client.ContainerInspect(ctx, id1) 338 assert.NilError(t, err) 339 340 // verify ipv4 connectivity to the explicit --ipv address second to first 341 _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress}) 342 assert.NilError(t, err) 343 // verify ipv6 connectivity to the explicit --ipv6 address second to first 344 _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address}) 345 assert.NilError(t, err) 346 347 // 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 348 id3 := container.Run(ctx, t, client, 349 container.WithNetworkMode(netName), 350 container.WithIPv4(netName, "172.28.12.20"), 351 container.WithIPv6(netName, "2001:db8:abc7::20"), 352 ) 353 id4 := container.Run(ctx, t, client, 354 container.WithNetworkMode(netName), 355 container.WithIPv4(netName, "172.28.12.21"), 356 container.WithIPv6(netName, "2001:db8:abc7::21"), 357 ) 358 c3, err := client.ContainerInspect(ctx, id3) 359 assert.NilError(t, err) 360 361 // verify ipv4 connectivity to the explicit --ipv address from third to fourth 362 _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress}) 363 assert.NilError(t, err) 364 // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth 365 _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address}) 366 assert.NilError(t, err) 367 368 // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode 369 assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "") 370 // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled 371 assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "") 372 // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode 373 assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "") 374 // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled 375 assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "") 376 } 377 } 378 379 func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) { 380 return func(t *testing.T) { 381 // Verify ipvlan l2 mode sets the proper default gateway routes via netlink 382 // for either an explicitly set route by the user or inferred via default IPAM 383 netNameL2 := "dualstackl2" 384 net.CreateNoError(context.Background(), t, client, netNameL2, 385 net.WithIPvlan("", "l2"), 386 net.WithIPv6(), 387 net.WithIPAM("172.28.140.0/24", "172.28.140.254"), 388 net.WithIPAM("2001:db8:abcb::/64", ""), 389 ) 390 assert.Check(t, n.IsNetworkAvailable(client, netNameL2)) 391 392 ctx := context.Background() 393 id1 := container.Run(ctx, t, client, 394 container.WithNetworkMode(netNameL2), 395 ) 396 // Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet 397 result, err := container.Exec(ctx, client, id1, []string{"ip", "route"}) 398 assert.NilError(t, err) 399 assert.Check(t, strings.Contains(result.Combined(), "default via 172.28.140.254 dev eth0")) 400 // Validate ipvlan l2 mode sets the v6 gateway to the user specified default gateway/next-hop 401 result, err = container.Exec(ctx, client, id1, []string{"ip", "-6", "route"}) 402 assert.NilError(t, err) 403 assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abcb::1 dev eth0")) 404 405 // Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops 406 netNameL3 := "dualstackl3" 407 net.CreateNoError(context.Background(), t, client, netNameL3, 408 net.WithIPvlan("", "l3"), 409 net.WithIPv6(), 410 net.WithIPAM("172.28.160.0/24", "172.28.160.254"), 411 net.WithIPAM("2001:db8:abcd::/64", "2001:db8:abcd::254"), 412 ) 413 assert.Check(t, n.IsNetworkAvailable(client, netNameL3)) 414 415 id2 := container.Run(ctx, t, client, 416 container.WithNetworkMode(netNameL3), 417 ) 418 // Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops 419 result, err = container.Exec(ctx, client, id2, []string{"ip", "route"}) 420 assert.NilError(t, err) 421 assert.Check(t, strings.Contains(result.Combined(), "default dev eth0")) 422 // Validate ipvlan l3 mode sets the v6 gateway to dev eth0 and disregards any explicit or inferred next-hops 423 result, err = container.Exec(ctx, client, id2, []string{"ip", "-6", "route"}) 424 assert.NilError(t, err) 425 assert.Check(t, strings.Contains(result.Combined(), "default dev eth0")) 426 } 427 } 428 429 var ( 430 once sync.Once 431 ipvlanSupported bool 432 ) 433 434 // figure out if ipvlan is supported by the kernel 435 func ipvlanKernelSupport(t *testing.T) bool { 436 once.Do(func() { 437 // this may have the side effect of enabling the ipvlan module 438 exec.Command("modprobe", "ipvlan").Run() 439 _, err := os.Stat("/sys/module/ipvlan") 440 if err == nil { 441 ipvlanSupported = true 442 } else if !os.IsNotExist(err) { 443 t.Logf("WARNING: ipvlanKernelSupport: stat failed: %v\n", err) 444 } 445 }) 446 447 return ipvlanSupported 448 }