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

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package link
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"strconv"
    11  	"sync"
    12  
    13  	"github.com/vishvananda/netlink"
    14  
    15  	"github.com/cilium/cilium/pkg/controller"
    16  	"github.com/cilium/cilium/pkg/lock"
    17  	"github.com/cilium/cilium/pkg/mac"
    18  	"github.com/cilium/cilium/pkg/time"
    19  )
    20  
    21  var (
    22  	// linkCache is the singleton instance of the LinkCache, only needed to
    23  	// ensure that the single controller used to update the LinkCache is
    24  	// triggered exactly once and the same instance is handed to all users.
    25  	linkCache LinkCache
    26  	once      sync.Once
    27  
    28  	linkCacheControllerGroup = controller.NewGroup("link-cache")
    29  )
    30  
    31  // DeleteByName deletes the interface with the name ifName.
    32  //
    33  // Returns nil if the interface does not exist.
    34  func DeleteByName(ifName string) error {
    35  	iface, err := netlink.LinkByName(ifName)
    36  	if errors.As(err, &netlink.LinkNotFoundError{}) {
    37  		return nil
    38  	}
    39  
    40  	if err != nil {
    41  		return fmt.Errorf("failed to lookup %q: %w", ifName, err)
    42  	}
    43  
    44  	if err = netlink.LinkDel(iface); err != nil {
    45  		return fmt.Errorf("failed to delete %q: %w", ifName, err)
    46  	}
    47  
    48  	return nil
    49  }
    50  
    51  // Rename renames a network link
    52  func Rename(curName, newName string) error {
    53  	link, err := netlink.LinkByName(curName)
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	return netlink.LinkSetName(link, newName)
    59  }
    60  
    61  func GetHardwareAddr(ifName string) (mac.MAC, error) {
    62  	iface, err := netlink.LinkByName(ifName)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	return mac.MAC(iface.Attrs().HardwareAddr), nil
    67  }
    68  
    69  func GetIfIndex(ifName string) (uint32, error) {
    70  	iface, err := netlink.LinkByName(ifName)
    71  	if err != nil {
    72  		return 0, err
    73  	}
    74  	return uint32(iface.Attrs().Index), nil
    75  }
    76  
    77  type LinkCache struct {
    78  	mu          lock.RWMutex
    79  	indexToName map[int]string
    80  }
    81  
    82  // NewLinkCache begins monitoring local interfaces for changes in order to
    83  // track local link information.
    84  func NewLinkCache() *LinkCache {
    85  	once.Do(func() {
    86  		linkCache = LinkCache{}
    87  		controller.NewManager().UpdateController("link-cache",
    88  			controller.ControllerParams{
    89  				Group:       linkCacheControllerGroup,
    90  				RunInterval: 15 * time.Second,
    91  				DoFunc: func(ctx context.Context) error {
    92  					return linkCache.syncCache()
    93  				},
    94  			},
    95  		)
    96  	})
    97  
    98  	return &linkCache
    99  }
   100  
   101  func (c *LinkCache) syncCache() error {
   102  	links, err := netlink.LinkList()
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	indexToName := make(map[int]string, len(links))
   108  	for _, link := range links {
   109  		indexToName[link.Attrs().Index] = link.Attrs().Name
   110  	}
   111  
   112  	c.mu.Lock()
   113  	c.indexToName = indexToName
   114  	c.mu.Unlock()
   115  	return nil
   116  }
   117  
   118  func (c *LinkCache) lookupName(ifIndex int) (string, bool) {
   119  	c.mu.RLock()
   120  	defer c.mu.RUnlock()
   121  
   122  	name, ok := c.indexToName[ifIndex]
   123  	return name, ok
   124  }
   125  
   126  // GetIfNameCached returns the name of an interface (if it exists) by looking
   127  // it up in a regularly updated cache. The return result is the same as a map
   128  // lookup, ie nil, false if there is no entry cached for this ifindex.
   129  func (c *LinkCache) GetIfNameCached(ifIndex int) (string, bool) {
   130  	return c.lookupName(ifIndex)
   131  }
   132  
   133  // Name returns the name of a link by looking up the 'LinkCache', or returns a
   134  // string containing the ifindex on cache miss.
   135  func (c *LinkCache) Name(ifIndex uint32) string {
   136  	if name, ok := c.lookupName(int(ifIndex)); ok {
   137  		return name
   138  	}
   139  	return strconv.Itoa(int(ifIndex))
   140  }