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