Skip to content

Commit deb30d9

Browse files
jaer-tsunJaeryn
andauthored
fix: Windows Overlay Gateway Bug (#1852)
* fix overlay gatway bug that prevented cloud-node-manager-windows from initializing node because it couldn't reach IMDS * 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 * fix tests and convert to old structs names --------- Co-authored-by: Jaeryn <[email protected]>
1 parent afdee48 commit deb30d9

File tree

6 files changed

+144
-17
lines changed

6 files changed

+144
-17
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: 86 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,12 +28,21 @@ 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
@@ -127,6 +139,76 @@ func TestCNSIPAMInvoker_Add(t *testing.T) {
127139
},
128140
wantErr: true,
129141
},
142+
{
143+
name: "Test happy CNI Overlay add",
144+
fields: fields{
145+
podName: testPodInfo.PodName,
146+
podNamespace: testPodInfo.PodNamespace,
147+
ipamMode: util.V4Overlay,
148+
cnsClient: &MockCNSClient{
149+
require: require,
150+
request: requestIPAddressHandler{
151+
ipconfigArgument: cns.IPConfigRequest{
152+
PodInterfaceID: "testcont-testifname3",
153+
InfraContainerID: "testcontainerid3",
154+
OrchestratorContext: marshallPodInfo(testPodInfo),
155+
},
156+
result: &cns.IPConfigResponse{
157+
PodIpInfo: cns.PodIpInfo{
158+
PodIPConfig: cns.IPSubnet{
159+
IPAddress: "10.240.1.242",
160+
PrefixLength: 16,
161+
},
162+
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
163+
IPSubnet: cns.IPSubnet{
164+
IPAddress: "10.240.1.0",
165+
PrefixLength: 16,
166+
},
167+
DNSServers: nil,
168+
GatewayIPAddress: "",
169+
},
170+
HostPrimaryIPInfo: cns.HostIPInfo{
171+
Gateway: "10.224.0.1",
172+
PrimaryIP: "10.224.0.5",
173+
Subnet: "10.224.0.0/16",
174+
},
175+
},
176+
Response: cns.Response{
177+
ReturnCode: 0,
178+
Message: "",
179+
},
180+
},
181+
err: nil,
182+
},
183+
},
184+
},
185+
args: args{
186+
nwCfg: &cni.NetworkConfig{},
187+
args: &cniSkel.CmdArgs{
188+
ContainerID: "testcontainerid3",
189+
Netns: "testnetns3",
190+
IfName: "testifname3",
191+
},
192+
hostSubnetPrefix: getCIDRNotationForAddress("10.224.0.0/16"),
193+
options: map[string]interface{}{},
194+
},
195+
want: &cniTypesCurr.Result{
196+
IPs: []*cniTypesCurr.IPConfig{
197+
{
198+
Address: *getCIDRNotationForAddress("10.240.1.242/16"),
199+
Gateway: getTestOverlayGateway(),
200+
},
201+
},
202+
Routes: []*cniTypes.Route{
203+
{
204+
Dst: network.Ipv4DefaultRouteDstPrefix,
205+
GW: getTestOverlayGateway(),
206+
},
207+
},
208+
},
209+
want1: nil,
210+
wantErr: false,
211+
},
130212
}
131213
for _, tt := range tests {
132214
tt := tt
@@ -136,13 +218,17 @@ func TestCNSIPAMInvoker_Add(t *testing.T) {
136218
podNamespace: tt.fields.podNamespace,
137219
cnsClient: tt.fields.cnsClient,
138220
}
221+
if tt.fields.ipamMode != "" {
222+
invoker.ipamMode = tt.fields.ipamMode
223+
}
139224
ipamAddResult, err := invoker.Add(IPAMAddConfig{nwCfg: tt.args.nwCfg, args: tt.args.args, options: tt.args.options})
140225
if tt.wantErr {
141226
require.Error(err)
142227
} else {
143228
require.NoError(err)
144229
}
145230

231+
fmt.Printf("want:%+v\nrest:%+v\n", tt.want, ipamAddResult.ipv4Result)
146232
require.Equalf(tt.want, ipamAddResult.ipv4Result, "incorrect ipv4 response")
147233
require.Equalf(tt.want1, ipamAddResult.ipv6Result, "incorrect ipv6 response")
148234
})

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: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,12 @@ 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+
func getOverlayGateway(_ *net.IPNet) (net.IP, error) {
146+
return net.ParseIP("169.254.1.1"), nil
147+
}

cni/network/network_test.go

Lines changed: 26 additions & 2 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"
@@ -706,7 +709,7 @@ func TestPluginMultitenancyDelete(t *testing.T) {
706709
}
707710

708711
/*
709-
Baremetal scenarios
712+
Baremetal scenarios
710713
*/
711714
func TestPluginBaremetalAdd(t *testing.T) {
712715
plugin, _ := cni.NewPlugin("test", "0.3.0")
@@ -835,7 +838,7 @@ func TestPluginBaremetalDelete(t *testing.T) {
835838
}
836839

837840
/*
838-
AKS-Swift scenario
841+
AKS-Swift scenario
839842
*/
840843
func TestPluginAKSSwiftAdd(t *testing.T) {
841844
plugin := GetTestResources()
@@ -1067,3 +1070,24 @@ func TestGetNetworkName(t *testing.T) {
10671070
})
10681071
}
10691072
}
1073+
1074+
func TestGetOverlayNatInfo(t *testing.T) {
1075+
nwCfg := &cni.NetworkConfig{ExecutionMode: string(util.V4Swift)}
1076+
nwCfg.Ipam.Mode = string(util.V4Overlay)
1077+
natInfo := getNATInfo(nwCfg, nil, false)
1078+
require.Empty(t, natInfo, "overlay natInfo should be empty")
1079+
}
1080+
1081+
func TestGetPodSubnetNatInfo(t *testing.T) {
1082+
ncPrimaryIP := "10.241.0.4"
1083+
nwCfg := &cni.NetworkConfig{ExecutionMode: string(util.V4Swift)}
1084+
natInfo := getNATInfo(nwCfg, ncPrimaryIP, false)
1085+
if runtime.GOOS == "windows" {
1086+
require.Equalf(t, natInfo, []policy.NATInfo{
1087+
{VirtualIP: ncPrimaryIP, Destinations: []string{networkutils.AzureDNS}},
1088+
{Destinations: []string{networkutils.AzureIMDS}},
1089+
}, "invalid windows podsubnet natInfo")
1090+
} else {
1091+
require.Empty(t, natInfo, "linux podsubnet natInfo should be empty")
1092+
}
1093+
}

cni/network/network_windows.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,15 +378,15 @@ func determineWinVer() {
378378
}
379379
}
380380

381-
func getNATInfo(executionMode string, ncPrimaryIPIface interface{}, multitenancy, enableSnatForDNS bool) (natInfo []policy.NATInfo) {
382-
if executionMode == string(util.V4Swift) {
381+
func getNATInfo(nwCfg *cni.NetworkConfig, ncPrimaryIPIface interface{}, enableSnatForDNS bool) (natInfo []policy.NATInfo) {
382+
if nwCfg.ExecutionMode == string(util.V4Swift) && nwCfg.Ipam.Mode != string(util.V4Overlay) {
383383
ncPrimaryIP := ""
384384
if ncPrimaryIPIface != nil {
385385
ncPrimaryIP = ncPrimaryIPIface.(string)
386386
}
387387

388388
natInfo = append(natInfo, []policy.NATInfo{{VirtualIP: ncPrimaryIP, Destinations: []string{networkutils.AzureDNS}}, {Destinations: []string{networkutils.AzureIMDS}}}...)
389-
} else if multitenancy && enableSnatForDNS {
389+
} else if nwCfg.MultiTenancy && enableSnatForDNS {
390390
natInfo = append(natInfo, policy.NATInfo{Destinations: []string{networkutils.AzureDNS}})
391391
}
392392

@@ -400,3 +400,14 @@ func platformInit(cniConfig *cni.NetworkConfig) {
400400
network.EnableHnsV2Timeout(cniConfig.WindowsSettings.HnsTimeoutDurationInSeconds)
401401
}
402402
}
403+
404+
func getOverlayGateway(podsubnet *net.IPNet) (net.IP, error) {
405+
ncgw := podsubnet.IP
406+
ncgw[3]++
407+
ncgw = net.ParseIP(ncgw.String())
408+
if ncgw == nil || !podsubnet.Contains(ncgw) {
409+
return nil, errors.Wrap(errInvalidArgs, "%w: Failed to retrieve overlay gateway from podsubnet"+podsubnet.IP.String())
410+
}
411+
412+
return ncgw, nil
413+
}

0 commit comments

Comments
 (0)