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