ipvlan_setup.go 7.3 KB

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