macvlan_setup.go 6.9 KB

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