[lxc-devel] [lxd/master] NIC: Bridged Openvswitch VLAN support
tomponline on Github
lxc-bot at linuxcontainers.org
Tue Jun 2 13:48:48 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 343 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200602/2c6722a6/attachment.bin>
-------------- next part --------------
From 59ccc7ca5463727c6b5684855c4a3d73a2f22bac Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 2 Jun 2020 10:50:08 +0100
Subject: [PATCH 1/4] lxd/network/utils: Adds IsNativeBridge function
To isolate the logic used for detecting a native linux bridge in a single function for re-use.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/network/network_utils.go | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go
index bb56b2b187..14fe9230df 100644
--- a/lxd/network/network_utils.go
+++ b/lxd/network/network_utils.go
@@ -92,9 +92,14 @@ func GetIP(subnet *net.IPNet, host int64) net.IP {
return newIP
}
+// IsNativeBridge returns whether the bridge name specified is a Linux native bridge.
+func IsNativeBridge(bridgeName string) bool {
+ return shared.PathExists(fmt.Sprintf("/sys/class/net/%s/bridge", bridgeName))
+}
+
// AttachInterface attaches an interface to a bridge.
func AttachInterface(bridgeName string, devName string) error {
- if shared.PathExists(fmt.Sprintf("/sys/class/net/%s/bridge", bridgeName)) {
+ if IsNativeBridge(bridgeName) {
_, err := shared.RunCommand("ip", "link", "set", "dev", devName, "master", bridgeName)
if err != nil {
return err
@@ -115,7 +120,7 @@ func AttachInterface(bridgeName string, devName string) error {
// DetachInterface detaches an interface from a bridge.
func DetachInterface(bridgeName string, devName string) error {
- if shared.PathExists(fmt.Sprintf("/sys/class/net/%s/bridge", bridgeName)) {
+ if IsNativeBridge(bridgeName) {
_, err := shared.RunCommand("ip", "link", "set", "dev", devName, "nomaster")
if err != nil {
return err
From a6d92324db52ddf9541c06a29b7db4a418cae8cf Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 2 Jun 2020 14:39:30 +0100
Subject: [PATCH 2/4] lxd/device/device/utils/network: Allow VLAN ID 0 in
networkValidVLAN
Openvswitch allows VLAN 0 for untagged frames.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/device_utils_network.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lxd/device/device_utils_network.go b/lxd/device/device_utils_network.go
index 0558635f9c..6a15a345d8 100644
--- a/lxd/device/device_utils_network.go
+++ b/lxd/device/device_utils_network.go
@@ -699,8 +699,8 @@ func networkValidVLAN(value string) error {
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)
+ if vlanID < 0 || vlanID > 4094 {
+ return fmt.Errorf("Out of range (0-4094) VLAN ID: %s", value)
}
return nil
From 607888d4a827eaa2c9cffd74bcd51140396e8e94 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 2 Jun 2020 14:40:10 +0100
Subject: [PATCH 3/4] test: Updates bridged vlan ID range tests
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
test/suites/container_devices_nic_bridged_vlan.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/suites/container_devices_nic_bridged_vlan.sh b/test/suites/container_devices_nic_bridged_vlan.sh
index 1713e2c10f..9f717098b5 100644
--- a/test/suites/container_devices_nic_bridged_vlan.sh
+++ b/test/suites/container_devices_nic_bridged_vlan.sh
@@ -71,7 +71,7 @@ test_container_devices_nic_bridged_vlan() {
! lxc config device set "${prefix}-ctA" eth0 vlan = 4096 # Check out of range VLAN ID.
! lxc config device set "${prefix}-ctA" eth0 vlan = 0 # Check out of range VLAN ID.
! lxc config device set "${prefix}-ctA" eth0 vlan.tagged = 5,invalid, 6 # Check invalid VLAN ID list.
- ! lxc config device set "${prefix}-ctA" eth0 vlan.tagged=0 # Check out of range VLAN ID list.
+ ! lxc config device set "${prefix}-ctA" eth0 vlan.tagged=-1 # Check out of range VLAN ID list.
! lxc config device set "${prefix}-ctA" eth0 vlan.tagged=4096 # Check out of range VLAN ID list.
lxc config device remove "${prefix}-ctA" eth0
From ccf2c7e221dd8c91ab2b788f321741e1d8fd16f3 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 2 Jun 2020 14:41:32 +0100
Subject: [PATCH 4/4] lxd/device/nic/bridged: Adds openvswitch vlan support
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic_bridged.go | 72 ++++++++++++++++++++++++++++++++++++---
1 file changed, 68 insertions(+), 4 deletions(-)
diff --git a/lxd/device/nic_bridged.go b/lxd/device/nic_bridged.go
index 132c8944e9..a1c48f1e00 100644
--- a/lxd/device/nic_bridged.go
+++ b/lxd/device/nic_bridged.go
@@ -286,8 +286,13 @@ func (d *nicBridged) Start() (*deviceConfig.RunConfig, error) {
return nil, err
}
- // Setup VLAN settings on bridge port.
- err = d.setupBridgePortVLANs(saveData["host_name"])
+ // Detech bridge type and setup VLAN settings on bridge port.
+ if network.IsNativeBridge(d.config["parent"]) {
+ err = d.setupNativeBridgePortVLANs(saveData["host_name"])
+ } else {
+ err = d.setupOVSBridgePortVLANs(saveData["host_name"])
+ }
+
if err != nil {
return nil, err
}
@@ -1202,8 +1207,8 @@ func (d *nicBridged) networkDHCPv6CreateIAAddress(IP net.IP) []byte {
return data
}
-// setupBridgePortVLANs configures the bridge port with the specified VLAN settings in device config.
-func (d *nicBridged) setupBridgePortVLANs(hostName string) error {
+// setupNativeBridgePortVLANs configures the bridge port with the specified VLAN settings on the native bridge.
+func (d *nicBridged) setupNativeBridgePortVLANs(hostName string) error {
// Check vlan_filtering is enabled on bridge if needed.
if d.config["vlan"] != "" || d.config["vlan.tagged"] != "" {
vlanFilteringStatus, err := network.BridgeVLANFilteringStatus(d.config["parent"])
@@ -1218,6 +1223,11 @@ func (d *nicBridged) setupBridgePortVLANs(hostName string) error {
// Set port on bridge to specified untagged PVID.
if d.config["vlan"] != "" {
+ // Reject VLAN ID 0 if specified (as main validation allows VLAN ID 0 to accomodate ovs).
+ if d.config["vlan"] == "0" {
+ return fmt.Errorf("VLAN ID 0 is not allowed for native Linux bridges")
+ }
+
// Get default PVID membership on port.
defaultPVID, err := network.BridgeVLANDefaultPVID(d.config["parent"])
if err != nil {
@@ -1246,6 +1256,12 @@ func (d *nicBridged) setupBridgePortVLANs(hostName string) error {
if d.config["vlan.tagged"] != "" {
for _, vlanID := range strings.Split(d.config["vlan.tagged"], ",") {
vlanID = strings.TrimSpace(vlanID)
+
+ // Reject VLAN ID 0 if specified (as main validation allows VLAN ID 0 to accomodate ovs).
+ if vlanID == "0" {
+ return fmt.Errorf("VLAN ID 0 is not allowed for native Linux bridges")
+ }
+
_, err := shared.RunCommand("bridge", "vlan", "add", "dev", hostName, "vid", vlanID)
if err != nil {
return err
@@ -1255,3 +1271,51 @@ func (d *nicBridged) setupBridgePortVLANs(hostName string) error {
return nil
}
+
+// setupOVSBridgePortVLANs configures the bridge port with the specified VLAN settings on the openvswitch bridge.
+func (d *nicBridged) setupOVSBridgePortVLANs(hostName string) error {
+ // Set port on bridge to specified untagged PVID.
+ if d.config["vlan"] != "" {
+ if d.config["vlan"] == "none" && d.config["vlan.tagged"] == "" {
+ return fmt.Errorf("vlan=none is not supported with openvswitch bridges when not using vlan.tagged")
+ }
+
+ // Configure the untagged 'native' membership settings of the port if VLAN ID specified.
+ // Also set the vlan_mode=access, which will drop any tagged frames.
+ // Order is important here, as vlan_mode is set to "access", assuming that vlan.tagged is not used.
+ // If vlan.tagged is specified, then we expect it to also change the vlan_mode as needed.
+ if d.config["vlan"] != "none" {
+ _, err := shared.RunCommand("ovs-vsctl", "set", "port", hostName, "vlan_mode=access", fmt.Sprintf("tag=%s", d.config["vlan"]))
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ // Add any tagged VLAN memberships.
+ if d.config["vlan.tagged"] != "" {
+ vlanIDs := strings.Split(d.config["vlan.tagged"], ",")
+
+ // Remove any spaces from raw config string.
+ for i, vlanID := range vlanIDs {
+ vlanIDs[i] = strings.TrimSpace(vlanID)
+ }
+
+ vlanMode := "trunk" // Default to only allowing tagged frames (drop untagged frames).
+ if d.config["vlan"] != "none" {
+ // If untagged vlan mode isn't "none" then allow untagged frames for port's 'native' VLAN.
+ vlanMode = "native-untagged"
+ }
+
+ // Configure the tagged membership settings of the port if VLAN ID specified.
+ // Also set the vlan_mode as needed from above.
+ // Must come after the ovs-vsctl command used for setting "vlan" mode above so that the correct
+ // vlan_mode is retained.
+ _, err := shared.RunCommand("ovs-vsctl", "set", "port", hostName, fmt.Sprintf("vlan_mode=%s", vlanMode), fmt.Sprintf("trunks=%s", strings.Join(vlanIDs, ",")))
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
More information about the lxc-devel
mailing list