mirror of
https://github.com/moby/moby.git
synced 2026-08-08 00:52:17 +00:00
The stringid.TruncateID utility is used to provide a consistent length
for "short IDs" (containers, networks). While the dummy interfaces need
a short identifier, they use their own format and don't have to follow
the same length as is used for "short IDs" elsewhere.
In addition, stringid.TruncateID has an additional check for the given
ID to contain colons (":"), which won't be the case for network-IDs that
are passed to it, so this check is redundant.
This patch moves the truncating local to the getDummyName function, so
that it can define its own semantics, independent of changes elsewhere.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
211 lines
7.0 KiB
Go
211 lines
7.0 KiB
Go
//go:build linux
|
|
|
|
package macvlan
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/containerd/log"
|
|
"github.com/docker/docker/libnetwork/ns"
|
|
"github.com/vishvananda/netlink"
|
|
)
|
|
|
|
const (
|
|
dummyPrefix = "dm-" // prefix for dummy macvlan parent interfaces.
|
|
dummyIDLength = 12 // length for dummy parent interface IDs.
|
|
)
|
|
|
|
// Create the macvlan slave specifying the source name
|
|
func createMacVlan(containerIfName, parent, macvlanMode string) (string, error) {
|
|
// Set the macvlan mode. Default is bridge mode
|
|
mode, err := setMacVlanMode(macvlanMode)
|
|
if err != nil {
|
|
return "", fmt.Errorf("Unsupported %s macvlan mode: %v", macvlanMode, err)
|
|
}
|
|
// verify the Docker host interface acting as the macvlan parent iface exists
|
|
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)
|
|
parentLink, err := ns.NlHandle().LinkByName(parent)
|
|
if err != nil {
|
|
return "", fmt.Errorf("error occurred looking up the macvlan parent iface %s error: %s", parent, err)
|
|
}
|
|
// Create a macvlan link
|
|
macvlan := &netlink.Macvlan{
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
Name: containerIfName,
|
|
ParentIndex: parentLink.Attrs().Index,
|
|
},
|
|
Mode: mode,
|
|
}
|
|
if err := ns.NlHandle().LinkAdd(macvlan); err != nil {
|
|
// 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 macvlan port: %v", err)
|
|
}
|
|
|
|
return macvlan.Attrs().Name, nil
|
|
}
|
|
|
|
// setMacVlanMode setter for one of the four macvlan port types
|
|
func setMacVlanMode(mode string) (netlink.MacvlanMode, error) {
|
|
switch mode {
|
|
case modePrivate:
|
|
return netlink.MACVLAN_MODE_PRIVATE, nil
|
|
case modeVepa:
|
|
return netlink.MACVLAN_MODE_VEPA, nil
|
|
case modeBridge:
|
|
return netlink.MACVLAN_MODE_BRIDGE, nil
|
|
case modePassthru:
|
|
return netlink.MACVLAN_MODE_PASSTHRU, nil
|
|
default:
|
|
return 0, fmt.Errorf("unknown macvlan mode: %s", mode)
|
|
}
|
|
}
|
|
|
|
// parentExists checks if the specified interface exists in the default namespace
|
|
func parentExists(ifaceStr string) bool {
|
|
_, err := ns.NlHandle().LinkByName(ifaceStr)
|
|
return err == nil
|
|
}
|
|
|
|
// createVlanLink parses sub-interfaces and vlan id for creation
|
|
func createVlanLink(parentName string) error {
|
|
if strings.Contains(parentName, ".") {
|
|
parent, vidInt, err := parseVlan(parentName)
|
|
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
|
|
parentLink, err := ns.NlHandle().LinkByName(parent)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find master interface %s on the Docker host: %v", parent, err)
|
|
}
|
|
vlanLink := &netlink.Vlan{
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
Name: parentName,
|
|
ParentIndex: parentLink.Attrs().Index,
|
|
},
|
|
VlanId: vidInt,
|
|
}
|
|
// create the subinterface
|
|
if err := ns.NlHandle().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 := ns.NlHandle().LinkSetUp(vlanLink); err != nil {
|
|
return fmt.Errorf("failed to enable %s the macvlan parent link %v", vlanLink.Name, err)
|
|
}
|
|
log.G(context.TODO()).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", parentName)
|
|
}
|
|
|
|
// 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
|
|
vlanLink, err := ns.NlHandle().LinkByName(linkName)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find interface %s on the Docker host : %v", linkName, err)
|
|
}
|
|
// verify a parent interface isn't being deleted
|
|
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 := ns.NlHandle().LinkDel(vlanLink); err != nil {
|
|
return fmt.Errorf("failed to delete %s link: %v", linkName, err)
|
|
}
|
|
log.G(context.TODO()).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 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)
|
|
}
|
|
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 !parentExists(parent) {
|
|
return "", 0, fmt.Errorf("-o parent interface was not found on the host: %s", parent)
|
|
}
|
|
|
|
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 := ns.NlHandle().LinkAdd(parent); err != nil {
|
|
return err
|
|
}
|
|
parentDummyLink, err := ns.NlHandle().LinkByName(dummyName)
|
|
if err != nil {
|
|
return fmt.Errorf("error occurred looking up the macvlan parent iface %s error: %s", dummyName, err)
|
|
}
|
|
// bring the new netlink iface up
|
|
if err := ns.NlHandle().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 := ns.NlHandle().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 := ns.NlHandle().LinkDel(dummyLink); err != nil {
|
|
return fmt.Errorf("failed to delete the dummy %s link: %v", linkName, err)
|
|
}
|
|
log.G(context.TODO()).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 {
|
|
if len(netID) > dummyIDLength {
|
|
netID = netID[:dummyIDLength]
|
|
}
|
|
return dummyPrefix + netID
|
|
}
|