|
@@ -13,8 +13,13 @@ import (
|
|
|
"github.com/vishvananda/netlink"
|
|
|
)
|
|
|
|
|
|
-// Create the netlink interface specifying the source name
|
|
|
-func createMacVlan(containerIfName, hostIface, macvlanMode string) (string, error) {
|
|
|
+const (
|
|
|
+ dummyPrefix = "dm-" // macvlan prefix for dummy parent interface
|
|
|
+ macvlanKernelVer = "3.9" // minimum ipvlan kernel version support
|
|
|
+)
|
|
|
+
|
|
|
+// Create the macvlan slave specifying the source name
|
|
|
+func createMacVlan(containerIfName, parent, macvlanMode string) (string, error) {
|
|
|
defer osl.InitOSContext()()
|
|
|
|
|
|
// Set the macvlan mode. Default is bridge mode
|
|
@@ -23,27 +28,27 @@ func createMacVlan(containerIfName, hostIface, macvlanMode string) (string, erro
|
|
|
return "", fmt.Errorf("Unsupported %s macvlan mode: %v", macvlanMode, err)
|
|
|
}
|
|
|
// verify the Docker host interface acting as the macvlan parent iface exists
|
|
|
- if ok := hostIfaceExists(hostIface); !ok {
|
|
|
- return "", fmt.Errorf("the requested host interface %s was not found on the Docker host", hostIface)
|
|
|
+ if !parentExists(parent) {
|
|
|
+ return "", fmt.Errorf("the requested parent interface %s was not found on the Docker host", parent)
|
|
|
}
|
|
|
// Get the link for the master index (Example: the docker host eth iface)
|
|
|
- hostEth, err := netlink.LinkByName(hostIface)
|
|
|
+ parentLink, err := netlink.LinkByName(parent)
|
|
|
if err != nil {
|
|
|
- logrus.Errorf("error occoured looking up the parent iface %s mode: %s error: %s", hostIface, macvlanMode, err)
|
|
|
+ return "", fmt.Errorf("error occoured looking up the %s parent iface %s error: %s", macvlanType, parent, err)
|
|
|
}
|
|
|
// Create a macvlan link
|
|
|
macvlan := &netlink.Macvlan{
|
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
|
Name: containerIfName,
|
|
|
- ParentIndex: hostEth.Attrs().Index,
|
|
|
+ ParentIndex: parentLink.Attrs().Index,
|
|
|
},
|
|
|
Mode: mode,
|
|
|
}
|
|
|
if err := netlink.LinkAdd(macvlan); err != nil {
|
|
|
- // verbose but will be an issue if user creates a macvlan and ipvlan on same parent.Netlink msg is uninformative
|
|
|
- logrus.Warn("Ensure there are no ipvlan networks using the same `-o host_iface` as the macvlan network.")
|
|
|
- return "", fmt.Errorf("Failed to create macvlan link: %s with the error: %v", macvlan.Name, err)
|
|
|
+ // If a user creates a macvlan and ipvlan on same parent, only one slave iface can be active at a time.
|
|
|
+ return "", fmt.Errorf("failed to create the %s port: %v", macvlanType, err)
|
|
|
}
|
|
|
+
|
|
|
return macvlan.Attrs().Name, nil
|
|
|
}
|
|
|
|
|
@@ -63,12 +68,13 @@ func setMacVlanMode(mode string) (netlink.MacvlanMode, error) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// validateHostIface check if the specified interface exists in the default namespace
|
|
|
-func hostIfaceExists(ifaceStr string) bool {
|
|
|
+// parentExists check if the specified interface exists in the default namespace
|
|
|
+func parentExists(ifaceStr string) bool {
|
|
|
_, err := netlink.LinkByName(ifaceStr)
|
|
|
if err != nil {
|
|
|
return false
|
|
|
}
|
|
|
+
|
|
|
return true
|
|
|
}
|
|
|
|
|
@@ -87,13 +93,14 @@ func kernelSupport(networkTpe string) error {
|
|
|
return nil
|
|
|
}
|
|
|
}
|
|
|
- return fmt.Errorf("required kernel module '%s' was not found in /proc/modules, kernel version >= 3.19 is recommended", macvlanType)
|
|
|
+
|
|
|
+ return fmt.Errorf("unable to load the Linux kernel module %s", macvlanType)
|
|
|
}
|
|
|
|
|
|
// createVlanLink parses sub-interfaces and vlan id for creation
|
|
|
-func createVlanLink(ifaceName string) error {
|
|
|
- if strings.Contains(ifaceName, ".") {
|
|
|
- parentIface, vidInt, err := parseVlan(ifaceName)
|
|
|
+func createVlanLink(parentName string) error {
|
|
|
+ if strings.Contains(parentName, ".") {
|
|
|
+ parent, vidInt, err := parseVlan(parentName)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
@@ -102,74 +109,124 @@ func createVlanLink(ifaceName string) error {
|
|
|
return fmt.Errorf("vlan id must be between 1-4094, received: %d", vidInt)
|
|
|
}
|
|
|
// get the parent link to attach a vlan subinterface
|
|
|
- hostIface, err := netlink.LinkByName(parentIface)
|
|
|
+ parentLink, err := netlink.LinkByName(parent)
|
|
|
if err != nil {
|
|
|
- return fmt.Errorf("failed to find master interface %s on the Docker host: %v", parentIface, err)
|
|
|
+ return fmt.Errorf("failed to find master interface %s on the Docker host: %v", parent, err)
|
|
|
}
|
|
|
- vlanIface := &netlink.Vlan{
|
|
|
+ vlanLink := &netlink.Vlan{
|
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
|
- Name: ifaceName,
|
|
|
- ParentIndex: hostIface.Attrs().Index,
|
|
|
+ Name: parentName,
|
|
|
+ ParentIndex: parentLink.Attrs().Index,
|
|
|
},
|
|
|
VlanId: vidInt,
|
|
|
}
|
|
|
// create the subinterface
|
|
|
- if err := netlink.LinkAdd(vlanIface); err != nil {
|
|
|
- return fmt.Errorf("failed to create %s vlan link: %v", vlanIface.Name, err)
|
|
|
+ if err := netlink.LinkAdd(vlanLink); err != nil {
|
|
|
+ return fmt.Errorf("failed to create %s vlan link: %v", vlanLink.Name, err)
|
|
|
}
|
|
|
// Bring the new netlink iface up
|
|
|
- if err := netlink.LinkSetUp(vlanIface); err != nil {
|
|
|
- return fmt.Errorf("failed to enable %s the macvlan netlink link: %v", vlanIface.Name, err)
|
|
|
+ if err := netlink.LinkSetUp(vlanLink); err != nil {
|
|
|
+ return fmt.Errorf("failed to enable %s the macvlan parent link %v", vlanLink.Name, err)
|
|
|
}
|
|
|
- logrus.Debugf("Added a vlan tagged netlink subinterface: %s with a vlan id: %d", ifaceName, vidInt)
|
|
|
+ logrus.Debugf("Added a vlan tagged netlink subinterface: %s with a vlan id: %d", parentName, vidInt)
|
|
|
return nil
|
|
|
}
|
|
|
- return fmt.Errorf("invalid subinterface vlan name %s, example formatting is eth0.10", ifaceName)
|
|
|
+
|
|
|
+ return fmt.Errorf("invalid subinterface vlan name %s, example formatting is eth0.10", parentName)
|
|
|
}
|
|
|
|
|
|
-// verifyVlanDel verifies only sub-interfaces with a vlan id get deleted
|
|
|
-func delVlanLink(ifaceName string) error {
|
|
|
- if strings.Contains(ifaceName, ".") {
|
|
|
- _, _, err := parseVlan(ifaceName)
|
|
|
+// delVlanLink verifies only sub-interfaces with a vlan id get deleted
|
|
|
+func delVlanLink(linkName string) error {
|
|
|
+ if strings.Contains(linkName, ".") {
|
|
|
+ _, _, err := parseVlan(linkName)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
// delete the vlan subinterface
|
|
|
- vlanIface, err := netlink.LinkByName(ifaceName)
|
|
|
+ vlanLink, err := netlink.LinkByName(linkName)
|
|
|
if err != nil {
|
|
|
- return fmt.Errorf("failed to find interface %s on the Docker host : %v", ifaceName, err)
|
|
|
+ return fmt.Errorf("failed to find interface %s on the Docker host : %v", linkName, err)
|
|
|
}
|
|
|
// verify a parent interface isn't being deleted
|
|
|
- if vlanIface.Attrs().ParentIndex == 0 {
|
|
|
- return fmt.Errorf("interface %s does not appear to be a slave device: %v", ifaceName, err)
|
|
|
+ if vlanLink.Attrs().ParentIndex == 0 {
|
|
|
+ return fmt.Errorf("interface %s does not appear to be a slave device: %v", linkName, err)
|
|
|
}
|
|
|
// delete the macvlan slave device
|
|
|
- if err := netlink.LinkDel(vlanIface); err != nil {
|
|
|
- return fmt.Errorf("failed to delete %s link: %v", ifaceName, err)
|
|
|
+ if err := netlink.LinkDel(vlanLink); err != nil {
|
|
|
+ return fmt.Errorf("failed to delete %s link: %v", linkName, err)
|
|
|
}
|
|
|
- logrus.Debugf("Deleted a vlan tagged netlink subinterface: %s", ifaceName)
|
|
|
+ logrus.Debugf("Deleted a vlan tagged netlink subinterface: %s", linkName)
|
|
|
}
|
|
|
// if the subinterface doesn't parse to iface.vlan_id leave the interface in
|
|
|
// place since it could be a user specified name not created by the driver.
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-// parseVlan parses and verifies a slave interface name: -o host_iface=eth0.10
|
|
|
-func parseVlan(ifaceName string) (string, int, error) {
|
|
|
- // parse -o host_iface=eth0.10
|
|
|
- splitIface := strings.Split(ifaceName, ".")
|
|
|
- if len(splitIface) != 2 {
|
|
|
- return "", 0, fmt.Errorf("required interface name format is: name.vlan_id, ex. eth0.10 for vlan 10, instead received %s", ifaceName)
|
|
|
+// parseVlan parses and verifies a slave interface name: -o parent=eth0.10
|
|
|
+func parseVlan(linkName string) (string, int, error) {
|
|
|
+ // parse -o parent=eth0.10
|
|
|
+ splitName := strings.Split(linkName, ".")
|
|
|
+ if len(splitName) != 2 {
|
|
|
+ return "", 0, fmt.Errorf("required interface name format is: name.vlan_id, ex. eth0.10 for vlan 10, instead received %s", linkName)
|
|
|
}
|
|
|
- parentIface, vidStr := splitIface[0], splitIface[1]
|
|
|
+ parent, vidStr := splitName[0], splitName[1]
|
|
|
// validate type and convert vlan id to int
|
|
|
vidInt, err := strconv.Atoi(vidStr)
|
|
|
if err != nil {
|
|
|
return "", 0, fmt.Errorf("unable to parse a valid vlan id from: %s (ex. eth0.10 for vlan 10)", vidStr)
|
|
|
}
|
|
|
// Check if the interface exists
|
|
|
- if ok := hostIfaceExists(parentIface); !ok {
|
|
|
- return "", 0, fmt.Errorf("-o host_iface parent interface does was not found on the host: %s", parentIface)
|
|
|
+ if !parentExists(parent) {
|
|
|
+ return "", 0, fmt.Errorf("-o parent interface does was not found on the host: %s", parent)
|
|
|
}
|
|
|
- return parentIface, vidInt, nil
|
|
|
+
|
|
|
+ return parent, vidInt, nil
|
|
|
+}
|
|
|
+
|
|
|
+// createDummyLink creates a dummy0 parent link
|
|
|
+func createDummyLink(dummyName, truncNetID string) error {
|
|
|
+ // create a parent interface since one was not specified
|
|
|
+ parent := &netlink.Dummy{
|
|
|
+ LinkAttrs: netlink.LinkAttrs{
|
|
|
+ Name: dummyName,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ if err := netlink.LinkAdd(parent); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ parentDummyLink, err := netlink.LinkByName(dummyName)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("error occoured looking up the %s parent iface %s error: %s", macvlanType, dummyName, err)
|
|
|
+ }
|
|
|
+ // bring the new netlink iface up
|
|
|
+ if err := netlink.LinkSetUp(parentDummyLink); err != nil {
|
|
|
+ return fmt.Errorf("failed to enable %s the macvlan parent link: %v", dummyName, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// delDummyLink deletes the link type dummy used when -o parent is not passed
|
|
|
+func delDummyLink(linkName string) error {
|
|
|
+ // delete the vlan subinterface
|
|
|
+ dummyLink, err := netlink.LinkByName(linkName)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("failed to find link %s on the Docker host : %v", linkName, err)
|
|
|
+ }
|
|
|
+ // verify a parent interface is being deleted
|
|
|
+ if dummyLink.Attrs().ParentIndex != 0 {
|
|
|
+ return fmt.Errorf("link %s is not a parent dummy interface", linkName)
|
|
|
+ }
|
|
|
+ // delete the macvlan dummy device
|
|
|
+ if err := netlink.LinkDel(dummyLink); err != nil {
|
|
|
+ return fmt.Errorf("failed to delete the dummy %s link: %v", linkName, err)
|
|
|
+ }
|
|
|
+ logrus.Debugf("Deleted a dummy parent link: %s", linkName)
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// getDummyName returns the name of a dummy parent with truncated net ID and driver prefix
|
|
|
+func getDummyName(netID string) string {
|
|
|
+ return fmt.Sprintf("%s%s", dummyPrefix, netID)
|
|
|
}
|