ipvlan_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. // +build !windows
  2. package ipvlan
  3. import (
  4. "context"
  5. "strings"
  6. "testing"
  7. "time"
  8. dclient "github.com/docker/docker/client"
  9. "github.com/docker/docker/integration/internal/container"
  10. net "github.com/docker/docker/integration/internal/network"
  11. n "github.com/docker/docker/integration/network"
  12. "github.com/docker/docker/internal/test/daemon"
  13. "gotest.tools/assert"
  14. "gotest.tools/skip"
  15. )
  16. func TestDockerNetworkIpvlanPersistance(t *testing.T) {
  17. // verify the driver automatically provisions the 802.1q link (di-dummy0.70)
  18. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  19. skip.If(t, testEnv.IsRemoteDaemon)
  20. skip.If(t, !ipvlanKernelSupport(), "Kernel doesn't support ipvlan")
  21. d := daemon.New(t, daemon.WithExperimental)
  22. d.StartWithBusybox(t)
  23. defer d.Stop(t)
  24. // master dummy interface 'di' notation represent 'docker ipvlan'
  25. master := "di-dummy0"
  26. n.CreateMasterDummy(t, master)
  27. defer n.DeleteInterface(t, master)
  28. c := d.NewClientT(t)
  29. // create a network specifying the desired sub-interface name
  30. netName := "di-persist"
  31. net.CreateNoError(t, context.Background(), c, netName,
  32. net.WithIPvlan("di-dummy0.70", ""),
  33. )
  34. assert.Check(t, n.IsNetworkAvailable(c, netName))
  35. // Restart docker daemon to test the config has persisted to disk
  36. d.Restart(t)
  37. assert.Check(t, n.IsNetworkAvailable(c, netName))
  38. }
  39. func TestDockerNetworkIpvlan(t *testing.T) {
  40. skip.If(t, testEnv.DaemonInfo.OSType == "windows")
  41. skip.If(t, testEnv.IsRemoteDaemon)
  42. skip.If(t, !ipvlanKernelSupport(), "Kernel doesn't support ipvlan")
  43. for _, tc := range []struct {
  44. name string
  45. test func(dclient.APIClient) func(*testing.T)
  46. }{
  47. {
  48. name: "Subinterface",
  49. test: testIpvlanSubinterface,
  50. }, {
  51. name: "OverlapParent",
  52. test: testIpvlanOverlapParent,
  53. }, {
  54. name: "L2NilParent",
  55. test: testIpvlanL2NilParent,
  56. }, {
  57. name: "L2InternalMode",
  58. test: testIpvlanL2InternalMode,
  59. }, {
  60. name: "L3NilParent",
  61. test: testIpvlanL3NilParent,
  62. }, {
  63. name: "L3InternalMode",
  64. test: testIpvlanL3InternalMode,
  65. }, {
  66. name: "L2MultiSubnet",
  67. test: testIpvlanL2MultiSubnet,
  68. }, {
  69. name: "L3MultiSubnet",
  70. test: testIpvlanL3MultiSubnet,
  71. }, {
  72. name: "Addressing",
  73. test: testIpvlanAddressing,
  74. },
  75. } {
  76. d := daemon.New(t, daemon.WithExperimental)
  77. d.StartWithBusybox(t)
  78. c := d.NewClientT(t)
  79. t.Run(tc.name, tc.test(c))
  80. d.Stop(t)
  81. // FIXME(vdemeester) clean network
  82. }
  83. }
  84. func testIpvlanSubinterface(client dclient.APIClient) func(*testing.T) {
  85. return func(t *testing.T) {
  86. master := "di-dummy0"
  87. n.CreateMasterDummy(t, master)
  88. defer n.DeleteInterface(t, master)
  89. netName := "di-subinterface"
  90. net.CreateNoError(t, context.Background(), client, netName,
  91. net.WithIPvlan("di-dummy0.60", ""),
  92. )
  93. assert.Check(t, n.IsNetworkAvailable(client, netName))
  94. // delete the network while preserving the parent link
  95. err := client.NetworkRemove(context.Background(), netName)
  96. assert.NilError(t, err)
  97. assert.Check(t, n.IsNetworkNotAvailable(client, netName))
  98. // verify the network delete did not delete the predefined link
  99. n.LinkExists(t, "di-dummy0")
  100. }
  101. }
  102. func testIpvlanOverlapParent(client dclient.APIClient) func(*testing.T) {
  103. return func(t *testing.T) {
  104. // verify the same parent interface cannot be used if already in use by an existing network
  105. master := "di-dummy0"
  106. parent := master + ".30"
  107. n.CreateMasterDummy(t, master)
  108. defer n.DeleteInterface(t, master)
  109. n.CreateVlanInterface(t, master, parent, "30")
  110. netName := "di-subinterface"
  111. net.CreateNoError(t, context.Background(), client, netName,
  112. net.WithIPvlan(parent, ""),
  113. )
  114. assert.Check(t, n.IsNetworkAvailable(client, netName))
  115. _, err := net.Create(context.Background(), client, netName,
  116. net.WithIPvlan(parent, ""),
  117. )
  118. // verify that the overlap returns an error
  119. assert.Check(t, err != nil)
  120. }
  121. }
  122. func testIpvlanL2NilParent(client dclient.APIClient) func(*testing.T) {
  123. return func(t *testing.T) {
  124. // ipvlan l2 mode - dummy parent interface is provisioned dynamically
  125. netName := "di-nil-parent"
  126. net.CreateNoError(t, context.Background(), client, netName,
  127. net.WithIPvlan("", ""),
  128. )
  129. assert.Check(t, n.IsNetworkAvailable(client, netName))
  130. ctx := context.Background()
  131. id1 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
  132. id2 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
  133. _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
  134. assert.NilError(t, err)
  135. }
  136. }
  137. func testIpvlanL2InternalMode(client dclient.APIClient) func(*testing.T) {
  138. return func(t *testing.T) {
  139. netName := "di-internal"
  140. net.CreateNoError(t, context.Background(), client, netName,
  141. net.WithIPvlan("", ""),
  142. net.WithInternal(),
  143. )
  144. assert.Check(t, n.IsNetworkAvailable(client, netName))
  145. ctx := context.Background()
  146. id1 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
  147. id2 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
  148. timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
  149. defer cancel()
  150. _, err := container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
  151. // FIXME(vdemeester) check the time of error ?
  152. assert.Check(t, err != nil)
  153. assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
  154. _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
  155. assert.NilError(t, err)
  156. }
  157. }
  158. func testIpvlanL3NilParent(client dclient.APIClient) func(*testing.T) {
  159. return func(t *testing.T) {
  160. netName := "di-nil-parent-l3"
  161. net.CreateNoError(t, context.Background(), client, netName,
  162. net.WithIPvlan("", "l3"),
  163. net.WithIPAM("172.28.230.0/24", ""),
  164. net.WithIPAM("172.28.220.0/24", ""),
  165. )
  166. assert.Check(t, n.IsNetworkAvailable(client, netName))
  167. ctx := context.Background()
  168. id1 := container.Run(t, ctx, client,
  169. container.WithNetworkMode(netName),
  170. container.WithIPv4(netName, "172.28.220.10"),
  171. )
  172. id2 := container.Run(t, ctx, client,
  173. container.WithNetworkMode(netName),
  174. container.WithIPv4(netName, "172.28.230.10"),
  175. )
  176. _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
  177. assert.NilError(t, err)
  178. }
  179. }
  180. func testIpvlanL3InternalMode(client dclient.APIClient) func(*testing.T) {
  181. return func(t *testing.T) {
  182. netName := "di-internal-l3"
  183. net.CreateNoError(t, context.Background(), client, netName,
  184. net.WithIPvlan("", "l3"),
  185. net.WithInternal(),
  186. net.WithIPAM("172.28.230.0/24", ""),
  187. net.WithIPAM("172.28.220.0/24", ""),
  188. )
  189. assert.Check(t, n.IsNetworkAvailable(client, netName))
  190. ctx := context.Background()
  191. id1 := container.Run(t, ctx, client,
  192. container.WithNetworkMode(netName),
  193. container.WithIPv4(netName, "172.28.220.10"),
  194. )
  195. id2 := container.Run(t, ctx, client,
  196. container.WithNetworkMode(netName),
  197. container.WithIPv4(netName, "172.28.230.10"),
  198. )
  199. timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
  200. defer cancel()
  201. _, err := container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
  202. // FIXME(vdemeester) check the time of error ?
  203. assert.Check(t, err != nil)
  204. assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
  205. _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
  206. assert.NilError(t, err)
  207. }
  208. }
  209. func testIpvlanL2MultiSubnet(client dclient.APIClient) func(*testing.T) {
  210. return func(t *testing.T) {
  211. netName := "dualstackl2"
  212. net.CreateNoError(t, context.Background(), client, netName,
  213. net.WithIPvlan("", ""),
  214. net.WithIPv6(),
  215. net.WithIPAM("172.28.200.0/24", ""),
  216. net.WithIPAM("172.28.202.0/24", "172.28.202.254"),
  217. net.WithIPAM("2001:db8:abc8::/64", ""),
  218. net.WithIPAM("2001:db8:abc6::/64", "2001:db8:abc6::254"),
  219. )
  220. assert.Check(t, n.IsNetworkAvailable(client, netName))
  221. // 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
  222. ctx := context.Background()
  223. id1 := container.Run(t, ctx, client,
  224. container.WithNetworkMode(netName),
  225. container.WithIPv4(netName, "172.28.200.20"),
  226. container.WithIPv6(netName, "2001:db8:abc8::20"),
  227. )
  228. id2 := container.Run(t, ctx, client,
  229. container.WithNetworkMode(netName),
  230. container.WithIPv4(netName, "172.28.200.21"),
  231. container.WithIPv6(netName, "2001:db8:abc8::21"),
  232. )
  233. c1, err := client.ContainerInspect(ctx, id1)
  234. assert.NilError(t, err)
  235. // verify ipv4 connectivity to the explicit --ipv address second to first
  236. _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress})
  237. assert.NilError(t, err)
  238. // verify ipv6 connectivity to the explicit --ipv6 address second to first
  239. _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address})
  240. assert.NilError(t, err)
  241. // 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
  242. id3 := container.Run(t, ctx, client,
  243. container.WithNetworkMode(netName),
  244. container.WithIPv4(netName, "172.28.202.20"),
  245. container.WithIPv6(netName, "2001:db8:abc6::20"),
  246. )
  247. id4 := container.Run(t, ctx, client,
  248. container.WithNetworkMode(netName),
  249. container.WithIPv4(netName, "172.28.202.21"),
  250. container.WithIPv6(netName, "2001:db8:abc6::21"),
  251. )
  252. c3, err := client.ContainerInspect(ctx, id3)
  253. assert.NilError(t, err)
  254. // verify ipv4 connectivity to the explicit --ipv address from third to fourth
  255. _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress})
  256. assert.NilError(t, err)
  257. // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
  258. _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address})
  259. assert.NilError(t, err)
  260. // Inspect the v4 gateway to ensure the proper default GW was assigned
  261. assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "172.28.200.1")
  262. // Inspect the v6 gateway to ensure the proper default GW was assigned
  263. assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc8::1")
  264. // Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
  265. assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "172.28.202.254")
  266. // Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
  267. assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "2001:db8:abc6::254")
  268. }
  269. }
  270. func testIpvlanL3MultiSubnet(client dclient.APIClient) func(*testing.T) {
  271. return func(t *testing.T) {
  272. netName := "dualstackl3"
  273. net.CreateNoError(t, context.Background(), client, netName,
  274. net.WithIPvlan("", "l3"),
  275. net.WithIPv6(),
  276. net.WithIPAM("172.28.10.0/24", ""),
  277. net.WithIPAM("172.28.12.0/24", "172.28.12.254"),
  278. net.WithIPAM("2001:db8:abc9::/64", ""),
  279. net.WithIPAM("2001:db8:abc7::/64", "2001:db8:abc7::254"),
  280. )
  281. assert.Check(t, n.IsNetworkAvailable(client, netName))
  282. // 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
  283. ctx := context.Background()
  284. id1 := container.Run(t, ctx, client,
  285. container.WithNetworkMode(netName),
  286. container.WithIPv4(netName, "172.28.10.20"),
  287. container.WithIPv6(netName, "2001:db8:abc9::20"),
  288. )
  289. id2 := container.Run(t, ctx, client,
  290. container.WithNetworkMode(netName),
  291. container.WithIPv4(netName, "172.28.10.21"),
  292. container.WithIPv6(netName, "2001:db8:abc9::21"),
  293. )
  294. c1, err := client.ContainerInspect(ctx, id1)
  295. assert.NilError(t, err)
  296. // verify ipv4 connectivity to the explicit --ipv address second to first
  297. _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks[netName].IPAddress})
  298. assert.NilError(t, err)
  299. // verify ipv6 connectivity to the explicit --ipv6 address second to first
  300. _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks[netName].GlobalIPv6Address})
  301. assert.NilError(t, err)
  302. // 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
  303. id3 := container.Run(t, ctx, client,
  304. container.WithNetworkMode(netName),
  305. container.WithIPv4(netName, "172.28.12.20"),
  306. container.WithIPv6(netName, "2001:db8:abc7::20"),
  307. )
  308. id4 := container.Run(t, ctx, client,
  309. container.WithNetworkMode(netName),
  310. container.WithIPv4(netName, "172.28.12.21"),
  311. container.WithIPv6(netName, "2001:db8:abc7::21"),
  312. )
  313. c3, err := client.ContainerInspect(ctx, id3)
  314. assert.NilError(t, err)
  315. // verify ipv4 connectivity to the explicit --ipv address from third to fourth
  316. _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks[netName].IPAddress})
  317. assert.NilError(t, err)
  318. // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
  319. _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks[netName].GlobalIPv6Address})
  320. assert.NilError(t, err)
  321. // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
  322. assert.Equal(t, c1.NetworkSettings.Networks[netName].Gateway, "")
  323. // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
  324. assert.Equal(t, c1.NetworkSettings.Networks[netName].IPv6Gateway, "")
  325. // Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
  326. assert.Equal(t, c3.NetworkSettings.Networks[netName].Gateway, "")
  327. // Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
  328. assert.Equal(t, c3.NetworkSettings.Networks[netName].IPv6Gateway, "")
  329. }
  330. }
  331. func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) {
  332. return func(t *testing.T) {
  333. // Verify ipvlan l2 mode sets the proper default gateway routes via netlink
  334. // for either an explicitly set route by the user or inferred via default IPAM
  335. netNameL2 := "dualstackl2"
  336. net.CreateNoError(t, context.Background(), client, netNameL2,
  337. net.WithIPvlan("", "l2"),
  338. net.WithIPv6(),
  339. net.WithIPAM("172.28.140.0/24", "172.28.140.254"),
  340. net.WithIPAM("2001:db8:abcb::/64", ""),
  341. )
  342. assert.Check(t, n.IsNetworkAvailable(client, netNameL2))
  343. ctx := context.Background()
  344. id1 := container.Run(t, ctx, client,
  345. container.WithNetworkMode(netNameL2),
  346. )
  347. // Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet
  348. result, err := container.Exec(ctx, client, id1, []string{"ip", "route"})
  349. assert.NilError(t, err)
  350. assert.Check(t, strings.Contains(result.Combined(), "default via 172.28.140.254 dev eth0"))
  351. // Validate ipvlan l2 mode sets the v6 gateway to the user specified default gateway/next-hop
  352. result, err = container.Exec(ctx, client, id1, []string{"ip", "-6", "route"})
  353. assert.NilError(t, err)
  354. assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abcb::1 dev eth0"))
  355. // Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
  356. netNameL3 := "dualstackl3"
  357. net.CreateNoError(t, context.Background(), client, netNameL3,
  358. net.WithIPvlan("", "l3"),
  359. net.WithIPv6(),
  360. net.WithIPAM("172.28.160.0/24", "172.28.160.254"),
  361. net.WithIPAM("2001:db8:abcd::/64", "2001:db8:abcd::254"),
  362. )
  363. assert.Check(t, n.IsNetworkAvailable(client, netNameL3))
  364. id2 := container.Run(t, ctx, client,
  365. container.WithNetworkMode(netNameL3),
  366. )
  367. // Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
  368. result, err = container.Exec(ctx, client, id2, []string{"ip", "route"})
  369. assert.NilError(t, err)
  370. assert.Check(t, strings.Contains(result.Combined(), "default dev eth0"))
  371. // Validate ipvlan l3 mode sets the v6 gateway to dev eth0 and disregards any explicit or inferred next-hops
  372. result, err = container.Exec(ctx, client, id2, []string{"ip", "-6", "route"})
  373. assert.NilError(t, err)
  374. assert.Check(t, strings.Contains(result.Combined(), "default dev eth0"))
  375. }
  376. }
  377. // ensure Kernel version is >= v4.2 for ipvlan support
  378. func ipvlanKernelSupport() bool {
  379. return n.CheckKernelMajorVersionGreaterOrEqualThen(4, 2)
  380. }