bridge_test.go 9.2 KB

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