7a7937bfc24e792a32227e32944d550daf7dcb18
[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.Classifier;
28 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Action;
29 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.SubjectFeatures;
30 import org.opendaylight.groupbasedpolicy.resolver.ConditionGroup;
31 import org.opendaylight.groupbasedpolicy.resolver.EgKey;
32 import org.opendaylight.groupbasedpolicy.resolver.IndexedTenant;
33 import org.opendaylight.groupbasedpolicy.resolver.Policy;
34 import org.opendaylight.groupbasedpolicy.resolver.PolicyInfo;
35 import org.opendaylight.groupbasedpolicy.resolver.RuleGroup;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.action.refs.ActionRef;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup.IntraGroupPolicy;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.Rule;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ActionInstance;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ClassifierInstance;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
61 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 import com.google.common.collect.ComparisonChain;
66 import com.google.common.collect.Ordering;
67
68 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.*;
69
70 /**
71  * Manage the table that enforces policy on the traffic.  Traffic is denied
72  * unless specifically allowed by policy
73  * @author readams
74  */
75 public class PolicyEnforcer extends FlowTable {
76     protected static final Logger LOG =
77             LoggerFactory.getLogger(PolicyEnforcer.class);
78
79     public static final short TABLE_ID = 3;
80
81     public PolicyEnforcer(OfContext ctx) {
82         super(ctx);
83     }
84
85     @Override
86     public short getTableId() {
87         return TABLE_ID;
88     }
89
90     @Override
91     public void sync(ReadWriteTransaction t, InstanceIdentifier<Table> tiid,
92                      Map<String, FlowCtx> flowMap, NodeId nodeId, 
93                      PolicyInfo policyInfo, Dirty dirty)
94                              throws Exception {
95         dropFlow(t, tiid, flowMap, Integer.valueOf(1), null);
96         allowFromTunnel(t, tiid, flowMap, nodeId);
97
98         HashSet<CgPair> visitedPairs = new HashSet<>();
99
100         for (EgKey sepg : ctx.getEndpointManager().getGroupsForNode(nodeId)) {
101             // Allow traffic within the same endpoint group if the policy
102             // specifies
103             IndexedTenant tenant = 
104                     ctx.getPolicyResolver().getTenant(sepg.getTenantId());
105             EndpointGroup group = 
106                     tenant.getEndpointGroup(sepg.getEgId());
107             IntraGroupPolicy igp = group.getIntraGroupPolicy();
108             int sepgId = 
109                     ctx.getPolicyManager().getContextOrdinal(sepg.getTenantId(),
110                                                         sepg.getEgId());
111             if (igp == null || igp.equals(IntraGroupPolicy.Allow)) {
112                 allowSameEpg(t, tiid, flowMap, nodeId, sepgId);
113             }
114
115             for (Endpoint src : ctx.getEndpointManager().getEPsForNode(nodeId, sepg)) {
116                 if (src.getTenant() == null || src.getEndpointGroup() == null)
117                     continue;
118                 
119                 List<ConditionName> conds = 
120                         ctx.getEndpointManager().getCondsForEndpoint(src);
121                 ConditionGroup scg = policyInfo.getEgCondGroup(sepg, conds);
122                 int scgId = ctx.getPolicyManager().getCondGroupOrdinal(scg);
123                 
124                 Set<EgKey> peers = policyInfo.getPeers(sepg);
125                 for (EgKey depg : peers) {
126                     int depgId = 
127                             ctx.getPolicyManager().getContextOrdinal(depg.getTenantId(),
128                                                                 depg.getEgId());
129                 
130                     for (Endpoint dst : ctx.getEndpointManager().getEndpointsForGroup(depg)) {
131                 
132                         conds = ctx.getEndpointManager().getCondsForEndpoint(dst);
133                         ConditionGroup dcg = 
134                                 policyInfo.getEgCondGroup(new EgKey(dst.getTenant(),
135                                                                     dst.getEndpointGroup()),
136                                                           conds);
137                         int dcgId = ctx.getPolicyManager().getCondGroupOrdinal(dcg);
138                         
139                         CgPair p = new CgPair(depgId, sepgId, dcgId, scgId);
140                         if (visitedPairs.contains(p)) continue;
141                         visitedPairs.add(p);
142                         syncPolicy(t, tiid, flowMap, nodeId, policyInfo, 
143                                    p, depg, sepg, dcg, scg);
144                 
145                         p = new CgPair(sepgId, depgId, scgId, dcgId);
146                         if (visitedPairs.contains(p)) continue;
147                         visitedPairs.add(p);
148                         syncPolicy(t, tiid, flowMap, nodeId, policyInfo, 
149                                    p, sepg, depg, scg, dcg);
150                         
151                     }
152                 }
153             }
154         }
155     }
156     
157     private void allowSameEpg(ReadWriteTransaction t, 
158                               InstanceIdentifier<Table> tiid,
159                               Map<String, FlowCtx> flowMap, NodeId nodeId,
160                               int sepgId) {
161         FlowId flowId = new FlowId(new StringBuilder()
162             .append("intraallow|")
163             .append(sepgId).toString());
164         if (visit(flowMap, flowId.getValue())) {
165             MatchBuilder mb = new MatchBuilder();
166             addNxRegMatch(mb, 
167                           RegMatch.of(NxmNxReg0.class,Long.valueOf(sepgId)),
168                           RegMatch.of(NxmNxReg2.class,Long.valueOf(sepgId)));
169             FlowBuilder flow = base()
170                 .setId(flowId)
171                 .setMatch(mb.build())
172                 .setPriority(65000)
173                 .setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
174             writeFlow(t, tiid, flow.build());
175         }
176     }
177     
178     private void allowFromTunnel(ReadWriteTransaction t, 
179                                  InstanceIdentifier<Table> tiid,
180                                  Map<String, FlowCtx> flowMap, NodeId nodeId) {
181         NodeConnectorId tunPort =
182                 ctx.getSwitchManager().getTunnelPort(nodeId);
183         if (tunPort == null) return;
184
185         FlowId flowId = new FlowId("tunnelallow");
186         if (visit(flowMap, flowId.getValue())) {
187             MatchBuilder mb = new MatchBuilder()
188                 .setInPort(tunPort);
189             addNxRegMatch(mb, 
190                           RegMatch.of(NxmNxReg1.class,Long.valueOf(0xffffff)));
191             FlowBuilder flow = base()
192                 .setId(flowId)
193                 .setMatch(mb.build())
194                 .setPriority(65000)
195                 .setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
196             writeFlow(t, tiid, flow.build());
197         }
198     }
199     
200     private void syncPolicy(ReadWriteTransaction t, 
201                             InstanceIdentifier<Table> tiid,
202                             Map<String, FlowCtx> flowMap, NodeId nodeId,
203                             PolicyInfo policyInfo, 
204                             CgPair p, EgKey sepg, EgKey depg,
205                             ConditionGroup scg, ConditionGroup dcg) 
206                              throws Exception {
207         // XXX - TODO raise an exception for rules between the same
208         // endpoint group that are asymmetric
209         Policy policy = policyInfo.getPolicy(sepg, depg);
210         List<RuleGroup> rgs = policy.getRules(scg, dcg);
211         
212         int priority = 65000;
213         for (RuleGroup rg : rgs) {
214             TenantId tenantId = rg.getContractTenant().getId();
215             IndexedTenant tenant = ctx.getPolicyResolver().getTenant(tenantId);
216             for (Rule r : rg.getRules()) {
217                 syncDirection(t, tiid, flowMap, nodeId, tenant,
218                                     p, r, Direction.In, priority);
219                 syncDirection(t, tiid, flowMap, nodeId, tenant,
220                                     p, r, Direction.Out, priority);
221                 
222                 priority -= 1;
223             }
224         }
225     }
226
227     /**
228      * Private internal class for ordering Actions in Rules. The
229      * order is determined first by the value of the order parameter,
230      * with the lower order actions being applied first; for Actions
231      * with either the same order or no order, ordering is lexicographical
232      * by name.
233      *
234      * @author tbachman
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(ReadWriteTransaction t, 
253                                InstanceIdentifier<Table> tiid,
254                                Map<String, FlowCtx> flowMap, NodeId nodeId,
255                                IndexedTenant contractTenant,
256                                CgPair p, Rule r, Direction d, int priority) {
257         /*
258          * Create the ordered action list. The implicit action is
259          * "allow", and is therefore always in the list
260          *
261          * TODO: revisit implicit vs. default for "allow"
262          * TODO: look into incorporating operational policy for actions
263          */
264         List<ActionBuilder> abl = new ArrayList<ActionBuilder>();
265         if (r.getActionRef() != null) {
266             /*
267              * Pre-sort by references using order, then name
268              */
269             List<ActionRef> arl = new ArrayList<ActionRef>(r.getActionRef());
270             Collections.sort(arl, ActionRefComparator.INSTANCE);
271
272             for (ActionRef ar: arl) {
273                 ActionInstance ai = contractTenant.getAction(ar.getName());
274                 if (ai == null) {
275                     // XXX TODO fail the match and raise an exception
276                     LOG.warn("Action instance {} not found",
277                              ar.getName().getValue());
278                     return;
279                 }
280                 Action act = SubjectFeatures.getAction(ai.getActionDefinitionId());
281                 if (act == null) {
282                     // XXX TODO fail the match and raise an exception
283                     LOG.warn("Action definition {} not found",
284                              ai.getActionDefinitionId().getValue());
285                     return;
286                 }
287
288                 Map<String,Object> params = new HashMap<>();
289                 if (ai.getParameterValue() != null) {
290                     for (ParameterValue v : ai.getParameterValue()) {
291                         if (v.getName() == null) 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
301                  * Actions
302                  */
303                 abl = act.updateAction(abl, params, ar.getOrder());
304             }
305         }
306         else {
307             Action act = SubjectFeatures.getAction(AllowAction.ID);
308             abl = act.updateAction(abl, new HashMap<String,Object>(),  0);
309         }
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
358             ClassifierInstance ci = contractTenant.getClassifier(cr.getName());
359             if (ci == null) {
360                 // XXX TODO fail the match and raise an exception
361                 LOG.warn("Classifier instance {} not found", 
362                          cr.getName().getValue());
363                 return;
364             }
365             Classifier cfier = SubjectFeatures
366                     .getClassifier(ci.getClassifierDefinitionId());
367             if (cfier == null) {
368                 // XXX TODO fail the match and raise an exception
369                 LOG.warn("Classifier definition {} not found", 
370                          ci.getClassifierDefinitionId().getValue());
371                 return;
372             }
373
374             List<MatchBuilder> matches = Collections.singletonList(baseMatch);
375             Map<String,Object> params = new HashMap<>();
376             for (ParameterValue v : ci.getParameterValue()) {
377                 if (v.getName() == null) continue;
378                 if (v.getIntValue() != null) {
379                     params.put(v.getName().getValue(), v.getIntValue());
380                 } else if (v.getStringValue() != null) {
381                     params.put(v.getName().getValue(), v.getStringValue());
382                 } else if (v.getRangeValue() != null) {
383                     params.put(v.getName().getValue(), v.getRangeValue());
384                 }
385             }
386             
387             matches = cfier.updateMatch(matches, params);
388             String baseId = idb.toString();
389             FlowBuilder flow = base()
390                     .setPriority(Integer.valueOf(priority));
391             for (MatchBuilder match : matches) {
392                 Match m = match.build();
393                 FlowId flowId = new FlowId(baseId + "|" + m.toString());
394                 if (visit(flowMap, flowId.getValue())) {
395                     flow.setMatch(m)
396                         .setId(flowId)
397                         .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 }