moby--moby/libnetwork/drivers/ipvlan/ipvlan_setup.go

167 lines
5.6 KiB
Go

package ipvlan
import (
"bufio"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/osl"
"github.com/vishvananda/netlink"
)
// Create the ipvlan netlink interface specifying the source name
func createIPVlan(containerIfName, hostIface, ipVlanMode string) (string, error) {
defer osl.InitOSContext()()
// Set the ipvlan mode. Default is L2 mode
mode, err := setIPVlanMode(ipVlanMode)
if err != nil {
return "", fmt.Errorf("unsupported %s ipvlan mode: %v", ipVlanMode, err)
}
// Get the link for the master index (Example: the docker host eth iface)
hostEth, err := netlink.LinkByName(hostIface)
if err != nil {
return "", fmt.Errorf("the requested host interface %s was not found on the Docker host", hostIface)
}
// Create a ipvlan link
ipvlan := &netlink.IPVlan{
LinkAttrs: netlink.LinkAttrs{
Name: containerIfName,
ParentIndex: hostEth.Attrs().Index,
},
Mode: mode,
}
if err := netlink.LinkAdd(ipvlan); err != nil {
// verbose but will be an issue if you create a macvlan and ipvlan using the same parent iface
logrus.Warn("Ensure there are no macvlan networks using the same `-o host_iface` as the ipvlan network.")
return "", fmt.Errorf("Failed to create ipvlan link: %s with the error: %v", ipvlan.Name, err)
}
return ipvlan.Attrs().Name, nil
}
// setIPVlanMode setter for one of the two ipvlan port types
func setIPVlanMode(mode string) (netlink.IPVlanMode, error) {
switch mode {
case modeL2:
return netlink.IPVLAN_MODE_L2, nil
case modeL3:
return netlink.IPVLAN_MODE_L3, nil
default:
return 0, fmt.Errorf("Unknown ipvlan mode: %s", mode)
}
}
// validateHostIface check if the specified interface exists in the default namespace
func hostIfaceExists(ifaceStr string) bool {
_, err := netlink.LinkByName(ifaceStr)
if err != nil {
return false
}
return true
}
// kernelSupport for the necessary kernel module for the driver type
func kernelSupport(networkTpe string) error {
// attempt to load the module, silent if successful or already loaded
exec.Command("modprobe", ipvlanType).Run()
f, err := os.Open("/proc/modules")
if err != nil {
return err
}
defer f.Close()
s := bufio.NewScanner(f)
for s.Scan() {
if strings.Contains(s.Text(), ipvlanType) {
return nil
}
}
return fmt.Errorf("required kernel module '%s' was not found in /proc/modules, ipvlan requires kernel version >= 3.19", ipvlanType)
}
// createVlanLink parses sub-interfaces and vlan id for creation
func createVlanLink(ifaceName string) error {
if strings.Contains(ifaceName, ".") {
parentIface, vidInt, err := parseVlan(ifaceName)
if err != nil {
return err
}
// VLAN identifier or VID is a 12-bit field specifying the VLAN to which the frame belongs
if vidInt > 4094 || vidInt < 1 {
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)
if err != nil {
return fmt.Errorf("failed to find master interface %s on the Docker host: %v", parentIface, err)
}
vlanIface := &netlink.Vlan{
LinkAttrs: netlink.LinkAttrs{
Name: ifaceName,
ParentIndex: hostIface.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)
}
// Bring the new netlink iface up
if err := netlink.LinkSetUp(vlanIface); err != nil {
return fmt.Errorf("failed to enable %s the ipvlan netlink link: %v", vlanIface.Name, err)
}
logrus.Debugf("Added a vlan tagged netlink subinterface: %s with a vlan id: %d", ifaceName, vidInt)
return nil
}
return fmt.Errorf("invalid subinterface vlan name %s, example formatting is eth0.10", ifaceName)
}
// verifyVlanDel verifies only sub-interfaces with a vlan id get deleted
func delVlanLink(ifaceName string) error {
if strings.Contains(ifaceName, ".") {
_, _, err := parseVlan(ifaceName)
if err != nil {
return err
}
// delete the vlan subinterface
vlanIface, err := netlink.LinkByName(ifaceName)
if err != nil {
return fmt.Errorf("failed to find interface %s on the Docker host : %v", ifaceName, 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)
}
// delete the ipvlan slave device
if err := netlink.LinkDel(vlanIface); err != nil {
return fmt.Errorf("failed to delete %s link: %v", ifaceName, err)
}
logrus.Debugf("Deleted a vlan tagged netlink subinterface: %s", ifaceName)
}
// 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)
}
parentIface, vidStr := splitIface[0], splitIface[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)
}
return parentIface, vidInt, nil
}