/* * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.mapper.policyenforcer; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxRegMatch; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.applyActionIns; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.gotoTableIns; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.instructions; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxOutputRegAction; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.opendaylight.groupbasedpolicy.api.sf.AllowActionDefinition; import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition; import org.opendaylight.groupbasedpolicy.dto.EgKey; import org.opendaylight.groupbasedpolicy.dto.EndpointConstraint; import org.opendaylight.groupbasedpolicy.dto.IndexedTenant; import org.opendaylight.groupbasedpolicy.dto.Policy; import org.opendaylight.groupbasedpolicy.dto.RuleGroup; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.endpoint.EndpointManager; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowIdUtils; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowTable; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory.EndpointFwdCtxOrdinals; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Action; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ChainAction; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ClassificationResult; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Classifier; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ParamDerivator; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.SubjectFeatures; import org.opendaylight.groupbasedpolicy.util.TenantUtils; import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName; import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix; import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.go.to.table._case.GoToTable; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.action.refs.ActionRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.EndpointGroup; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.EndpointGroup.IntraGroupPolicy; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.ExternalImplicitGroup; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.contract.subject.Rule; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstance; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; import com.google.common.collect.ComparisonChain; import com.google.common.collect.Ordering; import com.google.common.collect.Table.Cell; /** * <h1>Manage the table that enforces policy on the traffic. Traffic is denied * unless specifically allowed by policy (table=4)</h1> * In policy enforcer, according to current {@link Policy} specific traffic is sent to SFC (nsp and * nsi is set), or from SFC * to some {@link Endpoint} or to another classifier. * <p> * <i>Tunnel/overlay flows</i><br> * Priority = 65000 (if more flows, decrements)<br> * Matches:<br> * - ethertype (tcp, tcp6, ipv6, icmp or missing)<br> * - Reg0 {@link NxmNxReg0}<br> * - Reg1 {@link NxmNxReg1}<br> * - Reg2 {@link NxmNxReg2}<br> * - Reg3 {@link NxmNxReg3}<br> * - L3 for src_ip_prefix (if exists)<br> * - L3 for dst_ip_prefix (if exists)<br> * Actions:<br> * - set nsi (only chain action)<br> * - set nsp (only chain action)<br> * - {@link GoToTable} EXTERNAL MAPPER table<br> * <p> * <i>Allow from tunnel flow</i><br> * Priority = 65000<br> * Matches:<br> * - Reg1 (set to 0xffffff) {@link NxmNxReg1}<br> * - in_port (should be tunnel port) {@link NodeConnectorId}<br> * Actions:<br> * - output:port (Reg7) {@link NxmNxReg7}<br> * <p> * Traffic is sent from one {@link EndpointGroup} to the same EPG * <p> * <i>Allow from same EPG flow</i><br> * Priority = 65000<br> * Matches:<br> * - Reg0 {@link NxmNxReg0}<br> * - Reg2 {@link NxmNxReg2}<br> * Actions:<br> * - output:port (Reg7) {@link NxmNxReg7} * <p> * <i>Arp flow</i><br> * Priority = 20000<br> * Matches:<br> * - ethernet match (arp)<br> * - Reg5 {@link NxmNxReg5}<br> * Actions:<br> * - output:port (Reg7) {@link NxmNxReg7} */ public class PolicyEnforcer extends FlowTable { private static final Logger LOG = LoggerFactory.getLogger(PolicyEnforcer.class); private static short TABLE_ID; private static Instruction gotoEgressNatInstruction; private static Instruction gotoExternalInstruction; public PolicyEnforcer(OfContext ctx, short tableId) { super(ctx); TABLE_ID = tableId; gotoEgressNatInstruction = gotoTableIns(ctx.getPolicyManager().getTABLEID_EGRESS_NAT()); gotoExternalInstruction = gotoTableIns(ctx.getPolicyManager().getTABLEID_EXTERNAL_MAPPER()); } @Override public short getTableId() { return TABLE_ID; } @Override public void sync(Endpoint endpoint, OfWriter ofWriter) throws Exception { Preconditions.checkNotNull(endpoint); Preconditions.checkNotNull(ofWriter); NodeId nodeId = ctx.getEndpointManager().getEndpointNodeId(endpoint); ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(1, null, TABLE_ID)); NodeConnectorId tunPort = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlan.class); if (tunPort != null) { ofWriter.writeFlow(nodeId, TABLE_ID, allowFromTunnel(tunPort)); } EndpointFwdCtxOrdinals srcEpFwdCxtOrdinals = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, endpoint); if (srcEpFwdCxtOrdinals == null) { LOG.debug("Method getEndpointFwdCtxOrdinals returned null for EP {}", endpoint); return; } for (EgKey sourceEpg : ctx.getEndpointManager().getEgKeysForEndpoint(endpoint)) { for (EgKey destEpg : ctx.getCurrentPolicy().getPeers(sourceEpg)) { Collection<Endpoint> destinationEndpoints = getEndpointsForGroup(destEpg); for (Endpoint destinationEndpoint : destinationEndpoints) { EndpointFwdCtxOrdinals dstEpFwdCxtOrdinals = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, destinationEndpoint); if (dstEpFwdCxtOrdinals == null) { LOG.debug("Method getEndpointFwdCtxOrdinals returned null for EP {}", destinationEndpoint); continue; } NetworkElements netElements = new NetworkElements(endpoint, destinationEndpoint, sourceEpg, destEpg, nodeId, ctx); // Get policy in both directions Policy sourceEpgPolicy = ctx.getCurrentPolicy().getPolicy(destEpg, sourceEpg); Policy destinationEpgPolicy = ctx.getCurrentPolicy().getPolicy(sourceEpg, destEpg); // Resolve flows in both directions if possible according to policy. Get back // status // of resolution resolveSourceEpgPolicy(ofWriter, netElements, sourceEpgPolicy, destinationEpgPolicy); ofWriter.writeFlow(nodeId, TABLE_ID, createArpFlow(srcEpFwdCxtOrdinals.getFdId())); } } // Allow same EPG allowSameEpg(sourceEpg, endpoint, nodeId, ofWriter); } } private Set<Endpoint> getEndpointsForGroup(EgKey epg) { Set<Endpoint> destinationEndpoints = new HashSet<>(); destinationEndpoints.addAll(ctx.getEndpointManager().getEndpointsForGroup(epg)); destinationEndpoints.addAll(ctx.getEndpointManager().getExtEpsNoLocForGroup(epg)); return destinationEndpoints; } private void resolveSourceEpgPolicy(OfWriter ofWriter, NetworkElements netElements, Policy directPolicy, Policy reversedPolicy) { List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> providedRules = getActiveRulesBetweenEps(directPolicy, netElements.getDstEp(), netElements.getSrcEp()); List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> consumedRules = getActiveRulesBetweenEps(reversedPolicy, netElements.getSrcEp(), netElements.getDstEp()); List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> activeRules; if (!providedRules.isEmpty()) { activeRules = providedRules; } else { activeRules = consumedRules; } int priority = 65000; for (Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>> activeRulesByConstraints : activeRules) { Set<IpPrefix> sIpPrefixes; Set<IpPrefix> dIpPrefixes; if (providedRules.contains(activeRulesByConstraints)) { sIpPrefixes = Policy.getIpPrefixesFrom(activeRulesByConstraints.getRowKey().getL3EpPrefixes()); dIpPrefixes = Policy.getIpPrefixesFrom(activeRulesByConstraints.getColumnKey().getL3EpPrefixes()); } else { sIpPrefixes = Policy.getIpPrefixesFrom(activeRulesByConstraints.getColumnKey().getL3EpPrefixes()); dIpPrefixes = Policy.getIpPrefixesFrom(activeRulesByConstraints.getRowKey().getL3EpPrefixes()); } for (RuleGroup rg : Ordering.from(new RuleGroupComparator()) .immutableSortedCopy(activeRulesByConstraints.getValue())) { TenantId tenantId = rg.getContractTenant().getId(); IndexedTenant tenant = ctx.getTenant(tenantId); for (Direction direction : new Direction[] {Direction.In, Direction.Out}) { List<Rule> sameDirectionRules; if (providedRules.contains(activeRulesByConstraints)) { sameDirectionRules = uniteRulesByDirection(direction, rg.getRules(), getRules(consumedRules)); } else { sameDirectionRules = uniteRulesByDirection(direction, null, rg.getRules()); } for (Rule rule : Ordering.from(TenantUtils.RULE_COMPARATOR) .immutableSortedCopy(sameDirectionRules)) { createFlowsForRule(rule, getRules(providedRules), getRules(consumedRules), direction, netElements, ofWriter, tenant, sIpPrefixes, dIpPrefixes, priority); priority--; } } } } } private List<Rule> uniteRulesByDirection (Direction direction, List<Rule> directRules, List<Rule> reversedRules) { List<Rule> sameDirectionRules = findRulesInDirection(direction, directRules); sameDirectionRules .addAll(findRulesInDirection(reverse(direction), reversedRules)); return sameDirectionRules; } private void createFlowsForRule(Rule rule, List<Rule> providedRules, List<Rule> consumedRules, Direction direction, NetworkElements netElements,OfWriter ofWriter, IndexedTenant tenant, Set<IpPrefix> sIpPrefixes, Set<IpPrefix> dIpPrefixes, int priority) { List<Rule> reverseProvidedRules = findRulesInDirection(reverse(direction), providedRules); List<String> resolvedSymmetricChains = resolveSymetricChainActions(direction, rule, tenant, reverseProvidedRules, consumedRules); if (resolvedSymmetricChains == null) { LOG.debug("Rule {} skipped. Reason: asymmetric use of symmetric chain", rule); return; } // Create list of matches/actions. Also creates chain flows when // specific action requires it List<MatchBuilder> matches = null; if (consumedRules.contains(rule)) { matches = createMatches(direction, reverse(direction), netElements, tenant, rule, sIpPrefixes, dIpPrefixes); } else { matches = createMatches(direction, direction, netElements, tenant, rule, sIpPrefixes, dIpPrefixes); } List<ActionBuilder> actions = createActions(ofWriter, netElements, direction, tenant, rule, resolvedSymmetricChains); if (actions == null) { return; } // Compose flows createFlows(matches, actions, netElements, ofWriter, priority); } private List<String> resolveSymetricChainActions(Direction direction, Rule rule, IndexedTenant tenant, List<Rule> reversedProvidedRules, List<Rule> consumedRules) { List<String> chainNames = new ArrayList<>(); if (rule.getActionRef() != null) { for (ActionRef actionRef : rule.getActionRef()) { ActionInstance actionInstance = tenant.getAction(actionRef.getName()); if (actionInstance == null) { continue; } Action action = SubjectFeatures.getAction(actionInstance.getActionDefinitionId()); if (action == null) { continue; } if (action instanceof ChainAction) { chainNames = getSymetricChainNames(actionInstance); if (chainNames.isEmpty()) { continue; } List<Rule> reversedRules = findRulesInDirection(reverse(direction), reversedProvidedRules); reversedRules.addAll(findRulesInDirection(direction, consumedRules)); List<String> oppositeChainNames = new ArrayList<>(); for (Rule oppositeRule : reversedRules) { if (oppositeRule.getActionRef() == null) { continue; } for (ActionRef oppositeActionRef : oppositeRule.getActionRef()) { ActionInstance oppositeActionInstance = tenant.getAction(oppositeActionRef.getName()); if (oppositeActionInstance == null) { continue; } Action oppositeAction = SubjectFeatures.getAction(oppositeActionInstance.getActionDefinitionId()); if (oppositeAction == null) { continue; } if (oppositeAction instanceof ChainAction) { oppositeChainNames.addAll(getSymetricChainNames(oppositeActionInstance)); } } } if (!oppositeChainNames.containsAll(chainNames)) { return null; } if ((consumedRules.contains(rule) && (direction.equals(Direction.Out))) || ((!consumedRules.contains(rule)) && direction.equals(Direction.In))) { return new ArrayList<>(); } } } } return chainNames; } private List<String> getSymetricChainNames(ActionInstance action) { List<String> chainNames = new ArrayList<>(); for (ParameterValue param : action.getParameterValue()) { if (param.getStringValue() != null && param.getName().getValue().equals(ChainActionDefinition.SFC_CHAIN_NAME)) { String chainName = param.getStringValue(); ServiceFunctionPath sfcPath = ChainAction.getSfcPath(new SfcName(chainName)); if (sfcPath == null || sfcPath.getName() == null) { continue; } if (sfcPath.isSymmetric()) { chainNames.add(param.getStringValue()); } } } return chainNames; } private void allowSameEpg(EgKey epgKey, Endpoint sourceEp, NodeId nodeId, OfWriter ofWriter) throws Exception { IndexedTenant tenant = ctx.getTenant(epgKey.getTenantId()); if (tenant != null) { EndpointGroup group = tenant.getEndpointGroup(epgKey.getEgId()); if (group == null) { LOG.debug("EPG {} does not exit and is used ", epgKey); return; } IntraGroupPolicy igp = group.getIntraGroupPolicy(); if (igp == null || igp.equals(IntraGroupPolicy.Allow)) { EndpointFwdCtxOrdinals srcEpFwdCxtOrdinals = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, sourceEp); if (srcEpFwdCxtOrdinals == null) { LOG.debug("getEndpointFwdCtxOrdinals is null for EP {}", sourceEp); } int epgId = srcEpFwdCxtOrdinals.getEpgId(); ofWriter.writeFlow(nodeId, TABLE_ID, allowSameEpg(epgId)); } } } // Return list of all rules with opposite direction private List<Rule> findRulesInDirection(Direction direction, List<Rule> rules) { List<Rule> sameDirectionRules = new ArrayList<>(); if (rules != null) { for (Rule ruleToCompare : rules) { if (isSameDirection(direction, ruleToCompare)) { sameDirectionRules.add(ruleToCompare); } } } return sameDirectionRules; } private boolean isSameDirection(Direction direction, Rule rule) { for (ClassifierRef classifier : rule.getClassifierRef()) { if (direction.equals(classifier.getDirection()) || direction.equals(Direction.Bidirectional) || Direction.Bidirectional.equals(classifier.getDirection())) { return true; } } return false; } private List<Rule> getRules(List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> activeRules) { List<Rule> rules = new ArrayList<>(); for (Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>> activeRule : activeRules) { for (RuleGroup ruleGroup : activeRule.getValue()) { for (Rule rule : ruleGroup.getRules()) { rules.add(rule); } } } return rules; } private Flow createArpFlow(Integer fdId) { Long etherType = FlowUtils.ARP; // L2 Classifier so 20,000 for now Integer priority = 20000; MatchBuilder mb = new MatchBuilder().setEthernetMatch(FlowUtils.ethernetMatch(null, null, etherType)); addNxRegMatch(mb, RegMatch.of(NxmNxReg5.class, Long.valueOf(fdId))); Match match = mb.build(); FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "arp", match); return base().setPriority(priority) .setId(flowid) .setMatch(match) .setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class)))) .build(); } private Flow allowSameEpg(int epgId) { MatchBuilder mb = new MatchBuilder(); addNxRegMatch(mb, RegMatch.of(NxmNxReg0.class, (long) epgId), RegMatch.of(NxmNxReg2.class, (long) epgId)); Match match = mb.build(); FlowId flowId = FlowIdUtils.newFlowId(TABLE_ID, "intraallow", match); FlowBuilder flow = base().setId(flowId).setMatch(match).setPriority(65000).setInstructions( instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class)))); return flow.build(); } private Flow allowFromTunnel(NodeConnectorId tunPort) { MatchBuilder mb = new MatchBuilder().setInPort(tunPort); addNxRegMatch(mb, RegMatch.of(NxmNxReg1.class, 0xffffffL)); Match match = mb.build(); FlowId flowId = FlowIdUtils.newFlowId(TABLE_ID, "tunnelallow", match); FlowBuilder flow = base().setId(flowId).setMatch(match).setPriority(65000).setInstructions( instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class)))); return flow.build(); } private List<MatchBuilder> createMatches(Direction flowDirection, Direction classifierDirection, NetworkElements netElements, IndexedTenant contractTenant, Rule rule, Set<IpPrefix> sIpPrefixes, Set<IpPrefix> dIpPrefixes) { Map<String, ParameterValue> paramsFromClassifier = new HashMap<>(); Set<ClassifierDefinitionId> classifiers = new HashSet<>(); for (ClassifierRef cr : rule.getClassifierRef()) { if (cr.getDirection() != null && !cr.getDirection().equals(Direction.Bidirectional) && !cr.getDirection().equals(classifierDirection)) { continue; } // XXX - TODO - implement connection tracking (requires openflow // extension and data plane support - in 2.4. Will need to handle // case where we are working with mix of nodes. ClassifierInstance ci = contractTenant.getClassifier(cr.getInstanceName()); if (ci == null) { // XXX TODO fail the match and raise an exception LOG.warn("Classifier instance {} not found", cr.getInstanceName().getValue()); return null; } Classifier classifier = SubjectFeatures.getClassifier(ci.getClassifierDefinitionId()); if (classifier == null) { // XXX TODO fail the match and raise an exception LOG.warn("Classifier definition {} not found", ci.getClassifierDefinitionId().getValue()); return null; } classifiers.add(new ClassifierDefinitionId(ci.getClassifierDefinitionId())); for (ParameterValue v : ci.getParameterValue()) { if (paramsFromClassifier.get(v.getName().getValue()) == null) { if (v.getIntValue() != null || v.getStringValue() != null || v.getRangeValue() != null) { paramsFromClassifier.put(v.getName().getValue(), v); } } else { if (!paramsFromClassifier.get(v.getName().getValue()).equals(v)) { throw new IllegalArgumentException("Classification error in rule: " + rule.getName() + ".\nCause: " + "Classification conflict detected at parameter " + v.getName()); } } } } if (classifiers.isEmpty()) { return null; } List<Map<String, ParameterValue>> derivedParamsByName = ParamDerivator.ETHER_TYPE_DERIVATOR.deriveParameter(paramsFromClassifier); List<MatchBuilder> flowMatchBuilders = new ArrayList<>(); for (Map<String, ParameterValue> params : derivedParamsByName) { List<MatchBuilder> matchBuildersToResolve = new ArrayList<>(); if (sIpPrefixes.isEmpty() && dIpPrefixes.isEmpty()) { matchBuildersToResolve.add(createBaseMatch(flowDirection, netElements, null, null)); } else if (!sIpPrefixes.isEmpty() && dIpPrefixes.isEmpty()) { for (IpPrefix sIpPrefix : sIpPrefixes) { matchBuildersToResolve.add(createBaseMatch(flowDirection, netElements, sIpPrefix, null)); } } else if (sIpPrefixes.isEmpty() && !dIpPrefixes.isEmpty()) { for (IpPrefix dIpPrefix : dIpPrefixes) { matchBuildersToResolve.add(createBaseMatch(flowDirection, netElements, null, dIpPrefix)); } } else { for (IpPrefix sIpPrefix : sIpPrefixes) { for (IpPrefix dIpPrefix : dIpPrefixes) { matchBuildersToResolve.add(createBaseMatch(flowDirection, netElements, sIpPrefix, dIpPrefix)); } } } for (ClassifierDefinitionId clDefId : classifiers) { Classifier classifier = SubjectFeatures.getClassifier(clDefId); ClassificationResult result = classifier.updateMatch(matchBuildersToResolve, params); if (!result.isSuccessfull()) { // TODO consider different handling. throw new IllegalArgumentException("Classification conflict detected in rule: " + rule.getName() + ".\nCause: " + result.getErrorMessage()); } matchBuildersToResolve = new ArrayList<>(result.getMatchBuilders()); } flowMatchBuilders.addAll(matchBuildersToResolve); } return flowMatchBuilders; } private List<ActionBuilder> createActions(OfWriter ofWriter, NetworkElements netElements, Direction direction, IndexedTenant contractTenant, Rule rule, List<String> resolvedSymmetricChains) { List<ActionBuilder> actionBuilderList = new ArrayList<>(); if (rule.getActionRef() != null) { // Pre-sort by references using order, then name List<ActionRef> actionRefList = new ArrayList<>(rule.getActionRef()); Collections.sort(actionRefList, ActionRefComparator.INSTANCE); for (ActionRef actionRef : actionRefList) { ActionInstance actionInstance = contractTenant.getAction(actionRef.getName()); if (actionInstance == null) { // XXX TODO fail the match and raise an exception LOG.warn("Action instance {} not found", actionRef.getName().getValue()); return null; } Action action = SubjectFeatures.getAction(actionInstance.getActionDefinitionId()); if (action == null) { // XXX TODO fail the match and raise an exception LOG.warn("Action definition {} not found", actionInstance.getActionDefinitionId().getValue()); return null; } Map<String, Object> params = new HashMap<>(); if (actionInstance.getParameterValue() != null) { for (ParameterValue v : actionInstance.getParameterValue()) { if (v.getName() == null) continue; if (v.getIntValue() != null) { params.put(v.getName().getValue(), v.getIntValue()); } else if (v.getStringValue() != null) { params.put(v.getName().getValue(), v.getStringValue()); } } } if (action instanceof ChainAction) { ((ChainAction) action).setResolvedSymmetricChains(resolvedSymmetricChains); } // Convert the GBP Action to one or more OpenFlow Actions if ((!(actionRefList.indexOf(actionRef) == (actionRefList.size() - 1) && action.equals(SubjectFeatures.getAction(AllowActionDefinition.DEFINITION.getId())))) && actionBuilderList != null) { if (ctx.getDataBroker() != null) { actionBuilderList = action.updateAction(actionBuilderList, params, actionRef.getOrder(), netElements, ofWriter, ctx, direction); } else { LOG.error("DataBroket is null. Cannot update action {}", action.getActionDef().getName().getValue()); return null; } } } } return actionBuilderList; } public static Direction reverse(Direction direction) { if (direction.equals(Direction.In)) { return Direction.Out; } else if (direction.equals(Direction.Out)) { return Direction.In; } else { return Direction.Bidirectional; } } private void createFlows(List<MatchBuilder> flowMatchBuilders, List<ActionBuilder> actionBuilderList, NetworkElements netElements, OfWriter ofWriter, int priority) { FlowBuilder flow = base().setPriority(priority); if (flowMatchBuilders == null) { return; } for (MatchBuilder mb : flowMatchBuilders) { Match match = mb.build(); FlowId flowId = FlowIdUtils.newFlowId(TABLE_ID, "cg", match); flow.setMatch(match).setId(flowId).setPriority(priority); // If destination is External, the last Action ALLOW must be changed to goto // NAT/External table. // If actionBuilderList is empty (we removed the last Allow) then go straight to // ExternalMapper table. List<ExternalImplicitGroup> eigs = ctx.getTenant(netElements.getDstEp().getTenant()) .getTenant() .getPolicy() .getExternalImplicitGroup(); boolean performNat = false; for (EndpointL3 natEp : ctx.getEndpointManager().getL3EndpointsWithNat()) { if (natEp.getMacAddress() != null && natEp.getL2Context() != null && netElements.getSrcEp() .getKey() .equals(new EndpointKey(natEp.getL2Context(), natEp.getMacAddress())) && EndpointManager.isExternal(netElements.getDstEp(), eigs)) { performNat = true; break; } } if (actionBuilderList == null) { // flow with this match should not appear on switch (e.g. chain action IN) // //TODO - analyse, what happen for unknown action, SFC, etc. continue; } if (actionBuilderList.isEmpty()) { flow.setInstructions((performNat == true) ? instructions(gotoEgressNatInstruction) : instructions( gotoExternalInstruction)); } else { flow.setInstructions(instructions(applyActionIns(actionBuilderList), (performNat == true) ? gotoEgressNatInstruction : gotoExternalInstruction)); } ofWriter.writeFlow(netElements.getLocalNodeId(), TABLE_ID, flow.build()); } } private MatchBuilder createBaseMatch(Direction direction, NetworkElements netElements, IpPrefix sIpPrefix, IpPrefix dIpPrefix) { MatchBuilder baseMatch = new MatchBuilder(); if (direction.equals(Direction.In)) { addNxRegMatch(baseMatch, RegMatch.of(NxmNxReg0.class, (long) netElements.getDstEpOrdinals().getEpgId()), RegMatch.of(NxmNxReg1.class, (long) netElements.getDstEpOrdinals().getCgId()), RegMatch.of(NxmNxReg2.class, (long) netElements.getSrcEpOrdinals().getEpgId()), RegMatch.of(NxmNxReg3.class, (long) netElements.getSrcEpOrdinals().getCgId())); if (sIpPrefix != null) { baseMatch.setLayer3Match(createLayer3Match(sIpPrefix, true)); } if (dIpPrefix != null) { baseMatch.setLayer3Match(createLayer3Match(dIpPrefix, false)); } } else { addNxRegMatch(baseMatch, RegMatch.of(NxmNxReg0.class, (long) netElements.getSrcEpOrdinals().getEpgId()), RegMatch.of(NxmNxReg1.class, (long) netElements.getSrcEpOrdinals().getCgId()), RegMatch.of(NxmNxReg2.class, (long) netElements.getDstEpOrdinals().getEpgId()), RegMatch.of(NxmNxReg3.class, (long) netElements.getDstEpOrdinals().getCgId())); if (sIpPrefix != null) { baseMatch.setLayer3Match(createLayer3Match(sIpPrefix, false)); } if (dIpPrefix != null) { baseMatch.setLayer3Match(createLayer3Match(dIpPrefix, true)); } } return baseMatch; } private Layer3Match createLayer3Match(IpPrefix ipPrefix, boolean isSrc) { if (ipPrefix.getIpv4Prefix() != null) { if (isSrc) { return new Ipv4MatchBuilder().setIpv4Source(ipPrefix.getIpv4Prefix()).build(); } else { return new Ipv4MatchBuilder().setIpv4Destination(ipPrefix.getIpv4Prefix()).build(); } } else { if (isSrc) { return new Ipv6MatchBuilder().setIpv6Source(ipPrefix.getIpv6Prefix()).build(); } else { return new Ipv6MatchBuilder().setIpv6Destination(ipPrefix.getIpv6Prefix()).build(); } } } // TODO: move to a common utils for all renderers private List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> getActiveRulesBetweenEps(Policy policy, Endpoint consEp, Endpoint provEp) { List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> rulesWithEpConstraints = new ArrayList<>(); for (Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>> cell : policy.getRuleMap().cellSet()) { EndpointConstraint consEpConstraint = cell.getRowKey(); EndpointConstraint provEpConstraint = cell.getColumnKey(); if (epMatchesConstraint(consEp, consEpConstraint) && epMatchesConstraint(provEp, provEpConstraint)) { rulesWithEpConstraints.add(cell); } } return rulesWithEpConstraints; } private boolean epMatchesConstraint(Endpoint ep, EndpointConstraint constraint) { List<ConditionName> epConditions = Collections.emptyList(); if (ep.getCondition() != null) { epConditions = ep.getCondition(); } return constraint.getConditionSet().matches(epConditions); } /** * Private internal class for ordering Actions in Rules. The order is * determined first by the value of the order parameter, with the lower * order actions being applied first; for Actions with either the same order * or no order, ordering is lexicographical by name. */ private static class ActionRefComparator implements Comparator<ActionRef> { public static final ActionRefComparator INSTANCE = new ActionRefComparator(); @Override public int compare(ActionRef arg0, ActionRef arg1) { return ComparisonChain.start() .compare(arg0.getOrder(), arg1.getOrder(), Ordering.natural().nullsLast()) .compare(arg0.getName().getValue(), arg1.getName().getValue(), Ordering.natural().nullsLast()) .result(); } } private static class RuleGroupComparator implements Comparator<RuleGroup> { @Override public int compare(RuleGroup arg0, RuleGroup arg1) { return ComparisonChain.start() .compare(arg0.getOrder(), arg1.getOrder(), Ordering.natural().nullsLast()) .compare(arg0.getRelatedSubject().getValue(), arg1.getRelatedSubject().getValue() ,Ordering.natural().nullsLast()) .result(); } } }