macvlan_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. package network
  2. import (
  3. "context"
  4. "fmt"
  5. "testing"
  6. "time"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/api/types/network"
  9. "github.com/docker/docker/client"
  10. "github.com/docker/docker/integration/internal/container"
  11. "github.com/docker/docker/internal/test/daemon"
  12. "github.com/docker/docker/pkg/parsers/kernel"
  13. "github.com/gotestyourself/gotestyourself/assert"
  14. "github.com/gotestyourself/gotestyourself/assert/cmp"
  15. "github.com/gotestyourself/gotestyourself/icmd"
  16. "github.com/gotestyourself/gotestyourself/skip"
  17. )
  18. func TestDockerNetworkMacvlanPersistance(t *testing.T) {
  19. // verify the driver automatically provisions the 802.1q link (dm-dummy0.60)
  20. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  21. skip.If(t, testEnv.IsRemoteDaemon())
  22. skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
  23. d := daemon.New(t)
  24. d.StartWithBusybox(t)
  25. defer d.Stop(t)
  26. master := "dm-dummy0"
  27. createMasterDummy(t, master)
  28. defer deleteInterface(t, master)
  29. client, err := d.NewClient()
  30. assert.NilError(t, err)
  31. _, err = client.NetworkCreate(context.Background(), "dm-persist", types.NetworkCreate{
  32. Driver: "macvlan",
  33. Options: map[string]string{
  34. "parent": "dm-dummy0.60",
  35. },
  36. })
  37. assert.NilError(t, err)
  38. assert.Check(t, isNetworkAvailable(client, "dm-persist"))
  39. d.Restart(t)
  40. assert.Check(t, isNetworkAvailable(client, "dm-persist"))
  41. }
  42. func TestDockerNetworkMacvlanOverlapParent(t *testing.T) {
  43. // verify the same parent interface cannot be used if already in use by an existing network
  44. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  45. skip.If(t, testEnv.IsRemoteDaemon())
  46. skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
  47. d := daemon.New(t)
  48. d.StartWithBusybox(t)
  49. defer d.Stop(t)
  50. master := "dm-dummy0"
  51. createMasterDummy(t, master)
  52. defer deleteInterface(t, master)
  53. client, err := d.NewClient()
  54. assert.NilError(t, err)
  55. _, err = client.NetworkCreate(context.Background(), "dm-subinterface", types.NetworkCreate{
  56. Driver: "macvlan",
  57. Options: map[string]string{
  58. "parent": "dm-dummy0.40",
  59. },
  60. })
  61. assert.NilError(t, err)
  62. assert.Check(t, isNetworkAvailable(client, "dm-subinterface"))
  63. _, err = client.NetworkCreate(context.Background(), "dm-parent-net-overlap", types.NetworkCreate{
  64. Driver: "macvlan",
  65. Options: map[string]string{
  66. "parent": "dm-dummy0.40",
  67. },
  68. })
  69. assert.Check(t, err != nil)
  70. // delete the network while preserving the parent link
  71. err = client.NetworkRemove(context.Background(), "dm-subinterface")
  72. assert.NilError(t, err)
  73. assert.Check(t, isNetworkNotAvailable(client, "dm-subinterface"))
  74. // verify the network delete did not delete the predefined link
  75. linkExists(t, "dm-dummy0")
  76. }
  77. func TestDockerNetworkMacvlanSubinterface(t *testing.T) {
  78. // verify the same parent interface cannot be used if already in use by an existing network
  79. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  80. skip.If(t, testEnv.IsRemoteDaemon())
  81. skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
  82. d := daemon.New(t)
  83. d.StartWithBusybox(t)
  84. defer d.Stop(t)
  85. master := "dm-dummy0"
  86. createMasterDummy(t, master)
  87. defer deleteInterface(t, master)
  88. createVlanInterface(t, master, "dm-dummy0.20", "20")
  89. client, err := d.NewClient()
  90. assert.NilError(t, err)
  91. _, err = client.NetworkCreate(context.Background(), "dm-subinterface", types.NetworkCreate{
  92. Driver: "macvlan",
  93. Options: map[string]string{
  94. "parent": "dm-dummy0.20",
  95. },
  96. })
  97. assert.NilError(t, err)
  98. assert.Check(t, isNetworkAvailable(client, "dm-subinterface"))
  99. // delete the network while preserving the parent link
  100. err = client.NetworkRemove(context.Background(), "dm-subinterface")
  101. assert.NilError(t, err)
  102. assert.Check(t, isNetworkNotAvailable(client, "dm-subinterface"))
  103. // verify the network delete did not delete the predefined link
  104. linkExists(t, "dm-dummy0.20")
  105. }
  106. func TestDockerNetworkMacvlanBridgeNilParent(t *testing.T) {
  107. // macvlan bridge mode - dummy parent interface is provisioned dynamically
  108. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  109. skip.If(t, testEnv.IsRemoteDaemon())
  110. skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
  111. d := daemon.New(t)
  112. d.StartWithBusybox(t)
  113. defer d.Stop(t)
  114. client, err := d.NewClient()
  115. assert.NilError(t, err)
  116. _, err = client.NetworkCreate(context.Background(), "dm-nil-parent", types.NetworkCreate{
  117. Driver: "macvlan",
  118. })
  119. assert.NilError(t, err)
  120. assert.Check(t, isNetworkAvailable(client, "dm-nil-parent"))
  121. ctx := context.Background()
  122. container.Run(t, ctx, client, container.WithNetworkMode("dm-nil-parent"), container.WithName(t.Name()+"first"))
  123. id2 := container.Run(t, ctx, client, container.WithNetworkMode("dm-nil-parent"), container.WithName(t.Name()+"second"))
  124. _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", t.Name() + "first"})
  125. assert.Check(t, err == nil)
  126. }
  127. func TestDockerNetworkMacvlanBridgeInternal(t *testing.T) {
  128. // macvlan bridge mode - dummy parent interface is provisioned dynamically
  129. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  130. skip.If(t, testEnv.IsRemoteDaemon())
  131. skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
  132. d := daemon.New(t)
  133. d.StartWithBusybox(t)
  134. defer d.Stop(t)
  135. client, err := d.NewClient()
  136. assert.NilError(t, err)
  137. _, err = client.NetworkCreate(context.Background(), "dm-internal", types.NetworkCreate{
  138. Driver: "macvlan",
  139. Internal: true,
  140. })
  141. assert.NilError(t, err)
  142. assert.Check(t, isNetworkAvailable(client, "dm-internal"))
  143. ctx := context.Background()
  144. id1 := container.Run(t, ctx, client, container.WithNetworkMode("dm-internal"), container.WithName(t.Name()+"first"))
  145. id2 := container.Run(t, ctx, client, container.WithNetworkMode("dm-internal"), container.WithName(t.Name()+"second"))
  146. timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
  147. defer cancel()
  148. _, err = container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
  149. // FIXME(vdemeester) check the time of error ?
  150. assert.Check(t, err != nil)
  151. assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
  152. _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", t.Name() + "first"})
  153. assert.Check(t, err == nil)
  154. }
  155. func TestDockerNetworkMacvlanMultiSubnet(t *testing.T) {
  156. skip.If(t, testEnv.DaemonInfo.OSType != "linux")
  157. skip.If(t, testEnv.IsRemoteDaemon())
  158. skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
  159. t.Skip("Temporarily skipping while investigating sporadic v6 CI issues")
  160. d := daemon.New(t)
  161. d.StartWithBusybox(t)
  162. defer d.Stop(t)
  163. client, err := d.NewClient()
  164. assert.NilError(t, err)
  165. _, err = client.NetworkCreate(context.Background(), "dualstackbridge", types.NetworkCreate{
  166. Driver: "macvlan",
  167. EnableIPv6: true,
  168. IPAM: &network.IPAM{
  169. Config: []network.IPAMConfig{
  170. {
  171. Subnet: "172.28.100.0/24",
  172. AuxAddress: map[string]string{},
  173. },
  174. {
  175. Subnet: "172.28.102.0/24",
  176. Gateway: "172.28.102.54",
  177. AuxAddress: map[string]string{},
  178. },
  179. {
  180. Subnet: "2001:db8:abc2::/64",
  181. AuxAddress: map[string]string{},
  182. },
  183. {
  184. Subnet: "2001:db8:abc4::/64",
  185. Gateway: "2001:db8:abc4::254",
  186. AuxAddress: map[string]string{},
  187. },
  188. },
  189. },
  190. })
  191. assert.NilError(t, err)
  192. assert.Check(t, isNetworkAvailable(client, "dualstackbridge"))
  193. // 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
  194. ctx := context.Background()
  195. id1 := container.Run(t, ctx, client,
  196. container.WithNetworkMode("dualstackbridge"),
  197. container.WithName(t.Name()+"first"),
  198. container.WithIPv4("dualstackbridge", "172.28.100.20"),
  199. container.WithIPv6("dualstackbridge", "2001:db8:abc2::20"),
  200. )
  201. id2 := container.Run(t, ctx, client,
  202. container.WithNetworkMode("dualstackbridge"),
  203. container.WithName(t.Name()+"second"),
  204. container.WithIPv4("dualstackbridge", "172.28.100.21"),
  205. container.WithIPv6("dualstackbridge", "2001:db8:abc2::21"),
  206. )
  207. c1, err := client.ContainerInspect(ctx, id1)
  208. assert.NilError(t, err)
  209. // verify ipv4 connectivity to the explicit --ipv address second to first
  210. _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].IPAddress})
  211. assert.NilError(t, err)
  212. // verify ipv6 connectivity to the explicit --ipv6 address second to first
  213. _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address})
  214. assert.NilError(t, err)
  215. // 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
  216. id3 := container.Run(t, ctx, client,
  217. container.WithNetworkMode("dualstackbridge"),
  218. container.WithName(t.Name()+"third"),
  219. container.WithIPv4("dualstackbridge", "172.28.102.20"),
  220. container.WithIPv6("dualstackbridge", "2001:db8:abc4::20"),
  221. )
  222. id4 := container.Run(t, ctx, client,
  223. container.WithNetworkMode("dualstackbridge"),
  224. container.WithName(t.Name()+"fourth"),
  225. container.WithIPv4("dualstackbridge", "172.28.102.21"),
  226. container.WithIPv6("dualstackbridge", "2001:db8:abc4::21"),
  227. )
  228. c3, err := client.ContainerInspect(ctx, id3)
  229. assert.NilError(t, err)
  230. // verify ipv4 connectivity to the explicit --ipv address from third to fourth
  231. _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].IPAddress})
  232. assert.NilError(t, err)
  233. // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
  234. _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address})
  235. assert.NilError(t, err)
  236. // Inspect the v4 gateway to ensure the proper default GW was assigned
  237. assert.Equal(t, c1.NetworkSettings.Networks["dualstackbridge"].Gateway, "172.28.100.1")
  238. // Inspect the v6 gateway to ensure the proper default GW was assigned
  239. assert.Equal(t, c1.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8:abc2::1")
  240. // Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
  241. assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].Gateway, "172.28.102.254")
  242. // Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
  243. assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8.abc4::254")
  244. }
  245. func isNetworkAvailable(c client.NetworkAPIClient, name string) cmp.Comparison {
  246. return func() cmp.Result {
  247. networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
  248. if err != nil {
  249. return cmp.ResultFromError(err)
  250. }
  251. for _, network := range networks {
  252. if network.Name == name {
  253. return cmp.ResultSuccess
  254. }
  255. }
  256. return cmp.ResultFailure(fmt.Sprintf("could not find network %s", name))
  257. }
  258. }
  259. func isNetworkNotAvailable(c client.NetworkAPIClient, name string) cmp.Comparison {
  260. return func() cmp.Result {
  261. networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
  262. if err != nil {
  263. return cmp.ResultFromError(err)
  264. }
  265. for _, network := range networks {
  266. if network.Name == name {
  267. return cmp.ResultFailure(fmt.Sprintf("network %s is still present", name))
  268. }
  269. }
  270. return cmp.ResultSuccess
  271. }
  272. }
  273. func createMasterDummy(t *testing.T, master string) {
  274. // ip link add <dummy_name> type dummy
  275. icmd.RunCommand("ip", "link", "add", master, "type", "dummy").Assert(t, icmd.Success)
  276. icmd.RunCommand("ip", "link", "set", master, "up").Assert(t, icmd.Success)
  277. }
  278. func createVlanInterface(t *testing.T, master, slave, id string) {
  279. // ip link add link <master> name <master>.<VID> type vlan id <VID>
  280. icmd.RunCommand("ip", "link", "add", "link", master, "name", slave, "type", "vlan", "id", id).Assert(t, icmd.Success)
  281. // ip link set <sub_interface_name> up
  282. icmd.RunCommand("ip", "link", "set", slave, "up").Assert(t, icmd.Success)
  283. }
  284. func deleteInterface(t *testing.T, ifName string) {
  285. icmd.RunCommand("ip", "link", "delete", ifName).Assert(t, icmd.Success)
  286. icmd.RunCommand("iptables", "-t", "nat", "--flush").Assert(t, icmd.Success)
  287. icmd.RunCommand("iptables", "--flush").Assert(t, icmd.Success)
  288. }
  289. func linkExists(t *testing.T, master string) {
  290. // verify the specified link exists, ip link show <link_name>
  291. icmd.RunCommand("ip", "link", "show", master).Assert(t, icmd.Success)
  292. }
  293. // ensure Kernel version is >= v3.9 for macvlan support
  294. func macvlanKernelSupport() bool {
  295. return checkKernelMajorVersionGreaterOrEqualThen(3, 9)
  296. }
  297. func checkKernelMajorVersionGreaterOrEqualThen(kernelVersion int, majorVersion int) bool {
  298. kv, err := kernel.GetKernelVersion()
  299. if err != nil {
  300. return false
  301. }
  302. if kv.Kernel < kernelVersion || (kv.Kernel == kernelVersion && kv.Major < majorVersion) {
  303. return false
  304. }
  305. return true
  306. }