Skip to content

Commit c325121

Browse files
jaer-tsunJaeryn
authored andcommitted
fix: Windows CNI Overlay Gateway Bug (#1849)
* fix overlay gatway bug that prevented cloud-node-manager-windows from initializing node because it couldn't reach IMDS * add overlay invoker cns test * use parseip instead of mask * scope CNI overlay gateway bug fix to windows only * update test * adding UT to verify overlay NAT info is empty * add podsubnet netInfo test --------- Co-authored-by: Jaeryn <[email protected]> (cherry picked from commit dac5b31)
1 parent 27376dd commit c325121

File tree

6 files changed

+147
-15
lines changed

6 files changed

+147
-15
lines changed

cni/network/invoker_cns.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ import (
2020
)
2121

2222
var (
23-
errEmptyCNIArgs = errors.New("empty CNI cmd args not allowed")
24-
errInvalidArgs = errors.New("invalid arg(s)")
25-
overlayGatewayIP = "169.254.1.1"
23+
errEmptyCNIArgs = errors.New("empty CNI cmd args not allowed")
24+
errInvalidArgs = errors.New("invalid arg(s)")
2625
)
2726

2827
type CNSIPAMInvoker struct {
@@ -99,19 +98,22 @@ func (invoker *CNSIPAMInvoker) Add(addConfig IPAMAddConfig) (IPAMAddResult, erro
9998

10099
log.Printf("[cni-invoker-cns] Received info %+v for pod %v", info, podInfo)
101100

101+
// set result ipconfigArgument from CNS Response Body
102+
ip, ncipnet, err := net.ParseCIDR(info.podIPAddress + "/" + fmt.Sprint(info.ncSubnetPrefix))
103+
if ip == nil {
104+
return IPAMAddResult{}, errors.Wrap(err, "Unable to parse IP from response: "+info.podIPAddress+" with err %w")
105+
}
106+
102107
ncgw := net.ParseIP(info.ncGatewayIPAddress)
103108
if ncgw == nil {
104109
if invoker.ipamMode != util.V4Overlay {
105110
return IPAMAddResult{}, errors.Wrap(errInvalidArgs, "%w: Gateway address "+info.ncGatewayIPAddress+" from response is invalid")
106111
}
107112

108-
ncgw = net.ParseIP(overlayGatewayIP)
109-
}
110-
111-
// set result ipconfigArgument from CNS Response Body
112-
ip, ncipnet, err := net.ParseCIDR(info.podIPAddress + "/" + fmt.Sprint(info.ncSubnetPrefix))
113-
if ip == nil {
114-
return IPAMAddResult{}, errors.Wrap(err, "Unable to parse IP from response: "+info.podIPAddress+" with err %w")
113+
ncgw, err = getOverlayGateway(ncipnet)
114+
if err != nil {
115+
return IPAMAddResult{}, err
116+
}
115117
}
116118

117119
// construct ipnet for result

cni/network/invoker_cns_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ package network
22

33
import (
44
"errors"
5+
"fmt"
56
"net"
7+
"runtime"
68
"testing"
79

810
"github.com/Azure/azure-container-networking/cni"
11+
"github.com/Azure/azure-container-networking/cni/util"
912
"github.com/Azure/azure-container-networking/cns"
1013
"github.com/Azure/azure-container-networking/iptables"
1114
"github.com/Azure/azure-container-networking/network"
@@ -25,19 +28,29 @@ func getTestIPConfigRequest() cns.IPConfigRequest {
2528
}
2629
}
2730

31+
func getTestOverlayGateway() net.IP {
32+
if runtime.GOOS == "windows" {
33+
return net.ParseIP("10.240.0.1")
34+
}
35+
36+
return net.ParseIP("169.254.1.1")
37+
}
38+
2839
func TestCNSIPAMInvoker_Add(t *testing.T) {
2940
require := require.New(t) //nolint further usage of require without passing t
3041
type fields struct {
3142
podName string
3243
podNamespace string
3344
cnsClient cnsclient
45+
ipamMode util.IpamMode
3446
}
3547
type args struct {
3648
nwCfg *cni.NetworkConfig
3749
args *cniSkel.CmdArgs
3850
hostSubnetPrefix *net.IPNet
3951
options map[string]interface{}
4052
}
53+
4154
tests := []struct {
4255
name string
4356
fields fields
@@ -127,6 +140,76 @@ func TestCNSIPAMInvoker_Add(t *testing.T) {
127140
},
128141
wantErr: true,
129142
},
143+
{
144+
name: "Test happy CNI Overlay add",
145+
fields: fields{
146+
podName: testPodInfo.PodName,
147+
podNamespace: testPodInfo.PodNamespace,
148+
ipamMode: util.V4Overlay,
149+
cnsClient: &MockCNSClient{
150+
require: require,
151+
request: requestIPAddressHandler{
152+
ipconfigArgument: cns.IPConfigRequest{
153+
PodInterfaceID: "testcont-testifname3",
154+
InfraContainerID: "testcontainerid3",
155+
OrchestratorContext: marshallPodInfo(testPodInfo),
156+
},
157+
result: &cns.IPConfigResponse{
158+
PodIpInfo: cns.PodIpInfo{
159+
PodIPConfig: cns.IPSubnet{
160+
IPAddress: "10.240.1.242",
161+
PrefixLength: 16,
162+
},
163+
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
164+
IPSubnet: cns.IPSubnet{
165+
IPAddress: "10.240.1.0",
166+
PrefixLength: 16,
167+
},
168+
DNSServers: nil,
169+
GatewayIPAddress: "",
170+
},
171+
HostPrimaryIPInfo: cns.HostIPInfo{
172+
Gateway: "10.224.0.1",
173+
PrimaryIP: "10.224.0.5",
174+
Subnet: "10.224.0.0/16",
175+
},
176+
},
177+
Response: cns.Response{
178+
ReturnCode: 0,
179+
Message: "",
180+
},
181+
},
182+
err: nil,
183+
},
184+
},
185+
},
186+
args: args{
187+
nwCfg: &cni.NetworkConfig{},
188+
args: &cniSkel.CmdArgs{
189+
ContainerID: "testcontainerid3",
190+
Netns: "testnetns3",
191+
IfName: "testifname3",
192+
},
193+
hostSubnetPrefix: getCIDRNotationForAddress("10.224.0.0/16"),
194+
options: map[string]interface{}{},
195+
},
196+
want: &cniTypesCurr.Result{
197+
IPs: []*cniTypesCurr.IPConfig{
198+
{
199+
Address: *getCIDRNotationForAddress("10.240.1.242/16"),
200+
Gateway: getTestOverlayGateway(),
201+
},
202+
},
203+
Routes: []*cniTypes.Route{
204+
{
205+
Dst: network.Ipv4DefaultRouteDstPrefix,
206+
GW: getTestOverlayGateway(),
207+
},
208+
},
209+
},
210+
want1: nil,
211+
wantErr: false,
212+
},
130213
}
131214
for _, tt := range tests {
132215
tt := tt
@@ -136,13 +219,17 @@ func TestCNSIPAMInvoker_Add(t *testing.T) {
136219
podNamespace: tt.fields.podNamespace,
137220
cnsClient: tt.fields.cnsClient,
138221
}
222+
if tt.fields.ipamMode != "" {
223+
invoker.ipamMode = tt.fields.ipamMode
224+
}
139225
ipamAddResult, err := invoker.Add(IPAMAddConfig{nwCfg: tt.args.nwCfg, args: tt.args.args, options: tt.args.options})
140226
if tt.wantErr {
141227
require.Error(err)
142228
} else {
143229
require.NoError(err)
144230
}
145231

232+
fmt.Printf("want:%+v\nrest:%+v\n", tt.want, ipamAddResult.ipv4Result)
146233
require.Equalf(tt.want, ipamAddResult.ipv4Result, "incorrect ipv4 response")
147234
require.Equalf(tt.want1, ipamAddResult.ipv6Result, "incorrect ipv6 response")
148235
})

cni/network/network.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ func (plugin *NetPlugin) Add(args *cniSkel.CmdArgs) error {
536536
logAndSendEvent(plugin, fmt.Sprintf("[cni-net] Created network %v with subnet %v.", networkID, ipamAddResult.hostSubnetPrefix.String()))
537537
}
538538

539-
natInfo := getNATInfo(nwCfg.ExecutionMode, options[network.SNATIPKey], nwCfg.MultiTenancy, enableSnatForDNS)
539+
natInfo := getNATInfo(nwCfg, options[network.SNATIPKey], enableSnatForDNS)
540540

541541
createEndpointInternalOpt := createEndpointInternalOpt{
542542
nwCfg: nwCfg,

cni/network/network_linux.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,17 @@ func (plugin *NetPlugin) getNetworkName(_ string, _ *IPAMAddResult, nwCfg *cni.N
136136
return nwCfg.Name, nil
137137
}
138138

139-
func getNATInfo(_ string, _ interface{}, _, _ bool) (natInfo []policy.NATInfo) {
139+
func getNATInfo(_ *cni.NetworkConfig, _ interface{}, _ bool) (natInfo []policy.NATInfo) {
140140
return natInfo
141141
}
142142

143143
func platformInit(cniConfig *cni.NetworkConfig) {}
144+
145+
// isDualNicFeatureSupported returns if the dual nic feature is supported. Currently it's only supported for windows hnsv2 path
146+
func (plugin *NetPlugin) isDualNicFeatureSupported(netNs string) bool {
147+
return false
148+
}
149+
150+
func getOverlayGateway(_ *net.IPNet) (net.IP, error) {
151+
return net.ParseIP("169.254.1.1"), nil
152+
}

cni/network/network_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ import (
44
"fmt"
55
"net"
66
"os"
7+
"runtime"
78
"testing"
89

910
"github.com/Azure/azure-container-networking/cni"
1011
"github.com/Azure/azure-container-networking/cni/api"
1112
"github.com/Azure/azure-container-networking/cni/util"
1213
"github.com/Azure/azure-container-networking/common"
1314
acnnetwork "github.com/Azure/azure-container-networking/network"
15+
"github.com/Azure/azure-container-networking/network/networkutils"
16+
"github.com/Azure/azure-container-networking/network/policy"
1417
"github.com/Azure/azure-container-networking/nns"
1518
"github.com/Azure/azure-container-networking/telemetry"
1619
cniSkel "github.com/containernetworking/cni/pkg/skel"
@@ -1006,13 +1009,17 @@ func TestGetAllEndpointState(t *testing.T) {
10061009

10071010
ep1 := getTestEndpoint("podname1", "podnamespace1", "10.0.0.1/24", "podinterfaceid1", "testcontainerid1")
10081011
ep2 := getTestEndpoint("podname2", "podnamespace2", "10.0.0.2/24", "podinterfaceid2", "testcontainerid2")
1012+
ep3 := getTestEndpoint("podname3", "podnamespace3", "10.240.1.242/16", "podinterfaceid3", "testcontainerid3")
10091013

10101014
err := plugin.nm.CreateEndpoint(nil, networkid, ep1)
10111015
require.NoError(t, err)
10121016

10131017
err = plugin.nm.CreateEndpoint(nil, networkid, ep2)
10141018
require.NoError(t, err)
10151019

1020+
err = plugin.nm.CreateEndpoint(nil, networkid, ep3)
1021+
require.NoError(t, err)
1022+
10161023
state, err := plugin.GetAllEndpointState(networkid)
10171024
require.NoError(t, err)
10181025

@@ -1032,6 +1039,13 @@ func TestGetAllEndpointState(t *testing.T) {
10321039
ContainerID: ep2.ContainerID,
10331040
IPAddresses: ep2.IPAddresses,
10341041
},
1042+
ep3.Id: {
1043+
PodEndpointId: ep3.Id,
1044+
PodName: ep3.PODName,
1045+
PodNamespace: ep3.PODNameSpace,
1046+
ContainerID: ep3.ContainerID,
1047+
IPAddresses: ep3.IPAddresses,
1048+
},
10351049
},
10361050
}
10371051

@@ -1067,3 +1081,23 @@ func TestGetNetworkName(t *testing.T) {
10671081
})
10681082
}
10691083
}
1084+
1085+
func TestGetOverlayNatInfo(t *testing.T) {
1086+
nwCfg := &cni.NetworkConfig{ExecutionMode: string(util.V4Swift), IPAM: cni.IPAM{Mode: string(util.V4Overlay)}}
1087+
natInfo := getNATInfo(nwCfg, nil, false)
1088+
require.Empty(t, natInfo, "overlay natInfo should be empty")
1089+
}
1090+
1091+
func TestGetPodSubnetNatInfo(t *testing.T) {
1092+
ncPrimaryIP := "10.241.0.4"
1093+
nwCfg := &cni.NetworkConfig{ExecutionMode: string(util.V4Swift)}
1094+
natInfo := getNATInfo(nwCfg, ncPrimaryIP, false)
1095+
if runtime.GOOS == "windows" {
1096+
require.Equalf(t, natInfo, []policy.NATInfo{
1097+
{VirtualIP: ncPrimaryIP, Destinations: []string{networkutils.AzureDNS}},
1098+
{Destinations: []string{networkutils.AzureIMDS}},
1099+
}, "invalid windows podsubnet natInfo")
1100+
} else {
1101+
require.Empty(t, natInfo, "linux podsubnet natInfo should be empty")
1102+
}
1103+
}

cni/network/network_windows.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -381,15 +381,15 @@ func determineWinVer() {
381381
}
382382
}
383383

384-
func getNATInfo(executionMode string, ncPrimaryIPIface interface{}, multitenancy, enableSnatForDNS bool) (natInfo []policy.NATInfo) {
385-
if executionMode == string(util.V4Swift) {
384+
func getNATInfo(nwCfg *cni.NetworkConfig, ncPrimaryIPIface interface{}, enableSnatForDNS bool) (natInfo []policy.NATInfo) {
385+
if nwCfg.ExecutionMode == string(util.V4Swift) && nwCfg.IPAM.Mode != string(util.V4Overlay) {
386386
ncPrimaryIP := ""
387387
if ncPrimaryIPIface != nil {
388388
ncPrimaryIP = ncPrimaryIPIface.(string)
389389
}
390390

391391
natInfo = append(natInfo, []policy.NATInfo{{VirtualIP: ncPrimaryIP, Destinations: []string{networkutils.AzureDNS}}, {Destinations: []string{networkutils.AzureIMDS}}}...)
392-
} else if multitenancy && enableSnatForDNS {
392+
} else if nwCfg.MultiTenancy && enableSnatForDNS {
393393
natInfo = append(natInfo, policy.NATInfo{Destinations: []string{networkutils.AzureDNS}})
394394
}
395395

0 commit comments

Comments
 (0)