[lxc-devel] [lxd/master] Network: OVN Ingress mode
tomponline on Github
lxc-bot at linuxcontainers.org
Wed Dec 9 10:10:45 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 480 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20201209/2ff5550e/attachment.bin>
-------------- next part --------------
From 4ec52f656bb1c178a38f1bcffb489f12ad644a10 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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
More information about the lxc-devel
mailing list