[lxc-devel] [lxd/master] Network: Make OVN updates more nuanced and less destructive

tomponline on Github lxc-bot at linuxcontainers.org
Fri Dec 18 18:15:36 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 428 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20201218/c682a49b/attachment-0001.bin>
-------------- next part --------------
From cba9502715754f6ae05cfa8bb4a4dae4e62c8465 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 11:45:16 +0000
Subject: [PATCH 01/16] lxd/network/openvswitch/ovn: Adds mayExist argument to
 LogicalRouterAdd

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/openvswitch/ovn.go | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index d7ad3079ee..86625846c2 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -125,8 +125,14 @@ func (o *OVN) nbctl(args ...string) (string, error) {
 }
 
 // LogicalRouterAdd adds a named logical router.
-func (o *OVN) LogicalRouterAdd(routerName OVNRouter) error {
-	_, err := o.nbctl("lr-add", string(routerName))
+func (o *OVN) LogicalRouterAdd(routerName OVNRouter, mayExist bool) error {
+	args := []string{}
+
+	if mayExist {
+		args = append(args, "--may-exist")
+	}
+
+	_, err := o.nbctl(append(args, "lr-add", string(routerName))...)
 	if err != nil {
 		return err
 	}

From a67d1ed533580a1e7829ca9afd7a99363eba4ce4 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 11:46:13 +0000
Subject: [PATCH 02/16] lxd/network/openvswitch/ovn: Adds mayExist argument to
 LogicalRouterSNATAdd

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/openvswitch/ovn.go | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index 86625846c2..f529d39394 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -151,8 +151,14 @@ func (o OVN) LogicalRouterDelete(routerName OVNRouter) error {
 }
 
 // LogicalRouterSNATAdd adds an SNAT rule to a logical router to translate packets from intNet to extIP.
-func (o *OVN) LogicalRouterSNATAdd(routerName OVNRouter, intNet *net.IPNet, extIP net.IP) error {
-	_, err := o.nbctl("lr-nat-add", string(routerName), "snat", extIP.String(), intNet.String())
+func (o *OVN) LogicalRouterSNATAdd(routerName OVNRouter, intNet *net.IPNet, extIP net.IP, mayExist bool) error {
+	args := []string{}
+
+	if mayExist {
+		args = append(args, "--may-exist")
+	}
+
+	_, err := o.nbctl(append(args, "lr-nat-add", string(routerName), "snat", extIP.String(), intNet.String())...)
 	if err != nil {
 		return err
 	}

From 94fdc43a5e424d0a9ec4d35e70f12af2a1a5dd65 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 11:46:41 +0000
Subject: [PATCH 03/16] lxd/network/openvswitch/ovn: Simplifies
 LogicalRouterRouteAdd

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/openvswitch/ovn.go | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index f529d39394..fa6a409b6c 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -204,8 +204,7 @@ func (o *OVN) LogicalRouterRouteAdd(routerName OVNRouter, destination *net.IPNet
 		args = append(args, "--may-exist")
 	}
 
-	args = append(args, "lr-route-add", string(routerName), destination.String(), nextHop.String())
-	_, err := o.nbctl(args...)
+	_, err := o.nbctl(append(args, "lr-route-add", string(routerName), destination.String(), nextHop.String())...)
 	if err != nil {
 		return err
 	}

From e47204f3c35bc466639591ff1b13d6302e4d6a21 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 11:46:57 +0000
Subject: [PATCH 04/16] lxd/network/openvswitch/ovn: Adds mayExist argument to
 LogicalRouterPortAdd

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/openvswitch/ovn.go | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index fa6a409b6c..f4bd7f186f 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -230,7 +230,29 @@ func (o *OVN) LogicalRouterRouteDelete(routerName OVNRouter, destination *net.IP
 }
 
 // LogicalRouterPortAdd adds a named logical router port to a logical router.
-func (o *OVN) LogicalRouterPortAdd(routerName OVNRouter, portName OVNRouterPort, mac net.HardwareAddr, ipAddr ...*net.IPNet) error {
+func (o *OVN) LogicalRouterPortAdd(routerName OVNRouter, portName OVNRouterPort, mac net.HardwareAddr, ipAddr []*net.IPNet, mayExist bool) error {
+	if mayExist {
+		// Check if it exists and update addresses.
+		_, err := o.nbctl("list", "Logical_Router_Port", string(portName))
+		if err == nil {
+			// Router port exists.
+			ips := make([]string, 0, len(ipAddr))
+			for _, ip := range ipAddr {
+				ips = append(ips, ip.String())
+			}
+
+			_, err := o.nbctl("set", "Logical_Router_Port", string(portName),
+				fmt.Sprintf(`networks="%s"`, strings.Join(ips, `","`)),
+				fmt.Sprintf(`mac="%s"`, fmt.Sprintf(mac.String())),
+			)
+			if err != nil {
+				return err
+			}
+
+			return nil
+		}
+	}
+
 	args := []string{"lrp-add", string(routerName), string(portName), mac.String()}
 	for _, ipNet := range ipAddr {
 		args = append(args, ipNet.String())

From 053db5f5a862163d88b494f58a70e61ac3a26cd2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 11:47:14 +0000
Subject: [PATCH 05/16] lxd/network/openvswitch/ovn: Adds
 LogicalRouterSNATDeleteAll function

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/openvswitch/ovn.go | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index f4bd7f186f..47b2bdcceb 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -166,6 +166,16 @@ func (o *OVN) LogicalRouterSNATAdd(routerName OVNRouter, intNet *net.IPNet, extI
 	return nil
 }
 
+// LogicalRouterSNATDeleteAll deletes all SNAT rules from a logical router.
+func (o *OVN) LogicalRouterSNATDeleteAll(routerName OVNRouter) error {
+	_, err := o.nbctl("--if-exists", "lr-nat-del", string(routerName), "snat")
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 // LogicalRouterDNATSNATAdd adds a DNAT and SNAT rule to a logical router to translate packets from extIP to intIP.
 func (o *OVN) LogicalRouterDNATSNATAdd(routerName OVNRouter, extIP net.IP, intIP net.IP, stateless bool, mayExist bool) error {
 	args := []string{}

From b43527e9d9de2b0ff81d5bfda2fb759be59914e2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 16:30:02 +0000
Subject: [PATCH 06/16] lxd/network/openvswitch/ovn: Clear unused keys in
 LogicalSwitchSetIPAllocation

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/openvswitch/ovn.go | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index 47b2bdcceb..01ba706dc4 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -383,14 +383,19 @@ func (o *OVN) LogicalSwitchDelete(switchName OVNSwitch) error {
 
 // LogicalSwitchSetIPAllocation sets the IP allocation config on the logical switch.
 func (o *OVN) LogicalSwitchSetIPAllocation(switchName OVNSwitch, opts *OVNIPAllocationOpts) error {
+	var removeOtherConfigKeys []string
 	args := []string{"set", "logical_switch", string(switchName)}
 
 	if opts.PrefixIPv4 != nil {
 		args = append(args, fmt.Sprintf("other_config:subnet=%s", opts.PrefixIPv4.String()))
+	} else {
+		removeOtherConfigKeys = append(removeOtherConfigKeys, "subnet")
 	}
 
 	if opts.PrefixIPv6 != nil {
 		args = append(args, fmt.Sprintf("other_config:ipv6_prefix=%s", opts.PrefixIPv6.String()))
+	} else {
+		removeOtherConfigKeys = append(removeOtherConfigKeys, "ipv6_prefix")
 	}
 
 	if len(opts.ExcludeIPv4) > 0 {
@@ -406,6 +411,17 @@ func (o *OVN) LogicalSwitchSetIPAllocation(switchName OVNSwitch, opts *OVNIPAllo
 		}
 
 		args = append(args, fmt.Sprintf("other_config:exclude_ips=%s", strings.Join(excludeIPs, " ")))
+	} else {
+		removeOtherConfigKeys = append(removeOtherConfigKeys, "exclude_ips")
+	}
+
+	// Clear any unused keys first.
+	if len(removeOtherConfigKeys) > 0 {
+		removeArgs := append([]string{"remove", "logical_switch", string(switchName), "other_config"}, removeOtherConfigKeys...)
+		_, err := o.nbctl(removeArgs...)
+		if err != nil {
+			return err
+		}
 	}
 
 	// Only run command if at least one setting is specified.

From 799ce55e8f6a28d9d3ed09aaaeb93c11003a63f5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 17:47:47 +0000
Subject: [PATCH 07/16] lxd/network/openvswitch/ovn: Adds support for clearing
 unused settings in LogicalRouterPortSetIPv6Advertisements

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/openvswitch/ovn.go | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index 01ba706dc4..cbdb7e018e 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -290,27 +290,53 @@ func (o *OVN) LogicalRouterPortDelete(portName OVNRouterPort) error {
 func (o *OVN) LogicalRouterPortSetIPv6Advertisements(portName OVNRouterPort, opts *OVNIPv6RAOpts) error {
 	args := []string{"set", "logical_router_port", string(portName),
 		fmt.Sprintf("ipv6_ra_configs:send_periodic=%t", opts.SendPeriodic),
-		fmt.Sprintf("ipv6_ra_configs:address_mode=%s", string(opts.AddressMode)),
+	}
+
+	var removeRAConfigKeys []string
+
+	if opts.AddressMode != "" {
+		args = append(args, fmt.Sprintf("ipv6_ra_configs:address_mode=%s", string(opts.AddressMode)))
+	} else {
+		removeRAConfigKeys = append(removeRAConfigKeys, "address_mode")
 	}
 
 	if opts.MaxInterval > 0 {
 		args = append(args, fmt.Sprintf("ipv6_ra_configs:max_interval=%d", opts.MaxInterval/time.Second))
+	} else {
+		removeRAConfigKeys = append(removeRAConfigKeys, "max_interval")
 	}
 
 	if opts.MinInterval > 0 {
 		args = append(args, fmt.Sprintf("ipv6_ra_configs:min_interval=%d", opts.MinInterval/time.Second))
+	} else {
+		removeRAConfigKeys = append(removeRAConfigKeys, "min_interval")
 	}
 
 	if opts.MTU > 0 {
 		args = append(args, fmt.Sprintf("ipv6_ra_configs:mtu=%d", opts.MTU))
+	} else {
+		removeRAConfigKeys = append(removeRAConfigKeys, "mtu")
 	}
 
 	if len(opts.DNSSearchList) > 0 {
 		args = append(args, fmt.Sprintf("ipv6_ra_configs:dnssl=%s", strings.Join(opts.DNSSearchList, ",")))
+	} else {
+		removeRAConfigKeys = append(removeRAConfigKeys, "dnssl")
 	}
 
 	if opts.RecursiveDNSServer != nil {
 		args = append(args, fmt.Sprintf("ipv6_ra_configs:rdnss=%s", opts.RecursiveDNSServer.String()))
+	} else {
+		removeRAConfigKeys = append(removeRAConfigKeys, "rdnss")
+	}
+
+	// Clear any unused keys first.
+	if len(removeRAConfigKeys) > 0 {
+		removeArgs := append([]string{"remove", "logical_router_port", string(portName), "ipv6_ra_configs"}, removeRAConfigKeys...)
+		_, err := o.nbctl(removeArgs...)
+		if err != nil {
+			return err
+		}
 	}
 
 	// Configure IPv6 Router Advertisements.

From 0334dfe8dc8dbde5ff9ae9d78c879e9f064209ce Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 17:48:18 +0000
Subject: [PATCH 08/16] lxd/network/openvswitch/ovn: Adds
 LogicalRouterPortDeleteIPv6Advertisements function

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/openvswitch/ovn.go | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/lxd/network/openvswitch/ovn.go b/lxd/network/openvswitch/ovn.go
index cbdb7e018e..a263bb5ca4 100644
--- a/lxd/network/openvswitch/ovn.go
+++ b/lxd/network/openvswitch/ovn.go
@@ -348,6 +348,17 @@ func (o *OVN) LogicalRouterPortSetIPv6Advertisements(portName OVNRouterPort, opt
 	return nil
 }
 
+// LogicalRouterPortDeleteIPv6Advertisements removes the IPv6 RA announcement settings from a router port.
+func (o *OVN) LogicalRouterPortDeleteIPv6Advertisements(portName OVNRouterPort) error {
+	// Delete IPv6 Router Advertisements.
+	_, err := o.nbctl("clear", "logical_router_port", string(portName), "ipv6_ra_configs")
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 // LogicalRouterPortLinkChassisGroup links a logical router port to a HA chassis group.
 func (o *OVN) LogicalRouterPortLinkChassisGroup(portName OVNRouterPort, haChassisGroupName OVNChassisGroup) error {
 	chassisGroupID, err := o.nbctl("--format=csv", "--no-headings", "--data=bare", "--colum=_uuid", "find", "ha_chassis_group", fmt.Sprintf("name=%s", haChassisGroupName))

From 271257c7c3dc04495a1abfc1ed080ae08840342c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 17:52:22 +0000
Subject: [PATCH 09/16] lxd/network/driver/ovn: Enforce that ipv6.address if
 specified is at least a /64 subnet

OVN requires at least a /64 for DHCP and SLAAC.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_ovn.go | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index db9b7366d2..f9df44f31a 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -205,6 +205,16 @@ func (n *ovn) Validate(config map[string]string) error {
 		return err
 	}
 
+	// Check that if IPv6 enabled then the network size must be at least a /64 as both RA and DHCPv6
+	// in OVN (as it generates addresses using EUI64) require at least a /64 subnet to operate.
+	_, ipv6Net, _ := net.ParseCIDR(config["ipv6.address"])
+	if ipv6Net != nil {
+		ones, _ := ipv6Net.Mask.Size()
+		if ones > 64 {
+			return fmt.Errorf("IPv6 subnet must be at least a /64")
+		}
+	}
+
 	// Load the project to get uplink network restrictions.
 	p, err := n.state.Cluster.GetProject(n.project)
 	if err != nil {

From 87218ebf8aef6308a4cfaf11da4a5fd80e725e63 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 17:55:01 +0000
Subject: [PATCH 10/16] lxd/network/driver/ovn: Pass update flag to mayExist
 where possible

And don't delete on failure when updating.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_ovn.go | 62 ++++++++++++++++++++++++---------------
 1 file changed, 38 insertions(+), 24 deletions(-)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index f9df44f31a..f9b5d47573 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1557,18 +1557,19 @@ func (n *ovn) setup(update bool) error {
 		return err
 	}
 
-	revert.Add(func() { client.ChassisGroupDelete(n.getChassisGroupName()) })
-
-	// Create logical router.
-	if update {
-		client.LogicalRouterDelete(n.getRouterName())
+	if !update {
+		revert.Add(func() { client.ChassisGroupDelete(n.getChassisGroupName()) })
 	}
 
-	err = client.LogicalRouterAdd(n.getRouterName())
+	// Create logical router.
+	err = client.LogicalRouterAdd(n.getRouterName(), update)
 	if err != nil {
 		return errors.Wrapf(err, "Failed adding router")
 	}
-	revert.Add(func() { client.LogicalRouterDelete(n.getRouterName()) })
+
+	if !update {
+		revert.Add(func() { client.LogicalRouterDelete(n.getRouterName()) })
+	}
 
 	// Configure logical router.
 
@@ -1589,23 +1590,24 @@ func (n *ovn) setup(update bool) error {
 	}
 
 	if len(extRouterIPs) > 0 {
-		// Create external logical switch.
-		if update {
-			client.LogicalSwitchDelete(n.getExtSwitchName())
-		}
-
-		err = client.LogicalSwitchAdd(n.getExtSwitchName(), false)
+		err = client.LogicalSwitchAdd(n.getExtSwitchName(), update)
 		if err != nil {
 			return errors.Wrapf(err, "Failed adding external switch")
 		}
-		revert.Add(func() { client.LogicalSwitchDelete(n.getExtSwitchName()) })
+
+		if !update {
+			revert.Add(func() { client.LogicalSwitchDelete(n.getExtSwitchName()) })
+		}
 
 		// Create external router port.
-		err = client.LogicalRouterPortAdd(n.getRouterName(), n.getRouterExtPortName(), routerMAC, extRouterIPs...)
+		err = client.LogicalRouterPortAdd(n.getRouterName(), n.getRouterExtPortName(), routerMAC, extRouterIPs, update)
 		if err != nil {
 			return errors.Wrapf(err, "Failed adding external router port")
 		}
-		revert.Add(func() { client.LogicalRouterPortDelete(n.getRouterExtPortName()) })
+
+		if !update {
+			revert.Add(func() { client.LogicalRouterPortDelete(n.getRouterExtPortName()) })
+		}
 
 		// Associate external router port to chassis group.
 		err = client.LogicalRouterPortLinkChassisGroup(n.getRouterExtPortName(), n.getChassisGroupName())
@@ -1614,11 +1616,14 @@ func (n *ovn) setup(update bool) error {
 		}
 
 		// Create external switch port and link to router port.
-		err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), n.getExtSwitchRouterPortName(), false)
+		err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), n.getExtSwitchRouterPortName(), update)
 		if err != nil {
 			return errors.Wrapf(err, "Failed adding external switch router port")
 		}
-		revert.Add(func() { client.LogicalSwitchPortDelete(n.getExtSwitchRouterPortName()) })
+
+		if !update {
+			revert.Add(func() { client.LogicalSwitchPortDelete(n.getExtSwitchRouterPortName()) })
+		}
 
 		err = client.LogicalSwitchPortLinkRouter(n.getExtSwitchRouterPortName(), n.getRouterExtPortName())
 		if err != nil {
@@ -1626,11 +1631,14 @@ func (n *ovn) setup(update bool) error {
 		}
 
 		// Create external switch port and link to external provider network.
-		err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), n.getExtSwitchProviderPortName(), false)
+		err = client.LogicalSwitchPortAdd(n.getExtSwitchName(), n.getExtSwitchProviderPortName(), update)
 		if err != nil {
 			return errors.Wrapf(err, "Failed adding external switch provider port")
 		}
-		revert.Add(func() { client.LogicalSwitchPortDelete(n.getExtSwitchProviderPortName()) })
+
+		if !update {
+			revert.Add(func() { client.LogicalSwitchPortDelete(n.getExtSwitchProviderPortName()) })
+		}
 
 		err = client.LogicalSwitchPortLinkProviderNetwork(n.getExtSwitchProviderPortName(), uplinkNet.extSwitchProviderName)
 		if err != nil {
@@ -1639,14 +1647,14 @@ func (n *ovn) setup(update bool) error {
 
 		// Add SNAT rules.
 		if shared.IsTrue(n.config["ipv4.nat"]) && routerIntPortIPv4Net != nil && routerExtPortIPv4 != nil {
-			err = client.LogicalRouterSNATAdd(n.getRouterName(), routerIntPortIPv4Net, routerExtPortIPv4)
+			err = client.LogicalRouterSNATAdd(n.getRouterName(), routerIntPortIPv4Net, routerExtPortIPv4, update)
 			if err != nil {
 				return err
 			}
 		}
 
 		if shared.IsTrue(n.config["ipv6.nat"]) && routerIntPortIPv6Net != nil && routerExtPortIPv6 != nil {
-			err = client.LogicalRouterSNATAdd(n.getRouterName(), routerIntPortIPv6Net, routerExtPortIPv6)
+			err = client.LogicalRouterSNATAdd(n.getRouterName(), routerIntPortIPv6Net, routerExtPortIPv6, update)
 			if err != nil {
 				return err
 			}
@@ -1673,7 +1681,10 @@ func (n *ovn) setup(update bool) error {
 	if err != nil {
 		return errors.Wrapf(err, "Failed adding internal switch")
 	}
-	revert.Add(func() { client.LogicalSwitchDelete(n.getIntSwitchName()) })
+
+	if !update {
+		revert.Add(func() { client.LogicalSwitchDelete(n.getIntSwitchName()) })
+	}
 
 	var excludeIPV4 []shared.IPRange
 	if routerIntPortIPv4 != nil {
@@ -1821,7 +1832,10 @@ func (n *ovn) setup(update bool) error {
 	if err != nil {
 		return errors.Wrapf(err, "Failed adding internal switch router port")
 	}
-	revert.Add(func() { client.LogicalSwitchPortDelete(n.getIntSwitchRouterPortName()) })
+
+	if !update {
+		revert.Add(func() { client.LogicalSwitchPortDelete(n.getIntSwitchRouterPortName()) })
+	}
 
 	err = client.LogicalSwitchPortLinkRouter(n.getIntSwitchRouterPortName(), n.getRouterIntPortName())
 	if err != nil {

From c58ccecb18c04fc973a879989dff9b49c77593ef Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 17:55:51 +0000
Subject: [PATCH 11/16] lxd/network/driver/ovn: Delete SNAT rules from route
 before adding new ones

This ensures that during an update, old SNAT rules are removed.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_ovn.go | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index f9b5d47573..e4a39b519b 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1645,6 +1645,15 @@ func (n *ovn) setup(update bool) error {
 			return errors.Wrapf(err, "Failed linking external switch provider port to external provider network")
 		}
 
+		// Remove any existing SNAT rules on update. As currently these are only defined from the network
+		// config rather than from any instance NIC config, so we can re-create the active config below.
+		if update {
+			err = client.LogicalRouterSNATDeleteAll(n.getRouterName())
+			if err != nil {
+				return errors.Wrapf(err, "Failed removing existing router SNAT rules")
+			}
+		}
+
 		// Add SNAT rules.
 		if shared.IsTrue(n.config["ipv4.nat"]) && routerIntPortIPv4Net != nil && routerExtPortIPv4 != nil {
 			err = client.LogicalRouterSNATAdd(n.getRouterName(), routerIntPortIPv4Net, routerExtPortIPv4, update)

From cb9db30dbd7848b2f4c14ce636ab4bbb9c5e2f51 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 17:56:24 +0000
Subject: [PATCH 12/16] lxd/network/driver/ovn: Improve SNAT failure errors

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_ovn.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index e4a39b519b..1c0439f3a1 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1658,14 +1658,14 @@ func (n *ovn) setup(update bool) error {
 		if shared.IsTrue(n.config["ipv4.nat"]) && routerIntPortIPv4Net != nil && routerExtPortIPv4 != nil {
 			err = client.LogicalRouterSNATAdd(n.getRouterName(), routerIntPortIPv4Net, routerExtPortIPv4, update)
 			if err != nil {
-				return err
+				return errors.Wrapf(err, "Failed adding router IPv4 SNAT rule")
 			}
 		}
 
 		if shared.IsTrue(n.config["ipv6.nat"]) && routerIntPortIPv6Net != nil && routerExtPortIPv6 != nil {
 			err = client.LogicalRouterSNATAdd(n.getRouterName(), routerIntPortIPv6Net, routerExtPortIPv6, update)
 			if err != nil {
-				return err
+				return errors.Wrapf(err, "Failed adding router IPv6 SNAT rule")
 			}
 		}
 

From 0455da20308446fa359d010aee1b301f58e6f14d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 17:57:00 +0000
Subject: [PATCH 13/16] lxd/network/driver/ovn: Pass update to mayExists when
 setting up default routes

And remove default routes when IP protocol address isn't enabled.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_ovn.go | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 1c0439f3a1..f386d3a6c9 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1669,19 +1669,31 @@ func (n *ovn) setup(update bool) error {
 			}
 		}
 
-		// Add default routes.
+		// Add or remove default routes as config dictates.
+		defaultIPv4Route := &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}
 		if uplinkNet.routerExtGwIPv4 != nil {
-			err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}, uplinkNet.routerExtGwIPv4, false)
+			err = client.LogicalRouterRouteAdd(n.getRouterName(), defaultIPv4Route, uplinkNet.routerExtGwIPv4, update)
 			if err != nil {
 				return errors.Wrapf(err, "Failed adding IPv4 default route")
 			}
+		} else if update {
+			err = client.LogicalRouterRouteDelete(n.getRouterName(), defaultIPv4Route, nil)
+			if err != nil {
+				return errors.Wrapf(err, "Failed removing IPv4 default route")
+			}
 		}
 
+		defaultIPv6Route := &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}
 		if uplinkNet.routerExtGwIPv6 != nil {
-			err = client.LogicalRouterRouteAdd(n.getRouterName(), &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}, uplinkNet.routerExtGwIPv6, false)
+			err = client.LogicalRouterRouteAdd(n.getRouterName(), defaultIPv6Route, uplinkNet.routerExtGwIPv6, update)
 			if err != nil {
 				return errors.Wrapf(err, "Failed adding IPv6 default route")
 			}
+		} else if update {
+			err = client.LogicalRouterRouteDelete(n.getRouterName(), defaultIPv6Route, nil)
+			if err != nil {
+				return errors.Wrapf(err, "Failed removing IPv6 default route")
+			}
 		}
 	}
 

From acf0f3da29e98fb94c67636786a1e395c35b454a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 17:58:20 +0000
Subject: [PATCH 14/16] lxd/network/driver/ovn: Create internal router port
 before DHCP option setup

Keeps DHCP setup steps together.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_ovn.go | 60 +++++++++++++++++++++------------------
 1 file changed, 32 insertions(+), 28 deletions(-)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index f386d3a6c9..6ce441e752 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1722,6 +1722,38 @@ func (n *ovn) setup(update bool) error {
 		return errors.Wrapf(err, "Failed setting IP allocation settings on internal switch")
 	}
 
+	// Gather internal router port IPs (in CIDR format).
+	intRouterIPs := []*net.IPNet{}
+
+	if routerIntPortIPv4Net != nil {
+		intRouterIPs = append(intRouterIPs, &net.IPNet{
+			IP:   routerIntPortIPv4,
+			Mask: routerIntPortIPv4Net.Mask,
+		})
+	}
+
+	if routerIntPortIPv6Net != nil {
+		intRouterIPs = append(intRouterIPs, &net.IPNet{
+			IP:   routerIntPortIPv6,
+			Mask: routerIntPortIPv6Net.Mask,
+		})
+	}
+
+	if len(intRouterIPs) <= 0 {
+		return fmt.Errorf("No internal IPs defined for network router")
+	}
+
+	// Create internal router port.
+	err = client.LogicalRouterPortAdd(n.getRouterName(), n.getRouterIntPortName(), routerMAC, intRouterIPs, update)
+	if err != nil {
+		return errors.Wrapf(err, "Failed adding internal router port")
+	}
+
+	if !update {
+		revert.Add(func() { client.LogicalRouterPortDelete(n.getRouterIntPortName()) })
+	}
+
+	// Configure DHCP option sets.
 	var dhcpv4UUID, dhcpv6UUID string
 	dhcpV4Subnet := n.DHCPv4Subnet()
 	dhcpV6Subnet := n.DHCPv6Subnet()
@@ -1763,27 +1795,6 @@ func (n *ovn) setup(update bool) error {
 		}
 	}
 
-	// Internal router port IPs (in CIDR format).
-	intRouterIPs := []*net.IPNet{}
-
-	if routerIntPortIPv4Net != nil {
-		intRouterIPs = append(intRouterIPs, &net.IPNet{
-			IP:   routerIntPortIPv4,
-			Mask: routerIntPortIPv4Net.Mask,
-		})
-	}
-
-	if routerIntPortIPv6Net != nil {
-		intRouterIPs = append(intRouterIPs, &net.IPNet{
-			IP:   routerIntPortIPv6,
-			Mask: routerIntPortIPv6Net.Mask,
-		})
-	}
-
-	if len(intRouterIPs) <= 0 {
-		return fmt.Errorf("No IPs defined for network router")
-	}
-
 	// Create DHCPv4 options for internal switch.
 	if dhcpV4Subnet != nil {
 		err = client.LogicalSwitchDHCPv4OptionsSet(n.getIntSwitchName(), dhcpv4UUID, dhcpV4Subnet, &openvswitch.OVNDHCPv4Opts{
@@ -1812,13 +1823,6 @@ func (n *ovn) setup(update bool) error {
 		}
 	}
 
-	// Create internal router port.
-	err = client.LogicalRouterPortAdd(n.getRouterName(), n.getRouterIntPortName(), routerMAC, intRouterIPs...)
-	if err != nil {
-		return errors.Wrapf(err, "Failed adding internal router port")
-	}
-	revert.Add(func() { client.LogicalRouterPortDelete(n.getRouterIntPortName()) })
-
 	// Set IPv6 router advertisement settings.
 	if dhcpV6Subnet != nil {
 		adressMode := openvswitch.OVNIPv6AddressModeSLAAC

From 2b3cf479b84ce250ea8b90890fb5ade64e3ff1db Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 18:12:33 +0000
Subject: [PATCH 15/16] lxd/network/driver/ovn: Modifies IPv6 RA settings and
 removes them entirely when IPv6 disabled

Settings change to:

 - ipv6.dhcp = false
	RA with slaac
 - ipv6.dhcp = true
	RA with stateless DHCP
 - ipv6.dhcp = true and ipv6.dhcp.stateful = true
	RA with stateful DHCP

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_ovn.go | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 6ce441e752..630ae3c914 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1824,10 +1824,13 @@ func (n *ovn) setup(update bool) error {
 	}
 
 	// Set IPv6 router advertisement settings.
-	if dhcpV6Subnet != nil {
+	if routerIntPortIPv6Net != nil {
 		adressMode := openvswitch.OVNIPv6AddressModeSLAAC
-		if shared.IsTrue(n.config["ipv6.dhcp.stateful"]) {
-			adressMode = openvswitch.OVNIPv6AddressModeDHCPStateful
+		if dhcpV6Subnet != nil {
+			adressMode = openvswitch.OVNIPv6AddressModeDHCPStateless
+			if shared.IsTrue(n.config["ipv6.dhcp.stateful"]) {
+				adressMode = openvswitch.OVNIPv6AddressModeDHCPStateful
+			}
 		}
 
 		var recursiveDNSServer net.IP
@@ -1850,6 +1853,11 @@ func (n *ovn) setup(update bool) error {
 		if err != nil {
 			return errors.Wrapf(err, "Failed setting internal router port IPv6 advertisement settings")
 		}
+	} else {
+		err = client.LogicalRouterPortDeleteIPv6Advertisements(n.getRouterIntPortName())
+		if err != nil {
+			return errors.Wrapf(err, "Failed removing internal router port IPv6 advertisement settings")
+		}
 	}
 
 	// Create internal switch port and link to router port.

From f486c839c6f9cc884ba40d3cc67031aab69dbe0f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 18 Dec 2020 18:14:07 +0000
Subject: [PATCH 16/16] lxd/network/driver/ovn: Don't return DHCPv6 subnet if
 IPv6 prefix smaller than /64

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/network/driver_ovn.go | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 630ae3c914..6f2d9d7d39 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -2585,6 +2585,11 @@ func (n *ovn) DHCPv6Subnet() *net.IPNet {
 		return nil
 	}
 
+	ones, _ := subnet.Mask.Size()
+	if ones > 64 {
+		return nil // OVN only supports DHCPv6 allocated using EUI64 (which requires at least a /64).
+	}
+
 	return subnet
 }
 


More information about the lxc-devel mailing list