Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e52c30e
Prefix on NIC v6 support
kadevu Mar 21, 2025
01bb560
update dedup comment in ipam
nihanMicrosoft May 5, 2025
169e961
add log for a gatway error scenario
nihanMicrosoft May 20, 2025
8221ee9
handle synchostversion sync on pon swiftv2 nic
NihaNallappagari Jun 2, 2025
22c31c4
handle gatewayip parse failure or nil scenario
NihaNallappagari Jun 9, 2025
b03571a
remove unused code
NihaNallappagari Jun 9, 2025
789976d
add test scenario
NihaNallappagari Jun 16, 2025
869b774
update test to handle gatway nil case
NihaNallappagari Jun 16, 2025
bec7eb5
remove synchost skip and single ip skip
NihaNallappagari Jun 17, 2025
e8adcf4
Add missing test scenarios
NihaNallappagari Jun 25, 2025
b9aa947
fix ipam_test conflicts
NihaNallappagari Jun 25, 2025
0bd7bbb
fix conflits
NihaNallappagari Jun 25, 2025
119922a
fix linting
NihaNallappagari Jun 26, 2025
cb22a9d
add todo and remove multiple interface scenario
NihaNallappagari Jul 1, 2025
f37b4ed
Skip add primary ip's to secondary config for swiftv2 pon scenarios
NihaNallappagari Jul 7, 2025
0307051
code review comment
NihaNallappagari Aug 7, 2025
f555392
Update gateway ipv6 to use default value, that auto detects and adds …
NihaNallappagari Aug 20, 2025
920d081
remove unwanted changes
NihaNallappagari Sep 4, 2025
fe5e047
address ipfamily comment
NihaNallappagari Sep 9, 2025
8176411
fix test failure
NihaNallappagari Sep 10, 2025
d242430
Handle nilgateway scenario
NihaNallappagari Sep 16, 2025
ec755f6
remove unnedded else block
NihaNallappagari Sep 23, 2025
6e1a0d9
comments changes
NihaNallappagari Sep 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion azure-ipam/ipam.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func (p *IPAMPlugin) CmdAdd(args *cniSkel.CmdArgs) error {
p.logger.Debug("Received CNS IP config response", zap.Any("response", resp))

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

cniResult.Interfaces = []*types100.Interface{}
seenInterfaces := map[string]bool{}

for _, podIPInfo := range resp.PodIPInfo {
// Skip if interface already seen
// This is to avoid duplicate interfaces in the result
// Deduplication is necessary because there is one podIPInfo entry for each IP family(IPv4 and IPv6), and both may point to the same interface
if podIPInfo.MacAddress == "" || seenInterfaces[podIPInfo.MacAddress] {
continue
}

infMac, err := net.ParseMAC(podIPInfo.MacAddress)
if err != nil {
p.logger.Error("Failed to parse interface MAC address", zap.Error(err), zap.String("macAddress", podIPInfo.MacAddress))
return cniTypes.NewError(cniTypes.ErrUnsupportedField, err.Error(), "failed to parse interface MAC address")
}

cniResult.Interfaces = append(cniResult.Interfaces, &types100.Interface{
Mac: infMac.String(),
})
seenInterfaces[podIPInfo.MacAddress] = true
}

// Get versioned result
versionedCniResult, err := cniResult.GetAsVersion(nwCfg.CNIVersion)
if err != nil {
Expand Down
151 changes: 146 additions & 5 deletions azure-ipam/ipam_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,68 @@ func (c *MockCNSClient) RequestIPAddress(ctx context.Context, ipconfig cns.IPCon
},
}
return result, nil
case "nilGateway":
result := &cns.IPConfigResponse{
PodIpInfo: cns.PodIpInfo{
PodIPConfig: cns.IPSubnet{
IPAddress: "10.0.1.10",
PrefixLength: 24,
},
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
IPSubnet: cns.IPSubnet{
IPAddress: "10.0.1.0",
PrefixLength: 24,
},
DNSServers: nil,
GatewayIPAddress: "", // nil/empty gateway
},
HostPrimaryIPInfo: cns.HostIPInfo{
Gateway: "",
PrimaryIP: "10.0.0.1",
Subnet: "10.0.0.0/24",
},
},
Response: cns.Response{
ReturnCode: 0,
Message: "",
},
}
return result, nil
case "invalidGateway":
result := &cns.IPConfigResponse{
PodIpInfo: cns.PodIpInfo{
PodIPConfig: cns.IPSubnet{
IPAddress: "10.0.1.10",
PrefixLength: 24,
},
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
IPSubnet: cns.IPSubnet{
IPAddress: "10.0.1.0",
PrefixLength: 24,
},
DNSServers: nil,
GatewayIPAddress: "invalidgatewayip",
},
HostPrimaryIPInfo: cns.HostIPInfo{
Gateway: "invalidgatewayip",
PrimaryIP: "10.0.0.1",
Subnet: "10.0.0.0/24",
},
},
Response: cns.Response{
ReturnCode: 0,
Message: "",
},
}
return result, nil
default:
result := &cns.IPConfigResponse{
PodIpInfo: cns.PodIpInfo{
PodIPConfig: cns.IPSubnet{
IPAddress: "10.0.1.10",
PrefixLength: 24,
},
MacAddress: "00:11:22:33:44:55",
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
IPSubnet: cns.IPSubnet{
IPAddress: "10.0.1.0",
Expand Down Expand Up @@ -92,11 +147,60 @@ func (c *MockCNSClient) RequestIPs(ctx context.Context, ipconfig cns.IPConfigsRe
switch ipconfig.InfraContainerID {
case "failRequestCNSArgs":
return nil, errFoo
case "happyArgsSingle", "failProcessCNSRespSingleIP", "failRequestCNSArgsSingleIP":
case "happyArgsSingle", "failProcessCNSRespSingleIP", "failRequestCNSArgsSingleIP", "nilGateway", "invalidGateway":
e := &client.CNSClientError{}
e.Code = types.UnsupportedAPI
e.Err = errUnsupportedAPI
return nil, e
case "happyArgsDual":
result := &cns.IPConfigsResponse{
PodIPInfo: []cns.PodIpInfo{
{
PodIPConfig: cns.IPSubnet{
IPAddress: "10.0.1.10",
PrefixLength: 24,
},
MacAddress: "00:11:22:33:44:55",
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
IPSubnet: cns.IPSubnet{
IPAddress: "10.0.1.0",
PrefixLength: 24,
},
DNSServers: nil,
GatewayIPAddress: "10.0.0.1",
},
HostPrimaryIPInfo: cns.HostIPInfo{
Gateway: "10.0.0.1",
PrimaryIP: "10.0.0.1",
Subnet: "10.0.0.0/24",
},
},
{
PodIPConfig: cns.IPSubnet{
IPAddress: "fd11:1234::1",
PrefixLength: 120,
},
MacAddress: "00:11:22:33:44:55", // Same MAC for dual-stack scenario
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
IPSubnet: cns.IPSubnet{
IPAddress: "fd11:1234::",
PrefixLength: 120,
},
DNSServers: nil,
},
HostPrimaryIPInfo: cns.HostIPInfo{
Gateway: "fe80::1234:5678:9abc",
PrimaryIP: "fe80::1234:5678:9abc",
Subnet: "fd11:1234::/120",
},
},
},
Response: cns.Response{
ReturnCode: 0,
Message: "",
},
}
return result, nil
case "failProcessCNSResp":
result := &cns.IPConfigsResponse{
PodIPInfo: []cns.PodIpInfo{
Expand Down Expand Up @@ -129,8 +233,7 @@ func (c *MockCNSClient) RequestIPs(ctx context.Context, ipconfig cns.IPConfigsRe
IPAddress: "fd11:1234::",
PrefixLength: 112,
},
DNSServers: nil,
GatewayIPAddress: "fe80::1234:5678:9abc",
DNSServers: nil,
},
HostPrimaryIPInfo: cns.HostIPInfo{
Gateway: "fe80::1234:5678:9abc",
Expand All @@ -153,6 +256,7 @@ func (c *MockCNSClient) RequestIPs(ctx context.Context, ipconfig cns.IPConfigsRe
IPAddress: "10.0.1.10",
PrefixLength: 24,
},
MacAddress: "00:11:22:33:44:55",
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
IPSubnet: cns.IPSubnet{
IPAddress: "10.0.1.0",
Expand All @@ -172,13 +276,13 @@ func (c *MockCNSClient) RequestIPs(ctx context.Context, ipconfig cns.IPConfigsRe
IPAddress: "fd11:1234::1",
PrefixLength: 120,
},
MacAddress: "00:11:22:33:44:55", // Same MAC for dual-stack scenario
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
IPSubnet: cns.IPSubnet{
IPAddress: "fd11:1234::",
PrefixLength: 120,
},
DNSServers: nil,
GatewayIPAddress: "fe80::1234:5678:9abc",
DNSServers: nil,
},
HostPrimaryIPInfo: cns.HostIPInfo{
Gateway: "fe80::1234:5678:9abc",
Expand Down Expand Up @@ -281,12 +385,18 @@ func TestCmdAdd(t *testing.T) {
args: buildArgs("happyArgsSingle", happyPodArgs, happyNetConfByteArr),
want: &types100.Result{
CNIVersion: "1.0.0",
Interfaces: []*types100.Interface{
{
Mac: "00:11:22:33:44:55",
},
},
IPs: []*types100.IPConfig{
{
Address: net.IPNet{
IP: net.IPv4(10, 0, 1, 10),
Mask: net.CIDRMask(24, 32),
},
Gateway: net.IPv4(10, 0, 0, 1),
},
},
DNS: cniTypes.DNS{},
Expand All @@ -298,24 +408,55 @@ func TestCmdAdd(t *testing.T) {
args: buildArgs("happyArgsDual", happyPodArgs, happyNetConfByteArr),
want: &types100.Result{
CNIVersion: "1.0.0",
Interfaces: []*types100.Interface{
{
Mac: "00:11:22:33:44:55", // Single interface for dual-stack
},
},
IPs: []*types100.IPConfig{
{
Address: net.IPNet{
IP: net.IPv4(10, 0, 1, 10),
Mask: net.CIDRMask(24, 32),
},
Gateway: net.IPv4(10, 0, 0, 1),
},
{
Address: net.IPNet{
IP: net.ParseIP("fd11:1234::1"),
Mask: net.CIDRMask(120, 128),
},
Gateway: net.ParseIP("fe80::1234:5678:9abc"),
},
},
DNS: cniTypes.DNS{},
},
wantErr: false,
},
{
name: "CNI add with nil gateway IP",
args: buildArgs("nilGateway", happyPodArgs, happyNetConfByteArr),
want: &types100.Result{
CNIVersion: "1.0.0",
Interfaces: nil,
IPs: []*types100.IPConfig{
{
Address: net.IPNet{
IP: net.IPv4(10, 0, 1, 10),
Mask: net.CIDRMask(24, 32),
},
Gateway: nil, // No gateway
},
},
DNS: cniTypes.DNS{},
},
wantErr: false,
},
{
name: "CNI add with invalid gateway IP",
args: buildArgs("invalidGateway", happyPodArgs, happyNetConfByteArr),
wantErr: true,
},
{
name: "Fail request CNS ipconfig during CmdAdd",
args: buildArgs("failRequestCNSArgs", happyPodArgs, happyNetConfByteArr),
Expand Down
29 changes: 26 additions & 3 deletions azure-ipam/ipconfig/ipconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ipconfig
import (
"encoding/json"
"fmt"
"net"
"net/netip"

"github.com/Azure/azure-container-networking/cns"
Expand All @@ -11,6 +12,10 @@ import (
"github.com/pkg/errors"
)

const (
defaultV6Gateway = "fe80::1234:5678:9abc"
)

func CreateOrchestratorContext(args *cniSkel.CmdArgs) ([]byte, error) {
podConf, err := parsePodConf(args.Args)
if err != nil {
Expand Down Expand Up @@ -63,23 +68,41 @@ func CreateIPConfigsReq(args *cniSkel.CmdArgs) (cns.IPConfigsRequest, error) {
return req, nil
}

func ProcessIPConfigsResp(resp *cns.IPConfigsResponse) (*[]netip.Prefix, error) {
func ProcessIPConfigsResp(resp *cns.IPConfigsResponse) (*[]netip.Prefix, *[]net.IP, error) {
podIPNets := make([]netip.Prefix, len(resp.PodIPInfo))
gatewaysIPs := make([]net.IP, len(resp.PodIPInfo))

for i := range resp.PodIPInfo {
var gatewayIP net.IP

podCIDR := fmt.Sprintf(
"%s/%d",
resp.PodIPInfo[i].PodIPConfig.IPAddress,
resp.PodIPInfo[i].NetworkContainerPrimaryIPConfig.IPSubnet.PrefixLength,
)
podIPNet, err := netip.ParsePrefix(podCIDR)
if err != nil {
return nil, errors.Wrapf(err, "cns returned invalid pod CIDR %q", podCIDR)
return nil, nil, errors.Wrapf(err, "cns returned invalid pod CIDR %q", podCIDR)
}
podIPNets[i] = podIPNet

var gatewayStr string
if podIPNet.Addr().Is4() {
gatewayStr = resp.PodIPInfo[i].NetworkContainerPrimaryIPConfig.GatewayIPAddress
} else if podIPNet.Addr().Is6() {
gatewayStr = defaultV6Gateway
}

if gatewayStr != "" {
gatewayIP = net.ParseIP(gatewayStr)
if gatewayIP == nil {
return nil, nil, errors.Errorf("failed to parse gateway IP %q for pod ip %s", gatewayStr, resp.PodIPInfo[i].PodIPConfig.IPAddress)
}
}
gatewaysIPs[i] = gatewayIP
}

return &podIPNets, nil
return &podIPNets, &gatewaysIPs, nil
}

type k8sPodEnvArgs struct {
Expand Down
15 changes: 12 additions & 3 deletions cns/NetworkContainerContract.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,9 +398,10 @@ type NetworkInterfaceInfo struct {

// IPConfiguration contains details about ip config to provision in the VM.
type IPConfiguration struct {
IPSubnet IPSubnet
DNSServers []string
GatewayIPAddress string
IPSubnet IPSubnet
DNSServers []string
GatewayIPAddress string
GatewayIPv6Address string
}

// SecondaryIPConfig contains IP info of SecondaryIP
Expand Down Expand Up @@ -755,3 +756,11 @@ type NodeRegisterRequest struct {
NumCores int
NmAgentSupportedApis []string
}

// IPFamily - Enum for determining IPFamily when retrieving IPs from network containers
type IPFamily string

const (
IPv4 IPFamily = "ipv4"
IPv6 IPFamily = "ipv6"
)
4 changes: 2 additions & 2 deletions cns/kubecontroller/nodenetworkconfig/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func CreateNCRequestFromDynamicNC(nc v1alpha.NetworkContainer) (*cns.CreateNetwo
// CreateNCRequestFromStaticNC generates a CreateNetworkContainerRequest from a static NetworkContainer.
//
//nolint:gocritic //ignore hugeparam
func CreateNCRequestFromStaticNC(nc v1alpha.NetworkContainer) (*cns.CreateNetworkContainerRequest, error) {
func CreateNCRequestFromStaticNC(nc v1alpha.NetworkContainer, isSwiftV2 bool) (*cns.CreateNetworkContainerRequest, error) {
if nc.Type == v1alpha.Overlay {
nc.Version = 0 // fix for NMA always giving us version 0 for Overlay NCs
}
Expand All @@ -97,7 +97,7 @@ func CreateNCRequestFromStaticNC(nc v1alpha.NetworkContainer) (*cns.CreateNetwor
subnet.IPAddress = primaryPrefix.Addr().String()
}

req, err := createNCRequestFromStaticNCHelper(nc, primaryPrefix, subnet)
req, err := createNCRequestFromStaticNCHelper(nc, primaryPrefix, subnet, isSwiftV2)
if err != nil {
return nil, errors.Wrapf(err, "error while creating NC request from static NC")
}
Expand Down
Loading
Loading