Bug5427: Added JavaDoc for OfOverlay rendeder (flow description)
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / flow / ChainActionFlows.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.addNxNsiMatch;
12 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxNspMatch;
13 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxRegMatch;
14 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxTunIdMatch;
15 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxTunIpv4DstMatch;
16 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.applyActionIns;
17 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.gotoTableIns;
18 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.instructions;
19 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadNshc1RegAction;
20 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadNshc2RegAction;
21 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadRegAction;
22 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadTunIPv4Action;
23 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadTunIdAction;
24 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxOutputRegAction;
25 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.outputAction;
26
27 import java.math.BigInteger;
28
29 import org.apache.commons.lang3.StringUtils;
30 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
31 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
32 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager;
33 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch;
34 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory.EndpointFwdCtxOrdinals;
35 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.PolicyEnforcer.NetworkElements;
36 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchManager;
37 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ChainAction;
38 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sfcutils.SfcNshHeader;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.go.to.table._case.GoToTable;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg4;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlanGpe;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 import com.google.common.annotations.VisibleForTesting;
61
62 /**
63  * <h1>Creation of flows related to service chain</h1>
64  *
65  * These flows are built across most of gbp of tables and have higher priority than basic flows. It ensures, that
66  * packet redirected to chain will be sent to tunnel output
67  * <p>
68  *
69  * Flow that allows ALL traffic incoming from chain last hop
70  * <p>
71  * <i>Allow from chain flow</i><br>
72  * Table = 0<br>
73  * Priority = 1200<br>
74  * Matches:<br>
75  *      - Nshc1<br>
76  *      - Nsp<br>
77  *      - Nsi<br>
78  *      - in_port (tunnel port) {@link NodeConnectorId}<br>
79  * Actions:<br>
80  *      - {@link GoToTable} SOURCE MAPPER table
81  * <p>
82  * TODO: looks like duplicity, the same flow is created in policy enforcer
83  * <i>Allow from chain tunnel</i>
84  * Table = 4<br>
85  * Priority = 65000<br>
86  * Matches:<br>
87  *      - in_port (tunnel port) {@link NodeConnectorId}<br>
88  *      - Reg7 (fixed value 0xffffff) {@link NxmNxReg7}
89  * Actions:<br>
90  *      - {@link GoToTable} SOURCE MAPPER table
91  * <p>
92  * <i>Create external flow</i>
93  * Table = 6<br>
94  * Priority = 1000 (if dst node == src node, priority = 1500)<br>
95  * Matches:<br>
96  *      - Reg6 {@link NxmNxReg6}<br>
97  *      - tunnel ID<br>
98  *      - nsp<br>
99  *      - nsi<br>
100  *      - tun_dst (only if dst node == src node)<br>
101  * Actions:<br>
102  *      - set nshc1<br>
103  *      - set nshc2<br>
104  *      - load tunnel ID<br>
105  *      - load tunnel ipv4<br>
106  *      - output:(tunnel port)<br>
107  * <p>
108  * <i>Chain tunnel flow</i><br>
109  * Table = 2<br>
110  * Priority = 150<br>
111  * Matches:<br>
112  *      - in_port (tunnel port) {@link NodeConnectorId}<br>
113  *      - tunnel ID<br>
114  *      - nsp<br>
115  *      - nsi<br>
116  * Actions:<br>
117  *      - Reg0 {@link NxmNxReg0}<br>
118  *      - Reg1 {@link NxmNxReg1}<br>
119  *      - Reg4 {@link NxmNxReg4}<br>
120  *      - Reg5 {@link NxmNxReg5}<br>
121  *      - Reg6 {@link NxmNxReg6}<br>
122  *      - {@link GoToTable} DESTINATION MAPPER table<br>
123  * <p>
124  * <i>Chain broadcast flow</i><br>
125  * Table = 2<br>
126  * Priority = 150<br>
127  * Matches:<br>
128  *      - in_port (tunnel port) {@link NodeConnectorId}<br>
129  *      - tunnel ID<br>
130  *      - nsp<br>
131  *      - nsi<br>
132  * Actions:<br>
133  *      - load Reg5 {@link NxmNxReg5}<br>
134  *      - {@link GoToTable} DESTINATION MAPPER table<br>
135  */
136 public class ChainActionFlows {
137
138     private static final Logger LOG = LoggerFactory.getLogger(ChainAction.class);
139
140     public ChainActionFlows() {
141
142     }
143
144     public static void createChainTunnelFlows(SfcNshHeader sfcNshHeader, NetworkElements netElements, OfWriter ofWriter,
145             OfContext ctx) {
146
147         NodeId localNodeId = netElements.getLocalNodeId();
148         NodeId destNodeId = netElements.getDstEp().getAugmentation(OfOverlayContext.class).getNodeId();
149         EndpointFwdCtxOrdinals epOrdinals = netElements.getSrcEpOrdinals();
150
151         NodeConnectorId localNodeTunPort = ctx.getSwitchManager().getTunnelPort(localNodeId, TunnelTypeVxlanGpe.class);
152         NodeConnectorId destNodeTunPort = ctx.getSwitchManager().getTunnelPort(destNodeId, TunnelTypeVxlanGpe.class);
153         Ipv4Address tunDestAddress = ctx.getSwitchManager()
154             .getTunnelIP(netElements.getDstNodeId(), TunnelTypeVxlanGpe.class)
155             .getIpv4Address();
156         if (localNodeTunPort == null || destNodeTunPort == null) {
157             LOG.error("createChainTunnelFlows: No valid VXLAN GPE tunnel for Node {} or Node {}", localNodeId,
158                     destNodeId);
159             return;
160         }
161         ofWriter.writeFlow(localNodeId, ctx.getPolicyManager().getTABLEID_PORTSECURITY(), allowFromChainPort(
162                 sfcNshHeader, localNodeTunPort, ctx.getPolicyManager().getTABLEID_PORTSECURITY(), ctx));
163
164         ofWriter.writeFlow(localNodeId, ctx.getPolicyManager().getTABLEID_POLICY_ENFORCER(),
165                 allowFromChainTunnel(localNodeTunPort, ctx.getPolicyManager().getTABLEID_POLICY_ENFORCER()));
166
167         ofWriter.writeFlow(localNodeId, ctx.getPolicyManager().getTABLEID_EXTERNAL_MAPPER(),
168                 createExternalFlow(sfcNshHeader, localNodeTunPort, netElements,
169                         ctx.getPolicyManager(), ctx.getSwitchManager(), tunDestAddress));
170
171         ofWriter.writeFlow(destNodeId, ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER(), createChainTunnelFlow(
172                 sfcNshHeader, destNodeTunPort, epOrdinals, ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER(), ctx));
173
174         ofWriter.writeFlow(destNodeId, ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER(), createChainBroadcastFlow(
175                 sfcNshHeader, destNodeTunPort, epOrdinals, ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER(), ctx));
176     }
177
178     private static Flow createChainBroadcastFlow(SfcNshHeader sfcNshHeader, NodeConnectorId tunPort,
179             EndpointFwdCtxOrdinals epFwdCtxOrds, short tableId, OfContext ctx) {
180
181         int fdId = epFwdCtxOrds.getFdId();
182
183         MatchBuilder mb = new MatchBuilder().setInPort(tunPort);
184
185         addNxNsiMatch(mb, sfcNshHeader.getNshNsiFromChain());
186         addNxNspMatch(mb, sfcNshHeader.getNshNspFromChain());
187         addNxTunIdMatch(mb, fdId);
188
189         org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action fdReg =
190                 nxLoadRegAction(NxmNxReg5.class, BigInteger.valueOf(fdId));
191
192         Match match = mb.build();
193         FlowId flowId = FlowIdUtils.newFlowId(tableId, "chainbroadcast", match);
194
195         FlowBuilder flowb = base(tableId).setId(flowId)
196             .setPriority(150)
197             .setMatch(match)
198             .setInstructions(instructions(applyActionIns(fdReg),
199                     gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
200         return flowb.build();
201     }
202
203     @VisibleForTesting
204     static Flow createExternalFlow(SfcNshHeader sfcNshHeader, NodeConnectorId tunPort, NetworkElements netElements,
205                                    PolicyManager policyManager, SwitchManager switchManager, Ipv4Address ipTunDest) {
206
207         short tableId = policyManager.getTABLEID_EXTERNAL_MAPPER();
208         org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action loadC1;
209         org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action outputAction;
210
211         Integer priority = 1000;
212         int matchTunnelId = sfcNshHeader.getNshMetaC2().intValue();
213         Long l3c = (long) netElements.getSrcEpOrdinals().getL3Id();
214         loadC1 = nxLoadNshc1RegAction(sfcNshHeader.getNshMetaC1());
215
216         // Test for if SFF is on same node
217         IpAddress ipAddress = switchManager.getTunnelIP(netElements.getLocalNodeId(), TunnelTypeVxlanGpe.class);
218
219         if (ipAddress != null && ipAddress.getIpv4Address().equals(sfcNshHeader.getNshTunIpDst())) {
220             Integer newPort = returnOfPortFromNodeConnector(tunPort);
221             outputAction = FlowUtils.createActionResubmit(newPort, policyManager.getTABLEID_SFC_EGRESS());
222         } else {
223             outputAction = outputAction(tunPort);
224         }
225
226         org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action loadC2 =
227                 nxLoadNshc2RegAction(sfcNshHeader.getNshMetaC2());
228         org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action loadChainTunVnid =
229                 nxLoadTunIdAction(BigInteger.valueOf(sfcNshHeader.getNshMetaC2()), false);
230         org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action loadChainTunDest =
231                 nxLoadTunIPv4Action(sfcNshHeader.getNshTunIpDst().getValue(), false);
232
233         MatchBuilder mb = new MatchBuilder();
234         addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, l3c));
235         addNxTunIdMatch(mb, matchTunnelId);
236         addNxNspMatch(mb, sfcNshHeader.getNshNspToChain());
237         addNxNsiMatch(mb, sfcNshHeader.getNshNsiToChain());
238         if (!netElements.getDstNodeId().equals(netElements.getSrcNodeId())) {
239             addNxTunIpv4DstMatch(mb, ipTunDest);
240             priority = 1500;
241         }
242
243         Match match = mb.build();
244         FlowId flowId = FlowIdUtils.newFlowId(tableId, "chainexternal", match);
245         FlowBuilder flowb =
246                 base(tableId).setId(flowId).setPriority(priority).setMatch(match).setInstructions(
247                         instructions(applyActionIns(loadC1, loadC2, loadChainTunDest, loadChainTunVnid, outputAction)));
248         return flowb.build();
249     }
250
251     private static Flow createChainTunnelFlow(SfcNshHeader sfcNshHeader, NodeConnectorId tunPort,
252             EndpointFwdCtxOrdinals epFwdCtxOrds, short tableId, OfContext ctx) {
253
254         int egId = epFwdCtxOrds.getEpgId();
255         int bdId = epFwdCtxOrds.getBdId();
256         int fdId = epFwdCtxOrds.getFdId();
257         int l3Id = epFwdCtxOrds.getL3Id();
258         int tunnelId = epFwdCtxOrds.getTunnelId();
259
260         MatchBuilder mb = new MatchBuilder().setInPort(tunPort);
261         addNxTunIdMatch(mb, tunnelId);
262         addNxNspMatch(mb, sfcNshHeader.getNshNspFromChain());
263         addNxNsiMatch(mb, sfcNshHeader.getNshNsiFromChain());
264
265         org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action segReg =
266                 nxLoadRegAction(NxmNxReg0.class, BigInteger.valueOf(egId));
267         org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action scgReg =
268                 nxLoadRegAction(NxmNxReg1.class, BigInteger.valueOf(0xffffff));
269         org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action bdReg =
270                 nxLoadRegAction(NxmNxReg4.class, BigInteger.valueOf(bdId));
271         org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action fdReg =
272                 nxLoadRegAction(NxmNxReg5.class, BigInteger.valueOf(fdId));
273         org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action vrfReg =
274                 nxLoadRegAction(NxmNxReg6.class, BigInteger.valueOf(l3Id));
275
276         Match match = mb.build();
277         FlowId flowId = FlowIdUtils.newFlowId(tableId, "chaintunnel", match);
278         FlowBuilder flowb =
279                 base(tableId).setId(flowId).setPriority(150).setMatch(match).setInstructions(
280                         instructions(applyActionIns(segReg, scgReg, bdReg, fdReg, vrfReg),
281                                 gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
282         return flowb.build();
283     }
284
285     private static Flow allowFromChainPort(SfcNshHeader sfcNshHeader, NodeConnectorId port, short tableId,
286             OfContext ctx) {
287
288         // Matching on last NSP/NSI that SFF leaves on but with C1=0
289         MatchBuilder mb = new MatchBuilder();
290         FlowUtils.addNxNshc1RegMatch(mb, 0L);
291         FlowUtils.addNxNsiMatch(mb, sfcNshHeader.getNshNsiFromChain());
292         FlowUtils.addNxNspMatch(mb, sfcNshHeader.getNshNspFromChain());
293         Match match = mb.setInPort(port).build();
294
295         FlowId flowId = FlowIdUtils.newFlowId(tableId, "chainport", match);
296         FlowBuilder flowb =
297                 base(tableId).setId(flowId).setPriority(1200).setMatch(match).setInstructions(
298                         FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()));
299         return flowb.build();
300     }
301
302     private static Flow allowFromChainTunnel(NodeConnectorId tunPort, short tableId) {
303
304         MatchBuilder mb = new MatchBuilder().setInPort(tunPort);
305         addNxRegMatch(mb, RegMatch.of(NxmNxReg1.class, 0xffffffL));
306         Match match = mb.build();
307         FlowId flowId = FlowIdUtils.newFlowId(tableId, "chainport", match);
308
309         FlowBuilder flow = base(tableId).setId(flowId).setMatch(match).setPriority(65000).setInstructions(
310                 instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
311         return flow.build();
312
313     }
314
315     @VisibleForTesting
316     static Integer returnOfPortFromNodeConnector(NodeConnectorId nodeConnectorId) {
317         String[] elements = StringUtils.split(nodeConnectorId.getValue(), ":");
318         if (elements.length != 3)
319             return null;
320         return new Integer(elements[2]);
321     }
322
323     /**
324      * Get a base flow builder with some common features already set
325      */
326     private static FlowBuilder base(short tableId) {
327         return new FlowBuilder().setTableId(tableId).setBarrier(false).setHardTimeout(0).setIdleTimeout(0);
328     }
329 }