func()

in pkg/awsutils/awsutils.go [596:768]


func (cache *EC2InstanceMetadataCache) getENIMetadata(eniMAC string) (ENIMetadata, error) {
	ctx := context.TODO()

	log.Debugf("Found ENI MAC address: %s", eniMAC)
	var err error
	var deviceNum int

	eniID, err := cache.imds.GetInterfaceID(ctx, eniMAC)
	if err != nil {
		awsAPIErrInc("GetInterfaceID", err)
		return ENIMetadata{}, err
	}

	deviceNum, err = cache.imds.GetDeviceNumber(ctx, eniMAC)
	if err != nil {
		awsAPIErrInc("GetDeviceNumber", err)
		return ENIMetadata{}, err
	}

	primaryMAC, err := cache.imds.GetMAC(ctx)
	if err != nil {
		awsAPIErrInc("GetMAC", err)
		return ENIMetadata{}, err
	}
	if eniMAC == primaryMAC && deviceNum != 0 {
		// Can this even happen? To be backwards compatible, we will always use 0 here and log an error.
		log.Errorf("Device number of primary ENI is %d! Forcing it to be 0 as expected", deviceNum)
		deviceNum = 0
	}

	log.Debugf("Found ENI: %s, MAC %s, device %d", eniID, eniMAC, deviceNum)

	// Get IMDS fields for the interface
	macImdsFields, err := cache.imds.GetMACImdsFields(ctx, eniMAC)
	if err != nil {
		awsAPIErrInc("GetMACImdsFields", err)
		return ENIMetadata{}, err
	}
	ipv4Available := false
	ipv6Available := false
	// Efa-only interfaces do not have any ipv4s or ipv6s associated with it. If we don't find any local-ipv4 or ipv6 info in imds we assume it to be efa-only interface and validate this later via ec2 call
	for _, field := range macImdsFields {
		if field == "local-ipv4s" {
			imdsIPv4s, err := cache.imds.GetLocalIPv4s(ctx, eniMAC)
			if err != nil {
				awsAPIErrInc("GetLocalIPv4s", err)
				return ENIMetadata{}, err
			}
			if len(imdsIPv4s) > 0 {
				ipv4Available = true
				log.Debugf("Found IPv4 addresses associated with interface. This is not efa-only interface")
			}
		}
		if field == "ipv6s" {
			imdsIPv6s, err := cache.imds.GetIPv6s(ctx, eniMAC)
			if err != nil {
				awsAPIErrInc("GetIPv6s", err)
			} else if len(imdsIPv6s) > 0 {
				ipv6Available = true
				log.Debugf("Found IPv6 addresses associated with interface. This is not efa-only interface")
			}
		}
	}

	if !ipv4Available && !ipv6Available {
		return ENIMetadata{
			ENIID:          eniID,
			MAC:            eniMAC,
			DeviceNumber:   deviceNum,
			SubnetIPv4CIDR: "",
			IPv4Addresses:  make([]ec2types.NetworkInterfacePrivateIpAddress, 0),
			IPv4Prefixes:   make([]ec2types.Ipv4PrefixSpecification, 0),
			SubnetIPv6CIDR: "",
			IPv6Addresses:  make([]ec2types.NetworkInterfaceIpv6Address, 0),
			IPv6Prefixes:   make([]ec2types.Ipv6PrefixSpecification, 0),
		}, nil
	}

	// Get IPv4 and IPv6 addresses assigned to interface
	var ec2ip4s []ec2types.NetworkInterfacePrivateIpAddress
	var subnetV4Cidr string
	if ipv4Available {
		cidr, err := cache.imds.GetSubnetIPv4CIDRBlock(ctx, eniMAC)
		if err != nil {
			awsAPIErrInc("GetSubnetIPv4CIDRBlock", err)
			return ENIMetadata{}, err
		}

		subnetV4Cidr = cidr.String()

		imdsIPv4s, err := cache.imds.GetLocalIPv4s(ctx, eniMAC)
		if err != nil {
			awsAPIErrInc("GetLocalIPv4s", err)
			return ENIMetadata{}, err
		}

		ec2ip4s = make([]ec2types.NetworkInterfacePrivateIpAddress, len(imdsIPv4s))
		for i, ip4 := range imdsIPv4s {
			ec2ip4s[i] = ec2types.NetworkInterfacePrivateIpAddress{
				Primary:          aws.Bool(i == 0),
				PrivateIpAddress: aws.String(ip4.String()),
			}
		}
	}

	var ec2ip6s []ec2types.NetworkInterfaceIpv6Address
	var subnetV6Cidr string
	if cache.v6Enabled {
		// For IPv6 ENIs, do not error on missing IPv6 information
		v6cidr, err := cache.imds.GetSubnetIPv6CIDRBlocks(ctx, eniMAC)
		if err != nil {
			awsAPIErrInc("GetSubnetIPv6CIDRBlocks", err)
		} else {
			subnetV6Cidr = v6cidr.String()
		}

		imdsIPv6s, err := cache.imds.GetIPv6s(ctx, eniMAC)
		if err != nil {
			awsAPIErrInc("GetIPv6s", err)
		} else {
			ec2ip6s = make([]ec2types.NetworkInterfaceIpv6Address, len(imdsIPv6s))
			for i, ip6 := range imdsIPv6s {
				ec2ip6s[i] = ec2types.NetworkInterfaceIpv6Address{
					Ipv6Address: aws.String(ip6.String()),
				}
			}
		}
	}

	var ec2ipv4Prefixes []ec2types.Ipv4PrefixSpecification
	var ec2ipv6Prefixes []ec2types.Ipv6PrefixSpecification

	// If IPv6 is enabled, get attached v6 prefixes.
	if cache.v6Enabled {
		imdsIPv6Prefixes, err := cache.imds.GetIPv6Prefixes(ctx, eniMAC)
		if err != nil {
			awsAPIErrInc("GetIPv6Prefixes", err)
			return ENIMetadata{}, err
		}
		for _, ipv6prefix := range imdsIPv6Prefixes {
			ec2ipv6Prefixes = append(ec2ipv6Prefixes, ec2types.Ipv6PrefixSpecification{
				Ipv6Prefix: aws.String(ipv6prefix.String()),
			})
		}
	} else if cache.v4Enabled && ((eniMAC == primaryMAC && !cache.useCustomNetworking) || (eniMAC != primaryMAC)) {
		// Get prefix on primary ENI when custom networking is enabled is not needed.
		// If primary ENI has prefixes attached and then we move to custom networking, we don't need to fetch
		// the prefix since recommendation is to terminate the nodes and that would have deleted the prefix on the
		// primary ENI.
		imdsIPv4Prefixes, err := cache.imds.GetIPv4Prefixes(ctx, eniMAC)
		if err != nil {
			awsAPIErrInc("GetIPv4Prefixes", err)
			return ENIMetadata{}, err
		}
		for _, ipv4prefix := range imdsIPv4Prefixes {
			ec2ipv4Prefixes = append(ec2ipv4Prefixes, ec2types.Ipv4PrefixSpecification{
				Ipv4Prefix: aws.String(ipv4prefix.String()),
			})
		}
	}

	return ENIMetadata{
		ENIID:          eniID,
		MAC:            eniMAC,
		DeviceNumber:   deviceNum,
		SubnetIPv4CIDR: subnetV4Cidr,
		IPv4Addresses:  ec2ip4s,
		IPv4Prefixes:   ec2ipv4Prefixes,
		SubnetIPv6CIDR: subnetV6Cidr,
		IPv6Addresses:  ec2ip6s,
		IPv6Prefixes:   ec2ipv6Prefixes,
	}, nil
}