The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/3008
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === Closes #3002 Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
From 0313f486461d0b90e928a4d6fe972ce1614fd42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Fri, 3 Mar 2017 17:43:34 -0500 Subject: [PATCH] Add "vlan" property to "macvlan" interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #3002 Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- doc/api-extensions.md | 8 ++++++++ doc/configuration.md | 1 + lxd/api_1.0.go | 1 + lxd/container.go | 2 ++ lxd/container_lxc.go | 34 ++++++++++++++++++++++++++++++---- lxd/networks_utils.go | 42 +++++++++++++++++++++++++++++++++++++++++- 6 files changed, 83 insertions(+), 5 deletions(-) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index 4a2e182..22c7f4a 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -217,3 +217,11 @@ Introduces the ability to rename a volume group by setting "storage.lvm.vg\_name ## storage\_lvm\_thinpool\_rename Introduces the ability to rename a thinpool name by setting "storage.thinpool\_name". + +## network\_vlan +This adds a new "vlan" property to "macvlan" network devices. + +When set, this will instruct LXD to attach to the specified VLAN. LXD +will look for an existing interface for that VLAN on the host. If one +can't be found it will create one itself and then use that as the +macvlan parent. diff --git a/doc/configuration.md b/doc/configuration.md index 3a6efec..aa8f07c 100644 --- a/doc/configuration.md +++ b/doc/configuration.md @@ -213,6 +213,7 @@ host\_name | string | randomly assigned | no | bridged, p hwaddr | string | randomly assigned | no | all | - | The MAC address of the new interface mtu | integer | parent MTU | no | all | - | The MTU of the new interface parent | string | - | yes | physical, bridged, macvlan | - | The name of the host device or bridge +vlan | integer | - | no | macvlan | network\_vlan | The VLAN ID to attach to ipv4.address | string | - | no | bridged | network | An IPv4 address to assign to the container through DHCP ipv6.address | string | - | no | bridged | network | An IPv6 address to assign to the container through DHCP security.mac\_filtering | boolean | false | no | bridged | network | Prevent the container from spoofing another's MAC address diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go index d7bd673..9dfe7b5 100644 --- a/lxd/api_1.0.go +++ b/lxd/api_1.0.go @@ -95,6 +95,7 @@ func api10Get(d *Daemon, r *http.Request) Response { "network_dhcp_expiry", "storage_lvm_vg_rename", "storage_lvm_thinpool_rename", + "network_vlan", }, APIStatus: "stable", APIVersion: version.APIVersion, diff --git a/lxd/container.go b/lxd/container.go index 06939e5..1c5f8cd 100644 --- a/lxd/container.go +++ b/lxd/container.go @@ -107,6 +107,8 @@ func containerValidDeviceConfigKey(t, k string) bool { return true case "parent": return true + case "vlan": + return true case "ipv4.address": return true case "ipv6.address": diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go index c122ecd..acb5217 100644 --- a/lxd/container_lxc.go +++ b/lxd/container_lxc.go @@ -1224,11 +1224,16 @@ func (c *containerLXC) initLXC() error { return err } - if shared.StringInSlice(m["nictype"], []string{"bridged", "physical", "macvlan"}) { + if shared.StringInSlice(m["nictype"], []string{"bridged", "physical"}) { err = lxcSetConfigItem(cc, "lxc.network.link", m["parent"]) if err != nil { return err } + } else if m["nictype"] == "macvlan" { + err = lxcSetConfigItem(cc, "lxc.network.link", networkGetHostDevice(m["parent"], m["vlan"])) + if err != nil { + return err + } } // Host Virtual NIC name @@ -1489,8 +1494,7 @@ func (c *containerLXC) startCommon() (string, error) { } // Sanity checks for devices - for _, name := range c.expandedDevices.DeviceNames() { - m := c.expandedDevices[name] + for name, m := range c.expandedDevices { switch m["type"] { case "disk": // When we want to attach a storage volume created via @@ -1744,6 +1748,17 @@ func (c *containerLXC) startCommon() (string, error) { return "", err } } + + // Create VLAN devices + if m["nictype"] == "macvlan" && m["vlan"] != "" { + device := networkGetHostDevice(m["parent"], m["vlan"]) + if !shared.PathExists(fmt.Sprintf("/sys/class/net/%s", device)) { + err := shared.RunCommand("ip", "link", "add", "link", m["parent"], "name", device, "up", "type", "vlan", "id", m["vlan"]) + if err != nil { + return "", err + } + } + } } } @@ -5539,8 +5554,19 @@ func (c *containerLXC) createNetworkDevice(name string, m types.Device) (string, // Handle macvlan if m["nictype"] == "macvlan" { + // Deal with VLAN + device := m["parent"] + if m["vlan"] != "" { + device = networkGetHostDevice(m["parent"], m["vlan"]) + if !shared.PathExists(fmt.Sprintf("/sys/class/net/%s", device)) { + err := shared.RunCommand("ip", "link", "add", "link", m["parent"], "name", device, "up", "type", "vlan", "id", m["vlan"]) + if err != nil { + return "", err + } + } + } - err := exec.Command("ip", "link", "add", n1, "link", m["parent"], "type", "macvlan", "mode", "bridge").Run() + err := exec.Command("ip", "link", "add", n1, "link", device, "type", "macvlan", "mode", "bridge").Run() if err != nil { return "", fmt.Errorf("Failed to create the new macvlan interface: %s", err) } diff --git a/lxd/networks_utils.go b/lxd/networks_utils.go index 3418054..70e4334 100644 --- a/lxd/networks_utils.go +++ b/lxd/networks_utils.go @@ -105,7 +105,7 @@ func networkIsInUse(c container, name string) bool { continue } - if d["parent"] == name { + if networkGetHostDevice(d["parent"], d["vlan"]) == name { return true } } @@ -113,6 +113,46 @@ func networkIsInUse(c container, name string) bool { return false } +func networkGetHostDevice(parent string, vlan string) string { + // If no VLAN, just use the raw device + if vlan == "" { + return parent + } + + // If no VLANs are configured, use the default pattern + defaultVlan := fmt.Sprintf("%s.%s", parent, vlan) + if !shared.PathExists("/proc/net/vlan/config") { + return defaultVlan + } + + // Look for an existing VLAN + f, err := os.Open("/proc/net/vlan/config") + if err != nil { + return defaultVlan + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + // Only grab the lines we're interested in + s := strings.Split(scanner.Text(), "|") + if len(s) != 3 { + continue + } + + vlanIface := strings.TrimSpace(s[0]) + vlanId := strings.TrimSpace(s[1]) + vlanParent := strings.TrimSpace(s[2]) + + if vlanParent == parent && vlanId == vlan { + return vlanIface + } + } + + // Return the default pattern + return defaultVlan +} + func networkGetIP(subnet *net.IPNet, host int64) net.IP { // Convert IP to a big int bigIP := big.NewInt(0)
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel