The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7465

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) ===
WIP
From 979d50b45260307af029bb90a4554ec0b6c4165c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 May 2020 15:17:56 +0100
Subject: [PATCH 1/6] lxd/device/device/utils/network: Adds networkValidVLAN
 and networkValidVLANList functions

Validates a VLAN ID is a number and in usable range.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/device_utils_network.go | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/lxd/device/device_utils_network.go 
b/lxd/device/device_utils_network.go
index 24dea703b4..0558635f9c 100644
--- a/lxd/device/device_utils_network.go
+++ b/lxd/device/device_utils_network.go
@@ -692,6 +692,33 @@ func NetworkValidGateway(value string) error {
        return fmt.Errorf("Invalid gateway: %s", value)
 }
 
+// networkValidVLAN validates a VLAN ID.
+func networkValidVLAN(value string) error {
+       vlanID, err := strconv.Atoi(value)
+       if err != nil {
+               return fmt.Errorf("Invalid VLAN ID: %s", value)
+       }
+
+       if vlanID < 1 || vlanID > 4094 {
+               return fmt.Errorf("Out of range (1-4094) VLAN ID: %s", value)
+       }
+
+       return nil
+}
+
+// networkValidVLANList validates a comma delimited list of VLAN IDs.
+func networkValidVLANList(value string) error {
+       for _, vlanID := range strings.Split(value, ",") {
+               vlanID = strings.TrimSpace(vlanID)
+               err := networkValidVLAN(vlanID)
+               if err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
 // networkParsePortRange validates a port range in the form n-n.
 func networkParsePortRange(r string) (int64, int64, error) {
        entries := strings.Split(r, "-")

From 80733e9ae48006719a8638499633bda84ce6dc70 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 May 2020 15:18:48 +0100
Subject: [PATCH 2/6] lxd/device/nic/bridged: Adds vlan.tagged config
 validation

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/nic_bridged.go | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go
index 870155717e..b424fc1477 100644
--- a/lxd/device/nic_bridged.go
+++ b/lxd/device/nic_bridged.go
@@ -137,8 +137,19 @@ func (d *nicBridged) validateConfig(instConf 
instance.ConfigReader) error {
                requiredFields = append(requiredFields, "parent")
        }
 
+       rules := nicValidationRules(requiredFields, optionalFields)
+
+       // Add bridge specific vlan.tagged validation.
+       rules["vlan.tagged"] = func(value string) error {
+               if value == "" {
+                       return nil
+               }
+
+               return networkValidVLANList(value)
+       }
+
        // Now run normal validation.
-       err := d.config.Validate(nicValidationRules(requiredFields, 
optionalFields))
+       err := d.config.Validate(rules)
        if err != nil {
                return err
        }

From 9baaa1a374d40f3e5a279275ef984da11065a0df Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 May 2020 15:30:52 +0100
Subject: [PATCH 3/6] lxd/device/nic: Changes nicValidationRules to properly
 validation vlan

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/nic.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/device/nic.go b/lxd/device/nic.go
index 2195cc12b0..01dc1918f8 100644
--- a/lxd/device/nic.go
+++ b/lxd/device/nic.go
@@ -33,7 +33,7 @@ func nicValidationRules(requiredFields []string, 
optionalFields []string) map[st
                "parent":                  shared.IsAny,
                "network":                 shared.IsAny,
                "mtu":                     shared.IsAny,
-               "vlan":                    shared.IsAny,
+               "vlan":                    networkValidVLAN,
                "hwaddr":                  networkValidMAC,
                "host_name":               shared.IsAny,
                "limits.ingress":          shared.IsAny,

From d8db1f69c82271697bb62cfe600ab2536812ca4e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 May 2020 15:31:09 +0100
Subject: [PATCH 4/6] lxd/device/nic/bridged: Adds vlan validation

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/nic_bridged.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go
index b424fc1477..873fae80d4 100644
--- a/lxd/device/nic_bridged.go
+++ b/lxd/device/nic_bridged.go
@@ -62,6 +62,7 @@ func (d *nicBridged) validateConfig(instConf 
instance.ConfigReader) error {
                "maas.subnet.ipv4",
                "maas.subnet.ipv6",
                "boot.priority",
+               "vlan",
        }
 
        // Check that if network proeperty is set that conflicting keys are not 
present.

From 7f83ac5c27eaf095f234157fe8df1dee14c374f2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 May 2020 15:43:22 +0100
Subject: [PATCH 5/6] lxd/device/nic/bridged: Adds revert for veth pair cleanup
 on error

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/nic_bridged.go | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go
index 873fae80d4..b6d09e6421 100644
--- a/lxd/device/nic_bridged.go
+++ b/lxd/device/nic_bridged.go
@@ -25,6 +25,7 @@ import (
        "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/network"
+       "github.com/lxc/lxd/lxd/revert"
        "github.com/lxc/lxd/lxd/util"
        "github.com/lxc/lxd/shared"
        log "github.com/lxc/lxd/shared/log15"
@@ -195,6 +196,9 @@ func (d *nicBridged) Start() (*deviceConfig.RunConfig, 
error) {
                return nil, err
        }
 
+       revert := revert.New()
+       defer revert.Fail()
+
        saveData := make(map[string]string)
        saveData["host_name"] = d.config["host_name"]
 
@@ -218,10 +222,11 @@ func (d *nicBridged) Start() (*deviceConfig.RunConfig, 
error) {
                return nil, err
        }
 
+       revert.Add(func() { NetworkRemoveInterface(saveData["host_name"]) })
+
        // Apply and host-side limits and routes.
        err = networkSetupHostVethDevice(d.config, nil, saveData)
        if err != nil {
-               NetworkRemoveInterface(saveData["host_name"])
                return nil, err
        }
 
@@ -235,21 +240,19 @@ func (d *nicBridged) Start() (*deviceConfig.RunConfig, 
error) {
        // Apply and host-side network filters (uses enriched host_name from 
networkSetupHostVethDevice).
        err = d.setupHostFilters(nil)
        if err != nil {
-               NetworkRemoveInterface(saveData["host_name"])
                return nil, err
        }
+       revert.Add(func() { d.removeFilters(d.config) })
 
        // Attach host side veth interface to bridge.
        err = network.AttachInterface(d.config["parent"], saveData["host_name"])
        if err != nil {
-               NetworkRemoveInterface(saveData["host_name"])
                return nil, err
        }
 
        // Attempt to disable router advertisement acceptance.
        err = util.SysctlSet(fmt.Sprintf("net/ipv6/conf/%s/accept_ra", 
saveData["host_name"]), "0")
        if err != nil && !os.IsNotExist(err) {
-               NetworkRemoveInterface(saveData["host_name"])
                return nil, err
        }
 
@@ -274,6 +277,7 @@ func (d *nicBridged) Start() (*deviceConfig.RunConfig, 
error) {
                        }...)
        }
 
+       revert.Success()
        return &runConf, nil
 }
 

From 77b89b5c250cbf7ea0bb77352a34b6cabde70999 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 May 2020 17:10:56 +0100
Subject: [PATCH 6/6] lxd/device/nic/bridged: Adds support for untagged and
 tagged vlan membership

lxc config device set c1 eth0 vlan=2

Means:

 Remove from vlan 1 (or whatever the default vlan is for that bridge).
 Set PVID to vlan 2 for untagged ingress.
 Add to vlan 2 as untagged egress.

lxc config device set c1 eth0 vlan.tagged=3,4,5

Means:

 Add NIC port as trunk to bridge, with membership of VLANs 3, 4 and 5.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/nic_bridged.go | 81 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 81 insertions(+)

diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go
index b6d09e6421..7c868deabf 100644
--- a/lxd/device/nic_bridged.go
+++ b/lxd/device/nic_bridged.go
@@ -6,6 +6,7 @@ import (
        "encoding/binary"
        "encoding/hex"
        "fmt"
+       "io/ioutil"
        "math/big"
        "math/rand"
        "net"
@@ -256,6 +257,12 @@ func (d *nicBridged) Start() (*deviceConfig.RunConfig, 
error) {
                return nil, err
        }
 
+       // Setup VLAN settings on bridge port.
+       err = d.setupBridgePortVLANs(saveData["host_name"])
+       if err != nil {
+               return nil, err
+       }
+
        err = d.volatileSet(saveData)
        if err != nil {
                return nil, err
@@ -1159,3 +1166,77 @@ func (d *nicBridged) networkDHCPv6CreateIAAddress(IP 
net.IP) []byte {
        binary.BigEndian.PutUint32(data[24:28], uint32(0))                    
// Valid lifetime
        return data
 }
+
+// setupBridgePortVLANs configures the bridge port with the specified VLAN 
settings in device config.
+func (d *nicBridged) setupBridgePortVLANs(hostName string) error {
+       // Enable vlan_filtering on bridge if needed.
+       if d.config["vlan"] != "" || d.config["vlan.tagged"] != "" {
+               vlanFilteringStatus, err := d.bridgeVLANFilteringStatus()
+               if err != nil {
+                       return err
+               }
+
+               if vlanFilteringStatus != "1" {
+                       err := 
ioutil.WriteFile(fmt.Sprintf("/sys/class/net/%s/bridge/vlan_filtering", 
d.config["parent"]), []byte("1"), 0)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed enabling VLAN 
filtering on bridge %q", d.config["parent"])
+                       }
+               }
+       }
+
+       // Set port on bridge to specified untagged PVID.
+       if d.config["vlan"] != "" {
+               // Get default PVID membership on port.
+               defaultPVID, err := d.bridgeVLANDefaultPVID()
+               if err != nil {
+                       return err
+               }
+
+               // If the default is different to the specified untagged VLAN 
remove its membership.
+               if defaultPVID != d.config["vlan"] {
+                       _, err = shared.RunCommand("bridge", "vlan", "del", 
"dev", hostName, "vid", defaultPVID)
+                       if err != nil {
+                               return err
+                       }
+               }
+
+               // Configure the untagged membership of the port.
+               _, err = shared.RunCommand("bridge", "vlan", "add", "dev", 
hostName, "vid", d.config["vlan"], "pvid", "untagged", "master")
+               if err != nil {
+                       return err
+               }
+       }
+
+       // Add any tagged VLAN memberships.
+       if d.config["vlan.tagged"] != "" {
+               for _, vlanID := range strings.Split(d.config["vlan.tagged"], 
",") {
+                       vlanID = strings.TrimSpace(vlanID)
+                       _, err := shared.RunCommand("bridge", "vlan", "add", 
"dev", hostName, "vid", vlanID)
+                       if err != nil {
+                               return err
+                       }
+               }
+       }
+
+       return nil
+}
+
+// bridgeVLANFilteringStatus returns whether VLAN filtering is enabled on the 
bridge.
+func (d *nicBridged) bridgeVLANFilteringStatus() (string, error) {
+       content, err := 
ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/bridge/vlan_filtering", 
d.config["parent"]))
+       if err != nil {
+               return "", errors.Wrapf(err, "Failed getting bridge VLAN status 
for %q", d.config["parent"])
+       }
+
+       return strings.TrimSpace(fmt.Sprintf("%s", content)), nil
+}
+
+// bridgeVLANDefaultPVID returns the VLAN default port VLAN ID (PVID).
+func (d *nicBridged) bridgeVLANDefaultPVID() (string, error) {
+       content, err := 
ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/bridge/default_pvid", 
d.config["parent"]))
+       if err != nil {
+               return "", errors.Wrapf(err, "Failed getting bridge VLAN 
default PVID for %q", d.config["parent"])
+       }
+
+       return strings.TrimSpace(fmt.Sprintf("%s", content)), nil
+}
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to