Added check for tunPort existance and made non-null precondition for adding a flow...
[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.instructions;
14 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxOutputRegAction;
15
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24
25 import javax.annotation.concurrent.Immutable;
26
27 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
28 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.FlowMap;
29 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch;
30 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory.EndpointFwdCtxOrdinals;
31 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Action;
32 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.AllowAction;
33 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ClassificationResult;
34 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Classifier;
35 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ParamDerivator;
36 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.SubjectFeatures;
37 import org.opendaylight.groupbasedpolicy.resolver.ConditionGroup;
38 import org.opendaylight.groupbasedpolicy.resolver.EgKey;
39 import org.opendaylight.groupbasedpolicy.resolver.IndexedTenant;
40 import org.opendaylight.groupbasedpolicy.resolver.Policy;
41 import org.opendaylight.groupbasedpolicy.resolver.PolicyInfo;
42 import org.opendaylight.groupbasedpolicy.resolver.RuleGroup;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.action.refs.ActionRef;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup.IntraGroupPolicy;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.Rule;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ActionInstance;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ClassifierInstance;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71 import com.google.common.collect.ComparisonChain;
72 import com.google.common.collect.Ordering;
73
74 /**
75  * Manage the table that enforces policy on the traffic. Traffic is denied
76  * unless specifically allowed by policy
77  */
78 public class PolicyEnforcer extends FlowTable {
79     protected static final Logger LOG =
80             LoggerFactory.getLogger(PolicyEnforcer.class);
81
82     public static final short TABLE_ID = 3;
83
84     public PolicyEnforcer(OfContext ctx) {
85         super(ctx);
86     }
87
88     @Override
89     public short getTableId() {
90         return TABLE_ID;
91     }
92
93     @Override
94     public void sync(NodeId nodeId, PolicyInfo policyInfo, FlowMap flowMap) throws Exception {
95
96         flowMap.writeFlow(nodeId, TABLE_ID, dropFlow(Integer.valueOf(1), null));
97
98         NodeConnectorId tunPort = ctx.getSwitchManager().getTunnelPort(nodeId);
99         if (tunPort != null) {
100             flowMap.writeFlow(nodeId, TABLE_ID, allowFromTunnel(tunPort));
101         }
102
103         HashSet<CgPair> visitedPairs = new HashSet<>();
104
105         for (Endpoint srcEp : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
106             for (EgKey srcEpgKey : ctx.getEndpointManager().getEgKeysForEndpoint(srcEp)) {
107                 Set<EgKey> peers = policyInfo.getPeers(srcEpgKey);
108                 for (EgKey dstEpgKey : peers) {
109                     for (Endpoint dstEp : ctx.getEndpointManager().getEndpointsForGroup(dstEpgKey)) {
110                         // mEPG ordinals
111                         EndpointFwdCtxOrdinals srcEpFwdCxtOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, policyInfo, srcEp);
112                         EndpointFwdCtxOrdinals dstEpFwdCxtOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, policyInfo, dstEp);
113                         int dcgId = dstEpFwdCxtOrds.getCgId();
114                         int depgId = dstEpFwdCxtOrds.getEpgId();
115                         int scgId = srcEpFwdCxtOrds.getCgId();
116                         int sepgId = srcEpFwdCxtOrds.getEpgId();
117
118                         List<ConditionName> conds = ctx.getEndpointManager().getCondsForEndpoint(srcEp);
119                         ConditionGroup scg = policyInfo.getEgCondGroup(srcEpgKey, conds);
120                         conds = ctx.getEndpointManager().getCondsForEndpoint(dstEp);
121                         ConditionGroup dcg = policyInfo.getEgCondGroup(dstEpgKey, conds);
122
123                         CgPair p = new CgPair(depgId, sepgId, dcgId, scgId);
124                         if (visitedPairs.contains(p))
125                             continue;
126                         visitedPairs.add(p);
127                         syncPolicy(flowMap, nodeId, policyInfo,
128                                 p, dstEpgKey, srcEpgKey, dcg, scg);
129
130                         //Reverse
131                         p = new CgPair(sepgId, depgId, scgId, dcgId);
132                         if (visitedPairs.contains(p))
133                             continue;
134                         visitedPairs.add(p);
135                         syncPolicy(flowMap, nodeId, policyInfo,
136                                 p, srcEpgKey, dstEpgKey, scg, dcg);
137                     }
138                 }
139             }
140         }
141
142         // Allow same EPG
143 //        Set<Endpoint> visitedEps = new HashSet<>();
144         for (Endpoint srcEp : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
145 //            visitedEps.add(srcEp);
146             for (EgKey srcEpgKey : ctx.getEndpointManager().getEgKeysForEndpoint(srcEp)) {
147
148                 IndexedTenant tenant = ctx.getPolicyResolver().getTenant(srcEpgKey.getTenantId());
149                 EndpointGroup group = tenant.getEndpointGroup(srcEpgKey.getEgId());
150                 IntraGroupPolicy igp = group.getIntraGroupPolicy();
151
152                 if (igp == null || igp.equals(IntraGroupPolicy.Allow)) {
153                     for (Endpoint dstEp : ctx.getEndpointManager().getEndpointsForGroup(srcEpgKey)) {
154                         // mEPG ordinals
155 //                        if(visitedEps.contains(dstEp)) {
156 //                            continue;
157 //                        }
158 //                        visitedEps.add(dstEp);
159                         EndpointFwdCtxOrdinals srcEpFwdCxtOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, policyInfo, srcEp);
160                         EndpointFwdCtxOrdinals dstEpFwdCxtOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, policyInfo, dstEp);
161                         int depgId = dstEpFwdCxtOrds.getEpgId();
162                         int sepgId = srcEpFwdCxtOrds.getEpgId();
163                         flowMap.writeFlow(nodeId, TABLE_ID, allowSameEpg(sepgId, depgId));
164                         flowMap.writeFlow(nodeId, TABLE_ID, allowSameEpg(depgId, sepgId));
165                     }
166                 }
167             }
168         }
169
170     }
171
172     private Flow allowSameEpg(int sepgId, int depgId) {
173         FlowId flowId = new FlowId(new StringBuilder()
174                 .append("intraallow|")
175                 .append(sepgId).toString());
176             MatchBuilder mb = new MatchBuilder();
177             addNxRegMatch(mb,
178                     RegMatch.of(NxmNxReg0.class, Long.valueOf(sepgId)),
179                     RegMatch.of(NxmNxReg2.class, Long.valueOf(depgId)));
180             FlowBuilder flow = base()
181                     .setId(flowId)
182                     .setMatch(mb.build())
183                     .setPriority(65000)
184                     .setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
185             return flow.build();
186     }
187
188     private Flow allowFromTunnel(NodeConnectorId tunPort) {
189
190
191         FlowId flowId = new FlowId("tunnelallow");
192         MatchBuilder mb = new MatchBuilder()
193                 .setInPort(tunPort);
194         addNxRegMatch(mb,
195                 RegMatch.of(NxmNxReg1.class, Long.valueOf(0xffffff)));
196         FlowBuilder flow = base()
197                 .setId(flowId)
198                 .setMatch(mb.build())
199                 .setPriority(65000)
200                 .setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
201         return flow.build();
202
203     }
204
205     private void syncPolicy(FlowMap flowMap, NodeId nodeId,
206             PolicyInfo policyInfo,
207             CgPair p, EgKey sepg, EgKey depg,
208             ConditionGroup scg, ConditionGroup dcg)
209             throws Exception {
210         // XXX - TODO raise an exception for rules between the same
211         // endpoint group that are asymmetric
212         Policy policy = policyInfo.getPolicy(sepg, depg);
213         List<RuleGroup> rgs = policy.getRules(scg, dcg);
214
215         int priority = 65000;
216         for (RuleGroup rg : rgs) {
217             TenantId tenantId = rg.getContractTenant().getId();
218             IndexedTenant tenant = ctx.getPolicyResolver().getTenant(tenantId);
219             for (Rule r : rg.getRules()) {
220                 syncDirection(flowMap, nodeId, tenant,
221                         p, r, Direction.In, priority);
222                 syncDirection(flowMap, nodeId, tenant,
223                         p, r, Direction.Out, priority);
224
225                 priority -= 1;
226             }
227         }
228     }
229
230     /**
231      * Private internal class for ordering Actions in Rules. The order is
232      * determined first by the value of the order parameter, with the lower
233      * order actions being applied first; for Actions with either the same order
234      * or no order, ordering is lexicographical by name.
235      *
236      */
237     private static class ActionRefComparator implements Comparator<ActionRef> {
238         public static final ActionRefComparator INSTANCE = new ActionRefComparator();
239
240         @Override
241         public int compare(ActionRef arg0, ActionRef arg1) {
242             return ComparisonChain.start()
243                     .compare(arg0.getOrder(), arg1.getOrder(),
244                             Ordering.natural().nullsLast())
245                     .compare(arg0.getName().getValue(), arg1.getName().getValue(),
246                             Ordering.natural().nullsLast())
247                     .result();
248         }
249
250     }
251
252     private void syncDirection(FlowMap flowMap, NodeId nodeId, IndexedTenant contractTenant,
253             CgPair cgPair, Rule rule, Direction direction, int priority) {
254         /*
255          * Create the ordered action list. The implicit action is "allow", and
256          * is therefore always in the list
257          *
258          * TODO: revisit implicit vs. default for "allow" TODO: look into
259          * incorporating operational policy for actions
260          */
261
262         //TODO: can pass Comparator ActionRefComparator to List constructor, rather than referencing in sort
263         List<ActionBuilder> abl = new ArrayList<ActionBuilder>();
264         if (rule.getActionRef() != null) {
265             /*
266              * Pre-sort by references using order, then name
267              */
268             List<ActionRef> arl = new ArrayList<ActionRef>(rule.getActionRef());
269             Collections.sort(arl, ActionRefComparator.INSTANCE);
270
271             for (ActionRef ar : arl) {
272                 ActionInstance ai = contractTenant.getAction(ar.getName());
273                 if (ai == null) {
274                     // XXX TODO fail the match and raise an exception
275                     LOG.warn("Action instance {} not found",
276                             ar.getName().getValue());
277                     return;
278                 }
279                 Action act = SubjectFeatures.getAction(ai.getActionDefinitionId());
280                 if (act == null) {
281                     // XXX TODO fail the match and raise an exception
282                     LOG.warn("Action definition {} not found",
283                             ai.getActionDefinitionId().getValue());
284                     return;
285                 }
286
287                 Map<String, Object> params = new HashMap<>();
288                 if (ai.getParameterValue() != null) {
289                     for (ParameterValue v : ai.getParameterValue()) {
290                         if (v.getName() == null)
291                             continue;
292                         if (v.getIntValue() != null) {
293                             params.put(v.getName().getValue(), v.getIntValue());
294                         } else if (v.getStringValue() != null) {
295                             params.put(v.getName().getValue(), v.getStringValue());
296                         }
297                     }
298                 }
299                 /*
300                  * Convert the GBP Action to one or more OpenFlow Actions
301                  */
302                 abl = act.updateAction(abl, params, ar.getOrder());
303             }
304         }
305         else {
306             Action act = SubjectFeatures.getAction(AllowAction.DEFINITION.getId());
307             abl = act.updateAction(abl, new HashMap<String, Object>(), 0);
308         }
309
310         for (ClassifierRef cr : rule.getClassifierRef()) {
311             if (cr.getDirection() != null &&
312                     !cr.getDirection().equals(Direction.Bidirectional) &&
313                     !cr.getDirection().equals(direction)) {
314                 continue;
315             }
316
317             StringBuilder idb = new StringBuilder();
318             // XXX - TODO - implement connection tracking (requires openflow
319             // extension and data plane support - in 2.4. Will need to handle
320             // case where we are working with mix of nodes.
321
322             MatchBuilder baseMatch = new MatchBuilder();
323
324             if (direction.equals(Direction.In)) {
325                 idb.append(cgPair.sepg)
326                         .append("|")
327                         .append(cgPair.scgId)
328                         .append("|")
329                         .append(cgPair.depg)
330                         .append("|")
331                         .append(cgPair.dcgId)
332                         .append("|")
333                         .append(priority);
334                 addNxRegMatch(baseMatch,
335                         RegMatch.of(NxmNxReg0.class, Long.valueOf(cgPair.sepg)),
336                         RegMatch.of(NxmNxReg1.class, Long.valueOf(cgPair.scgId)),
337                         RegMatch.of(NxmNxReg2.class, Long.valueOf(cgPair.depg)),
338                         RegMatch.of(NxmNxReg3.class, Long.valueOf(cgPair.dcgId)));
339             } else {
340                 idb.append(cgPair.depg)
341                         .append("|")
342                         .append(cgPair.dcgId)
343                         .append("|")
344                         .append(cgPair.sepg)
345                         .append("|")
346                         .append(cgPair.scgId)
347                         .append("|")
348                         .append(priority);
349                 addNxRegMatch(baseMatch,
350                         RegMatch.of(NxmNxReg0.class, Long.valueOf(cgPair.depg)),
351                         RegMatch.of(NxmNxReg1.class, Long.valueOf(cgPair.dcgId)),
352                         RegMatch.of(NxmNxReg2.class, Long.valueOf(cgPair.sepg)),
353                         RegMatch.of(NxmNxReg3.class, Long.valueOf(cgPair.scgId)));
354             }
355
356             ClassifierInstance ci = contractTenant.getClassifier(cr.getName());
357             if (ci == null) {
358                 // XXX TODO fail the match and raise an exception
359                 LOG.warn("Classifier instance {} not found",
360                         cr.getName().getValue());
361                 return;
362             }
363             Classifier cfier = SubjectFeatures
364                     .getClassifier(ci.getClassifierDefinitionId());
365             if (cfier == null) {
366                 // XXX TODO fail the match and raise an exception
367                 LOG.warn("Classifier definition {} not found",
368                         ci.getClassifierDefinitionId().getValue());
369                 return;
370             }
371
372             Map<String,ParameterValue> params = new HashMap<>();
373             for (ParameterValue v : ci.getParameterValue()) {
374
375                 if (v.getIntValue() != null) {
376                     params.put(v.getName().getValue(), v);
377                 } else if (v.getStringValue() != null) {
378                     params.put(v.getName().getValue(), v);
379                 } else if (v.getRangeValue() != null) {
380                     params.put(v.getName().getValue(), v);
381                 }
382             }
383             List<Map<String, ParameterValue>> derivedParams = ParamDerivator.ETHER_TYPE_DERIVATOR.deriveParameter(params);
384             for (Map<String, ParameterValue> flowParams : derivedParams) {
385                 List<MatchBuilder> matches = Collections.singletonList(new MatchBuilder(baseMatch.build()));
386                 ClassificationResult result = cfier.updateMatch(matches, flowParams);
387                 if(!result.isSuccessfull()) {
388                     //TODO consider different handling.
389                     throw new IllegalArgumentException(result.getErrorMessage());
390                 }
391                 String baseId = idb.toString();
392                 FlowBuilder flow = base().setPriority(Integer.valueOf(priority));
393                 for (MatchBuilder match : result.getMatchBuilders()) {
394                     Match m = match.build();
395                     FlowId flowId = new FlowId(baseId + "|" + m.toString());
396                 flow.setMatch(m)
397                         .setId(flowId)
398                         .setPriority(Integer.valueOf(priority))
399                         .setInstructions(instructions(applyActionIns(abl)));
400                 flowMap.writeFlow(nodeId, TABLE_ID, flow.build());
401
402
403                 }
404             }
405         }
406     }
407
408     @Immutable
409     private static class CgPair {
410         private final int sepg;
411         private final int depg;
412         private final int scgId;
413         private final int dcgId;
414
415         public CgPair(int sepg, int depg, int scgId, int dcgId) {
416             super();
417             this.sepg = sepg;
418             this.depg = depg;
419             this.scgId = scgId;
420             this.dcgId = dcgId;
421         }
422
423         @Override
424         public int hashCode() {
425             final int prime = 31;
426             int result = 1;
427             result = prime * result + dcgId;
428             result = prime * result + depg;
429             result = prime * result + scgId;
430             result = prime * result + sepg;
431             return result;
432         }
433
434         @Override
435         public boolean equals(Object obj) {
436             if (this == obj)
437                 return true;
438             if (obj == null)
439                 return false;
440             if (getClass() != obj.getClass())
441                 return false;
442             CgPair other = (CgPair) obj;
443             if (dcgId != other.dcgId)
444                 return false;
445             if (depg != other.depg)
446                 return false;
447             if (scgId != other.scgId)
448                 return false;
449             if (sepg != other.sepg)
450                 return false;
451             return true;
452         }
453     }
454 }