Skip to content

Commit c1ab6ed

Browse files
committed
integration/networking: Test bridge ICC and INC
Following tests are implemented in this specific commit: - Inter-container communications for internal and non-internal bridge networks, over IPv4 and IPv6. - Inter-container communications using IPv6 link-local addresses for internal and non-internal bridge networks. - Inter-network communications for internal and non-internal bridge networks, over IPv4 and IPv6, are disallowed. Signed-off-by: Albin Kerouanton <[email protected]>
1 parent 409ea70 commit c1ab6ed

File tree

1 file changed

+294
-0
lines changed

1 file changed

+294
-0
lines changed
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
package networking
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
"time"
8+
9+
"github.com/docker/docker/api/types"
10+
containertypes "github.com/docker/docker/api/types/container"
11+
"github.com/docker/docker/integration/internal/container"
12+
"github.com/docker/docker/integration/internal/network"
13+
"github.com/docker/docker/testutil"
14+
"github.com/docker/docker/testutil/daemon"
15+
"gotest.tools/v3/assert"
16+
is "gotest.tools/v3/assert/cmp"
17+
"gotest.tools/v3/skip"
18+
)
19+
20+
// TestBridgeICC tries to ping container ctr1 from container ctr2 using its hostname. Thus, this test checks:
21+
// 1. DNS resolution ; 2. ARP/NDP ; 3. whether containers can communicate with each other ; 4. kernel-assigned SLAAC
22+
// addresses.
23+
func TestBridgeICC(t *testing.T) {
24+
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
25+
26+
ctx := setupTest(t)
27+
28+
d := daemon.New(t)
29+
d.StartWithBusybox(ctx, t, "-D", "--experimental", "--ip6tables")
30+
defer d.Stop(t)
31+
32+
c := d.NewClientT(t)
33+
defer c.Close()
34+
35+
testcases := []struct {
36+
name string
37+
bridgeOpts []func(*types.NetworkCreate)
38+
ctr1Opts []func(*container.TestContainerConfig)
39+
linkLocal bool
40+
pingHost string
41+
}{
42+
{
43+
name: "IPv4 non-internal network",
44+
bridgeOpts: []func(*types.NetworkCreate){},
45+
},
46+
{
47+
name: "IPv4 internal network",
48+
bridgeOpts: []func(*types.NetworkCreate){
49+
network.WithInternal(),
50+
},
51+
},
52+
{
53+
name: "IPv6 ULA on non-internal network",
54+
bridgeOpts: []func(*types.NetworkCreate){
55+
network.WithIPv6(),
56+
network.WithIPAM("fdf1:a844:380c:b200::/64", "fdf1:a844:380c:b200::1"),
57+
},
58+
},
59+
{
60+
name: "IPv6 ULA on internal network",
61+
bridgeOpts: []func(*types.NetworkCreate){
62+
network.WithIPv6(),
63+
network.WithInternal(),
64+
network.WithIPAM("fdf1:a844:380c:b247::/64", "fdf1:a844:380c:b247::1"),
65+
},
66+
},
67+
{
68+
name: "IPv6 link-local address on non-internal network",
69+
bridgeOpts: []func(*types.NetworkCreate){
70+
network.WithIPv6(),
71+
// There's no real way to specify an IPv6 network is only used with SLAAC link-local IPv6 addresses.
72+
// What we can do instead, is to tell the IPAM driver to assign addresses from the link-local prefix.
73+
// Each container will have two link-local addresses: 1. a SLAAC address assigned by the kernel ;
74+
// 2. the one dynamically assigned by the IPAM driver.
75+
network.WithIPAM("fe80::/64", "fe80::1"),
76+
},
77+
linkLocal: true,
78+
},
79+
{
80+
name: "IPv6 link-local address on internal network",
81+
bridgeOpts: []func(*types.NetworkCreate){
82+
network.WithIPv6(),
83+
network.WithInternal(),
84+
// See the note above about link-local addresses.
85+
network.WithIPAM("fe80::/64", "fe80::1"),
86+
},
87+
linkLocal: true,
88+
},
89+
{
90+
name: "IPv6 non-internal network with SLAAC LL address",
91+
bridgeOpts: []func(*types.NetworkCreate){
92+
network.WithIPv6(),
93+
network.WithIPAM("fdf1:a844:380c:b247::/64", "fdf1:a844:380c:b247::1"),
94+
},
95+
ctr1Opts: []func(*container.TestContainerConfig){
96+
// Link-local address is derived from the MAC address, so we need to
97+
// specify one here to hardcode the SLAAC LL address below.
98+
container.WithMacAddress("02:42:ac:11:00:02"),
99+
},
100+
pingHost: "fe80::42:acff:fe11:2%eth0",
101+
},
102+
{
103+
name: "IPv6 internal network with SLAAC LL address",
104+
bridgeOpts: []func(*types.NetworkCreate){
105+
network.WithIPv6(),
106+
network.WithIPAM("fdf1:a844:380c:b247::/64", "fdf1:a844:380c:b247::1"),
107+
},
108+
ctr1Opts: []func(*container.TestContainerConfig){
109+
// Link-local address is derived from the MAC address, so we need to
110+
// specify one here to hardcode the SLAAC LL address below.
111+
container.WithMacAddress("02:42:ac:11:00:02"),
112+
},
113+
pingHost: "fe80::42:acff:fe11:2%eth0",
114+
},
115+
}
116+
117+
for tcID, tc := range testcases {
118+
t.Run(tc.name, func(t *testing.T) {
119+
ctx := testutil.StartSpan(ctx, t)
120+
121+
bridgeName := fmt.Sprintf("testnet-icc-%d", tcID)
122+
network.CreateNoError(ctx, t, c, bridgeName, append(tc.bridgeOpts,
123+
network.WithDriver("bridge"),
124+
network.WithOption("com.docker.network.bridge.name", bridgeName))...)
125+
defer network.RemoveNoError(ctx, t, c, bridgeName)
126+
127+
ctr1Name := fmt.Sprintf("ctr-icc-%d-1", tcID)
128+
id1 := container.Run(ctx, t, c, append(tc.ctr1Opts,
129+
container.WithName(ctr1Name),
130+
container.WithImage("busybox:latest"),
131+
container.WithCmd("top"),
132+
container.WithNetworkMode(bridgeName))...)
133+
defer c.ContainerRemove(ctx, id1, containertypes.RemoveOptions{
134+
Force: true,
135+
})
136+
137+
pingHost := tc.pingHost
138+
if pingHost == "" {
139+
if tc.linkLocal {
140+
inspect := container.Inspect(ctx, t, c, id1)
141+
pingHost = inspect.NetworkSettings.Networks[bridgeName].GlobalIPv6Address + "%eth0"
142+
} else {
143+
pingHost = ctr1Name
144+
}
145+
}
146+
147+
pingCmd := []string{"ping", "-c1", "-W3", pingHost}
148+
149+
ctr2Name := fmt.Sprintf("ctr-icc-%d-2", tcID)
150+
attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
151+
defer cancel()
152+
res := container.RunAttach(attachCtx, t, c,
153+
container.WithName(ctr2Name),
154+
container.WithImage("busybox:latest"),
155+
container.WithCmd(pingCmd...),
156+
container.WithNetworkMode(bridgeName))
157+
defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{
158+
Force: true,
159+
})
160+
161+
assert.Check(t, is.Equal(res.ExitCode, 0))
162+
assert.Check(t, is.Equal(res.Stderr.Len(), 0))
163+
assert.Check(t, is.Contains(res.Stdout.String(), "1 packets transmitted, 1 packets received"))
164+
})
165+
}
166+
}
167+
168+
// TestBridgeINC makes sure two containers on two different bridge networks can't communicate with each other.
169+
func TestBridgeINC(t *testing.T) {
170+
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
171+
172+
ctx := setupTest(t)
173+
174+
d := daemon.New(t)
175+
d.StartWithBusybox(ctx, t, "-D", "--experimental", "--ip6tables")
176+
defer d.Stop(t)
177+
178+
c := d.NewClientT(t)
179+
defer c.Close()
180+
181+
type bridgesOpts struct {
182+
bridge1Opts []func(*types.NetworkCreate)
183+
bridge2Opts []func(*types.NetworkCreate)
184+
}
185+
186+
testcases := []struct {
187+
name string
188+
bridges bridgesOpts
189+
ipv6 bool
190+
stdout string
191+
stderr string
192+
}{
193+
{
194+
name: "IPv4 non-internal network",
195+
bridges: bridgesOpts{
196+
bridge1Opts: []func(*types.NetworkCreate){},
197+
bridge2Opts: []func(*types.NetworkCreate){},
198+
},
199+
stdout: "1 packets transmitted, 0 packets received",
200+
},
201+
{
202+
name: "IPv4 internal network",
203+
bridges: bridgesOpts{
204+
bridge1Opts: []func(*types.NetworkCreate){network.WithInternal()},
205+
bridge2Opts: []func(*types.NetworkCreate){network.WithInternal()},
206+
},
207+
stderr: "sendto: Network is unreachable",
208+
},
209+
{
210+
name: "IPv6 ULA on non-internal network",
211+
bridges: bridgesOpts{
212+
bridge1Opts: []func(*types.NetworkCreate){
213+
network.WithIPv6(),
214+
network.WithIPAM("fdf1:a844:380c:b200::/64", "fdf1:a844:380c:b200::1"),
215+
},
216+
bridge2Opts: []func(*types.NetworkCreate){
217+
network.WithIPv6(),
218+
network.WithIPAM("fdf1:a844:380c:b247::/64", "fdf1:a844:380c:b247::1"),
219+
},
220+
},
221+
ipv6: true,
222+
stdout: "1 packets transmitted, 0 packets received",
223+
},
224+
{
225+
name: "IPv6 ULA on internal network",
226+
bridges: bridgesOpts{
227+
bridge1Opts: []func(*types.NetworkCreate){
228+
network.WithIPv6(),
229+
network.WithInternal(),
230+
network.WithIPAM("fdf1:a844:390c:b200::/64", "fdf1:a844:390c:b200::1"),
231+
},
232+
bridge2Opts: []func(*types.NetworkCreate){
233+
network.WithIPv6(),
234+
network.WithInternal(),
235+
network.WithIPAM("fdf1:a844:390c:b247::/64", "fdf1:a844:390c:b247::1"),
236+
},
237+
},
238+
ipv6: true,
239+
stderr: "sendto: Network is unreachable",
240+
},
241+
}
242+
243+
for tcID, tc := range testcases {
244+
t.Run(tc.name, func(t *testing.T) {
245+
ctx := testutil.StartSpan(ctx, t)
246+
247+
bridge1 := fmt.Sprintf("testnet-inc-%d-1", tcID)
248+
bridge2 := fmt.Sprintf("testnet-inc-%d-2", tcID)
249+
250+
network.CreateNoError(ctx, t, c, bridge1, append(tc.bridges.bridge1Opts,
251+
network.WithDriver("bridge"),
252+
network.WithOption("com.docker.network.bridge.name", bridge1))...)
253+
defer network.RemoveNoError(ctx, t, c, bridge1)
254+
network.CreateNoError(ctx, t, c, bridge2, append(tc.bridges.bridge2Opts,
255+
network.WithDriver("bridge"),
256+
network.WithOption("com.docker.network.bridge.name", bridge2))...)
257+
defer network.RemoveNoError(ctx, t, c, bridge2)
258+
259+
ctr1Name := sanitizeCtrName(t.Name() + "-ctr1")
260+
id1 := container.Run(ctx, t, c,
261+
container.WithName(ctr1Name),
262+
container.WithImage("busybox:latest"),
263+
container.WithCmd("top"),
264+
container.WithNetworkMode(bridge1))
265+
defer c.ContainerRemove(ctx, id1, containertypes.RemoveOptions{
266+
Force: true,
267+
})
268+
269+
ctr1Info := container.Inspect(ctx, t, c, id1)
270+
targetAddr := ctr1Info.NetworkSettings.Networks[bridge1].IPAddress
271+
if tc.ipv6 {
272+
targetAddr = ctr1Info.NetworkSettings.Networks[bridge1].GlobalIPv6Address
273+
}
274+
275+
pingCmd := []string{"ping", "-c1", "-W3", targetAddr}
276+
277+
ctr2Name := sanitizeCtrName(t.Name() + "-ctr2")
278+
attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
279+
defer cancel()
280+
res := container.RunAttach(attachCtx, t, c,
281+
container.WithName(ctr2Name),
282+
container.WithImage("busybox:latest"),
283+
container.WithCmd(pingCmd...),
284+
container.WithNetworkMode(bridge2))
285+
defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{
286+
Force: true,
287+
})
288+
289+
assert.Check(t, res.ExitCode != 0, "ping unexpectedly succeeded")
290+
assert.Check(t, is.Contains(res.Stdout.String(), tc.stdout))
291+
assert.Check(t, is.Contains(res.Stderr.String(), tc.stderr))
292+
})
293+
}
294+
}

0 commit comments

Comments
 (0)