Skip to content

Commit 419d904

Browse files
committed
Prefix on NIC v6 support
1 parent b845d57 commit 419d904

File tree

8 files changed

+602
-307
lines changed

8 files changed

+602
-307
lines changed

azure-ipam/ipam.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func (p *IPAMPlugin) CmdAdd(args *cniSkel.CmdArgs) error {
115115
p.logger.Debug("Received CNS IP config response", zap.Any("response", resp))
116116

117117
// Get Pod IP and gateway IP from ip config response
118-
podIPNet, err := ipconfig.ProcessIPConfigsResp(resp)
118+
podIPNet, gatewayIP, err := ipconfig.ProcessIPConfigsResp(resp)
119119
if err != nil {
120120
p.logger.Error("Failed to interpret CNS IPConfigResponse", zap.Error(err), zap.Any("response", resp))
121121
return cniTypes.NewError(ErrProcessIPConfigResponse, err.Error(), "failed to interpret CNS IPConfigResponse")
@@ -136,9 +136,34 @@ func (p *IPAMPlugin) CmdAdd(args *cniSkel.CmdArgs) error {
136136
Mask: net.CIDRMask(ipNet.Bits(), 128), // nolint
137137
}
138138
}
139+
ipConfig.Gateway = (*gatewayIP)[i]
139140
cniResult.IPs[i] = ipConfig
140141
}
141142

143+
cniResult.Interfaces = []*types100.Interface{}
144+
seenInterfaces := map[string]bool{}
145+
146+
for _, podIPInfo := range resp.PodIPInfo {
147+
// Skip if interface already seen
148+
// This is to avoid duplicate interfaces in the result
149+
// which can happen if multiple IPs are assigned to the same interface
150+
// or if multiple interfaces are assigned to the same pod
151+
if podIPInfo.MacAddress == "" || seenInterfaces[podIPInfo.MacAddress] {
152+
continue
153+
}
154+
155+
infMac, err := net.ParseMAC(podIPInfo.MacAddress)
156+
if err != nil {
157+
p.logger.Error("Failed to parse interface MAC address", zap.Error(err), zap.String("macAddress", podIPInfo.MacAddress))
158+
return cniTypes.NewError(cniTypes.ErrUnsupportedField, err.Error(), "failed to parse interface MAC address")
159+
}
160+
161+
cniResult.Interfaces = append(cniResult.Interfaces, &types100.Interface{
162+
Mac: infMac.String(),
163+
})
164+
seenInterfaces[podIPInfo.MacAddress] = true
165+
}
166+
142167
// Get versioned result
143168
versionedCniResult, err := cniResult.GetAsVersion(nwCfg.CNIVersion)
144169
if err != nil {

azure-ipam/ipconfig/ipconfig.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ipconfig
33
import (
44
"encoding/json"
55
"fmt"
6+
"net"
67
"net/netip"
78

89
"github.com/Azure/azure-container-networking/cns"
@@ -63,23 +64,36 @@ func CreateIPConfigsReq(args *cniSkel.CmdArgs) (cns.IPConfigsRequest, error) {
6364
return req, nil
6465
}
6566

66-
func ProcessIPConfigsResp(resp *cns.IPConfigsResponse) (*[]netip.Prefix, error) {
67+
func ProcessIPConfigsResp(resp *cns.IPConfigsResponse) (*[]netip.Prefix, *[]net.IP, error) {
6768
podIPNets := make([]netip.Prefix, len(resp.PodIPInfo))
69+
gatewaysIPs := make([]net.IP, len(resp.PodIPInfo))
6870

6971
for i := range resp.PodIPInfo {
72+
var gatewayIP net.IP
73+
7074
podCIDR := fmt.Sprintf(
7175
"%s/%d",
7276
resp.PodIPInfo[i].PodIPConfig.IPAddress,
7377
resp.PodIPInfo[i].NetworkContainerPrimaryIPConfig.IPSubnet.PrefixLength,
7478
)
7579
podIPNet, err := netip.ParsePrefix(podCIDR)
7680
if err != nil {
77-
return nil, errors.Wrapf(err, "cns returned invalid pod CIDR %q", podCIDR)
81+
return nil, nil, errors.Wrapf(err, "cns returned invalid pod CIDR %q", podCIDR)
7882
}
7983
podIPNets[i] = podIPNet
84+
85+
if podIPNet.Addr().Is4() {
86+
gatewayIP = net.ParseIP(resp.PodIPInfo[i].NetworkContainerPrimaryIPConfig.GatewayIPAddress)
87+
} else if podIPNet.Addr().Is6() {
88+
gatewayIP = net.ParseIP(resp.PodIPInfo[i].NetworkContainerPrimaryIPConfig.GatewayIPv6Address)
89+
}
90+
91+
if gatewayIP != nil {
92+
gatewaysIPs[i] = gatewayIP
93+
}
8094
}
8195

82-
return &podIPNets, nil
96+
return &podIPNets, &gatewaysIPs, nil
8397
}
8498

8599
type k8sPodEnvArgs struct {

cns/NetworkContainerContract.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,9 +389,10 @@ type NetworkInterfaceInfo struct {
389389

390390
// IPConfiguration contains details about ip config to provision in the VM.
391391
type IPConfiguration struct {
392-
IPSubnet IPSubnet
393-
DNSServers []string
394-
GatewayIPAddress string
392+
IPSubnet IPSubnet
393+
DNSServers []string
394+
GatewayIPAddress string
395+
GatewayIPv6Address string
395396
}
396397

397398
// SecondaryIPConfig contains IP info of SecondaryIP
@@ -746,3 +747,11 @@ type NodeRegisterRequest struct {
746747
NumCores int
747748
NmAgentSupportedApis []string
748749
}
750+
751+
// IPFamily - Enum for determining IPFamily when retrieving IPs from network containers
752+
type IPFamily string
753+
754+
const (
755+
IPv4 IPFamily = "ipv4"
756+
IPv6 IPFamily = "ipv6"
757+
)

cns/kubecontroller/nodenetworkconfig/conversion_linux.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ import (
1616
func createNCRequestFromStaticNCHelper(nc v1alpha.NetworkContainer, primaryIPPrefix netip.Prefix, subnet cns.IPSubnet) (*cns.CreateNetworkContainerRequest, error) {
1717
secondaryIPConfigs := map[string]cns.SecondaryIPConfig{}
1818

19-
// iterate through all IP addresses in the subnet described by primaryPrefix and
20-
// add them to the request as secondary IPConfigs.
21-
for addr := primaryIPPrefix.Masked().Addr(); primaryIPPrefix.Contains(addr); addr = addr.Next() {
22-
secondaryIPConfigs[addr.String()] = cns.SecondaryIPConfig{
23-
IPAddress: addr.String(),
24-
NCVersion: int(nc.Version),
19+
// in the case of vnet prefix on swift v2 the primary IP is a /32 and should not be added to secondary IP configs
20+
if !primaryIPPrefix.IsSingleIP() {
21+
// iterate through all IP addresses in the subnet described by primaryPrefix and
22+
// add them to the request as secondary IPConfigs.
23+
for addr := primaryIPPrefix.Masked().Addr(); primaryIPPrefix.Contains(addr); addr = addr.Next() {
24+
secondaryIPConfigs[addr.String()] = cns.SecondaryIPConfig{
25+
IPAddress: addr.String(),
26+
NCVersion: int(nc.Version),
27+
}
2528
}
2629
}
2730

@@ -52,9 +55,13 @@ func createNCRequestFromStaticNCHelper(nc v1alpha.NetworkContainer, primaryIPPre
5255
NetworkContainerType: cns.Docker,
5356
Version: strconv.FormatInt(nc.Version, 10), //nolint:gomnd // it's decimal
5457
IPConfiguration: cns.IPConfiguration{
55-
IPSubnet: subnet,
56-
GatewayIPAddress: nc.DefaultGateway,
58+
IPSubnet: subnet,
59+
GatewayIPAddress: nc.DefaultGateway,
60+
GatewayIPv6Address: nc.DefaultGatewayV6,
5761
},
5862
NCStatus: nc.Status,
63+
NetworkInterfaceInfo: cns.NetworkInterfaceInfo{
64+
MACAddress: nc.MacAddress,
65+
},
5966
}, nil
6067
}

cns/restserver/internalapi_linux.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ func (service *HTTPRestService) programSNATRules(req *cns.CreateNetworkContainer
6868

6969
// use any secondary ip + the nnc prefix length to get an iptables rule to allow dns and imds traffic from the pods
7070
for _, v := range req.SecondaryIPConfigs {
71+
// check if the ip address is IPv4
72+
if net.ParseIP(v.IPAddress).To4() == nil {
73+
// skip if the ip address is not IPv4
74+
continue
75+
}
76+
7177
// put the ip address in standard cidr form (where we zero out the parts that are not relevant)
7278
_, podSubnet, _ := net.ParseCIDR(v.IPAddress + "/" + fmt.Sprintf("%d", req.IPConfiguration.IPSubnet.PrefixLength))
7379

cns/restserver/ipam.go

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -995,38 +995,85 @@ func (service *HTTPRestService) AssignAvailableIPConfigs(podInfo cns.PodInfo) ([
995995
if numOfNCs == 0 {
996996
return nil, ErrNoNCs
997997
}
998+
999+
// Map used to get the number of IPFamilies across all NCs
1000+
ncIPFamilies := map[cns.IPFamily]struct{}{}
1001+
// Gets the IPFamilies from all NCs and store them in a map. This will be used to determine the number of IPs to return
1002+
for ncID := range service.state.ContainerStatus {
1003+
if len(ncIPFamilies) == 2 {
1004+
break
1005+
}
1006+
1007+
for _, secIPConfig := range service.state.ContainerStatus[ncID].CreateNetworkContainerRequest.SecondaryIPConfigs {
1008+
if len(ncIPFamilies) == 2 {
1009+
break
1010+
}
1011+
1012+
ip := net.ParseIP(secIPConfig.IPAddress)
1013+
if ip == nil {
1014+
continue
1015+
}
1016+
1017+
if ip.To4() != nil {
1018+
ncIPFamilies[cns.IPv4] = struct{}{}
1019+
} else {
1020+
ncIPFamilies[cns.IPv6] = struct{}{}
1021+
}
1022+
}
1023+
}
1024+
// Makes sure we have at least one IPFamily across all NCs
1025+
numOfIPFamilies := len(ncIPFamilies)
1026+
1027+
numberOfIPs := numOfNCs
1028+
if numOfIPFamilies != 0 {
1029+
numberOfIPs = numOfIPFamilies
1030+
}
1031+
9981032
service.Lock()
9991033
defer service.Unlock()
10001034
// Creates a slice of PodIpInfo with the size as number of NCs to hold the result for assigned IP configs
1001-
podIPInfo := make([]cns.PodIpInfo, numOfNCs)
1035+
podIPInfo := make([]cns.PodIpInfo, numberOfIPs)
10021036
// This map is used to store whether or not we have found an available IP from an NC when looping through the pool
10031037
ipsToAssign := make(map[string]cns.IPConfigurationStatus)
10041038

10051039
// Searches for available IPs in the pool
10061040
for _, ipState := range service.PodIPConfigState {
1007-
// check if an IP from this NC is already set side for assignment.
1008-
if _, ncAlreadyMarkedForAssignment := ipsToAssign[ipState.NCID]; ncAlreadyMarkedForAssignment {
1041+
1042+
// get the IPFamily of the current ipState
1043+
var ipStateFamily cns.IPFamily
1044+
if net.ParseIP(ipState.IPAddress).To4() != nil {
1045+
ipStateFamily = cns.IPv4
1046+
} else {
1047+
ipStateFamily = cns.IPv6
1048+
}
1049+
1050+
key := generateAssignedIPKey(ipState.NCID, ipStateFamily)
1051+
1052+
// check if the IP with the same family type exists already
1053+
if _, ncIPFamilyAlreadyMarkedForAssignment := ipsToAssign[key]; ncIPFamilyAlreadyMarkedForAssignment {
10091054
continue
10101055
}
10111056
// Checks if the current IP is available
10121057
if ipState.GetState() != types.Available {
10131058
continue
10141059
}
1015-
ipsToAssign[ipState.NCID] = ipState
1016-
// Once one IP per container is found break out of the loop and stop searching
1017-
if len(ipsToAssign) == numOfNCs {
1060+
ipsToAssign[key] = ipState
1061+
// Once numberOfIPs per container is found break out of the loop and stop searching
1062+
if len(ipsToAssign) == numberOfIPs {
10181063
break
10191064
}
10201065
}
10211066

1022-
// Checks to make sure we found one IP for each NC
1023-
if len(ipsToAssign) != numOfNCs {
1067+
// Checks to make sure we found one IP for each NCxIPFamily
1068+
if len(ipsToAssign) != numberOfIPs {
10241069
for ncID := range service.state.ContainerStatus {
1025-
if _, found := ipsToAssign[ncID]; found {
1026-
continue
1070+
for ipFamily := range ncIPFamilies {
1071+
if _, found := ipsToAssign[generateAssignedIPKey(ncID, ipFamily)]; found {
1072+
continue
1073+
}
1074+
return podIPInfo, errors.Errorf("not enough IPs available of type %s for %s, waiting on Azure CNS to allocate more with NC Status: %s",
1075+
ipFamily, ncID, string(service.state.ContainerStatus[ncID].CreateNetworkContainerRequest.NCStatus))
10271076
}
1028-
return podIPInfo, errors.Errorf("not enough IPs available for %s, waiting on Azure CNS to allocate more with NC Status: %s",
1029-
ncID, string(service.state.ContainerStatus[ncID].CreateNetworkContainerRequest.NCStatus))
10301077
}
10311078
}
10321079

@@ -1061,10 +1108,14 @@ func (service *HTTPRestService) AssignAvailableIPConfigs(podInfo cns.PodInfo) ([
10611108
return podIPInfo, fmt.Errorf("not enough IPs available, waiting on Azure CNS to allocate more")
10621109
}
10631110

1064-
logger.Printf("[AssignDesiredIPConfigs] Successfully assigned IPs for pod %+v", podInfo)
1111+
logger.Printf("[AssignAvailableIPConfigs] Successfully assigned IPs for pod %+v", podInfo)
10651112
return podIPInfo, nil
10661113
}
10671114

1115+
func generateAssignedIPKey(ncID string, ipFamily cns.IPFamily) string {
1116+
return fmt.Sprintf("%s_%s", ncID, string(ipFamily))
1117+
}
1118+
10681119
// If IPConfigs are already assigned to the pod, it returns that else it returns the available ipconfigs.
10691120
func requestIPConfigsHelper(service *HTTPRestService, req cns.IPConfigsRequest) ([]cns.PodIpInfo, error) {
10701121
// check if ipconfigs already assigned to this pod and return if exists or error

0 commit comments

Comments
 (0)