macvlan_test.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. // +build !windows
  2. package macvlan
  3. import (
  4. "context"
  5. "strings"
  6. "testing"
  7. "time"
  8. "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 TestDockerNetworkMacvlanPersistance(t *testing.T) {
  17. // verify the driver automatically provisions the 802.1q link (dm-dummy0.60)
  18. skip.If(t, testEnv.IsRemoteDaemon)
  19. skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
  20. d := daemon.New(t)
  21. d.StartWithBusybox(t)
  22. defer d.Stop(t)
  23. master := "dm-dummy0"
  24. n.CreateMasterDummy(t, master)
  25. defer n.DeleteInterface(t, master)
  26. c := d.NewClientT(t)
  27. netName := "dm-persist"
  28. net.CreateNoError(t, context.Background(), c, netName,
  29. net.WithMacvlan("dm-dummy0.60"),
  30. )
  31. assert.Check(t, n.IsNetworkAvailable(c, netName))
  32. d.Restart(t)
  33. assert.Check(t, n.IsNetworkAvailable(c, netName))
  34. }
  35. func TestDockerNetworkMacvlan(t *testing.T) {
  36. skip.If(t, testEnv.IsRemoteDaemon)
  37. skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
  38. for _, tc := range []struct {
  39. name string
  40. test func(client.APIClient) func(*testing.T)
  41. }{
  42. {
  43. name: "Subinterface",
  44. test: testMacvlanSubinterface,
  45. }, {
  46. name: "OverlapParent",
  47. test: testMacvlanOverlapParent,
  48. }, {
  49. name: "NilParent",
  50. test: testMacvlanNilParent,
  51. }, {
  52. name: "InternalMode",
  53. test: testMacvlanInternalMode,
  54. }, {
  55. name: "Addressing",
  56. test: testMacvlanAddressing,
  57. },
  58. } {
  59. d := daemon.New(t)
  60. d.StartWithBusybox(t)
  61. c := d.NewClientT(t)
  62. t.Run(tc.name, tc.test(c))
  63. d.Stop(t)
  64. // FIXME(vdemeester) clean network
  65. }
  66. }
  67. func testMacvlanOverlapParent(client client.APIClient) func(*testing.T) {
  68. return func(t *testing.T) {
  69. // verify the same parent interface cannot be used if already in use by an existing network
  70. master := "dm-dummy0"
  71. n.CreateMasterDummy(t, master)
  72. defer n.DeleteInterface(t, master)
  73. netName := "dm-subinterface"
  74. parentName := "dm-dummy0.40"
  75. net.CreateNoError(t, context.Background(), client, netName,
  76. net.WithMacvlan(parentName),
  77. )
  78. assert.Check(t, n.IsNetworkAvailable(client, netName))
  79. _, err := net.Create(context.Background(), client, "dm-parent-net-overlap",
  80. net.WithMacvlan(parentName),
  81. )
  82. assert.Check(t, err != nil)
  83. // delete the network while preserving the parent link
  84. err = client.NetworkRemove(context.Background(), netName)
  85. assert.NilError(t, err)
  86. assert.Check(t, n.IsNetworkNotAvailable(client, netName))
  87. // verify the network delete did not delete the predefined link
  88. n.LinkExists(t, master)
  89. }
  90. }
  91. func testMacvlanSubinterface(client client.APIClient) func(*testing.T) {
  92. return func(t *testing.T) {
  93. // verify the same parent interface cannot be used if already in use by an existing network
  94. master := "dm-dummy0"
  95. parentName := "dm-dummy0.20"
  96. n.CreateMasterDummy(t, master)
  97. defer n.DeleteInterface(t, master)
  98. n.CreateVlanInterface(t, master, parentName, "20")
  99. netName := "dm-subinterface"
  100. net.CreateNoError(t, context.Background(), client, netName,
  101. net.WithMacvlan(parentName),
  102. )
  103. assert.Check(t, n.IsNetworkAvailable(client, netName))
  104. // delete the network while preserving the parent link
  105. err := client.NetworkRemove(context.Background(), netName)
  106. assert.NilError(t, err)
  107. assert.Check(t, n.IsNetworkNotAvailable(client, netName))
  108. // verify the network delete did not delete the predefined link
  109. n.LinkExists(t, parentName)
  110. }
  111. }
  112. func testMacvlanNilParent(client client.APIClient) func(*testing.T) {
  113. return func(t *testing.T) {
  114. // macvlan bridge mode - dummy parent interface is provisioned dynamically
  115. netName := "dm-nil-parent"
  116. net.CreateNoError(t, context.Background(), client, netName,
  117. net.WithMacvlan(""),
  118. )
  119. assert.Check(t, n.IsNetworkAvailable(client, netName))
  120. ctx := context.Background()
  121. id1 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
  122. id2 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
  123. _, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
  124. assert.Check(t, err == nil)
  125. }
  126. }
  127. func testMacvlanInternalMode(client client.APIClient) func(*testing.T) {
  128. return func(t *testing.T) {
  129. // macvlan bridge mode - dummy parent interface is provisioned dynamically
  130. netName := "dm-internal"
  131. net.CreateNoError(t, context.Background(), client, netName,
  132. net.WithMacvlan(""),
  133. net.WithInternal(),
  134. )
  135. assert.Check(t, n.IsNetworkAvailable(client, netName))
  136. ctx := context.Background()
  137. id1 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
  138. id2 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
  139. timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
  140. defer cancel()
  141. _, err := container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
  142. // FIXME(vdemeester) check the time of error ?
  143. assert.Check(t, err != nil)
  144. assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
  145. _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
  146. assert.Check(t, err == nil)
  147. }
  148. }
  149. func testMacvlanMultiSubnet(client client.APIClient) func(*testing.T) {
  150. return func(t *testing.T) {
  151. netName := "dualstackbridge"
  152. net.CreateNoError(t, context.Background(), client, netName,
  153. net.WithMacvlan(""),
  154. net.WithIPv6(),
  155. net.WithIPAM("172.28.100.0/24", ""),
  156. net.WithIPAM("172.28.102.0/24", "172.28.102.254"),
  157. net.WithIPAM("2001:db8:abc2::/64", ""),
  158. net.WithIPAM("2001:db8:abc4::/64", "2001:db8:abc4::254"),
  159. )
  160. assert.Check(t, n.IsNetworkAvailable(client, netName))
  161. // 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
  162. ctx := context.Background()
  163. id1 := container.Run(t, ctx, client,
  164. container.WithNetworkMode("dualstackbridge"),
  165. container.WithIPv4("dualstackbridge", "172.28.100.20"),
  166. container.WithIPv6("dualstackbridge", "2001:db8:abc2::20"),
  167. )
  168. id2 := container.Run(t, ctx, client,
  169. container.WithNetworkMode("dualstackbridge"),
  170. container.WithIPv4("dualstackbridge", "172.28.100.21"),
  171. container.WithIPv6("dualstackbridge", "2001:db8:abc2::21"),
  172. )
  173. c1, err := client.ContainerInspect(ctx, id1)
  174. assert.NilError(t, err)
  175. // verify ipv4 connectivity to the explicit --ipv address second to first
  176. _, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].IPAddress})
  177. assert.NilError(t, err)
  178. // verify ipv6 connectivity to the explicit --ipv6 address second to first
  179. _, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address})
  180. assert.NilError(t, err)
  181. // 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
  182. id3 := container.Run(t, ctx, client,
  183. container.WithNetworkMode("dualstackbridge"),
  184. container.WithIPv4("dualstackbridge", "172.28.102.20"),
  185. container.WithIPv6("dualstackbridge", "2001:db8:abc4::20"),
  186. )
  187. id4 := container.Run(t, ctx, client,
  188. container.WithNetworkMode("dualstackbridge"),
  189. container.WithIPv4("dualstackbridge", "172.28.102.21"),
  190. container.WithIPv6("dualstackbridge", "2001:db8:abc4::21"),
  191. )
  192. c3, err := client.ContainerInspect(ctx, id3)
  193. assert.NilError(t, err)
  194. // verify ipv4 connectivity to the explicit --ipv address from third to fourth
  195. _, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].IPAddress})
  196. assert.NilError(t, err)
  197. // verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
  198. _, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address})
  199. assert.NilError(t, err)
  200. // Inspect the v4 gateway to ensure the proper default GW was assigned
  201. assert.Equal(t, c1.NetworkSettings.Networks["dualstackbridge"].Gateway, "172.28.100.1")
  202. // Inspect the v6 gateway to ensure the proper default GW was assigned
  203. assert.Equal(t, c1.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8:abc2::1")
  204. // Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
  205. assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].Gateway, "172.28.102.254")
  206. // Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
  207. assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8.abc4::254")
  208. }
  209. }
  210. func testMacvlanAddressing(client client.APIClient) func(*testing.T) {
  211. return func(t *testing.T) {
  212. // Ensure the default gateways, next-hops and default dev devices are properly set
  213. netName := "dualstackbridge"
  214. net.CreateNoError(t, context.Background(), client, netName,
  215. net.WithMacvlan(""),
  216. net.WithIPv6(),
  217. net.WithOption("macvlan_mode", "bridge"),
  218. net.WithIPAM("172.28.130.0/24", ""),
  219. net.WithIPAM("2001:db8:abca::/64", "2001:db8:abca::254"),
  220. )
  221. assert.Check(t, n.IsNetworkAvailable(client, netName))
  222. ctx := context.Background()
  223. id1 := container.Run(t, ctx, client,
  224. container.WithNetworkMode("dualstackbridge"),
  225. )
  226. // Validate macvlan bridge mode defaults gateway sets the default IPAM next-hop inferred from the subnet
  227. result, err := container.Exec(ctx, client, id1, []string{"ip", "route"})
  228. assert.NilError(t, err)
  229. assert.Check(t, strings.Contains(result.Combined(), "default via 172.28.130.1 dev eth0"))
  230. // Validate macvlan bridge mode sets the v6 gateway to the user specified default gateway/next-hop
  231. result, err = container.Exec(ctx, client, id1, []string{"ip", "-6", "route"})
  232. assert.NilError(t, err)
  233. assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abca::254 dev eth0"))
  234. }
  235. }
  236. // ensure Kernel version is >= v3.9 for macvlan support
  237. func macvlanKernelSupport() bool {
  238. return n.CheckKernelMajorVersionGreaterOrEqualThen(3, 9)
  239. }