pkg/utils/cniutils/cni_utils.go (159 lines of code) (raw):

package cniutils import ( "fmt" "strings" "syscall" "time" current "github.com/containernetworking/cni/pkg/types/100" "github.com/coreos/go-iptables/iptables" "github.com/vishvananda/netlink" "github.com/aws/amazon-vpc-cni-k8s/pkg/netlinkwrapper" "github.com/aws/amazon-vpc-cni-k8s/pkg/procsyswrapper" "github.com/aws/amazon-vpc-cni-k8s/utils/imds" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" ) const ( ipv4ForwardKey = "net/ipv4/ip_forward" ipv6ForwardKey = "net/ipv6/conf/all/forwarding" ) func FindInterfaceByName(ifaceList []*current.Interface, ifaceName string) (ifaceIndex int, iface *current.Interface, found bool) { for ifaceIndex, iface := range ifaceList { if iface.Name == ifaceName { return ifaceIndex, iface, true } } return 0, nil, false } func FindIPConfigsByIfaceIndex(ipConfigs []*current.IPConfig, ifaceIndex int) []*current.IPConfig { var matchedIPConfigs []*current.IPConfig for _, ipConfig := range ipConfigs { if ipConfig.Interface != nil && *ipConfig.Interface == ifaceIndex { matchedIPConfigs = append(matchedIPConfigs, ipConfig) } } return matchedIPConfigs } // WaitForAddressesToBeStable Implements `SettleAddresses` functionality of the `ip` package. // waitForAddressesToBeStable waits for all addresses on a link to leave tentative state. // Will be particularly useful for ipv6, where all addresses need to do DAD. // If any addresses are still tentative after timeout seconds, then error. func WaitForAddressesToBeStable(netLink netlinkwrapper.NetLink, ifName string, timeout, waitInterval time.Duration) error { link, err := netLink.LinkByName(ifName) if err != nil { return fmt.Errorf("failed to retrieve link: %v", err) } deadline := time.Now().Add(timeout) for { addrs, err := netLink.AddrList(link, netlink.FAMILY_V6) if err != nil { return fmt.Errorf("could not list addresses: %v", err) } ok := true for _, addr := range addrs { if addr.Flags&(syscall.IFA_F_TENTATIVE|syscall.IFA_F_DADFAILED) > 0 { ok = false break } } if ok { return nil } if time.Now().After(deadline) { return fmt.Errorf("link %s still has tentative addresses after %d seconds", ifName, timeout) } time.Sleep(waitInterval) } } // GetNodeMetadata calling node local imds metadata service using provided key // return either a non-empty value or an error func GetNodeMetadata(key string) (string, error) { var value string var err error for { value, err = imds.GetMetaData(key) if err != nil { return "", err } if value != "" { return value, nil } } } // EnableIpForwarding sets forwarding to 1 for both IPv4 and IPv6 if applicable. // This func is to have a unit testable version of ip.EnableForward in ipforward_linux.go file // link: https://github.com/containernetworking/plugins/blob/main/pkg/ip/ipforward_linux.go#L34 func EnableIpForwarding(procSys procsyswrapper.ProcSys, ips []*current.IPConfig) error { v4 := false v6 := false for _, ip := range ips { isV4 := ip.Address.IP.To4() != nil if isV4 && !v4 { valueV4, err := procSys.Get(ipv4ForwardKey) if err != nil { return err } if valueV4 != "1" { err = procSys.Set(ipv4ForwardKey, "1") if err != nil { return err } } v4 = true } else if !isV4 && !v6 { valueV6, err := procSys.Get(ipv6ForwardKey) if err != nil { return err } if valueV6 != "1" { err = procSys.Set(ipv6ForwardKey, "1") if err != nil { return err } } v6 = true } } return nil } // IsLinkNotFoundError return true if err contains "Link not found" func IsLinkNotFoundError(err error) bool { return strings.Contains(err.Error(), "Link not found") } // IsIptableTargetNotExist returns true if the error is from iptables indicating // that the target does not exist. func IsIptableTargetNotExist(err error) bool { e, ok := err.(*iptables.Error) if !ok { return false } return e.IsNotExist() } // PrefixSimilar checks if prefix pool and eni prefix are equivalent. func PrefixSimilar(prefixPool []string, eniPrefixes []ec2types.Ipv4PrefixSpecification) bool { if len(prefixPool) != len(eniPrefixes) { return false } prefixPoolSet := make(map[string]struct{}, len(prefixPool)) for _, ip := range prefixPool { prefixPoolSet[ip] = struct{}{} } for _, prefix := range eniPrefixes { if prefix.Ipv4Prefix == nil { return false } if _, exists := prefixPoolSet[*prefix.Ipv4Prefix]; !exists { return false } } return true } // IPsSimilar checks if ipPool and eniIPs are equivalent. func IPsSimilar(ipPool []string, eniIPs []ec2types.NetworkInterfacePrivateIpAddress) bool { // Here we do +1 in ipPool because eniIPs will also have primary IP which is not used by pods. if len(ipPool)+1 != len(eniIPs) { return false } ipPoolSet := make(map[string]struct{}, len(ipPool)) for _, ip := range ipPool { ipPoolSet[ip] = struct{}{} } for _, ip := range eniIPs { if ip.PrivateIpAddress == nil || ip.Primary == nil { return false } if *ip.Primary { continue } if _, exists := ipPoolSet[*ip.PrivateIpAddress]; !exists { return false } } return true }