func()

in pkg/ipamd/rpc_handler.go [67:236]


func (s *server) AddNetwork(ctx context.Context, in *rpc.AddNetworkRequest) (*rpc.AddNetworkReply, error) {
	log.Infof("Received AddNetwork for NS %s, Sandbox %s, ifname %s",
		in.Netns, in.ContainerID, in.IfName)
	log.Debugf("AddNetworkRequest: %s", in)
	prometheusmetrics.AddIPCnt.Inc()

	// Do this early, but after logging trace
	if err := s.validateVersion(in.ClientVersion); err != nil {
		log.Warnf("Rejecting AddNetwork request: %v", err)
		return nil, err
	}

	failureResponse := rpc.AddNetworkReply{Success: false}
	var deviceNumber, vlanID, trunkENILinkIndex int
	var ipv4Addr, ipv6Addr, branchENIMAC, podENISubnetGW string
	var err error
	if s.ipamContext.enablePodENI {
		// Check pod spec for Branch ENI
		pod, err := s.ipamContext.GetPod(in.K8S_POD_NAME, in.K8S_POD_NAMESPACE)
		if err != nil {
			log.Warnf("Send AddNetworkReply: Failed to get pod: %v", err)
			return &failureResponse, nil
		}
		limits := pod.Spec.Containers[0].Resources.Limits
		for resName := range limits {
			if strings.HasPrefix(string(resName), "vpc.amazonaws.com/pod-eni") {
				// Check that we have a trunk
				trunkENI := s.ipamContext.dataStore.GetTrunkENI()
				if trunkENI == "" {
					log.Warn("Send AddNetworkReply: No trunk ENI found, cannot add a pod ENI")
					return &failureResponse, nil
				}
				trunkENILinkIndex, err = s.ipamContext.getTrunkLinkIndex()
				if err != nil {
					log.Warn("Send AddNetworkReply: No trunk ENI Link Index found, cannot add a pod ENI")
					return &failureResponse, nil
				}
				val, branch := pod.Annotations["vpc.amazonaws.com/pod-eni"]
				if branch {
					// Parse JSON data
					var podENIData []PodENIData
					err := json.Unmarshal([]byte(val), &podENIData)
					if err != nil || len(podENIData) < 1 {
						log.Errorf("Failed to unmarshal PodENIData JSON: %v", err)
						return &failureResponse, nil
					}
					firstENI := podENIData[0]
					// Get pod IPv4 or IPv6 address based on mode
					if s.ipamContext.enableIPv6 {
						ipv6Addr = firstENI.IPV6Addr
					} else {
						ipv4Addr = firstENI.PrivateIP
					}
					branchENIMAC = firstENI.IfAddress
					vlanID = firstENI.VlanID
					log.Debugf("Pod vlandId: %d", vlanID)

					if (ipv4Addr == "" && ipv6Addr == "") || branchENIMAC == "" || vlanID == 0 {
						log.Errorf("Failed to parse pod-ENI annotation: %s", val)
						return &failureResponse, nil
					}
					var subnetCIDR *net.IPNet
					if s.ipamContext.enableIPv6 {
						_, subnetCIDR, err = net.ParseCIDR(firstENI.SubnetV6CIDR)
						if err != nil {
							log.Errorf("Failed to parse V6 subnet CIDR: %s", firstENI.SubnetV6CIDR)
							return &failureResponse, nil
						}
					} else {
						_, subnetCIDR, err = net.ParseCIDR(firstENI.SubnetCIDR)
						if err != nil {
							log.Errorf("Failed to parse V4 subnet CIDR: %s", firstENI.SubnetCIDR)
							return &failureResponse, nil
						}
					}
					var gw net.IP
					// For IPv6, the gateway is derived from the RA route on the primary ENI. The primary ENI is always in the same subnet as the trunk and branch ENI.
					// For IPv4, the gateway is always the .1 address for the subnet CIDR.
					if s.ipamContext.enableIPv6 {
						gw = networkutils.GetIPv6Gateway()
					} else {
						gw = networkutils.GetIPv4Gateway(subnetCIDR)
					}
					podENISubnetGW = gw.String()
					deviceNumber = -1 // Not needed for branch ENI, they depend on trunkENIDeviceIndex
				} else {
					log.Infof("Send AddNetworkReply: failed to get Branch ENI resource")
					return &failureResponse, nil
				}
			}
		}
	}

	if s.ipamContext.enableIPv4 && ipv4Addr == "" ||
		s.ipamContext.enableIPv6 && ipv6Addr == "" {
		if in.ContainerID == "" || in.IfName == "" || in.NetworkName == "" {
			log.Errorf("Unable to generate IPAMKey from %+v", in)
			return &failureResponse, nil
		}
		ipamKey := datastore.IPAMKey{
			ContainerID: in.ContainerID,
			IfName:      in.IfName,
			NetworkName: in.NetworkName,
		}
		ipamMetadata := datastore.IPAMMetadata{
			K8SPodNamespace: in.K8S_POD_NAMESPACE,
			K8SPodName:      in.K8S_POD_NAME,
		}
		ipv4Addr, ipv6Addr, deviceNumber, err = s.ipamContext.dataStore.AssignPodIPAddress(ipamKey, ipamMetadata, s.ipamContext.enableIPv4, s.ipamContext.enableIPv6)
	}

	var pbVPCV4cidrs, pbVPCV6cidrs []string
	var useExternalSNAT bool
	if s.ipamContext.enableIPv4 && ipv4Addr != "" {
		pbVPCV4cidrs, err = s.ipamContext.awsClient.GetVPCIPv4CIDRs()
		if err != nil {
			return nil, err
		}
		for _, cidr := range pbVPCV4cidrs {
			log.Debugf("VPC CIDR %s", cidr)
		}
		useExternalSNAT = s.ipamContext.networkClient.UseExternalSNAT()
		if !useExternalSNAT {
			for _, cidr := range s.ipamContext.networkClient.GetExcludeSNATCIDRs() {
				log.Debugf("CIDR SNAT Exclusion %s", cidr)
				pbVPCV4cidrs = append(pbVPCV4cidrs, cidr)
			}
		}
	} else if s.ipamContext.enableIPv6 && ipv6Addr != "" {
		pbVPCV6cidrs, err = s.ipamContext.awsClient.GetVPCIPv6CIDRs()
		if err != nil {
			return nil, err
		}
		for _, cidr := range pbVPCV6cidrs {
			log.Debugf("VPC V6 CIDR %s", cidr)
		}
	}

	if s.ipamContext.enablePodIPAnnotation {
		// On ADD, we pass empty string as there is no IP being released
		if ipv4Addr != "" {
			err = s.ipamContext.AnnotatePod(in.K8S_POD_NAME, in.K8S_POD_NAMESPACE, vpccniPodIPKey, ipv4Addr, "")
			if err != nil {
				log.Errorf("Failed to add the pod annotation: %v", err)
			}
		} else if ipv6Addr != "" {
			err = s.ipamContext.AnnotatePod(in.K8S_POD_NAME, in.K8S_POD_NAMESPACE, vpccniPodIPKey, ipv6Addr, "")
			if err != nil {
				log.Errorf("Failed to add the pod annotation: %v", err)
			}
		}
	}
	resp := rpc.AddNetworkReply{
		Success:           err == nil,
		IPv4Addr:          ipv4Addr,
		IPv6Addr:          ipv6Addr,
		DeviceNumber:      int32(deviceNumber),
		UseExternalSNAT:   useExternalSNAT,
		VPCv4CIDRs:        pbVPCV4cidrs,
		VPCv6CIDRs:        pbVPCV6cidrs,
		PodVlanId:         int32(vlanID),
		PodENIMAC:         branchENIMAC,
		PodENISubnetGW:    podENISubnetGW,
		ParentIfIndex:     int32(trunkENILinkIndex),
		NetworkPolicyMode: s.ipamContext.networkPolicyMode,
	}

	log.Infof("Send AddNetworkReply: IPv4Addr: %s, IPv6Addr: %s, DeviceNumber: %d, err: %v", ipv4Addr, ipv6Addr, deviceNumber, err)
	return &resp, nil
}