diff --git a/cni/cni.go b/cni/cni.go index 6279a80..8ac53b1 100644 --- a/cni/cni.go +++ b/cni/cni.go @@ -108,6 +108,7 @@ type OptionalFlags struct { AllowAclPortMapping bool `json:"allowAclPortMapping"` ForceBridgeGateway bool `json:"forceBridgeGateway"` // Intended to be temporary workaround EnableDualStack bool `json:"enableDualStack"` + LoopbackDSR bool `json:"loopbackDSR"` } func (r *Result) Print() { @@ -192,7 +193,13 @@ func (config *NetworkConfig) Serialize() []byte { } // GetNetworkInfo from the NetworkConfig -func (config *NetworkConfig) GetNetworkInfo(podNamespace string) *network.NetworkInfo { +func (config *NetworkConfig) GetNetworkInfo(podNamespace string) (ninfo *network.NetworkInfo, err error) { + if config.OptionalFlags.LoopbackDSR { + if err := hcn.DSRSupported(); err != nil { + logrus.Errorf("[cni-net] Failed to enable loopbackDSR on unsupported HCN version, err:%v.", err) + return nil, err + } + } var subnets []network.SubnetInfo // Note the code below is looking inside the ipam specific configuration. if config.Ipam.Subnet != "" { @@ -234,7 +241,7 @@ func (config *NetworkConfig) GetNetworkInfo(podNamespace string) *network.Networ dnsSettings.Options = config.RuntimeConfig.DNS.Options } - ninfo := &network.NetworkInfo{ + ninfo = &network.NetworkInfo{ ID: config.Name, Name: config.Name, Type: network.NetworkType(config.Name), @@ -251,7 +258,7 @@ func (config *NetworkConfig) GetNetworkInfo(podNamespace string) *network.Networ } } - return ninfo + return ninfo, err } // getInACLRule generates an In ACLs for mapped ports @@ -364,14 +371,14 @@ func GetCurrResult(network *network.NetworkInfo, endpoint *network.EndpointInfo, var ip = GetIP(network, endpoint) ip.InterfaceIndex = 0 - + cIP := cniTypesCurr.IPConfig{ - Version: ip.Version, - Address: net.IPNet{ - IP: ip.Address.IP, - Mask: ip.Address.Mask}, - Gateway: ip.Gateway, - Interface: &ip.InterfaceIndex, + Version: ip.Version, + Address: net.IPNet{ + IP: ip.Address.IP, + Mask: ip.Address.Mask}, + Gateway: ip.Gateway, + Interface: &ip.InterfaceIndex, } result.IPs = append(result.IPs, &cIP) @@ -383,29 +390,29 @@ func GetCurrResult(network *network.NetworkInfo, endpoint *network.EndpointInfo, ip4.InterfaceIndex = 0 cIP4 := cniTypesCurr.IPConfig{ - Version: ip4.Version, - Address: net.IPNet{ - IP: ip4.Address.IP, - Mask: ip4.Address.Mask}, - Gateway: ip4.Gateway, - Interface: &ip4.InterfaceIndex, + Version: ip4.Version, + Address: net.IPNet{ + IP: ip4.Address.IP, + Mask: ip4.Address.Mask}, + Gateway: ip4.Gateway, + Interface: &ip4.InterfaceIndex, } - + result.IPs = append(result.IPs, &cIP4) - if ip6 != nil { + if ip6 != nil { ip6.InterfaceIndex = 0 cIP6 := cniTypesCurr.IPConfig{ - Version: ip6.Version, - Address: net.IPNet{ - IP: ip6.Address.IP, - Mask: ip6.Address.Mask}, - Gateway: ip6.Gateway, - Interface: &ip6.InterfaceIndex, + Version: ip6.Version, + Address: net.IPNet{ + IP: ip6.Address.IP, + Mask: ip6.Address.Mask}, + Gateway: ip6.Gateway, + Interface: &ip6.InterfaceIndex, } - + result.IPs = append(result.IPs, &cIP6) } } @@ -440,23 +447,23 @@ func GetDualStackAddresses(endpoint *network.EndpointInfo) (*IP, *IP) { var ip4 *IP var ip6 *IP - + ip4address := net.IPNet{} ip4address.IP = endpoint.IPAddress - ip4address.Mask = endpoint.IP4Mask + ip4address.Mask = endpoint.IP4Mask ip4 = &IP{ - Version: "4", - Address: cniTypes.IPNet(ip4address), - Gateway: endpoint.Gateway, + Version: "4", + Address: cniTypes.IPNet(ip4address), + Gateway: endpoint.Gateway, InterfaceIndex: 0, } if endpoint.IPAddress6.IP != nil { ip6 = &IP{ - Version: "6", - Address: cniTypes.IPNet(endpoint.IPAddress6), - Gateway: endpoint.Gateway6, + Version: "6", + Address: cniTypes.IPNet(endpoint.IPAddress6), + Gateway: endpoint.Gateway6, InterfaceIndex: 0, } } diff --git a/common/core/network.go b/common/core/network.go index 64ee3ef..3476991 100644 --- a/common/core/network.go +++ b/common/core/network.go @@ -121,7 +121,11 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) (resultError error) { // Convert cniConfig to NetworkInfo // We don't set namespace, setting namespace is not valid for EP creation - networkInfo := cniConfig.GetNetworkInfo(k8sNamespace) + networkInfo, err := cniConfig.GetNetworkInfo(k8sNamespace) + if err != nil { + logrus.Errorf("[cni-net] Failed to get network information from network configuration, err:%v.", err) + return err + } epInfo, err := cniConfig.GetEndpointInfo(networkInfo, args.ContainerID, "") if err != nil { @@ -203,6 +207,12 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) (resultError error) { // Apply the Network Policy for Endpoint epInfo.Policies = append(epInfo.Policies, networkInfo.Policies...) + // If LoopbackDSR is set, add to policies + if cniConfig.OptionalFlags.LoopbackDSR { + hcnLoopbackRoute, _ := network.GetLoopbackDSRPolicy(&epInfo.IPAddress) + epInfo.Policies = append(epInfo.Policies, hcnLoopbackRoute) + } + epInfo, err = plugin.nm.CreateEndpoint(nwConfig.ID, epInfo, args.Netns) if err != nil { logrus.Errorf("[cni-net] Failed to create endpoint, error : %v.", err) @@ -381,7 +391,11 @@ func (plugin *netPlugin) Delete(args *cniSkel.CmdArgs) error { } // Convert cniConfig to NetworkInfo - networkInfo := cniConfig.GetNetworkInfo(k8sNamespace) + networkInfo, err := cniConfig.GetNetworkInfo(k8sNamespace) + if err != nil { + logrus.Errorf("[cni-net] Failed to get network information from network configuration, err:%v.", err) + return err + } epInfo, err := cniConfig.GetEndpointInfo(networkInfo, args.ContainerID, args.Netns) if err != nil { return err diff --git a/example/flannel_l2bridge.conf b/example/flannel_l2bridge.conf index 0727c9c..429234a 100644 --- a/example/flannel_l2bridge.conf +++ b/example/flannel_l2bridge.conf @@ -7,9 +7,10 @@         "dnsCapabilities": true     },     "delegate": { -        "type": "sdnoverlay", +        "type": "sdnbridge", "optionalFlags" : { - "forceBridgeGateway" : true + "forceBridgeGateway" : true, + "loopbackDSR": false },         "AdditionalArgs": [             { diff --git a/example/flannel_overlay.conf b/example/flannel_overlay.conf index d4b8bbe..db3bf10 100644 --- a/example/flannel_overlay.conf +++ b/example/flannel_overlay.conf @@ -8,6 +8,9 @@     },     "delegate": {         "type": "sdnoverlay", + "optionalFlags" : { + "loopbackDSR": false + },         "AdditionalArgs": [             {                 "Name": "EndpointPolicy", diff --git a/example/l2bridge.conf b/example/l2bridge.conf index 55875d4..59178f4 100644 --- a/example/l2bridge.conf +++ b/example/l2bridge.conf @@ -7,6 +7,9 @@ "portMappings": true, "dnsCapabilities": true }, + "optionalFlags" : { + "loopbackDSR": false + }, "ipam": { "subnet": "192.168.1.0/24", "routes": [ diff --git a/example/l2tunnel.conf b/example/l2tunnel.conf index c702a50..0278ead 100644 --- a/example/l2tunnel.conf +++ b/example/l2tunnel.conf @@ -3,6 +3,9 @@ "name": "l2tunnelNetwork", "type": "l2tunnel", "master": "Ethernet", + "optionalFlags" : { + "loopbackDSR": false + }, "ipam": { "type": "azure-vnet-ipam", "Subnet": "10.240.0.0/12" diff --git a/network/policy.go b/network/policy.go index 6a8407f..a1b05d1 100644 --- a/network/policy.go +++ b/network/policy.go @@ -6,9 +6,11 @@ package network import ( "encoding/json" "errors" - "github.com/Microsoft/hcsshim/hcn" + "net" "strconv" "strings" + + "github.com/Microsoft/hcsshim/hcn" ) type CNIPolicyType string @@ -57,7 +59,6 @@ func GetPortEnumValue(protocol string) (uint32, error) { // GetPortMappingPolicy creates an HCN PortMappingPolicy and stores it in CNI Policy. func GetPortMappingPolicy(externalPort int, internalPort int, protocol string, hostIp string, flags uint32) (Policy, error) { - // protocol can be passed either as a number or a name protocolInt, err := GetPortEnumValue(protocol) if err != nil { @@ -83,3 +84,20 @@ func GetPortMappingPolicy(externalPort int, internalPort int, protocol string, h Data: rawData, }, nil } + +// GetLoopbackDSRPolicy creates a policy to support loopback direct server return. +func GetLoopbackDSRPolicy(ip *net.IP) (Policy, error) { + hcnLoopbackRoute := hcn.OutboundNatPolicySetting{ + Destinations: []string{ip.String()}, + } + rawPolicy, _ := json.Marshal(hcnLoopbackRoute) + endpointPolicy := hcn.EndpointPolicy{ + Type: hcn.OutBoundNAT, + Settings: rawPolicy, + } + rawData, _ := json.Marshal(endpointPolicy) + return Policy{ + Type: EndpointPolicy, + Data: rawData, + }, nil +}