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