network/policy/policy_windows.go (448 lines of code) (raw):

package policy import ( "encoding/json" "fmt" "net" "github.com/Azure/azure-container-networking/cni/log" "github.com/Azure/azure-container-networking/network/networkutils" "github.com/Microsoft/hcsshim" "github.com/Microsoft/hcsshim/hcn" "github.com/pkg/errors" "go.uber.org/zap" ) var logger = log.CNILogger.With(zap.String("component", "net-policy")) const ( // ProtocolTcp indicates tcp protocol id for portmapping ProtocolTcp = 6 // ProtocolUdp indicates udp protocol id for portmapping ProtocolUdp = 17 // CnetAddressSpace indicates constant for the key string CnetAddressSpace = "cnetAddressSpace" // Accelnet NIC IOV policy setting flags // The interrupt moderation value for I/O virtualization(IOV) offloading interruptModeration = 0 // The weight assigned to this port for I/0 virtualization(IOV) offloading; iovOffloadWeight = 100 // The number of queue pairs requested for this port for I/0 virtualization(IOV) offloading queuePairsRequested = 1 ) type KVPairRoutePolicy struct { Type CNIPolicyType `json:"Type"` DestinationPrefix json.RawMessage `json:"DestinationPrefix"` NeedEncap json.RawMessage `json:"NeedEncap"` } type KVPairOutBoundNAT struct { Type CNIPolicyType `json:"Type"` ExceptionList json.RawMessage `json:"ExceptionList"` } type KVPairRoute struct { Type CNIPolicyType `json:"Type"` DestinationPrefix string `json:"DestinationPrefix"` NeedEncap bool `json:"NeedEncap"` } type KVPairL4WfpProxyPolicy struct { Type CNIPolicyType `json:"Type"` OutboundProxyPort string `json:"OutboundProxyPort"` InboundProxyPort string `json:"InboundProxyPort"` UserSID string `json:"UserSID"` FilterTuple json.RawMessage `json:"FilterTuple"` InboundExceptions json.RawMessage `json:"InboundExceptions"` OutboundExceptions json.RawMessage `json:"OutboundExceptions"` } type LoopbackDSR struct { Type CNIPolicyType `json:"Type"` IPAddress net.IP `json:"IPAddress"` } var ValidWinVerForDnsNat bool // SerializePolicies serializes policies to json. func SerializePolicies(policyType CNIPolicyType, policies []Policy, epInfoData map[string]interface{}, enableSnatForDns, enableMultiTenancy bool) []json.RawMessage { var ( jsonPolicies []json.RawMessage snatAndSerialize = enableMultiTenancy && enableSnatForDns ) for _, policy := range policies { if policy.Type == policyType { endpointPolicyType := GetPolicyType(policy) switch endpointPolicyType { case OutBoundNatPolicy: if snatAndSerialize || !enableMultiTenancy { if serializedOutboundNatPolicy, err := SerializeOutBoundNATPolicy(policy, epInfoData); err != nil { logger.Error("Failed to serialize OutBoundNAT policy", zap.Error(err)) } else { jsonPolicies = append(jsonPolicies, serializedOutboundNatPolicy) } } case PortMappingPolicy: // NATPolicy comes as a HNSv2 type, it needs to be converted to HNSv1 if serializedNatPolicy, err := SerializeNATPolicy(policy); err != nil { logger.Error("Failed to serialize NatPolicy", zap.Error(err)) } else { jsonPolicies = append(jsonPolicies, serializedNatPolicy) } case LoopbackDSRPolicy: if dsrPolicy, err := SerializeLoopbackDSRPolicy(policy); err != nil { logger.Error("Failed to serialize DSR policy", zap.Error(err)) } else { jsonPolicies = append(jsonPolicies, dsrPolicy) } default: jsonPolicies = append(jsonPolicies, policy.Data) } } } if snatAndSerialize && ValidWinVerForDnsNat { // SerializePolicies is only called for HnsV1 operations if serializedDnsNatPolicy, err := AddDnsNATPolicyV1(); err != nil { logger.Error("Failed to serialize DnsNAT policy", zap.Error(err)) } else { jsonPolicies = append(jsonPolicies, serializedDnsNatPolicy) } } return jsonPolicies } // GetOutBoundNatExceptionList returns exception list for outbound nat policy func GetOutBoundNatExceptionList(policy Policy) ([]string, error) { var data KVPairOutBoundNAT if err := json.Unmarshal(policy.Data, &data); err != nil { return nil, err } if data.Type == OutBoundNatPolicy { var exceptionList []string if err := json.Unmarshal(data.ExceptionList, &exceptionList); err != nil { return nil, err } return exceptionList, nil } logger.Info("OutBoundNAT policy not set") return nil, nil } func SerializeNATPolicy(policy Policy) (json.RawMessage, error) { var ( endpointPolicy hcn.EndpointPolicy portMappingPolicy hcn.PortMappingPolicySetting ) if err := json.Unmarshal(policy.Data, &endpointPolicy); err != nil { return nil, err } if err := json.Unmarshal(endpointPolicy.Settings, &portMappingPolicy); err != nil { return nil, err } natPolicy := hcsshim.NatPolicy{ Type: "NAT", InternalPort: portMappingPolicy.InternalPort, ExternalPort: portMappingPolicy.ExternalPort, } switch portMappingPolicy.Protocol { case ProtocolTcp: natPolicy.Protocol = "TCP" case ProtocolUdp: natPolicy.Protocol = "UDP" } return json.Marshal(natPolicy) } // SerializeOutBoundNATPolicy formulates OutBoundNAT policy and returns serialized json func SerializeOutBoundNATPolicy(policy Policy, epInfoData map[string]interface{}) (json.RawMessage, error) { outBoundNatPolicy := hcsshim.OutboundNatPolicy{} outBoundNatPolicy.Policy.Type = hcsshim.OutboundNat exceptionList, err := GetOutBoundNatExceptionList(policy) if err != nil { logger.Error("Failed to parse outbound NAT policy", zap.Error(err)) return nil, err } if exceptionList != nil { for _, ipAddress := range exceptionList { outBoundNatPolicy.Exceptions = append(outBoundNatPolicy.Exceptions, ipAddress) } } if epInfoData[CnetAddressSpace] != nil { if cnetAddressSpace := epInfoData[CnetAddressSpace].([]string); cnetAddressSpace != nil { for _, ipAddress := range cnetAddressSpace { outBoundNatPolicy.Exceptions = append(outBoundNatPolicy.Exceptions, ipAddress) } } } if outBoundNatPolicy.Exceptions != nil { serializedOutboundNatPolicy, _ := json.Marshal(outBoundNatPolicy) return serializedOutboundNatPolicy, nil } return nil, fmt.Errorf("OutBoundNAT policy not set") } func SerializeLoopbackDSRPolicy(policy Policy) (json.RawMessage, error) { var dsrData LoopbackDSR if err := hcn.DSRSupported(); err != nil { return []byte{}, errors.Wrap(err, "hcn doesn't support DSR") } if err := json.Unmarshal(policy.Data, &dsrData); err != nil { return []byte{}, errors.Wrap(err, "unmarshal dsr data failed") } logger.Info("DSR policy for", zap.String("ip", dsrData.IPAddress.String())) hnsLoopbackRoute := hcsshim.OutboundNatPolicy{ Policy: hcsshim.Policy{Type: hcsshim.OutboundNat}, Destinations: []string{dsrData.IPAddress.String()}, } rawPolicy, err := json.Marshal(hnsLoopbackRoute) if err != nil { return []byte{}, errors.Wrap(err, "marshal error for loopbackdsr policy") } return rawPolicy, nil } // GetPolicyType parses the policy and returns the policy type func GetPolicyType(policy Policy) CNIPolicyType { // Check if the type is OutBoundNAT var dataOutBoundNAT KVPairOutBoundNAT if err := json.Unmarshal(policy.Data, &dataOutBoundNAT); err == nil { if dataOutBoundNAT.Type == OutBoundNatPolicy { return OutBoundNatPolicy } } // Check if the type is Route var dataRoute KVPairRoute if err := json.Unmarshal(policy.Data, &dataRoute); err == nil { if dataRoute.Type == RoutePolicy { return RoutePolicy } } // Check if the type is L4WFPProxy var l4WfpProxyPolicy KVPairL4WfpProxyPolicy if err := json.Unmarshal(policy.Data, &l4WfpProxyPolicy); err == nil { if l4WfpProxyPolicy.Type == L4WFPProxyPolicy { return L4WFPProxyPolicy } } // Check if the type if Port mapping / NAT var dataPortMapping hcn.EndpointPolicy if err := json.Unmarshal(policy.Data, &dataPortMapping); err == nil { if dataPortMapping.Type == hcn.PortMapping { return PortMappingPolicy } } // Check if the type is ACLPolicy var aclPolicy hcn.AclPolicySetting if err := json.Unmarshal(policy.Data, &aclPolicy); err == nil { if aclPolicy.Action != "" { return ACLPolicy } } // Check if the type is LoopbackDSRPolicy var loopbackDsr LoopbackDSR if err := json.Unmarshal(policy.Data, &loopbackDsr); err == nil { if loopbackDsr.Type == LoopbackDSRPolicy { return LoopbackDSRPolicy } } // Return empty string if the policy type is invalid logger.Info("Returning policyType INVALID") return "" } // SerializeHcnSubnetVlanPolicy serializes subnet policy for VLAN to json. func SerializeHcnSubnetVlanPolicy(vlanID uint32) ([]byte, error) { vlanPolicySetting := &hcn.VlanPolicySetting{ IsolationId: vlanID, } vlanPolicySettingBytes, err := json.Marshal(vlanPolicySetting) if err != nil { return nil, err } vlanSubnetPolicy := &hcn.SubnetPolicy{ Type: hcn.VLAN, Settings: vlanPolicySettingBytes, } vlanSubnetPolicyBytes, err := json.Marshal(vlanSubnetPolicy) if err != nil { return nil, err } return vlanSubnetPolicyBytes, nil } // GetHcnNetAdapterPolicy returns network adapter name policy. func GetHcnNetAdapterPolicy(networkAdapterName string) (hcn.NetworkPolicy, error) { networkAdapterNamePolicy := hcn.NetworkPolicy{ Type: hcn.NetAdapterName, } netAdapterNamePolicySetting := &hcn.NetAdapterNameNetworkPolicySetting{ NetworkAdapterName: networkAdapterName, } netAdapterNamePolicySettingBytes, err := json.Marshal(netAdapterNamePolicySetting) if err != nil { return networkAdapterNamePolicy, err } networkAdapterNamePolicy.Settings = netAdapterNamePolicySettingBytes return networkAdapterNamePolicy, nil } // GetHcnOutBoundNATPolicy returns outBoundNAT policy. func GetHcnOutBoundNATPolicy(policy Policy, epInfoData map[string]interface{}) (hcn.EndpointPolicy, error) { outBoundNATPolicy := hcn.EndpointPolicy{ Type: hcn.OutBoundNAT, } outBoundNATPolicySetting := hcn.OutboundNatPolicySetting{} exceptionList, err := GetOutBoundNatExceptionList(policy) if err != nil { logger.Error("Failed to parse outbound NAT policy", zap.Error(err)) return outBoundNATPolicy, err } if exceptionList != nil { for _, ipAddress := range exceptionList { outBoundNATPolicySetting.Exceptions = append(outBoundNATPolicySetting.Exceptions, ipAddress) } } if epInfoData[CnetAddressSpace] != nil { if cnetAddressSpace := epInfoData[CnetAddressSpace].([]string); cnetAddressSpace != nil { for _, ipAddress := range cnetAddressSpace { outBoundNATPolicySetting.Exceptions = append(outBoundNATPolicySetting.Exceptions, ipAddress) } } } if outBoundNATPolicySetting.Exceptions != nil { outBoundNATPolicySettingBytes, err := json.Marshal(outBoundNATPolicySetting) if err != nil { return outBoundNATPolicy, err } outBoundNATPolicy.Settings = outBoundNATPolicySettingBytes return outBoundNATPolicy, nil } return outBoundNATPolicy, fmt.Errorf("OutBoundNAT policy not set") } // GetHcnRoutePolicy returns Route policy. func GetHcnRoutePolicy(policy Policy) (hcn.EndpointPolicy, error) { routePolicy := hcn.EndpointPolicy{ Type: hcn.SDNRoute, } var data KVPairRoutePolicy if err := json.Unmarshal(policy.Data, &data); err != nil { return routePolicy, err } if data.Type == RoutePolicy { var destinationPrefix string var needEncap bool if err := json.Unmarshal(data.DestinationPrefix, &destinationPrefix); err != nil { return routePolicy, err } if err := json.Unmarshal(data.NeedEncap, &needEncap); err != nil { return routePolicy, err } sdnRoutePolicySetting := &hcn.SDNRoutePolicySetting{ DestinationPrefix: destinationPrefix, NeedEncap: needEncap, } routePolicySettingBytes, err := json.Marshal(sdnRoutePolicySetting) if err != nil { return routePolicy, err } routePolicy.Settings = routePolicySettingBytes return routePolicy, nil } return routePolicy, fmt.Errorf("Invalid policy: %+v. Expecting Route policy", policy) } // GetHcnPortMappingPolicy returns port mapping policy. func GetHcnPortMappingPolicy(policy Policy) (hcn.EndpointPolicy, error) { var portMappingPolicy hcn.EndpointPolicy if err := json.Unmarshal(policy.Data, &portMappingPolicy); err != nil { return portMappingPolicy, fmt.Errorf("Invalid policy: %+v. Expecting PortMapping policy. Error: %v", policy, err) } portMappingPolicy.Type = hcn.PortMapping return portMappingPolicy, nil } // GetHcnACLPolicy returns ACL policy. func GetHcnACLPolicy(policy Policy) (hcn.EndpointPolicy, error) { aclEndpolicySetting := hcn.EndpointPolicy{ Type: hcn.ACL, } // Check beforehand, the input meets the expected format // otherwise, endpoint creation will fail later on. var aclPolicySetting hcn.AclPolicySetting if err := json.Unmarshal(policy.Data, &aclPolicySetting); err != nil { return aclEndpolicySetting, err } aclPolicySettingBytes, err := json.Marshal(aclPolicySetting) if err != nil { return aclEndpolicySetting, err } aclEndpolicySetting.Settings = aclPolicySettingBytes return aclEndpolicySetting, nil } // GetHcnL4WFPProxyPolicy returns L4WFPProxy policy. func GetHcnL4WFPProxyPolicy(policy Policy) (hcn.EndpointPolicy, error) { l4WfpEndpolicySetting := hcn.EndpointPolicy{ Type: hcn.L4WFPPROXY, } // Check beforehand, the input meets the expected format // otherwise, endpoint creation will fail later on. var l4WfpProxyPolicySetting hcn.L4WfpProxyPolicySetting if err := json.Unmarshal(policy.Data, &l4WfpProxyPolicySetting); err != nil { return l4WfpEndpolicySetting, err } l4WfpProxyPolicySettingBytes, err := json.Marshal(l4WfpProxyPolicySetting) if err != nil { return l4WfpEndpolicySetting, err } l4WfpEndpolicySetting.Settings = l4WfpProxyPolicySettingBytes return l4WfpEndpolicySetting, nil } // GetHcnLoopbackDSRPolicy policy is for pod to reach itself by cluster service IP. func GetHcnLoopbackDSRPolicy(policy Policy) (hcn.EndpointPolicy, error) { var dsrData LoopbackDSR if err := hcn.DSRSupported(); err != nil { return hcn.EndpointPolicy{}, errors.Wrap(err, "hcn doesn't support DSR") } if err := json.Unmarshal(policy.Data, &dsrData); err != nil { return hcn.EndpointPolicy{}, errors.Wrap(err, "unmarshal dsr data failed") } logger.Info("DSR policy for", zap.String("ip", dsrData.IPAddress.String())) hcnLoopbackRoute := hcn.OutboundNatPolicySetting{ Destinations: []string{dsrData.IPAddress.String()}, } rawPolicy, _ := json.Marshal(hcnLoopbackRoute) endpointPolicy := hcn.EndpointPolicy{ Type: hcn.OutBoundNAT, Settings: rawPolicy, } return endpointPolicy, nil } // GetHcnEndpointPolicies returns array of all endpoint policies. func GetHcnEndpointPolicies(policyType CNIPolicyType, policies []Policy, epInfoData map[string]interface{}, enableSnatForDns, enableMultiTenancy bool, natInfo []NATInfo) ([]hcn.EndpointPolicy, error) { var hcnEndPointPolicies []hcn.EndpointPolicy for _, policy := range policies { if policy.Type == policyType { var err error var endpointPolicy hcn.EndpointPolicy var isOutboundNatPolicy bool switch GetPolicyType(policy) { case OutBoundNatPolicy: endpointPolicy, err = GetHcnOutBoundNATPolicy(policy, epInfoData) isOutboundNatPolicy = true case RoutePolicy: endpointPolicy, err = GetHcnRoutePolicy(policy) case PortMappingPolicy: endpointPolicy, err = GetHcnPortMappingPolicy(policy) case ACLPolicy: endpointPolicy, err = GetHcnACLPolicy(policy) case L4WFPProxyPolicy: endpointPolicy, err = GetHcnL4WFPProxyPolicy(policy) case LoopbackDSRPolicy: endpointPolicy, err = GetHcnLoopbackDSRPolicy(policy) default: // return error as we should be able to parse all the policies specified return hcnEndPointPolicies, fmt.Errorf("Failed to set Policy: Type: %s, Data: %s", policy.Type, policy.Data) } if err != nil { logger.Error("Failed to parse policy", zap.Any("data", policy.Data), zap.Error(err)) return hcnEndPointPolicies, err } if !(isOutboundNatPolicy && enableMultiTenancy && !enableSnatForDns) { hcnEndPointPolicies = append(hcnEndPointPolicies, endpointPolicy) logger.Info("Successfully retrieve endpoint policy", zap.Any("type", endpointPolicy.Type)) } } } if ValidWinVerForDnsNat { for _, natRule := range natInfo { natPolicy, err := AddNATPolicyV2(natRule.VirtualIP, natRule.Destinations) if err != nil { logger.Error("Failed to retrieve NAT endpoint policy due to error", zap.Error(err)) return hcnEndPointPolicies, err } hcnEndPointPolicies = append(hcnEndPointPolicies, natPolicy) logger.Info("Successfully retrieve natInfo policy", zap.Any("type", natPolicy.Type)) } } return hcnEndPointPolicies, nil } // AddDnsNATPolicyV1 returns serialized DNS NAT policy (json) for HNSv1 func AddDnsNATPolicyV1() (json.RawMessage, error) { outBoundNatPolicy := hcsshim.OutboundNatPolicy{ Policy: hcsshim.Policy{Type: hcsshim.OutboundNat}, Destinations: []string{networkutils.AzureDNS}, } serializedPolicy, err := json.Marshal(outBoundNatPolicy) return serializedPolicy, err } // AddNATPolicyV2 returns serialized endpoint policy based on vip (IP to snat to) and destination(s) func AddNATPolicyV2(vip string, destinations []string) (hcn.EndpointPolicy, error) { outBoundNatPolicySettings := hcn.OutboundNatPolicySetting{VirtualIP: vip, Destinations: destinations} outBoundNatPolicySettingsBytes, err := json.Marshal(outBoundNatPolicySettings) endpointPolicy := hcn.EndpointPolicy{ Type: hcn.OutBoundNAT, Settings: outBoundNatPolicySettingsBytes, } return endpointPolicy, err } // AddAccelnetPolicySetting returns serialized endpoint IOV policy // IOV(I/O virtualization) is the accelnet from networking point of view == VF = Accelnet // To enable IOV, must set iovOffloadWeight to 100 func AddAccelnetPolicySetting() (hcn.EndpointPolicy, error) { accelnetPolicy := hcn.IovPolicySetting{ InterruptModeration: interruptModeration, IovOffloadWeight: iovOffloadWeight, QueuePairsRequested: queuePairsRequested, } rawPolicy, err := json.Marshal(accelnetPolicy) if err != nil { return hcn.EndpointPolicy{}, errors.Wrap(err, "Failed to marshal accelnet policy") } endpointPolicy := hcn.EndpointPolicy{ Type: hcn.IOV, Settings: rawPolicy, } return endpointPolicy, nil }