ipvlan_setup.go 7.5 KB

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