macvlan_setup.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. //go:build linux
  2. // +build linux
  3. package macvlan
  4. import (
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. "github.com/docker/docker/libnetwork/ns"
  9. "github.com/sirupsen/logrus"
  10. "github.com/vishvananda/netlink"
  11. )
  12. const (
  13. dummyPrefix = "dm-" // macvlan prefix for dummy parent interface
  14. )
  15. // Create the macvlan slave specifying the source name
  16. func createMacVlan(containerIfName, parent, macvlanMode string) (string, error) {
  17. // Set the macvlan mode. Default is bridge mode
  18. mode, err := setMacVlanMode(macvlanMode)
  19. if err != nil {
  20. return "", fmt.Errorf("Unsupported %s macvlan mode: %v", macvlanMode, err)
  21. }
  22. // verify the Docker host interface acting as the macvlan parent iface exists
  23. if !parentExists(parent) {
  24. return "", fmt.Errorf("the requested parent interface %s was not found on the Docker host", parent)
  25. }
  26. // Get the link for the master index (Example: the docker host eth iface)
  27. parentLink, err := ns.NlHandle().LinkByName(parent)
  28. if err != nil {
  29. return "", fmt.Errorf("error occurred looking up the macvlan parent iface %s error: %s", parent, err)
  30. }
  31. // Create a macvlan link
  32. macvlan := &netlink.Macvlan{
  33. LinkAttrs: netlink.LinkAttrs{
  34. Name: containerIfName,
  35. ParentIndex: parentLink.Attrs().Index,
  36. },
  37. Mode: mode,
  38. }
  39. if err := ns.NlHandle().LinkAdd(macvlan); err != nil {
  40. // If a user creates a macvlan and ipvlan on same parent, only one slave iface can be active at a time.
  41. return "", fmt.Errorf("failed to create the macvlan port: %v", err)
  42. }
  43. return macvlan.Attrs().Name, nil
  44. }
  45. // setMacVlanMode setter for one of the four macvlan port types
  46. func setMacVlanMode(mode string) (netlink.MacvlanMode, error) {
  47. switch mode {
  48. case modePrivate:
  49. return netlink.MACVLAN_MODE_PRIVATE, nil
  50. case modeVepa:
  51. return netlink.MACVLAN_MODE_VEPA, nil
  52. case modeBridge:
  53. return netlink.MACVLAN_MODE_BRIDGE, nil
  54. case modePassthru:
  55. return netlink.MACVLAN_MODE_PASSTHRU, nil
  56. default:
  57. return 0, fmt.Errorf("unknown macvlan mode: %s", mode)
  58. }
  59. }
  60. // parentExists checks if the specified interface exists in the default namespace
  61. func parentExists(ifaceStr string) bool {
  62. _, err := ns.NlHandle().LinkByName(ifaceStr)
  63. return err == nil
  64. }
  65. // createVlanLink parses sub-interfaces and vlan id for creation
  66. func createVlanLink(parentName string) error {
  67. if strings.Contains(parentName, ".") {
  68. parent, vidInt, err := parseVlan(parentName)
  69. if err != nil {
  70. return err
  71. }
  72. // VLAN identifier or VID is a 12-bit field specifying the VLAN to which the frame belongs
  73. if vidInt > 4094 || vidInt < 1 {
  74. return fmt.Errorf("vlan id must be between 1-4094, received: %d", vidInt)
  75. }
  76. // get the parent link to attach a vlan subinterface
  77. parentLink, err := ns.NlHandle().LinkByName(parent)
  78. if err != nil {
  79. return fmt.Errorf("failed to find master interface %s on the Docker host: %v", parent, err)
  80. }
  81. vlanLink := &netlink.Vlan{
  82. LinkAttrs: netlink.LinkAttrs{
  83. Name: parentName,
  84. ParentIndex: parentLink.Attrs().Index,
  85. },
  86. VlanId: vidInt,
  87. }
  88. // create the subinterface
  89. if err := ns.NlHandle().LinkAdd(vlanLink); err != nil {
  90. return fmt.Errorf("failed to create %s vlan link: %v", vlanLink.Name, err)
  91. }
  92. // Bring the new netlink iface up
  93. if err := ns.NlHandle().LinkSetUp(vlanLink); err != nil {
  94. return fmt.Errorf("failed to enable %s the macvlan parent link %v", vlanLink.Name, err)
  95. }
  96. logrus.Debugf("Added a vlan tagged netlink subinterface: %s with a vlan id: %d", parentName, vidInt)
  97. return nil
  98. }
  99. return fmt.Errorf("invalid subinterface vlan name %s, example formatting is eth0.10", parentName)
  100. }
  101. // delVlanLink verifies only sub-interfaces with a vlan id get deleted
  102. func delVlanLink(linkName string) error {
  103. if strings.Contains(linkName, ".") {
  104. _, _, err := parseVlan(linkName)
  105. if err != nil {
  106. return err
  107. }
  108. // delete the vlan subinterface
  109. vlanLink, err := ns.NlHandle().LinkByName(linkName)
  110. if err != nil {
  111. return fmt.Errorf("failed to find interface %s on the Docker host : %v", linkName, err)
  112. }
  113. // verify a parent interface isn't being deleted
  114. if vlanLink.Attrs().ParentIndex == 0 {
  115. return fmt.Errorf("interface %s does not appear to be a slave device: %v", linkName, err)
  116. }
  117. // delete the macvlan slave device
  118. if err := ns.NlHandle().LinkDel(vlanLink); err != nil {
  119. return fmt.Errorf("failed to delete %s link: %v", linkName, err)
  120. }
  121. logrus.Debugf("Deleted a vlan tagged netlink subinterface: %s", linkName)
  122. }
  123. // if the subinterface doesn't parse to iface.vlan_id leave the interface in
  124. // place since it could be a user specified name not created by the driver.
  125. return nil
  126. }
  127. // parseVlan parses and verifies a slave interface name: -o parent=eth0.10
  128. func parseVlan(linkName string) (string, int, error) {
  129. // parse -o parent=eth0.10
  130. splitName := strings.Split(linkName, ".")
  131. if len(splitName) != 2 {
  132. return "", 0, fmt.Errorf("required interface name format is: name.vlan_id, ex. eth0.10 for vlan 10, instead received %s", linkName)
  133. }
  134. parent, vidStr := splitName[0], splitName[1]
  135. // validate type and convert vlan id to int
  136. vidInt, err := strconv.Atoi(vidStr)
  137. if err != nil {
  138. return "", 0, fmt.Errorf("unable to parse a valid vlan id from: %s (ex. eth0.10 for vlan 10)", vidStr)
  139. }
  140. // Check if the interface exists
  141. if !parentExists(parent) {
  142. return "", 0, fmt.Errorf("-o parent interface was not found on the host: %s", parent)
  143. }
  144. return parent, vidInt, nil
  145. }
  146. // createDummyLink creates a dummy0 parent link
  147. func createDummyLink(dummyName, truncNetID string) error {
  148. // create a parent interface since one was not specified
  149. parent := &netlink.Dummy{
  150. LinkAttrs: netlink.LinkAttrs{
  151. Name: dummyName,
  152. },
  153. }
  154. if err := ns.NlHandle().LinkAdd(parent); err != nil {
  155. return err
  156. }
  157. parentDummyLink, err := ns.NlHandle().LinkByName(dummyName)
  158. if err != nil {
  159. return fmt.Errorf("error occurred looking up the macvlan parent iface %s error: %s", dummyName, err)
  160. }
  161. // bring the new netlink iface up
  162. if err := ns.NlHandle().LinkSetUp(parentDummyLink); err != nil {
  163. return fmt.Errorf("failed to enable %s the macvlan parent link: %v", dummyName, err)
  164. }
  165. return nil
  166. }
  167. // delDummyLink deletes the link type dummy used when -o parent is not passed
  168. func delDummyLink(linkName string) error {
  169. // delete the vlan subinterface
  170. dummyLink, err := ns.NlHandle().LinkByName(linkName)
  171. if err != nil {
  172. return fmt.Errorf("failed to find link %s on the Docker host : %v", linkName, err)
  173. }
  174. // verify a parent interface is being deleted
  175. if dummyLink.Attrs().ParentIndex != 0 {
  176. return fmt.Errorf("link %s is not a parent dummy interface", linkName)
  177. }
  178. // delete the macvlan dummy device
  179. if err := ns.NlHandle().LinkDel(dummyLink); err != nil {
  180. return fmt.Errorf("failed to delete the dummy %s link: %v", linkName, err)
  181. }
  182. logrus.Debugf("Deleted a dummy parent link: %s", linkName)
  183. return nil
  184. }
  185. // getDummyName returns the name of a dummy parent with truncated net ID and driver prefix
  186. func getDummyName(netID string) string {
  187. return dummyPrefix + netID
  188. }