ipvlan_setup.go 6.9 KB

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