macvlan_setup.go 7.5 KB

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