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