From 498b0b0a6f2efec4b6e5dbe81aa624a8f9d5654e Mon Sep 17 00:00:00 2001 From: Mark Bolton Date: Thu, 3 Oct 2024 01:33:32 -0700 Subject: [PATCH] lxd/network: Configure necessary ports for VLAN handling Signed-off-by: Mark Bolton --- lxd/network/driver_ovn.go | 72 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 640c93450d8b..18a11ee00aad 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1430,6 +1430,52 @@ func (n *ovn) startUplinkPort() error { return fmt.Errorf("Failed starting uplink port, network type %q unsupported as OVN uplink", uplinkNet.Type()) } +// configurePortsForVLAN configures the necessary ports to handle VLAN traffic between +// the uplink network and OVN. +func (n *ovn) configurePortsForVLAN(vlan string, update bool) error { + if vlan == "" || update && n.config["vlan"] == vlan { + return nil + } + + uplinkNet, err := LoadByName(n.state, api.ProjectDefaultName, n.config["network"]) + if err != nil { + return err + } + + ovs := openvswitch.NewOVS() + vars := n.uplinkPortBridgeVars(uplinkNet) + uplinkHostName := GetHostDevice(uplinkNet.Config()["parent"], uplinkNet.Config()["vlan"]) + portName := uplinkHostName + + // If uplink is a native linux bridge, set the VID of the uplink end of the veth interface. + if uplinkNet.Type() == "bridge" || IsNativeBridge(uplinkHostName) { + portName = vars.ovsEnd + + // If we are updating the VLAN ID, ensure we remove the old filter entry from the uplink end of the veth interface. + link := &ip.Link{Name: vars.uplinkEnd} + if update { + err = link.BridgeVLANDelete(n.Config()["vlan"], false) + if err != nil { + return fmt.Errorf("Failed to remove old VLAN filter entry for uplink veth interface %q: %w", vars.uplinkEnd, err) + } + } + + // Add VLAN filter entry to the uplink end of the veth interface. + err = link.BridgeVLANAdd(vlan, false, false, false) + if err != nil { + return fmt.Errorf("Failed to configure VLAN for uplink veth interface %q: %w", vars.uplinkEnd, err) + } + } + + // Configure vlan_mode and trunk on the OVS end of the veth interface. + err = ovs.BridgePortSet(portName, "vlan_mode=trunk", fmt.Sprintf("trunk=%s", vlan)) + if err != nil { + return fmt.Errorf("Failed to configure VLAN for uplink veth interface %q on OVS bridge %q: %w", portName, vars.ovsBridge, err) + } + + return err +} + // uplinkOperationLockName returns the lock name to use for operations on the uplink network. func (n *ovn) uplinkOperationLockName(uplinkNet Network) string { return fmt.Sprintf("network.ovn.%s", uplinkNet.Name()) @@ -2302,8 +2348,19 @@ func (n *ovn) setup(update bool) error { return fmt.Errorf("Failed linking external router port to external switch port: %w", err) } + // If VLAN is provided, ensure localnet port is properly tagged. + localnetOpts := &openvswitch.OVNSwitchPortOpts{} + if n.config["vlan"] != "" { + vlan, err := strconv.ParseUint(n.Config()["vlan"], 10, 16) + if err != nil { + return fmt.Errorf("Invalid VLAN ID %q: %w", n.Config()["vlan"], err) + } + + localnetOpts.VLAN = uint16(vlan) + } + // Create external switch port and link to external provider network. - err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), n.getExtSwitchProviderPortName(), nil, update) + err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), n.getExtSwitchProviderPortName(), localnetOpts, update) if err != nil { return fmt.Errorf("Failed adding external switch provider port: %w", err) } @@ -3104,6 +3161,13 @@ func (n *ovn) Start() error { return err } + // If VLAN config is provided to the OVN network, configure the necessary ports + // to handle traffic on the specified VLAN. + err = n.configurePortsForVLAN(n.config["vlan"], false) + if err != nil { + return err + } + // Setup BGP. err = n.bgpSetup(nil) if err != nil { @@ -3218,6 +3282,12 @@ func (n *ovn) Update(newNetwork api.NetworkPut, targetNode string, clientType re delete(newNetwork.Config, ovnVolatileUplinkIPv6) } + // Update port configurations for VLAN if necessary. + err = n.configurePortsForVLAN(newNetwork.Config["vlan"], true) + if err != nil { + return err + } + // Apply changes to all nodes and databse. err = n.common.update(newNetwork, targetNode, clientType) if err != nil {