github.com/khulnasoft-lab/khulnasoft@v26.0.1-0.20240328202558-330a6f959fe0+incompatible/integration/network/ipvlan/ipvlan_test.go (about)

     1  //go: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  
    13  	dclient "github.com/docker/docker/client"
    14  	"github.com/docker/docker/integration/internal/container"
    15  	net "github.com/docker/docker/integration/internal/network"
    16  	n "github.com/docker/docker/integration/network"
    17  	"github.com/docker/docker/testutil"
    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  	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(*testing.T, context.Context, dclient.APIClient)
    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(t, ctx, c)
   100  		})
   101  
   102  		// FIXME(vdemeester) clean network
   103  	}
   104  }
   105  
   106  func testIpvlanSubinterface(t *testing.T, ctx context.Context, client dclient.APIClient) {
   107  	master := "di-dummy0"
   108  	n.CreateMasterDummy(ctx, t, master)
   109  	defer n.DeleteInterface(ctx, t, master)
   110  
   111  	netName := "di-subinterface"
   112  	net.CreateNoError(ctx, t, client, netName,
   113  		net.WithIPvlan("di-dummy0.60", ""),
   114  	)
   115  	assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   116  
   117  	// delete the network while preserving the parent link
   118  	err := client.NetworkRemove(ctx, netName)
   119  	assert.NilError(t, err)
   120  
   121  	assert.Check(t, n.IsNetworkNotAvailable(ctx, client, netName))
   122  	// verify the network delete did not delete the predefined link
   123  	n.LinkExists(ctx, t, "di-dummy0")
   124  }
   125  
   126  func testIpvlanOverlapParent(t *testing.T, ctx context.Context, client dclient.APIClient) {
   127  	// verify the same parent interface cannot be used if already in use by an existing network
   128  	master := "di-dummy0"
   129  	parent := master + ".30"
   130  	n.CreateMasterDummy(ctx, t, master)
   131  	defer n.DeleteInterface(ctx, t, master)
   132  	n.CreateVlanInterface(ctx, t, master, parent, "30")
   133  
   134  	netName := "di-subinterface"
   135  	net.CreateNoError(ctx, t, client, netName,
   136  		net.WithIPvlan(parent, ""),
   137  	)
   138  	assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   139  
   140  	_, err := net.Create(ctx, client, netName,
   141  		net.WithIPvlan(parent, ""),
   142  	)
   143  	// verify that the overlap returns an error
   144  	assert.Check(t, err != nil)
   145  }
   146  
   147  func testIpvlanL2NilParent(t *testing.T, ctx context.Context, client dclient.APIClient) {
   148  	// ipvlan l2 mode - dummy parent interface is provisioned dynamically
   149  	netName := "di-nil-parent"
   150  	net.CreateNoError(ctx, t, client, netName,
   151  		net.WithIPvlan("", ""),
   152  	)
   153  	assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   154  
   155  	id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
   156  	id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
   157  
   158  	_, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
   159  	assert.NilError(t, err)
   160  }
   161  
   162  func testIpvlanL2InternalMode(t *testing.T, ctx context.Context, client dclient.APIClient) {
   163  	netName := "di-internal"
   164  	net.CreateNoError(ctx, t, client, netName,
   165  		net.WithIPvlan("", ""),
   166  		net.WithInternal(),
   167  	)
   168  	assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   169  
   170  	id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
   171  	id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
   172  
   173  	result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"})
   174  	assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable"))
   175  
   176  	_, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
   177  	assert.NilError(t, err)
   178  }
   179  
   180  func testIpvlanL3NilParent(t *testing.T, ctx context.Context, client dclient.APIClient) {
   181  	netName := "di-nil-parent-l3"
   182  	net.CreateNoError(ctx, t, client, netName,
   183  		net.WithIPvlan("", "l3"),
   184  		net.WithIPAM("172.28.230.0/24", ""),
   185  		net.WithIPAM("172.28.220.0/24", ""),
   186  	)
   187  	assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   188  
   189  	id1 := container.Run(ctx, t, client,
   190  		container.WithNetworkMode(netName),
   191  		container.WithIPv4(netName, "172.28.220.10"),
   192  	)
   193  	id2 := container.Run(ctx, t, client,
   194  		container.WithNetworkMode(netName),
   195  		container.WithIPv4(netName, "172.28.230.10"),
   196  	)
   197  
   198  	_, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
   199  	assert.NilError(t, err)
   200  }
   201  
   202  func testIpvlanL3InternalMode(t *testing.T, ctx context.Context, client dclient.APIClient) {
   203  	netName := "di-internal-l3"
   204  	net.CreateNoError(ctx, t, client, netName,
   205  		net.WithIPvlan("", "l3"),
   206  		net.WithInternal(),
   207  		net.WithIPAM("172.28.230.0/24", ""),
   208  		net.WithIPAM("172.28.220.0/24", ""),
   209  	)
   210  	assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   211  
   212  	id1 := container.Run(ctx, t, client,
   213  		container.WithNetworkMode(netName),
   214  		container.WithIPv4(netName, "172.28.220.10"),
   215  	)
   216  	id2 := container.Run(ctx, t, client,
   217  		container.WithNetworkMode(netName),
   218  		container.WithIPv4(netName, "172.28.230.10"),
   219  	)
   220  
   221  	result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"})
   222  	assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable"))
   223  
   224  	_, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
   225  	assert.NilError(t, err)
   226  }
   227  
   228  func testIpvlanL2MultiSubnet(t *testing.T, ctx context.Context, client dclient.APIClient) {
   229  	netName := "dualstackl2"
   230  	net.CreateNoError(ctx, t, client, netName,
   231  		net.WithIPvlan("", ""),
   232  		net.WithIPv6(),
   233  		net.WithIPAM("172.28.200.0/24", ""),
   234  		net.WithIPAM("172.28.202.0/24", "172.28.202.254"),
   235  		net.WithIPAM("2001:db8:abc8::/64", ""),
   236  		net.WithIPAM("2001:db8:abc6::/64", "2001:db8:abc6::254"),
   237  	)
   238  	assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   239  
   240  	// 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
   241  	id1 := container.Run(ctx, t, client,
   242  		container.WithNetworkMode(netName),
   243  		container.WithIPv4(netName, "172.28.200.20"),
   244  		container.WithIPv6(netName, "2001:db8:abc8::20"),
   245  	)
   246  	id2 := container.Run(ctx, t, client,
   247  		container.WithNetworkMode(netName),
   248  		container.WithIPv4(netName, "172.28.200.21"),
   249  		container.WithIPv6(netName, "2001:db8:abc8::21"),
   250  	)
   251  	c1, err := client.ContainerInspect(ctx, id1)
   252  	assert.NilError(t, err)
   253  
   254  	// verify ipv4 connectivity to the explicit --ipv address second to first
   255  	_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress})
   256  	assert.NilError(t, err)
   257  	// verify ipv6 connectivity to the explicit --ipv6 address second to first
   258  	_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address})
   259  	assert.NilError(t, err)
   260  
   261  	// 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
   262  	id3 := container.Run(ctx, t, client,
   263  		container.WithNetworkMode(netName),
   264  		container.WithIPv4(netName, "172.28.202.20"),
   265  		container.WithIPv6(netName, "2001:db8:abc6::20"),
   266  	)
   267  	id4 := container.Run(ctx, t, client,
   268  		container.WithNetworkMode(netName),
   269  		container.WithIPv4(netName, "172.28.202.21"),
   270  		container.WithIPv6(netName, "2001:db8:abc6::21"),
   271  	)
   272  	c3, err := client.ContainerInspect(ctx, id3)
   273  	assert.NilError(t, err)
   274  
   275  	// verify ipv4 connectivity to the explicit --ipv address from third to fourth
   276  	_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress})
   277  	assert.NilError(t, err)
   278  	// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
   279  	_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address})
   280  	assert.NilError(t, err)
   281  
   282  	// Inspect the v4 gateway to ensure the proper default GW was assigned
   283  	assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "172.28.200.1")
   284  	// Inspect the v6 gateway to ensure the proper default GW was assigned
   285  	assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc8::1")
   286  	// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
   287  	assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "172.28.202.254")
   288  	// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
   289  	assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc6::254")
   290  }
   291  
   292  func testIpvlanL3MultiSubnet(t *testing.T, ctx context.Context, client dclient.APIClient) {
   293  	netName := "dualstackl3"
   294  	net.CreateNoError(ctx, t, client, netName,
   295  		net.WithIPvlan("", "l3"),
   296  		net.WithIPv6(),
   297  		net.WithIPAM("172.28.10.0/24", ""),
   298  		net.WithIPAM("172.28.12.0/24", "172.28.12.254"),
   299  		net.WithIPAM("2001:db8:abc9::/64", ""),
   300  		net.WithIPAM("2001:db8:abc7::/64", "2001:db8:abc7::254"),
   301  	)
   302  	assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
   303  
   304  	// 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
   305  	id1 := container.Run(ctx, t, client,
   306  		container.WithNetworkMode(netName),
   307  		container.WithIPv4(netName, "172.28.10.20"),
   308  		container.WithIPv6(netName, "2001:db8:abc9::20"),
   309  	)
   310  	id2 := container.Run(ctx, t, client,
   311  		container.WithNetworkMode(netName),
   312  		container.WithIPv4(netName, "172.28.10.21"),
   313  		container.WithIPv6(netName, "2001:db8:abc9::21"),
   314  	)
   315  	c1, err := client.ContainerInspect(ctx, id1)
   316  	assert.NilError(t, err)
   317  
   318  	// verify ipv4 connectivity to the explicit --ipv address second to first
   319  	_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress})
   320  	assert.NilError(t, err)
   321  	// verify ipv6 connectivity to the explicit --ipv6 address second to first
   322  	_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address})
   323  	assert.NilError(t, err)
   324  
   325  	// 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
   326  	id3 := container.Run(ctx, t, client,
   327  		container.WithNetworkMode(netName),
   328  		container.WithIPv4(netName, "172.28.12.20"),
   329  		container.WithIPv6(netName, "2001:db8:abc7::20"),
   330  	)
   331  	id4 := container.Run(ctx, t, client,
   332  		container.WithNetworkMode(netName),
   333  		container.WithIPv4(netName, "172.28.12.21"),
   334  		container.WithIPv6(netName, "2001:db8:abc7::21"),
   335  	)
   336  	c3, err := client.ContainerInspect(ctx, id3)
   337  	assert.NilError(t, err)
   338  
   339  	// verify ipv4 connectivity to the explicit --ipv address from third to fourth
   340  	_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress})
   341  	assert.NilError(t, err)
   342  	// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
   343  	_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address})
   344  	assert.NilError(t, err)
   345  
   346  	// Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
   347  	assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "")
   348  	// Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
   349  	assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "")
   350  	// Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
   351  	assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "")
   352  	// Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
   353  	assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "")
   354  }
   355  
   356  func testIpvlanAddressing(t *testing.T, ctx context.Context, client dclient.APIClient) {
   357  	// Verify ipvlan l2 mode sets the proper default gateway routes via netlink
   358  	// for either an explicitly set route by the user or inferred via default IPAM
   359  	netNameL2 := "dualstackl2"
   360  	net.CreateNoError(ctx, t, client, netNameL2,
   361  		net.WithIPvlan("", "l2"),
   362  		net.WithIPv6(),
   363  		net.WithIPAM("172.28.140.0/24", "172.28.140.254"),
   364  		net.WithIPAM("2001:db8:abcb::/64", ""),
   365  	)
   366  	assert.Check(t, n.IsNetworkAvailable(ctx, client, netNameL2))
   367  
   368  	id1 := container.Run(ctx, t, client,
   369  		container.WithNetworkMode(netNameL2),
   370  	)
   371  	// Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet
   372  	result, err := container.Exec(ctx, client, id1, []string{"ip", "route"})
   373  	assert.NilError(t, err)
   374  	assert.Check(t, strings.Contains(result.Combined(), "default via 172.28.140.254 dev eth0"))
   375  	// Validate ipvlan l2 mode sets the v6 gateway to the user specified default gateway/next-hop
   376  	result, err = container.Exec(ctx, client, id1, []string{"ip", "-6", "route"})
   377  	assert.NilError(t, err)
   378  	assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abcb::1 dev eth0"))
   379  
   380  	// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
   381  	netNameL3 := "dualstackl3"
   382  	net.CreateNoError(ctx, t, client, netNameL3,
   383  		net.WithIPvlan("", "l3"),
   384  		net.WithIPv6(),
   385  		net.WithIPAM("172.28.160.0/24", "172.28.160.254"),
   386  		net.WithIPAM("2001:db8:abcd::/64", "2001:db8:abcd::254"),
   387  	)
   388  	assert.Check(t, n.IsNetworkAvailable(ctx, client, netNameL3))
   389  
   390  	id2 := container.Run(ctx, t, client,
   391  		container.WithNetworkMode(netNameL3),
   392  	)
   393  	// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
   394  	result, err = container.Exec(ctx, client, id2, []string{"ip", "route"})
   395  	assert.NilError(t, err)
   396  	assert.Check(t, strings.Contains(result.Combined(), "default dev eth0"))
   397  	// Validate ipvlan l3 mode sets the v6 gateway to dev eth0 and disregards any explicit or inferred next-hops
   398  	result, err = container.Exec(ctx, client, id2, []string{"ip", "-6", "route"})
   399  	assert.NilError(t, err)
   400  	assert.Check(t, strings.Contains(result.Combined(), "default dev eth0"))
   401  }
   402  
   403  var (
   404  	once            sync.Once
   405  	ipvlanSupported bool
   406  )
   407  
   408  // figure out if ipvlan is supported by the kernel
   409  func ipvlanKernelSupport(t *testing.T) bool {
   410  	once.Do(func() {
   411  		// this may have the side effect of enabling the ipvlan module
   412  		exec.Command("modprobe", "ipvlan").Run()
   413  		_, err := os.Stat("/sys/module/ipvlan")
   414  		if err == nil {
   415  			ipvlanSupported = true
   416  		} else if !os.IsNotExist(err) {
   417  			t.Logf("WARNING: ipvlanKernelSupport: stat failed: %v\n", err)
   418  		}
   419  	})
   420  
   421  	return ipvlanSupported
   422  }