github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/integration/network/ipvlan/ipvlan_test.go (about)

     1  package ipvlan
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/docker/docker/api/types"
     9  	"github.com/docker/docker/api/types/network"
    10  	dclient "github.com/docker/docker/client"
    11  	"github.com/docker/docker/integration/internal/container"
    12  	n "github.com/docker/docker/integration/network"
    13  	"github.com/docker/docker/internal/test/daemon"
    14  	"github.com/gotestyourself/gotestyourself/assert"
    15  	"github.com/gotestyourself/gotestyourself/skip"
    16  	"golang.org/x/net/context"
    17  )
    18  
    19  func TestDockerNetworkIpvlanPersistance(t *testing.T) {
    20  	// verify the driver automatically provisions the 802.1q link (di-dummy0.70)
    21  	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
    22  	skip.If(t, testEnv.IsRemoteDaemon())
    23  	skip.If(t, !ipvlanKernelSupport(), "Kernel doesn't support ipvlan")
    24  
    25  	d := daemon.New(t, daemon.WithExperimental)
    26  	d.StartWithBusybox(t)
    27  	defer d.Stop(t)
    28  
    29  	// master dummy interface 'di' notation represent 'docker ipvlan'
    30  	master := "di-dummy0"
    31  	n.CreateMasterDummy(t, master)
    32  	defer n.DeleteInterface(t, master)
    33  
    34  	client, err := d.NewClient()
    35  	assert.NilError(t, err)
    36  
    37  	// create a network specifying the desired sub-interface name
    38  	_, err = client.NetworkCreate(context.Background(), "di-persist", types.NetworkCreate{
    39  		Driver: "ipvlan",
    40  		Options: map[string]string{
    41  			"parent": "di-dummy0.70",
    42  		},
    43  	})
    44  	assert.NilError(t, err)
    45  	assert.Check(t, n.IsNetworkAvailable(client, "di-persist"))
    46  	// Restart docker daemon to test the config has persisted to disk
    47  	d.Restart(t)
    48  	assert.Check(t, n.IsNetworkAvailable(client, "di-persist"))
    49  }
    50  
    51  func TestDockerNetworkIpvlan(t *testing.T) {
    52  	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
    53  	skip.If(t, testEnv.IsRemoteDaemon())
    54  	skip.If(t, !ipvlanKernelSupport(), "Kernel doesn't support ipvlan")
    55  
    56  	for _, tc := range []struct {
    57  		name string
    58  		test func(dclient.APIClient) func(*testing.T)
    59  	}{
    60  		{
    61  			name: "Subinterface",
    62  			test: testIpvlanSubinterface,
    63  		}, {
    64  			name: "OverlapParent",
    65  			test: testIpvlanOverlapParent,
    66  		}, {
    67  			name: "L2NilParent",
    68  			test: testIpvlanL2NilParent,
    69  		}, {
    70  			name: "L2InternalMode",
    71  			test: testIpvlanL2InternalMode,
    72  		}, {
    73  			name: "L3NilParent",
    74  			test: testIpvlanL3NilParent,
    75  		}, {
    76  			name: "L3InternalMode",
    77  			test: testIpvlanL3InternalMode,
    78  		}, {
    79  			name: "L2MultiSubnet",
    80  			test: testIpvlanL2MultiSubnet,
    81  		}, {
    82  			name: "L3MultiSubnet",
    83  			test: testIpvlanL3MultiSubnet,
    84  		}, {
    85  			name: "Addressing",
    86  			test: testIpvlanAddressing,
    87  		},
    88  	} {
    89  		d := daemon.New(t, daemon.WithExperimental)
    90  		d.StartWithBusybox(t)
    91  
    92  		client, err := d.NewClient()
    93  		assert.NilError(t, err)
    94  
    95  		t.Run(tc.name, tc.test(client))
    96  
    97  		d.Stop(t)
    98  		// FIXME(vdemeester) clean network
    99  	}
   100  }
   101  
   102  func testIpvlanSubinterface(client dclient.APIClient) func(*testing.T) {
   103  	return func(t *testing.T) {
   104  		master := "di-dummy0"
   105  		n.CreateMasterDummy(t, master)
   106  		defer n.DeleteInterface(t, master)
   107  
   108  		_, err := client.NetworkCreate(context.Background(), "di-subinterface", types.NetworkCreate{
   109  			Driver: "ipvlan",
   110  			Options: map[string]string{
   111  				"parent": "di-dummy0.60",
   112  			},
   113  		})
   114  		assert.NilError(t, err)
   115  		assert.Check(t, n.IsNetworkAvailable(client, "di-subinterface"))
   116  
   117  		// delete the network while preserving the parent link
   118  		err = client.NetworkRemove(context.Background(), "di-subinterface")
   119  		assert.NilError(t, err)
   120  
   121  		assert.Check(t, n.IsNetworkNotAvailable(client, "di-subinterface"))
   122  		// verify the network delete did not delete the predefined link
   123  		n.LinkExists(t, "di-dummy0")
   124  	}
   125  }
   126  
   127  func testIpvlanOverlapParent(client dclient.APIClient) func(*testing.T) {
   128  	return func(t *testing.T) {
   129  		// verify the same parent interface cannot be used if already in use by an existing network
   130  		master := "di-dummy0"
   131  		n.CreateMasterDummy(t, master)
   132  		defer n.DeleteInterface(t, master)
   133  		n.CreateVlanInterface(t, master, "di-dummy0.30", "30")
   134  
   135  		_, err := client.NetworkCreate(context.Background(), "di-subinterface", types.NetworkCreate{
   136  			Driver: "ipvlan",
   137  			Options: map[string]string{
   138  				"parent": "di-dummy0.30",
   139  			},
   140  		})
   141  		assert.NilError(t, err)
   142  		assert.Check(t, n.IsNetworkAvailable(client, "di-subinterface"))
   143  
   144  		_, err = client.NetworkCreate(context.Background(), "di-subinterface", types.NetworkCreate{
   145  			Driver: "ipvlan",
   146  			Options: map[string]string{
   147  				"parent": "di-dummy0.30",
   148  			},
   149  		})
   150  		// verify that the overlap returns an error
   151  		assert.Check(t, err != nil)
   152  	}
   153  }
   154  
   155  func testIpvlanL2NilParent(client dclient.APIClient) func(*testing.T) {
   156  	return func(t *testing.T) {
   157  		// ipvlan l2 mode - dummy parent interface is provisioned dynamically
   158  		_, err := client.NetworkCreate(context.Background(), "di-nil-parent", types.NetworkCreate{
   159  			Driver: "ipvlan",
   160  		})
   161  		assert.NilError(t, err)
   162  		assert.Check(t, n.IsNetworkAvailable(client, "di-nil-parent"))
   163  
   164  		ctx := context.Background()
   165  		id1 := container.Run(t, ctx, client, container.WithNetworkMode("di-nil-parent"))
   166  		id2 := container.Run(t, ctx, client, container.WithNetworkMode("di-nil-parent"))
   167  
   168  		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
   169  		assert.NilError(t, err)
   170  	}
   171  }
   172  
   173  func testIpvlanL2InternalMode(client dclient.APIClient) func(*testing.T) {
   174  	return func(t *testing.T) {
   175  		_, err := client.NetworkCreate(context.Background(), "di-internal", types.NetworkCreate{
   176  			Driver:   "ipvlan",
   177  			Internal: true,
   178  		})
   179  		assert.NilError(t, err)
   180  		assert.Check(t, n.IsNetworkAvailable(client, "di-internal"))
   181  
   182  		ctx := context.Background()
   183  		id1 := container.Run(t, ctx, client, container.WithNetworkMode("di-internal"))
   184  		id2 := container.Run(t, ctx, client, container.WithNetworkMode("di-internal"))
   185  
   186  		timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   187  		defer cancel()
   188  		_, err = container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
   189  		// FIXME(vdemeester) check the time of error ?
   190  		assert.Check(t, err != nil)
   191  		assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
   192  
   193  		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
   194  		assert.NilError(t, err)
   195  	}
   196  }
   197  
   198  func testIpvlanL3NilParent(client dclient.APIClient) func(*testing.T) {
   199  	return func(t *testing.T) {
   200  		_, err := client.NetworkCreate(context.Background(), "di-nil-parent-l3", types.NetworkCreate{
   201  			Driver: "ipvlan",
   202  			Options: map[string]string{
   203  				"ipvlan_mode": "l3",
   204  			},
   205  			IPAM: &network.IPAM{
   206  				Config: []network.IPAMConfig{
   207  					{
   208  						Subnet:     "172.28.230.0/24",
   209  						AuxAddress: map[string]string{},
   210  					},
   211  					{
   212  						Subnet:     "172.28.220.0/24",
   213  						AuxAddress: map[string]string{},
   214  					},
   215  				},
   216  			},
   217  		})
   218  		assert.NilError(t, err)
   219  		assert.Check(t, n.IsNetworkAvailable(client, "di-nil-parent-l3"))
   220  
   221  		ctx := context.Background()
   222  		id1 := container.Run(t, ctx, client,
   223  			container.WithNetworkMode("di-nil-parent-l3"),
   224  			container.WithIPv4("di-nil-parent-l3", "172.28.220.10"),
   225  		)
   226  		id2 := container.Run(t, ctx, client,
   227  			container.WithNetworkMode("di-nil-parent-l3"),
   228  			container.WithIPv4("di-nil-parent-l3", "172.28.230.10"),
   229  		)
   230  
   231  		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
   232  		assert.NilError(t, err)
   233  	}
   234  }
   235  
   236  func testIpvlanL3InternalMode(client dclient.APIClient) func(*testing.T) {
   237  	return func(t *testing.T) {
   238  		_, err := client.NetworkCreate(context.Background(), "di-internal-l3", types.NetworkCreate{
   239  			Driver:   "ipvlan",
   240  			Internal: true,
   241  			Options: map[string]string{
   242  				"ipvlan_mode": "l3",
   243  			},
   244  			IPAM: &network.IPAM{
   245  				Config: []network.IPAMConfig{
   246  					{
   247  						Subnet:     "172.28.230.0/24",
   248  						AuxAddress: map[string]string{},
   249  					},
   250  					{
   251  						Subnet:     "172.28.220.0/24",
   252  						AuxAddress: map[string]string{},
   253  					},
   254  				},
   255  			},
   256  		})
   257  		assert.NilError(t, err)
   258  		assert.Check(t, n.IsNetworkAvailable(client, "di-internal-l3"))
   259  
   260  		ctx := context.Background()
   261  		id1 := container.Run(t, ctx, client,
   262  			container.WithNetworkMode("di-internal-l3"),
   263  			container.WithIPv4("di-internal-l3", "172.28.220.10"),
   264  		)
   265  		id2 := container.Run(t, ctx, client,
   266  			container.WithNetworkMode("di-internal-l3"),
   267  			container.WithIPv4("di-internal-l3", "172.28.230.10"),
   268  		)
   269  
   270  		timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   271  		defer cancel()
   272  		_, err = container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
   273  		// FIXME(vdemeester) check the time of error ?
   274  		assert.Check(t, err != nil)
   275  		assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
   276  
   277  		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
   278  		assert.NilError(t, err)
   279  	}
   280  }
   281  
   282  func testIpvlanL2MultiSubnet(client dclient.APIClient) func(*testing.T) {
   283  	return func(t *testing.T) {
   284  		_, err := client.NetworkCreate(context.Background(), "dualstackl2", types.NetworkCreate{
   285  			Driver:     "ipvlan",
   286  			EnableIPv6: true,
   287  			IPAM: &network.IPAM{
   288  				Config: []network.IPAMConfig{
   289  					{
   290  						Subnet:     "172.28.200.0/24",
   291  						AuxAddress: map[string]string{},
   292  					},
   293  					{
   294  						Subnet:     "172.28.202.0/24",
   295  						Gateway:    "172.28.202.254",
   296  						AuxAddress: map[string]string{},
   297  					},
   298  					{
   299  						Subnet:     "2001:db8:abc8::/64",
   300  						AuxAddress: map[string]string{},
   301  					},
   302  					{
   303  						Subnet:     "2001:db8:abc6::/64",
   304  						Gateway:    "2001:db8:abc6::254",
   305  						AuxAddress: map[string]string{},
   306  					},
   307  				},
   308  			},
   309  		})
   310  		assert.NilError(t, err)
   311  		assert.Check(t, n.IsNetworkAvailable(client, "dualstackl2"))
   312  
   313  		// 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
   314  		ctx := context.Background()
   315  		id1 := container.Run(t, ctx, client,
   316  			container.WithNetworkMode("dualstackl2"),
   317  			container.WithIPv4("dualstackl2", "172.28.200.20"),
   318  			container.WithIPv6("dualstackl2", "2001:db8:abc8::20"),
   319  		)
   320  		id2 := container.Run(t, ctx, client,
   321  			container.WithNetworkMode("dualstackl2"),
   322  			container.WithIPv4("dualstackl2", "172.28.200.21"),
   323  			container.WithIPv6("dualstackl2", "2001:db8:abc8::21"),
   324  		)
   325  		c1, err := client.ContainerInspect(ctx, id1)
   326  		assert.NilError(t, err)
   327  
   328  		// verify ipv4 connectivity to the explicit --ipv address second to first
   329  		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackl2"].IPAddress})
   330  		assert.NilError(t, err)
   331  		// verify ipv6 connectivity to the explicit --ipv6 address second to first
   332  		_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackl2"].GlobalIPv6Address})
   333  		assert.NilError(t, err)
   334  
   335  		// 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
   336  		id3 := container.Run(t, ctx, client,
   337  			container.WithNetworkMode("dualstackl2"),
   338  			container.WithIPv4("dualstackl2", "172.28.202.20"),
   339  			container.WithIPv6("dualstackl2", "2001:db8:abc6::20"),
   340  		)
   341  		id4 := container.Run(t, ctx, client,
   342  			container.WithNetworkMode("dualstackl2"),
   343  			container.WithIPv4("dualstackl2", "172.28.202.21"),
   344  			container.WithIPv6("dualstackl2", "2001:db8:abc6::21"),
   345  		)
   346  		c3, err := client.ContainerInspect(ctx, id3)
   347  		assert.NilError(t, err)
   348  
   349  		// verify ipv4 connectivity to the explicit --ipv address from third to fourth
   350  		_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackl2"].IPAddress})
   351  		assert.NilError(t, err)
   352  		// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
   353  		_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackl2"].GlobalIPv6Address})
   354  		assert.NilError(t, err)
   355  
   356  		// Inspect the v4 gateway to ensure the proper default GW was assigned
   357  		assert.Equal(t, c1.NetworkSettings.Networks["dualstackl2"].Gateway, "172.28.200.1")
   358  		// Inspect the v6 gateway to ensure the proper default GW was assigned
   359  		assert.Equal(t, c1.NetworkSettings.Networks["dualstackl2"].IPv6Gateway, "2001:db8:abc8::1")
   360  		// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
   361  		assert.Equal(t, c3.NetworkSettings.Networks["dualstackl2"].Gateway, "172.28.202.254")
   362  		// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
   363  		assert.Equal(t, c3.NetworkSettings.Networks["dualstackl2"].IPv6Gateway, "2001:db8:abc6::254")
   364  	}
   365  }
   366  
   367  func testIpvlanL3MultiSubnet(client dclient.APIClient) func(*testing.T) {
   368  	return func(t *testing.T) {
   369  		_, err := client.NetworkCreate(context.Background(), "dualstackl3", types.NetworkCreate{
   370  			Driver:     "ipvlan",
   371  			EnableIPv6: true,
   372  			Options: map[string]string{
   373  				"ipvlan_mode": "l3",
   374  			},
   375  			IPAM: &network.IPAM{
   376  				Config: []network.IPAMConfig{
   377  					{
   378  						Subnet:     "172.28.10.0/24",
   379  						AuxAddress: map[string]string{},
   380  					},
   381  					{
   382  						Subnet:     "172.28.12.0/24",
   383  						Gateway:    "172.28.12.254",
   384  						AuxAddress: map[string]string{},
   385  					},
   386  					{
   387  						Subnet:     "2001:db8:abc9::/64",
   388  						AuxAddress: map[string]string{},
   389  					},
   390  					{
   391  						Subnet:     "2001:db8:abc7::/64",
   392  						Gateway:    "2001:db8:abc7::254",
   393  						AuxAddress: map[string]string{},
   394  					},
   395  				},
   396  			},
   397  		})
   398  		assert.NilError(t, err)
   399  		assert.Check(t, n.IsNetworkAvailable(client, "dualstackl3"))
   400  
   401  		// 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
   402  		ctx := context.Background()
   403  		id1 := container.Run(t, ctx, client,
   404  			container.WithNetworkMode("dualstackl3"),
   405  			container.WithIPv4("dualstackl3", "172.28.10.20"),
   406  			container.WithIPv6("dualstackl3", "2001:db8:abc9::20"),
   407  		)
   408  		id2 := container.Run(t, ctx, client,
   409  			container.WithNetworkMode("dualstackl3"),
   410  			container.WithIPv4("dualstackl3", "172.28.10.21"),
   411  			container.WithIPv6("dualstackl3", "2001:db8:abc9::21"),
   412  		)
   413  		c1, err := client.ContainerInspect(ctx, id1)
   414  		assert.NilError(t, err)
   415  
   416  		// verify ipv4 connectivity to the explicit --ipv address second to first
   417  		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackl3"].IPAddress})
   418  		assert.NilError(t, err)
   419  		// verify ipv6 connectivity to the explicit --ipv6 address second to first
   420  		_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackl3"].GlobalIPv6Address})
   421  		assert.NilError(t, err)
   422  
   423  		// 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
   424  		id3 := container.Run(t, ctx, client,
   425  			container.WithNetworkMode("dualstackl3"),
   426  			container.WithIPv4("dualstackl3", "172.28.12.20"),
   427  			container.WithIPv6("dualstackl3", "2001:db8:abc7::20"),
   428  		)
   429  		id4 := container.Run(t, ctx, client,
   430  			container.WithNetworkMode("dualstackl3"),
   431  			container.WithIPv4("dualstackl3", "172.28.12.21"),
   432  			container.WithIPv6("dualstackl3", "2001:db8:abc7::21"),
   433  		)
   434  		c3, err := client.ContainerInspect(ctx, id3)
   435  		assert.NilError(t, err)
   436  
   437  		// verify ipv4 connectivity to the explicit --ipv address from third to fourth
   438  		_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackl3"].IPAddress})
   439  		assert.NilError(t, err)
   440  		// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
   441  		_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackl3"].GlobalIPv6Address})
   442  		assert.NilError(t, err)
   443  
   444  		// Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
   445  		assert.Equal(t, c1.NetworkSettings.Networks["dualstackl3"].Gateway, "")
   446  		// Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
   447  		assert.Equal(t, c1.NetworkSettings.Networks["dualstackl3"].IPv6Gateway, "")
   448  		// Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
   449  		assert.Equal(t, c3.NetworkSettings.Networks["dualstackl3"].Gateway, "")
   450  		// Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
   451  		assert.Equal(t, c3.NetworkSettings.Networks["dualstackl3"].IPv6Gateway, "")
   452  	}
   453  }
   454  
   455  func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) {
   456  	return func(t *testing.T) {
   457  		// Verify ipvlan l2 mode sets the proper default gateway routes via netlink
   458  		// for either an explicitly set route by the user or inferred via default IPAM
   459  		_, err := client.NetworkCreate(context.Background(), "dualstackl2", types.NetworkCreate{
   460  			Driver:     "ipvlan",
   461  			EnableIPv6: true,
   462  			Options: map[string]string{
   463  				"ipvlan_mode": "l2",
   464  			},
   465  			IPAM: &network.IPAM{
   466  				Config: []network.IPAMConfig{
   467  					{
   468  						Subnet:     "172.28.140.0/24",
   469  						Gateway:    "172.28.140.254",
   470  						AuxAddress: map[string]string{},
   471  					},
   472  					{
   473  						Subnet:     "2001:db8:abcb::/64",
   474  						AuxAddress: map[string]string{},
   475  					},
   476  				},
   477  			},
   478  		})
   479  		assert.NilError(t, err)
   480  		assert.Check(t, n.IsNetworkAvailable(client, "dualstackl2"))
   481  
   482  		ctx := context.Background()
   483  		id1 := container.Run(t, ctx, client,
   484  			container.WithNetworkMode("dualstackl2"),
   485  		)
   486  		// Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet
   487  		result, err := container.Exec(ctx, client, id1, []string{"ip", "route"})
   488  		assert.NilError(t, err)
   489  		assert.Check(t, strings.Contains(result.Combined(), "default via 172.28.140.254 dev eth0"))
   490  		// Validate ipvlan l2 mode sets the v6 gateway to the user specified default gateway/next-hop
   491  		result, err = container.Exec(ctx, client, id1, []string{"ip", "-6", "route"})
   492  		assert.NilError(t, err)
   493  		assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abcb::1 dev eth0"))
   494  
   495  		// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
   496  		_, err = client.NetworkCreate(context.Background(), "dualstackl3", types.NetworkCreate{
   497  			Driver:     "ipvlan",
   498  			EnableIPv6: true,
   499  			Options: map[string]string{
   500  				"ipvlan_mode": "l3",
   501  			},
   502  			IPAM: &network.IPAM{
   503  				Config: []network.IPAMConfig{
   504  					{
   505  						Subnet:     "172.28.160.0/24",
   506  						Gateway:    "172.28.160.254",
   507  						AuxAddress: map[string]string{},
   508  					},
   509  					{
   510  						Subnet:     "2001:db8:abcd::/64",
   511  						Gateway:    "2001:db8:abcd::254",
   512  						AuxAddress: map[string]string{},
   513  					},
   514  				},
   515  			},
   516  		})
   517  		assert.NilError(t, err)
   518  		assert.Check(t, n.IsNetworkAvailable(client, "dualstackl3"))
   519  
   520  		id2 := container.Run(t, ctx, client,
   521  			container.WithNetworkMode("dualstackl3"),
   522  		)
   523  		// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
   524  		result, err = container.Exec(ctx, client, id2, []string{"ip", "route"})
   525  		assert.NilError(t, err)
   526  		assert.Check(t, strings.Contains(result.Combined(), "default dev eth0"))
   527  		// Validate ipvlan l3 mode sets the v6 gateway to dev eth0 and disregards any explicit or inferred next-hops
   528  		result, err = container.Exec(ctx, client, id2, []string{"ip", "-6", "route"})
   529  		assert.NilError(t, err)
   530  		assert.Check(t, strings.Contains(result.Combined(), "default dev eth0"))
   531  	}
   532  }
   533  
   534  // ensure Kernel version is >= v4.2 for ipvlan support
   535  func ipvlanKernelSupport() bool {
   536  	return n.CheckKernelMajorVersionGreaterOrEqualThen(4, 2)
   537  }