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