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

     1  package networking
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"regexp"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/docker/docker/api/types"
    11  	containertypes "github.com/docker/docker/api/types/container"
    12  	"github.com/docker/docker/integration/internal/container"
    13  	"github.com/docker/docker/integration/internal/network"
    14  	"github.com/docker/docker/testutil"
    15  	"github.com/docker/docker/testutil/daemon"
    16  	"github.com/google/go-cmp/cmp/cmpopts"
    17  	"gotest.tools/v3/assert"
    18  	is "gotest.tools/v3/assert/cmp"
    19  	"gotest.tools/v3/skip"
    20  )
    21  
    22  // TestBridgeICC tries to ping container ctr1 from container ctr2 using its hostname. Thus, this test checks:
    23  // 1. DNS resolution ; 2. ARP/NDP ; 3. whether containers can communicate with each other ; 4. kernel-assigned SLAAC
    24  // addresses.
    25  func TestBridgeICC(t *testing.T) {
    26  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
    27  
    28  	ctx := setupTest(t)
    29  
    30  	d := daemon.New(t)
    31  	d.StartWithBusybox(ctx, t, "-D", "--experimental", "--ip6tables")
    32  	defer d.Stop(t)
    33  
    34  	c := d.NewClientT(t)
    35  	defer c.Close()
    36  
    37  	testcases := []struct {
    38  		name           string
    39  		bridgeOpts     []func(*types.NetworkCreate)
    40  		ctr1MacAddress string
    41  		isIPv6         bool
    42  		isLinkLocal    bool
    43  		pingHost       string
    44  	}{
    45  		{
    46  			name:       "IPv4 non-internal network",
    47  			bridgeOpts: []func(*types.NetworkCreate){},
    48  		},
    49  		{
    50  			name: "IPv4 internal network",
    51  			bridgeOpts: []func(*types.NetworkCreate){
    52  				network.WithInternal(),
    53  			},
    54  		},
    55  		{
    56  			name: "IPv6 ULA on non-internal network",
    57  			bridgeOpts: []func(*types.NetworkCreate){
    58  				network.WithIPv6(),
    59  				network.WithIPAM("fdf1:a844:380c:b200::/64", "fdf1:a844:380c:b200::1"),
    60  			},
    61  			isIPv6: true,
    62  		},
    63  		{
    64  			name: "IPv6 ULA on internal network",
    65  			bridgeOpts: []func(*types.NetworkCreate){
    66  				network.WithIPv6(),
    67  				network.WithInternal(),
    68  				network.WithIPAM("fdf1:a844:380c:b247::/64", "fdf1:a844:380c:b247::1"),
    69  			},
    70  			isIPv6: true,
    71  		},
    72  		{
    73  			name: "IPv6 link-local address on non-internal network",
    74  			bridgeOpts: []func(*types.NetworkCreate){
    75  				network.WithIPv6(),
    76  				// There's no real way to specify an IPv6 network is only used with SLAAC link-local IPv6 addresses.
    77  				// What we can do instead, is to tell the IPAM driver to assign addresses from the link-local prefix.
    78  				// Each container will have two link-local addresses: 1. a SLAAC address assigned by the kernel ;
    79  				// 2. the one dynamically assigned by the IPAM driver.
    80  				network.WithIPAM("fe80::/64", "fe80::1"),
    81  			},
    82  			isLinkLocal: true,
    83  			isIPv6:      true,
    84  		},
    85  		{
    86  			name: "IPv6 link-local address on internal network",
    87  			bridgeOpts: []func(*types.NetworkCreate){
    88  				network.WithIPv6(),
    89  				network.WithInternal(),
    90  				// See the note above about link-local addresses.
    91  				network.WithIPAM("fe80::/64", "fe80::1"),
    92  			},
    93  			isLinkLocal: true,
    94  			isIPv6:      true,
    95  		},
    96  		{
    97  			// As for 'LL non-internal', ping the container by name instead of by address
    98  			// - the busybox test containers only have one interface with a link local
    99  			// address, so the zone index is not required:
   100  			//   RFC-4007, section 6: "[...] for nodes with only a single non-loopback
   101  			//   interface (e.g., a single Ethernet interface), the common case, link-local
   102  			//   addresses need not be qualified with a zone index."
   103  			// So, for this common case, LL addresses should be included in DNS config.
   104  			name: "IPv6 link-local address on non-internal network ping by name",
   105  			bridgeOpts: []func(*types.NetworkCreate){
   106  				network.WithIPv6(),
   107  				network.WithIPAM("fe80::/64", "fe80::1"),
   108  			},
   109  			isIPv6: true,
   110  		},
   111  		{
   112  			name: "IPv6 nonstandard link-local subnet on non-internal network ping by name",
   113  			// No interfaces apart from the one on the bridge network with this non-default
   114  			// subnet will be on this link local subnet (it's not currently possible to
   115  			// configure two networks with the same LL subnet, although perhaps it should
   116  			// be). So, again, no zone index is required and the LL address should be
   117  			// included in DNS config.
   118  			bridgeOpts: []func(*types.NetworkCreate){
   119  				network.WithIPv6(),
   120  				network.WithIPAM("fe80:1234::/64", "fe80:1234::1"),
   121  			},
   122  			isIPv6: true,
   123  		},
   124  		{
   125  			name: "IPv6 non-internal network with SLAAC LL address",
   126  			bridgeOpts: []func(*types.NetworkCreate){
   127  				network.WithIPv6(),
   128  				network.WithIPAM("fdf1:a844:380c:b247::/64", "fdf1:a844:380c:b247::1"),
   129  			},
   130  			// Link-local address is derived from the MAC address, so we need to
   131  			// specify one here to hardcode the SLAAC LL address below.
   132  			ctr1MacAddress: "02:42:ac:11:00:02",
   133  			pingHost:       "fe80::42:acff:fe11:2%eth0",
   134  			isIPv6:         true,
   135  		},
   136  		{
   137  			name: "IPv6 internal network with SLAAC LL address",
   138  			bridgeOpts: []func(*types.NetworkCreate){
   139  				network.WithIPv6(),
   140  				network.WithIPAM("fdf1:a844:380c:b247::/64", "fdf1:a844:380c:b247::1"),
   141  			},
   142  			// Link-local address is derived from the MAC address, so we need to
   143  			// specify one here to hardcode the SLAAC LL address below.
   144  			ctr1MacAddress: "02:42:ac:11:00:02",
   145  			pingHost:       "fe80::42:acff:fe11:2%eth0",
   146  			isIPv6:         true,
   147  		},
   148  	}
   149  
   150  	for tcID, tc := range testcases {
   151  		t.Run(tc.name, func(t *testing.T) {
   152  			ctx := testutil.StartSpan(ctx, t)
   153  
   154  			bridgeName := fmt.Sprintf("testnet-icc-%d", tcID)
   155  			network.CreateNoError(ctx, t, c, bridgeName, append(tc.bridgeOpts,
   156  				network.WithDriver("bridge"),
   157  				network.WithOption("com.docker.network.bridge.name", bridgeName))...)
   158  			defer network.RemoveNoError(ctx, t, c, bridgeName)
   159  
   160  			ctr1Name := fmt.Sprintf("ctr-icc-%d-1", tcID)
   161  			var ctr1Opts []func(config *container.TestContainerConfig)
   162  			if tc.ctr1MacAddress != "" {
   163  				ctr1Opts = append(ctr1Opts, container.WithMacAddress(bridgeName, tc.ctr1MacAddress))
   164  			}
   165  			id1 := container.Run(ctx, t, c, append(ctr1Opts,
   166  				container.WithName(ctr1Name),
   167  				container.WithImage("busybox:latest"),
   168  				container.WithCmd("top"),
   169  				container.WithNetworkMode(bridgeName))...)
   170  			defer c.ContainerRemove(ctx, id1, containertypes.RemoveOptions{
   171  				Force: true,
   172  			})
   173  
   174  			pingHost := tc.pingHost
   175  			if pingHost == "" {
   176  				if tc.isLinkLocal {
   177  					inspect := container.Inspect(ctx, t, c, id1)
   178  					pingHost = inspect.NetworkSettings.Networks[bridgeName].GlobalIPv6Address + "%eth0"
   179  				} else {
   180  					pingHost = ctr1Name
   181  				}
   182  			}
   183  
   184  			ipv := "-4"
   185  			if tc.isIPv6 {
   186  				ipv = "-6"
   187  			}
   188  
   189  			pingCmd := []string{"ping", "-c1", "-W3", ipv, pingHost}
   190  
   191  			ctr2Name := fmt.Sprintf("ctr-icc-%d-2", tcID)
   192  			attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
   193  			defer cancel()
   194  			res := container.RunAttach(attachCtx, t, c,
   195  				container.WithName(ctr2Name),
   196  				container.WithImage("busybox:latest"),
   197  				container.WithCmd(pingCmd...),
   198  				container.WithNetworkMode(bridgeName))
   199  			defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{
   200  				Force: true,
   201  			})
   202  
   203  			assert.Check(t, is.Equal(res.ExitCode, 0))
   204  			assert.Check(t, is.Equal(res.Stderr.Len(), 0))
   205  			assert.Check(t, is.Contains(res.Stdout.String(), "1 packets transmitted, 1 packets received"))
   206  		})
   207  	}
   208  }
   209  
   210  // TestBridgeICCWindows tries to ping container ctr1 from container ctr2 using its hostname.
   211  // Checks DNS resolution, and whether containers can communicate with each other.
   212  // Regression test for https://github.com/moby/moby/issues/47370
   213  func TestBridgeICCWindows(t *testing.T) {
   214  	skip.If(t, testEnv.DaemonInfo.OSType != "windows")
   215  
   216  	ctx := setupTest(t)
   217  	c := testEnv.APIClient()
   218  
   219  	testcases := []struct {
   220  		name    string
   221  		netName string
   222  	}{
   223  		{
   224  			name:    "Default nat network",
   225  			netName: "nat",
   226  		},
   227  		{
   228  			name:    "User defined nat network",
   229  			netName: "mynat",
   230  		},
   231  	}
   232  
   233  	for _, tc := range testcases {
   234  		t.Run(tc.name, func(t *testing.T) {
   235  			ctx := testutil.StartSpan(ctx, t)
   236  
   237  			if tc.netName != "nat" {
   238  				network.CreateNoError(ctx, t, c, tc.netName,
   239  					network.WithDriver("nat"),
   240  				)
   241  				defer network.RemoveNoError(ctx, t, c, tc.netName)
   242  			}
   243  
   244  			const ctr1Name = "ctr1"
   245  			id1 := container.Run(ctx, t, c,
   246  				container.WithName(ctr1Name),
   247  				container.WithNetworkMode(tc.netName),
   248  			)
   249  			defer c.ContainerRemove(ctx, id1, containertypes.RemoveOptions{Force: true})
   250  
   251  			pingCmd := []string{"ping", "-n", "1", "-w", "3000", ctr1Name}
   252  
   253  			const ctr2Name = "ctr2"
   254  			attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
   255  			defer cancel()
   256  			res := container.RunAttach(attachCtx, t, c,
   257  				container.WithName(ctr2Name),
   258  				container.WithCmd(pingCmd...),
   259  				container.WithNetworkMode(tc.netName),
   260  			)
   261  			defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{Force: true})
   262  
   263  			assert.Check(t, is.Equal(res.ExitCode, 0))
   264  			assert.Check(t, is.Equal(res.Stderr.Len(), 0))
   265  			assert.Check(t, is.Contains(res.Stdout.String(), "Sent = 1, Received = 1, Lost = 0"))
   266  		})
   267  	}
   268  }
   269  
   270  // TestBridgeINC makes sure two containers on two different bridge networks can't communicate with each other.
   271  func TestBridgeINC(t *testing.T) {
   272  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   273  
   274  	ctx := setupTest(t)
   275  
   276  	d := daemon.New(t)
   277  	d.StartWithBusybox(ctx, t, "-D", "--experimental", "--ip6tables")
   278  	defer d.Stop(t)
   279  
   280  	c := d.NewClientT(t)
   281  	defer c.Close()
   282  
   283  	type bridgesOpts struct {
   284  		bridge1Opts []func(*types.NetworkCreate)
   285  		bridge2Opts []func(*types.NetworkCreate)
   286  	}
   287  
   288  	testcases := []struct {
   289  		name    string
   290  		bridges bridgesOpts
   291  		ipv6    bool
   292  		stdout  string
   293  		stderr  string
   294  	}{
   295  		{
   296  			name: "IPv4 non-internal network",
   297  			bridges: bridgesOpts{
   298  				bridge1Opts: []func(*types.NetworkCreate){},
   299  				bridge2Opts: []func(*types.NetworkCreate){},
   300  			},
   301  			stdout: "1 packets transmitted, 0 packets received",
   302  		},
   303  		{
   304  			name: "IPv4 internal network",
   305  			bridges: bridgesOpts{
   306  				bridge1Opts: []func(*types.NetworkCreate){network.WithInternal()},
   307  				bridge2Opts: []func(*types.NetworkCreate){network.WithInternal()},
   308  			},
   309  			stderr: "sendto: Network is unreachable",
   310  		},
   311  		{
   312  			name: "IPv6 ULA on non-internal network",
   313  			bridges: bridgesOpts{
   314  				bridge1Opts: []func(*types.NetworkCreate){
   315  					network.WithIPv6(),
   316  					network.WithIPAM("fdf1:a844:380c:b200::/64", "fdf1:a844:380c:b200::1"),
   317  				},
   318  				bridge2Opts: []func(*types.NetworkCreate){
   319  					network.WithIPv6(),
   320  					network.WithIPAM("fdf1:a844:380c:b247::/64", "fdf1:a844:380c:b247::1"),
   321  				},
   322  			},
   323  			ipv6:   true,
   324  			stdout: "1 packets transmitted, 0 packets received",
   325  		},
   326  		{
   327  			name: "IPv6 ULA on internal network",
   328  			bridges: bridgesOpts{
   329  				bridge1Opts: []func(*types.NetworkCreate){
   330  					network.WithIPv6(),
   331  					network.WithInternal(),
   332  					network.WithIPAM("fdf1:a844:390c:b200::/64", "fdf1:a844:390c:b200::1"),
   333  				},
   334  				bridge2Opts: []func(*types.NetworkCreate){
   335  					network.WithIPv6(),
   336  					network.WithInternal(),
   337  					network.WithIPAM("fdf1:a844:390c:b247::/64", "fdf1:a844:390c:b247::1"),
   338  				},
   339  			},
   340  			ipv6:   true,
   341  			stderr: "sendto: Network is unreachable",
   342  		},
   343  	}
   344  
   345  	for tcID, tc := range testcases {
   346  		t.Run(tc.name, func(t *testing.T) {
   347  			ctx := testutil.StartSpan(ctx, t)
   348  
   349  			bridge1 := fmt.Sprintf("testnet-inc-%d-1", tcID)
   350  			bridge2 := fmt.Sprintf("testnet-inc-%d-2", tcID)
   351  
   352  			network.CreateNoError(ctx, t, c, bridge1, append(tc.bridges.bridge1Opts,
   353  				network.WithDriver("bridge"),
   354  				network.WithOption("com.docker.network.bridge.name", bridge1))...)
   355  			defer network.RemoveNoError(ctx, t, c, bridge1)
   356  			network.CreateNoError(ctx, t, c, bridge2, append(tc.bridges.bridge2Opts,
   357  				network.WithDriver("bridge"),
   358  				network.WithOption("com.docker.network.bridge.name", bridge2))...)
   359  			defer network.RemoveNoError(ctx, t, c, bridge2)
   360  
   361  			ctr1Name := sanitizeCtrName(t.Name() + "-ctr1")
   362  			id1 := container.Run(ctx, t, c,
   363  				container.WithName(ctr1Name),
   364  				container.WithImage("busybox:latest"),
   365  				container.WithCmd("top"),
   366  				container.WithNetworkMode(bridge1))
   367  			defer c.ContainerRemove(ctx, id1, containertypes.RemoveOptions{
   368  				Force: true,
   369  			})
   370  
   371  			ctr1Info := container.Inspect(ctx, t, c, id1)
   372  			targetAddr := ctr1Info.NetworkSettings.Networks[bridge1].IPAddress
   373  			if tc.ipv6 {
   374  				targetAddr = ctr1Info.NetworkSettings.Networks[bridge1].GlobalIPv6Address
   375  			}
   376  
   377  			pingCmd := []string{"ping", "-c1", "-W3", targetAddr}
   378  
   379  			ctr2Name := sanitizeCtrName(t.Name() + "-ctr2")
   380  			attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
   381  			defer cancel()
   382  			res := container.RunAttach(attachCtx, t, c,
   383  				container.WithName(ctr2Name),
   384  				container.WithImage("busybox:latest"),
   385  				container.WithCmd(pingCmd...),
   386  				container.WithNetworkMode(bridge2))
   387  			defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{
   388  				Force: true,
   389  			})
   390  
   391  			assert.Check(t, res.ExitCode != 0, "ping unexpectedly succeeded")
   392  			assert.Check(t, is.Contains(res.Stdout.String(), tc.stdout))
   393  			assert.Check(t, is.Contains(res.Stderr.String(), tc.stderr))
   394  		})
   395  	}
   396  }
   397  
   398  func TestDefaultBridgeIPv6(t *testing.T) {
   399  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   400  
   401  	ctx := setupTest(t)
   402  
   403  	testcases := []struct {
   404  		name          string
   405  		fixed_cidr_v6 string
   406  	}{
   407  		{
   408  			name:          "IPv6 ULA",
   409  			fixed_cidr_v6: "fd00:1234::/64",
   410  		},
   411  		{
   412  			name:          "IPv6 LLA only",
   413  			fixed_cidr_v6: "fe80::/64",
   414  		},
   415  		{
   416  			name:          "IPv6 nonstandard LLA only",
   417  			fixed_cidr_v6: "fe80:1234::/64",
   418  		},
   419  	}
   420  
   421  	for _, tc := range testcases {
   422  		t.Run(tc.name, func(t *testing.T) {
   423  			ctx := testutil.StartSpan(ctx, t)
   424  
   425  			d := daemon.New(t)
   426  			d.StartWithBusybox(ctx, t,
   427  				"--experimental",
   428  				"--ip6tables",
   429  				"--ipv6",
   430  				"--fixed-cidr-v6", tc.fixed_cidr_v6,
   431  			)
   432  			defer d.Stop(t)
   433  
   434  			c := d.NewClientT(t)
   435  			defer c.Close()
   436  
   437  			cID := container.Run(ctx, t, c,
   438  				container.WithImage("busybox:latest"),
   439  				container.WithCmd("top"),
   440  			)
   441  			defer c.ContainerRemove(ctx, cID, containertypes.RemoveOptions{
   442  				Force: true,
   443  			})
   444  
   445  			networkName := "bridge"
   446  			inspect := container.Inspect(ctx, t, c, cID)
   447  			pingHost := inspect.NetworkSettings.Networks[networkName].GlobalIPv6Address
   448  
   449  			attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
   450  			defer cancel()
   451  			res := container.RunAttach(attachCtx, t, c,
   452  				container.WithImage("busybox:latest"),
   453  				container.WithCmd("ping", "-c1", "-W3", pingHost),
   454  			)
   455  			defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{
   456  				Force: true,
   457  			})
   458  
   459  			assert.Check(t, is.Equal(res.ExitCode, 0))
   460  			assert.Check(t, is.Equal(res.Stderr.String(), ""))
   461  			assert.Check(t, is.Contains(res.Stdout.String(), "1 packets transmitted, 1 packets received"))
   462  		})
   463  	}
   464  }
   465  
   466  // Check that it's possible to change 'fixed-cidr-v6' and restart the daemon.
   467  func TestDefaultBridgeAddresses(t *testing.T) {
   468  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   469  
   470  	ctx := setupTest(t)
   471  	d := daemon.New(t)
   472  
   473  	type testStep struct {
   474  		stepName    string
   475  		fixedCIDRV6 string
   476  		expAddrs    []string
   477  	}
   478  
   479  	testcases := []struct {
   480  		name  string
   481  		steps []testStep
   482  	}{
   483  		{
   484  			name: "Unique-Local Subnet Changes",
   485  			steps: []testStep{
   486  				{
   487  					stepName:    "Set up initial UL prefix",
   488  					fixedCIDRV6: "fd1c:f1a0:5d8d:aaaa::/64",
   489  					expAddrs:    []string{"fd1c:f1a0:5d8d:aaaa::1/64", "fe80::1/64"},
   490  				},
   491  				{
   492  					// Modify that prefix, the default bridge's address must be deleted and re-added.
   493  					stepName:    "Modify UL prefix - address change",
   494  					fixedCIDRV6: "fd1c:f1a0:5d8d:bbbb::/64",
   495  					expAddrs:    []string{"fd1c:f1a0:5d8d:bbbb::1/64", "fe80::1/64"},
   496  				},
   497  				{
   498  					// Modify the prefix length, the default bridge's address should not change.
   499  					stepName:    "Modify UL prefix - no address change",
   500  					fixedCIDRV6: "fd1c:f1a0:5d8d:bbbb::/80",
   501  					// The prefix length displayed by 'ip a' is not updated - it's informational, and
   502  					// can't be changed without unnecessarily deleting and re-adding the address.
   503  					expAddrs: []string{"fd1c:f1a0:5d8d:bbbb::1/64", "fe80::1/64"},
   504  				},
   505  			},
   506  		},
   507  		{
   508  			name: "Link-Local Subnet Changes",
   509  			steps: []testStep{
   510  				{
   511  					stepName:    "Standard LL subnet prefix",
   512  					fixedCIDRV6: "fe80::/64",
   513  					expAddrs:    []string{"fe80::1/64"},
   514  				},
   515  				{
   516  					// Modify that prefix, the default bridge's address must be deleted and re-added.
   517  					// The bridge must still have an address in the required (standard) LL subnet.
   518  					stepName:    "Nonstandard LL prefix - address change",
   519  					fixedCIDRV6: "fe80:1234::/32",
   520  					expAddrs:    []string{"fe80:1234::1/32", "fe80::1/64"},
   521  				},
   522  				{
   523  					// Modify the prefix length, the addresses should not change.
   524  					stepName:    "Modify LL prefix - no address change",
   525  					fixedCIDRV6: "fe80:1234::/64",
   526  					// The prefix length displayed by 'ip a' is not updated - it's informational, and
   527  					// can't be changed without unnecessarily deleting and re-adding the address.
   528  					expAddrs: []string{"fe80:1234::1/", "fe80::1/64"},
   529  				},
   530  			},
   531  		},
   532  	}
   533  
   534  	for _, tc := range testcases {
   535  		t.Run(tc.name, func(t *testing.T) {
   536  			for _, step := range tc.steps {
   537  				// Check that the daemon starts - regression test for:
   538  				//   https://github.com/moby/moby/issues/46829
   539  				d.Start(t, "--experimental", "--ipv6", "--ip6tables", "--fixed-cidr-v6="+step.fixedCIDRV6)
   540  				d.Stop(t)
   541  
   542  				// Check that the expected addresses have been applied to the bridge. (Skip in
   543  				// rootless mode, because the bridge is in a different network namespace.)
   544  				if !testEnv.IsRootless() {
   545  					res := testutil.RunCommand(ctx, "ip", "-6", "addr", "show", "docker0")
   546  					assert.Equal(t, res.ExitCode, 0, step.stepName)
   547  					stdout := res.Stdout()
   548  					for _, expAddr := range step.expAddrs {
   549  						assert.Check(t, is.Contains(stdout, expAddr))
   550  					}
   551  				}
   552  			}
   553  		})
   554  	}
   555  }
   556  
   557  // Test that a container on an 'internal' network has IP connectivity with
   558  // the host (on its own subnet, because the n/w bridge has an address on that
   559  // subnet, and it's in the host's namespace).
   560  // Regression test for https://github.com/moby/moby/issues/47329
   561  func TestInternalNwConnectivity(t *testing.T) {
   562  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   563  
   564  	ctx := setupTest(t)
   565  
   566  	d := daemon.New(t)
   567  	d.StartWithBusybox(ctx, t, "-D", "--experimental", "--ip6tables")
   568  	defer d.Stop(t)
   569  
   570  	c := d.NewClientT(t)
   571  	defer c.Close()
   572  
   573  	const bridgeName = "intnw"
   574  	const gw4 = "172.30.0.1"
   575  	const gw6 = "fda9:4130:4715::1234"
   576  	network.CreateNoError(ctx, t, c, bridgeName,
   577  		network.WithInternal(),
   578  		network.WithIPv6(),
   579  		network.WithIPAM("172.30.0.0/24", gw4),
   580  		network.WithIPAM("fda9:4130:4715::/64", gw6),
   581  		network.WithDriver("bridge"),
   582  		network.WithOption("com.docker.network.bridge.name", bridgeName),
   583  	)
   584  	defer network.RemoveNoError(ctx, t, c, bridgeName)
   585  
   586  	const ctrName = "intctr"
   587  	id := container.Run(ctx, t, c,
   588  		container.WithName(ctrName),
   589  		container.WithImage("busybox:latest"),
   590  		container.WithCmd("top"),
   591  		container.WithNetworkMode(bridgeName),
   592  	)
   593  	defer c.ContainerRemove(ctx, id, containertypes.RemoveOptions{Force: true})
   594  
   595  	execCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
   596  	defer cancel()
   597  
   598  	res := container.ExecT(execCtx, t, c, id, []string{"ping", "-c1", "-W3", gw4})
   599  	assert.Check(t, is.Equal(res.ExitCode, 0))
   600  	assert.Check(t, is.Equal(res.Stderr(), ""))
   601  	assert.Check(t, is.Contains(res.Stdout(), "1 packets transmitted, 1 packets received"))
   602  
   603  	res = container.ExecT(execCtx, t, c, id, []string{"ping6", "-c1", "-W3", gw6})
   604  	assert.Check(t, is.Equal(res.ExitCode, 0))
   605  	assert.Check(t, is.Equal(res.Stderr(), ""))
   606  	assert.Check(t, is.Contains(res.Stdout(), "1 packets transmitted, 1 packets received"))
   607  
   608  	// Addresses outside the internal subnet must not be accessible.
   609  	res = container.ExecT(execCtx, t, c, id, []string{"ping", "-c1", "-W3", "8.8.8.8"})
   610  	assert.Check(t, is.Equal(res.ExitCode, 1))
   611  	assert.Check(t, is.Contains(res.Stderr(), "Network is unreachable"))
   612  }
   613  
   614  // Check that the container's interface has no IPv6 address when IPv6 is
   615  // disabled in a container via sysctl.
   616  func TestDisableIPv6Addrs(t *testing.T) {
   617  	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
   618  
   619  	ctx := setupTest(t)
   620  	d := daemon.New(t)
   621  	d.StartWithBusybox(ctx, t)
   622  	defer d.Stop(t)
   623  
   624  	c := d.NewClientT(t)
   625  	defer c.Close()
   626  
   627  	testcases := []struct {
   628  		name    string
   629  		sysctls map[string]string
   630  		expIPv6 bool
   631  	}{
   632  		{
   633  			name:    "IPv6 enabled",
   634  			expIPv6: true,
   635  		},
   636  		{
   637  			name:    "IPv6 disabled",
   638  			sysctls: map[string]string{"net.ipv6.conf.all.disable_ipv6": "1"},
   639  		},
   640  	}
   641  
   642  	const netName = "testnet"
   643  	network.CreateNoError(ctx, t, c, netName,
   644  		network.WithIPv6(),
   645  		network.WithIPAM("fda0:ef3d:6430:abcd::/64", "fda0:ef3d:6430:abcd::1"),
   646  	)
   647  	defer network.RemoveNoError(ctx, t, c, netName)
   648  
   649  	inet6RE := regexp.MustCompile(`inet6[ \t]+[0-9a-f:]*`)
   650  
   651  	for _, tc := range testcases {
   652  		t.Run(tc.name, func(t *testing.T) {
   653  			ctx := testutil.StartSpan(ctx, t)
   654  
   655  			opts := []func(config *container.TestContainerConfig){
   656  				container.WithCmd("ip", "a"),
   657  				container.WithNetworkMode(netName),
   658  			}
   659  			if len(tc.sysctls) > 0 {
   660  				opts = append(opts, container.WithSysctls(tc.sysctls))
   661  			}
   662  
   663  			runRes := container.RunAttach(ctx, t, c, opts...)
   664  			defer c.ContainerRemove(ctx, runRes.ContainerID,
   665  				containertypes.RemoveOptions{Force: true},
   666  			)
   667  
   668  			stdout := runRes.Stdout.String()
   669  			inet6 := inet6RE.FindAllString(stdout, -1)
   670  			if tc.expIPv6 {
   671  				assert.Check(t, len(inet6) > 0, "Expected IPv6 addresses but found none.")
   672  			} else {
   673  				assert.Check(t, is.DeepEqual(inet6, []string{}, cmpopts.EquateEmpty()))
   674  			}
   675  		})
   676  	}
   677  }
   678  
   679  // Test that it's possible to set a sysctl on an interface in the container.
   680  // Regression test for https://github.com/moby/moby/issues/47619
   681  func TestSetInterfaceSysctl(t *testing.T) {
   682  	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "no sysctl on Windows")
   683  
   684  	ctx := setupTest(t)
   685  	d := daemon.New(t)
   686  	d.StartWithBusybox(ctx, t)
   687  	defer d.Stop(t)
   688  
   689  	c := d.NewClientT(t)
   690  	defer c.Close()
   691  
   692  	const scName = "net.ipv4.conf.eth0.forwarding"
   693  	opts := []func(config *container.TestContainerConfig){
   694  		container.WithCmd("sysctl", scName),
   695  		container.WithSysctls(map[string]string{scName: "1"}),
   696  	}
   697  
   698  	runRes := container.RunAttach(ctx, t, c, opts...)
   699  	defer c.ContainerRemove(ctx, runRes.ContainerID,
   700  		containertypes.RemoveOptions{Force: true},
   701  	)
   702  
   703  	stdout := runRes.Stdout.String()
   704  	assert.Check(t, is.Contains(stdout, scName))
   705  }