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

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) ===
- Auto generates `bridge.mtu` setting for OVN networks if not specified (on create and update).
- Bridge MTU setting is derived from OVN's underlay network's `ovn-encap-ip`, which is used to ascertain the underlay network interface's MTU and IP address family (which both influence the maximum MTU size that can be used without fragmentation in the overlay network due to geneve tunnel overhead).
From 6a43c6dcc11ed81d42a91f352ff70cc1d8216206 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 22 Sep 2020 11:19:32 +0100
Subject: [PATCH 01/11] shared/validate: Use ParseUint in IsNetworkMTU

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

diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index 2db3f92efd..d9645f6dfc 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -389,7 +389,7 @@ func IsNetworkVLAN(value string) error {
 // Anything below 68 and the kernel doesn't allow IPv4, anything below 1280 
and the kernel doesn't allow IPv6.
 // So require an IPv6-compatible MTU as the low value and cap at the max 
ethernet jumbo frame size.
 func IsNetworkMTU(value string) error {
-       mtu, err := strconv.ParseInt(value, 10, 32)
+       mtu, err := strconv.ParseUint(value, 10, 32)
        if err != nil {
                return fmt.Errorf("Invalid MTU %q", value)
        }

From cefdfacababb21e9a4768b58103a4c3684685091 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 22 Sep 2020 11:20:02 +0100
Subject: [PATCH 02/11] lxd/device/device/utils/network: Change argument for
 NetworkSetDevMTU to uint32

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

diff --git a/lxd/device/device_utils_network.go 
b/lxd/device/device_utils_network.go
index 3e779b10c7..15069d2216 100644
--- a/lxd/device/device_utils_network.go
+++ b/lxd/device/device_utils_network.go
@@ -29,7 +29,7 @@ import (
 var networkCreateSharedDeviceLock sync.Mutex
 
 // NetworkSetDevMTU sets the MTU setting for a named network device if 
different from current.
-func NetworkSetDevMTU(devName string, mtu uint64) error {
+func NetworkSetDevMTU(devName string, mtu uint32) error {
        curMTU, err := network.GetDevMTU(devName)
        if err != nil {
                return err

From b61d96bf5ca63e8699dad37747b889c90e85cbc4 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 22 Sep 2020 11:20:36 +0100
Subject: [PATCH 03/11] lxd/device/device/utils/network: NetworkSetDevMTU usage

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

diff --git a/lxd/device/device_utils_network.go 
b/lxd/device/device_utils_network.go
index 15069d2216..eb95d3501b 100644
--- a/lxd/device/device_utils_network.go
+++ b/lxd/device/device_utils_network.go
@@ -192,7 +192,7 @@ func networkRestorePhysicalNic(hostName string, volatile 
map[string]string) erro
                        return fmt.Errorf("Failed to convert mtu for \"%s\" mtu 
\"%s\": %v", hostName, volatile["last_state.mtu"], err)
                }
 
-               err = NetworkSetDevMTU(hostName, mtuInt)
+               err = NetworkSetDevMTU(hostName, uint32(mtuInt))
                if err != nil {
                        return fmt.Errorf("Failed to restore physical dev 
\"%s\" mtu to \"%d\": %v", hostName, mtuInt, err)
                }
@@ -238,18 +238,18 @@ func networkCreateVethPair(hostName string, m 
deviceConfig.Device) (string, erro
 
        // Set the MTU on peer. If not specified and has parent, will inherit 
MTU from parent.
        if m["mtu"] != "" {
-               MTU, err := strconv.ParseUint(m["mtu"], 10, 32)
+               mtu, err := strconv.ParseUint(m["mtu"], 10, 32)
                if err != nil {
                        return "", fmt.Errorf("Invalid MTU specified: %v", err)
                }
 
-               err = NetworkSetDevMTU(peerName, MTU)
+               err = NetworkSetDevMTU(peerName, uint32(mtu))
                if err != nil {
                        NetworkRemoveInterface(peerName)
                        return "", fmt.Errorf("Failed to set the MTU: %v", err)
                }
 
-               err = NetworkSetDevMTU(hostName, MTU)
+               err = NetworkSetDevMTU(hostName, uint32(mtu))
                if err != nil {
                        NetworkRemoveInterface(peerName)
                        return "", fmt.Errorf("Failed to set the MTU: %v", err)
@@ -294,12 +294,12 @@ func networkCreateTap(hostName string, m 
deviceConfig.Device) error {
 
        // Set the MTU on peer. If not specified and has parent, will inherit 
MTU from parent.
        if m["mtu"] != "" {
-               MTU, err := strconv.ParseUint(m["mtu"], 10, 32)
+               mtu, err := strconv.ParseUint(m["mtu"], 10, 32)
                if err != nil {
                        return errors.Wrap(err, "Invalid MTU specified")
                }
 
-               err = NetworkSetDevMTU(hostName, MTU)
+               err = NetworkSetDevMTU(hostName, uint32(mtu))
                if err != nil {
                        return errors.Wrap(err, "Failed to set the MTU")
                }

From 13cda6c28fcdfa075937cbd30f6518f594b3101c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 22 Sep 2020 11:20:55 +0100
Subject: [PATCH 04/11] lxd/network/network/utils: Changes GetDevMTU to return
 uint32

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

diff --git a/lxd/network/network_utils.go b/lxd/network/network_utils.go
index fdd0b904ee..c195228a3d 100644
--- a/lxd/network/network_utils.go
+++ b/lxd/network/network_utils.go
@@ -292,7 +292,7 @@ func DetachInterface(bridgeName string, devName string) 
error {
 }
 
 // GetDevMTU retrieves the current MTU setting for a named network device.
-func GetDevMTU(devName string) (uint64, error) {
+func GetDevMTU(devName string) (uint32, error) {
        content, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/mtu", 
devName))
        if err != nil {
                return 0, err
@@ -304,7 +304,7 @@ func GetDevMTU(devName string) (uint64, error) {
                return 0, err
        }
 
-       return mtu, nil
+       return uint32(mtu), nil
 }
 
 // DefaultGatewaySubnetV4 returns subnet of default gateway interface.

From 7376a8b6158877b9c74ae35aadabe067031b4bf1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 22 Sep 2020 11:21:13 +0100
Subject: [PATCH 05/11] lxd/network/openvswitch/ovs: Adds OVNEncapIP function

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

diff --git a/lxd/network/openvswitch/ovs.go b/lxd/network/openvswitch/ovs.go
index a8d4002d6a..478d9325aa 100644
--- a/lxd/network/openvswitch/ovs.go
+++ b/lxd/network/openvswitch/ovs.go
@@ -2,6 +2,7 @@ package openvswitch
 
 import (
        "fmt"
+       "net"
        "os/exec"
        "strconv"
        "strings"
@@ -169,6 +170,31 @@ func (o *OVS) ChassisID() (string, error) {
        return chassisID, nil
 }
 
+// OVNEncapIP returns the enscapsulation IP used for OVN underlay tunnels.
+func (o *OVS) OVNEncapIP() (net.IP, error) {
+       // ovs-vsctl's get command doesn't support its --format flag, so we 
always get the output quoted.
+       // However ovs-vsctl's find and list commands don't support retrieving 
a single column's map field.
+       // And ovs-vsctl's JSON output is unfriendly towards statically typed 
languages as it mixes data types
+       // in a slice. So stick with "get" command and use Go's strconv.Unquote 
to return the actual values.
+       encapIPStr, err := shared.RunCommand("ovs-vsctl", "get", 
"open_vswitch", ".", "external_ids:ovn-encap-ip")
+       if err != nil {
+               return nil, err
+       }
+
+       encapIPStr = strings.TrimSpace(encapIPStr)
+       encapIPStr, err = strconv.Unquote(encapIPStr)
+       if err != nil {
+               return nil, err
+       }
+
+       encapIP := net.ParseIP(encapIPStr)
+       if encapIP == nil {
+               return nil, fmt.Errorf("Invalid ovn-encap-ip address")
+       }
+
+       return encapIP, nil
+}
+
 // OVNBridgeMappings gets the current OVN bridge mappings.
 func (o *OVS) OVNBridgeMappings(bridgeName string) ([]string, error) {
        // ovs-vsctl's get command doesn't support its --format flag, so we 
always get the output quoted.

From 2f43a2aa00c15f4af7ada85be3a29d21a42db635 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 22 Sep 2020 12:27:28 +0100
Subject: [PATCH 06/11] lxd/network/driver/ovn: Removes ovnGeneveTunnelMTU
 constant

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 0f62cc7d4c..5482dd0583 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -28,9 +28,6 @@ import (
        "github.com/lxc/lxd/shared/validate"
 )
 
-// ovnGeneveTunnelMTU is the MTU that is safe to use when tunneling using 
geneve.
-const ovnGeneveTunnelMTU = 1442
-
 const ovnChassisPriorityMax = 32767
 const ovnVolatileParentIPv4 = "volatile.network.ipv4.address"
 const ovnVolatileParentIPv6 = "volatile.network.ipv6.address"

From b477c2ad2f469e5b924b280a214eca137f32f6e1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 22 Sep 2020 12:28:39 +0100
Subject: [PATCH 07/11] lxd/network/network/utils/ovn: Removes
 OVNInstanceDeviceMTU function

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

diff --git a/lxd/network/network_utils_ovn.go b/lxd/network/network_utils_ovn.go
index 34ad39b59e..d603034019 100644
--- a/lxd/network/network_utils_ovn.go
+++ b/lxd/network/network_utils_ovn.go
@@ -29,14 +29,3 @@ func OVNInstanceDevicePortDelete(network Network, instanceID 
int, deviceName str
 
        return n.instanceDevicePortDelete(instanceID, deviceName)
 }
-
-// OVNInstanceDeviceMTU returns the MTU that should be used for an OVN 
instance device.
-func OVNInstanceDeviceMTU(network Network) (uint32, error) {
-       // Check network is of type OVN.
-       n, ok := network.(*ovn)
-       if !ok {
-               return 0, fmt.Errorf("Network is not OVN type")
-       }
-
-       return n.getBridgeMTU(), nil
-}

From fb736aa95fda2ed776214fa246b61f6925a6027e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 22 Sep 2020 12:30:29 +0100
Subject: [PATCH 08/11] lxd/network/driver/ovn: Updates getBridgeMTU() to not
 depend on ovnGeneveTunnelMTU

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index 5482dd0583..c566cdc851 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -127,19 +127,19 @@ func (n *ovn) getClient() (*openvswitch.OVN, error) {
        return client, nil
 }
 
-// getBridgeMTU returns MTU that should be used for the bridge and instance 
devices. Will also be used to configure
-// the OVN DHCP and IPv6 RA options.
+// getBridgeMTU returns MTU that should be used for the bridge and instance 
devices.
+// Will also be used to configure the OVN DHCP and IPv6 RA options. Returns 0 
if the bridge.mtu is not set/invalid.
 func (n *ovn) getBridgeMTU() uint32 {
        if n.config["bridge.mtu"] != "" {
                mtu, err := strconv.ParseUint(n.config["bridge.mtu"], 10, 32)
                if err != nil {
-                       return ovnGeneveTunnelMTU
+                       return 0
                }
 
                return uint32(mtu)
        }
 
-       return ovnGeneveTunnelMTU
+       return 0
 }
 
 // getNetworkPrefix returns OVN network prefix to use for object names.

From 42fdd5f6ff47841c4b621acdcb42c2177ece90dd Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 22 Sep 2020 12:31:10 +0100
Subject: [PATCH 09/11] lxd/network/driver/ovn: Adds getOptimalBridgeMTU and
 getUnderlayInfo functions

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index c566cdc851..da4108331e 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -142,6 +142,82 @@ func (n *ovn) getBridgeMTU() uint32 {
        return 0
 }
 
+// getUnderlayInfo returns the MTU for the underlay network interface and the 
enscapsulation IP for OVN tunnels.
+func (n *ovn) getUnderlayInfo() (uint32, net.IP, error) {
+       ovs := openvswitch.NewOVS()
+       encapIP, err := ovs.OVNEncapIP()
+       if err != nil {
+               return 0, nil, errors.Wrapf(err, "Failed getting OVN 
enscapsulation IP from OVS")
+       }
+
+       // Look for interface that has the OVN enscapsulation IP assigned.
+       ifaces, err := net.Interfaces()
+       if err != nil {
+               return 0, nil, errors.Wrapf(err, "Failed getting local network 
interfaces")
+       }
+
+       var encapMTU uint32
+
+       for _, iface := range ifaces {
+               addrs, err := iface.Addrs()
+               if err != nil {
+                       continue
+               }
+
+               for _, addr := range addrs {
+                       ip, _, err := net.ParseCIDR(addr.String())
+                       if err != nil {
+                               continue
+                       }
+
+                       if ip.Equal(encapIP) {
+                               encapMTU, err = GetDevMTU(iface.Name)
+                               if err != nil {
+                                       return 0, nil, errors.Wrapf(err, 
"Failed getting MTU for %q", iface.Name)
+                               }
+                       }
+               }
+       }
+
+       if encapMTU <= 0 {
+               return 0, nil, fmt.Errorf("No matching interface found for OVN 
enscapsulation IP %q", encapIP.String())
+       }
+
+       return encapMTU, encapIP, nil
+}
+
+// getOptimalBridgeMTU returns the MTU that can be used for the bridge and 
instance devices based on the MTU value
+// of the OVN underlay network interface. This assumes that the OVN tunnel 
mechanism used is geneve and that the
+// same underlying network settings (MTU and encapsulation IP family) are used 
on all OVN nodes.
+func (n *ovn) getOptimalBridgeMTU() (uint32, error) {
+       // Get underlay MTU and encapsulation IP.
+       underlayMTU, encapIP, err := n.getUnderlayInfo()
+       if err != nil {
+               return 0, errors.Wrapf(err, "Failed getting OVN underlay info")
+       }
+
+       // Encapsulation family is IPv6.
+       if encapIP.To4() == nil {
+               // If the underlay's MTU is large enough to accomodate a 1500 
overlay MTU and the geneve tunnel
+               // overhead of 78 bytes (when used with IPv6 encapsulation) 
then indicate 1500 MTU can be used.
+               if underlayMTU >= 1578 {
+                       return 1500, nil
+               }
+
+               // Default to 1422 which can work with an underlay MTU of 1500.
+               return 1422, nil
+       }
+
+       // If the underlay's MTU is large enough to accomodate a 1500 overlay 
MTU and the geneve tunnel
+       // overhead of 58 bytes (when used with IPv4 encapsulation) then 
indicate 1500 MTU can be used.
+       if underlayMTU >= 1558 {
+               return 1500, nil
+       }
+
+       // Default to 1442 which can work with underlay MTU of 1500.
+       return 1442, nil
+}
+
 // getNetworkPrefix returns OVN network prefix to use for object names.
 func (n *ovn) getNetworkPrefix() string {
        return fmt.Sprintf("lxd-net%d", n.id)

From 735226d74a4b09f532da811b945af1581255cf68 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 22 Sep 2020 12:31:45 +0100
Subject: [PATCH 10/11] lxd/network/driver/ovn: Updates setup to generate an
 optimal bridge.mtu setting if not specified manually

The bridge.mtu setting is derived from the OVN underlay network's MTU and 
encapsulation IP address family.

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

diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
index da4108331e..ae4560e691 100644
--- a/lxd/network/driver_ovn.go
+++ b/lxd/network/driver_ovn.go
@@ -838,6 +838,30 @@ func (n *ovn) setup(update bool) error {
        var routerExtPortIPv4, routerIntPortIPv4, routerExtPortIPv6, 
routerIntPortIPv6 net.IP
        var routerExtPortIPv4Net, routerIntPortIPv4Net, routerExtPortIPv6Net, 
routerIntPortIPv6Net *net.IPNet
 
+       // Get bridge MTU to use.
+       bridgeMTU := n.getBridgeMTU()
+       if bridgeMTU == 0 {
+               // If no manual bridge MTU specified, derive it from the 
underlay network.
+               bridgeMTU, err = n.getOptimalBridgeMTU()
+               if err != nil {
+                       return errors.Wrapf(err, "Failed getting optimal bridge 
MTU")
+               }
+
+               // Save to config so the value can be read by instances 
connecting to network.
+               n.config["bridge.mtu"] = fmt.Sprintf("%d", bridgeMTU)
+               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 nil
+               })
+               if err != nil {
+                       return err
+               }
+       }
+
        // Get router MAC address.
        routerMAC, err := n.getRouterMAC()
        if err != nil {
@@ -1043,7 +1067,7 @@ func (n *ovn) setup(update bool) error {
                RecursiveDNSServer: parent.dnsIPv4,
                DomainName:         n.getDomainName(),
                LeaseTime:          time.Duration(time.Hour * 1),
-               MTU:                n.getBridgeMTU(),
+               MTU:                bridgeMTU,
        })
        if err != nil {
                return errors.Wrapf(err, "Failed adding DHCPv4 settings for 
internal switch")
@@ -1089,7 +1113,7 @@ func (n *ovn) setup(update bool) error {
                        SendPeriodic:       true,
                        DNSSearchList:      n.getDNSSearchList(),
                        RecursiveDNSServer: parent.dnsIPv6,
-                       MTU:                n.getBridgeMTU(),
+                       MTU:                bridgeMTU,
 
                        // Keep these low until we support DNS search domains 
via DHCPv4, as otherwise RA DNSSL
                        // won't take effect until advert after DHCPv4 has run 
on instance.

From c44796b1d248fa446aafa7c9d699347a3cb5c427 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 22 Sep 2020 12:29:02 +0100
Subject: [PATCH 11/11] lxd/device/nic/ovn: Read mtu directly from parent
 network config bridge.mtu setting

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

diff --git a/lxd/device/nic_ovn.go b/lxd/device/nic_ovn.go
index 66c58ab4f3..79aa018feb 100644
--- a/lxd/device/nic_ovn.go
+++ b/lxd/device/nic_ovn.go
@@ -125,12 +125,7 @@ func (d *nicOVN) validateConfig(instConf 
instance.ConfigReader) error {
        }
 
        // Apply network level config options to device config before 
validation.
-       mtu, err := network.OVNInstanceDeviceMTU(n)
-       if err != nil {
-               return err
-       }
-
-       d.config["mtu"] = fmt.Sprintf("%d", mtu)
+       d.config["mtu"] = fmt.Sprintf("%s", netConfig["bridge.mtu"])
 
        rules := nicValidationRules(requiredFields, optionalFields)
 
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to