OF-overlay PolicyEnforcer tests, refactoring
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / mapper / policyenforcer / 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.mapper.policyenforcer;
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.Collection;
19 import java.util.Collections;
20 import java.util.Comparator;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.opendaylight.groupbasedpolicy.api.sf.AllowActionDefinition;
28 import org.opendaylight.groupbasedpolicy.api.sf.ChainActionDefinition;
29 import org.opendaylight.groupbasedpolicy.dto.EgKey;
30 import org.opendaylight.groupbasedpolicy.dto.EndpointConstraint;
31 import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
32 import org.opendaylight.groupbasedpolicy.dto.Policy;
33 import org.opendaylight.groupbasedpolicy.dto.RuleGroup;
34 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
35 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
36 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.endpoint.EndpointManager;
37 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowIdUtils;
38 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowTable;
39 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils;
40 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch;
41 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory;
42 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory.EndpointFwdCtxOrdinals;
43 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Action;
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.cisco.params.xml.ns.yang.sfc.common.rev151017.SfcName;
51 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.go.to.table._case.GoToTable;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.action.refs.ActionRef;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.EndpointGroup;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.EndpointGroup.IntraGroupPolicy;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.ExternalImplicitGroup;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.contract.subject.Rule;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ActionInstance;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.subject.feature.instances.ClassifierInstance;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
89 import org.slf4j.Logger;
90 import org.slf4j.LoggerFactory;
91
92 import com.google.common.base.Preconditions;
93 import com.google.common.collect.ComparisonChain;
94 import com.google.common.collect.Ordering;
95 import com.google.common.collect.Table.Cell;
96
97 /**
98  * <h1>Manage the table that enforces policy on the traffic. Traffic is denied
99  * unless specifically allowed by policy (table=4)</h1>
100  * In policy enforcer, according to current {@link Policy} specific traffic is sent to SFC (nsp and
101  * nsi is set), or from SFC
102  * to some {@link Endpoint} or to another classifier.
103  * <p>
104  * <i>Tunnel/overlay flows</i><br>
105  * Priority = 65000 (if more flows, decrements)<br>
106  * Matches:<br>
107  * - ethertype (tcp, tcp6, ipv6, icmp or missing)<br>
108  * - Reg0 {@link NxmNxReg0}<br>
109  * - Reg1 {@link NxmNxReg1}<br>
110  * - Reg2 {@link NxmNxReg2}<br>
111  * - Reg3 {@link NxmNxReg3}<br>
112  * - L3 for src_ip_prefix (if exists)<br>
113  * - L3 for dst_ip_prefix (if exists)<br>
114  * Actions:<br>
115  * - set nsi (only chain action)<br>
116  * - set nsp (only chain action)<br>
117  * - {@link GoToTable} EXTERNAL MAPPER table<br>
118  * <p>
119  * <i>Allow from tunnel flow</i><br>
120  * Priority = 65000<br>
121  * Matches:<br>
122  * - Reg1 (set to 0xffffff) {@link NxmNxReg1}<br>
123  * - in_port (should be tunnel port) {@link NodeConnectorId}<br>
124  * Actions:<br>
125  * - output:port (Reg7) {@link NxmNxReg7}<br>
126  * <p>
127  * Traffic is sent from one {@link EndpointGroup} to the same EPG
128  * <p>
129  * <i>Allow from same EPG flow</i><br>
130  * Priority = 65000<br>
131  * Matches:<br>
132  * - Reg0 {@link NxmNxReg0}<br>
133  * - Reg2 {@link NxmNxReg2}<br>
134  * Actions:<br>
135  * - output:port (Reg7) {@link NxmNxReg7}
136  * <p>
137  * <i>Arp flow</i><br>
138  * Priority = 20000<br>
139  * Matches:<br>
140  * - ethernet match (arp)<br>
141  * - Reg5 {@link NxmNxReg5}<br>
142  * Actions:<br>
143  * - output:port (Reg7) {@link NxmNxReg7}
144  */
145 public class PolicyEnforcer extends FlowTable {
146
147     private static final Logger LOG = LoggerFactory.getLogger(PolicyEnforcer.class);
148     private static short TABLE_ID;
149     private static Instruction gotoEgressNatInstruction;
150     private static Instruction gotoExternalInstruction;
151
152     public PolicyEnforcer(OfContext ctx, short tableId) {
153         super(ctx);
154         TABLE_ID = tableId;
155         gotoEgressNatInstruction = gotoTableIns(ctx.getPolicyManager().getTABLEID_EGRESS_NAT());
156         gotoExternalInstruction = gotoTableIns(ctx.getPolicyManager().getTABLEID_EXTERNAL_MAPPER());
157     }
158
159     @Override
160     public short getTableId() {
161         return TABLE_ID;
162     }
163
164     @Override
165     public void sync(Endpoint endpoint, OfWriter ofWriter) throws Exception {
166         Preconditions.checkNotNull(endpoint);
167         Preconditions.checkNotNull(ofWriter);
168
169         NodeId nodeId = ctx.getEndpointManager().getEndpointNodeId(endpoint);
170
171         ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(1, null, TABLE_ID));
172
173         NodeConnectorId tunPort = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlan.class);
174         if (tunPort != null) {
175             ofWriter.writeFlow(nodeId, TABLE_ID, allowFromTunnel(tunPort));
176         }
177         EndpointFwdCtxOrdinals srcEpFwdCxtOrdinals =
178                 OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, endpoint);
179         if (srcEpFwdCxtOrdinals == null) {
180             LOG.debug("Method getEndpointFwdCtxOrdinals returned null for EP {}", endpoint);
181             return;
182         }
183         for (EgKey sourceEpg : ctx.getEndpointManager().getEgKeysForEndpoint(endpoint)) {
184             for (EgKey destEpg : ctx.getCurrentPolicy().getPeers(sourceEpg)) {
185                 Collection<Endpoint> destinationEndpoints = getEndpointsForGroup(destEpg);
186                 for (Endpoint destinationEndpoint : destinationEndpoints) {
187                     EndpointFwdCtxOrdinals dstEpFwdCxtOrdinals =
188                             OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, destinationEndpoint);
189                     if (dstEpFwdCxtOrdinals == null) {
190                         LOG.debug("Method getEndpointFwdCtxOrdinals returned null for EP {}", destinationEndpoint);
191                         continue;
192                     }
193
194                     NetworkElements netElements =
195                             new NetworkElements(endpoint, destinationEndpoint, sourceEpg, destEpg, nodeId, ctx);
196
197                     // Get policy in both directions
198                     Policy sourceEpgPolicy = ctx.getCurrentPolicy().getPolicy(destEpg, sourceEpg);
199                     Policy destinationEpgPolicy = ctx.getCurrentPolicy().getPolicy(sourceEpg, destEpg);
200
201                     // Resolve flows in both directions if possible according to policy. Get back
202                     // status
203                     // of resolution
204                     resolveSourceEpgPolicy(ofWriter, netElements, sourceEpgPolicy, destinationEpgPolicy);
205
206                     ofWriter.writeFlow(nodeId, TABLE_ID, createArpFlow(srcEpFwdCxtOrdinals.getFdId()));
207                 }
208             }
209             // Allow same EPG
210             allowSameEpg(sourceEpg, endpoint, nodeId, ofWriter);
211         }
212     }
213
214     private Set<Endpoint> getEndpointsForGroup(EgKey epg) {
215         Set<Endpoint> destinationEndpoints = new HashSet<>();
216         destinationEndpoints.addAll(ctx.getEndpointManager().getEndpointsForGroup(epg));
217         destinationEndpoints.addAll(ctx.getEndpointManager().getExtEpsNoLocForGroup(epg));
218         return destinationEndpoints;
219     }
220
221     private void resolveSourceEpgPolicy(OfWriter ofWriter, NetworkElements netElements, Policy directPolicy,
222             Policy reversedPolicy) {
223
224         List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> providedRules =
225                 getActiveRulesBetweenEps(directPolicy, netElements.getDstEp(), netElements.getSrcEp());
226         List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> consumedRules =
227                 getActiveRulesBetweenEps(reversedPolicy, netElements.getSrcEp(), netElements.getDstEp());
228         List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> activeRules;
229         if (!providedRules.isEmpty()) {
230             activeRules = providedRules;
231         } else {
232             activeRules = consumedRules;
233         }
234         int priority = 65000;
235         for (Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>> activeRulesByConstraints : activeRules) {
236             Set<IpPrefix> sIpPrefixes;
237             Set<IpPrefix> dIpPrefixes;
238             if (providedRules.contains(activeRulesByConstraints)) {
239                 sIpPrefixes = Policy.getIpPrefixesFrom(activeRulesByConstraints.getRowKey().getL3EpPrefixes());
240                 dIpPrefixes = Policy.getIpPrefixesFrom(activeRulesByConstraints.getColumnKey().getL3EpPrefixes());
241             } else {
242                 sIpPrefixes = Policy.getIpPrefixesFrom(activeRulesByConstraints.getColumnKey().getL3EpPrefixes());
243                 dIpPrefixes = Policy.getIpPrefixesFrom(activeRulesByConstraints.getRowKey().getL3EpPrefixes());
244             }
245             for (RuleGroup rg : Ordering.from(new RuleGroupComparator())
246                 .immutableSortedCopy(activeRulesByConstraints.getValue())) {
247                 TenantId tenantId = rg.getContractTenant().getId();
248                 IndexedTenant tenant = ctx.getTenant(tenantId);
249                 for (Direction direction : new Direction[] {Direction.In, Direction.Out}) {
250                     List<Rule> sameDirectionRules;
251                     if (providedRules.contains(activeRulesByConstraints)) {
252                         sameDirectionRules = uniteRulesByDirection(direction, rg.getRules(), getRules(consumedRules));
253                     } else {
254                         sameDirectionRules = uniteRulesByDirection(direction, null, rg.getRules());
255                     }
256                     for (Rule rule : Ordering.from(TenantUtils.RULE_COMPARATOR)
257                         .immutableSortedCopy(sameDirectionRules)) {
258                         createFlowsForRule(rule, getRules(providedRules), getRules(consumedRules), direction,
259                                 netElements, ofWriter, tenant, sIpPrefixes, dIpPrefixes, priority);
260                         priority--;
261                     }
262                 }
263             }
264         }
265     }
266
267
268     private List<Rule> uniteRulesByDirection (Direction direction, List<Rule> directRules, List<Rule> reversedRules) {
269         List<Rule> sameDirectionRules = findRulesInDirection(direction, directRules);
270         sameDirectionRules
271             .addAll(findRulesInDirection(reverse(direction), reversedRules));
272         return sameDirectionRules;
273     }
274
275     private void createFlowsForRule(Rule rule, List<Rule> providedRules, List<Rule> consumedRules,
276             Direction direction, NetworkElements netElements,OfWriter ofWriter, IndexedTenant tenant, Set<IpPrefix> sIpPrefixes,
277             Set<IpPrefix> dIpPrefixes, int priority) {
278         List<Rule> reverseProvidedRules = findRulesInDirection(reverse(direction), providedRules);
279         List<String> resolvedSymmetricChains =
280                 resolveSymetricChainActions(direction, rule, tenant, reverseProvidedRules, consumedRules);
281         if (resolvedSymmetricChains == null) {
282             LOG.debug("Rule {} skipped. Reason: asymmetric use of symmetric chain", rule);
283             return;
284         }
285         // Create list of matches/actions. Also creates chain flows when
286         // specific action requires it
287         List<MatchBuilder> matches = null;
288         if (consumedRules.contains(rule)) {
289             matches = createMatches(direction, reverse(direction), netElements, tenant, rule,
290                     sIpPrefixes, dIpPrefixes);
291         } else {
292             matches = createMatches(direction, direction, netElements, tenant, rule, sIpPrefixes,
293                     dIpPrefixes);
294         }
295         List<ActionBuilder> actions = createActions(ofWriter, netElements, direction, tenant, rule,
296                 resolvedSymmetricChains);
297         if (actions == null) {
298             return;
299         }
300
301         // Compose flows
302         createFlows(matches, actions, netElements, ofWriter, priority);
303     }
304
305     private List<String> resolveSymetricChainActions(Direction direction, Rule rule, IndexedTenant tenant,
306             List<Rule> reversedProvidedRules, List<Rule> consumedRules) {
307         List<String> chainNames = new ArrayList<>();
308         if (rule.getActionRef() != null) {
309
310             for (ActionRef actionRef : rule.getActionRef()) {
311                 ActionInstance actionInstance = tenant.getAction(actionRef.getName());
312                 if (actionInstance == null) {
313                     continue;
314                 }
315                 Action action = SubjectFeatures.getAction(actionInstance.getActionDefinitionId());
316                 if (action == null) {
317                     continue;
318                 }
319                 if (action instanceof ChainAction) {
320                     chainNames = getSymetricChainNames(actionInstance);
321                     if (chainNames.isEmpty()) {
322                         continue;
323                     }
324                     List<Rule> reversedRules = findRulesInDirection(reverse(direction), reversedProvidedRules);
325                     reversedRules.addAll(findRulesInDirection(direction, consumedRules));
326
327                     List<String> oppositeChainNames = new ArrayList<>();
328                     for (Rule oppositeRule : reversedRules) {
329                         if (oppositeRule.getActionRef() == null) {
330                             continue;
331                         }
332                         for (ActionRef oppositeActionRef : oppositeRule.getActionRef()) {
333                             ActionInstance oppositeActionInstance = tenant.getAction(oppositeActionRef.getName());
334                             if (oppositeActionInstance == null) {
335                                 continue;
336                             }
337                             Action oppositeAction = SubjectFeatures.getAction(oppositeActionInstance.getActionDefinitionId());
338                             if (oppositeAction == null) {
339                                 continue;
340                             }
341                             if (oppositeAction instanceof ChainAction) {
342                                 oppositeChainNames.addAll(getSymetricChainNames(oppositeActionInstance));
343
344                             }
345                         }
346                     }
347                     if (!oppositeChainNames.containsAll(chainNames)) {
348                         return null;
349                     }
350                     if ((consumedRules.contains(rule) && (direction.equals(Direction.Out)))
351                             || ((!consumedRules.contains(rule)) && direction.equals(Direction.In))) {
352                         return new ArrayList<>();
353                     }
354                 }
355             }
356         }
357         return chainNames;
358     }
359
360     private List<String> getSymetricChainNames(ActionInstance action) {
361         List<String> chainNames = new ArrayList<>();
362         for (ParameterValue param : action.getParameterValue()) {
363             if (param.getStringValue() != null
364                     && param.getName().getValue().equals(ChainActionDefinition.SFC_CHAIN_NAME)) {
365                 String chainName = param.getStringValue();
366                 ServiceFunctionPath sfcPath = ChainAction.getSfcPath(new SfcName(chainName));
367                 if (sfcPath == null || sfcPath.getName() == null) {
368                     continue;
369                 }
370                 if (sfcPath.isSymmetric()) {
371                     chainNames.add(param.getStringValue());
372                 }
373             }
374         }
375         return chainNames;
376     }
377
378     private void allowSameEpg(EgKey epgKey, Endpoint sourceEp, NodeId nodeId, OfWriter ofWriter) throws Exception {
379
380         IndexedTenant tenant = ctx.getTenant(epgKey.getTenantId());
381         if (tenant != null) {
382             EndpointGroup group = tenant.getEndpointGroup(epgKey.getEgId());
383             if (group == null) {
384                 LOG.debug("EPG {} does not exit and is used ", epgKey);
385                 return;
386             }
387             IntraGroupPolicy igp = group.getIntraGroupPolicy();
388
389             if (igp == null || igp.equals(IntraGroupPolicy.Allow)) {
390                 EndpointFwdCtxOrdinals srcEpFwdCxtOrdinals = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, sourceEp);
391                 if (srcEpFwdCxtOrdinals == null) {
392                     LOG.debug("getEndpointFwdCtxOrdinals is null for EP {}", sourceEp);
393                 }
394                 int epgId = srcEpFwdCxtOrdinals.getEpgId();
395                 ofWriter.writeFlow(nodeId, TABLE_ID, allowSameEpg(epgId));
396             }
397         }
398     }
399
400     // Return list of all rules with opposite direction
401     private List<Rule> findRulesInDirection(Direction direction, List<Rule> rules) {
402         List<Rule> sameDirectionRules = new ArrayList<>();
403         if (rules != null) {
404             for (Rule ruleToCompare : rules) {
405                 if (isSameDirection(direction, ruleToCompare)) {
406                     sameDirectionRules.add(ruleToCompare);
407                 }
408             }
409         }
410         return sameDirectionRules;
411     }
412
413     private boolean isSameDirection(Direction direction, Rule rule) {
414         for (ClassifierRef classifier : rule.getClassifierRef()) {
415             if (direction.equals(classifier.getDirection()) || direction.equals(Direction.Bidirectional)
416                     || Direction.Bidirectional.equals(classifier.getDirection())) {
417                 return true;
418             }
419         }
420         return false;
421     }
422
423     private List<Rule> getRules(List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> activeRules) {
424         List<Rule> rules = new ArrayList<>();
425         for (Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>> activeRule : activeRules) {
426             for (RuleGroup ruleGroup : activeRule.getValue()) {
427                 for (Rule rule : ruleGroup.getRules()) {
428                     rules.add(rule);
429                 }
430             }
431         }
432         return rules;
433     }
434
435     private Flow createArpFlow(Integer fdId) {
436
437         Long etherType = FlowUtils.ARP;
438         // L2 Classifier so 20,000 for now
439         Integer priority = 20000;
440
441         MatchBuilder mb = new MatchBuilder().setEthernetMatch(FlowUtils.ethernetMatch(null, null, etherType));
442
443         addNxRegMatch(mb, RegMatch.of(NxmNxReg5.class, Long.valueOf(fdId)));
444
445         Match match = mb.build();
446         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "arp", match);
447         return base().setPriority(priority)
448             .setId(flowid)
449             .setMatch(match)
450             .setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))))
451             .build();
452     }
453
454     private Flow allowSameEpg(int epgId) {
455
456         MatchBuilder mb = new MatchBuilder();
457         addNxRegMatch(mb, RegMatch.of(NxmNxReg0.class, (long) epgId), RegMatch.of(NxmNxReg2.class, (long) epgId));
458         Match match = mb.build();
459         FlowId flowId = FlowIdUtils.newFlowId(TABLE_ID, "intraallow", match);
460         FlowBuilder flow = base().setId(flowId).setMatch(match).setPriority(65000).setInstructions(
461                 instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
462         return flow.build();
463     }
464
465     private Flow allowFromTunnel(NodeConnectorId tunPort) {
466
467         MatchBuilder mb = new MatchBuilder().setInPort(tunPort);
468         addNxRegMatch(mb, RegMatch.of(NxmNxReg1.class, 0xffffffL));
469         Match match = mb.build();
470         FlowId flowId = FlowIdUtils.newFlowId(TABLE_ID, "tunnelallow", match);
471         FlowBuilder flow = base().setId(flowId).setMatch(match).setPriority(65000).setInstructions(
472                 instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
473         return flow.build();
474
475     }
476
477     private List<MatchBuilder> createMatches(Direction flowDirection, Direction classifierDirection,
478             NetworkElements netElements, IndexedTenant contractTenant, Rule rule, Set<IpPrefix> sIpPrefixes,
479             Set<IpPrefix> dIpPrefixes) {
480         Map<String, ParameterValue> paramsFromClassifier = new HashMap<>();
481         Set<ClassifierDefinitionId> classifiers = new HashSet<>();
482         for (ClassifierRef cr : rule.getClassifierRef()) {
483
484             if (cr.getDirection() != null && !cr.getDirection().equals(Direction.Bidirectional)
485                     && !cr.getDirection().equals(classifierDirection)) {
486                 continue;
487             }
488
489             // XXX - TODO - implement connection tracking (requires openflow
490             // extension and data plane support - in 2.4. Will need to handle
491             // case where we are working with mix of nodes.
492
493             ClassifierInstance ci = contractTenant.getClassifier(cr.getInstanceName());
494             if (ci == null) {
495                 // XXX TODO fail the match and raise an exception
496                 LOG.warn("Classifier instance {} not found", cr.getInstanceName().getValue());
497                 return null;
498             }
499             Classifier classifier = SubjectFeatures.getClassifier(ci.getClassifierDefinitionId());
500             if (classifier == null) {
501                 // XXX TODO fail the match and raise an exception
502                 LOG.warn("Classifier definition {} not found", ci.getClassifierDefinitionId().getValue());
503                 return null;
504             }
505             classifiers.add(new ClassifierDefinitionId(ci.getClassifierDefinitionId()));
506             for (ParameterValue v : ci.getParameterValue()) {
507                 if (paramsFromClassifier.get(v.getName().getValue()) == null) {
508                     if (v.getIntValue() != null || v.getStringValue() != null || v.getRangeValue() != null) {
509                         paramsFromClassifier.put(v.getName().getValue(), v);
510                     }
511                 } else {
512                     if (!paramsFromClassifier.get(v.getName().getValue()).equals(v)) {
513                         throw new IllegalArgumentException("Classification error in rule: " + rule.getName()
514                                 + ".\nCause: " + "Classification conflict detected at parameter " + v.getName());
515                     }
516                 }
517             }
518         }
519         if (classifiers.isEmpty()) {
520             return null;
521         }
522         List<Map<String, ParameterValue>> derivedParamsByName =
523                 ParamDerivator.ETHER_TYPE_DERIVATOR.deriveParameter(paramsFromClassifier);
524         List<MatchBuilder> flowMatchBuilders = new ArrayList<>();
525         for (Map<String, ParameterValue> params : derivedParamsByName) {
526             List<MatchBuilder> matchBuildersToResolve = new ArrayList<>();
527             if (sIpPrefixes.isEmpty() && dIpPrefixes.isEmpty()) {
528                 matchBuildersToResolve.add(createBaseMatch(flowDirection, netElements, null, null));
529             } else if (!sIpPrefixes.isEmpty() && dIpPrefixes.isEmpty()) {
530                 for (IpPrefix sIpPrefix : sIpPrefixes) {
531                     matchBuildersToResolve.add(createBaseMatch(flowDirection, netElements, sIpPrefix, null));
532                 }
533             } else if (sIpPrefixes.isEmpty() && !dIpPrefixes.isEmpty()) {
534                 for (IpPrefix dIpPrefix : dIpPrefixes) {
535                     matchBuildersToResolve.add(createBaseMatch(flowDirection, netElements, null, dIpPrefix));
536                 }
537             } else {
538                 for (IpPrefix sIpPrefix : sIpPrefixes) {
539                     for (IpPrefix dIpPrefix : dIpPrefixes) {
540                         matchBuildersToResolve.add(createBaseMatch(flowDirection, netElements, sIpPrefix, dIpPrefix));
541                     }
542                 }
543             }
544             for (ClassifierDefinitionId clDefId : classifiers) {
545                 Classifier classifier = SubjectFeatures.getClassifier(clDefId);
546                 ClassificationResult result = classifier.updateMatch(matchBuildersToResolve, params);
547                 if (!result.isSuccessfull()) {
548                     // TODO consider different handling.
549                     throw new IllegalArgumentException("Classification conflict detected in rule: " + rule.getName()
550                             + ".\nCause: " + result.getErrorMessage());
551                 }
552                 matchBuildersToResolve = new ArrayList<>(result.getMatchBuilders());
553             }
554             flowMatchBuilders.addAll(matchBuildersToResolve);
555         }
556         return flowMatchBuilders;
557     }
558
559     private List<ActionBuilder> createActions(OfWriter ofWriter, NetworkElements netElements, Direction direction,
560             IndexedTenant contractTenant, Rule rule, List<String> resolvedSymmetricChains) {
561         List<ActionBuilder> actionBuilderList = new ArrayList<>();
562         if (rule.getActionRef() != null) {
563
564             // Pre-sort by references using order, then name
565             List<ActionRef> actionRefList = new ArrayList<>(rule.getActionRef());
566             Collections.sort(actionRefList, ActionRefComparator.INSTANCE);
567
568             for (ActionRef actionRef : actionRefList) {
569                 ActionInstance actionInstance = contractTenant.getAction(actionRef.getName());
570                 if (actionInstance == null) {
571                     // XXX TODO fail the match and raise an exception
572                     LOG.warn("Action instance {} not found", actionRef.getName().getValue());
573                     return null;
574                 }
575                 Action action = SubjectFeatures.getAction(actionInstance.getActionDefinitionId());
576                 if (action == null) {
577                     // XXX TODO fail the match and raise an exception
578                     LOG.warn("Action definition {} not found", actionInstance.getActionDefinitionId().getValue());
579                     return null;
580                 }
581
582                 Map<String, Object> params = new HashMap<>();
583                 if (actionInstance.getParameterValue() != null) {
584                     for (ParameterValue v : actionInstance.getParameterValue()) {
585                         if (v.getName() == null)
586                             continue;
587                         if (v.getIntValue() != null) {
588                             params.put(v.getName().getValue(), v.getIntValue());
589                         } else if (v.getStringValue() != null) {
590                             params.put(v.getName().getValue(), v.getStringValue());
591                         }
592                     }
593                 }
594                 if (action instanceof ChainAction) {
595                     ((ChainAction) action).setResolvedSymmetricChains(resolvedSymmetricChains);
596                 }
597
598                 // Convert the GBP Action to one or more OpenFlow Actions
599                 if ((!(actionRefList.indexOf(actionRef) == (actionRefList.size() - 1)
600                         && action.equals(SubjectFeatures.getAction(AllowActionDefinition.DEFINITION.getId()))))
601                         && actionBuilderList != null) {
602                     if (ctx.getDataBroker() != null) {
603                         actionBuilderList =
604                             action.updateAction(actionBuilderList, params, actionRef.getOrder(), netElements, ofWriter, ctx, direction);
605                     } else {
606                         LOG.error("DataBroket is null. Cannot update action {}",
607                             action.getActionDef().getName().getValue());
608                         return null;
609                     }
610                 }
611             }
612         }
613
614         return actionBuilderList;
615     }
616
617     public static Direction reverse(Direction direction) {
618         if (direction.equals(Direction.In)) {
619             return Direction.Out;
620         } else if (direction.equals(Direction.Out)) {
621             return Direction.In;
622         } else {
623             return Direction.Bidirectional;
624         }
625     }
626
627     private void createFlows(List<MatchBuilder> flowMatchBuilders, List<ActionBuilder> actionBuilderList,
628             NetworkElements netElements, OfWriter ofWriter, int priority) {
629         FlowBuilder flow = base().setPriority(priority);
630         if (flowMatchBuilders == null) {
631             return;
632         }
633         for (MatchBuilder mb : flowMatchBuilders) {
634             Match match = mb.build();
635             FlowId flowId = FlowIdUtils.newFlowId(TABLE_ID, "cg", match);
636             flow.setMatch(match).setId(flowId).setPriority(priority);
637
638             // If destination is External, the last Action ALLOW must be changed to goto
639             // NAT/External table.
640             // If actionBuilderList is empty (we removed the last Allow) then go straight to
641             // ExternalMapper table.
642
643             List<ExternalImplicitGroup> eigs = ctx.getTenant(netElements.getDstEp().getTenant())
644                 .getTenant()
645                 .getPolicy()
646                 .getExternalImplicitGroup();
647             boolean performNat = false;
648             for (EndpointL3 natEp : ctx.getEndpointManager().getL3EndpointsWithNat()) {
649                 if (natEp.getMacAddress() != null && natEp.getL2Context() != null
650                         && netElements.getSrcEp()
651                             .getKey()
652                             .equals(new EndpointKey(natEp.getL2Context(), natEp.getMacAddress()))
653                         && EndpointManager.isExternal(netElements.getDstEp(), eigs)) {
654                     performNat = true;
655                     break;
656                 }
657             }
658             if (actionBuilderList == null) {
659                 // flow with this match should not appear on switch (e.g. chain action IN)
660                 // //TODO - analyse, what happen for unknown action, SFC, etc.
661                 continue;
662             }
663             if (actionBuilderList.isEmpty()) {
664                 flow.setInstructions((performNat == true) ? instructions(gotoEgressNatInstruction) : instructions(
665                         gotoExternalInstruction));
666             } else {
667                 flow.setInstructions(instructions(applyActionIns(actionBuilderList),
668                         (performNat == true) ? gotoEgressNatInstruction : gotoExternalInstruction));
669             }
670             ofWriter.writeFlow(netElements.getLocalNodeId(), TABLE_ID, flow.build());
671         }
672     }
673
674     private MatchBuilder createBaseMatch(Direction direction, NetworkElements netElements, IpPrefix sIpPrefix,
675             IpPrefix dIpPrefix) {
676         MatchBuilder baseMatch = new MatchBuilder();
677         if (direction.equals(Direction.In)) {
678             addNxRegMatch(baseMatch, RegMatch.of(NxmNxReg0.class, (long) netElements.getDstEpOrdinals().getEpgId()),
679                     RegMatch.of(NxmNxReg1.class, (long) netElements.getDstEpOrdinals().getCgId()),
680                     RegMatch.of(NxmNxReg2.class, (long) netElements.getSrcEpOrdinals().getEpgId()),
681                     RegMatch.of(NxmNxReg3.class, (long) netElements.getSrcEpOrdinals().getCgId()));
682             if (sIpPrefix != null) {
683                 baseMatch.setLayer3Match(createLayer3Match(sIpPrefix, true));
684             }
685             if (dIpPrefix != null) {
686                 baseMatch.setLayer3Match(createLayer3Match(dIpPrefix, false));
687             }
688         } else {
689             addNxRegMatch(baseMatch, RegMatch.of(NxmNxReg0.class, (long) netElements.getSrcEpOrdinals().getEpgId()),
690                     RegMatch.of(NxmNxReg1.class, (long) netElements.getSrcEpOrdinals().getCgId()),
691                     RegMatch.of(NxmNxReg2.class, (long) netElements.getDstEpOrdinals().getEpgId()),
692                     RegMatch.of(NxmNxReg3.class, (long) netElements.getDstEpOrdinals().getCgId()));
693             if (sIpPrefix != null) {
694                 baseMatch.setLayer3Match(createLayer3Match(sIpPrefix, false));
695             }
696             if (dIpPrefix != null) {
697                 baseMatch.setLayer3Match(createLayer3Match(dIpPrefix, true));
698             }
699         }
700         return baseMatch;
701     }
702
703     private Layer3Match createLayer3Match(IpPrefix ipPrefix, boolean isSrc) {
704         if (ipPrefix.getIpv4Prefix() != null) {
705             if (isSrc) {
706                 return new Ipv4MatchBuilder().setIpv4Source(ipPrefix.getIpv4Prefix()).build();
707             } else {
708                 return new Ipv4MatchBuilder().setIpv4Destination(ipPrefix.getIpv4Prefix()).build();
709             }
710         } else {
711             if (isSrc) {
712                 return new Ipv6MatchBuilder().setIpv6Source(ipPrefix.getIpv6Prefix()).build();
713             } else {
714                 return new Ipv6MatchBuilder().setIpv6Destination(ipPrefix.getIpv6Prefix()).build();
715             }
716         }
717     }
718
719     // TODO: move to a common utils for all renderers
720     private List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> getActiveRulesBetweenEps(Policy policy,
721             Endpoint consEp, Endpoint provEp) {
722         List<Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>>> rulesWithEpConstraints = new ArrayList<>();
723         for (Cell<EndpointConstraint, EndpointConstraint, List<RuleGroup>> cell : policy.getRuleMap().cellSet()) {
724             EndpointConstraint consEpConstraint = cell.getRowKey();
725             EndpointConstraint provEpConstraint = cell.getColumnKey();
726             if (epMatchesConstraint(consEp, consEpConstraint) && epMatchesConstraint(provEp, provEpConstraint)) {
727                 rulesWithEpConstraints.add(cell);
728             }
729         }
730         return rulesWithEpConstraints;
731     }
732
733     private boolean epMatchesConstraint(Endpoint ep, EndpointConstraint constraint) {
734         List<ConditionName> epConditions = Collections.emptyList();
735         if (ep.getCondition() != null) {
736             epConditions = ep.getCondition();
737         }
738         return constraint.getConditionSet().matches(epConditions);
739     }
740
741     /**
742      * Private internal class for ordering Actions in Rules. The order is
743      * determined first by the value of the order parameter, with the lower
744      * order actions being applied first; for Actions with either the same order
745      * or no order, ordering is lexicographical by name.
746      */
747     private static class ActionRefComparator implements Comparator<ActionRef> {
748
749         public static final ActionRefComparator INSTANCE = new ActionRefComparator();
750
751         @Override
752         public int compare(ActionRef arg0, ActionRef arg1) {
753             return ComparisonChain.start()
754                 .compare(arg0.getOrder(), arg1.getOrder(), Ordering.natural().nullsLast())
755                 .compare(arg0.getName().getValue(), arg1.getName().getValue(), Ordering.natural().nullsLast())
756                 .result();
757         }
758
759     }
760
761     private static class RuleGroupComparator implements Comparator<RuleGroup> {
762
763         @Override
764         public int compare(RuleGroup arg0, RuleGroup arg1) {
765             return ComparisonChain.start()
766                 .compare(arg0.getOrder(), arg1.getOrder(), Ordering.natural().nullsLast())
767                 .compare(arg0.getRelatedSubject().getValue(), arg1.getRelatedSubject().getValue()
768                         ,Ordering.natural().nullsLast())
769                 .result();
770         }
771
772     }
773 }