ipvlan_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. //go:build !windows
  2. package ipvlan // import "github.com/docker/docker/integration/network/ipvlan"
  3. import (
  4. "context"
  5. "fmt"
  6. "strings"
  7. "testing"
  8. "github.com/docker/docker/api/types"
  9. containertypes "github.com/docker/docker/api/types/container"
  10. dclient "github.com/docker/docker/client"
  11. "github.com/docker/docker/integration/internal/container"
  12. net "github.com/docker/docker/integration/internal/network"
  13. n "github.com/docker/docker/integration/network"
  14. "github.com/docker/docker/testutil"
  15. "github.com/docker/docker/testutil/daemon"
  16. "gotest.tools/v3/assert"
  17. is "gotest.tools/v3/assert/cmp"
  18. "gotest.tools/v3/skip"
  19. )
  20. func TestDockerNetworkIpvlanPersistance(t *testing.T) {
  21. // verify the driver automatically provisions the 802.1q link (di-dummy0.70)
  22. skip.If(t, testEnv.IsRemoteDaemon)
  23. skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
  24. ctx := testutil.StartSpan(baseContext, t)
  25. d := daemon.New(t)
  26. d.StartWithBusybox(ctx, t)
  27. defer d.Stop(t)
  28. // master dummy interface 'di' notation represent 'docker ipvlan'
  29. master := "di-dummy0"
  30. n.CreateMasterDummy(ctx, t, master)
  31. defer n.DeleteInterface(ctx, t, master)
  32. c := d.NewClientT(t)
  33. // create a network specifying the desired sub-interface name
  34. netName := "di-persist"
  35. net.CreateNoError(ctx, t, c, netName,
  36. net.WithIPvlan("di-dummy0.70", ""),
  37. )
  38. assert.Check(t, n.IsNetworkAvailable(ctx, c, netName))
  39. // Restart docker daemon to test the config has persisted to disk
  40. d.Restart(t)
  41. assert.Check(t, n.IsNetworkAvailable(ctx, c, netName))
  42. }
  43. func TestDockerNetworkIpvlan(t *testing.T) {
  44. skip.If(t, testEnv.IsRemoteDaemon)
  45. skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
  46. ctx := testutil.StartSpan(baseContext, t)
  47. for _, tc := range []struct {
  48. name string
  49. test func(*testing.T, context.Context, dclient.APIClient)
  50. }{
  51. {
  52. name: "Subinterface",
  53. test: testIpvlanSubinterface,
  54. }, {
  55. name: "OverlapParent",
  56. test: testIpvlanOverlapParent,
  57. }, {
  58. name: "L2NilParent",
  59. test: testIpvlanL2NilParent,
  60. }, {
  61. name: "L2InternalMode",
  62. test: testIpvlanL2InternalMode,
  63. }, {
  64. name: "L3NilParent",
  65. test: testIpvlanL3NilParent,
  66. }, {
  67. name: "L3InternalMode",
  68. test: testIpvlanL3InternalMode,
  69. }, {
  70. name: "L2MultiSubnetWithParent",
  71. test: testIpvlanL2MultiSubnetWithParent,
  72. }, {
  73. name: "L2MultiSubnetNoParent",
  74. test: testIpvlanL2MultiSubnetNoParent,
  75. }, {
  76. name: "L3MultiSubnet",
  77. test: testIpvlanL3MultiSubnet,
  78. }, {
  79. name: "L2Addressing",
  80. test: testIpvlanL2Addressing,
  81. }, {
  82. name: "L3Addressing",
  83. test: testIpvlanL3Addressing,
  84. },
  85. } {
  86. t.Run(tc.name, func(t *testing.T) {
  87. testutil.StartSpan(ctx, t)
  88. d := daemon.New(t)
  89. t.Cleanup(func() { d.Stop(t) })
  90. d.StartWithBusybox(ctx, t)
  91. c := d.NewClientT(t)
  92. tc.test(t, ctx, c)
  93. })
  94. // FIXME(vdemeester) clean network
  95. }
  96. }
  97. func testIpvlanSubinterface(t *testing.T, ctx context.Context, client dclient.APIClient) {
  98. master := "di-dummy0"
  99. n.CreateMasterDummy(ctx, t, master)
  100. defer n.DeleteInterface(ctx, t, master)
  101. netName := "di-subinterface"
  102. net.CreateNoError(ctx, t, client, netName,
  103. net.WithIPvlan("di-dummy0.60", ""),
  104. )
  105. assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
  106. // delete the network while preserving the parent link
  107. err := client.NetworkRemove(ctx, netName)
  108. assert.NilError(t, err)
  109. assert.Check(t, n.IsNetworkNotAvailable(ctx, client, netName))
  110. // verify the network delete did not delete the predefined link
  111. n.LinkExists(ctx, t, "di-dummy0")
  112. }
  113. func testIpvlanOverlapParent(t *testing.T, ctx context.Context, client dclient.APIClient) {
  114. // verify the same parent interface cannot be used if already in use by an existing network
  115. master := "di-dummy0"
  116. parent := master + ".30"
  117. n.CreateMasterDummy(ctx, t, master)
  118. defer n.DeleteInterface(ctx, t, master)
  119. n.CreateVlanInterface(ctx, t, master, parent, "30")
  120. netName := "di-subinterface"
  121. net.CreateNoError(ctx, t, client, netName,
  122. net.WithIPvlan(parent, ""),
  123. )
  124. assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
  125. _, err := net.Create(ctx, client, netName,
  126. net.WithIPvlan(parent, ""),
  127. )
  128. // verify that the overlap returns an error
  129. assert.Check(t, err != nil)
  130. }
  131. func testIpvlanL2NilParent(t *testing.T, ctx context.Context, client dclient.APIClient) {
  132. // ipvlan l2 mode - dummy parent interface is provisioned dynamically
  133. netName := "di-nil-parent"
  134. net.CreateNoError(ctx, t, client, netName,
  135. net.WithIPvlan("", ""),
  136. )
  137. assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
  138. id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
  139. id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
  140. _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
  141. assert.NilError(t, err)
  142. }
  143. func testIpvlanL2InternalMode(t *testing.T, ctx context.Context, client dclient.APIClient) {
  144. netName := "di-internal"
  145. net.CreateNoError(ctx, t, client, netName,
  146. net.WithIPvlan("", ""),
  147. net.WithInternal(),
  148. )
  149. assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
  150. id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
  151. id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
  152. result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"})
  153. assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable"))
  154. _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
  155. assert.NilError(t, err)
  156. }
  157. func testIpvlanL3NilParent(t *testing.T, ctx context.Context, client dclient.APIClient) {
  158. netName := "di-nil-parent-l3"
  159. net.CreateNoError(ctx, t, client, netName,
  160. net.WithIPvlan("", "l3"),
  161. net.WithIPAM("172.28.230.0/24", ""),
  162. net.WithIPAM("172.28.220.0/24", ""),
  163. )
  164. assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
  165. id1 := container.Run(ctx, t, client,
  166. container.WithNetworkMode(netName),
  167. container.WithIPv4(netName, "172.28.220.10"),
  168. )
  169. id2 := container.Run(ctx, t, client,
  170. container.WithNetworkMode(netName),
  171. container.WithIPv4(netName, "172.28.230.10"),
  172. )
  173. _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
  174. assert.NilError(t, err)
  175. }
  176. func testIpvlanL3InternalMode(t *testing.T, ctx context.Context, client dclient.APIClient) {
  177. netName := "di-internal-l3"
  178. net.CreateNoError(ctx, t, client, netName,
  179. net.WithIPvlan("", "l3"),
  180. net.WithInternal(),
  181. net.WithIPAM("172.28.230.0/24", ""),
  182. net.WithIPAM("172.28.220.0/24", ""),
  183. )
  184. assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
  185. id1 := container.Run(ctx, t, client,
  186. container.WithNetworkMode(netName),
  187. container.WithIPv4(netName, "172.28.220.10"),
  188. )
  189. id2 := container.Run(ctx, t, client,
  190. container.WithNetworkMode(netName),
  191. container.WithIPv4(netName, "172.28.230.10"),
  192. )
  193. result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"})
  194. assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable"))
  195. _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
  196. assert.NilError(t, err)
  197. }
  198. func testIpvlanL2MultiSubnetWithParent(t *testing.T, ctx context.Context, client dclient.APIClient) {
  199. const parentIfName = "di-dummy0"
  200. n.CreateMasterDummy(ctx, t, parentIfName)
  201. defer n.DeleteInterface(ctx, t, parentIfName)
  202. testIpvlanL2MultiSubnet(t, ctx, client, parentIfName)
  203. }
  204. func testIpvlanL2MultiSubnetNoParent(t *testing.T, ctx context.Context, client dclient.APIClient) {
  205. testIpvlanL2MultiSubnet(t, ctx, client, "")
  206. }
  207. func testIpvlanL2MultiSubnet(t *testing.T, ctx context.Context, client dclient.APIClient, parent string) {
  208. netName := "dualstackl2"
  209. net.CreateNoError(ctx, t, client, netName,
  210. net.WithIPvlan(parent, ""),
  211. net.WithIPv6(),
  212. net.WithIPAM("172.28.200.0/24", ""),
  213. net.WithIPAM("172.28.202.0/24", "172.28.202.254"),
  214. net.WithIPAM("2001:db8:abc8::/64", ""),
  215. net.WithIPAM("2001:db8:abc6::/64", "2001:db8:abc6::254"),
  216. )
  217. assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
  218. // 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
  219. id1 := container.Run(ctx, t, client,
  220. container.WithNetworkMode(netName),
  221. container.WithIPv4(netName, "172.28.200.20"),
  222. container.WithIPv6(netName, "2001:db8:abc8::20"),
  223. )
  224. id2 := container.Run(ctx, t, client,
  225. container.WithNetworkMode(netName),
  226. container.WithIPv4(netName, "172.28.200.21"),
  227. container.WithIPv6(netName, "2001:db8:abc8::21"),
  228. )
  229. c1, err := client.ContainerInspect(ctx, id1)
  230. assert.NilError(t, err)
  231. if parent == "" {
  232. // Inspect the v4 gateway to ensure no default GW was assigned
  233. assert.Check(t, is.Equal(c1.NetworkSettings.Networks[netName].Gateway, ""))
  234. // Inspect the v6 gateway to ensure no default GW was assigned
  235. assert.Check(t, is.Equal(c1.NetworkSettings.Networks[netName].IPv6Gateway, ""))
  236. } else {
  237. // Inspect the v4 gateway to ensure the proper default GW was assigned
  238. assert.Check(t, is.Equal(c1.NetworkSettings.Networks[netName].Gateway, "172.28.200.1"))
  239. // Inspect the v6 gateway to ensure the proper default GW was assigned
  240. assert.Check(t, is.Equal(c1.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc8::1"))
  241. }
  242. // verify ipv4 connectivity to the explicit --ip address second to first
  243. _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress})
  244. assert.NilError(t, err)
  245. // verify ipv6 connectivity to the explicit --ip6 address second to first
  246. _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address})
  247. assert.NilError(t, err)
  248. // 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
  249. id3 := container.Run(ctx, t, client,
  250. container.WithNetworkMode(netName),
  251. container.WithIPv4(netName, "172.28.202.20"),
  252. container.WithIPv6(netName, "2001:db8:abc6::20"),
  253. )
  254. id4 := container.Run(ctx, t, client,
  255. container.WithNetworkMode(netName),
  256. container.WithIPv4(netName, "172.28.202.21"),
  257. container.WithIPv6(netName, "2001:db8:abc6::21"),
  258. )
  259. c3, err := client.ContainerInspect(ctx, id3)
  260. assert.NilError(t, err)
  261. if parent == "" {
  262. // Inspect the v4 gateway to ensure no default GW was assigned
  263. assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].Gateway, ""))
  264. // Inspect the v6 gateway to ensure no default GW was assigned
  265. assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].IPv6Gateway, ""))
  266. } else {
  267. // Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
  268. assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].Gateway, "172.28.202.254"))
  269. // Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
  270. assert.Check(t, is.Equal(c3.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc6::254"))
  271. }
  272. // verify ipv4 connectivity to the explicit --ip address from third to fourth
  273. _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress})
  274. assert.NilError(t, err)
  275. // verify ipv6 connectivity to the explicit --ip6 address from third to fourth
  276. _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address})
  277. assert.NilError(t, err)
  278. }
  279. func testIpvlanL3MultiSubnet(t *testing.T, ctx context.Context, client dclient.APIClient) {
  280. netName := "dualstackl3"
  281. net.CreateNoError(ctx, t, client, netName,
  282. net.WithIPvlan("", "l3"),
  283. net.WithIPv6(),
  284. net.WithIPAM("172.28.10.0/24", ""),
  285. net.WithIPAM("172.28.12.0/24", "172.28.12.254"),
  286. net.WithIPAM("2001:db8:abc9::/64", ""),
  287. net.WithIPAM("2001:db8:abc7::/64", "2001:db8:abc7::254"),
  288. )
  289. assert.Check(t, n.IsNetworkAvailable(ctx, client, netName))
  290. // 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
  291. id1 := container.Run(ctx, t, client,
  292. container.WithNetworkMode(netName),
  293. container.WithIPv4(netName, "172.28.10.20"),
  294. container.WithIPv6(netName, "2001:db8:abc9::20"),
  295. )
  296. id2 := container.Run(ctx, t, client,
  297. container.WithNetworkMode(netName),
  298. container.WithIPv4(netName, "172.28.10.21"),
  299. container.WithIPv6(netName, "2001:db8:abc9::21"),
  300. )
  301. c1, err := client.ContainerInspect(ctx, id1)
  302. assert.NilError(t, err)
  303. // verify ipv4 connectivity to the explicit --ipv address second to first
  304. _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress})
  305. assert.NilError(t, err)
  306. // verify ipv6 connectivity to the explicit --ipv6 address second to first
  307. _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address})
  308. assert.NilError(t, err)
  309. // 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
  310. id3 := container.Run(ctx, t, client,
  311. container.WithNetworkMode(netName),
  312. container.WithIPv4(netName, "172.28.12.20"),
  313. container.WithIPv6(netName, "2001:db8:abc7::20"),
  314. )
  315. id4 := container.Run(ctx, t, client,
  316. container.WithNetworkMode(netName),
  317. container.WithIPv4(netName, "172.28.12.21"),
  318. container.WithIPv6(netName, "2001:db8:abc7::21"),
  319. )
  320. c3, err := client.ContainerInspect(ctx, id3)
  321. assert.NilError(t, err)
  322. // verify ipv4 connectivity to the explicit --ipv address from third to fourth
  323. _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress})
  324. assert.NilError(t, err)
  325. // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
  326. _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address})
  327. assert.NilError(t, err)
  328. // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
  329. assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "")
  330. // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
  331. assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "")
  332. // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
  333. assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "")
  334. // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
  335. assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "")
  336. }
  337. // Verify ipvlan l2 mode sets the proper default gateway routes via netlink
  338. // for either an explicitly set route by the user or inferred via default IPAM
  339. func testIpvlanL2Addressing(t *testing.T, ctx context.Context, client dclient.APIClient) {
  340. const parentIfName = "di-dummy0"
  341. n.CreateMasterDummy(ctx, t, parentIfName)
  342. defer n.DeleteInterface(ctx, t, parentIfName)
  343. netNameL2 := "dualstackl2"
  344. net.CreateNoError(ctx, t, client, netNameL2,
  345. net.WithIPvlan(parentIfName, "l2"),
  346. net.WithIPv6(),
  347. net.WithIPAM("172.28.140.0/24", "172.28.140.254"),
  348. net.WithIPAM("2001:db8:abcb::/64", ""),
  349. )
  350. assert.Check(t, n.IsNetworkAvailable(ctx, client, netNameL2))
  351. id := container.Run(ctx, t, client,
  352. container.WithNetworkMode(netNameL2),
  353. )
  354. // Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet
  355. result, err := container.Exec(ctx, client, id, []string{"ip", "route"})
  356. assert.NilError(t, err)
  357. assert.Check(t, is.Contains(result.Combined(), "default via 172.28.140.254 dev eth0"))
  358. // Validate ipvlan l2 mode sets the v6 gateway to the user specified default gateway/next-hop
  359. result, err = container.Exec(ctx, client, id, []string{"ip", "-6", "route"})
  360. assert.NilError(t, err)
  361. assert.Check(t, is.Contains(result.Combined(), "default via 2001:db8:abcb::1 dev eth0"))
  362. }
  363. // Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
  364. func testIpvlanL3Addressing(t *testing.T, ctx context.Context, client dclient.APIClient) {
  365. const parentIfName = "di-dummy0"
  366. n.CreateMasterDummy(ctx, t, parentIfName)
  367. defer n.DeleteInterface(ctx, t, parentIfName)
  368. netNameL3 := "dualstackl3"
  369. net.CreateNoError(ctx, t, client, netNameL3,
  370. net.WithIPvlan(parentIfName, "l3"),
  371. net.WithIPv6(),
  372. net.WithIPAM("172.28.160.0/24", "172.28.160.254"),
  373. net.WithIPAM("2001:db8:abcd::/64", "2001:db8:abcd::254"),
  374. )
  375. assert.Check(t, n.IsNetworkAvailable(ctx, client, netNameL3))
  376. id := container.Run(ctx, t, client,
  377. container.WithNetworkMode(netNameL3),
  378. )
  379. // Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
  380. result, err := container.Exec(ctx, client, id, []string{"ip", "route"})
  381. assert.NilError(t, err)
  382. assert.Check(t, is.Contains(result.Combined(), "default dev eth0"))
  383. // Validate ipvlan l3 mode sets the v6 gateway to dev eth0 and disregards any explicit or inferred next-hops
  384. result, err = container.Exec(ctx, client, id, []string{"ip", "-6", "route"})
  385. assert.NilError(t, err)
  386. assert.Check(t, is.Contains(result.Combined(), "default dev eth0"))
  387. }
  388. // TestIPVlanDNS checks whether DNS is forwarded, for combinations of l2/l3 mode,
  389. // with/without a parent interface, and with '--internal'. Note that, there's no
  390. // attempt here to give the ipvlan network external connectivity - when this test
  391. // supplies a parent interface, it's a dummy. External DNS lookups only work
  392. // because the daemon is configured to see a host resolver on a loopback
  393. // interface, so the external DNS lookup happens in the host's namespace. The
  394. // test is checking that an automatically configured dummy interface causes the
  395. // network to behave as if it was '--internal'. Regression test for
  396. // https://github.com/moby/moby/issues/47662
  397. func TestIPVlanDNS(t *testing.T) {
  398. skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
  399. ctx := testutil.StartSpan(baseContext, t)
  400. net.StartDaftDNS(t, "127.0.0.1")
  401. tmpFileName := net.WriteTempResolvConf(t, "127.0.0.1")
  402. d := daemon.New(t, daemon.WithEnvVars("DOCKER_TEST_RESOLV_CONF_PATH="+tmpFileName))
  403. d.StartWithBusybox(ctx, t)
  404. t.Cleanup(func() { d.Stop(t) })
  405. c := d.NewClientT(t)
  406. const parentIfName = "di-dummy0"
  407. n.CreateMasterDummy(ctx, t, parentIfName)
  408. defer n.DeleteInterface(ctx, t, parentIfName)
  409. const netName = "ipvlan-dns-net"
  410. testcases := []struct {
  411. name string
  412. parent string
  413. internal bool
  414. expDNS bool
  415. }{
  416. {
  417. name: "with parent",
  418. parent: parentIfName,
  419. // External DNS should be used (even though the network has no external connectivity).
  420. expDNS: true,
  421. },
  422. {
  423. name: "no parent",
  424. // External DNS should not be used, equivalent to '--internal'.
  425. },
  426. {
  427. name: "with parent, internal",
  428. parent: parentIfName,
  429. internal: true,
  430. // External DNS should not be used.
  431. },
  432. }
  433. for _, mode := range []string{"l2", "l3"} {
  434. for _, tc := range testcases {
  435. name := fmt.Sprintf("Mode=%v/HasParent=%v/Internal=%v", mode, tc.parent != "", tc.internal)
  436. t.Run(name, func(t *testing.T) {
  437. ctx := testutil.StartSpan(ctx, t)
  438. createOpts := []func(*types.NetworkCreate){
  439. net.WithIPvlan(tc.parent, mode),
  440. }
  441. if tc.internal {
  442. createOpts = append(createOpts, net.WithInternal())
  443. }
  444. net.CreateNoError(ctx, t, c, netName, createOpts...)
  445. defer c.NetworkRemove(ctx, netName)
  446. ctrId := container.Run(ctx, t, c, container.WithNetworkMode(netName))
  447. defer c.ContainerRemove(ctx, ctrId, containertypes.RemoveOptions{Force: true})
  448. res, err := container.Exec(ctx, c, ctrId, []string{"nslookup", "test.example"})
  449. assert.NilError(t, err)
  450. if tc.expDNS {
  451. assert.Check(t, is.Equal(res.ExitCode, 0))
  452. assert.Check(t, is.Contains(res.Stdout(), net.DNSRespAddr))
  453. } else {
  454. assert.Check(t, is.Equal(res.ExitCode, 1))
  455. assert.Check(t, is.Contains(res.Stdout(), "SERVFAIL"))
  456. }
  457. })
  458. }
  459. }
  460. }