BUG 5402 - PolicyEnforcer creates wrong directed flows
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / flow / PolicyEnforcer.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
10
11 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxRegMatch;
12 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.applyActionIns;
13 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.gotoTableIns;
14 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.instructions;
15 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxOutputRegAction;
16
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Comparator;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25
26 import javax.annotation.concurrent.Immutable;
27
28 import org.opendaylight.groupbasedpolicy.api.sf.AllowActionDefinition;
29 import org.opendaylight.groupbasedpolicy.api.sf.EtherTypeClassifierDefinition;
30 import org.opendaylight.groupbasedpolicy.api.sf.IpProtoClassifierDefinition;
31 import org.opendaylight.groupbasedpolicy.api.sf.L4ClassifierDefinition;
32 import org.opendaylight.groupbasedpolicy.dto.EgKey;
33 import org.opendaylight.groupbasedpolicy.dto.EndpointConstraint;
34 import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
35 import org.opendaylight.groupbasedpolicy.dto.Policy;
36 import org.opendaylight.groupbasedpolicy.dto.RuleGroup;
37 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
38 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
39 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.endpoint.EndpointManager;
40 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch;
41 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory.EndpointFwdCtxOrdinals;
42 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Action;
43 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.AllowAction;
44 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ChainAction;
45 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ClassificationResult;
46 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Classifier;
47 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ParamDerivator;
48 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.SubjectFeatures;
49 import org.opendaylight.groupbasedpolicy.util.TenantUtils;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.go.to.table._case.GoToTable;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.action.refs.ActionRef;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.EndpointGroup;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.EndpointGroup.IntraGroupPolicy;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.ExternalImplicitGroup;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.contract.subject.Rule;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstance;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
87 import org.slf4j.Logger;
88 import org.slf4j.LoggerFactory;
89
90 import com.google.common.collect.ArrayListMultimap;
91 import com.google.common.collect.ComparisonChain;
92 import com.google.common.collect.ListMultimap;
93 import com.google.common.collect.Ordering;
94 import com.google.common.collect.Table.Cell;
95
96 /**
97  * <h1>Manage the table that enforces policy on the traffic. Traffic is denied
98  * unless specifically allowed by policy (table=4)</h1>
99  *
100  * In policy enforcer, according to current {@link Policy} specific traffic is sent to SFC (nsp and nsi is set), or from SFC
101  * to some {@link Endpoint} or to another classifier.
102  * <p>
103  * <i>Tunnel/overlay flows</i><br>
104  * Priority = 65000 (if more flows, decrements)<br>
105  * Matches:<br>
106  *      - ethertype (tcp, tcp6, ipv6, icmp or missing)<br>
107  *      - Reg0 {@link NxmNxReg0}<br>
108  *      - Reg1 {@link NxmNxReg1}<br>
109  *      - Reg2 {@link NxmNxReg2}<br>
110  *      - Reg3 {@link NxmNxReg3}<br>
111  *      - L3 for src_ip_prefix (if exists)<br>
112  *      - L3 for dst_ip_prefix (if exists)<br>
113  * Actions:<br>
114  *      - set nsi (only chain action)<br>
115  *      - set nsp (only chain action)<br>
116  *      - {@link GoToTable} EXTERNAL MAPPER table<br>
117  *<p>
118  * <i>Allow from tunnel flow</i><br>
119  * Priority = 65000<br>
120  * Matches:<br>
121  *      - Reg1 (set to 0xffffff) {@link NxmNxReg1}<br>
122  *      - in_port (should be tunnel port) {@link NodeConnectorId}<br>
123  * Actions:<br>
124  *      - output:port (Reg7) {@link NxmNxReg7}<br>
125  * <p>
126  * Traffic is sent from one {@link EndpointGroup} to the same EPG
127  * <p>
128  * <i>Allow from same EPG flow</i><br>
129  * Priority = 65000<br>
130  * Matches:<br>
131  *      - Reg0 {@link NxmNxReg0}<br>
132  *      - Reg2 {@link NxmNxReg2}<br>
133  * Actions:<br>
134  *      - output:port (Reg7) {@link NxmNxReg7}
135  * <p>
136  * <i>Arp flow</i><br>
137  * Priority = 20000<br>
138  * Matches:<br>
139  *      - ethernet match (arp)<br>
140  *      - Reg5 {@link NxmNxReg5}<br>
141  * Actions:<br>
142  *      - output:port (Reg7) {@link NxmNxReg7}
143  *
144  */
145 public class PolicyEnforcer extends FlowTable {
146
147     private static final Logger LOG = LoggerFactory.getLogger(PolicyEnforcer.class);
148     public static short TABLE_ID;
149     private static boolean isReversedPolicy;
150     private static org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction gotoEgressNatInstruction;
151     private static org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction gotoExternalInstruction;
152     private HashSet<PolicyPair> visitedPairs = new HashSet<>();
153     private HashSet<PolicyPair> visitedReversePairs = new HashSet<>();
154     private List<Rule> reversedActiveRules = new ArrayList<>();
155     private ListMultimap<EgKey, EgKey> resolvedEpgPairs = ArrayListMultimap.create();
156     private boolean directPathFlowsCreated = false;
157     private boolean reversePathFlowsCreated = false;
158
159     public PolicyEnforcer(OfContext ctx, short tableId) {
160         super(ctx);
161         TABLE_ID = tableId;
162         isReversedPolicy = false;
163         gotoEgressNatInstruction = gotoTableIns(ctx.getPolicyManager().getTABLEID_EGRESS_NAT());
164         gotoExternalInstruction = gotoTableIns(ctx.getPolicyManager().getTABLEID_EXTERNAL_MAPPER());
165     }
166
167     private static org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction getGotoEgressNatInstruction() {
168         return gotoEgressNatInstruction;
169     }
170
171     private static org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction getGotoExternalInstruction() {
172         return gotoExternalInstruction;
173     }
174
175     @Override
176     public short getTableId() {
177         return TABLE_ID;
178     }
179
180     @Override
181     public void sync(NodeId nodeId, OfWriter ofWriter) throws Exception {
182
183         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(1, null, TABLE_ID));
184
185         NodeConnectorId tunPort = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlan.class);
186         if (tunPort != null) {
187             ofWriter.writeFlow(nodeId, TABLE_ID, allowFromTunnel(tunPort));
188         }
189
190         visitedPairs = new HashSet<>();
191         reversedActiveRules = new ArrayList<>();
192         visitedReversePairs = new HashSet<>();
193         resolvedEpgPairs = ArrayListMultimap.create();
194
195         // Used for ARP flows
196         Set<Integer> fdIds = new HashSet<>();
197
198         for (Endpoint sourceEp : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
199             for (EgKey sourceEpgKey : ctx.getEndpointManager().getEgKeysForEndpoint(sourceEp)) {
200                 Set<EgKey> peers = ctx.getCurrentPolicy().getPeers(sourceEpgKey);
201                 for (EgKey destinationEpgKey : peers) {
202
203                     Set<Endpoint> destinationEndpoints = new HashSet<>();
204                     destinationEndpoints.addAll(ctx.getEndpointManager().getEndpointsForGroup(destinationEpgKey));
205                     destinationEndpoints.addAll(ctx.getEndpointManager().getExtEpsNoLocForGroup(destinationEpgKey));
206                     for (Endpoint destinationEp : destinationEndpoints) {
207
208                         EndpointFwdCtxOrdinals srcEpFwdCxtOrdinals = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, sourceEp);
209                         if (srcEpFwdCxtOrdinals == null) {
210                             LOG.debug("Method getEndpointFwdCtxOrdinals returned null for EP {}", sourceEp);
211                             continue;
212                         }
213
214                         EndpointFwdCtxOrdinals dstEpFwdCxtOrdinals = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, destinationEp);
215                         if (dstEpFwdCxtOrdinals == null) {
216                             LOG.debug("Method getEndpointFwdCtxOrdinals returned null for EP {}", destinationEp);
217                             continue;
218                         }
219
220                         fdIds.add(srcEpFwdCxtOrdinals.getFdId());
221                         NetworkElements netElements = new NetworkElements(sourceEp, destinationEp, sourceEpgKey,
222                                 destinationEpgKey, nodeId, ctx);
223
224                         // Get policy in both directions
225                         Policy sourceEpgPolicy = ctx.getCurrentPolicy().getPolicy(destinationEpgKey, sourceEpgKey);
226                         Policy destinationEpgPolicy = ctx.getCurrentPolicy().getPolicy(sourceEpgKey, destinationEpgKey);
227                         reversedActiveRules = getRules(getActiveRulesBetweenEps(destinationEpgPolicy, sourceEp, destinationEp));
228
229                         // Resolve flows in both directions if possible according to policy. Get back status of resolution
230                         PathStatus status = resolveSourceEpgPolicy(ofWriter, netElements, sourceEpgPolicy);
231
232                         // When source Epg policy creates no flows, destination Epg policy has to be resolved
233                         if (status.equals(PathStatus.none)) {
234                             resolveDestinationEpgPolicy(ofWriter, netElements, destinationEpgPolicy, false);
235                         }
236                         // When source Epg policy creates flows only in one direction, the other direction has to be
237                         // created here. Is essential to revert directions to prevent flow overriding and incorrect nsp
238                         // evaluation
239                         else if (status.equals(PathStatus.partial)) {
240                             resolveDestinationEpgPolicy(ofWriter, netElements, destinationEpgPolicy, true);
241                         }
242                     }
243                 }
244             }
245         }
246
247         // Allow same EPG
248         allowSameEpg(nodeId, ofWriter);
249
250         // Write ARP flows per flood domain
251         for (Integer fdId : fdIds) {
252             ofWriter.writeFlow(nodeId, TABLE_ID, createArpFlow(fdId));
253         }
254     }
255
256     private PathStatus resolveSourceEpgPolicy(OfWriter ofWriter, NetworkElements netElements, Policy directPolicy) {
257         isReversedPolicy = false;
258         directPathFlowsCreated = false;
259         reversePathFlowsCreated = false;
260
261         for (Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>> activeRulesByConstraints : getActiveRulesBetweenEps(
262                 directPolicy, netElements.getDstEp(), netElements.getSrcEp())) {
263             Set<IpPrefix> sIpPrefixes = Policy.getIpPrefixesFrom(activeRulesByConstraints.getRowKey()
264                     .getL3EpPrefixes());
265             Set<IpPrefix> dIpPrefixes = Policy.getIpPrefixesFrom(activeRulesByConstraints.getColumnKey()
266                     .getL3EpPrefixes());
267
268             int priority = 65000;
269             for (RuleGroup rg : activeRulesByConstraints.getValue()) {
270                 TenantId tenantId = rg.getContractTenant().getId();
271                 IndexedTenant tenant = ctx.getTenant(tenantId);
272                 for (Rule rule : rg.getRules()) {
273
274                     // Find all rules in the same traffic direction
275                     List<Rule> sameDirectionRules = findRulesInSameDirection(rule, reversedActiveRules);
276                     if (sameDirectionRules.isEmpty()) {
277                         sameDirectionRules.add(rule);
278                     }
279                     sameDirectionRules = Ordering.from(TenantUtils.RULE_COMPARATOR)
280                             .immutableSortedCopy(sameDirectionRules);
281
282                     // Create flows for every pair of rules
283                     for (Rule oppositeRule : sameDirectionRules) {
284
285                         // Evaluate which rule has more specific matches
286                         Rule ruleWithMatches = findRuleWithSpecificMatches(rule, oppositeRule, tenant);
287                         Rule ruleWithActions = mergeRuleActions(rule, oppositeRule, tenant);
288                         if (ruleWithMatches == null) {
289                             LOG.trace("No matches found for pair of rules {}, {}", rule, oppositeRule);
290                             continue;
291                         }
292                         if (ruleWithActions == null) {
293                             LOG.trace("No actions found for pair of rules {}, {}", rule, oppositeRule);
294                             continue;
295                         }
296                         PolicyPair policyPair = null;
297                         if (rule.equals(ruleWithMatches)) {
298                             policyPair = new PolicyPair(netElements.getDstEpOrdinals().getEpgId(),
299                                     netElements.getSrcEpOrdinals().getEpgId(), netElements.getDstEpOrdinals().getCgId(),
300                                     netElements.getSrcEpOrdinals().getCgId(), dIpPrefixes, sIpPrefixes,
301                                     netElements.getDstNodeId(), netElements.getSrcNodeId());
302                         } else {
303                             policyPair = new PolicyPair(netElements.getSrcEpOrdinals().getEpgId(),
304                                     netElements.getDstEpOrdinals().getEpgId(), netElements.getSrcEpOrdinals().getCgId(),
305                                     netElements.getDstEpOrdinals().getCgId(), sIpPrefixes, dIpPrefixes,
306                                     netElements.getSrcNodeId(), netElements.getDstNodeId());
307                         }
308                         LOG.trace("PolicyEnforcer: Visiting PolicyPair {} endpoints {} {}", policyPair,
309                                 netElements.getSrcEp().getKey(), netElements.getDstEp().getKey());
310
311                         // Preserve original rule direction
312                         Set<Direction> directions = getRuleDirections(rule);
313
314                         for(Direction direction : directions) {
315
316                             // Create list of matches/actions. Also creates chain flows when specific action requires it
317                             List<MatchBuilder> inMatches = createMatches(Direction.In, policyPair, tenant,
318                                     ruleWithMatches);
319                             List<MatchBuilder> outMatches = createMatches(Direction.Out, policyPair, tenant,
320                                     ruleWithMatches);
321
322                             List<ActionBuilder> actions = createActions(ofWriter, netElements, direction,
323                                     policyPair, tenant, ruleWithActions, false);
324
325                             // Compose flows
326                             createFlows(inMatches, actions, netElements, ofWriter, priority);
327                             createFlows(outMatches, actions, netElements, ofWriter, priority);
328
329                             priority -= 1;
330
331                             // Keep info about what direction has flows already created
332                             if (direction.equals(Direction.In)) {
333                                 directPathFlowsCreated = true;
334                             }
335                             if (direction.equals(Direction.Out)) {
336                                 reversePathFlowsCreated = true;
337                             }
338
339                             // Fully resolved Ep groups are saved to prevent duplicates
340                             if (directPathFlowsCreated && reversePathFlowsCreated) {
341                                 LOG.trace("Epg pair added: {}, {} ", netElements.getSrcEpg(), netElements.getDstEpg());
342                                 resolvedEpgPairs.put(netElements.getSrcEpg(), netElements.getDstEpg());
343                             }
344                         }
345                     }
346                 }
347             }
348
349         }
350         // Returns appropriate result of resolving
351         if (directPathFlowsCreated && reversePathFlowsCreated) {
352             return PathStatus.both;
353         } else if ((!directPathFlowsCreated && reversePathFlowsCreated) || (directPathFlowsCreated && !reversePathFlowsCreated)) {
354             return PathStatus.partial;
355         } else {
356             return PathStatus.none;
357         }
358     }
359
360     private Set<Direction> getRuleDirections(Rule ruleWithMatches) {
361         Set<Direction> directions = new HashSet<>();
362         for (ClassifierRef classifierRef : ruleWithMatches.getClassifierRef()) {
363             if (!directions.contains(classifierRef.getDirection()) && classifierRef.getDirection() == Direction.In) {
364                 directions.add(classifierRef.getDirection());
365             }
366             if (!directions.contains(classifierRef.getDirection()) && classifierRef.getDirection() == Direction.Out) {
367                 directions.add(classifierRef.getDirection());
368             }
369         }
370         if (directions.isEmpty()) {
371             directions.add(Direction.Bidirectional);
372         }
373         return directions;
374     }
375
376     private Rule mergeRuleActions(Rule rule, Rule oppositeRule, IndexedTenant tenant) {
377         if (oppositeRule.equals(rule)) {
378             return rule;
379         }
380
381         Action ruleAction = null;
382         Action oppositeRuleAction = null;
383
384         // For now, only allow action and chain action is supported
385         for (ActionRef actionRef : rule.getActionRef()) {
386             ActionInstance actionInstance = tenant.getAction(actionRef.getName());
387             if (actionRef.getOrder() == 0 && (actionInstance.getActionDefinitionId().equals(new AllowAction().getId()))) {
388                 ruleAction = new AllowAction();
389             }
390             if (actionRef.getOrder() == 0 && (actionInstance.getActionDefinitionId().equals(new ChainAction().getId()))) {
391                 ruleAction = new ChainAction();
392             }
393         }
394         for (ActionRef actionRef : oppositeRule.getActionRef()) {
395             ActionInstance actionInstance = tenant.getAction(actionRef.getName());
396             if (actionRef.getOrder() == 0 && (actionInstance.getActionDefinitionId().equals(new AllowAction().getId()))) {
397                 oppositeRuleAction = new AllowAction();
398             }
399             if (actionRef.getOrder() == 0 && (actionInstance.getActionDefinitionId().equals(new ChainAction().getId()))) {
400                 oppositeRuleAction = new ChainAction();
401             }
402         }
403
404         if (ruleAction == null && oppositeRuleAction == null) {
405             return null;
406         } else if (ruleAction != null && ruleAction.getClass().equals(AllowAction.class)) {
407             return oppositeRule;
408         } else if (oppositeRuleAction != null && oppositeRuleAction.getClass().equals(AllowAction.class)) {
409             return rule;
410         } else {
411             // TODO both rules have chain action - add support for more different chain actions. This works for now
412             return rule;
413         }
414     }
415
416     private Rule findRuleWithSpecificMatches(Rule rule, Rule oppositeRule, IndexedTenant tenant) {
417         if (oppositeRule.equals(rule)) {
418             return rule;
419         }
420
421         // TODO check all classifierRefs
422         ClassifierRef ruleClassifierRef = rule.getClassifierRef().get(0);
423         ClassifierRef oppositeRuleClassifierRef = oppositeRule.getClassifierRef().get(0);
424
425         ClassifierInstance ruleClassifierInstance = tenant.getClassifier(ruleClassifierRef.getInstanceName());
426         ClassifierInstance oppositeRuleClassifierInstance = tenant.getClassifier(oppositeRuleClassifierRef.getInstanceName());
427
428         if (ruleClassifierInstance == null) {
429             LOG.trace("Classifier instance not found, ClassifierRef name: {} ", ruleClassifierRef.getInstanceName());
430             return null;
431         }
432         if (oppositeRuleClassifierInstance == null) {
433             LOG.trace("Opposite classifier instance not found, ClassifierRef name: {} ", oppositeRuleClassifierRef.getInstanceName());
434             return null;
435         }
436
437         // Check ethertype. Values must be equal
438         for (ParameterValue ruleParameter : ruleClassifierInstance.getParameterValue()) {
439             for (ParameterValue oppositeRuleParameter : oppositeRuleClassifierInstance.getParameterValue()) {
440                 if ((ruleParameter.getName().getValue().equals(EtherTypeClassifierDefinition.ETHERTYPE_PARAM))
441                         && oppositeRuleParameter.getName().getValue().equals(EtherTypeClassifierDefinition.ETHERTYPE_PARAM)) {
442                     if (!ruleParameter.getIntValue().equals(oppositeRuleParameter.getIntValue())) {
443                         LOG.trace("Ethertype values are not equal, rule: {}, opposite rule: {} ", rule, oppositeRule);
444                         return null;
445                     }
446                 }
447             }
448         }
449         // Check proto if exists. Values must be equal or missing
450         ParameterValue ruleProtoParameter = null;
451         ParameterValue oppositeRuleProtoParameter = null;
452         for (ParameterValue ruleParameter : ruleClassifierInstance.getParameterValue()) {
453             if (ruleParameter.getName().getValue().equals(IpProtoClassifierDefinition.PROTO_PARAM)) {
454                 ruleProtoParameter = ruleParameter;
455             }
456         }
457         for (ParameterValue oppositeRuleParameter : oppositeRuleClassifierInstance.getParameterValue()) {
458             if (oppositeRuleParameter.getName().getValue().equals(IpProtoClassifierDefinition.PROTO_PARAM)) {
459                 oppositeRuleProtoParameter = oppositeRuleParameter;
460             }
461         }
462
463         if (ruleProtoParameter == null || ruleProtoParameter.getIntValue() == null) {
464             return oppositeRule;
465         } else if (oppositeRuleProtoParameter == null || oppositeRuleProtoParameter.getIntValue() == null) {
466             return rule;
467         } else if (!ruleProtoParameter.getIntValue().equals(oppositeRuleProtoParameter.getIntValue())) {
468             LOG.trace("Proto parameters are not equal, rule parameters: {}, opposite rule parameters {} ",
469                     ruleProtoParameter, oppositeRuleProtoParameter);
470             return null;
471         }
472
473         // Check ports
474         // TODO add support for port ranges
475         ParameterValue ruleL4Src = null;
476         ParameterValue oppositeRuleL4Src = null;
477         ParameterValue ruleL4Dst = null;
478         ParameterValue oppositeRuleL4Dst = null;
479
480         for (ParameterValue ruleParameter : ruleClassifierInstance.getParameterValue()) {
481             if (ruleParameter.getName().getValue().equals(L4ClassifierDefinition.SRC_PORT_PARAM)) {
482                 ruleL4Src = ruleParameter;
483             }
484             if (ruleParameter.getName().getValue().equals(L4ClassifierDefinition.DST_PORT_PARAM)) {
485                 ruleL4Dst = ruleParameter;
486             }
487         }
488         for (ParameterValue oppositeRuleParameter : oppositeRuleClassifierInstance.getParameterValue()) {
489             if (oppositeRuleParameter.getName().getValue().equals(L4ClassifierDefinition.SRC_PORT_PARAM)) {
490                 oppositeRuleL4Src = oppositeRuleParameter;
491             }
492             if (oppositeRuleParameter.getName().getValue().equals(L4ClassifierDefinition.DST_PORT_PARAM)) {
493                 oppositeRuleL4Dst = oppositeRuleParameter;
494             }
495         }
496
497         if (ruleL4Src == null && ruleL4Dst == null && oppositeRuleL4Src == null && oppositeRuleL4Dst == null) {
498             return rule;
499         }
500
501         // Source rules
502         if (ruleL4Src == null && oppositeRuleL4Src != null) {
503             return oppositeRule;
504         }
505         if (ruleL4Src != null && oppositeRuleL4Src == null) {
506             return rule;
507         }
508         if (ruleL4Src != null && ruleL4Src.getIntValue() != null && oppositeRuleL4Src.getIntValue() != null
509                 && ruleL4Src.equals(oppositeRuleL4Src)) {
510             return rule;
511         }
512         if (ruleL4Src != null && ruleL4Src.getIntValue() != null && oppositeRuleL4Src.getIntValue() != null
513                 && !ruleL4Src.equals(oppositeRuleL4Src)) {
514             return null;
515         }
516
517         // Destination rules
518         if (ruleL4Dst == null && oppositeRuleL4Dst != null) {
519             return oppositeRule;
520         }
521         if (ruleL4Dst != null && oppositeRuleL4Dst == null) {
522             return rule;
523         }
524         if (ruleL4Dst != null && ruleL4Dst.getIntValue() != null && oppositeRuleL4Dst.getIntValue() != null
525                 && ruleL4Dst.equals(oppositeRuleL4Dst)) {
526             return rule;
527         }
528         if (ruleL4Dst != null && ruleL4Dst.getIntValue() != null && oppositeRuleL4Dst.getIntValue() != null
529                 && !ruleL4Dst.equals(oppositeRuleL4Dst)) {
530             return null;
531         }
532
533         return null;
534     }
535
536     private void resolveDestinationEpgPolicy(OfWriter ofWriter, NetworkElements netElements, Policy reversedPolicy,
537                                              boolean isReverted) {
538         isReversedPolicy = true;
539         for (Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>> activeRulesByConstraints : getActiveRulesBetweenEps(
540                 reversedPolicy, netElements.getSrcEp(), netElements.getDstEp())) {
541             Set<IpPrefix> sIpPrefixes = Policy.getIpPrefixesFrom(activeRulesByConstraints.getRowKey()
542                     .getL3EpPrefixes());
543             Set<IpPrefix> dIpPrefixes = Policy.getIpPrefixesFrom(activeRulesByConstraints.getColumnKey()
544                     .getL3EpPrefixes());
545             PolicyPair policyPair = new PolicyPair(netElements.getSrcEpOrdinals().getEpgId(), netElements.getDstEpOrdinals().getEpgId(),
546                     netElements.getSrcEpOrdinals().getCgId(), netElements.getDstEpOrdinals().getCgId(), sIpPrefixes, dIpPrefixes,
547                     netElements.getSrcNodeId(), netElements.getDstNodeId());
548             if (visitedReversePairs.contains(policyPair)) {
549                 LOG.trace(
550                         "PolicyEnforcer: Reverse: Already visited PolicyPair {}, endpoints {} {} skipped",
551                         policyPair, netElements.getSrcEp().getKey(), netElements.getDstEp().getKey());
552                 continue;
553             } else {
554                 LOG.trace("PolicyEnforcer: Reverse: Visiting: PolicyPair {} via endpoints {} {}",
555                         policyPair, netElements.getSrcEp().getKey(), netElements.getDstEp().getKey());
556                 visitedReversePairs.add(policyPair);
557
558             }
559             int priority = 65000;
560             for (RuleGroup rg : activeRulesByConstraints.getValue()) {
561                 TenantId tenantId = rg.getContractTenant().getId();
562                 IndexedTenant tenant = ctx.getTenant(tenantId);
563                 for (Rule rule : rg.getRules()) {
564
565                     Set<Direction> directions = getRuleDirections(rule);
566                     if (directions.isEmpty()) {
567                         continue;
568                     }
569
570                     for(Direction direction : directions) {
571
572                         // When specific direction flows exists, do not create them again
573                         if (direction.equals(Direction.In) && reversePathFlowsCreated) {
574                             continue;
575                         }
576                         if (direction.equals(Direction.Out) && directPathFlowsCreated) {
577                             continue;
578                         }
579
580                         List<MatchBuilder> inMatches = createMatches(Direction.In, policyPair, tenant, rule);
581                         List<MatchBuilder> outMatches = createMatches(Direction.Out, policyPair, tenant, rule);
582
583                         // In case chain action is called here, it has to know that this is reversed policy to set tunnel
584                         // ordinal correctly
585                         List<ActionBuilder> inActions = createActions(ofWriter, netElements, Direction.In, policyPair, tenant,
586                                 rule, isReverted);
587                         List<ActionBuilder> outActions = createActions(ofWriter, netElements, Direction.Out, policyPair, tenant,
588                                 rule, isReverted);
589
590                         createFlows(inMatches, inActions, netElements, ofWriter, priority);
591                         createFlows(outMatches, outActions, netElements, ofWriter, priority);
592
593                         if (direction.equals(Direction.In)) {
594                             reversePathFlowsCreated = true;
595                         }
596                         if (direction.equals(Direction.Out)) {
597                             directPathFlowsCreated = true;
598                         }
599
600                         priority -= 1;
601
602                         if (directPathFlowsCreated && reversePathFlowsCreated) {
603                             resolvedEpgPairs.put(netElements.getSrcEpg(), netElements.getDstEpg());
604                         }
605                     }
606                 }
607             }
608         }
609     }
610
611     private void allowSameEpg(NodeId nodeId, OfWriter ofWriter) throws Exception {
612         for (Endpoint sourceEp : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
613             for (EgKey sourceEpgKey : ctx.getEndpointManager().getEgKeysForEndpoint(sourceEp)) {
614
615                 IndexedTenant tenant = ctx.getTenant(sourceEpgKey.getTenantId());
616                 if (tenant != null) {
617                     EndpointGroup group = tenant.getEndpointGroup(sourceEpgKey.getEgId());
618                     if (group == null) {
619                         LOG.debug("EPG {} does not exit and is used in EP {}", sourceEpgKey, sourceEp.getKey());
620                         continue;
621                     }
622                     IntraGroupPolicy igp = group.getIntraGroupPolicy();
623
624                     if (igp == null || igp.equals(IntraGroupPolicy.Allow)) {
625                         for (Endpoint dstEp : ctx.getEndpointManager().getEndpointsForGroup(sourceEpgKey)) {
626                             EndpointFwdCtxOrdinals srcEpFwdCxtOrdinals =
627                                     OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, sourceEp);
628                             if (srcEpFwdCxtOrdinals == null) {
629                                 LOG.debug("getEndpointFwdCtxOrdinals is null for EP {}", sourceEp);
630                                 continue;
631                             }
632
633                             EndpointFwdCtxOrdinals dstEpFwdCxtOrdinals =
634                                     OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, dstEp);
635                             if (dstEpFwdCxtOrdinals == null) {
636                                 LOG.debug("getEndpointFwdCtxOrdinals is null for EP {}", dstEp);
637                                 continue;
638                             }
639
640                             int destinationEpgId = dstEpFwdCxtOrdinals.getEpgId();
641                             int sourceEpgId = srcEpFwdCxtOrdinals.getEpgId();
642                             ofWriter.writeFlow(nodeId, TABLE_ID, allowSameEpg(sourceEpgId, destinationEpgId));
643                             ofWriter.writeFlow(nodeId, TABLE_ID, allowSameEpg(destinationEpgId, sourceEpgId));
644                         }
645                     }
646                 }
647             }
648         }
649     }
650
651     // Return list of all rules with opposite direction
652     private List<Rule> findRulesInSameDirection(Rule ruleToResolve, List<Rule> reversedRules) {
653         List<Rule> sameDirectionRules = new ArrayList<>();
654         for (Rule ruleToCompare : reversedRules) {
655             for (ClassifierRef classifierRefToCompare : ruleToCompare.getClassifierRef()) {
656                 for (ClassifierRef classifierRefToResolve : ruleToResolve.getClassifierRef()) {
657                     if (isDirectionOpposite(classifierRefToCompare.getDirection(), classifierRefToResolve.getDirection())) {
658                         sameDirectionRules.add(ruleToCompare);
659                     }
660                 }
661             }
662         }
663         return sameDirectionRules;
664     }
665
666     private boolean isDirectionOpposite(Direction one, Direction two) {
667         return ((one.equals(Direction.In) && two.equals(Direction.Out))
668                 || (one.equals(Direction.Out) && two.equals(Direction.In)));
669     }
670
671     private List<Rule> getRules(List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> activeRules) {
672         List<Rule> rules = new ArrayList<>();
673         for (Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>> activeRule : activeRules) {
674             for (RuleGroup ruleGroup : activeRule.getValue()) {
675                 for (Rule rule : ruleGroup.getRules()) {
676                     rules.add(rule);
677                 }
678             }
679         }
680         return rules;
681     }
682
683     private Flow createArpFlow(Integer fdId) {
684
685         Long etherType = FlowUtils.ARP;
686         // L2 Classifier so 20,000 for now
687         Integer priority = 20000;
688
689         MatchBuilder mb = new MatchBuilder().setEthernetMatch(FlowUtils.ethernetMatch(null, null, etherType));
690
691         addNxRegMatch(mb, RegMatch.of(NxmNxReg5.class, Long.valueOf(fdId)));
692
693         Match match = mb.build();
694         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "arp", match);
695         return base().setPriority(priority)
696                 .setId(flowid)
697                 .setMatch(match)
698                 .setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))))
699                 .build();
700     }
701
702     private Flow allowSameEpg(int sourceEpgId, int destinationEpgId) {
703
704         MatchBuilder mb = new MatchBuilder();
705         addNxRegMatch(mb, RegMatch.of(NxmNxReg0.class, (long) sourceEpgId),
706                 RegMatch.of(NxmNxReg2.class, (long) destinationEpgId));
707         Match match = mb.build();
708         FlowId flowId = FlowIdUtils.newFlowId(TABLE_ID, "intraallow", match);
709         FlowBuilder flow = base().setId(flowId)
710                 .setMatch(match)
711                 .setPriority(65000)
712                 .setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
713         return flow.build();
714     }
715
716     private Flow allowFromTunnel(NodeConnectorId tunPort) {
717
718         MatchBuilder mb = new MatchBuilder().setInPort(tunPort);
719         addNxRegMatch(mb, RegMatch.of(NxmNxReg1.class, 0xffffffL));
720         Match match = mb.build();
721         FlowId flowId = FlowIdUtils.newFlowId(TABLE_ID, "tunnelallow", match);
722         FlowBuilder flow = base().setId(flowId)
723                 .setMatch(match)
724                 .setPriority(65000)
725                 .setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
726         return flow.build();
727
728     }
729
730     private List<MatchBuilder> createMatches(Direction direction, PolicyPair policyPair, IndexedTenant contractTenant,
731                                              Rule rule) {
732         Map<String, ParameterValue> paramsFromClassifier = new HashMap<>();
733         Set<ClassifierDefinitionId> classifiers = new HashSet<>();
734         for (ClassifierRef cr : rule.getClassifierRef()) {
735
736             if (cr.getDirection() != null && !cr.getDirection().equals(Direction.Bidirectional)
737                     && !cr.getDirection().equals(direction)) {
738                 continue;
739             }
740
741             // XXX - TODO - implement connection tracking (requires openflow
742             // extension and data plane support - in 2.4. Will need to handle
743             // case where we are working with mix of nodes.
744
745             ClassifierInstance ci = contractTenant.getClassifier(cr.getInstanceName());
746             if (ci == null) {
747                 // XXX TODO fail the match and raise an exception
748                 LOG.warn("Classifier instance {} not found", cr.getInstanceName().getValue());
749                 return null;
750             }
751             Classifier classifier = SubjectFeatures.getClassifier(ci.getClassifierDefinitionId());
752             if (classifier == null) {
753                 // XXX TODO fail the match and raise an exception
754                 LOG.warn("Classifier definition {} not found", ci.getClassifierDefinitionId().getValue());
755                 return null;
756             }
757             classifiers.add(new ClassifierDefinitionId(ci.getClassifierDefinitionId()));
758             for (ParameterValue v : ci.getParameterValue()) {
759                 if (paramsFromClassifier.get(v.getName().getValue()) == null) {
760                     if (v.getIntValue() != null || v.getStringValue() != null || v.getRangeValue() != null) {
761                         paramsFromClassifier.put(v.getName().getValue(), v);
762                     }
763                 } else {
764                     if (!paramsFromClassifier.get(v.getName().getValue()).equals(v)) {
765                         throw new IllegalArgumentException("Classification error in rule: " + rule.getName()
766                                 + ".\nCause: " + "Classification conflict detected at parameter " + v.getName());
767                     }
768                 }
769             }
770         }
771         if (classifiers.isEmpty()) {
772             return null;
773         }
774         List<Map<String, ParameterValue>> derivedParamsByName = ParamDerivator.ETHER_TYPE_DERIVATOR.deriveParameter(paramsFromClassifier);
775         List<MatchBuilder> flowMatchBuilders = new ArrayList<>();
776         for (Map<String, ParameterValue> params : derivedParamsByName) {
777             List<MatchBuilder> matchBuildersToResolve = new ArrayList<>();
778             if (policyPair.consumerEicIpPrefixes.isEmpty() && policyPair.providerEicIpPrefixes.isEmpty()) {
779                 matchBuildersToResolve.add(createBaseMatch(direction, policyPair, null, null));
780             } else if (!policyPair.consumerEicIpPrefixes.isEmpty() && policyPair.providerEicIpPrefixes.isEmpty()) {
781                 for (IpPrefix sIpPrefix : policyPair.consumerEicIpPrefixes) {
782                     matchBuildersToResolve.add(createBaseMatch(direction, policyPair, sIpPrefix, null));
783                 }
784             } else if (policyPair.consumerEicIpPrefixes.isEmpty() && !policyPair.providerEicIpPrefixes.isEmpty()) {
785                 for (IpPrefix dIpPrefix : policyPair.consumerEicIpPrefixes) {
786                     matchBuildersToResolve.add(createBaseMatch(direction, policyPair, null, dIpPrefix));
787                 }
788             } else {
789                 for (IpPrefix sIpPrefix : policyPair.consumerEicIpPrefixes) {
790                     for (IpPrefix dIpPrefix : policyPair.consumerEicIpPrefixes) {
791                         matchBuildersToResolve.add(createBaseMatch(direction, policyPair, sIpPrefix, dIpPrefix));
792                     }
793                 }
794             }
795             for (ClassifierDefinitionId clDefId : classifiers) {
796                 Classifier classifier = SubjectFeatures.getClassifier(clDefId);
797                 ClassificationResult result = classifier.updateMatch(matchBuildersToResolve, params);
798                 if (!result.isSuccessfull()) {
799                     // TODO consider different handling.
800                     throw new IllegalArgumentException("Classification conflict detected in rule: " + rule.getName()
801                             + ".\nCause: " + result.getErrorMessage());
802                 }
803                 matchBuildersToResolve = new ArrayList<>(result.getMatchBuilders());
804             }
805             flowMatchBuilders.addAll(matchBuildersToResolve);
806         }
807         return flowMatchBuilders;
808     }
809
810     private List<ActionBuilder> createActions(OfWriter ofWriter, NetworkElements netElements, Direction direction, PolicyPair policyPair,
811                                               IndexedTenant contractTenant, Rule rule, boolean isReversedDirection) {
812         List<ActionBuilder> actionBuilderList = new ArrayList<>();
813         if (rule.getActionRef() != null) {
814
815             // Pre-sort by references using order, then name
816             List<ActionRef> actionRefList = new ArrayList<>(rule.getActionRef());
817             Collections.sort(actionRefList, ActionRefComparator.INSTANCE);
818
819             for (ActionRef actionRule : actionRefList) {
820                 ActionInstance actionInstance = contractTenant.getAction(actionRule.getName());
821                 if (actionInstance == null) {
822                     // XXX TODO fail the match and raise an exception
823                     LOG.warn("Action instance {} not found", actionRule.getName().getValue());
824                     return null;
825                 }
826                 Action action = SubjectFeatures.getAction(actionInstance.getActionDefinitionId());
827                 if (action == null) {
828                     // XXX TODO fail the match and raise an exception
829                     LOG.warn("Action definition {} not found", actionInstance.getActionDefinitionId().getValue());
830                     return null;
831                 }
832
833                 Map<String, Object> params = new HashMap<>();
834                 if (actionInstance.getParameterValue() != null) {
835                     for (ParameterValue v : actionInstance.getParameterValue()) {
836                         if (v.getName() == null)
837                             continue;
838                         if (v.getIntValue() != null) {
839                             params.put(v.getName().getValue(), v.getIntValue());
840                         } else if (v.getStringValue() != null) {
841                             params.put(v.getName().getValue(), v.getStringValue());
842                         }
843                     }
844                 }
845                 if (isReversedDirection) {
846                     direction = reverse(direction);
847                 }
848
849                 // Convert the GBP Action to one or more OpenFlow Actions
850                 if (!(actionRefList.indexOf(actionRule) == (actionRefList.size() - 1)
851                         && action.equals(SubjectFeatures.getAction(AllowActionDefinition.DEFINITION.getId())))) {
852                     actionBuilderList = action.updateAction(actionBuilderList, params, actionRule.getOrder(), netElements,
853                             policyPair, ofWriter, ctx, direction);
854                 }
855             }
856         }
857
858         return actionBuilderList;
859     }
860
861     private Direction reverse(Direction direction) {
862         if (direction.equals(Direction.In)) {
863             return Direction.Out;
864         }
865         else if(direction.equals(Direction.Out)) {
866             return Direction.In;
867         }
868         else {
869             return Direction.Bidirectional;
870         }
871     }
872
873     private void createFlows(List<MatchBuilder> flowMatchBuilders, List<ActionBuilder> actionBuilderList, NetworkElements netElements,
874                              OfWriter ofWriter, int priority) {
875         FlowBuilder flow = base().setPriority(priority);
876         if(flowMatchBuilders == null) {
877             return;
878         }
879         for (MatchBuilder mb : flowMatchBuilders) {
880             Match match = mb.build();
881             FlowId flowId = FlowIdUtils.newFlowId(TABLE_ID, "cg", match);
882             flow.setMatch(match).setId(flowId).setPriority(priority);
883
884             // If destination is External, the last Action ALLOW must be changed to goto
885             // NAT/External table.
886             // If actionBuilderList is empty (we removed the last Allow) then go straight to
887             // ExternalMapper table.
888
889             List<ExternalImplicitGroup> eigs = ctx.getTenant(netElements.getDstEp().getTenant())
890                 .getTenant()
891                 .getPolicy()
892                 .getExternalImplicitGroup();
893             boolean performNat = false;
894             for (EndpointL3 natEp : ctx.getEndpointManager().getL3EndpointsWithNat()) {
895                 if (natEp.getMacAddress() != null &&
896                     natEp.getL2Context() != null &&
897                     netElements.getSrcEp().getKey().equals(new EndpointKey(natEp.getL2Context(),
898                         natEp.getMacAddress())) &&
899                     EndpointManager.isExternal(netElements.getDstEp(), eigs)) {
900                     performNat = true;
901                     break;
902                 }
903             }
904             if (actionBuilderList == null) {
905                 //TODO - analyse, what happen for unknown action, SFC, etc.
906                 LOG.warn("Action builder list not found, partially flow which is not created: {}", flow.build());
907                 continue;
908             }
909             if (actionBuilderList.isEmpty()) {
910                 flow.setInstructions((performNat == true) ? instructions(gotoEgressNatInstruction) : instructions(gotoExternalInstruction));
911             } else {
912                 flow.setInstructions(instructions(applyActionIns(actionBuilderList),
913                         (performNat == true) ? gotoEgressNatInstruction : gotoExternalInstruction));
914             }
915             ofWriter.writeFlow(netElements.getLocalNodeId(), TABLE_ID, flow.build());
916         }
917     }
918
919     private MatchBuilder createBaseMatch(Direction direction, PolicyPair policyPair, IpPrefix sIpPrefix,
920                                          IpPrefix dIpPrefix) {
921         MatchBuilder baseMatch = new MatchBuilder();
922         if (direction.equals(Direction.In)) {
923             addNxRegMatch(baseMatch, RegMatch.of(NxmNxReg0.class, (long) policyPair.consumerEpgId),
924                     RegMatch.of(NxmNxReg1.class, (long) policyPair.consumerCondGrpId),
925                     RegMatch.of(NxmNxReg2.class, (long) policyPair.providerEpgId),
926                     RegMatch.of(NxmNxReg3.class, (long) policyPair.providerCondGrpId));
927             if (sIpPrefix != null) {
928                 baseMatch.setLayer3Match(createLayer3Match(sIpPrefix, true));
929             }
930             if (dIpPrefix != null) {
931                 baseMatch.setLayer3Match(createLayer3Match(dIpPrefix, true));
932             }
933         } else {
934             addNxRegMatch(baseMatch, RegMatch.of(NxmNxReg0.class, (long) policyPair.providerEpgId),
935                     RegMatch.of(NxmNxReg1.class, (long) policyPair.providerCondGrpId),
936                     RegMatch.of(NxmNxReg2.class, (long) policyPair.consumerEpgId),
937                     RegMatch.of(NxmNxReg3.class, (long) policyPair.consumerCondGrpId));
938             if (sIpPrefix != null) {
939                 baseMatch.setLayer3Match(createLayer3Match(sIpPrefix, false));
940             }
941             if (dIpPrefix != null) {
942                 baseMatch.setLayer3Match(createLayer3Match(dIpPrefix, false));
943             }
944         }
945         return baseMatch;
946     }
947
948     private Layer3Match createLayer3Match(IpPrefix ipPrefix, boolean isSrc) {
949         if (ipPrefix.getIpv4Prefix() != null) {
950             if (isSrc) {
951                 return new Ipv4MatchBuilder().setIpv4Source(ipPrefix.getIpv4Prefix()).build();
952             } else {
953                 return new Ipv4MatchBuilder().setIpv4Destination(ipPrefix.getIpv4Prefix()).build();
954             }
955         } else {
956             if (isSrc) {
957                 return new Ipv6MatchBuilder().setIpv6Source(ipPrefix.getIpv6Prefix()).build();
958             } else {
959                 return new Ipv6MatchBuilder().setIpv6Destination(ipPrefix.getIpv6Prefix()).build();
960             }
961         }
962     }
963
964     // TODO: move to a common utils for all renderers
965     private List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> getActiveRulesBetweenEps(Policy policy,
966                                                                                                          Endpoint consEp, Endpoint provEp) {
967         List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> rulesWithEpConstraints = new ArrayList<>();
968         for (Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>> cell : policy.getRuleMap().cellSet()) {
969             EndpointConstraint consEpConstraint = cell.getRowKey();
970             EndpointConstraint provEpConstraint = cell.getColumnKey();
971             if (epMatchesConstraint(consEp, consEpConstraint) && epMatchesConstraint(provEp, provEpConstraint)) {
972                 rulesWithEpConstraints.add(cell);
973             }
974         }
975         return rulesWithEpConstraints;
976     }
977
978     private boolean epMatchesConstraint(Endpoint ep, EndpointConstraint constraint) {
979         List<ConditionName> epConditions = Collections.emptyList();
980         if (ep.getCondition() != null) {
981             epConditions = ep.getCondition();
982         }
983         return constraint.getConditionSet().matches(epConditions);
984     }
985
986     private enum PathStatus { both, partial, none }
987
988     public static boolean checkPolicyOrientation() {
989         return isReversedPolicy;
990     }
991
992     /**
993      * Private internal class for ordering Actions in Rules. The order is
994      * determined first by the value of the order parameter, with the lower
995      * order actions being applied first; for Actions with either the same order
996      * or no order, ordering is lexicographical by name.
997      */
998     private static class ActionRefComparator implements Comparator<ActionRef> {
999
1000         public static final ActionRefComparator INSTANCE = new ActionRefComparator();
1001
1002         @Override
1003         public int compare(ActionRef arg0, ActionRef arg1) {
1004             return ComparisonChain.start()
1005                     .compare(arg0.getOrder(), arg1.getOrder(), Ordering.natural().nullsLast())
1006                     .compare(arg0.getName().getValue(), arg1.getName().getValue(), Ordering.natural().nullsLast())
1007                     .result();
1008         }
1009
1010     }
1011
1012     @Immutable
1013     public static class PolicyPair {
1014
1015         private final int consumerEpgId;
1016         private final int providerEpgId;
1017         private final int consumerCondGrpId;
1018         private final int providerCondGrpId;
1019         private final Set<IpPrefix> consumerEicIpPrefixes;
1020         private final Set<IpPrefix> providerEicIpPrefixes;
1021         private final NodeId consumerEpNodeId;
1022         private final NodeId providerEpNodeId;
1023
1024         public PolicyPair(int consumerEpgId, int providerEpgId, int consumerCondGrpId, int providerCondGrpId,
1025                           Set<IpPrefix> consumerEicIpPrefixes, Set<IpPrefix> providerEicIpPrefixes, NodeId consumerEpNodeId, NodeId providerEpNodeId) {
1026             super();
1027             this.consumerEpgId = consumerEpgId;
1028             this.providerEpgId = providerEpgId;
1029             this.consumerCondGrpId = consumerCondGrpId;
1030             this.providerCondGrpId = providerCondGrpId;
1031             this.consumerEicIpPrefixes = consumerEicIpPrefixes;
1032             this.providerEicIpPrefixes = providerEicIpPrefixes;
1033             this.consumerEpNodeId = consumerEpNodeId;
1034             this.providerEpNodeId = providerEpNodeId;
1035         }
1036
1037         public int getConsumerEpgId() {
1038             return consumerEpgId;
1039         }
1040
1041         public int getProviderEpgId() {
1042             return providerEpgId;
1043         }
1044
1045         public NodeId getConsumerEpNodeId() {
1046             return consumerEpNodeId;
1047         }
1048
1049         public NodeId getProviderEpNodeId() {
1050             return providerEpNodeId;
1051         }
1052
1053         @Override
1054         public int hashCode() {
1055             final int prime = 31;
1056             int result = 1;
1057             result = prime * result + ((providerEicIpPrefixes == null) ? 0 : providerEicIpPrefixes.hashCode());
1058             result = prime * result + providerCondGrpId;
1059             result = prime * result + providerEpgId;
1060             result = prime * result + ((consumerEicIpPrefixes == null) ? 0 : consumerEicIpPrefixes.hashCode());
1061             result = prime * result + consumerCondGrpId;
1062             result = prime * result + consumerEpgId;
1063             result = prime * result + ((consumerEpNodeId == null) ? 0 : consumerEpNodeId.hashCode());
1064             result = prime * result + ((providerEpNodeId == null) ? 0 : providerEpNodeId.hashCode());
1065
1066             return result;
1067         }
1068
1069         @Override
1070         public boolean equals(Object obj) {
1071             if (this == obj)
1072                 return true;
1073             if (obj == null)
1074                 return false;
1075             if (getClass() != obj.getClass())
1076                 return false;
1077             PolicyPair other = (PolicyPair) obj;
1078             if (providerEicIpPrefixes == null) {
1079                 if (other.providerEicIpPrefixes != null) {
1080                     return false;
1081                 }
1082             } else if (!providerEicIpPrefixes.equals(other.providerEicIpPrefixes)) {
1083                 return false;
1084             }
1085             if (consumerEicIpPrefixes == null) {
1086                 if (other.consumerEicIpPrefixes != null) {
1087                     return false;
1088                 }
1089             } else if (!consumerEicIpPrefixes.equals(other.consumerEicIpPrefixes)) {
1090                 return false;
1091             }
1092             if (consumerEpNodeId == null) {
1093                 if (other.consumerEpNodeId != null) {
1094                     return false;
1095                 }
1096             } else if (!consumerEpNodeId.getValue().equals(other.consumerEpNodeId.getValue())) {
1097                 return false;
1098             }
1099             if (providerEpNodeId == null) {
1100                 if (other.providerEpNodeId != null) {
1101                     return false;
1102                 }
1103             } else if (!providerEpNodeId.getValue().equals(other.providerEpNodeId.getValue())) {
1104                 return false;
1105             }
1106             return (providerCondGrpId == other.providerCondGrpId)
1107                     && (providerEpgId == other.providerEpgId)
1108                     && (consumerCondGrpId == other.consumerCondGrpId)
1109                     && (consumerEpgId == other.consumerEpgId);
1110
1111         }
1112
1113         @Override
1114         public String toString() {
1115             return "consumerEPG: " + consumerEpgId +
1116                     "consumerCG: " + consumerCondGrpId +
1117                     "providerEPG: " + providerEpgId +
1118                     "providerCG: " + providerCondGrpId +
1119                     "consumerEpNodeId: " + consumerEpNodeId +
1120                     "providerEpNodeId: " + providerEpNodeId +
1121                     "consumerEicIpPrefixes: " + consumerEicIpPrefixes +
1122                     "providerEicIpPrefixes: " + providerEicIpPrefixes;
1123         }
1124     }
1125
1126     public class NetworkElements {
1127
1128         private final Endpoint srcEp;
1129         private final Endpoint dstEp;
1130         private final EgKey srcEpg;
1131         private final EgKey dstEpg;
1132         private NodeId srcNodeId;
1133         private NodeId dstNodeId;
1134         private final NodeId localNodeId;
1135         private EndpointFwdCtxOrdinals srcEpOrdinals;
1136         private EndpointFwdCtxOrdinals dstEpOrdinals;
1137
1138         public NetworkElements(Endpoint srcEp, Endpoint dstEp, EgKey srcEpg, EgKey dstEpg, NodeId nodeId, OfContext ctx) throws Exception {
1139             this.srcEp = srcEp;
1140             this.dstEp = dstEp;
1141             this.srcEpg = srcEpg;
1142             this.dstEpg = dstEpg;
1143             this.localNodeId = nodeId;
1144             this.srcEpOrdinals = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, srcEp);
1145             if (this.srcEpOrdinals == null) {
1146                 LOG.debug("getEndpointFwdCtxOrdinals is null for EP {}", srcEp);
1147                 return;
1148             }
1149             this.dstEpOrdinals = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, dstEp);
1150             if (this.dstEpOrdinals == null) {
1151                 LOG.debug("getEndpointFwdCtxOrdinals is null for EP {}", dstEp);
1152                 return;
1153             }
1154             if (dstEp.getAugmentation(OfOverlayContext.class) != null) {
1155                 this.dstNodeId = dstEp.getAugmentation(OfOverlayContext.class).getNodeId();
1156             }
1157             if (srcEp.getAugmentation(OfOverlayContext.class) != null) {
1158                 this.srcNodeId = srcEp.getAugmentation(OfOverlayContext.class).getNodeId();
1159             }
1160         }
1161
1162
1163         public Endpoint getSrcEp() {
1164             return srcEp;
1165         }
1166
1167
1168         public Endpoint getDstEp() {
1169             return dstEp;
1170         }
1171
1172         public EgKey getSrcEpg() {
1173             return srcEpg;
1174         }
1175
1176         public EgKey getDstEpg() {
1177             return dstEpg;
1178         }
1179
1180         public NodeId getSrcNodeId() {
1181             return srcNodeId;
1182         }
1183
1184
1185         public NodeId getDstNodeId() {
1186             return dstNodeId;
1187         }
1188
1189
1190         public NodeId getLocalNodeId() {
1191             return localNodeId;
1192         }
1193
1194
1195         public EndpointFwdCtxOrdinals getSrcEpOrdinals() {
1196             return srcEpOrdinals;
1197         }
1198
1199
1200         public EndpointFwdCtxOrdinals getDstEpOrdinals() {
1201             return dstEpOrdinals;
1202         }
1203
1204
1205     }
1206 }