Skip to content

Commit 134c118

Browse files
NihaNallappagarikadevunihanMicrosoft
authored
Prefix on nicv6 support (#3658)
* Prefix on NIC v6 support * update dedup comment in ipam * add log for a gatway error scenario * handle synchostversion sync on pon swiftv2 nic * handle gatewayip parse failure or nil scenario * remove unused code Address PR comments remove unnecessary logger update synhost skip test scenario fix linting error updated lint error fix linting error . update synhost skip test scenario fix linting error updated lint error fix linting error . * add test scenario * update test to handle gatway nil case * remove synchost skip and single ip skip . . * Add missing test scenarios * fix ipam_test conflicts * fix conflits small fixes fix lint errors lint fix . Delete examples/imds_nc_lookup.go Signed-off-by: NihaNallappagari <[email protected]> fix lint . * fix linting * add todo and remove multiple interface scenario * Skip add primary ip's to secondary config for swiftv2 pon scenarios fix lint error . * code review comment * Update gateway ipv6 to use default value, that auto detects and adds to neigh table * remove unwanted changes * address ipfamily comment * fix test failure --------- Co-authored-by: Kaushik Vuligonda <[email protected]> Co-authored-by: nn052161 <[email protected]>
1 parent bd7e2ae commit 134c118

File tree

16 files changed

+965
-328
lines changed

16 files changed

+965
-328
lines changed

azure-ipam/ipam.go

Lines changed: 25 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,33 @@ 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+
// Deduplication is necessary because there is one podIPInfo entry for each IP family(IPv4 and IPv6), and both may point to the same interface
150+
if podIPInfo.MacAddress == "" || seenInterfaces[podIPInfo.MacAddress] {
151+
continue
152+
}
153+
154+
infMac, err := net.ParseMAC(podIPInfo.MacAddress)
155+
if err != nil {
156+
p.logger.Error("Failed to parse interface MAC address", zap.Error(err), zap.String("macAddress", podIPInfo.MacAddress))
157+
return cniTypes.NewError(cniTypes.ErrUnsupportedField, err.Error(), "failed to parse interface MAC address")
158+
}
159+
160+
cniResult.Interfaces = append(cniResult.Interfaces, &types100.Interface{
161+
Mac: infMac.String(),
162+
})
163+
seenInterfaces[podIPInfo.MacAddress] = true
164+
}
165+
142166
// Get versioned result
143167
versionedCniResult, err := cniResult.GetAsVersion(nwCfg.CNIVersion)
144168
if err != nil {

azure-ipam/ipam_test.go

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,41 @@ func (c *MockCNSClient) RequestIPAddress(ctx context.Context, ipconfig cns.IPCon
5858
},
5959
}
6060
return result, nil
61+
case "nilGateway":
62+
result := &cns.IPConfigResponse{
63+
PodIpInfo: cns.PodIpInfo{
64+
PodIPConfig: cns.IPSubnet{
65+
IPAddress: "10.0.1.10",
66+
PrefixLength: 24,
67+
},
68+
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
69+
IPSubnet: cns.IPSubnet{
70+
IPAddress: "10.0.1.0",
71+
PrefixLength: 24,
72+
},
73+
DNSServers: nil,
74+
GatewayIPAddress: "", // nil/empty gateway
75+
},
76+
HostPrimaryIPInfo: cns.HostIPInfo{
77+
Gateway: "",
78+
PrimaryIP: "10.0.0.1",
79+
Subnet: "10.0.0.0/24",
80+
},
81+
},
82+
Response: cns.Response{
83+
ReturnCode: 0,
84+
Message: "",
85+
},
86+
}
87+
return result, nil
6188
default:
6289
result := &cns.IPConfigResponse{
6390
PodIpInfo: cns.PodIpInfo{
6491
PodIPConfig: cns.IPSubnet{
6592
IPAddress: "10.0.1.10",
6693
PrefixLength: 24,
6794
},
95+
MacAddress: "00:11:22:33:44:55",
6896
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
6997
IPSubnet: cns.IPSubnet{
7098
IPAddress: "10.0.1.0",
@@ -92,11 +120,60 @@ func (c *MockCNSClient) RequestIPs(ctx context.Context, ipconfig cns.IPConfigsRe
92120
switch ipconfig.InfraContainerID {
93121
case "failRequestCNSArgs":
94122
return nil, errFoo
95-
case "happyArgsSingle", "failProcessCNSRespSingleIP", "failRequestCNSArgsSingleIP":
123+
case "happyArgsSingle", "failProcessCNSRespSingleIP", "failRequestCNSArgsSingleIP", "nilGateway":
96124
e := &client.CNSClientError{}
97125
e.Code = types.UnsupportedAPI
98126
e.Err = errUnsupportedAPI
99127
return nil, e
128+
case "happyArgsDual":
129+
result := &cns.IPConfigsResponse{
130+
PodIPInfo: []cns.PodIpInfo{
131+
{
132+
PodIPConfig: cns.IPSubnet{
133+
IPAddress: "10.0.1.10",
134+
PrefixLength: 24,
135+
},
136+
MacAddress: "00:11:22:33:44:55",
137+
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
138+
IPSubnet: cns.IPSubnet{
139+
IPAddress: "10.0.1.0",
140+
PrefixLength: 24,
141+
},
142+
DNSServers: nil,
143+
GatewayIPAddress: "10.0.0.1",
144+
},
145+
HostPrimaryIPInfo: cns.HostIPInfo{
146+
Gateway: "10.0.0.1",
147+
PrimaryIP: "10.0.0.1",
148+
Subnet: "10.0.0.0/24",
149+
},
150+
},
151+
{
152+
PodIPConfig: cns.IPSubnet{
153+
IPAddress: "fd11:1234::1",
154+
PrefixLength: 120,
155+
},
156+
MacAddress: "00:11:22:33:44:55", // Same MAC for dual-stack scenario
157+
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
158+
IPSubnet: cns.IPSubnet{
159+
IPAddress: "fd11:1234::",
160+
PrefixLength: 120,
161+
},
162+
DNSServers: nil,
163+
},
164+
HostPrimaryIPInfo: cns.HostIPInfo{
165+
Gateway: "fe80::1234:5678:9abc",
166+
PrimaryIP: "fe80::1234:5678:9abc",
167+
Subnet: "fd11:1234::/120",
168+
},
169+
},
170+
},
171+
Response: cns.Response{
172+
ReturnCode: 0,
173+
Message: "",
174+
},
175+
}
176+
return result, nil
100177
case "failProcessCNSResp":
101178
result := &cns.IPConfigsResponse{
102179
PodIPInfo: []cns.PodIpInfo{
@@ -129,8 +206,7 @@ func (c *MockCNSClient) RequestIPs(ctx context.Context, ipconfig cns.IPConfigsRe
129206
IPAddress: "fd11:1234::",
130207
PrefixLength: 112,
131208
},
132-
DNSServers: nil,
133-
GatewayIPAddress: "fe80::1234:5678:9abc",
209+
DNSServers: nil,
134210
},
135211
HostPrimaryIPInfo: cns.HostIPInfo{
136212
Gateway: "fe80::1234:5678:9abc",
@@ -153,6 +229,7 @@ func (c *MockCNSClient) RequestIPs(ctx context.Context, ipconfig cns.IPConfigsRe
153229
IPAddress: "10.0.1.10",
154230
PrefixLength: 24,
155231
},
232+
MacAddress: "00:11:22:33:44:55",
156233
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
157234
IPSubnet: cns.IPSubnet{
158235
IPAddress: "10.0.1.0",
@@ -172,13 +249,13 @@ func (c *MockCNSClient) RequestIPs(ctx context.Context, ipconfig cns.IPConfigsRe
172249
IPAddress: "fd11:1234::1",
173250
PrefixLength: 120,
174251
},
252+
MacAddress: "00:11:22:33:44:55", // Same MAC for dual-stack scenario
175253
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
176254
IPSubnet: cns.IPSubnet{
177255
IPAddress: "fd11:1234::",
178256
PrefixLength: 120,
179257
},
180-
DNSServers: nil,
181-
GatewayIPAddress: "fe80::1234:5678:9abc",
258+
DNSServers: nil,
182259
},
183260
HostPrimaryIPInfo: cns.HostIPInfo{
184261
Gateway: "fe80::1234:5678:9abc",
@@ -281,12 +358,18 @@ func TestCmdAdd(t *testing.T) {
281358
args: buildArgs("happyArgsSingle", happyPodArgs, happyNetConfByteArr),
282359
want: &types100.Result{
283360
CNIVersion: "1.0.0",
361+
Interfaces: []*types100.Interface{
362+
{
363+
Mac: "00:11:22:33:44:55",
364+
},
365+
},
284366
IPs: []*types100.IPConfig{
285367
{
286368
Address: net.IPNet{
287369
IP: net.IPv4(10, 0, 1, 10),
288370
Mask: net.CIDRMask(24, 32),
289371
},
372+
Gateway: net.IPv4(10, 0, 0, 1),
290373
},
291374
},
292375
DNS: cniTypes.DNS{},
@@ -298,24 +381,36 @@ func TestCmdAdd(t *testing.T) {
298381
args: buildArgs("happyArgsDual", happyPodArgs, happyNetConfByteArr),
299382
want: &types100.Result{
300383
CNIVersion: "1.0.0",
384+
Interfaces: []*types100.Interface{
385+
{
386+
Mac: "00:11:22:33:44:55", // Single interface for dual-stack
387+
},
388+
},
301389
IPs: []*types100.IPConfig{
302390
{
303391
Address: net.IPNet{
304392
IP: net.IPv4(10, 0, 1, 10),
305393
Mask: net.CIDRMask(24, 32),
306394
},
395+
Gateway: net.IPv4(10, 0, 0, 1),
307396
},
308397
{
309398
Address: net.IPNet{
310399
IP: net.ParseIP("fd11:1234::1"),
311400
Mask: net.CIDRMask(120, 128),
312401
},
402+
Gateway: net.ParseIP("fe80::1234:5678:9abc"),
313403
},
314404
},
315405
DNS: cniTypes.DNS{},
316406
},
317407
wantErr: false,
318408
},
409+
{
410+
name: "CNI add with nil gateway IP",
411+
args: buildArgs("nilGateway", happyPodArgs, happyNetConfByteArr),
412+
wantErr: true,
413+
},
319414
{
320415
name: "Fail request CNS ipconfig during CmdAdd",
321416
args: buildArgs("failRequestCNSArgs", happyPodArgs, happyNetConfByteArr),

azure-ipam/ipconfig/ipconfig.go

Lines changed: 24 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"
@@ -11,6 +12,10 @@ import (
1112
"github.com/pkg/errors"
1213
)
1314

15+
const (
16+
defaultV6Gateway = "fe80::1234:5678:9abc"
17+
)
18+
1419
func CreateOrchestratorContext(args *cniSkel.CmdArgs) ([]byte, error) {
1520
podConf, err := parsePodConf(args.Args)
1621
if err != nil {
@@ -63,23 +68,39 @@ func CreateIPConfigsReq(args *cniSkel.CmdArgs) (cns.IPConfigsRequest, error) {
6368
return req, nil
6469
}
6570

66-
func ProcessIPConfigsResp(resp *cns.IPConfigsResponse) (*[]netip.Prefix, error) {
71+
func ProcessIPConfigsResp(resp *cns.IPConfigsResponse) (*[]netip.Prefix, *[]net.IP, error) {
6772
podIPNets := make([]netip.Prefix, len(resp.PodIPInfo))
73+
gatewaysIPs := make([]net.IP, len(resp.PodIPInfo))
6874

6975
for i := range resp.PodIPInfo {
76+
var gatewayIP net.IP
77+
7078
podCIDR := fmt.Sprintf(
7179
"%s/%d",
7280
resp.PodIPInfo[i].PodIPConfig.IPAddress,
7381
resp.PodIPInfo[i].NetworkContainerPrimaryIPConfig.IPSubnet.PrefixLength,
7482
)
7583
podIPNet, err := netip.ParsePrefix(podCIDR)
7684
if err != nil {
77-
return nil, errors.Wrapf(err, "cns returned invalid pod CIDR %q", podCIDR)
85+
return nil, nil, errors.Wrapf(err, "cns returned invalid pod CIDR %q", podCIDR)
7886
}
7987
podIPNets[i] = podIPNet
88+
89+
var gatewayStr string
90+
if podIPNet.Addr().Is4() {
91+
gatewayStr = resp.PodIPInfo[i].NetworkContainerPrimaryIPConfig.GatewayIPAddress
92+
} else if podIPNet.Addr().Is6() {
93+
gatewayStr = defaultV6Gateway
94+
}
95+
96+
gatewayIP = net.ParseIP(gatewayStr)
97+
if gatewayIP == nil {
98+
return nil, nil, errors.Errorf("failed to parse gateway IP %q for pod ip %s", gatewayStr, resp.PodIPInfo[i].PodIPConfig.IPAddress)
99+
}
100+
gatewaysIPs[i] = gatewayIP
80101
}
81102

82-
return &podIPNets, nil
103+
return &podIPNets, &gatewaysIPs, nil
83104
}
84105

85106
type k8sPodEnvArgs struct {

cns/NetworkContainerContract.go

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

399399
// IPConfiguration contains details about ip config to provision in the VM.
400400
type IPConfiguration struct {
401-
IPSubnet IPSubnet
402-
DNSServers []string
403-
GatewayIPAddress string
401+
IPSubnet IPSubnet
402+
DNSServers []string
403+
GatewayIPAddress string
404+
GatewayIPv6Address string
404405
}
405406

406407
// SecondaryIPConfig contains IP info of SecondaryIP
@@ -755,3 +756,11 @@ type NodeRegisterRequest struct {
755756
NumCores int
756757
NmAgentSupportedApis []string
757758
}
759+
760+
// IPFamily - Enum for determining IPFamily when retrieving IPs from network containers
761+
type IPFamily string
762+
763+
const (
764+
IPv4 IPFamily = "ipv4"
765+
IPv6 IPFamily = "ipv6"
766+
)

cns/kubecontroller/nodenetworkconfig/conversion.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func CreateNCRequestFromDynamicNC(nc v1alpha.NetworkContainer) (*cns.CreateNetwo
7373
// CreateNCRequestFromStaticNC generates a CreateNetworkContainerRequest from a static NetworkContainer.
7474
//
7575
//nolint:gocritic //ignore hugeparam
76-
func CreateNCRequestFromStaticNC(nc v1alpha.NetworkContainer) (*cns.CreateNetworkContainerRequest, error) {
76+
func CreateNCRequestFromStaticNC(nc v1alpha.NetworkContainer, isSwiftV2 bool) (*cns.CreateNetworkContainerRequest, error) {
7777
if nc.Type == v1alpha.Overlay {
7878
nc.Version = 0 // fix for NMA always giving us version 0 for Overlay NCs
7979
}
@@ -97,7 +97,7 @@ func CreateNCRequestFromStaticNC(nc v1alpha.NetworkContainer) (*cns.CreateNetwor
9797
subnet.IPAddress = primaryPrefix.Addr().String()
9898
}
9999

100-
req, err := createNCRequestFromStaticNCHelper(nc, primaryPrefix, subnet)
100+
req, err := createNCRequestFromStaticNCHelper(nc, primaryPrefix, subnet, isSwiftV2)
101101
if err != nil {
102102
return nil, errors.Wrapf(err, "error while creating NC request from static NC")
103103
}

cns/kubecontroller/nodenetworkconfig/conversion_linux.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,18 @@ import (
1313
// by adding all IPs in the the block to the secondary IP configs list. It does not skip any IPs.
1414
//
1515
//nolint:gocritic //ignore hugeparam
16-
func createNCRequestFromStaticNCHelper(nc v1alpha.NetworkContainer, primaryIPPrefix netip.Prefix, subnet cns.IPSubnet) (*cns.CreateNetworkContainerRequest, error) {
16+
func createNCRequestFromStaticNCHelper(nc v1alpha.NetworkContainer, primaryIPPrefix netip.Prefix, subnet cns.IPSubnet, isSwiftV2 bool) (*cns.CreateNetworkContainerRequest, error) {
1717
secondaryIPConfigs := map[string]cns.SecondaryIPConfig{}
1818

1919
// iterate through all IP addresses in the subnet described by primaryPrefix and
2020
// 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),
21+
// Process primary prefix IPs in all scenarios except when nc.Type is v1alpha.VNETBlock AND SwiftV2 is enabled
22+
if !(isSwiftV2 && nc.Type == v1alpha.VNETBlock) {
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
}

0 commit comments

Comments
 (0)