github.com/cilium/cilium@v1.16.2/pkg/mtu/detect_linux.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  //go:build linux
     5  
     6  package mtu
     7  
     8  import (
     9  	"fmt"
    10  	"net"
    11  
    12  	"github.com/sirupsen/logrus"
    13  	"github.com/vishvananda/netlink"
    14  
    15  	"github.com/cilium/cilium/pkg/logging/logfields"
    16  )
    17  
    18  const (
    19  	// externalProbeIPv4 is an IPv4 address specifically designed for tests. We
    20  	// only want to retrieve default route for external IP addresses, thus it
    21  	// doesn't need to be a real address.
    22  	externalProbeIPv4 = "203.0.113.1"
    23  
    24  	// externalProbeIPv6 is an IPv4 address specifically designed for tests. We
    25  	// only want to retrieve default route for external IP addresses, thus it
    26  	// doesn't need to be a real address.
    27  	externalProbeIPv6 = "2001:db8::1"
    28  )
    29  
    30  func getRoute(externalProbe string) ([]netlink.Route, error) {
    31  	ip := net.ParseIP(externalProbe)
    32  	if ip == nil {
    33  		return nil, fmt.Errorf("unable to parse IP %s", externalProbe)
    34  	}
    35  
    36  	routes, err := netlink.RouteGet(ip)
    37  	if err != nil {
    38  		return nil, fmt.Errorf("unable to lookup route to %s: %w", externalProbe, err)
    39  	}
    40  
    41  	if len(routes) == 0 {
    42  		return nil, fmt.Errorf("no route to %s", externalProbe)
    43  	}
    44  
    45  	return routes, nil
    46  }
    47  
    48  func autoDetect() (int, error) {
    49  	var routes []netlink.Route
    50  	var err error
    51  
    52  	routes, err = getRoute(externalProbeIPv4)
    53  	if err != nil {
    54  		prevErr := err
    55  		routes, err = getRoute(externalProbeIPv6)
    56  		if err != nil {
    57  			return 0, fmt.Errorf("%w: %w", err, prevErr)
    58  		}
    59  	}
    60  
    61  	if routes[0].Gw == nil {
    62  		return 0, fmt.Errorf("unable to find default gateway from the routes: %s", routes)
    63  	}
    64  
    65  	link, err := netlink.LinkByIndex(routes[0].LinkIndex)
    66  	if err != nil {
    67  		return 0, fmt.Errorf("unable to find interface of default route: %w", err)
    68  	}
    69  
    70  	if mtu := link.Attrs().MTU; mtu != 0 {
    71  		log.Infof("Detected MTU %d", mtu)
    72  		return mtu, nil
    73  	}
    74  
    75  	return EthernetMTU, nil
    76  }
    77  
    78  // getMTUFromIf finds the interface that holds the ip and returns its mtu
    79  func getMTUFromIf(ip net.IP) (int, error) {
    80  	ifaces, err := netlink.LinkList()
    81  	if err != nil {
    82  		return 0, fmt.Errorf("unable to list interfaces: %w", err)
    83  	}
    84  
    85  	for _, iface := range ifaces {
    86  		addrs, err := netlink.AddrList(iface, netlink.FAMILY_ALL)
    87  		if err != nil {
    88  			log.WithFields(logrus.Fields{
    89  				logfields.Device: iface.Attrs().Name,
    90  			}).Warning("Unable to list all addresses")
    91  			continue
    92  		}
    93  
    94  		for _, addr := range addrs {
    95  			if addr.IPNet.IP.Equal(ip) {
    96  				myMTU := iface.Attrs().MTU
    97  				log.WithFields(logrus.Fields{
    98  					logfields.Device: iface.Attrs().Name,
    99  					logfields.IPAddr: ip,
   100  					logfields.MTU:    myMTU,
   101  				}).Info("Inheriting MTU from external network interface")
   102  				return myMTU, nil
   103  			}
   104  		}
   105  	}
   106  	return 0, fmt.Errorf("No interface contains the provided ip: %v", ip)
   107  }