diff --git a/pkg/components/networking.go b/pkg/components/networking.go index 02fd72d0754..ab878a5d4b8 100644 --- a/pkg/components/networking.go +++ b/pkg/components/networking.go @@ -8,6 +8,7 @@ import ( "github.com/openshift/microshift/pkg/assets" "github.com/openshift/microshift/pkg/config" "github.com/openshift/microshift/pkg/config/ovn" + "github.com/vishvananda/netlink" "k8s.io/klog/v2" ) @@ -52,7 +53,16 @@ func startCNIPlugin(ctx context.Context, cfg *config.Config, kubeconfigPath stri } } - ovnConfig, err := ovn.NewOVNKubernetesConfigFromFileOrDefault(filepath.Dir(config.ConfigFile), cfg.MultiNode.Enabled) + ip_family := netlink.FAMILY_ALL + if cfg.IsIPv4() && !cfg.IsIPv6() { + ip_family = netlink.FAMILY_V4 + } + + if cfg.IsIPv6() && !cfg.IsIPv4() { + ip_family = netlink.FAMILY_V6 + } + + ovnConfig, err := ovn.NewOVNKubernetesConfigFromFileOrDefault(filepath.Dir(config.ConfigFile), cfg.MultiNode.Enabled, ip_family) if err != nil { return fmt.Errorf("failed to create OVN-K configuration from %q: %w", config.ConfigFile, err) } diff --git a/pkg/config/ovn/ovn.go b/pkg/config/ovn/ovn.go index bb8d8fe29ac..4d2651dbac5 100644 --- a/pkg/config/ovn/ovn.go +++ b/pkg/config/ovn/ovn.go @@ -9,6 +9,7 @@ import ( "regexp" "github.com/openshift/microshift/pkg/util" + "github.com/vishvananda/netlink" "k8s.io/klog/v2" "sigs.k8s.io/yaml" ) @@ -55,16 +56,29 @@ func (o *OVNKubernetesConfig) validateOVSBridge() error { // getClusterMTU retrieves MTU from the default route network interface, // and falls back to use 1500 when unable to get the mtu or less than 0. -func (o *OVNKubernetesConfig) getClusterMTU(multinode bool) { +func (o *OVNKubernetesConfig) getClusterMTU(multinode bool, ip_family int) { klog.Infof("getClusterMTU: finding default route interface") - link, err := util.FindDefaultRouteIface() + o.MTU = defaultMTU + + // if configure both IPV4 and IPV6 check the smallest + //nolint:nestif + if ip_family == netlink.FAMILY_ALL { + mtu, err := util.FindDefaultRouteMinMTU() + if err == nil { + o.MTU = mtu + } else { + klog.Infof("getClusterMTU: error %s.", err) + } + klog.Infof("getClusterMTU: using Dual stack MTU discovery found - %d", mtu) - if err == nil && link.MTU > 0 { - klog.Infof("getClusterMTU: Using Interface %s with MTU %d as source default route.", link.Name, link.MTU) - o.MTU = link.MTU } else { - klog.Infof("getClusterMTU: Couldnt extract MTU from the default route interface , Using Default MTU.") - o.MTU = defaultMTU + mtu, err := util.FindDefaultRouteMTU(ip_family) + if err == nil { + o.MTU = mtu + } else { + klog.Infof("getClusterMTU: error %s.", err) + } + klog.Infof("getClusterMTU: using ip version: %d stack MTU discovery found - with MTU %d", ip_family, mtu) } if multinode { @@ -73,12 +87,12 @@ func (o *OVNKubernetesConfig) getClusterMTU(multinode bool) { } // withDefaults returns the default values when ovn.yaml is not provided -func (o *OVNKubernetesConfig) withDefaults(multinode bool) *OVNKubernetesConfig { - o.getClusterMTU(multinode) +func (o *OVNKubernetesConfig) withDefaults(multinode bool, ip_family int) *OVNKubernetesConfig { + o.getClusterMTU(multinode, ip_family) return o } -func newOVNKubernetesConfigFromFile(path string, multinode bool) (*OVNKubernetesConfig, error) { +func newOVNKubernetesConfigFromFile(path string, multinode bool, ip_family int) (*OVNKubernetesConfig, error) { o := new(OVNKubernetesConfig) buf, err := os.ReadFile(path) if err != nil { @@ -91,24 +105,24 @@ func newOVNKubernetesConfigFromFile(path string, multinode bool) (*OVNKubernetes } // in case mtu is not defined if o.MTU == 0 { - o.getClusterMTU(multinode) + o.getClusterMTU(multinode, ip_family) } klog.Infof("parsed OVNKubernetes config from file %q: %+v", path, o) return o, nil } -func NewOVNKubernetesConfigFromFileOrDefault(dir string, multinode bool) (*OVNKubernetesConfig, error) { +func NewOVNKubernetesConfigFromFileOrDefault(dir string, multinode bool, ip_family int) (*OVNKubernetesConfig, error) { path := filepath.Join(dir, ovnConfigFileName) if _, err := os.Stat(path); err != nil { if errors.Is(err, os.ErrNotExist) { klog.Infof("OVNKubernetes config file not found, assuming default values") - return new(OVNKubernetesConfig).withDefaults(multinode), nil + return new(OVNKubernetesConfig).withDefaults(multinode, ip_family), nil } return nil, fmt.Errorf("failed to get OVNKubernetes config file: %v", err) } - o, err := newOVNKubernetesConfigFromFile(path, multinode) + o, err := newOVNKubernetesConfigFromFile(path, multinode, ip_family) if err == nil { return o, nil } diff --git a/pkg/config/ovn/ovn_test.go b/pkg/config/ovn/ovn_test.go index 54a885b0fe9..89a1bc51888 100644 --- a/pkg/config/ovn/ovn_test.go +++ b/pkg/config/ovn/ovn_test.go @@ -2,6 +2,8 @@ package ovn import ( "testing" + + "github.com/vishvananda/netlink" ) // tests to make sure that the config file is parsed correctly @@ -15,7 +17,7 @@ func TestNewOVNKubernetesConfigFromFileOrDefault(t *testing.T) { } for _, tt := range ttests { - _, err := NewOVNKubernetesConfigFromFileOrDefault(tt.configFile, false) + _, err := NewOVNKubernetesConfigFromFileOrDefault(tt.configFile, false, netlink.FAMILY_V4) if (err != nil) != (tt.err != nil) { t.Errorf("NewOVNKubernetesConfigFromFileOrDefault() error = %v, wantErr %v", err, tt.err) } diff --git a/pkg/util/net.go b/pkg/util/net.go index 4aabfc456ed..f65eeb0ea47 100644 --- a/pkg/util/net.go +++ b/pkg/util/net.go @@ -22,6 +22,7 @@ import ( tcpnet "net" "net/http" "os" + "slices" "sort" "strings" "time" @@ -275,16 +276,40 @@ func GetHostIPv6(ipHint string) (string, error) { return "", fmt.Errorf("unable to find host IPv6 address") } -// Find the Default route Interface based on ipv4 or ipv6 routes. -func FindDefaultRouteIface() (iface *tcpnet.Interface, err error) { - // first try IPv4 - parsedStruct, err := findDefaultRouteForFamily(netlink.FAMILY_V4) - if err != nil { - // then try IPv6 - parsedStruct, err = findDefaultRouteForFamily(netlink.FAMILY_V6) +func FindDefaultRouteMinMTU() (mtu int, err error) { + ip_families := []int{netlink.FAMILY_V4, netlink.FAMILY_V6} + + mtu_slice := []int{} + + for _, ip_family := range ip_families { + new_mtu, err := FindDefaultRouteMTU(ip_family) if err != nil { - return nil, err + continue } + mtu_slice = append(mtu_slice, new_mtu) + } + if len(mtu_slice) > 0 { + return slices.Min(mtu_slice), nil + } + return 0, fmt.Errorf("could not find minimal MTU") +} + +func FindDefaultRouteMTU(ip_family int) (mtu int, err error) { + link, err := FindDefaultRouteIface(ip_family) + if err != nil && link.MTU == 0 { + return 0, err + } + + klog.Infof("using IP %d on Interface %s with MTU %d ", ip_family, link.Name, link.MTU) + + return link.MTU, nil +} + +// Find the Default route Interface based on ipv4 or ipv6 routes. +func FindDefaultRouteIface(ip_family int) (iface *tcpnet.Interface, err error) { + parsedStruct, err := findDefaultRouteForFamily(ip_family) + if err != nil { + return nil, err } iface, err = tcpnet.InterfaceByName(parsedStruct.Iface)