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 }