github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/integration/network/ipvlan/ipvlan_test.go (about) 1 //go:build !windows 2 3 package ipvlan // import "github.com/Prakhar-Agarwal-byte/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/Prakhar-Agarwal-byte/moby/client" 14 "github.com/Prakhar-Agarwal-byte/moby/integration/internal/container" 15 net "github.com/Prakhar-Agarwal-byte/moby/integration/internal/network" 16 n "github.com/Prakhar-Agarwal-byte/moby/integration/network" 17 "github.com/Prakhar-Agarwal-byte/moby/testutil" 18 "github.com/Prakhar-Agarwal-byte/moby/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 ctx := testutil.StartSpan(baseContext, t) 29 30 d := daemon.New(t) 31 d.StartWithBusybox(ctx, t) 32 defer d.Stop(t) 33 34 // master dummy interface 'di' notation represent 'docker ipvlan' 35 master := "di-dummy0" 36 n.CreateMasterDummy(ctx, t, master) 37 defer n.DeleteInterface(ctx, t, master) 38 39 c := d.NewClientT(t) 40 41 // create a network specifying the desired sub-interface name 42 netName := "di-persist" 43 net.CreateNoError(ctx, t, c, netName, 44 net.WithIPvlan("di-dummy0.70", ""), 45 ) 46 47 assert.Check(t, n.IsNetworkAvailable(ctx, c, netName)) 48 // Restart docker daemon to test the config has persisted to disk 49 d.Restart(t) 50 assert.Check(t, n.IsNetworkAvailable(ctx, c, netName)) 51 } 52 53 func TestDockerNetworkIpvlan(t *testing.T) { 54 skip.If(t, testEnv.IsRemoteDaemon) 55 skip.If(t, !ipvlanKernelSupport(t), "Kernel doesn't support ipvlan") 56 57 ctx := testutil.StartSpan(baseContext, t) 58 59 for _, tc := range []struct { 60 name string 61 test func(context.Context, dclient.APIClient) func(*testing.T) 62 }{ 63 { 64 name: "Subinterface", 65 test: testIpvlanSubinterface, 66 }, { 67 name: "OverlapParent", 68 test: testIpvlanOverlapParent, 69 }, { 70 name: "L2NilParent", 71 test: testIpvlanL2NilParent, 72 }, { 73 name: "L2InternalMode", 74 test: testIpvlanL2InternalMode, 75 }, { 76 name: "L3NilParent", 77 test: testIpvlanL3NilParent, 78 }, { 79 name: "L3InternalMode", 80 test: testIpvlanL3InternalMode, 81 }, { 82 name: "L2MultiSubnet", 83 test: testIpvlanL2MultiSubnet, 84 }, { 85 name: "L3MultiSubnet", 86 test: testIpvlanL3MultiSubnet, 87 }, { 88 name: "Addressing", 89 test: testIpvlanAddressing, 90 }, 91 } { 92 93 t.Run(tc.name, func(t *testing.T) { 94 testutil.StartSpan(ctx, t) 95 d := daemon.New(t) 96 t.Cleanup(func() { d.Stop(t) }) 97 d.StartWithBusybox(ctx, t) 98 c := d.NewClientT(t) 99 tc.test(ctx, c) 100 }) 101 102 // FIXME(vdemeester) clean network 103 } 104 } 105 106 func testIpvlanSubinterface(ctx context.Context, client dclient.APIClient) func(*testing.T) { 107 return func(t *testing.T) { 108 master := "di-dummy0" 109 n.CreateMasterDummy(ctx, t, master) 110 defer n.DeleteInterface(ctx, t, master) 111 112 netName := "di-subinterface" 113 net.CreateNoError(ctx, t, client, netName, 114 net.WithIPvlan("di-dummy0.60", ""), 115 ) 116 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 117 118 // delete the network while preserving the parent link 119 err := client.NetworkRemove(ctx, netName) 120 assert.NilError(t, err) 121 122 assert.Check(t, n.IsNetworkNotAvailable(ctx, client, netName)) 123 // verify the network delete did not delete the predefined link 124 n.LinkExists(ctx, t, "di-dummy0") 125 } 126 } 127 128 func testIpvlanOverlapParent(ctx context.Context, client dclient.APIClient) func(*testing.T) { 129 return func(t *testing.T) { 130 // verify the same parent interface cannot be used if already in use by an existing network 131 master := "di-dummy0" 132 parent := master + ".30" 133 n.CreateMasterDummy(ctx, t, master) 134 defer n.DeleteInterface(ctx, t, master) 135 n.CreateVlanInterface(ctx, t, master, parent, "30") 136 137 netName := "di-subinterface" 138 net.CreateNoError(ctx, t, client, netName, 139 net.WithIPvlan(parent, ""), 140 ) 141 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 142 143 _, err := net.Create(ctx, client, netName, 144 net.WithIPvlan(parent, ""), 145 ) 146 // verify that the overlap returns an error 147 assert.Check(t, err != nil) 148 } 149 } 150 151 func testIpvlanL2NilParent(ctx context.Context, client dclient.APIClient) func(*testing.T) { 152 return func(t *testing.T) { 153 // ipvlan l2 mode - dummy parent interface is provisioned dynamically 154 netName := "di-nil-parent" 155 net.CreateNoError(ctx, t, client, netName, 156 net.WithIPvlan("", ""), 157 ) 158 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 159 160 id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 161 id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 162 163 _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 164 assert.NilError(t, err) 165 } 166 } 167 168 func testIpvlanL2InternalMode(ctx context.Context, client dclient.APIClient) func(*testing.T) { 169 return func(t *testing.T) { 170 netName := "di-internal" 171 net.CreateNoError(ctx, t, client, netName, 172 net.WithIPvlan("", ""), 173 net.WithInternal(), 174 ) 175 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 176 177 id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 178 id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName)) 179 180 result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"}) 181 assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable")) 182 183 _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 184 assert.NilError(t, err) 185 } 186 } 187 188 func testIpvlanL3NilParent(ctx context.Context, client dclient.APIClient) func(*testing.T) { 189 return func(t *testing.T) { 190 netName := "di-nil-parent-l3" 191 net.CreateNoError(ctx, t, client, netName, 192 net.WithIPvlan("", "l3"), 193 net.WithIPAM("172.28.230.0/24", ""), 194 net.WithIPAM("172.28.220.0/24", ""), 195 ) 196 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 197 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(ctx context.Context, client dclient.APIClient) func(*testing.T) { 213 return func(t *testing.T) { 214 netName := "di-internal-l3" 215 net.CreateNoError(ctx, 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(ctx, client, netName)) 222 223 id1 := container.Run(ctx, t, client, 224 container.WithNetworkMode(netName), 225 container.WithIPv4(netName, "172.28.220.10"), 226 ) 227 id2 := container.Run(ctx, t, client, 228 container.WithNetworkMode(netName), 229 container.WithIPv4(netName, "172.28.230.10"), 230 ) 231 232 result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"}) 233 assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable")) 234 235 _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1}) 236 assert.NilError(t, err) 237 } 238 } 239 240 func testIpvlanL2MultiSubnet(ctx context.Context, client dclient.APIClient) func(*testing.T) { 241 return func(t *testing.T) { 242 netName := "dualstackl2" 243 net.CreateNoError(ctx, t, client, netName, 244 net.WithIPvlan("", ""), 245 net.WithIPv6(), 246 net.WithIPAM("172.28.200.0/24", ""), 247 net.WithIPAM("172.28.202.0/24", "172.28.202.254"), 248 net.WithIPAM("2001:db8:abc8::/64", ""), 249 net.WithIPAM("2001:db8:abc6::/64", "2001:db8:abc6::254"), 250 ) 251 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 252 253 // 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 254 id1 := container.Run(ctx, t, client, 255 container.WithNetworkMode(netName), 256 container.WithIPv4(netName, "172.28.200.20"), 257 container.WithIPv6(netName, "2001:db8:abc8::20"), 258 ) 259 id2 := container.Run(ctx, t, client, 260 container.WithNetworkMode(netName), 261 container.WithIPv4(netName, "172.28.200.21"), 262 container.WithIPv6(netName, "2001:db8:abc8::21"), 263 ) 264 c1, err := client.ContainerInspect(ctx, id1) 265 assert.NilError(t, err) 266 267 // verify ipv4 connectivity to the explicit --ipv address second to first 268 _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress}) 269 assert.NilError(t, err) 270 // verify ipv6 connectivity to the explicit --ipv6 address second to first 271 _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address}) 272 assert.NilError(t, err) 273 274 // 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 275 id3 := container.Run(ctx, t, client, 276 container.WithNetworkMode(netName), 277 container.WithIPv4(netName, "172.28.202.20"), 278 container.WithIPv6(netName, "2001:db8:abc6::20"), 279 ) 280 id4 := container.Run(ctx, t, client, 281 container.WithNetworkMode(netName), 282 container.WithIPv4(netName, "172.28.202.21"), 283 container.WithIPv6(netName, "2001:db8:abc6::21"), 284 ) 285 c3, err := client.ContainerInspect(ctx, id3) 286 assert.NilError(t, err) 287 288 // verify ipv4 connectivity to the explicit --ipv address from third to fourth 289 _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress}) 290 assert.NilError(t, err) 291 // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth 292 _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address}) 293 assert.NilError(t, err) 294 295 // Inspect the v4 gateway to ensure the proper default GW was assigned 296 assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "172.28.200.1") 297 // Inspect the v6 gateway to ensure the proper default GW was assigned 298 assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc8::1") 299 // Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned 300 assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "172.28.202.254") 301 // Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned 302 assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc6::254") 303 } 304 } 305 306 func testIpvlanL3MultiSubnet(ctx context.Context, client dclient.APIClient) func(*testing.T) { 307 return func(t *testing.T) { 308 netName := "dualstackl3" 309 net.CreateNoError(ctx, t, client, netName, 310 net.WithIPvlan("", "l3"), 311 net.WithIPv6(), 312 net.WithIPAM("172.28.10.0/24", ""), 313 net.WithIPAM("172.28.12.0/24", "172.28.12.254"), 314 net.WithIPAM("2001:db8:abc9::/64", ""), 315 net.WithIPAM("2001:db8:abc7::/64", "2001:db8:abc7::254"), 316 ) 317 assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) 318 319 // 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 320 id1 := container.Run(ctx, t, client, 321 container.WithNetworkMode(netName), 322 container.WithIPv4(netName, "172.28.10.20"), 323 container.WithIPv6(netName, "2001:db8:abc9::20"), 324 ) 325 id2 := container.Run(ctx, t, client, 326 container.WithNetworkMode(netName), 327 container.WithIPv4(netName, "172.28.10.21"), 328 container.WithIPv6(netName, "2001:db8:abc9::21"), 329 ) 330 c1, err := client.ContainerInspect(ctx, id1) 331 assert.NilError(t, err) 332 333 // verify ipv4 connectivity to the explicit --ipv address second to first 334 _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress}) 335 assert.NilError(t, err) 336 // verify ipv6 connectivity to the explicit --ipv6 address second to first 337 _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address}) 338 assert.NilError(t, err) 339 340 // 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 341 id3 := container.Run(ctx, t, client, 342 container.WithNetworkMode(netName), 343 container.WithIPv4(netName, "172.28.12.20"), 344 container.WithIPv6(netName, "2001:db8:abc7::20"), 345 ) 346 id4 := container.Run(ctx, t, client, 347 container.WithNetworkMode(netName), 348 container.WithIPv4(netName, "172.28.12.21"), 349 container.WithIPv6(netName, "2001:db8:abc7::21"), 350 ) 351 c3, err := client.ContainerInspect(ctx, id3) 352 assert.NilError(t, err) 353 354 // verify ipv4 connectivity to the explicit --ipv address from third to fourth 355 _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress}) 356 assert.NilError(t, err) 357 // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth 358 _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address}) 359 assert.NilError(t, err) 360 361 // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode 362 assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "") 363 // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled 364 assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "") 365 // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode 366 assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "") 367 // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled 368 assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "") 369 } 370 } 371 372 func testIpvlanAddressing(ctx context.Context, client dclient.APIClient) func(*testing.T) { 373 return func(t *testing.T) { 374 // Verify ipvlan l2 mode sets the proper default gateway routes via netlink 375 // for either an explicitly set route by the user or inferred via default IPAM 376 netNameL2 := "dualstackl2" 377 net.CreateNoError(ctx, t, client, netNameL2, 378 net.WithIPvlan("", "l2"), 379 net.WithIPv6(), 380 net.WithIPAM("172.28.140.0/24", "172.28.140.254"), 381 net.WithIPAM("2001:db8:abcb::/64", ""), 382 ) 383 assert.Check(t, n.IsNetworkAvailable(ctx, client, netNameL2)) 384 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(ctx, 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(ctx, 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 }