[lxc-devel] [lxd/master] Add "vlan" property to "macvlan" interfaces
stgraber on Github
lxc-bot at linuxcontainers.org
Fri Mar 3 22:44:54 UTC 2017
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 370 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170303/caff6b86/attachment.bin>
-------------- next part --------------
From 0313f486461d0b90e928a4d6fe972ce1614fd42b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at 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 <stgraber at 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)
More information about the lxc-devel
mailing list