@@ -6,7 +6,12 @@ package network
66import (
77 "encoding/json"
88 "fmt"
9+ "io/ioutil"
910 "net"
11+ "net/http"
12+ "os"
13+ "strings"
14+ "time"
1015
1116 "github.com/Azure/azure-container-networking/cni"
1217 "github.com/Azure/azure-container-networking/cns"
@@ -35,13 +40,34 @@ const (
3540 CNI_UPDATE = "UPDATE"
3641)
3742
43+ const (
44+ // URL to query NMAgent version and determine whether we snat on host
45+ nmAgentSupportedApisURL = "http://168.63.129.16/machine/plugins/?comp=nmagent&type=GetSupportedApis"
46+ // Only SNAT support (no DNS support)
47+ nmAgentSnatSupportAPI = "NetworkManagementSnatSupport"
48+ // SNAT and DNS are both supported
49+ nmAgentSnatAndDnsSupportAPI = "NetworkManagementDNSSupport"
50+ )
51+
52+ // temporary consts related func determineSnat() which is to be deleted after
53+ // a baking period with newest NMAgent changes
54+ const (
55+ jsonFileExtension = ".json"
56+ )
57+
3858// NetPlugin represents the CNI network plugin.
3959type netPlugin struct {
4060 * cni.Plugin
4161 nm network.NetworkManager
4262 report * telemetry.CNIReport
4363}
4464
65+ // snatConfiguration contains a bool that determines whether CNI enables snat on host and snat for dns
66+ type snatConfiguration struct {
67+ EnableSnatOnHost bool
68+ EnableSnatForDns bool
69+ }
70+
4571// NewPlugin creates a new netPlugin object.
4672func NewPlugin (name string , config * common.PluginConfig ) (* netPlugin , error ) {
4773 // Setup base plugin.
@@ -190,6 +216,7 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
190216 subnetPrefix net.IPNet
191217 cnsNetworkConfig * cns.GetNetworkContainerResponse
192218 enableInfraVnet bool
219+ enableSnatForDns bool
193220 nwDNSInfo network.DNSInfo
194221 )
195222
@@ -205,6 +232,13 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
205232
206233 log .Printf ("[cni-net] Read network configuration %+v." , nwCfg )
207234
235+ // Temporary if block to determing whether we disable SNAT on host (for multi-tenant scenario only)
236+ if nwCfg .MultiTenancy {
237+ if enableSnatForDns , nwCfg .EnableSnatOnHost , err = determineSnat (); err != nil {
238+ return err
239+ }
240+ }
241+
208242 plugin .setCNIReportDetails (nwCfg , CNI_ADD , "" )
209243
210244 defer func () {
@@ -453,6 +487,7 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
453487 EnableSnatOnHost : nwCfg .EnableSnatOnHost ,
454488 EnableMultiTenancy : nwCfg .MultiTenancy ,
455489 EnableInfraVnet : enableInfraVnet ,
490+ EnableSnatForDns : enableSnatForDns ,
456491 PODName : k8sPodName ,
457492 PODNameSpace : k8sNamespace ,
458493 SkipHotAttachEp : false , // Hot attach at the time of endpoint creation
@@ -855,3 +890,68 @@ func (plugin *netPlugin) Update(args *cniSkel.CmdArgs) error {
855890
856891 return nil
857892}
893+
894+ // Temporary function to determine whether we need to disable SNAT due to NMAgent support
895+ func determineSnat () (bool , bool , error ) {
896+ var (
897+ snatConfig snatConfiguration
898+ retrieveSnatConfigErr error
899+ jsonFile * os.File
900+ httpClient = & http.Client {Timeout : time .Second * 5 }
901+ snatConfigFile = snatConfigFileName + jsonFileExtension
902+ )
903+
904+ // Check if we've already retrieved NMAgent version and determined whether to disable snat on host
905+ if jsonFile , retrieveSnatConfigErr = os .Open (snatConfigFile ); retrieveSnatConfigErr == nil {
906+ bytes , _ := ioutil .ReadAll (jsonFile )
907+ jsonFile .Close ()
908+ if retrieveSnatConfigErr = json .Unmarshal (bytes , & snatConfig ); retrieveSnatConfigErr != nil {
909+ log .Errorf ("[cni-net] failed to unmarshal to snatConfig with error %v" ,
910+ retrieveSnatConfigErr )
911+ }
912+
913+ }
914+
915+ // If we weren't able to retrieve snatConfiguration, query NMAgent
916+ if retrieveSnatConfigErr != nil {
917+ var resp * http.Response
918+ resp , retrieveSnatConfigErr = httpClient .Get (nmAgentSupportedApisURL )
919+ if retrieveSnatConfigErr == nil {
920+ defer resp .Body .Close ()
921+
922+ if resp .StatusCode == http .StatusOK {
923+ var bodyBytes []byte
924+ // if the list of APIs (strings) contains the nmAgentSnatSupportAPI we will disable snat on host
925+ if bodyBytes , retrieveSnatConfigErr = ioutil .ReadAll (resp .Body ); retrieveSnatConfigErr == nil {
926+ bodyStr := string (bodyBytes )
927+ if ! strings .Contains (bodyStr , nmAgentSnatAndDnsSupportAPI ) {
928+ snatConfig .EnableSnatForDns = true
929+ snatConfig .EnableSnatOnHost = ! strings .Contains (bodyStr , nmAgentSnatSupportAPI )
930+ }
931+
932+ jsonStr , _ := json .Marshal (snatConfig )
933+ fp , err := os .OpenFile (snatConfigFile , os .O_CREATE | os .O_WRONLY | os .O_TRUNC , os .FileMode (0664 ))
934+ if err == nil {
935+ fp .Write (jsonStr )
936+ fp .Close ()
937+ } else {
938+ log .Printf ("[cni-net] failed to save snatConfig" )
939+ }
940+ }
941+ } else {
942+ retrieveSnatConfigErr = fmt .Errorf ("nmagent request status code %d" , resp .StatusCode )
943+ }
944+ }
945+ }
946+
947+ // Log and return the error when we fail acquire snat configuration for host and dns
948+ if retrieveSnatConfigErr != nil {
949+ log .Errorf ("[cni-net] failed to acquire SNAT configuration with error %v" ,
950+ retrieveSnatConfigErr )
951+ return snatConfig .EnableSnatForDns , snatConfig .EnableSnatOnHost , retrieveSnatConfigErr
952+ }
953+
954+ log .Printf ("[cni-net] EnableSnatOnHost set to %t; EnableSnatForDns set to %t" , snatConfig .EnableSnatOnHost , snatConfig .EnableSnatForDns )
955+
956+ return snatConfig .EnableSnatForDns , snatConfig .EnableSnatOnHost , nil
957+ }
0 commit comments