github.com/cilium/cilium@v1.16.2/pkg/datapath/connector/veth.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package connector
     5  
     6  import (
     7  	"fmt"
     8  	"net"
     9  
    10  	"github.com/vishvananda/netlink"
    11  
    12  	"github.com/cilium/cilium/api/v1/models"
    13  	"github.com/cilium/cilium/pkg/datapath/link"
    14  	"github.com/cilium/cilium/pkg/datapath/linux/sysctl"
    15  	"github.com/cilium/cilium/pkg/logging/logfields"
    16  	"github.com/cilium/cilium/pkg/mac"
    17  	"github.com/cilium/cilium/pkg/netns"
    18  )
    19  
    20  // SetupVethRemoteNs renames the netdevice in the target namespace to the
    21  // provided dstIfName.
    22  func SetupVethRemoteNs(ns *netns.NetNS, srcIfName, dstIfName string) error {
    23  	return ns.Do(func() error {
    24  		err := link.Rename(srcIfName, dstIfName)
    25  		if err != nil {
    26  			return fmt.Errorf("failed to rename veth from %q to %q: %w", srcIfName, dstIfName, err)
    27  		}
    28  		return nil
    29  	})
    30  }
    31  
    32  // SetupVeth sets up the net interface, the temporary interface and fills up some endpoint
    33  // fields such as mac, NodeMac, ifIndex and ifName. Returns a pointer for the created
    34  // veth, a pointer for the temporary link, the name of the temporary link and error if
    35  // something fails.
    36  func SetupVeth(id string, mtu, groIPv6MaxSize, gsoIPv6MaxSize, groIPv4MaxSize, gsoIPv4MaxSize int, ep *models.EndpointChangeRequest, sysctl sysctl.Sysctl) (*netlink.Veth, netlink.Link, string, error) {
    37  	if id == "" {
    38  		return nil, nil, "", fmt.Errorf("invalid: empty ID")
    39  	}
    40  
    41  	lxcIfName := Endpoint2IfName(id)
    42  	tmpIfName := Endpoint2TempIfName(id)
    43  
    44  	veth, link, err := SetupVethWithNames(lxcIfName, tmpIfName, mtu,
    45  		groIPv6MaxSize, gsoIPv6MaxSize, groIPv4MaxSize, gsoIPv4MaxSize, ep, sysctl)
    46  	return veth, link, tmpIfName, err
    47  }
    48  
    49  // SetupVethWithNames sets up the net interface, the peer interface and fills up some endpoint
    50  // fields such as mac, NodeMac, ifIndex and ifName. Returns a pointer for the created
    51  // veth, a pointer for the peer link and error if something fails.
    52  func SetupVethWithNames(lxcIfName, peerIfName string, mtu, groIPv6MaxSize, gsoIPv6MaxSize, groIPv4MaxSize, gsoIPv4MaxSize int, ep *models.EndpointChangeRequest, sysctl sysctl.Sysctl) (*netlink.Veth, netlink.Link, error) {
    53  	// systemd 242+ tries to set a "persistent" MAC addr for any virtual device
    54  	// by default (controlled by MACAddressPolicy). As setting happens
    55  	// asynchronously after a device has been created, ep.Mac and ep.HostMac
    56  	// can become stale which has a serious consequence - the kernel will drop
    57  	// any packet sent to/from the endpoint. However, we can trick systemd by
    58  	// explicitly setting MAC addrs for both veth ends. This sets
    59  	// addr_assign_type for NET_ADDR_SET which prevents systemd from changing
    60  	// the addrs.
    61  	epHostMAC, err := mac.GenerateRandMAC()
    62  	if err != nil {
    63  		return nil, nil, fmt.Errorf("unable to generate rnd mac addr: %w", err)
    64  	}
    65  	epLXCMAC, err := mac.GenerateRandMAC()
    66  	if err != nil {
    67  		return nil, nil, fmt.Errorf("unable to generate rnd mac addr: %w", err)
    68  	}
    69  
    70  	veth := &netlink.Veth{
    71  		LinkAttrs: netlink.LinkAttrs{
    72  			Name:         lxcIfName,
    73  			HardwareAddr: net.HardwareAddr(epHostMAC),
    74  			TxQLen:       1000,
    75  		},
    76  		PeerName:         peerIfName,
    77  		PeerHardwareAddr: net.HardwareAddr(epLXCMAC),
    78  	}
    79  
    80  	if err := netlink.LinkAdd(veth); err != nil {
    81  		return nil, nil, fmt.Errorf("unable to create veth pair: %w", err)
    82  	}
    83  	defer func() {
    84  		if err != nil {
    85  			if err = netlink.LinkDel(veth); err != nil {
    86  				log.WithError(err).WithField(logfields.Veth, veth.Name).Warn("failed to clean up veth")
    87  			}
    88  		}
    89  	}()
    90  
    91  	log.WithField(logfields.VethPair, []string{veth.PeerName, lxcIfName}).Debug("Created veth pair")
    92  
    93  	// Disable reverse path filter on the host side veth peer to allow
    94  	// container addresses to be used as source address when the linux
    95  	// stack performs routing.
    96  	err = DisableRpFilter(sysctl, lxcIfName)
    97  	if err != nil {
    98  		return nil, nil, err
    99  	}
   100  
   101  	peer, err := netlink.LinkByName(peerIfName)
   102  	if err != nil {
   103  		return nil, nil, fmt.Errorf("unable to lookup veth peer just created: %w", err)
   104  	}
   105  
   106  	if err = netlink.LinkSetMTU(peer, mtu); err != nil {
   107  		return nil, nil, fmt.Errorf("unable to set MTU to %q: %w", peerIfName, err)
   108  	}
   109  
   110  	hostVeth, err := netlink.LinkByName(lxcIfName)
   111  	if err != nil {
   112  		return nil, nil, fmt.Errorf("unable to lookup veth just created: %w", err)
   113  	}
   114  
   115  	if err = netlink.LinkSetMTU(hostVeth, mtu); err != nil {
   116  		return nil, nil, fmt.Errorf("unable to set MTU to %q: %w", lxcIfName, err)
   117  	}
   118  
   119  	if err = netlink.LinkSetUp(veth); err != nil {
   120  		return nil, nil, fmt.Errorf("unable to bring up veth pair: %w", err)
   121  	}
   122  
   123  	if groIPv6MaxSize > 0 {
   124  		if err = netlink.LinkSetGROMaxSize(hostVeth, groIPv6MaxSize); err != nil {
   125  			return nil, nil, fmt.Errorf("unable to set GRO max size to %q: %w",
   126  				lxcIfName, err)
   127  		}
   128  		if err = netlink.LinkSetGROMaxSize(peer, groIPv6MaxSize); err != nil {
   129  			return nil, nil, fmt.Errorf("unable to set GRO max size to %q: %w",
   130  				peerIfName, err)
   131  		}
   132  	}
   133  
   134  	if gsoIPv6MaxSize > 0 {
   135  		if err = netlink.LinkSetGSOMaxSize(hostVeth, gsoIPv6MaxSize); err != nil {
   136  			return nil, nil, fmt.Errorf("unable to set GSO max size to %q: %w",
   137  				lxcIfName, err)
   138  		}
   139  		if err = netlink.LinkSetGSOMaxSize(peer, gsoIPv6MaxSize); err != nil {
   140  			return nil, nil, fmt.Errorf("unable to set GSO max size to %q: %w",
   141  				peerIfName, err)
   142  		}
   143  	}
   144  
   145  	if groIPv4MaxSize > 0 {
   146  		if err = netlink.LinkSetGROIPv4MaxSize(hostVeth, groIPv4MaxSize); err != nil {
   147  			return nil, nil, fmt.Errorf("unable to set GRO max size to %q: %w",
   148  				lxcIfName, err)
   149  		}
   150  		if err = netlink.LinkSetGROIPv4MaxSize(peer, groIPv4MaxSize); err != nil {
   151  			return nil, nil, fmt.Errorf("unable to set GRO max size to %q: %w",
   152  				peerIfName, err)
   153  		}
   154  	}
   155  
   156  	if gsoIPv4MaxSize > 0 {
   157  		if err = netlink.LinkSetGSOIPv4MaxSize(hostVeth, gsoIPv4MaxSize); err != nil {
   158  			return nil, nil, fmt.Errorf("unable to set GSO max size to %q: %w",
   159  				lxcIfName, err)
   160  		}
   161  		if err = netlink.LinkSetGSOIPv4MaxSize(peer, gsoIPv4MaxSize); err != nil {
   162  			return nil, nil, fmt.Errorf("unable to set GSO max size to %q: %w",
   163  				peerIfName, err)
   164  		}
   165  	}
   166  
   167  	ep.Mac = peer.Attrs().HardwareAddr.String()
   168  	ep.HostMac = hostVeth.Attrs().HardwareAddr.String()
   169  	ep.InterfaceIndex = int64(hostVeth.Attrs().Index)
   170  	ep.InterfaceName = lxcIfName
   171  
   172  	return veth, peer, nil
   173  }