1// +build !windows
2
3package main
4
5import (
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
35const dummyNetworkDriver = "dummy-network-driver"
36const dummyIPAMDriver = "dummy-ipam-driver"
37
38var remoteDriverNetworkRequest remoteapi.CreateNetworkRequest
39
40func (s *DockerNetworkSuite) SetUpTest(c *testing.T) {
41	s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
42}
43
44func (s *DockerNetworkSuite) TearDownTest(c *testing.T) {
45	if s.d != nil {
46		s.d.Stop(c)
47		s.ds.TearDownTest(c)
48	}
49}
50
51func (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
58func 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
211func (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
222func 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
228func 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
234func 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
249func 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
262func 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
270func (s *DockerNetworkSuite) TestDockerNetworkLsDefault(c *testing.T) {
271	defaults := []string{"bridge", "host", "none"}
272	for _, nn := range defaults {
273		assertNwIsAvailable(c, nn)
274	}
275}
276
277func (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
286func (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
297func (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
306func (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
362func (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
370func (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
386func (s *DockerSuite) TestDockerNetworkDeleteNotExists(c *testing.T) {
387	out, _, err := dockerCmdWithError("network", "rm", "test")
388	assert.ErrorContains(c, err, "", out)
389}
390
391func (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
415func (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
426func (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
437func (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
447func (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
486func (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
526func (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
578func (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
619func (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
635func (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
648func (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
664func (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
690func (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
707func (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
726func (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
750func (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
765func (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
788func (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
840func (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
893func (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
919func (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
946func (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
987func (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
1002func (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
1030func 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
1044func 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
1053func (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
1072func (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
1093func (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
1099func (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
1125func (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
1134func (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
1142func (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
1150func 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
1159func (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
1196func (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
1208func (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
1214func (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
1225func (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
1255func (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
1270func (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
1305func (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
1328func (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
1348func 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
1354func 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
1366func 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
1374func (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
1421func (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
1462func (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
1482func (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
1495func (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
1549func (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
1572func (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
1580func (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
1595func (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
1613func (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
1625func (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
1694func (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
1703func (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
1728func (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
1742func (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}
1789