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

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) ===
Add `ovn.ingress_mode` (either `l2proxy` (default) or `routed`) on `physical` networks to allow OVN NICs to change the way they advertise their external IPs on the uplink network.
From 4ec52f656bb1c178a38f1bcffb489f12ad644a10 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 9 Dec 2020 09:24:48 +0000
Subject: [PATCH 1/7] lxd/network/driver/ovn: Improve error message

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index e222099fee..3dc4db91bb 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1411,7 +1411,7 @@ func (n *ovn) setup(update bool) error {
                err := n.state.Cluster.Transaction(func(tx *db.ClusterTx) error 
{
                        err = tx.UpdateNetwork(n.id, n.description, n.config)
                        if err != nil {
-                               return errors.Wrapf(err, "Failed saving optimal 
bridge MTU")
+                               return errors.Wrapf(err, "Failed saving updated 
network config")
                        }
 
                        return nil

From 7cbdd62e06db2b2758a4d2b7ad620722fdc3f719 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 9 Dec 2020 10:04:37 +0000
Subject: [PATCH 2/7] lxd/network/driver/physical: Adds ovn.ingress_mode config
 key

Allows specifying how external OVN NIC IPs are advertised to the uplink; either 
"l2proxy" (default) or "routed".

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

diff --git a/lxd/network/driver_physical.go b/lxd/network/driver_physical.go
index 19d0001a57..99a8be7f11 100644
--- a/lxd/network/driver_physical.go
+++ b/lxd/network/driver_physical.go
@@ -34,18 +34,21 @@ func (n *physical) DBType() db.NetworkType {
 // Validate network config.
 func (n *physical) Validate(config map[string]string) error {
        rules := map[string]func(value string) error{
-               "parent":                      
validate.Required(validate.IsNotEmpty, validInterfaceName),
-               "mtu":                         
validate.Optional(validate.IsNetworkMTU),
-               "vlan":                        
validate.Optional(validate.IsNetworkVLAN),
-               "maas.subnet.ipv4":            validate.IsAny,
-               "maas.subnet.ipv6":            validate.IsAny,
-               "ipv4.gateway":                
validate.Optional(validate.IsNetworkAddressCIDRV4),
-               "ipv6.gateway":                
validate.Optional(validate.IsNetworkAddressCIDRV6),
-               "ipv4.ovn.ranges":             
validate.Optional(validate.IsNetworkRangeV4List),
-               "ipv6.ovn.ranges":             
validate.Optional(validate.IsNetworkRangeV6List),
-               "ipv4.routes":                 
validate.Optional(validate.IsNetworkV4List),
-               "ipv6.routes":                 
validate.Optional(validate.IsNetworkV6List),
-               "dns.nameservers":             
validate.Optional(validate.IsNetworkAddressList),
+               "parent":           validate.Required(validate.IsNotEmpty, 
validInterfaceName),
+               "mtu":              validate.Optional(validate.IsNetworkMTU),
+               "vlan":             validate.Optional(validate.IsNetworkVLAN),
+               "maas.subnet.ipv4": validate.IsAny,
+               "maas.subnet.ipv6": validate.IsAny,
+               "ipv4.gateway":     
validate.Optional(validate.IsNetworkAddressCIDRV4),
+               "ipv6.gateway":     
validate.Optional(validate.IsNetworkAddressCIDRV6),
+               "ipv4.ovn.ranges":  
validate.Optional(validate.IsNetworkRangeV4List),
+               "ipv6.ovn.ranges":  
validate.Optional(validate.IsNetworkRangeV6List),
+               "ipv4.routes":      validate.Optional(validate.IsNetworkV4List),
+               "ipv6.routes":      validate.Optional(validate.IsNetworkV6List),
+               "dns.nameservers":  
validate.Optional(validate.IsNetworkAddressList),
+               "ovn.ingress_mode": validate.Optional(func(value string) error {
+                       return validate.IsOneOf(value, []string{"l2proxy", 
"routed"})
+               }),
                "volatile.last_state.created": 
validate.Optional(validate.IsBool),
        }
 

From 0e1537b591c9f317a47523eca3328c7bac5e058c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 9 Dec 2020 10:05:39 +0000
Subject: [PATCH 3/7] lxd/network/driver/ovn: Updates uplinkRoutes to accept an
 *api.Network argument

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 3dc4db91bb..a20cbcd9da 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -81,13 +81,9 @@ func (n *ovn) Info() Info {
        }
 }
 
-// uplinkRoutes parses ipv4.routes and ipv6.routes settings for a named uplink 
network into a slice of *net.IPNet.
-func (n *ovn) uplinkRoutes(uplinkNetworkName string) ([]*net.IPNet, error) {
-       _, uplink, _, err := 
n.state.Cluster.GetNetworkInAnyState(project.Default, uplinkNetworkName)
-       if err != nil {
-               return nil, err
-       }
-
+// uplinkRoutes parses ipv4.routes and ipv6.routes settings for an uplink 
network into a slice of *net.IPNet.
+func (n *ovn) uplinkRoutes(uplink *api.Network) ([]*net.IPNet, error) {
+       var err error
        var uplinkRoutes []*net.IPNet
        for _, k := range []string{"ipv4.routes", "ipv6.routes"} {
                if uplink.Config[k] == "" {

From dce5376a83b39d371907e397c6390617e12cc347 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 9 Dec 2020 10:06:07 +0000
Subject: [PATCH 4/7] lxd/network/driver/ovn: n.uplinkRoutes usage

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/network/driver_ovn.go | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index a20cbcd9da..3b36606b9b 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -216,7 +216,12 @@ func (n *ovn) Validate(config map[string]string) error {
        }
 
        // Get uplink routes.
-       uplinkRoutes, err := n.uplinkRoutes(uplinkNetworkName)
+       _, uplink, _, err := 
n.state.Cluster.GetNetworkInAnyState(project.Default, uplinkNetworkName)
+       if err != nil {
+               return errors.Wrapf(err, "Failed to load uplink network %q", 
uplinkNetworkName)
+       }
+
+       uplinkRoutes, err := n.uplinkRoutes(uplink)
        if err != nil {
                return err
        }

From 20ea6d892ecbc259d7c0e0f41bcb5988fb489dea Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 9 Dec 2020 10:06:34 +0000
Subject: [PATCH 5/7] lxd/network/driver/ovn: Moves subnet size validation into
 InstanceDevicePortValidateExternalRoutes

And makes subnet size validation conditional on uplink's `ovn.ingress_node` 
setting being in l2proxy mode.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/network/driver_ovn.go | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 3b36606b9b..b6a444cd72 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -1974,11 +1974,28 @@ func (n *ovn) 
InstanceDevicePortValidateExternalRoutes(deviceInstance instance.I
        var projectNetworks map[string]map[int64]api.Network
 
        // Get uplink routes.
-       uplinkRoutes, err := n.uplinkRoutes(n.config["network"])
+       _, uplink, _, err := 
n.state.Cluster.GetNetworkInAnyState(project.Default, n.config["network"])
+       if err != nil {
+               return errors.Wrapf(err, "Failed to load uplink network %q", 
n.config["network"])
+       }
+
+       uplinkRoutes, err := n.uplinkRoutes(uplink)
        if err != nil {
                return err
        }
 
+       // Check port's external routes are suffciently small when using 
l2proxy ingress mode on uplink.
+       if shared.StringInSlice(uplink.Config["ovn.ingress_mode"], 
[]string{"l2proxy", ""}) {
+               for _, portExternalRoute := range portExternalRoutes {
+                       rOnes, rBits := portExternalRoute.Mask.Size()
+                       if rBits > 32 && rOnes < 122 {
+                               return fmt.Errorf("External route %q is too 
large. Maximum size for IPv6 external route is /122", 
portExternalRoute.String())
+                       } else if rOnes < 26 {
+                               return fmt.Errorf("External route %q is too 
large. Maximum size for IPv4 external route is /26", portExternalRoute.String())
+                       }
+               }
+       }
+
        err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
                // Load the project to get uplink network restrictions.
                p, err = tx.GetProject(n.project)

From 04e8ff8310be9061be802815bf8f19e3a34da0b8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 9 Dec 2020 10:07:43 +0000
Subject: [PATCH 6/7] lxd/network/driver/ovn: Updates InstanceDevicePortAdd to
 only publish external IPs using DNAT when uplink l2proxy mode enabled

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/network/driver_ovn.go | 79 ++++++++++++++++++++++-----------------
 1 file changed, 45 insertions(+), 34 deletions(-)

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index b6a444cd72..0984080469 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -2087,6 +2087,12 @@ func (n *ovn) InstanceDevicePortAdd(instanceUUID string, 
instanceName string, de
                return "", err
        }
 
+       // Load uplink network config.
+       _, uplink, _, err := 
n.state.Cluster.GetNetworkInAnyState(project.Default, n.config["network"])
+       if err != nil {
+               return "", errors.Wrapf(err, "Failed to load uplink network 
%q", n.config["network"])
+       }
+
        // Get DHCP options IDs.
        if validate.IsOneOf(n.getRouterIntPortIPv4Net(), []string{"none", ""}) 
!= nil {
                _, routerIntPortIPv4Net, err := 
net.ParseCIDR(n.getRouterIntPortIPv4Net())
@@ -2180,30 +2186,32 @@ func (n *ovn) InstanceDevicePortAdd(instanceUUID 
string, instanceName string, de
 
        revert.Add(func() { 
client.LogicalSwitchPortDeleteDNS(n.getIntSwitchName(), dnsUUID) })
 
-       // Publish NIC's IPs on uplink network if NAT is disabled.
-       for _, k := range []string{"ipv4.nat", "ipv6.nat"} {
-               if shared.IsTrue(n.config[k]) {
-                       continue
-               }
+       // Publish NIC's IPs on uplink network if NAT is disabled and using 
l2proxy ingress mode on uplink.
+       if shared.StringInSlice(uplink.Config["ovn.ingress_mode"], 
[]string{"l2proxy", ""}) {
+               for _, k := range []string{"ipv4.nat", "ipv6.nat"} {
+                       if shared.IsTrue(n.config[k]) {
+                               continue
+                       }
 
-               // Select the correct destination IP from the DNS records.
-               var ip net.IP
-               if k == "ipv4.nat" {
-                       ip = dnsIPv4
-               } else if k == "ipv6.nat" {
-                       ip = dnsIPv6
-               }
+                       // Select the correct destination IP from the DNS 
records.
+                       var ip net.IP
+                       if k == "ipv4.nat" {
+                               ip = dnsIPv4
+                       } else if k == "ipv6.nat" {
+                               ip = dnsIPv6
+                       }
 
-               if ip == nil {
-                       continue //No qualifying target IP from DNS records.
-               }
+                       if ip == nil {
+                               continue //No qualifying target IP from DNS 
records.
+                       }
 
-               err = client.LogicalRouterDNATSNATAdd(n.getRouterName(), ip, 
ip, true, true)
-               if err != nil {
-                       return "", err
-               }
+                       err = 
client.LogicalRouterDNATSNATAdd(n.getRouterName(), ip, ip, true, true)
+                       if err != nil {
+                               return "", err
+                       }
 
-               revert.Add(func() { 
client.LogicalRouterDNATSNATDelete(n.getRouterName(), ip) })
+                       revert.Add(func() { 
client.LogicalRouterDNATSNATDelete(n.getRouterName(), ip) })
+               }
        }
 
        // Add each internal route (using the IPs set for DNS as target).
@@ -2243,22 +2251,25 @@ func (n *ovn) InstanceDevicePortAdd(instanceUUID 
string, instanceName string, de
 
                revert.Add(func() { 
client.LogicalRouterRouteDelete(n.getRouterName(), externalRoute, targetIP) })
 
-               // In order to advertise the external route to the uplink 
network using proxy ARP/NDP we need to
-               // add a stateless dnat_and_snat rule (as to my knowledge this 
is the only way to get the OVN
-               // router to respond to ARP/NDP requests for IPs that it 
doesn't actually have). However we have
-               // to add each IP in the external route individually as DNAT 
doesn't support whole subnets.
-               err = SubnetIterate(externalRoute, func(ip net.IP) error {
-                       err = 
client.LogicalRouterDNATSNATAdd(n.getRouterName(), ip, ip, true, true)
-                       if err != nil {
-                               return err
-                       }
+               // When using l2proxy ingress mode on uplink, in order to 
advertise the external route to the
+               // uplink network using proxy ARP/NDP we need to add a 
stateless dnat_and_snat rule (as to my
+               // knowledge this is the only way to get the OVN router to 
respond to ARP/NDP requests for IPs that
+               // it doesn't actually have). However we have to add each IP in 
the external route individually as
+               // DNAT doesn't support whole subnets.
+               if shared.StringInSlice(uplink.Config["ovn.ingress_mode"], 
[]string{"l2proxy", ""}) {
+                       err = SubnetIterate(externalRoute, func(ip net.IP) 
error {
+                               err = 
client.LogicalRouterDNATSNATAdd(n.getRouterName(), ip, ip, true, true)
+                               if err != nil {
+                                       return err
+                               }
 
-                       revert.Add(func() { 
client.LogicalRouterDNATSNATDelete(n.getRouterName(), ip) })
+                               revert.Add(func() { 
client.LogicalRouterDNATSNATDelete(n.getRouterName(), ip) })
 
-                       return nil
-               })
-               if err != nil {
-                       return "", err
+                               return nil
+                       })
+                       if err != nil {
+                               return "", err
+                       }
                }
        }
 

From dcfcb0ba11dcd2fea7a72b7a64b996af7f4e04b0 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 9 Dec 2020 10:08:21 +0000
Subject: [PATCH 7/7] lxd/device/nic/ovn: Removes external subnet validation

Now done by d.network.InstanceDevicePortValidateExternalRoutes.

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

diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go
index 415f5271cc..a7cf93d04f 100644
--- a/lxd/device/nic_ovn.go
+++ b/lxd/device/nic_ovn.go
@@ -171,15 +171,6 @@ func (d *nicOVN) validateConfig(instConf 
instance.ConfigReader) error {
        }
 
        if len(externalRoutes) > 0 {
-               for _, externalRoute := range externalRoutes {
-                       rOnes, rBits := externalRoute.Mask.Size()
-                       if rBits > 32 && rOnes < 122 {
-                               return fmt.Errorf("External route %q is too 
large. Maximum size for IPv6 external route is /122", externalRoute.String())
-                       } else if rOnes < 26 {
-                               return fmt.Errorf("External route %q is too 
large. Maximum size for IPv4 external route is /26", externalRoute.String())
-                       }
-               }
-
                err = 
d.network.InstanceDevicePortValidateExternalRoutes(d.inst, d.name, 
externalRoutes)
                if err != nil {
                        return err
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to