Skip to content

Commit fb380ce

Browse files
committed
OCPBUGS-38468: use default route mtu
Signed-off-by: Evgeny Slutsky <[email protected]>
1 parent 6fb3ac9 commit fb380ce

File tree

4 files changed

+125
-34
lines changed

4 files changed

+125
-34
lines changed

pkg/components/networking.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/openshift/microshift/pkg/assets"
99
"github.com/openshift/microshift/pkg/config"
1010
"github.com/openshift/microshift/pkg/config/ovn"
11+
"github.com/vishvananda/netlink"
1112
"k8s.io/klog/v2"
1213
)
1314

@@ -52,7 +53,16 @@ func startCNIPlugin(ctx context.Context, cfg *config.Config, kubeconfigPath stri
5253
}
5354
}
5455

55-
ovnConfig, err := ovn.NewOVNKubernetesConfigFromFileOrDefault(filepath.Dir(config.ConfigFile), cfg.MultiNode.Enabled)
56+
ipFamily := netlink.FAMILY_ALL
57+
if cfg.IsIPv4() && !cfg.IsIPv6() {
58+
ipFamily = netlink.FAMILY_V4
59+
}
60+
61+
if cfg.IsIPv6() && !cfg.IsIPv4() {
62+
ipFamily = netlink.FAMILY_V6
63+
}
64+
65+
ovnConfig, err := ovn.NewOVNKubernetesConfigFromFileOrDefault(filepath.Dir(config.ConfigFile), cfg.MultiNode.Enabled, ipFamily)
5666
if err != nil {
5767
return fmt.Errorf("failed to create OVN-K configuration from %q: %w", config.ConfigFile, err)
5868
}

pkg/config/ovn/ovn.go

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"path/filepath"
99
"regexp"
1010

11+
"github.com/openshift/microshift/pkg/util"
12+
"github.com/vishvananda/netlink"
1113
"k8s.io/klog/v2"
1214
"sigs.k8s.io/yaml"
1315
)
@@ -40,10 +42,6 @@ func (o *OVNKubernetesConfig) Validate() error {
4042
if err != nil {
4143
return fmt.Errorf("failed to validate OVS bridge: %w", err)
4244
}
43-
err = o.validateConfig()
44-
if err != nil {
45-
return fmt.Errorf("failed to validate OVN-K configuration: %w", err)
46-
}
4745
return nil
4846
}
4947

@@ -56,28 +54,29 @@ func (o *OVNKubernetesConfig) validateOVSBridge() error {
5654
return nil
5755
}
5856

59-
// validateConfig validates the user defined configuration in /etc/microshift/ovn.yaml
60-
func (o *OVNKubernetesConfig) validateConfig() error {
61-
// validate MTU conf
62-
iface, err := net.InterfaceByName(OVNGatewayInterface)
63-
if err != nil {
64-
return fmt.Errorf("failed to find OVN gateway interface %q: %w", OVNGatewayInterface, err)
65-
}
66-
67-
if iface.MTU < o.MTU {
68-
return fmt.Errorf("interface MTU (%d) is too small for specified overlay (%d)", iface.MTU, o.MTU)
69-
}
70-
return nil
71-
}
72-
73-
// getClusterMTU retrieves MTU from ovn-kubernetes gateway interface "br-ex",
74-
// and falls back to use 1500 when "br-ex" mtu is unable to get or less than 0.
75-
func (o *OVNKubernetesConfig) getClusterMTU(multinode bool) {
76-
link, err := net.InterfaceByName(OVNGatewayInterface)
77-
if err == nil && link.MTU > 0 {
78-
o.MTU = link.MTU
57+
// getClusterMTU retrieves MTU from the default route network interface,
58+
// and falls back to use 1500 when unable to get the mtu or ipFamily than 0.
59+
func (o *OVNKubernetesConfig) getClusterMTU(multinode bool, ipFamily int) {
60+
klog.Infof("getClusterMTU: finding default route interface")
61+
o.MTU = defaultMTU
62+
63+
// if configure both IPV4 and IPV6 check the smallest
64+
//nolint:nestif
65+
if ipFamily == netlink.FAMILY_ALL {
66+
mtu, err := util.FindDefaultRouteMinMTU()
67+
68+
if err == nil {
69+
o.MTU = mtu
70+
} else {
71+
klog.Infof("getClusterMTU: error %s.", err)
72+
}
7973
} else {
80-
o.MTU = defaultMTU
74+
mtu, err := util.FindDefaultRouteMTU(ipFamily)
75+
if err == nil {
76+
o.MTU = mtu
77+
} else {
78+
klog.Infof("getClusterMTU: error %s.", err)
79+
}
8180
}
8281

8382
if multinode {
@@ -86,12 +85,12 @@ func (o *OVNKubernetesConfig) getClusterMTU(multinode bool) {
8685
}
8786

8887
// withDefaults returns the default values when ovn.yaml is not provided
89-
func (o *OVNKubernetesConfig) withDefaults(multinode bool) *OVNKubernetesConfig {
90-
o.getClusterMTU(multinode)
88+
func (o *OVNKubernetesConfig) withDefaults(multinode bool, ipFamily int) *OVNKubernetesConfig {
89+
o.getClusterMTU(multinode, ipFamily)
9190
return o
9291
}
9392

94-
func newOVNKubernetesConfigFromFile(path string, multinode bool) (*OVNKubernetesConfig, error) {
93+
func newOVNKubernetesConfigFromFile(path string, multinode bool, ipFamily int) (*OVNKubernetesConfig, error) {
9594
o := new(OVNKubernetesConfig)
9695
buf, err := os.ReadFile(path)
9796
if err != nil {
@@ -104,24 +103,24 @@ func newOVNKubernetesConfigFromFile(path string, multinode bool) (*OVNKubernetes
104103
}
105104
// in case mtu is not defined
106105
if o.MTU == 0 {
107-
o.getClusterMTU(multinode)
106+
o.getClusterMTU(multinode, ipFamily)
108107
}
109108
klog.Infof("parsed OVNKubernetes config from file %q: %+v", path, o)
110109

111110
return o, nil
112111
}
113112

114-
func NewOVNKubernetesConfigFromFileOrDefault(dir string, multinode bool) (*OVNKubernetesConfig, error) {
113+
func NewOVNKubernetesConfigFromFileOrDefault(dir string, multinode bool, ipFamily int) (*OVNKubernetesConfig, error) {
115114
path := filepath.Join(dir, ovnConfigFileName)
116115
if _, err := os.Stat(path); err != nil {
117116
if errors.Is(err, os.ErrNotExist) {
118117
klog.Infof("OVNKubernetes config file not found, assuming default values")
119-
return new(OVNKubernetesConfig).withDefaults(multinode), nil
118+
return new(OVNKubernetesConfig).withDefaults(multinode, ipFamily), nil
120119
}
121120
return nil, fmt.Errorf("failed to get OVNKubernetes config file: %v", err)
122121
}
123122

124-
o, err := newOVNKubernetesConfigFromFile(path, multinode)
123+
o, err := newOVNKubernetesConfigFromFile(path, multinode, ipFamily)
125124
if err == nil {
126125
return o, nil
127126
}

pkg/config/ovn/ovn_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package ovn
22

33
import (
44
"testing"
5+
6+
"github.com/vishvananda/netlink"
57
)
68

79
// tests to make sure that the config file is parsed correctly
@@ -15,7 +17,7 @@ func TestNewOVNKubernetesConfigFromFileOrDefault(t *testing.T) {
1517
}
1618

1719
for _, tt := range ttests {
18-
_, err := NewOVNKubernetesConfigFromFileOrDefault(tt.configFile, false)
20+
_, err := NewOVNKubernetesConfigFromFileOrDefault(tt.configFile, false, netlink.FAMILY_V4)
1921
if (err != nil) != (tt.err != nil) {
2022
t.Errorf("NewOVNKubernetesConfigFromFileOrDefault() error = %v, wantErr %v", err, tt.err)
2123
}

pkg/util/net.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
tcpnet "net"
2323
"net/http"
2424
"os"
25+
"slices"
2526
"sort"
2627
"strings"
2728
"time"
@@ -34,6 +35,14 @@ import (
3435

3536
var previousGatewayIP string = ""
3637

38+
type routeStruct struct {
39+
// Name of interface
40+
Iface string
41+
42+
// big-endian hex string
43+
Gateway string
44+
}
45+
3746
// Remember whether we have successfully found the hard-coded nodeIP
3847
// on this host.
3948
var foundHardCodedNodeIP bool
@@ -266,3 +275,74 @@ func GetHostIPv6(ipHint string) (string, error) {
266275

267276
return "", fmt.Errorf("unable to find host IPv6 address")
268277
}
278+
279+
func FindDefaultRouteMinMTU() (mtu int, err error) {
280+
ipFamilies := []int{netlink.FAMILY_V4, netlink.FAMILY_V6}
281+
282+
mtu_slice := []int{}
283+
284+
for _, ipFamily := range ipFamilies {
285+
new_mtu, err := FindDefaultRouteMTU(ipFamily)
286+
if err != nil {
287+
continue
288+
}
289+
mtu_slice = append(mtu_slice, new_mtu)
290+
}
291+
if len(mtu_slice) > 0 {
292+
return slices.Min(mtu_slice), nil
293+
}
294+
return 0, fmt.Errorf("could not find minimal MTU")
295+
}
296+
297+
func FindDefaultRouteMTU(ipFamily int) (mtu int, err error) {
298+
link, err := FindDefaultRouteIface(ipFamily)
299+
if err != nil || link.MTU == 0 {
300+
return 0, err
301+
}
302+
303+
klog.Infof("using IP %d on Interface %s with MTU %d ", ipFamily, link.Name, link.MTU)
304+
305+
return link.MTU, nil
306+
}
307+
308+
// Find the Default route Interface based on ipv4 or ipv6 routes.
309+
func FindDefaultRouteIface(ipFamily int) (iface *tcpnet.Interface, err error) {
310+
parsedStruct, err := findDefaultRouteForFamily(ipFamily)
311+
if err != nil {
312+
return nil, err
313+
}
314+
315+
iface, err = tcpnet.InterfaceByName(parsedStruct.Iface)
316+
if err != nil {
317+
return nil, err
318+
}
319+
320+
return iface, nil
321+
}
322+
323+
func findDefaultRouteForFamily(family int) (routeStruct, error) {
324+
handle, err := netlink.NewHandle()
325+
if err != nil {
326+
return routeStruct{}, err
327+
}
328+
329+
routeList, err := handle.RouteList(nil, family)
330+
if err != nil {
331+
return routeStruct{}, err
332+
}
333+
334+
for _, route := range routeList {
335+
// for Default route the Destination should be nil (0)
336+
if route.Dst == nil {
337+
link, err := handle.LinkByIndex(route.LinkIndex)
338+
if err != nil {
339+
return routeStruct{}, err
340+
}
341+
return routeStruct{
342+
Iface: link.Attrs().Name,
343+
Gateway: route.Gw.String(),
344+
}, nil
345+
}
346+
}
347+
return routeStruct{}, fmt.Errorf("no default gateway found")
348+
}

0 commit comments

Comments
 (0)