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  }