github.com/wozhu6104/docker@v20.10.10+incompatible/integration-cli/docker_cli_network_unix_test.go (about)

     1  // +build !windows
     2  
     3  package main
     4  
     5  import (
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"net"
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"os"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/docker/docker/api/types"
    18  	"github.com/docker/docker/api/types/versions/v1p20"
    19  	"github.com/docker/docker/integration-cli/cli"
    20  	"github.com/docker/docker/integration-cli/daemon"
    21  	"github.com/docker/docker/pkg/stringid"
    22  	"github.com/docker/docker/runconfig"
    23  	testdaemon "github.com/docker/docker/testutil/daemon"
    24  	"github.com/docker/libnetwork/driverapi"
    25  	remoteapi "github.com/docker/libnetwork/drivers/remote/api"
    26  	"github.com/docker/libnetwork/ipamapi"
    27  	remoteipam "github.com/docker/libnetwork/ipams/remote/api"
    28  	"github.com/docker/libnetwork/netlabel"
    29  	"github.com/vishvananda/netlink"
    30  	"golang.org/x/sys/unix"
    31  	"gotest.tools/v3/assert"
    32  	"gotest.tools/v3/icmd"
    33  )
    34  
    35  const dummyNetworkDriver = "dummy-network-driver"
    36  const dummyIPAMDriver = "dummy-ipam-driver"
    37  
    38  var remoteDriverNetworkRequest remoteapi.CreateNetworkRequest
    39  
    40  func (s *DockerNetworkSuite) SetUpTest(c *testing.T) {
    41  	s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
    42  }
    43  
    44  func (s *DockerNetworkSuite) TearDownTest(c *testing.T) {
    45  	if s.d != nil {
    46  		s.d.Stop(c)
    47  		s.ds.TearDownTest(c)
    48  	}
    49  }
    50  
    51  func (s *DockerNetworkSuite) SetUpSuite(c *testing.T) {
    52  	mux := http.NewServeMux()
    53  	s.server = httptest.NewServer(mux)
    54  	assert.Assert(c, s.server != nil, "Failed to start an HTTP Server")
    55  	setupRemoteNetworkDrivers(c, mux, s.server.URL, dummyNetworkDriver, dummyIPAMDriver)
    56  }
    57  
    58  func setupRemoteNetworkDrivers(c *testing.T, mux *http.ServeMux, url, netDrv, ipamDrv string) {
    59  
    60  	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
    61  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
    62  		fmt.Fprintf(w, `{"Implements": ["%s", "%s"]}`, driverapi.NetworkPluginEndpointType, ipamapi.PluginEndpointType)
    63  	})
    64  
    65  	// Network driver implementation
    66  	mux.HandleFunc(fmt.Sprintf("/%s.GetCapabilities", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
    67  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
    68  		fmt.Fprintf(w, `{"Scope":"local"}`)
    69  	})
    70  
    71  	mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
    72  		err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest)
    73  		if err != nil {
    74  			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
    75  			return
    76  		}
    77  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
    78  		fmt.Fprintf(w, "null")
    79  	})
    80  
    81  	mux.HandleFunc(fmt.Sprintf("/%s.DeleteNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
    82  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
    83  		fmt.Fprintf(w, "null")
    84  	})
    85  
    86  	mux.HandleFunc(fmt.Sprintf("/%s.CreateEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
    87  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
    88  		fmt.Fprintf(w, `{"Interface":{"MacAddress":"a0:b1:c2:d3:e4:f5"}}`)
    89  	})
    90  
    91  	mux.HandleFunc(fmt.Sprintf("/%s.Join", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
    92  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
    93  
    94  		veth := &netlink.Veth{
    95  			LinkAttrs: netlink.LinkAttrs{Name: "randomIfName", TxQLen: 0}, PeerName: "cnt0"}
    96  		if err := netlink.LinkAdd(veth); err != nil {
    97  			fmt.Fprintf(w, `{"Error":"failed to add veth pair: `+err.Error()+`"}`)
    98  		} else {
    99  			fmt.Fprintf(w, `{"InterfaceName":{ "SrcName":"cnt0", "DstPrefix":"veth"}}`)
   100  		}
   101  	})
   102  
   103  	mux.HandleFunc(fmt.Sprintf("/%s.Leave", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   104  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   105  		fmt.Fprintf(w, "null")
   106  	})
   107  
   108  	mux.HandleFunc(fmt.Sprintf("/%s.DeleteEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   109  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   110  		if link, err := netlink.LinkByName("cnt0"); err == nil {
   111  			netlink.LinkDel(link)
   112  		}
   113  		fmt.Fprintf(w, "null")
   114  	})
   115  
   116  	// IPAM Driver implementation
   117  	var (
   118  		poolRequest       remoteipam.RequestPoolRequest
   119  		poolReleaseReq    remoteipam.ReleasePoolRequest
   120  		addressRequest    remoteipam.RequestAddressRequest
   121  		addressReleaseReq remoteipam.ReleaseAddressRequest
   122  		lAS               = "localAS"
   123  		gAS               = "globalAS"
   124  		pool              = "172.28.0.0/16"
   125  		poolID            = lAS + "/" + pool
   126  		gw                = "172.28.255.254/16"
   127  	)
   128  
   129  	mux.HandleFunc(fmt.Sprintf("/%s.GetDefaultAddressSpaces", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   130  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   131  		fmt.Fprintf(w, `{"LocalDefaultAddressSpace":"`+lAS+`", "GlobalDefaultAddressSpace": "`+gAS+`"}`)
   132  	})
   133  
   134  	mux.HandleFunc(fmt.Sprintf("/%s.RequestPool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   135  		err := json.NewDecoder(r.Body).Decode(&poolRequest)
   136  		if err != nil {
   137  			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   138  			return
   139  		}
   140  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   141  		if poolRequest.AddressSpace != lAS && poolRequest.AddressSpace != gAS {
   142  			fmt.Fprintf(w, `{"Error":"Unknown address space in pool request: `+poolRequest.AddressSpace+`"}`)
   143  		} else if poolRequest.Pool != "" && poolRequest.Pool != pool {
   144  			fmt.Fprintf(w, `{"Error":"Cannot handle explicit pool requests yet"}`)
   145  		} else {
   146  			fmt.Fprintf(w, `{"PoolID":"`+poolID+`", "Pool":"`+pool+`"}`)
   147  		}
   148  	})
   149  
   150  	mux.HandleFunc(fmt.Sprintf("/%s.RequestAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   151  		err := json.NewDecoder(r.Body).Decode(&addressRequest)
   152  		if err != nil {
   153  			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   154  			return
   155  		}
   156  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   157  		// make sure libnetwork is now querying on the expected pool id
   158  		if addressRequest.PoolID != poolID {
   159  			fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
   160  		} else if addressRequest.Address != "" {
   161  			fmt.Fprintf(w, `{"Error":"Cannot handle explicit address requests yet"}`)
   162  		} else {
   163  			fmt.Fprintf(w, `{"Address":"`+gw+`"}`)
   164  		}
   165  	})
   166  
   167  	mux.HandleFunc(fmt.Sprintf("/%s.ReleaseAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   168  		err := json.NewDecoder(r.Body).Decode(&addressReleaseReq)
   169  		if err != nil {
   170  			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   171  			return
   172  		}
   173  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   174  		// make sure libnetwork is now asking to release the expected address from the expected poolid
   175  		if addressRequest.PoolID != poolID {
   176  			fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
   177  		} else if addressReleaseReq.Address != gw {
   178  			fmt.Fprintf(w, `{"Error":"unknown address"}`)
   179  		} else {
   180  			fmt.Fprintf(w, "null")
   181  		}
   182  	})
   183  
   184  	mux.HandleFunc(fmt.Sprintf("/%s.ReleasePool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   185  		err := json.NewDecoder(r.Body).Decode(&poolReleaseReq)
   186  		if err != nil {
   187  			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   188  			return
   189  		}
   190  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   191  		// make sure libnetwork is now asking to release the expected poolid
   192  		if addressRequest.PoolID != poolID {
   193  			fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
   194  		} else {
   195  			fmt.Fprintf(w, "null")
   196  		}
   197  	})
   198  
   199  	err := os.MkdirAll("/etc/docker/plugins", 0755)
   200  	assert.NilError(c, err)
   201  
   202  	fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", netDrv)
   203  	err = ioutil.WriteFile(fileName, []byte(url), 0644)
   204  	assert.NilError(c, err)
   205  
   206  	ipamFileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", ipamDrv)
   207  	err = ioutil.WriteFile(ipamFileName, []byte(url), 0644)
   208  	assert.NilError(c, err)
   209  }
   210  
   211  func (s *DockerNetworkSuite) TearDownSuite(c *testing.T) {
   212  	if s.server == nil {
   213  		return
   214  	}
   215  
   216  	s.server.Close()
   217  
   218  	err := os.RemoveAll("/etc/docker/plugins")
   219  	assert.NilError(c, err)
   220  }
   221  
   222  func assertNwIsAvailable(c *testing.T, name string) {
   223  	if !isNwPresent(c, name) {
   224  		c.Fatalf("Network %s not found in network ls o/p", name)
   225  	}
   226  }
   227  
   228  func assertNwNotAvailable(c *testing.T, name string) {
   229  	if isNwPresent(c, name) {
   230  		c.Fatalf("Found network %s in network ls o/p", name)
   231  	}
   232  }
   233  
   234  func isNwPresent(c *testing.T, name string) bool {
   235  	out, _ := dockerCmd(c, "network", "ls")
   236  	lines := strings.Split(out, "\n")
   237  	for i := 1; i < len(lines)-1; i++ {
   238  		netFields := strings.Fields(lines[i])
   239  		if netFields[1] == name {
   240  			return true
   241  		}
   242  	}
   243  	return false
   244  }
   245  
   246  // assertNwList checks network list retrieved with ls command
   247  // equals to expected network list
   248  // note: out should be `network ls [option]` result
   249  func assertNwList(c *testing.T, out string, expectNws []string) {
   250  	lines := strings.Split(out, "\n")
   251  	var nwList []string
   252  	for _, line := range lines[1 : len(lines)-1] {
   253  		netFields := strings.Fields(line)
   254  		// wrap all network name in nwList
   255  		nwList = append(nwList, netFields[1])
   256  	}
   257  
   258  	// network ls should contains all expected networks
   259  	assert.DeepEqual(c, nwList, expectNws)
   260  }
   261  
   262  func getNwResource(c *testing.T, name string) *types.NetworkResource {
   263  	out, _ := dockerCmd(c, "network", "inspect", name)
   264  	var nr []types.NetworkResource
   265  	err := json.Unmarshal([]byte(out), &nr)
   266  	assert.NilError(c, err)
   267  	return &nr[0]
   268  }
   269  
   270  func (s *DockerNetworkSuite) TestDockerNetworkLsDefault(c *testing.T) {
   271  	defaults := []string{"bridge", "host", "none"}
   272  	for _, nn := range defaults {
   273  		assertNwIsAvailable(c, nn)
   274  	}
   275  }
   276  
   277  func (s *DockerNetworkSuite) TestDockerNetworkCreatePredefined(c *testing.T) {
   278  	predefined := []string{"bridge", "host", "none", "default"}
   279  	for _, net := range predefined {
   280  		// predefined networks can't be created again
   281  		out, _, err := dockerCmdWithError("network", "create", net)
   282  		assert.ErrorContains(c, err, "", out)
   283  	}
   284  }
   285  
   286  func (s *DockerNetworkSuite) TestDockerNetworkCreateHostBind(c *testing.T) {
   287  	dockerCmd(c, "network", "create", "--subnet=192.168.10.0/24", "--gateway=192.168.10.1", "-o", "com.docker.network.bridge.host_binding_ipv4=192.168.10.1", "testbind")
   288  	assertNwIsAvailable(c, "testbind")
   289  
   290  	out := runSleepingContainer(c, "--net=testbind", "-p", "5000:5000")
   291  	id := strings.TrimSpace(out)
   292  	assert.NilError(c, waitRun(id))
   293  	out, _ = dockerCmd(c, "ps")
   294  	assert.Assert(c, strings.Contains(out, "192.168.10.1:5000->5000/tcp"))
   295  }
   296  
   297  func (s *DockerNetworkSuite) TestDockerNetworkRmPredefined(c *testing.T) {
   298  	predefined := []string{"bridge", "host", "none", "default"}
   299  	for _, net := range predefined {
   300  		// predefined networks can't be removed
   301  		out, _, err := dockerCmdWithError("network", "rm", net)
   302  		assert.ErrorContains(c, err, "", out)
   303  	}
   304  }
   305  
   306  func (s *DockerNetworkSuite) TestDockerNetworkLsFilter(c *testing.T) {
   307  	testRequires(c, OnlyDefaultNetworks)
   308  	testNet := "testnet1"
   309  	testLabel := "foo"
   310  	testValue := "bar"
   311  	out, _ := dockerCmd(c, "network", "create", "dev")
   312  	defer func() {
   313  		dockerCmd(c, "network", "rm", "dev")
   314  		dockerCmd(c, "network", "rm", testNet)
   315  	}()
   316  	networkID := strings.TrimSpace(out)
   317  
   318  	// filter with partial ID
   319  	// only show 'dev' network
   320  	out, _ = dockerCmd(c, "network", "ls", "-f", "id="+networkID[0:5])
   321  	assertNwList(c, out, []string{"dev"})
   322  
   323  	out, _ = dockerCmd(c, "network", "ls", "-f", "name=dge")
   324  	assertNwList(c, out, []string{"bridge"})
   325  
   326  	// only show built-in network (bridge, none, host)
   327  	out, _ = dockerCmd(c, "network", "ls", "-f", "type=builtin")
   328  	assertNwList(c, out, []string{"bridge", "host", "none"})
   329  
   330  	// only show custom networks (dev)
   331  	out, _ = dockerCmd(c, "network", "ls", "-f", "type=custom")
   332  	assertNwList(c, out, []string{"dev"})
   333  
   334  	// show all networks with filter
   335  	// it should be equivalent of ls without option
   336  	out, _ = dockerCmd(c, "network", "ls", "-f", "type=custom", "-f", "type=builtin")
   337  	assertNwList(c, out, []string{"bridge", "dev", "host", "none"})
   338  
   339  	dockerCmd(c, "network", "create", "--label", testLabel+"="+testValue, testNet)
   340  	assertNwIsAvailable(c, testNet)
   341  
   342  	out, _ = dockerCmd(c, "network", "ls", "-f", "label="+testLabel)
   343  	assertNwList(c, out, []string{testNet})
   344  
   345  	out, _ = dockerCmd(c, "network", "ls", "-f", "label="+testLabel+"="+testValue)
   346  	assertNwList(c, out, []string{testNet})
   347  
   348  	out, _ = dockerCmd(c, "network", "ls", "-f", "label=nonexistent")
   349  	outArr := strings.Split(strings.TrimSpace(out), "\n")
   350  	assert.Equal(c, len(outArr), 1, fmt.Sprintf("%s\n", out))
   351  
   352  	out, _ = dockerCmd(c, "network", "ls", "-f", "driver=null")
   353  	assertNwList(c, out, []string{"none"})
   354  
   355  	out, _ = dockerCmd(c, "network", "ls", "-f", "driver=host")
   356  	assertNwList(c, out, []string{"host"})
   357  
   358  	out, _ = dockerCmd(c, "network", "ls", "-f", "driver=bridge")
   359  	assertNwList(c, out, []string{"bridge", "dev", testNet})
   360  }
   361  
   362  func (s *DockerNetworkSuite) TestDockerNetworkCreateDelete(c *testing.T) {
   363  	dockerCmd(c, "network", "create", "test")
   364  	assertNwIsAvailable(c, "test")
   365  
   366  	dockerCmd(c, "network", "rm", "test")
   367  	assertNwNotAvailable(c, "test")
   368  }
   369  
   370  func (s *DockerNetworkSuite) TestDockerNetworkCreateLabel(c *testing.T) {
   371  	testNet := "testnetcreatelabel"
   372  	testLabel := "foo"
   373  	testValue := "bar"
   374  
   375  	dockerCmd(c, "network", "create", "--label", testLabel+"="+testValue, testNet)
   376  	assertNwIsAvailable(c, testNet)
   377  
   378  	out, _, err := dockerCmdWithError("network", "inspect", "--format={{ .Labels."+testLabel+" }}", testNet)
   379  	assert.NilError(c, err)
   380  	assert.Equal(c, strings.TrimSpace(out), testValue)
   381  
   382  	dockerCmd(c, "network", "rm", testNet)
   383  	assertNwNotAvailable(c, testNet)
   384  }
   385  
   386  func (s *DockerSuite) TestDockerNetworkDeleteNotExists(c *testing.T) {
   387  	out, _, err := dockerCmdWithError("network", "rm", "test")
   388  	assert.ErrorContains(c, err, "", out)
   389  }
   390  
   391  func (s *DockerSuite) TestDockerNetworkDeleteMultiple(c *testing.T) {
   392  	dockerCmd(c, "network", "create", "testDelMulti0")
   393  	assertNwIsAvailable(c, "testDelMulti0")
   394  	dockerCmd(c, "network", "create", "testDelMulti1")
   395  	assertNwIsAvailable(c, "testDelMulti1")
   396  	dockerCmd(c, "network", "create", "testDelMulti2")
   397  	assertNwIsAvailable(c, "testDelMulti2")
   398  	out, _ := dockerCmd(c, "run", "-d", "--net", "testDelMulti2", "busybox", "top")
   399  	containerID := strings.TrimSpace(out)
   400  	waitRun(containerID)
   401  
   402  	// delete three networks at the same time, since testDelMulti2
   403  	// contains active container, its deletion should fail.
   404  	out, _, err := dockerCmdWithError("network", "rm", "testDelMulti0", "testDelMulti1", "testDelMulti2")
   405  	// err should not be nil due to deleting testDelMulti2 failed.
   406  	assert.Assert(c, err != nil, "out: %s", out)
   407  	// testDelMulti2 should fail due to network has active endpoints
   408  	assert.Assert(c, strings.Contains(out, "has active endpoints"))
   409  	assertNwNotAvailable(c, "testDelMulti0")
   410  	assertNwNotAvailable(c, "testDelMulti1")
   411  	// testDelMulti2 can't be deleted, so it should exist
   412  	assertNwIsAvailable(c, "testDelMulti2")
   413  }
   414  
   415  func (s *DockerSuite) TestDockerNetworkInspect(c *testing.T) {
   416  	out, _ := dockerCmd(c, "network", "inspect", "host")
   417  	var networkResources []types.NetworkResource
   418  	err := json.Unmarshal([]byte(out), &networkResources)
   419  	assert.NilError(c, err)
   420  	assert.Equal(c, len(networkResources), 1)
   421  
   422  	out, _ = dockerCmd(c, "network", "inspect", "--format={{ .Name }}", "host")
   423  	assert.Equal(c, strings.TrimSpace(out), "host")
   424  }
   425  
   426  func (s *DockerSuite) TestDockerNetworkInspectWithID(c *testing.T) {
   427  	out, _ := dockerCmd(c, "network", "create", "test2")
   428  	networkID := strings.TrimSpace(out)
   429  	assertNwIsAvailable(c, "test2")
   430  	out, _ = dockerCmd(c, "network", "inspect", "--format={{ .Id }}", "test2")
   431  	assert.Equal(c, strings.TrimSpace(out), networkID)
   432  
   433  	out, _ = dockerCmd(c, "network", "inspect", "--format={{ .ID }}", "test2")
   434  	assert.Equal(c, strings.TrimSpace(out), networkID)
   435  }
   436  
   437  func (s *DockerSuite) TestDockerInspectMultipleNetwork(c *testing.T) {
   438  	result := dockerCmdWithResult("network", "inspect", "host", "none")
   439  	result.Assert(c, icmd.Success)
   440  
   441  	var networkResources []types.NetworkResource
   442  	err := json.Unmarshal([]byte(result.Stdout()), &networkResources)
   443  	assert.NilError(c, err)
   444  	assert.Equal(c, len(networkResources), 2)
   445  }
   446  
   447  func (s *DockerSuite) TestDockerInspectMultipleNetworksIncludingNonexistent(c *testing.T) {
   448  	// non-existent network was not at the beginning of the inspect list
   449  	// This should print an error, return an exitCode 1 and print the host network
   450  	result := dockerCmdWithResult("network", "inspect", "host", "nonexistent")
   451  	result.Assert(c, icmd.Expected{
   452  		ExitCode: 1,
   453  		Err:      "Error: No such network: nonexistent",
   454  		Out:      "host",
   455  	})
   456  
   457  	var networkResources []types.NetworkResource
   458  	err := json.Unmarshal([]byte(result.Stdout()), &networkResources)
   459  	assert.NilError(c, err)
   460  	assert.Equal(c, len(networkResources), 1)
   461  
   462  	// Only one non-existent network to inspect
   463  	// Should print an error and return an exitCode, nothing else
   464  	result = dockerCmdWithResult("network", "inspect", "nonexistent")
   465  	result.Assert(c, icmd.Expected{
   466  		ExitCode: 1,
   467  		Err:      "Error: No such network: nonexistent",
   468  		Out:      "[]",
   469  	})
   470  
   471  	// non-existent network was at the beginning of the inspect list
   472  	// Should not fail fast, and still print host network but print an error
   473  	result = dockerCmdWithResult("network", "inspect", "nonexistent", "host")
   474  	result.Assert(c, icmd.Expected{
   475  		ExitCode: 1,
   476  		Err:      "Error: No such network: nonexistent",
   477  		Out:      "host",
   478  	})
   479  
   480  	networkResources = []types.NetworkResource{}
   481  	err = json.Unmarshal([]byte(result.Stdout()), &networkResources)
   482  	assert.NilError(c, err)
   483  	assert.Equal(c, len(networkResources), 1)
   484  }
   485  
   486  func (s *DockerSuite) TestDockerInspectNetworkWithContainerName(c *testing.T) {
   487  	dockerCmd(c, "network", "create", "brNetForInspect")
   488  	assertNwIsAvailable(c, "brNetForInspect")
   489  	defer func() {
   490  		dockerCmd(c, "network", "rm", "brNetForInspect")
   491  		assertNwNotAvailable(c, "brNetForInspect")
   492  	}()
   493  
   494  	out, _ := dockerCmd(c, "run", "-d", "--name", "testNetInspect1", "--net", "brNetForInspect", "busybox", "top")
   495  	assert.Assert(c, waitRun("testNetInspect1") == nil)
   496  	containerID := strings.TrimSpace(out)
   497  	defer func() {
   498  		// we don't stop container by name, because we'll rename it later
   499  		dockerCmd(c, "stop", containerID)
   500  	}()
   501  
   502  	out, _ = dockerCmd(c, "network", "inspect", "brNetForInspect")
   503  	var networkResources []types.NetworkResource
   504  	err := json.Unmarshal([]byte(out), &networkResources)
   505  	assert.NilError(c, err)
   506  	assert.Equal(c, len(networkResources), 1)
   507  	container, ok := networkResources[0].Containers[containerID]
   508  	assert.Assert(c, ok)
   509  	assert.Equal(c, container.Name, "testNetInspect1")
   510  
   511  	// rename container and check docker inspect output update
   512  	newName := "HappyNewName"
   513  	dockerCmd(c, "rename", "testNetInspect1", newName)
   514  
   515  	// check whether network inspect works properly
   516  	out, _ = dockerCmd(c, "network", "inspect", "brNetForInspect")
   517  	var newNetRes []types.NetworkResource
   518  	err = json.Unmarshal([]byte(out), &newNetRes)
   519  	assert.NilError(c, err)
   520  	assert.Equal(c, len(newNetRes), 1)
   521  	container1, ok := newNetRes[0].Containers[containerID]
   522  	assert.Assert(c, ok)
   523  	assert.Equal(c, container1.Name, newName)
   524  }
   525  
   526  func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnect(c *testing.T) {
   527  	dockerCmd(c, "network", "create", "test")
   528  	assertNwIsAvailable(c, "test")
   529  	nr := getNwResource(c, "test")
   530  
   531  	assert.Equal(c, nr.Name, "test")
   532  	assert.Equal(c, len(nr.Containers), 0)
   533  
   534  	// run a container
   535  	out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
   536  	assert.Assert(c, waitRun("test") == nil)
   537  	containerID := strings.TrimSpace(out)
   538  
   539  	// connect the container to the test network
   540  	dockerCmd(c, "network", "connect", "test", containerID)
   541  
   542  	// inspect the network to make sure container is connected
   543  	nr = getNetworkResource(c, nr.ID)
   544  	assert.Equal(c, len(nr.Containers), 1)
   545  
   546  	// check if container IP matches network inspect
   547  	ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address)
   548  	assert.NilError(c, err)
   549  	containerIP := findContainerIP(c, "test", "test")
   550  	assert.Equal(c, ip.String(), containerIP)
   551  
   552  	// disconnect container from the network
   553  	dockerCmd(c, "network", "disconnect", "test", containerID)
   554  	nr = getNwResource(c, "test")
   555  	assert.Equal(c, nr.Name, "test")
   556  	assert.Equal(c, len(nr.Containers), 0)
   557  
   558  	// run another container
   559  	out, _ = dockerCmd(c, "run", "-d", "--net", "test", "--name", "test2", "busybox", "top")
   560  	assert.Assert(c, waitRun("test2") == nil)
   561  	containerID = strings.TrimSpace(out)
   562  
   563  	nr = getNwResource(c, "test")
   564  	assert.Equal(c, nr.Name, "test")
   565  	assert.Equal(c, len(nr.Containers), 1)
   566  
   567  	// force disconnect the container to the test network
   568  	dockerCmd(c, "network", "disconnect", "-f", "test", containerID)
   569  
   570  	nr = getNwResource(c, "test")
   571  	assert.Equal(c, nr.Name, "test")
   572  	assert.Equal(c, len(nr.Containers), 0)
   573  
   574  	dockerCmd(c, "network", "rm", "test")
   575  	assertNwNotAvailable(c, "test")
   576  }
   577  
   578  func (s *DockerNetworkSuite) TestDockerNetworkIPAMMultipleNetworks(c *testing.T) {
   579  	testRequires(c, testEnv.IsLocalDaemon)
   580  	// test0 bridge network
   581  	dockerCmd(c, "network", "create", "--subnet=192.168.0.0/16", "test1")
   582  	assertNwIsAvailable(c, "test1")
   583  
   584  	// test2 bridge network does not overlap
   585  	dockerCmd(c, "network", "create", "--subnet=192.169.0.0/16", "test2")
   586  	assertNwIsAvailable(c, "test2")
   587  
   588  	// for networks w/o ipam specified, docker will choose proper non-overlapping subnets
   589  	dockerCmd(c, "network", "create", "test3")
   590  	assertNwIsAvailable(c, "test3")
   591  	dockerCmd(c, "network", "create", "test4")
   592  	assertNwIsAvailable(c, "test4")
   593  	dockerCmd(c, "network", "create", "test5")
   594  	assertNwIsAvailable(c, "test5")
   595  
   596  	// test network with multiple subnets
   597  	// bridge network doesn't support multiple subnets. hence, use a dummy driver that supports
   598  
   599  	dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, "--subnet=192.170.0.0/16", "--subnet=192.171.0.0/16", "test6")
   600  	assertNwIsAvailable(c, "test6")
   601  
   602  	// test network with multiple subnets with valid ipam combinations
   603  	// also check same subnet across networks when the driver supports it.
   604  	dockerCmd(c, "network", "create", "-d", dummyNetworkDriver,
   605  		"--subnet=192.172.0.0/16", "--subnet=192.173.0.0/16",
   606  		"--gateway=192.172.0.100", "--gateway=192.173.0.100",
   607  		"--ip-range=192.172.1.0/24",
   608  		"--aux-address", "a=192.172.1.5", "--aux-address", "b=192.172.1.6",
   609  		"--aux-address", "c=192.173.1.5", "--aux-address", "d=192.173.1.6",
   610  		"test7")
   611  	assertNwIsAvailable(c, "test7")
   612  
   613  	// cleanup
   614  	for i := 1; i < 8; i++ {
   615  		dockerCmd(c, "network", "rm", fmt.Sprintf("test%d", i))
   616  	}
   617  }
   618  
   619  func (s *DockerNetworkSuite) TestDockerNetworkCustomIPAM(c *testing.T) {
   620  	testRequires(c, testEnv.IsLocalDaemon)
   621  	// Create a bridge network using custom ipam driver
   622  	dockerCmd(c, "network", "create", "--ipam-driver", dummyIPAMDriver, "br0")
   623  	assertNwIsAvailable(c, "br0")
   624  
   625  	// Verify expected network ipam fields are there
   626  	nr := getNetworkResource(c, "br0")
   627  	assert.Equal(c, nr.Driver, "bridge")
   628  	assert.Equal(c, nr.IPAM.Driver, dummyIPAMDriver)
   629  
   630  	// remove network and exercise remote ipam driver
   631  	dockerCmd(c, "network", "rm", "br0")
   632  	assertNwNotAvailable(c, "br0")
   633  }
   634  
   635  func (s *DockerNetworkSuite) TestDockerNetworkIPAMOptions(c *testing.T) {
   636  	testRequires(c, testEnv.IsLocalDaemon)
   637  	// Create a bridge network using custom ipam driver and options
   638  	dockerCmd(c, "network", "create", "--ipam-driver", dummyIPAMDriver, "--ipam-opt", "opt1=drv1", "--ipam-opt", "opt2=drv2", "br0")
   639  	assertNwIsAvailable(c, "br0")
   640  
   641  	// Verify expected network ipam options
   642  	nr := getNetworkResource(c, "br0")
   643  	opts := nr.IPAM.Options
   644  	assert.Equal(c, opts["opt1"], "drv1")
   645  	assert.Equal(c, opts["opt2"], "drv2")
   646  }
   647  
   648  func (s *DockerNetworkSuite) TestDockerNetworkNullIPAMDriver(c *testing.T) {
   649  	testRequires(c, testEnv.IsLocalDaemon)
   650  	// Create a network with null ipam driver
   651  	_, _, err := dockerCmdWithError("network", "create", "-d", dummyNetworkDriver, "--ipam-driver", "null", "test000")
   652  	assert.NilError(c, err)
   653  	assertNwIsAvailable(c, "test000")
   654  
   655  	// Verify the inspect data contains the default subnet provided by the null
   656  	// ipam driver and no gateway, as the null ipam driver does not provide one
   657  	nr := getNetworkResource(c, "test000")
   658  	assert.Equal(c, nr.IPAM.Driver, "null")
   659  	assert.Equal(c, len(nr.IPAM.Config), 1)
   660  	assert.Equal(c, nr.IPAM.Config[0].Subnet, "0.0.0.0/0")
   661  	assert.Equal(c, nr.IPAM.Config[0].Gateway, "")
   662  }
   663  
   664  func (s *DockerNetworkSuite) TestDockerNetworkInspectDefault(c *testing.T) {
   665  	nr := getNetworkResource(c, "none")
   666  	assert.Equal(c, nr.Driver, "null")
   667  	assert.Equal(c, nr.Scope, "local")
   668  	assert.Equal(c, nr.Internal, false)
   669  	assert.Equal(c, nr.EnableIPv6, false)
   670  	assert.Equal(c, nr.IPAM.Driver, "default")
   671  	assert.Equal(c, len(nr.IPAM.Config), 0)
   672  
   673  	nr = getNetworkResource(c, "host")
   674  	assert.Equal(c, nr.Driver, "host")
   675  	assert.Equal(c, nr.Scope, "local")
   676  	assert.Equal(c, nr.Internal, false)
   677  	assert.Equal(c, nr.EnableIPv6, false)
   678  	assert.Equal(c, nr.IPAM.Driver, "default")
   679  	assert.Equal(c, len(nr.IPAM.Config), 0)
   680  
   681  	nr = getNetworkResource(c, "bridge")
   682  	assert.Equal(c, nr.Driver, "bridge")
   683  	assert.Equal(c, nr.Scope, "local")
   684  	assert.Equal(c, nr.Internal, false)
   685  	assert.Equal(c, nr.EnableIPv6, false)
   686  	assert.Equal(c, nr.IPAM.Driver, "default")
   687  	assert.Equal(c, len(nr.IPAM.Config), 1)
   688  }
   689  
   690  func (s *DockerNetworkSuite) TestDockerNetworkInspectCustomUnspecified(c *testing.T) {
   691  	// if unspecified, network subnet will be selected from inside preferred pool
   692  	dockerCmd(c, "network", "create", "test01")
   693  	assertNwIsAvailable(c, "test01")
   694  
   695  	nr := getNetworkResource(c, "test01")
   696  	assert.Equal(c, nr.Driver, "bridge")
   697  	assert.Equal(c, nr.Scope, "local")
   698  	assert.Equal(c, nr.Internal, false)
   699  	assert.Equal(c, nr.EnableIPv6, false)
   700  	assert.Equal(c, nr.IPAM.Driver, "default")
   701  	assert.Equal(c, len(nr.IPAM.Config), 1)
   702  
   703  	dockerCmd(c, "network", "rm", "test01")
   704  	assertNwNotAvailable(c, "test01")
   705  }
   706  
   707  func (s *DockerNetworkSuite) TestDockerNetworkInspectCustomSpecified(c *testing.T) {
   708  	dockerCmd(c, "network", "create", "--driver=bridge", "--ipv6", "--subnet=fd80:24e2:f998:72d6::/64", "--subnet=172.28.0.0/16", "--ip-range=172.28.5.0/24", "--gateway=172.28.5.254", "br0")
   709  	assertNwIsAvailable(c, "br0")
   710  
   711  	nr := getNetworkResource(c, "br0")
   712  	assert.Equal(c, nr.Driver, "bridge")
   713  	assert.Equal(c, nr.Scope, "local")
   714  	assert.Equal(c, nr.Internal, false)
   715  	assert.Equal(c, nr.EnableIPv6, true)
   716  	assert.Equal(c, nr.IPAM.Driver, "default")
   717  	assert.Equal(c, len(nr.IPAM.Config), 2)
   718  	assert.Equal(c, nr.IPAM.Config[0].Subnet, "172.28.0.0/16")
   719  	assert.Equal(c, nr.IPAM.Config[0].IPRange, "172.28.5.0/24")
   720  	assert.Equal(c, nr.IPAM.Config[0].Gateway, "172.28.5.254")
   721  	assert.Equal(c, nr.Internal, false)
   722  	dockerCmd(c, "network", "rm", "br0")
   723  	assertNwNotAvailable(c, "br0")
   724  }
   725  
   726  func (s *DockerNetworkSuite) TestDockerNetworkIPAMInvalidCombinations(c *testing.T) {
   727  	// network with ip-range out of subnet range
   728  	_, _, err := dockerCmdWithError("network", "create", "--subnet=192.168.0.0/16", "--ip-range=192.170.0.0/16", "test")
   729  	assert.ErrorContains(c, err, "")
   730  
   731  	// network with multiple gateways for a single subnet
   732  	_, _, err = dockerCmdWithError("network", "create", "--subnet=192.168.0.0/16", "--gateway=192.168.0.1", "--gateway=192.168.0.2", "test")
   733  	assert.ErrorContains(c, err, "")
   734  
   735  	// Multiple overlapping subnets in the same network must fail
   736  	_, _, err = dockerCmdWithError("network", "create", "--subnet=192.168.0.0/16", "--subnet=192.168.1.0/16", "test")
   737  	assert.ErrorContains(c, err, "")
   738  
   739  	// overlapping subnets across networks must fail
   740  	// create a valid test0 network
   741  	dockerCmd(c, "network", "create", "--subnet=192.168.0.0/16", "test0")
   742  	assertNwIsAvailable(c, "test0")
   743  	// create an overlapping test1 network
   744  	_, _, err = dockerCmdWithError("network", "create", "--subnet=192.168.128.0/17", "test1")
   745  	assert.ErrorContains(c, err, "")
   746  	dockerCmd(c, "network", "rm", "test0")
   747  	assertNwNotAvailable(c, "test0")
   748  }
   749  
   750  func (s *DockerNetworkSuite) TestDockerNetworkDriverOptions(c *testing.T) {
   751  	testRequires(c, testEnv.IsLocalDaemon)
   752  	dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, "-o", "opt1=drv1", "-o", "opt2=drv2", "testopt")
   753  	assertNwIsAvailable(c, "testopt")
   754  	gopts := remoteDriverNetworkRequest.Options[netlabel.GenericData]
   755  	assert.Assert(c, gopts != nil)
   756  	opts, ok := gopts.(map[string]interface{})
   757  	assert.Equal(c, ok, true)
   758  	assert.Equal(c, opts["opt1"], "drv1")
   759  	assert.Equal(c, opts["opt2"], "drv2")
   760  	dockerCmd(c, "network", "rm", "testopt")
   761  	assertNwNotAvailable(c, "testopt")
   762  
   763  }
   764  
   765  func (s *DockerNetworkSuite) TestDockerPluginV2NetworkDriver(c *testing.T) {
   766  	testRequires(c, DaemonIsLinux, IsAmd64, Network)
   767  
   768  	var (
   769  		npName        = "tiborvass/test-docker-netplugin"
   770  		npTag         = "latest"
   771  		npNameWithTag = npName + ":" + npTag
   772  	)
   773  	_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", npNameWithTag)
   774  	assert.NilError(c, err)
   775  
   776  	out, _, err := dockerCmdWithError("plugin", "ls")
   777  	assert.NilError(c, err)
   778  	assert.Assert(c, strings.Contains(out, npName))
   779  	assert.Assert(c, strings.Contains(out, npTag))
   780  	assert.Assert(c, strings.Contains(out, "true"))
   781  	dockerCmd(c, "network", "create", "-d", npNameWithTag, "v2net")
   782  	assertNwIsAvailable(c, "v2net")
   783  	dockerCmd(c, "network", "rm", "v2net")
   784  	assertNwNotAvailable(c, "v2net")
   785  
   786  }
   787  
   788  func (s *DockerDaemonSuite) TestDockerNetworkNoDiscoveryDefaultBridgeNetwork(c *testing.T) {
   789  	// On default bridge network built-in service discovery should not happen
   790  	hostsFile := "/etc/hosts"
   791  	bridgeName := "external-bridge"
   792  	bridgeIP := "192.169.255.254/24"
   793  	createInterface(c, "bridge", bridgeName, bridgeIP)
   794  	defer deleteInterface(c, bridgeName)
   795  
   796  	s.d.StartWithBusybox(c, "--bridge", bridgeName)
   797  	defer s.d.Restart(c)
   798  
   799  	// run two containers and store first container's etc/hosts content
   800  	out, err := s.d.Cmd("run", "-d", "busybox", "top")
   801  	assert.NilError(c, err)
   802  	cid1 := strings.TrimSpace(out)
   803  	defer s.d.Cmd("stop", cid1)
   804  
   805  	hosts, err := s.d.Cmd("exec", cid1, "cat", hostsFile)
   806  	assert.NilError(c, err)
   807  
   808  	out, err = s.d.Cmd("run", "-d", "--name", "container2", "busybox", "top")
   809  	assert.NilError(c, err)
   810  	cid2 := strings.TrimSpace(out)
   811  
   812  	// verify first container's etc/hosts file has not changed after spawning the second named container
   813  	hostsPost, err := s.d.Cmd("exec", cid1, "cat", hostsFile)
   814  	assert.NilError(c, err)
   815  	assert.Equal(c, hosts, hostsPost, fmt.Sprintf("Unexpected %s change on second container creation", hostsFile))
   816  	// stop container 2 and verify first container's etc/hosts has not changed
   817  	_, err = s.d.Cmd("stop", cid2)
   818  	assert.NilError(c, err)
   819  
   820  	hostsPost, err = s.d.Cmd("exec", cid1, "cat", hostsFile)
   821  	assert.NilError(c, err)
   822  	assert.Equal(c, hosts, hostsPost, fmt.Sprintf("Unexpected %s change on second container creation", hostsFile))
   823  	// but discovery is on when connecting to non default bridge network
   824  	network := "anotherbridge"
   825  	out, err = s.d.Cmd("network", "create", network)
   826  	assert.NilError(c, err, out)
   827  	defer s.d.Cmd("network", "rm", network)
   828  
   829  	out, err = s.d.Cmd("network", "connect", network, cid1)
   830  	assert.NilError(c, err, out)
   831  
   832  	hosts, err = s.d.Cmd("exec", cid1, "cat", hostsFile)
   833  	assert.NilError(c, err)
   834  
   835  	hostsPost, err = s.d.Cmd("exec", cid1, "cat", hostsFile)
   836  	assert.NilError(c, err)
   837  	assert.Equal(c, hosts, hostsPost, fmt.Sprintf("Unexpected %s change on second network connection", hostsFile))
   838  }
   839  
   840  func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *testing.T) {
   841  	testRequires(c, NotArm)
   842  	hostsFile := "/etc/hosts"
   843  	cstmBridgeNw := "custom-bridge-nw"
   844  	cstmBridgeNw1 := "custom-bridge-nw1"
   845  
   846  	dockerCmd(c, "network", "create", "-d", "bridge", cstmBridgeNw)
   847  	assertNwIsAvailable(c, cstmBridgeNw)
   848  
   849  	// run two anonymous containers and store their etc/hosts content
   850  	out, _ := dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "busybox", "top")
   851  	cid1 := strings.TrimSpace(out)
   852  
   853  	hosts1 := readContainerFileWithExec(c, cid1, hostsFile)
   854  
   855  	out, _ = dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "busybox", "top")
   856  	cid2 := strings.TrimSpace(out)
   857  
   858  	// verify first container etc/hosts file has not changed
   859  	hosts1post := readContainerFileWithExec(c, cid1, hostsFile)
   860  	assert.Equal(c, string(hosts1), string(hosts1post), fmt.Sprintf("Unexpected %s change on anonymous container creation", hostsFile))
   861  	// Connect the 2nd container to a new network and verify the
   862  	// first container /etc/hosts file still hasn't changed.
   863  	dockerCmd(c, "network", "create", "-d", "bridge", cstmBridgeNw1)
   864  	assertNwIsAvailable(c, cstmBridgeNw1)
   865  
   866  	dockerCmd(c, "network", "connect", cstmBridgeNw1, cid2)
   867  
   868  	hosts2 := readContainerFileWithExec(c, cid2, hostsFile)
   869  	hosts1post = readContainerFileWithExec(c, cid1, hostsFile)
   870  	assert.Equal(c, string(hosts1), string(hosts1post), fmt.Sprintf("Unexpected %s change on container connect", hostsFile))
   871  	// start a named container
   872  	cName := "AnyName"
   873  	out, _ = dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "--name", cName, "busybox", "top")
   874  	cid3 := strings.TrimSpace(out)
   875  
   876  	// verify that container 1 and 2 can ping the named container
   877  	dockerCmd(c, "exec", cid1, "ping", "-c", "1", cName)
   878  	dockerCmd(c, "exec", cid2, "ping", "-c", "1", cName)
   879  
   880  	// Stop named container and verify first two containers' etc/hosts file hasn't changed
   881  	dockerCmd(c, "stop", cid3)
   882  	hosts1post = readContainerFileWithExec(c, cid1, hostsFile)
   883  	assert.Equal(c, string(hosts1), string(hosts1post), fmt.Sprintf("Unexpected %s change on name container creation", hostsFile))
   884  	hosts2post := readContainerFileWithExec(c, cid2, hostsFile)
   885  	assert.Equal(c, string(hosts2), string(hosts2post), fmt.Sprintf("Unexpected %s change on name container creation", hostsFile))
   886  	// verify that container 1 and 2 can't ping the named container now
   887  	_, _, err := dockerCmdWithError("exec", cid1, "ping", "-c", "1", cName)
   888  	assert.ErrorContains(c, err, "")
   889  	_, _, err = dockerCmdWithError("exec", cid2, "ping", "-c", "1", cName)
   890  	assert.ErrorContains(c, err, "")
   891  }
   892  
   893  func (s *DockerNetworkSuite) TestDockerNetworkLinkOnDefaultNetworkOnly(c *testing.T) {
   894  	// Legacy Link feature must work only on default network, and not across networks
   895  	cnt1 := "container1"
   896  	cnt2 := "container2"
   897  	network := "anotherbridge"
   898  
   899  	// Run first container on default network
   900  	dockerCmd(c, "run", "-d", "--name", cnt1, "busybox", "top")
   901  
   902  	// Create another network and run the second container on it
   903  	dockerCmd(c, "network", "create", network)
   904  	assertNwIsAvailable(c, network)
   905  	dockerCmd(c, "run", "-d", "--net", network, "--name", cnt2, "busybox", "top")
   906  
   907  	// Try launching a container on default network, linking to the first container. Must succeed
   908  	dockerCmd(c, "run", "-d", "--link", fmt.Sprintf("%s:%s", cnt1, cnt1), "busybox", "top")
   909  
   910  	// Try launching a container on default network, linking to the second container. Must fail
   911  	_, _, err := dockerCmdWithError("run", "-d", "--link", fmt.Sprintf("%s:%s", cnt2, cnt2), "busybox", "top")
   912  	assert.ErrorContains(c, err, "")
   913  
   914  	// Connect second container to default network. Now a container on default network can link to it
   915  	dockerCmd(c, "network", "connect", "bridge", cnt2)
   916  	dockerCmd(c, "run", "-d", "--link", fmt.Sprintf("%s:%s", cnt2, cnt2), "busybox", "top")
   917  }
   918  
   919  func (s *DockerNetworkSuite) TestDockerNetworkOverlayPortMapping(c *testing.T) {
   920  	testRequires(c, testEnv.IsLocalDaemon)
   921  	// Verify exposed ports are present in ps output when running a container on
   922  	// a network managed by a driver which does not provide the default gateway
   923  	// for the container
   924  	nwn := "ov"
   925  	ctn := "bb"
   926  	port1 := 80
   927  	port2 := 443
   928  	expose1 := fmt.Sprintf("--expose=%d", port1)
   929  	expose2 := fmt.Sprintf("--expose=%d", port2)
   930  
   931  	dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, nwn)
   932  	assertNwIsAvailable(c, nwn)
   933  
   934  	dockerCmd(c, "run", "-d", "--net", nwn, "--name", ctn, expose1, expose2, "busybox", "top")
   935  
   936  	// Check docker ps o/p for last created container reports the unpublished ports
   937  	unpPort1 := fmt.Sprintf("%d/tcp", port1)
   938  	unpPort2 := fmt.Sprintf("%d/tcp", port2)
   939  	out, _ := dockerCmd(c, "ps", "-n=1")
   940  	// Missing unpublished ports in docker ps output
   941  	assert.Assert(c, strings.Contains(out, unpPort1))
   942  	// Missing unpublished ports in docker ps output
   943  	assert.Assert(c, strings.Contains(out, unpPort2))
   944  }
   945  
   946  func (s *DockerNetworkSuite) TestDockerNetworkDriverUngracefulRestart(c *testing.T) {
   947  	testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon)
   948  	dnd := "dnd"
   949  	did := "did"
   950  
   951  	mux := http.NewServeMux()
   952  	server := httptest.NewServer(mux)
   953  	setupRemoteNetworkDrivers(c, mux, server.URL, dnd, did)
   954  
   955  	s.d.StartWithBusybox(c)
   956  	_, err := s.d.Cmd("network", "create", "-d", dnd, "--subnet", "1.1.1.0/24", "net1")
   957  	assert.NilError(c, err)
   958  
   959  	_, err = s.d.Cmd("run", "-d", "--net", "net1", "--name", "foo", "--ip", "1.1.1.10", "busybox", "top")
   960  	assert.NilError(c, err)
   961  
   962  	// Kill daemon and restart
   963  	assert.Assert(c, s.d.Kill() == nil)
   964  
   965  	server.Close()
   966  
   967  	startTime := time.Now().Unix()
   968  	s.d.Restart(c)
   969  	lapse := time.Now().Unix() - startTime
   970  	if lapse > 60 {
   971  		// In normal scenarios, daemon restart takes ~1 second.
   972  		// Plugin retry mechanism can delay the daemon start. systemd may not like it.
   973  		// Avoid accessing plugins during daemon bootup
   974  		c.Logf("daemon restart took too long : %d seconds", lapse)
   975  	}
   976  
   977  	// Restart the custom dummy plugin
   978  	mux = http.NewServeMux()
   979  	server = httptest.NewServer(mux)
   980  	setupRemoteNetworkDrivers(c, mux, server.URL, dnd, did)
   981  
   982  	// trying to reuse the same ip must succeed
   983  	_, err = s.d.Cmd("run", "-d", "--net", "net1", "--name", "bar", "--ip", "1.1.1.10", "busybox", "top")
   984  	assert.NilError(c, err)
   985  }
   986  
   987  func (s *DockerNetworkSuite) TestDockerNetworkMacInspect(c *testing.T) {
   988  	testRequires(c, testEnv.IsLocalDaemon)
   989  	// Verify endpoint MAC address is correctly populated in container's network settings
   990  	nwn := "ov"
   991  	ctn := "bb"
   992  
   993  	dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, nwn)
   994  	assertNwIsAvailable(c, nwn)
   995  
   996  	dockerCmd(c, "run", "-d", "--net", nwn, "--name", ctn, "busybox", "top")
   997  
   998  	mac := inspectField(c, ctn, "NetworkSettings.Networks."+nwn+".MacAddress")
   999  	assert.Equal(c, mac, "a0:b1:c2:d3:e4:f5")
  1000  }
  1001  
  1002  func (s *DockerSuite) TestInspectAPIMultipleNetworks(c *testing.T) {
  1003  	dockerCmd(c, "network", "create", "mybridge1")
  1004  	dockerCmd(c, "network", "create", "mybridge2")
  1005  	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
  1006  	id := strings.TrimSpace(out)
  1007  	assert.NilError(c, waitRun(id))
  1008  
  1009  	dockerCmd(c, "network", "connect", "mybridge1", id)
  1010  	dockerCmd(c, "network", "connect", "mybridge2", id)
  1011  
  1012  	body := getInspectBody(c, "v1.20", id)
  1013  	var inspect120 v1p20.ContainerJSON
  1014  	err := json.Unmarshal(body, &inspect120)
  1015  	assert.NilError(c, err)
  1016  
  1017  	versionedIP := inspect120.NetworkSettings.IPAddress
  1018  
  1019  	body = getInspectBody(c, "v1.21", id)
  1020  	var inspect121 types.ContainerJSON
  1021  	err = json.Unmarshal(body, &inspect121)
  1022  	assert.NilError(c, err)
  1023  	assert.Equal(c, len(inspect121.NetworkSettings.Networks), 3)
  1024  
  1025  	bridge := inspect121.NetworkSettings.Networks["bridge"]
  1026  	assert.Equal(c, bridge.IPAddress, versionedIP)
  1027  	assert.Equal(c, bridge.IPAddress, inspect121.NetworkSettings.IPAddress)
  1028  }
  1029  
  1030  func connectContainerToNetworks(c *testing.T, d *daemon.Daemon, cName string, nws []string) {
  1031  	// Run a container on the default network
  1032  	out, err := d.Cmd("run", "-d", "--name", cName, "busybox", "top")
  1033  	assert.NilError(c, err, out)
  1034  
  1035  	// Attach the container to other networks
  1036  	for _, nw := range nws {
  1037  		out, err = d.Cmd("network", "create", nw)
  1038  		assert.NilError(c, err, out)
  1039  		out, err = d.Cmd("network", "connect", nw, cName)
  1040  		assert.NilError(c, err, out)
  1041  	}
  1042  }
  1043  
  1044  func verifyContainerIsConnectedToNetworks(c *testing.T, d *daemon.Daemon, cName string, nws []string) {
  1045  	// Verify container is connected to all the networks
  1046  	for _, nw := range nws {
  1047  		out, err := d.Cmd("inspect", "-f", fmt.Sprintf("{{.NetworkSettings.Networks.%s}}", nw), cName)
  1048  		assert.NilError(c, err, out)
  1049  		assert.Assert(c, out != "<no value>\n")
  1050  	}
  1051  }
  1052  
  1053  func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksGracefulDaemonRestart(c *testing.T) {
  1054  	testRequires(c, testEnv.IsLocalDaemon)
  1055  	cName := "bb"
  1056  	nwList := []string{"nw1", "nw2", "nw3"}
  1057  
  1058  	s.d.StartWithBusybox(c)
  1059  
  1060  	connectContainerToNetworks(c, s.d, cName, nwList)
  1061  	verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList)
  1062  
  1063  	// Reload daemon
  1064  	s.d.Restart(c)
  1065  
  1066  	_, err := s.d.Cmd("start", cName)
  1067  	assert.NilError(c, err)
  1068  
  1069  	verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList)
  1070  }
  1071  
  1072  func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksUngracefulDaemonRestart(c *testing.T) {
  1073  	testRequires(c, testEnv.IsLocalDaemon)
  1074  	cName := "cc"
  1075  	nwList := []string{"nw1", "nw2", "nw3"}
  1076  
  1077  	s.d.StartWithBusybox(c)
  1078  
  1079  	connectContainerToNetworks(c, s.d, cName, nwList)
  1080  	verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList)
  1081  
  1082  	// Kill daemon and restart
  1083  	assert.Assert(c, s.d.Kill() == nil)
  1084  	s.d.Restart(c)
  1085  
  1086  	// Restart container
  1087  	_, err := s.d.Cmd("start", cName)
  1088  	assert.NilError(c, err)
  1089  
  1090  	verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList)
  1091  }
  1092  
  1093  func (s *DockerNetworkSuite) TestDockerNetworkRunNetByID(c *testing.T) {
  1094  	out, _ := dockerCmd(c, "network", "create", "one")
  1095  	containerOut, _, err := dockerCmdWithError("run", "-d", "--net", strings.TrimSpace(out), "busybox", "top")
  1096  	assert.Assert(c, err == nil, containerOut)
  1097  }
  1098  
  1099  func (s *DockerNetworkSuite) TestDockerNetworkHostModeUngracefulDaemonRestart(c *testing.T) {
  1100  	testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon)
  1101  	s.d.StartWithBusybox(c)
  1102  
  1103  	// Run a few containers on host network
  1104  	for i := 0; i < 10; i++ {
  1105  		cName := fmt.Sprintf("hostc-%d", i)
  1106  		out, err := s.d.Cmd("run", "-d", "--name", cName, "--net=host", "--restart=always", "busybox", "top")
  1107  		assert.NilError(c, err, out)
  1108  
  1109  		// verify container has finished starting before killing daemon
  1110  		err = s.d.WaitRun(cName)
  1111  		assert.NilError(c, err)
  1112  	}
  1113  
  1114  	// Kill daemon ungracefully and restart
  1115  	assert.Assert(c, s.d.Kill() == nil)
  1116  	s.d.Restart(c)
  1117  
  1118  	// make sure all the containers are up and running
  1119  	for i := 0; i < 10; i++ {
  1120  		err := s.d.WaitRun(fmt.Sprintf("hostc-%d", i))
  1121  		assert.NilError(c, err)
  1122  	}
  1123  }
  1124  
  1125  func (s *DockerNetworkSuite) TestDockerNetworkConnectToHostFromOtherNetwork(c *testing.T) {
  1126  	dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top")
  1127  	assert.Assert(c, waitRun("container1") == nil)
  1128  	dockerCmd(c, "network", "disconnect", "bridge", "container1")
  1129  	out, _, err := dockerCmdWithError("network", "connect", "host", "container1")
  1130  	assert.ErrorContains(c, err, "", out)
  1131  	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictHostNetwork.Error()))
  1132  }
  1133  
  1134  func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromHost(c *testing.T) {
  1135  	dockerCmd(c, "run", "-d", "--name", "container1", "--net=host", "busybox", "top")
  1136  	assert.Assert(c, waitRun("container1") == nil)
  1137  	out, _, err := dockerCmdWithError("network", "disconnect", "host", "container1")
  1138  	assert.Assert(c, err != nil, "Should err out disconnect from host")
  1139  	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictHostNetwork.Error()))
  1140  }
  1141  
  1142  func (s *DockerNetworkSuite) TestDockerNetworkConnectWithPortMapping(c *testing.T) {
  1143  	testRequires(c, NotArm)
  1144  	dockerCmd(c, "network", "create", "test1")
  1145  	dockerCmd(c, "run", "-d", "--name", "c1", "-p", "5000:5000", "busybox", "top")
  1146  	assert.Assert(c, waitRun("c1") == nil)
  1147  	dockerCmd(c, "network", "connect", "test1", "c1")
  1148  }
  1149  
  1150  func verifyPortMap(c *testing.T, container, port, originalMapping string, mustBeEqual bool) {
  1151  	currentMapping, _ := dockerCmd(c, "port", container, port)
  1152  	if mustBeEqual {
  1153  		assert.Equal(c, currentMapping, originalMapping)
  1154  	} else {
  1155  		assert.Assert(c, currentMapping != originalMapping)
  1156  	}
  1157  }
  1158  
  1159  func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectWithPortMapping(c *testing.T) {
  1160  	// Connect and disconnect a container with explicit and non-explicit
  1161  	// host port mapping to/from networks which do cause and do not cause
  1162  	// the container default gateway to change, and verify docker port cmd
  1163  	// returns congruent information
  1164  	testRequires(c, NotArm)
  1165  	cnt := "c1"
  1166  	dockerCmd(c, "network", "create", "aaa")
  1167  	dockerCmd(c, "network", "create", "ccc")
  1168  
  1169  	dockerCmd(c, "run", "-d", "--name", cnt, "-p", "9000:90", "-p", "70", "busybox", "top")
  1170  	assert.Assert(c, waitRun(cnt) == nil)
  1171  	curPortMap, _ := dockerCmd(c, "port", cnt, "70")
  1172  	curExplPortMap, _ := dockerCmd(c, "port", cnt, "90")
  1173  
  1174  	// Connect to a network which causes the container's default gw switch
  1175  	dockerCmd(c, "network", "connect", "aaa", cnt)
  1176  	verifyPortMap(c, cnt, "70", curPortMap, false)
  1177  	verifyPortMap(c, cnt, "90", curExplPortMap, true)
  1178  
  1179  	// Read current mapping
  1180  	curPortMap, _ = dockerCmd(c, "port", cnt, "70")
  1181  
  1182  	// Disconnect from a network which causes the container's default gw switch
  1183  	dockerCmd(c, "network", "disconnect", "aaa", cnt)
  1184  	verifyPortMap(c, cnt, "70", curPortMap, false)
  1185  	verifyPortMap(c, cnt, "90", curExplPortMap, true)
  1186  
  1187  	// Read current mapping
  1188  	curPortMap, _ = dockerCmd(c, "port", cnt, "70")
  1189  
  1190  	// Connect to a network which does not cause the container's default gw switch
  1191  	dockerCmd(c, "network", "connect", "ccc", cnt)
  1192  	verifyPortMap(c, cnt, "70", curPortMap, true)
  1193  	verifyPortMap(c, cnt, "90", curExplPortMap, true)
  1194  }
  1195  
  1196  func (s *DockerNetworkSuite) TestDockerNetworkConnectWithMac(c *testing.T) {
  1197  	macAddress := "02:42:ac:11:00:02"
  1198  	dockerCmd(c, "network", "create", "mynetwork")
  1199  	dockerCmd(c, "run", "--name=test", "-d", "--mac-address", macAddress, "busybox", "top")
  1200  	assert.Assert(c, waitRun("test") == nil)
  1201  	mac1 := inspectField(c, "test", "NetworkSettings.Networks.bridge.MacAddress")
  1202  	assert.Equal(c, strings.TrimSpace(mac1), macAddress)
  1203  	dockerCmd(c, "network", "connect", "mynetwork", "test")
  1204  	mac2 := inspectField(c, "test", "NetworkSettings.Networks.mynetwork.MacAddress")
  1205  	assert.Assert(c, strings.TrimSpace(mac2) != strings.TrimSpace(mac1))
  1206  }
  1207  
  1208  func (s *DockerNetworkSuite) TestDockerNetworkInspectCreatedContainer(c *testing.T) {
  1209  	dockerCmd(c, "create", "--name", "test", "busybox")
  1210  	networks := inspectField(c, "test", "NetworkSettings.Networks")
  1211  	assert.Assert(c, strings.Contains(networks, "bridge"), "Should return 'bridge' network")
  1212  }
  1213  
  1214  func (s *DockerNetworkSuite) TestDockerNetworkRestartWithMultipleNetworks(c *testing.T) {
  1215  	dockerCmd(c, "network", "create", "test")
  1216  	dockerCmd(c, "run", "--name=foo", "-d", "busybox", "top")
  1217  	assert.Assert(c, waitRun("foo") == nil)
  1218  	dockerCmd(c, "network", "connect", "test", "foo")
  1219  	dockerCmd(c, "restart", "foo")
  1220  	networks := inspectField(c, "foo", "NetworkSettings.Networks")
  1221  	assert.Assert(c, strings.Contains(networks, "bridge"), "Should contain 'bridge' network")
  1222  	assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network")
  1223  }
  1224  
  1225  func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectToStoppedContainer(c *testing.T) {
  1226  	testRequires(c, testEnv.IsLocalDaemon)
  1227  	dockerCmd(c, "network", "create", "test")
  1228  	dockerCmd(c, "create", "--name=foo", "busybox", "top")
  1229  	dockerCmd(c, "network", "connect", "test", "foo")
  1230  	networks := inspectField(c, "foo", "NetworkSettings.Networks")
  1231  	assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network")
  1232  	// Restart docker daemon to test the config has persisted to disk
  1233  	s.d.Restart(c)
  1234  	networks = inspectField(c, "foo", "NetworkSettings.Networks")
  1235  	assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network")
  1236  	// start the container and test if we can ping it from another container in the same network
  1237  	dockerCmd(c, "start", "foo")
  1238  	assert.Assert(c, waitRun("foo") == nil)
  1239  	ip := inspectField(c, "foo", "NetworkSettings.Networks.test.IPAddress")
  1240  	ip = strings.TrimSpace(ip)
  1241  	dockerCmd(c, "run", "--net=test", "busybox", "sh", "-c", fmt.Sprintf("ping -c 1 %s", ip))
  1242  
  1243  	dockerCmd(c, "stop", "foo")
  1244  
  1245  	// Test disconnect
  1246  	dockerCmd(c, "network", "disconnect", "test", "foo")
  1247  	networks = inspectField(c, "foo", "NetworkSettings.Networks")
  1248  	assert.Assert(c, !strings.Contains(networks, "test"), "Should not contain 'test' network")
  1249  	// Restart docker daemon to test the config has persisted to disk
  1250  	s.d.Restart(c)
  1251  	networks = inspectField(c, "foo", "NetworkSettings.Networks")
  1252  	assert.Assert(c, !strings.Contains(networks, "test"), "Should not contain 'test' network")
  1253  }
  1254  
  1255  func (s *DockerNetworkSuite) TestDockerNetworkDisconnectContainerNonexistingNetwork(c *testing.T) {
  1256  	dockerCmd(c, "network", "create", "test")
  1257  	dockerCmd(c, "run", "--net=test", "-d", "--name=foo", "busybox", "top")
  1258  	networks := inspectField(c, "foo", "NetworkSettings.Networks")
  1259  	assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network")
  1260  	// Stop container and remove network
  1261  	dockerCmd(c, "stop", "foo")
  1262  	dockerCmd(c, "network", "rm", "test")
  1263  
  1264  	// Test disconnecting stopped container from nonexisting network
  1265  	dockerCmd(c, "network", "disconnect", "-f", "test", "foo")
  1266  	networks = inspectField(c, "foo", "NetworkSettings.Networks")
  1267  	assert.Assert(c, !strings.Contains(networks, "test"), "Should not contain 'test' network")
  1268  }
  1269  
  1270  func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *testing.T) {
  1271  	// create two networks
  1272  	dockerCmd(c, "network", "create", "--ipv6", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "n0")
  1273  	assertNwIsAvailable(c, "n0")
  1274  
  1275  	dockerCmd(c, "network", "create", "--ipv6", "--subnet=172.30.0.0/16", "--ip-range=172.30.5.0/24", "--subnet=2001:db8:abcd::/64", "--ip-range=2001:db8:abcd::/80", "n1")
  1276  	assertNwIsAvailable(c, "n1")
  1277  
  1278  	// run a container on first network specifying the ip addresses
  1279  	dockerCmd(c, "run", "-d", "--name", "c0", "--net=n0", "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top")
  1280  	assert.Assert(c, waitRun("c0") == nil)
  1281  	verifyIPAddressConfig(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988")
  1282  	verifyIPAddresses(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988")
  1283  
  1284  	// connect the container to the second network specifying an ip addresses
  1285  	dockerCmd(c, "network", "connect", "--ip", "172.30.55.44", "--ip6", "2001:db8:abcd::5544", "n1", "c0")
  1286  	verifyIPAddressConfig(c, "c0", "n1", "172.30.55.44", "2001:db8:abcd::5544")
  1287  	verifyIPAddresses(c, "c0", "n1", "172.30.55.44", "2001:db8:abcd::5544")
  1288  
  1289  	// Stop and restart the container
  1290  	dockerCmd(c, "stop", "c0")
  1291  	dockerCmd(c, "start", "c0")
  1292  
  1293  	// verify requested addresses are applied and configs are still there
  1294  	verifyIPAddressConfig(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988")
  1295  	verifyIPAddresses(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988")
  1296  	verifyIPAddressConfig(c, "c0", "n1", "172.30.55.44", "2001:db8:abcd::5544")
  1297  	verifyIPAddresses(c, "c0", "n1", "172.30.55.44", "2001:db8:abcd::5544")
  1298  
  1299  	// Still it should fail to connect to the default network with a specified IP (whatever ip)
  1300  	out, _, err := dockerCmdWithError("network", "connect", "--ip", "172.21.55.44", "bridge", "c0")
  1301  	assert.Assert(c, err != nil, "out: %s", out)
  1302  	assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndIP.Error()))
  1303  }
  1304  
  1305  func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIPStoppedContainer(c *testing.T) {
  1306  	// create a container
  1307  	dockerCmd(c, "create", "--name", "c0", "busybox", "top")
  1308  
  1309  	// create a network
  1310  	dockerCmd(c, "network", "create", "--ipv6", "--subnet=172.30.0.0/16", "--subnet=2001:db8:abcd::/64", "n0")
  1311  	assertNwIsAvailable(c, "n0")
  1312  
  1313  	// connect the container to the network specifying an ip addresses
  1314  	dockerCmd(c, "network", "connect", "--ip", "172.30.55.44", "--ip6", "2001:db8:abcd::5544", "n0", "c0")
  1315  	verifyIPAddressConfig(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544")
  1316  
  1317  	// start the container, verify config has not changed and ip addresses are assigned
  1318  	dockerCmd(c, "start", "c0")
  1319  	assert.Assert(c, waitRun("c0") == nil)
  1320  	verifyIPAddressConfig(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544")
  1321  	verifyIPAddresses(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544")
  1322  
  1323  	// stop the container and check ip config has not changed
  1324  	dockerCmd(c, "stop", "c0")
  1325  	verifyIPAddressConfig(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544")
  1326  }
  1327  
  1328  func (s *DockerNetworkSuite) TestDockerNetworkUnsupportedRequiredIP(c *testing.T) {
  1329  	// requested IP is not supported on predefined networks
  1330  	for _, mode := range []string{"none", "host", "bridge", "default"} {
  1331  		checkUnsupportedNetworkAndIP(c, mode)
  1332  	}
  1333  
  1334  	// requested IP is not supported on networks with no user defined subnets
  1335  	dockerCmd(c, "network", "create", "n0")
  1336  	assertNwIsAvailable(c, "n0")
  1337  
  1338  	out, _, err := dockerCmdWithError("run", "-d", "--ip", "172.28.99.88", "--net", "n0", "busybox", "top")
  1339  	assert.Assert(c, err != nil, "out: %s", out)
  1340  	assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error()))
  1341  	out, _, err = dockerCmdWithError("run", "-d", "--ip6", "2001:db8:1234::9988", "--net", "n0", "busybox", "top")
  1342  	assert.Assert(c, err != nil, "out: %s", out)
  1343  	assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error()))
  1344  	dockerCmd(c, "network", "rm", "n0")
  1345  	assertNwNotAvailable(c, "n0")
  1346  }
  1347  
  1348  func checkUnsupportedNetworkAndIP(c *testing.T, nwMode string) {
  1349  	out, _, err := dockerCmdWithError("run", "-d", "--net", nwMode, "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top")
  1350  	assert.Assert(c, err != nil, "out: %s", out)
  1351  	assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndIP.Error()))
  1352  }
  1353  
  1354  func verifyIPAddressConfig(c *testing.T, cName, nwname, ipv4, ipv6 string) {
  1355  	if ipv4 != "" {
  1356  		out := inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.IPAMConfig.IPv4Address", nwname))
  1357  		assert.Equal(c, strings.TrimSpace(out), ipv4)
  1358  	}
  1359  
  1360  	if ipv6 != "" {
  1361  		out := inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.IPAMConfig.IPv6Address", nwname))
  1362  		assert.Equal(c, strings.TrimSpace(out), ipv6)
  1363  	}
  1364  }
  1365  
  1366  func verifyIPAddresses(c *testing.T, cName, nwname, ipv4, ipv6 string) {
  1367  	out := inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.IPAddress", nwname))
  1368  	assert.Equal(c, strings.TrimSpace(out), ipv4)
  1369  
  1370  	out = inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.GlobalIPv6Address", nwname))
  1371  	assert.Equal(c, strings.TrimSpace(out), ipv6)
  1372  }
  1373  
  1374  func (s *DockerNetworkSuite) TestDockerNetworkConnectLinkLocalIP(c *testing.T) {
  1375  	// create one test network
  1376  	dockerCmd(c, "network", "create", "--ipv6", "--subnet=2001:db8:1234::/64", "n0")
  1377  	assertNwIsAvailable(c, "n0")
  1378  
  1379  	// run a container with incorrect link-local address
  1380  	_, _, err := dockerCmdWithError("run", "--link-local-ip", "169.253.5.5", "busybox", "true")
  1381  	assert.ErrorContains(c, err, "")
  1382  	_, _, err = dockerCmdWithError("run", "--link-local-ip", "2001:db8::89", "busybox", "true")
  1383  	assert.ErrorContains(c, err, "")
  1384  
  1385  	// run two containers with link-local ip on the test network
  1386  	dockerCmd(c, "run", "-d", "--name", "c0", "--net=n0", "--link-local-ip", "169.254.7.7", "--link-local-ip", "fe80::254:77", "busybox", "top")
  1387  	assert.Assert(c, waitRun("c0") == nil)
  1388  	dockerCmd(c, "run", "-d", "--name", "c1", "--net=n0", "--link-local-ip", "169.254.8.8", "--link-local-ip", "fe80::254:88", "busybox", "top")
  1389  	assert.Assert(c, waitRun("c1") == nil)
  1390  
  1391  	// run a container on the default network and connect it to the test network specifying a link-local address
  1392  	dockerCmd(c, "run", "-d", "--name", "c2", "busybox", "top")
  1393  	assert.Assert(c, waitRun("c2") == nil)
  1394  	dockerCmd(c, "network", "connect", "--link-local-ip", "169.254.9.9", "n0", "c2")
  1395  
  1396  	// verify the three containers can ping each other via the link-local addresses
  1397  	_, _, err = dockerCmdWithError("exec", "c0", "ping", "-c", "1", "169.254.8.8")
  1398  	assert.NilError(c, err)
  1399  	_, _, err = dockerCmdWithError("exec", "c1", "ping", "-c", "1", "169.254.9.9")
  1400  	assert.NilError(c, err)
  1401  	_, _, err = dockerCmdWithError("exec", "c2", "ping", "-c", "1", "169.254.7.7")
  1402  	assert.NilError(c, err)
  1403  
  1404  	// Stop and restart the three containers
  1405  	dockerCmd(c, "stop", "c0")
  1406  	dockerCmd(c, "stop", "c1")
  1407  	dockerCmd(c, "stop", "c2")
  1408  	dockerCmd(c, "start", "c0")
  1409  	dockerCmd(c, "start", "c1")
  1410  	dockerCmd(c, "start", "c2")
  1411  
  1412  	// verify the ping again
  1413  	_, _, err = dockerCmdWithError("exec", "c0", "ping", "-c", "1", "169.254.8.8")
  1414  	assert.NilError(c, err)
  1415  	_, _, err = dockerCmdWithError("exec", "c1", "ping", "-c", "1", "169.254.9.9")
  1416  	assert.NilError(c, err)
  1417  	_, _, err = dockerCmdWithError("exec", "c2", "ping", "-c", "1", "169.254.7.7")
  1418  	assert.NilError(c, err)
  1419  }
  1420  
  1421  func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectLink(c *testing.T) {
  1422  	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
  1423  	dockerCmd(c, "network", "create", "-d", "bridge", "foo1")
  1424  	dockerCmd(c, "network", "create", "-d", "bridge", "foo2")
  1425  
  1426  	dockerCmd(c, "run", "-d", "--net=foo1", "--name=first", "busybox", "top")
  1427  	assert.Assert(c, waitRun("first") == nil)
  1428  
  1429  	// run a container in a user-defined network with a link for an existing container
  1430  	// and a link for a container that doesn't exist
  1431  	dockerCmd(c, "run", "-d", "--net=foo1", "--name=second", "--link=first:FirstInFoo1",
  1432  		"--link=third:bar", "busybox", "top")
  1433  	assert.Assert(c, waitRun("second") == nil)
  1434  
  1435  	// ping to first and its alias FirstInFoo1 must succeed
  1436  	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
  1437  	assert.NilError(c, err)
  1438  	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1")
  1439  	assert.NilError(c, err)
  1440  
  1441  	// connect first container to foo2 network
  1442  	dockerCmd(c, "network", "connect", "foo2", "first")
  1443  	// connect second container to foo2 network with a different alias for first container
  1444  	dockerCmd(c, "network", "connect", "--link=first:FirstInFoo2", "foo2", "second")
  1445  
  1446  	// ping the new alias in network foo2
  1447  	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2")
  1448  	assert.NilError(c, err)
  1449  
  1450  	// disconnect first container from foo1 network
  1451  	dockerCmd(c, "network", "disconnect", "foo1", "first")
  1452  
  1453  	// link in foo1 network must fail
  1454  	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1")
  1455  	assert.ErrorContains(c, err, "")
  1456  
  1457  	// link in foo2 network must succeed
  1458  	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2")
  1459  	assert.NilError(c, err)
  1460  }
  1461  
  1462  func (s *DockerNetworkSuite) TestDockerNetworkDisconnectDefault(c *testing.T) {
  1463  	netWorkName1 := "test1"
  1464  	netWorkName2 := "test2"
  1465  	containerName := "foo"
  1466  
  1467  	dockerCmd(c, "network", "create", netWorkName1)
  1468  	dockerCmd(c, "network", "create", netWorkName2)
  1469  	dockerCmd(c, "create", "--name", containerName, "busybox", "top")
  1470  	dockerCmd(c, "network", "connect", netWorkName1, containerName)
  1471  	dockerCmd(c, "network", "connect", netWorkName2, containerName)
  1472  	dockerCmd(c, "network", "disconnect", "bridge", containerName)
  1473  
  1474  	dockerCmd(c, "start", containerName)
  1475  	assert.Assert(c, waitRun(containerName) == nil)
  1476  	networks := inspectField(c, containerName, "NetworkSettings.Networks")
  1477  	assert.Assert(c, strings.Contains(networks, netWorkName1), fmt.Sprintf("Should contain '%s' network", netWorkName1))
  1478  	assert.Assert(c, strings.Contains(networks, netWorkName2), fmt.Sprintf("Should contain '%s' network", netWorkName2))
  1479  	assert.Assert(c, !strings.Contains(networks, "bridge"), "Should not contain 'bridge' network")
  1480  }
  1481  
  1482  func (s *DockerNetworkSuite) TestDockerNetworkConnectWithAliasOnDefaultNetworks(c *testing.T) {
  1483  	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
  1484  
  1485  	defaults := []string{"bridge", "host", "none"}
  1486  	out, _ := dockerCmd(c, "run", "-d", "--net=none", "busybox", "top")
  1487  	containerID := strings.TrimSpace(out)
  1488  	for _, net := range defaults {
  1489  		res, _, err := dockerCmdWithError("network", "connect", "--alias", "alias"+net, net, containerID)
  1490  		assert.ErrorContains(c, err, "")
  1491  		assert.Assert(c, strings.Contains(res, runconfig.ErrUnsupportedNetworkAndAlias.Error()))
  1492  	}
  1493  }
  1494  
  1495  func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectAlias(c *testing.T) {
  1496  	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
  1497  	dockerCmd(c, "network", "create", "-d", "bridge", "net1")
  1498  	dockerCmd(c, "network", "create", "-d", "bridge", "net2")
  1499  
  1500  	cid, _ := dockerCmd(c, "run", "-d", "--net=net1", "--name=first", "--net-alias=foo", "busybox:glibc", "top")
  1501  	assert.Assert(c, waitRun("first") == nil)
  1502  
  1503  	dockerCmd(c, "run", "-d", "--net=net1", "--name=second", "busybox:glibc", "top")
  1504  	assert.Assert(c, waitRun("second") == nil)
  1505  
  1506  	// ping first container and its alias
  1507  	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
  1508  	assert.NilError(c, err)
  1509  	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
  1510  	assert.NilError(c, err)
  1511  
  1512  	// ping first container's short-id alias
  1513  	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", stringid.TruncateID(cid))
  1514  	assert.NilError(c, err)
  1515  
  1516  	// connect first container to net2 network
  1517  	dockerCmd(c, "network", "connect", "--alias=bar", "net2", "first")
  1518  	// connect second container to foo2 network with a different alias for first container
  1519  	dockerCmd(c, "network", "connect", "net2", "second")
  1520  
  1521  	// ping the new alias in network foo2
  1522  	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
  1523  	assert.NilError(c, err)
  1524  
  1525  	// disconnect first container from net1 network
  1526  	dockerCmd(c, "network", "disconnect", "net1", "first")
  1527  
  1528  	// ping to net1 scoped alias "foo" must fail
  1529  	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
  1530  	assert.ErrorContains(c, err, "")
  1531  
  1532  	// ping to net2 scoped alias "bar" must still succeed
  1533  	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
  1534  	assert.NilError(c, err)
  1535  	// ping to net2 scoped alias short-id must still succeed
  1536  	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", stringid.TruncateID(cid))
  1537  	assert.NilError(c, err)
  1538  
  1539  	// verify the alias option is rejected when running on predefined network
  1540  	out, _, err := dockerCmdWithError("run", "--rm", "--name=any", "--net-alias=any", "busybox:glibc", "true")
  1541  	assert.Assert(c, err != nil, "out: %s", out)
  1542  	assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndAlias.Error()))
  1543  	// verify the alias option is rejected when connecting to predefined network
  1544  	out, _, err = dockerCmdWithError("network", "connect", "--alias=any", "bridge", "first")
  1545  	assert.Assert(c, err != nil, "out: %s", out)
  1546  	assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndAlias.Error()))
  1547  }
  1548  
  1549  func (s *DockerSuite) TestUserDefinedNetworkConnectivity(c *testing.T) {
  1550  	testRequires(c, DaemonIsLinux, NotUserNamespace)
  1551  	dockerCmd(c, "network", "create", "-d", "bridge", "br.net1")
  1552  
  1553  	dockerCmd(c, "run", "-d", "--net=br.net1", "--name=c1.net1", "busybox:glibc", "top")
  1554  	assert.Assert(c, waitRun("c1.net1") == nil)
  1555  
  1556  	dockerCmd(c, "run", "-d", "--net=br.net1", "--name=c2.net1", "busybox:glibc", "top")
  1557  	assert.Assert(c, waitRun("c2.net1") == nil)
  1558  
  1559  	// ping first container by its unqualified name
  1560  	_, _, err := dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1")
  1561  	assert.NilError(c, err)
  1562  
  1563  	// ping first container by its qualified name
  1564  	_, _, err = dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1.br.net1")
  1565  	assert.NilError(c, err)
  1566  
  1567  	// ping with first qualified name masked by an additional domain. should fail
  1568  	_, _, err = dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1.br.net1.google.com")
  1569  	assert.ErrorContains(c, err, "")
  1570  }
  1571  
  1572  func (s *DockerSuite) TestEmbeddedDNSInvalidInput(c *testing.T) {
  1573  	testRequires(c, DaemonIsLinux, NotUserNamespace)
  1574  	dockerCmd(c, "network", "create", "-d", "bridge", "nw1")
  1575  
  1576  	// Sending garbage to embedded DNS shouldn't crash the daemon
  1577  	dockerCmd(c, "run", "-i", "--net=nw1", "--name=c1", "debian:bullseye", "bash", "-c", "echo InvalidQuery > /dev/udp/127.0.0.11/53")
  1578  }
  1579  
  1580  func (s *DockerSuite) TestDockerNetworkConnectFailsNoInspectChange(c *testing.T) {
  1581  	dockerCmd(c, "run", "-d", "--name=bb", "busybox", "top")
  1582  	assert.Assert(c, waitRun("bb") == nil)
  1583  	defer dockerCmd(c, "stop", "bb")
  1584  
  1585  	ns0 := inspectField(c, "bb", "NetworkSettings.Networks.bridge")
  1586  
  1587  	// A failing redundant network connect should not alter current container's endpoint settings
  1588  	_, _, err := dockerCmdWithError("network", "connect", "bridge", "bb")
  1589  	assert.ErrorContains(c, err, "")
  1590  
  1591  	ns1 := inspectField(c, "bb", "NetworkSettings.Networks.bridge")
  1592  	assert.Equal(c, ns1, ns0)
  1593  }
  1594  
  1595  func (s *DockerSuite) TestDockerNetworkInternalMode(c *testing.T) {
  1596  	dockerCmd(c, "network", "create", "--driver=bridge", "--internal", "internal")
  1597  	assertNwIsAvailable(c, "internal")
  1598  	nr := getNetworkResource(c, "internal")
  1599  	assert.Assert(c, nr.Internal)
  1600  
  1601  	dockerCmd(c, "run", "-d", "--net=internal", "--name=first", "busybox:glibc", "top")
  1602  	assert.Assert(c, waitRun("first") == nil)
  1603  	dockerCmd(c, "run", "-d", "--net=internal", "--name=second", "busybox:glibc", "top")
  1604  	assert.Assert(c, waitRun("second") == nil)
  1605  	out, _, err := dockerCmdWithError("exec", "first", "ping", "-W", "4", "-c", "1", "8.8.8.8")
  1606  	assert.ErrorContains(c, err, "")
  1607  	assert.Assert(c, strings.Contains(out, "100% packet loss"))
  1608  	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
  1609  	assert.NilError(c, err)
  1610  }
  1611  
  1612  // Test for #21401
  1613  func (s *DockerNetworkSuite) TestDockerNetworkCreateDeleteSpecialCharacters(c *testing.T) {
  1614  	dockerCmd(c, "network", "create", "test@#$")
  1615  	assertNwIsAvailable(c, "test@#$")
  1616  	dockerCmd(c, "network", "rm", "test@#$")
  1617  	assertNwNotAvailable(c, "test@#$")
  1618  
  1619  	dockerCmd(c, "network", "create", "kiwl$%^")
  1620  	assertNwIsAvailable(c, "kiwl$%^")
  1621  	dockerCmd(c, "network", "rm", "kiwl$%^")
  1622  	assertNwNotAvailable(c, "kiwl$%^")
  1623  }
  1624  
  1625  func (s *DockerDaemonSuite) TestDaemonRestartRestoreBridgeNetwork(t *testing.T) {
  1626  	s.d.StartWithBusybox(t, "--live-restore")
  1627  	defer s.d.Stop(t)
  1628  	oldCon := "old"
  1629  
  1630  	_, err := s.d.Cmd("run", "-d", "--name", oldCon, "-p", "80:80", "busybox", "top")
  1631  	if err != nil {
  1632  		t.Fatal(err)
  1633  	}
  1634  	oldContainerIP, err := s.d.Cmd("inspect", "-f", "{{ .NetworkSettings.Networks.bridge.IPAddress }}", oldCon)
  1635  	if err != nil {
  1636  		t.Fatal(err)
  1637  	}
  1638  	// Kill the daemon
  1639  	if err := s.d.Kill(); err != nil {
  1640  		t.Fatal(err)
  1641  	}
  1642  
  1643  	// restart the daemon
  1644  	s.d.Start(t, "--live-restore")
  1645  
  1646  	// start a new container, the new container's ip should not be the same with
  1647  	// old running container.
  1648  	newCon := "new"
  1649  	_, err = s.d.Cmd("run", "-d", "--name", newCon, "busybox", "top")
  1650  	if err != nil {
  1651  		t.Fatal(err)
  1652  	}
  1653  	newContainerIP, err := s.d.Cmd("inspect", "-f", "{{ .NetworkSettings.Networks.bridge.IPAddress }}", newCon)
  1654  	if err != nil {
  1655  		t.Fatal(err)
  1656  	}
  1657  	if strings.Compare(strings.TrimSpace(oldContainerIP), strings.TrimSpace(newContainerIP)) == 0 {
  1658  		t.Fatalf("new container ip should not equal to old running container  ip")
  1659  	}
  1660  
  1661  	// start a new container, the new container should ping old running container
  1662  	_, err = s.d.Cmd("run", "-t", "busybox", "ping", "-c", "1", oldContainerIP)
  1663  	if err != nil {
  1664  		t.Fatal(err)
  1665  	}
  1666  
  1667  	// start a new container, trying to publish port 80:80 should fail
  1668  	out, err := s.d.Cmd("run", "-p", "80:80", "-d", "busybox", "top")
  1669  	if err == nil || !strings.Contains(out, "Bind for 0.0.0.0:80 failed: port is already allocated") {
  1670  		t.Fatalf("80 port is allocated to old running container, it should failed on allocating to new container")
  1671  	}
  1672  
  1673  	// kill old running container and try to allocate again
  1674  	_, err = s.d.Cmd("kill", oldCon)
  1675  	if err != nil {
  1676  		t.Fatal(err)
  1677  	}
  1678  	id, err := s.d.Cmd("run", "-p", "80:80", "-d", "busybox", "top")
  1679  	if err != nil {
  1680  		t.Fatal(err)
  1681  	}
  1682  
  1683  	// Cleanup because these containers will not be shut down by daemon
  1684  	out, err = s.d.Cmd("stop", newCon)
  1685  	if err != nil {
  1686  		t.Fatalf("err: %v %v", err, out)
  1687  	}
  1688  	_, err = s.d.Cmd("stop", strings.TrimSpace(id))
  1689  	if err != nil {
  1690  		t.Fatal(err)
  1691  	}
  1692  }
  1693  
  1694  func (s *DockerNetworkSuite) TestDockerNetworkFlagAlias(c *testing.T) {
  1695  	dockerCmd(c, "network", "create", "user")
  1696  	output, status := dockerCmd(c, "run", "--rm", "--network=user", "--network-alias=foo", "busybox", "true")
  1697  	assert.Equal(c, status, 0, fmt.Sprintf("unexpected status code %d (%s)", status, output))
  1698  
  1699  	output, status, _ = dockerCmdWithError("run", "--rm", "--network=user", "--net-alias=foo", "--network-alias=bar", "busybox", "true")
  1700  	assert.Equal(c, status, 0, fmt.Sprintf("unexpected status code %d (%s)", status, output))
  1701  }
  1702  
  1703  func (s *DockerNetworkSuite) TestDockerNetworkValidateIP(c *testing.T) {
  1704  	_, _, err := dockerCmdWithError("network", "create", "--ipv6", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "mynet")
  1705  	assert.NilError(c, err)
  1706  	assertNwIsAvailable(c, "mynet")
  1707  
  1708  	_, _, err = dockerCmdWithError("run", "-d", "--name", "mynet0", "--net=mynet", "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top")
  1709  	assert.NilError(c, err)
  1710  	assert.Assert(c, waitRun("mynet0") == nil)
  1711  	verifyIPAddressConfig(c, "mynet0", "mynet", "172.28.99.88", "2001:db8:1234::9988")
  1712  	verifyIPAddresses(c, "mynet0", "mynet", "172.28.99.88", "2001:db8:1234::9988")
  1713  
  1714  	_, _, err = dockerCmdWithError("run", "--net=mynet", "--ip", "mynet_ip", "--ip6", "2001:db8:1234::9999", "busybox", "top")
  1715  	assert.ErrorContains(c, err, "invalid IPv4 address")
  1716  	_, _, err = dockerCmdWithError("run", "--net=mynet", "--ip", "172.28.99.99", "--ip6", "mynet_ip6", "busybox", "top")
  1717  	assert.ErrorContains(c, err, "invalid IPv6 address")
  1718  
  1719  	// This is a case of IPv4 address to `--ip6`
  1720  	_, _, err = dockerCmdWithError("run", "--net=mynet", "--ip6", "172.28.99.99", "busybox", "top")
  1721  	assert.ErrorContains(c, err, "invalid IPv6 address")
  1722  	// This is a special case of an IPv4-mapped IPv6 address
  1723  	_, _, err = dockerCmdWithError("run", "--net=mynet", "--ip6", "::ffff:172.28.99.99", "busybox", "top")
  1724  	assert.ErrorContains(c, err, "invalid IPv6 address")
  1725  }
  1726  
  1727  // Test case for 26220
  1728  func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromBridge(c *testing.T) {
  1729  	out, _ := dockerCmd(c, "network", "inspect", "--format", "{{.Id}}", "bridge")
  1730  
  1731  	network := strings.TrimSpace(out)
  1732  
  1733  	name := "test"
  1734  	dockerCmd(c, "create", "--name", name, "busybox", "top")
  1735  
  1736  	_, _, err := dockerCmdWithError("network", "disconnect", network, name)
  1737  	assert.NilError(c, err)
  1738  }
  1739  
  1740  // TestConntrackFlowsLeak covers the failure scenario of ticket: https://github.com/docker/docker/issues/8795
  1741  // Validates that conntrack is correctly cleaned once a container is destroyed
  1742  func (s *DockerNetworkSuite) TestConntrackFlowsLeak(c *testing.T) {
  1743  	testRequires(c, IsAmd64, DaemonIsLinux, Network, testEnv.IsLocalDaemon)
  1744  
  1745  	// Create a new network
  1746  	cli.DockerCmd(c, "network", "create", "--subnet=192.168.10.0/24", "--gateway=192.168.10.1", "-o", "com.docker.network.bridge.host_binding_ipv4=192.168.10.1", "testbind")
  1747  	assertNwIsAvailable(c, "testbind")
  1748  
  1749  	// Launch the server, this will remain listening on an exposed port and reply to any request in a ping/pong fashion
  1750  	cmd := "while true; do echo hello | nc -w 1 -lu 8080; done"
  1751  	cli.DockerCmd(c, "run", "-d", "--name", "server", "--net", "testbind", "-p", "8080:8080/udp", "appropriate/nc", "sh", "-c", cmd)
  1752  
  1753  	// Launch a container client, here the objective is to create a flow that is natted in order to expose the bug
  1754  	cmd = "echo world | nc -q 1 -u 192.168.10.1 8080"
  1755  	cli.DockerCmd(c, "run", "-d", "--name", "client", "--net=host", "appropriate/nc", "sh", "-c", cmd)
  1756  
  1757  	// Get all the flows using netlink
  1758  	flows, err := netlink.ConntrackTableList(netlink.ConntrackTable, unix.AF_INET)
  1759  	assert.NilError(c, err)
  1760  	var flowMatch int
  1761  	for _, flow := range flows {
  1762  		// count only the flows that we are interested in, skipping others that can be laying around the host
  1763  		if flow.Forward.Protocol == unix.IPPROTO_UDP &&
  1764  			flow.Forward.DstIP.Equal(net.ParseIP("192.168.10.1")) &&
  1765  			flow.Forward.DstPort == 8080 {
  1766  			flowMatch++
  1767  		}
  1768  	}
  1769  	// The client should have created only 1 flow
  1770  	assert.Equal(c, flowMatch, 1)
  1771  
  1772  	// Now delete the server, this will trigger the conntrack cleanup
  1773  	cli.DockerCmd(c, "rm", "-fv", "server")
  1774  
  1775  	// Fetch again all the flows and validate that there is no server flow in the conntrack laying around
  1776  	flows, err = netlink.ConntrackTableList(netlink.ConntrackTable, unix.AF_INET)
  1777  	assert.NilError(c, err)
  1778  	flowMatch = 0
  1779  	for _, flow := range flows {
  1780  		if flow.Forward.Protocol == unix.IPPROTO_UDP &&
  1781  			flow.Forward.DstIP.Equal(net.ParseIP("192.168.10.1")) &&
  1782  			flow.Forward.DstPort == 8080 {
  1783  			flowMatch++
  1784  		}
  1785  	}
  1786  	// All the flows have to be gone
  1787  	assert.Equal(c, flowMatch, 0)
  1788  }