Browse code

Experimental it for net vlan drivers

Signed-off-by: Brent Salisbury <brent@docker.com>
Signed-off-by: Madhu Venugopal <madhu@docker.com>

Brent Salisbury authored on 2016/03/11 14:59:49
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,590 @@
0
+// +build experimental
1
+
2
+package main
3
+
4
+import (
5
+	"os/exec"
6
+	"strings"
7
+	"time"
8
+
9
+	"github.com/docker/docker/pkg/integration/checker"
10
+	"github.com/docker/docker/pkg/parsers/kernel"
11
+	"github.com/go-check/check"
12
+)
13
+
14
+var (
15
+	MacvlanKernelSupport = testRequirement{
16
+		func() bool {
17
+			const macvlanKernelVer = 3 // minimum macvlan kernel support
18
+			const macvlanMajorVer = 9  // minimum macvlan major kernel support
19
+			kv, err := kernel.GetKernelVersion()
20
+			if err != nil {
21
+				return false
22
+			}
23
+			// ensure Kernel version is >= v3.9 for macvlan support
24
+			if kv.Kernel < macvlanKernelVer || (kv.Kernel == macvlanKernelVer && kv.Major < macvlanMajorVer) {
25
+				return false
26
+			}
27
+			return true
28
+		},
29
+		"kernel version failed to meet the minimum macvlan kernel requirement of 3.9",
30
+	}
31
+	IpvlanKernelSupport = testRequirement{
32
+		func() bool {
33
+			const ipvlanKernelVer = 4 // minimum ipvlan kernel support
34
+			const ipvlanMajorVer = 2  // minimum ipvlan major kernel support
35
+			kv, err := kernel.GetKernelVersion()
36
+			if err != nil {
37
+				return false
38
+			}
39
+			// ensure Kernel version is >= v4.2 for ipvlan support
40
+			if kv.Kernel < ipvlanKernelVer || (kv.Kernel == ipvlanKernelVer && kv.Major < ipvlanMajorVer) {
41
+				return false
42
+			}
43
+			return true
44
+		},
45
+		"kernel version failed to meet the minimum ipvlan kernel requirement of 4.0.0",
46
+	}
47
+)
48
+
49
+func (s *DockerNetworkSuite) TestDockerNetworkMacvlanPersistance(c *check.C) {
50
+	// verify the driver automatically provisions the 802.1q link (dm-dummy0.60)
51
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
52
+	// master dummy interface 'dm' abbreviation represents 'docker macvlan'
53
+	master := "dm-dummy0"
54
+	// simulate the master link the vlan tagged subinterface parent link will use
55
+	out, err := createMasterDummy(c, master)
56
+	c.Assert(err, check.IsNil, check.Commentf(out))
57
+	// create a network specifying the desired sub-interface name
58
+	dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.60", "dm-persist")
59
+	assertNwIsAvailable(c, "dm-persist")
60
+	// Restart docker daemon to test the config has persisted to disk
61
+	s.d.Restart()
62
+	// verify network is recreated from persistence
63
+	assertNwIsAvailable(c, "dm-persist")
64
+	// cleanup the master interface that also collects the slave dev
65
+	deleteInterface(c, "dm-dummy0")
66
+}
67
+
68
+func (s *DockerNetworkSuite) TestDockerNetworkIpvlanPersistance(c *check.C) {
69
+	// verify the driver automatically provisions the 802.1q link (di-dummy0.70)
70
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
71
+	// master dummy interface 'di' notation represent 'docker ipvlan'
72
+	master := "di-dummy0"
73
+	// simulate the master link the vlan tagged subinterface parent link will use
74
+	out, err := createMasterDummy(c, master)
75
+	c.Assert(err, check.IsNil, check.Commentf(out))
76
+	// create a network specifying the desired sub-interface name
77
+	dockerCmd(c, "network", "create", "--driver=ipvlan", "-o", "parent=di-dummy0.70", "di-persist")
78
+	assertNwIsAvailable(c, "di-persist")
79
+	// Restart docker daemon to test the config has persisted to disk
80
+	s.d.Restart()
81
+	// verify network is recreated from persistence
82
+	assertNwIsAvailable(c, "di-persist")
83
+	// cleanup the master interface that also collects the slave dev
84
+	deleteInterface(c, "di-dummy0")
85
+}
86
+
87
+func (s *DockerNetworkSuite) TestDockerNetworkMacvlanSubIntCreate(c *check.C) {
88
+	// verify the driver automatically provisions the 802.1q link (dm-dummy0.50)
89
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
90
+	// master dummy interface 'dm' abbreviation represents 'docker macvlan'
91
+	master := "dm-dummy0"
92
+	// simulate the master link the vlan tagged subinterface parent link will use
93
+	out, err := createMasterDummy(c, master)
94
+	c.Assert(err, check.IsNil, check.Commentf(out))
95
+	// create a network specifying the desired sub-interface name
96
+	dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.50", "dm-subinterface")
97
+	assertNwIsAvailable(c, "dm-subinterface")
98
+	// cleanup the master interface which also collects the slave dev
99
+	deleteInterface(c, "dm-dummy0")
100
+}
101
+
102
+func (s *DockerNetworkSuite) TestDockerNetworkIpvlanSubIntCreate(c *check.C) {
103
+	// verify the driver automatically provisions the 802.1q link (di-dummy0.50)
104
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
105
+	// master dummy interface 'dm' abbreviation represents 'docker ipvlan'
106
+	master := "di-dummy0"
107
+	// simulate the master link the vlan tagged subinterface parent link will use
108
+	out, err := createMasterDummy(c, master)
109
+	c.Assert(err, check.IsNil, check.Commentf(out))
110
+	// create a network specifying the desired sub-interface name
111
+	dockerCmd(c, "network", "create", "--driver=ipvlan", "-o", "parent=di-dummy0.60", "di-subinterface")
112
+	assertNwIsAvailable(c, "di-subinterface")
113
+	// cleanup the master interface which also collects the slave dev
114
+	deleteInterface(c, "di-dummy0")
115
+}
116
+
117
+func (s *DockerNetworkSuite) TestDockerNetworkMacvlanOverlapParent(c *check.C) {
118
+	// verify the same parent interface cannot be used if already in use by an existing network
119
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
120
+	// master dummy interface 'dm' abbreviation represents 'docker macvlan'
121
+	master := "dm-dummy0"
122
+	out, err := createMasterDummy(c, master)
123
+	c.Assert(err, check.IsNil, check.Commentf(out))
124
+	out, err = createVlanInterface(c, master, "dm-dummy0.40", "40")
125
+	c.Assert(err, check.IsNil, check.Commentf(out))
126
+	// create a network using an existing parent interface
127
+	dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.40", "dm-subinterface")
128
+	assertNwIsAvailable(c, "dm-subinterface")
129
+	// attempt to create another network using the same parent iface that should fail
130
+	out, _, err = dockerCmdWithError("network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.40", "dm-parent-net-overlap")
131
+	// verify that the overlap returns an error
132
+	c.Assert(err, check.NotNil)
133
+	// cleanup the master interface which also collects the slave dev
134
+	deleteInterface(c, "dm-dummy0")
135
+}
136
+
137
+func (s *DockerNetworkSuite) TestDockerNetworkIpvlanOverlapParent(c *check.C) {
138
+	// verify the same parent interface cannot be used if already in use by an existing network
139
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
140
+	// master dummy interface 'dm' abbreviation represents 'docker ipvlan'
141
+	master := "di-dummy0"
142
+	out, err := createMasterDummy(c, master)
143
+	c.Assert(err, check.IsNil, check.Commentf(out))
144
+	out, err = createVlanInterface(c, master, "di-dummy0.30", "30")
145
+	c.Assert(err, check.IsNil, check.Commentf(out))
146
+	// create a network using an existing parent interface
147
+	dockerCmd(c, "network", "create", "--driver=ipvlan", "-o", "parent=di-dummy0.30", "di-subinterface")
148
+	assertNwIsAvailable(c, "di-subinterface")
149
+	// attempt to create another network using the same parent iface that should fail
150
+	out, _, err = dockerCmdWithError("network", "create", "--driver=ipvlan", "-o", "parent=di-dummy0.30", "di-parent-net-overlap")
151
+	// verify that the overlap returns an error
152
+	c.Assert(err, check.NotNil)
153
+	// cleanup the master interface which also collects the slave dev
154
+	deleteInterface(c, "di-dummy0")
155
+}
156
+
157
+func (s *DockerNetworkSuite) TestDockerNetworkMacvlanMultiSubnet(c *check.C) {
158
+	// create a dual stack multi-subnet Macvlan bridge mode network and validate connectivity between four containers, two on each subnet
159
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
160
+	dockerCmd(c, "network", "create", "--driver=macvlan", "--subnet=172.28.100.0/24", "--subnet=172.28.102.0/24", "--gateway=172.28.102.254",
161
+		"--subnet=2001:db8:abc2::/64", "--subnet=2001:db8:abc4::/64", "--gateway=2001:db8:abc4::254", "dualstackbridge")
162
+	// Ensure the network was created
163
+	assertNwIsAvailable(c, "dualstackbridge")
164
+	// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64
165
+	dockerCmd(c, "run", "-d", "--net=dualstackbridge", "--name=first", "--ip", "172.28.100.20", "--ip6", "2001:db8:abc2::20", "busybox", "top")
166
+	dockerCmd(c, "run", "-d", "--net=dualstackbridge", "--name=second", "--ip", "172.28.100.21", "--ip6", "2001:db8:abc2::21", "busybox", "top")
167
+
168
+	// Inspect and store the v4 address from specified container on the network dualstackbridge
169
+	ip := inspectField(c, "first", "NetworkSettings.Networks.dualstackbridge.IPAddress")
170
+	// Inspect and store the v6 address from specified container on the network dualstackbridge
171
+	ip6 := inspectField(c, "first", "NetworkSettings.Networks.dualstackbridge.GlobalIPv6Address")
172
+
173
+	// verify ipv4 connectivity to the explicit --ipv address second to first
174
+	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", strings.TrimSpace(ip))
175
+	c.Assert(err, check.IsNil)
176
+	// verify ipv6 connectivity to the explicit --ipv6 address second to first
177
+	_, _, err = dockerCmdWithError("exec", "second", "ping6", "-c", "1", strings.TrimSpace(ip6))
178
+	c.Assert(err, check.IsNil)
179
+
180
+	// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
181
+	dockerCmd(c, "run", "-d", "--net=dualstackbridge", "--name=third", "--ip", "172.28.102.20", "--ip6", "2001:db8:abc4::20", "busybox", "top")
182
+	dockerCmd(c, "run", "-d", "--net=dualstackbridge", "--name=fourth", "--ip", "172.28.102.21", "--ip6", "2001:db8:abc4::21", "busybox", "top")
183
+
184
+	// Inspect and store the v4 address from specified container on the network dualstackbridge
185
+	ip = inspectField(c, "third", "NetworkSettings.Networks.dualstackbridge.IPAddress")
186
+	// Inspect and store the v6 address from specified container on the network dualstackbridge
187
+	ip6 = inspectField(c, "third", "NetworkSettings.Networks.dualstackbridge.GlobalIPv6Address")
188
+
189
+	// verify ipv4 connectivity to the explicit --ipv address from third to fourth
190
+	_, _, err = dockerCmdWithError("exec", "fourth", "ping", "-c", "1", strings.TrimSpace(ip))
191
+	c.Assert(err, check.IsNil)
192
+	// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
193
+	_, _, err = dockerCmdWithError("exec", "fourth", "ping6", "-c", "1", strings.TrimSpace(ip6))
194
+	c.Assert(err, check.IsNil)
195
+
196
+	// Inspect the v4 gateway to ensure the proper default GW was assigned
197
+	ip4gw := inspectField(c, "first", "NetworkSettings.Networks.dualstackbridge.Gateway")
198
+	c.Assert(strings.TrimSpace(ip4gw), check.Equals, "172.28.100.1")
199
+	// Inspect the v6 gateway to ensure the proper default GW was assigned
200
+	ip6gw := inspectField(c, "first", "NetworkSettings.Networks.dualstackbridge.IPv6Gateway")
201
+	c.Assert(strings.TrimSpace(ip6gw), check.Equals, "2001:db8:abc2::1")
202
+
203
+	// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
204
+	ip4gw = inspectField(c, "third", "NetworkSettings.Networks.dualstackbridge.Gateway")
205
+	c.Assert(strings.TrimSpace(ip4gw), check.Equals, "172.28.102.254")
206
+	// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
207
+	ip6gw = inspectField(c, "third", "NetworkSettings.Networks.dualstackbridge.IPv6Gateway")
208
+	c.Assert(strings.TrimSpace(ip6gw), check.Equals, "2001:db8:abc4::254")
209
+}
210
+
211
+func (s *DockerNetworkSuite) TestDockerNetworkIpvlanL2MultiSubnet(c *check.C) {
212
+	// create a dual stack multi-subnet Ipvlan L2 network and validate connectivity within the subnets, two on each subnet
213
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
214
+	dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.200.0/24", "--subnet=172.28.202.0/24", "--gateway=172.28.202.254",
215
+		"--subnet=2001:db8:abc8::/64", "--subnet=2001:db8:abc6::/64", "--gateway=2001:db8:abc6::254", "dualstackl2")
216
+	// Ensure the network was created
217
+	assertNwIsAvailable(c, "dualstackl2")
218
+	// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.200.0/24 and 2001:db8:abc8::/64
219
+	dockerCmd(c, "run", "-d", "--net=dualstackl2", "--name=first", "--ip", "172.28.200.20", "--ip6", "2001:db8:abc8::20", "busybox", "top")
220
+	dockerCmd(c, "run", "-d", "--net=dualstackl2", "--name=second", "--ip", "172.28.200.21", "--ip6", "2001:db8:abc8::21", "busybox", "top")
221
+
222
+	// Inspect and store the v4 address from specified container on the network dualstackl2
223
+	ip := inspectField(c, "first", "NetworkSettings.Networks.dualstackl2.IPAddress")
224
+	// Inspect and store the v6 address from specified container on the network dualstackl2
225
+	ip6 := inspectField(c, "first", "NetworkSettings.Networks.dualstackl2.GlobalIPv6Address")
226
+
227
+	// verify ipv4 connectivity to the explicit --ipv address second to first
228
+	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", strings.TrimSpace(ip))
229
+	c.Assert(err, check.IsNil)
230
+	// verify ipv6 connectivity to the explicit --ipv6 address second to first
231
+	_, _, err = dockerCmdWithError("exec", "second", "ping6", "-c", "1", strings.TrimSpace(ip6))
232
+	c.Assert(err, check.IsNil)
233
+
234
+	// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.202.0/24 and 2001:db8:abc6::/64
235
+	dockerCmd(c, "run", "-d", "--net=dualstackl2", "--name=third", "--ip", "172.28.202.20", "--ip6", "2001:db8:abc6::20", "busybox", "top")
236
+	dockerCmd(c, "run", "-d", "--net=dualstackl2", "--name=fourth", "--ip", "172.28.202.21", "--ip6", "2001:db8:abc6::21", "busybox", "top")
237
+
238
+	// Inspect and store the v4 address from specified container on the network dualstackl2
239
+	ip = inspectField(c, "third", "NetworkSettings.Networks.dualstackl2.IPAddress")
240
+	// Inspect and store the v6 address from specified container on the network dualstackl2
241
+	ip6 = inspectField(c, "third", "NetworkSettings.Networks.dualstackl2.GlobalIPv6Address")
242
+
243
+	// verify ipv4 connectivity to the explicit --ipv address from third to fourth
244
+	_, _, err = dockerCmdWithError("exec", "fourth", "ping", "-c", "1", strings.TrimSpace(ip))
245
+	c.Assert(err, check.IsNil)
246
+	// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
247
+	_, _, err = dockerCmdWithError("exec", "fourth", "ping6", "-c", "1", strings.TrimSpace(ip6))
248
+	c.Assert(err, check.IsNil)
249
+
250
+	// Inspect the v4 gateway to ensure the proper default GW was assigned
251
+	ip4gw := inspectField(c, "first", "NetworkSettings.Networks.dualstackl2.Gateway")
252
+	c.Assert(strings.TrimSpace(ip4gw), check.Equals, "172.28.200.1")
253
+	// Inspect the v6 gateway to ensure the proper default GW was assigned
254
+	ip6gw := inspectField(c, "first", "NetworkSettings.Networks.dualstackl2.IPv6Gateway")
255
+	c.Assert(strings.TrimSpace(ip6gw), check.Equals, "2001:db8:abc8::1")
256
+
257
+	// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
258
+	ip4gw = inspectField(c, "third", "NetworkSettings.Networks.dualstackl2.Gateway")
259
+	c.Assert(strings.TrimSpace(ip4gw), check.Equals, "172.28.202.254")
260
+	// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
261
+	ip6gw = inspectField(c, "third", "NetworkSettings.Networks.dualstackl2.IPv6Gateway")
262
+	c.Assert(strings.TrimSpace(ip6gw), check.Equals, "2001:db8:abc6::254")
263
+}
264
+
265
+func (s *DockerNetworkSuite) TestDockerNetworkIpvlanL3MultiSubnet(c *check.C) {
266
+	// create a dual stack multi-subnet Ipvlan L3 network and validate connectivity between all four containers per L3 mode
267
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
268
+	dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.10.0/24", "--subnet=172.28.12.0/24", "--gateway=172.28.12.254",
269
+		"--subnet=2001:db8:abc9::/64", "--subnet=2001:db8:abc7::/64", "--gateway=2001:db8:abc7::254", "-o", "ipvlan_mode=l3", "dualstackl3")
270
+	// Ensure the network was created
271
+	assertNwIsAvailable(c, "dualstackl3")
272
+
273
+	// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.10.0/24 and 2001:db8:abc9::/64
274
+	dockerCmd(c, "run", "-d", "--net=dualstackl3", "--name=first", "--ip", "172.28.10.20", "--ip6", "2001:db8:abc9::20", "busybox", "top")
275
+	dockerCmd(c, "run", "-d", "--net=dualstackl3", "--name=second", "--ip", "172.28.10.21", "--ip6", "2001:db8:abc9::21", "busybox", "top")
276
+
277
+	// Inspect and store the v4 address from specified container on the network dualstackl3
278
+	ip := inspectField(c, "first", "NetworkSettings.Networks.dualstackl3.IPAddress")
279
+	// Inspect and store the v6 address from specified container on the network dualstackl3
280
+	ip6 := inspectField(c, "first", "NetworkSettings.Networks.dualstackl3.GlobalIPv6Address")
281
+
282
+	// verify ipv4 connectivity to the explicit --ipv address second to first
283
+	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", strings.TrimSpace(ip))
284
+	c.Assert(err, check.IsNil)
285
+	// verify ipv6 connectivity to the explicit --ipv6 address second to first
286
+	_, _, err = dockerCmdWithError("exec", "second", "ping6", "-c", "1", strings.TrimSpace(ip6))
287
+	c.Assert(err, check.IsNil)
288
+
289
+	// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.12.0/24 and 2001:db8:abc7::/64
290
+	dockerCmd(c, "run", "-d", "--net=dualstackl3", "--name=third", "--ip", "172.28.12.20", "--ip6", "2001:db8:abc7::20", "busybox", "top")
291
+	dockerCmd(c, "run", "-d", "--net=dualstackl3", "--name=fourth", "--ip", "172.28.12.21", "--ip6", "2001:db8:abc7::21", "busybox", "top")
292
+
293
+	// Inspect and store the v4 address from specified container on the network dualstackl3
294
+	ip = inspectField(c, "third", "NetworkSettings.Networks.dualstackl3.IPAddress")
295
+	// Inspect and store the v6 address from specified container on the network dualstackl3
296
+	ip6 = inspectField(c, "third", "NetworkSettings.Networks.dualstackl3.GlobalIPv6Address")
297
+
298
+	// verify ipv4 connectivity to the explicit --ipv address from third to fourth
299
+	_, _, err = dockerCmdWithError("exec", "fourth", "ping", "-c", "1", strings.TrimSpace(ip))
300
+	c.Assert(err, check.IsNil)
301
+	// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
302
+	_, _, err = dockerCmdWithError("exec", "fourth", "ping6", "-c", "1", strings.TrimSpace(ip6))
303
+	c.Assert(err, check.IsNil)
304
+
305
+	// Inspect and store the v4 address from specified container on the network dualstackl3
306
+	ip = inspectField(c, "second", "NetworkSettings.Networks.dualstackl3.IPAddress")
307
+	// Inspect and store the v6 address from specified container on the network dualstackl3
308
+	ip6 = inspectField(c, "second", "NetworkSettings.Networks.dualstackl3.GlobalIPv6Address")
309
+
310
+	// Verify connectivity across disparate subnets which is unique to L3 mode only
311
+	_, _, err = dockerCmdWithError("exec", "third", "ping", "-c", "1", strings.TrimSpace(ip))
312
+	c.Assert(err, check.IsNil)
313
+	_, _, err = dockerCmdWithError("exec", "third", "ping6", "-c", "1", strings.TrimSpace(ip6))
314
+	c.Assert(err, check.IsNil)
315
+
316
+	// Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
317
+	ip4gw := inspectField(c, "first", "NetworkSettings.Networks.dualstackl3.Gateway")
318
+	c.Assert(strings.TrimSpace(ip4gw), check.Equals, "")
319
+	// Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
320
+	ip6gw := inspectField(c, "third", "NetworkSettings.Networks.dualstackl3.IPv6Gateway")
321
+	c.Assert(strings.TrimSpace(ip6gw), check.Equals, "")
322
+}
323
+
324
+func (s *DockerNetworkSuite) TestDockerNetworkIpvlanAddressing(c *check.C) {
325
+	// Ensure the default gateways, next-hops and default dev devices are properly set
326
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
327
+	dockerCmd(c, "network", "create", "--driver=macvlan", "--subnet=172.28.130.0/24",
328
+		"--subnet=2001:db8:abca::/64", "--gateway=2001:db8:abca::254", "-o", "macvlan_mode=bridge", "dualstackbridge")
329
+	assertNwIsAvailable(c, "dualstackbridge")
330
+	dockerCmd(c, "run", "-d", "--net=dualstackbridge", "--name=first", "busybox", "top")
331
+	// Validate macvlan bridge mode defaults gateway sets the default IPAM next-hop inferred from the subnet
332
+	out, _, err := dockerCmdWithError("exec", "first", "ip", "route")
333
+	c.Assert(err, check.IsNil)
334
+	c.Assert(out, checker.Contains, "default via 172.28.130.1 dev eth0")
335
+	// Validate macvlan bridge mode sets the v6 gateway to the user specified default gateway/next-hop
336
+	out, _, err = dockerCmdWithError("exec", "first", "ip", "-6", "route")
337
+	c.Assert(err, check.IsNil)
338
+	c.Assert(out, checker.Contains, "default via 2001:db8:abca::254 dev eth0")
339
+
340
+	// Verify ipvlan l2 mode sets the proper default gateway routes via netlink
341
+	// for either an explicitly set route by the user or inferred via default IPAM
342
+	dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.140.0/24", "--gateway=172.28.140.254",
343
+		"--subnet=2001:db8:abcb::/64", "-o", "ipvlan_mode=l2", "dualstackl2")
344
+	assertNwIsAvailable(c, "dualstackl2")
345
+	dockerCmd(c, "run", "-d", "--net=dualstackl2", "--name=second", "busybox", "top")
346
+	// Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet
347
+	out, _, err = dockerCmdWithError("exec", "second", "ip", "route")
348
+	c.Assert(err, check.IsNil)
349
+	c.Assert(out, checker.Contains, "default via 172.28.140.254 dev eth0")
350
+	// Validate ipvlan l2 mode sets the v6 gateway to the user specified default gateway/next-hop
351
+	out, _, err = dockerCmdWithError("exec", "second", "ip", "-6", "route")
352
+	c.Assert(err, check.IsNil)
353
+	c.Assert(out, checker.Contains, "default via 2001:db8:abcb::1 dev eth0")
354
+
355
+	// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
356
+	dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.160.0/24", "--gateway=172.28.160.254",
357
+		"--subnet=2001:db8:abcd::/64", "--gateway=2001:db8:abcd::254", "-o", "ipvlan_mode=l3", "dualstackl3")
358
+	assertNwIsAvailable(c, "dualstackl3")
359
+	dockerCmd(c, "run", "-d", "--net=dualstackl3", "--name=third", "busybox", "top")
360
+	// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
361
+	out, _, err = dockerCmdWithError("exec", "third", "ip", "route")
362
+	c.Assert(err, check.IsNil)
363
+	c.Assert(out, checker.Contains, "default dev eth0")
364
+	// Validate ipvlan l3 mode sets the v6 gateway to dev eth0 and disregards any explicit or inferred next-hops
365
+	out, _, err = dockerCmdWithError("exec", "third", "ip", "-6", "route")
366
+	c.Assert(err, check.IsNil)
367
+	c.Assert(out, checker.Contains, "default dev eth0")
368
+}
369
+
370
+func (s *DockerSuite) TestDockerNetworkMacVlanBridgeNilParent(c *check.C) {
371
+	// macvlan bridge mode - dummy parent interface is provisioned dynamically
372
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
373
+	dockerCmd(c, "network", "create", "--driver=macvlan", "dm-nil-parent")
374
+	assertNwIsAvailable(c, "dm-nil-parent")
375
+
376
+	// start two containers on the same subnet
377
+	dockerCmd(c, "run", "-d", "--net=dm-nil-parent", "--name=first", "busybox", "top")
378
+	c.Assert(waitRun("first"), check.IsNil)
379
+	dockerCmd(c, "run", "-d", "--net=dm-nil-parent", "--name=second", "busybox", "top")
380
+	c.Assert(waitRun("second"), check.IsNil)
381
+
382
+	// intra-network communications should succeed
383
+	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
384
+	c.Assert(err, check.IsNil)
385
+}
386
+
387
+func (s *DockerSuite) TestDockerNetworkMacVlanBridgeInternalMode(c *check.C) {
388
+	// macvlan bridge mode --internal containers can communicate inside the network but not externally
389
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
390
+	dockerCmd(c, "network", "create", "--driver=macvlan", "--internal", "dm-internal")
391
+	assertNwIsAvailable(c, "dm-internal")
392
+	nr := getNetworkResource(c, "dm-internal")
393
+	c.Assert(nr.Internal, checker.True)
394
+
395
+	// start two containers on the same subnet
396
+	dockerCmd(c, "run", "-d", "--net=dm-internal", "--name=first", "busybox", "top")
397
+	c.Assert(waitRun("first"), check.IsNil)
398
+	dockerCmd(c, "run", "-d", "--net=dm-internal", "--name=second", "busybox", "top")
399
+	c.Assert(waitRun("second"), check.IsNil)
400
+
401
+	// access outside of the network should fail
402
+	_, _, err := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
403
+	c.Assert(err, check.NotNil)
404
+	// intra-network communications should succeed
405
+	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
406
+	c.Assert(err, check.IsNil)
407
+}
408
+
409
+func (s *DockerSuite) TestDockerNetworkIpvlanL2NilParent(c *check.C) {
410
+	// ipvlan l2 mode - dummy parent interface is provisioned dynamically
411
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
412
+	dockerCmd(c, "network", "create", "--driver=ipvlan", "di-nil-parent")
413
+	assertNwIsAvailable(c, "di-nil-parent")
414
+
415
+	// start two containers on the same subnet
416
+	dockerCmd(c, "run", "-d", "--net=di-nil-parent", "--name=first", "busybox", "top")
417
+	c.Assert(waitRun("first"), check.IsNil)
418
+	dockerCmd(c, "run", "-d", "--net=di-nil-parent", "--name=second", "busybox", "top")
419
+	c.Assert(waitRun("second"), check.IsNil)
420
+
421
+	// intra-network communications should succeed
422
+	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
423
+	c.Assert(err, check.IsNil)
424
+}
425
+
426
+func (s *DockerSuite) TestDockerNetworkIpvlanL2InternalMode(c *check.C) {
427
+	// ipvlan l2 mode --internal containers can communicate inside the network but not externally
428
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
429
+	dockerCmd(c, "network", "create", "--driver=ipvlan", "--internal", "di-internal")
430
+	assertNwIsAvailable(c, "di-internal")
431
+	nr := getNetworkResource(c, "di-internal")
432
+	c.Assert(nr.Internal, checker.True)
433
+
434
+	// start two containers on the same subnet
435
+	dockerCmd(c, "run", "-d", "--net=di-internal", "--name=first", "busybox", "top")
436
+	c.Assert(waitRun("first"), check.IsNil)
437
+	dockerCmd(c, "run", "-d", "--net=di-internal", "--name=second", "busybox", "top")
438
+	c.Assert(waitRun("second"), check.IsNil)
439
+
440
+	// access outside of the network should fail
441
+	_, _, err := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
442
+	c.Assert(err, check.NotNil)
443
+	// intra-network communications should succeed
444
+	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
445
+	c.Assert(err, check.IsNil)
446
+}
447
+
448
+func (s *DockerSuite) TestDockerNetworkIpvlanL3NilParent(c *check.C) {
449
+	// ipvlan l3 mode - dummy parent interface is provisioned dynamically
450
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
451
+	dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.230.0/24",
452
+		"--subnet=172.28.220.0/24", "-o", "ipvlan_mode=l3", "di-nil-parent-l3")
453
+	assertNwIsAvailable(c, "di-nil-parent-l3")
454
+
455
+	// start two containers on separate subnets
456
+	dockerCmd(c, "run", "-d", "--ip=172.28.220.10", "--net=di-nil-parent-l3", "--name=first", "busybox", "top")
457
+	c.Assert(waitRun("first"), check.IsNil)
458
+	dockerCmd(c, "run", "-d", "--ip=172.28.230.10", "--net=di-nil-parent-l3", "--name=second", "busybox", "top")
459
+	c.Assert(waitRun("second"), check.IsNil)
460
+
461
+	// intra-network communications should succeed
462
+	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
463
+	c.Assert(err, check.IsNil)
464
+}
465
+
466
+func (s *DockerSuite) TestDockerNetworkIpvlanL3InternalMode(c *check.C) {
467
+	// ipvlan l3 mode --internal containers can communicate inside the network but not externally
468
+	testRequires(c, DaemonIsLinux, IpvlanKernelSupport, NotUserNamespace, NotArm)
469
+	dockerCmd(c, "network", "create", "--driver=ipvlan", "--subnet=172.28.230.0/24",
470
+		"--subnet=172.28.220.0/24", "-o", "ipvlan_mode=l3", "--internal", "di-internal-l3")
471
+	assertNwIsAvailable(c, "di-internal-l3")
472
+	nr := getNetworkResource(c, "di-internal-l3")
473
+	c.Assert(nr.Internal, checker.True)
474
+
475
+	// start two containers on separate subnets
476
+	dockerCmd(c, "run", "-d", "--ip=172.28.220.10", "--net=di-internal-l3", "--name=first", "busybox", "top")
477
+	c.Assert(waitRun("first"), check.IsNil)
478
+	dockerCmd(c, "run", "-d", "--ip=172.28.230.10", "--net=di-internal-l3", "--name=second", "busybox", "top")
479
+	c.Assert(waitRun("second"), check.IsNil)
480
+
481
+	// access outside of the network should fail
482
+	_, _, err := dockerCmdWithTimeout(time.Second, "exec", "first", "ping", "-c", "1", "-w", "1", "8.8.8.8")
483
+	c.Assert(err, check.NotNil)
484
+	// intra-network communications should succeed
485
+	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
486
+	c.Assert(err, check.IsNil)
487
+}
488
+
489
+func (s *DockerSuite) TestDockerNetworkMacVlanExistingParent(c *check.C) {
490
+	// macvlan bridge mode - empty parent interface containers can reach each other internally but not externally
491
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
492
+	netName := "dm-parent-exists"
493
+	out, err := createMasterDummy(c, "dm-dummy0")
494
+	//out, err := createVlanInterface(c, "dm-parent", "dm-slave", "macvlan", "bridge")
495
+	c.Assert(err, check.IsNil, check.Commentf(out))
496
+	// create a network using an existing parent interface
497
+	dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0", netName)
498
+	assertNwIsAvailable(c, netName)
499
+	// delete the network while preserving the parent link
500
+	dockerCmd(c, "network", "rm", netName)
501
+	assertNwNotAvailable(c, netName)
502
+	// verify the network delete did not delete the predefined link
503
+	out, err = linkExists(c, "dm-dummy0")
504
+	c.Assert(err, check.IsNil, check.Commentf(out))
505
+	deleteInterface(c, "dm-dummy0")
506
+	c.Assert(err, check.IsNil, check.Commentf(out))
507
+}
508
+
509
+func (s *DockerSuite) TestDockerNetworkMacVlanSubinterface(c *check.C) {
510
+	// macvlan bridge mode -  empty parent interface containers can reach each other internally but not externally
511
+	testRequires(c, DaemonIsLinux, MacvlanKernelSupport, NotUserNamespace, NotArm)
512
+	netName := "dm-subinterface"
513
+	out, err := createMasterDummy(c, "dm-dummy0")
514
+	c.Assert(err, check.IsNil, check.Commentf(out))
515
+	out, err = createVlanInterface(c, "dm-dummy0", "dm-dummy0.20", "20")
516
+	c.Assert(err, check.IsNil, check.Commentf(out))
517
+	// create a network using an existing parent interface
518
+	dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.20", netName)
519
+	assertNwIsAvailable(c, netName)
520
+
521
+	// start containers on 802.1q tagged '-o parent' sub-interface
522
+	dockerCmd(c, "run", "-d", "--net=dm-subinterface", "--name=first", "busybox", "top")
523
+	c.Assert(waitRun("first"), check.IsNil)
524
+	dockerCmd(c, "run", "-d", "--net=dm-subinterface", "--name=second", "busybox", "top")
525
+	c.Assert(waitRun("second"), check.IsNil)
526
+	// verify containers can communicate
527
+	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
528
+	c.Assert(err, check.IsNil)
529
+
530
+	// remove the containers
531
+	dockerCmd(c, "rm", "-f", "first")
532
+	dockerCmd(c, "rm", "-f", "second")
533
+	// delete the network while preserving the parent link
534
+	dockerCmd(c, "network", "rm", netName)
535
+	assertNwNotAvailable(c, netName)
536
+	// verify the network delete did not delete the predefined sub-interface
537
+	out, err = linkExists(c, "dm-dummy0.20")
538
+	c.Assert(err, check.IsNil, check.Commentf(out))
539
+	// delete the parent interface which also collects the slave
540
+	deleteInterface(c, "dm-dummy0")
541
+	c.Assert(err, check.IsNil, check.Commentf(out))
542
+}
543
+
544
+func createMasterDummy(c *check.C, master string) (string, error) {
545
+	// ip link add <dummy_name> type dummy
546
+	args := []string{"link", "add", master, "type", "dummy"}
547
+	ipLinkCmd := exec.Command("ip", args...)
548
+	out, _, err := runCommandWithOutput(ipLinkCmd)
549
+	if err != nil {
550
+		return out, err
551
+	}
552
+	// ip link set dummy_name up
553
+	args = []string{"link", "set", master, "up"}
554
+	ipLinkCmd = exec.Command("ip", args...)
555
+	out, _, err = runCommandWithOutput(ipLinkCmd)
556
+	if err != nil {
557
+		return out, err
558
+	}
559
+	return out, err
560
+}
561
+
562
+func createVlanInterface(c *check.C, master, slave, id string) (string, error) {
563
+	// ip link add link <master> name <master>.<VID> type vlan id <VID>
564
+	args := []string{"link", "add", "link", master, "name", slave, "type", "vlan", "id", id}
565
+	ipLinkCmd := exec.Command("ip", args...)
566
+	out, _, err := runCommandWithOutput(ipLinkCmd)
567
+	if err != nil {
568
+		return out, err
569
+	}
570
+	// ip link set <sub_interface_name> up
571
+	args = []string{"link", "set", slave, "up"}
572
+	ipLinkCmd = exec.Command("ip", args...)
573
+	out, _, err = runCommandWithOutput(ipLinkCmd)
574
+	if err != nil {
575
+		return out, err
576
+	}
577
+	return out, err
578
+}
579
+
580
+func linkExists(c *check.C, master string) (string, error) {
581
+	// verify the specified link exists, ip link show <link_name>
582
+	args := []string{"link", "show", master}
583
+	ipLinkCmd := exec.Command("ip", args...)
584
+	out, _, err := runCommandWithOutput(ipLinkCmd)
585
+	if err != nil {
586
+		return out, err
587
+	}
588
+	return out, err
589
+}