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
}