BUG 8193 - Fix Netvirt classifier egress service port binding
[netvirt.git] / vpnservice / sfc / classifier / impl / src / main / java / org / opendaylight / netvirt / sfc / classifier / providers / OpenFlow13Provider.java
1 /*
2  * Copyright © 2017 Ericsson, 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.netvirt.sfc.classifier.providers;
10
11 import com.google.common.net.InetAddresses;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.List;
15 import javax.inject.Singleton;
16 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.genius.mdsalutil.NwConstants;
19 import org.opendaylight.netvirt.sfc.classifier.utils.AclMatches;
20 import org.opendaylight.netvirt.sfc.classifier.utils.OpenFlow13Utils;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.Matches;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
34
35 @Singleton
36 public class OpenFlow13Provider {
37     // Unique cookie values for each type of flow
38     public static final BigInteger INGRESS_CLASSIFIER_FILTER_COOKIE = new BigInteger("F005BA1100000001", 16);
39     public static final BigInteger INGRESS_CLASSIFIER_ACL_COOKIE = new BigInteger("F005BA1100000002", 16);
40     public static final BigInteger EGRESS_CLASSIFIER_FILTER_COOKIE = new BigInteger("F005BA1100000003", 16);
41     public static final BigInteger EGRESS_CLASSIFIER_NEXTHOP_COOKIE = new BigInteger("F005BA1100000004", 16);
42     public static final BigInteger EGRESS_CLASSIFIER_TPORTEGRESS_COOKIE = new BigInteger("F005BA1100000005", 16);
43     public static final int SFC_SERVICE_PRIORITY = 6;
44
45     // Priorities for each flow
46     public static final int INGRESS_CLASSIFIER_FILTER_NSH_PRIORITY = 510;
47     public static final int INGRESS_CLASSIFIER_FILTER_NONSH_PRIORITY = 500;
48     public static final int INGRESS_CLASSIFIER_ACL_PRIORITY = 500;
49     public static final int EGRESS_CLASSIFIER_FILTER_NSH_PRIORITY = 260;
50     public static final int EGRESS_CLASSIFIER_FILTER_NONSH_PRIORITY = 250;
51     public static final int EGRESS_CLASSIFIER_NEXTHOP_C1C2_PRIORITY = 250;
52     public static final int EGRESS_CLASSIFIER_NEXTHOP_NOC1C2_PRIORITY = 260;
53     public static final int EGRESS_CLASSIFIER_EGRESS_LOCAL_PRIORITY = 260;
54     public static final int EGRESS_CLASSIFIER_EGRESS_REMOTE_PRIORITY = 250;
55
56     // Flow names for each table
57     public static final String INGRESS_CLASSIFIER_FILTER_VXGPENSH_FLOW_NAME = "nvsfc_ingr_class_filter_vxgpe";
58     public static final String INGRESS_CLASSIFIER_FILTER_ETHNSH_FLOW_NAME = "nvsfc_ingr_class_filter_eth";
59     public static final String INGRESS_CLASSIFIER_FILTER_NONSH_FLOW_NAME = "nvsfc_ingr_class_filter_nonsh";
60     public static final String INGRESS_CLASSIFIER_ACL_FLOW_NAME = "nvsfc_ingr_class_acl";
61     public static final String EGRESS_CLASSIFIER_FILTER_VXGPENSH_FLOW_NAME = "nvsfc_egr_class_filter_vxgpe";
62     public static final String EGRESS_CLASSIFIER_FILTER_ETHNSH_FLOW_NAME = "nvsfc_egr_class_filter_eth";
63     public static final String EGRESS_CLASSIFIER_FILTER_NONSH_FLOW_NAME = "nvsfc_egr_class_filter_nonsh";
64     public static final String EGRESS_CLASSIFIER_NEXTHOP_C1C2_FLOW_NAME = "nvsfc_egr_class_nexthop_c1c2";
65     public static final String EGRESS_CLASSIFIER_NEXTHOP_NOC1C2_FLOW_NAME = "nvsfc_egr_class_nexthop_noc1c2";
66     public static final String EGRESS_CLASSIFIER_TPORTEGRESS_FLOW_NAME = "nvsfc_egr_class_ tport egress";
67
68     public static final short NSH_MDTYPE_ONE = 0x01;
69     public static final long DEFAULT_NSH_CONTEXT_VALUE = 0L;
70     private static final int DEFAULT_NETMASK = 32;
71
72     public MatchBuilder getMatchBuilderFromAceMatches(Matches matches) {
73         if (matches == null) {
74             return null;
75         }
76
77         return new AclMatches(matches).buildMatch();
78     }
79
80     public void appendFlowForCreate(NodeId node, Flow flow, WriteTransaction tx) {
81         NodeKey nodeKey = new NodeKey(node);
82         InstanceIdentifier<Flow> iidFlow = InstanceIdentifier.builder(Nodes.class)
83             .child(Node.class, nodeKey)
84             .augmentation(FlowCapableNode.class)
85             .child(Table.class, new TableKey(flow.getTableId()))
86             .child(Flow.class, flow.getKey())
87             .build();
88
89         tx.put(LogicalDatastoreType.CONFIGURATION, iidFlow, flow, true);
90     }
91
92     public void appendFlowForDelete(NodeId node, Flow flow, WriteTransaction tx) {
93         NodeKey nodeKey = new NodeKey(node);
94         InstanceIdentifier<Flow> iidFlow = InstanceIdentifier.builder(Nodes.class)
95             .child(Node.class, nodeKey)
96             .augmentation(FlowCapableNode.class)
97             .child(Table.class, new TableKey(flow.getTableId()))
98             .child(Flow.class, flow.getKey())
99             .build();
100
101         tx.delete(LogicalDatastoreType.CONFIGURATION, iidFlow);
102     }
103
104     /*
105      * Ingress Classifier Filter Vxgpe NSH flow:
106      *     Only allows Non-NSH packets to proceed in the classifier
107      *     Match on NSP and resubmit to Ingress Dispatch on match
108      */
109     public Flow createIngressClassifierFilterVxgpeNshFlow(NodeId nodeId) {
110         MatchBuilder match = new MatchBuilder();
111         OpenFlow13Utils.addMatchVxgpeNsh(match);
112
113         List<Action> actionList = new ArrayList<>();
114         actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.LPORT_DISPATCHER_TABLE,
115             actionList.size()));
116
117         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
118         String flowIdStr = INGRESS_CLASSIFIER_FILTER_VXGPENSH_FLOW_NAME + nodeId.getValue();
119
120         return OpenFlow13Utils.createFlowBuilder(NwConstants.INGRESS_SFC_CLASSIFIER_FILTER_TABLE,
121             INGRESS_CLASSIFIER_FILTER_NSH_PRIORITY, INGRESS_CLASSIFIER_FILTER_COOKIE,
122             INGRESS_CLASSIFIER_FILTER_VXGPENSH_FLOW_NAME, flowIdStr, match, isb).build();
123     }
124
125     /*
126      * Ingress Classifier Filter Eth NSH flow:
127      *     Only allows Non-NSH packets to proceed in the classifier
128      *     Match on NSP and resubmit to Ingress Dispatch on match
129      */
130     public Flow createIngressClassifierFilterEthNshFlow(NodeId nodeId) {
131         MatchBuilder match = new MatchBuilder();
132         OpenFlow13Utils.addMatchEthNsh(match);
133
134         List<Action> actionList = new ArrayList<>();
135         actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.LPORT_DISPATCHER_TABLE,
136             actionList.size()));
137
138         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
139         String flowIdStr = INGRESS_CLASSIFIER_FILTER_ETHNSH_FLOW_NAME + nodeId.getValue();
140
141         return OpenFlow13Utils.createFlowBuilder(NwConstants.INGRESS_SFC_CLASSIFIER_FILTER_TABLE,
142             INGRESS_CLASSIFIER_FILTER_NSH_PRIORITY, INGRESS_CLASSIFIER_FILTER_COOKIE,
143             INGRESS_CLASSIFIER_FILTER_ETHNSH_FLOW_NAME, flowIdStr, match, isb).build();
144     }
145
146     /*
147      * Ingress Classifier Filter No NSH flow:
148      *     Only allows Non-NSH packets to proceed in the classifier
149      *     Match Any (NSH not present), Goto Classifier ACL table
150      */
151     public Flow createIngressClassifierFilterNoNshFlow(NodeId nodeId) {
152         // MatchAny
153         MatchBuilder match = new MatchBuilder();
154
155         InstructionsBuilder isb = OpenFlow13Utils.appendGotoTableInstruction(new InstructionsBuilder(),
156             NwConstants.INGRESS_SFC_CLASSIFIER_ACL_TABLE);
157         String flowIdStr = INGRESS_CLASSIFIER_FILTER_NONSH_FLOW_NAME + nodeId.getValue();
158
159         return OpenFlow13Utils.createFlowBuilder(NwConstants.INGRESS_SFC_CLASSIFIER_FILTER_TABLE,
160             INGRESS_CLASSIFIER_FILTER_NONSH_PRIORITY, INGRESS_CLASSIFIER_FILTER_COOKIE,
161             INGRESS_CLASSIFIER_FILTER_NONSH_FLOW_NAME, flowIdStr, match, isb).build();
162     }
163
164     /*
165      * Ingress Classifier ACL flow:
166      *     Performs the ACL classification, and sends packets to Ingress Dispatcher
167      *     Match on inport (corresponds to Neutron NW/tenant), Push NSH, init(nsp, nsi, C1, C2),
168      *     set SFFIp in Reg0, and resubmit to Ingress Dispatcher to be sent down the rest of
169      *     the pipeline
170      */
171     public Flow createIngressClassifierAclFlow(NodeId nodeId, MatchBuilder match, Long port, String sffIpStr,
172             long nsp, short nsi) {
173         OpenFlow13Utils.addMatchInPort(match, nodeId, port);
174
175         Long ipl = InetAddresses.coerceToInteger(InetAddresses.forString(sffIpStr)) & 0xffffffffL;
176         List<Action> actionList = new ArrayList<>();
177         actionList.add(OpenFlow13Utils.createActionNxPushNsh(actionList.size()));
178         actionList.add(OpenFlow13Utils.createActionNxLoadNshMdtype(NSH_MDTYPE_ONE, actionList.size()));
179         actionList.add(OpenFlow13Utils.createActionNxLoadNsp((int) nsp, actionList.size()));
180         actionList.add(OpenFlow13Utils.createActionNxLoadNsi(nsi, actionList.size()));
181         actionList.add(OpenFlow13Utils.createActionNxLoadNshc1(DEFAULT_NSH_CONTEXT_VALUE, actionList.size()));
182         actionList.add(OpenFlow13Utils.createActionNxLoadNshc2(DEFAULT_NSH_CONTEXT_VALUE, actionList.size()));
183         actionList.add(OpenFlow13Utils.createActionNxLoadReg0(ipl, actionList.size()));
184         actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.LPORT_DISPATCHER_TABLE,
185             actionList.size()));
186
187         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
188
189         // The flowIdStr needs to be unique, so the best way to make it unique is to use the match
190         String flowIdStr = INGRESS_CLASSIFIER_ACL_FLOW_NAME + "_" + nodeId.getValue() + match.build().toString();
191
192         return OpenFlow13Utils.createFlowBuilder(NwConstants.INGRESS_SFC_CLASSIFIER_ACL_TABLE,
193             INGRESS_CLASSIFIER_ACL_PRIORITY, INGRESS_CLASSIFIER_ACL_COOKIE, INGRESS_CLASSIFIER_ACL_FLOW_NAME,
194             flowIdStr, match, isb).build();
195
196     }
197
198     //
199     // Internal EgressFlow util methods
200     //
201
202     /*
203      * Egress Classifier Filter Vxgpe NSH flow:
204      *     Only allows NSH packets to proceed in the egress classifier
205      *     Match on NSP, Goto table Egress Classifier NextHop on match
206      */
207     public Flow createEgressClassifierFilterVxgpeNshFlow(NodeId nodeId) {
208         MatchBuilder match = new MatchBuilder();
209         OpenFlow13Utils.addMatchVxgpeNsh(match);
210
211         InstructionsBuilder isb = OpenFlow13Utils.appendGotoTableInstruction(new InstructionsBuilder(),
212             NwConstants.EGRESS_SFC_CLASSIFIER_NEXTHOP_TABLE);
213         String flowIdStr = EGRESS_CLASSIFIER_FILTER_VXGPENSH_FLOW_NAME + nodeId.getValue();
214
215         return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_FILTER_TABLE,
216             EGRESS_CLASSIFIER_FILTER_NSH_PRIORITY, EGRESS_CLASSIFIER_FILTER_COOKIE,
217             EGRESS_CLASSIFIER_FILTER_VXGPENSH_FLOW_NAME, flowIdStr, match, isb).build();
218     }
219
220     /*
221      * Egress Classifier Filter Eth NSH flow:
222      *     Only allows NSH packets to proceed in the egress classifier
223      *     Match on NSP, Goto table Egress Classifier NextHop on match
224      */
225     public Flow createEgressClassifierFilterEthNshFlow(NodeId nodeId) {
226         MatchBuilder match = new MatchBuilder();
227         OpenFlow13Utils.addMatchEthNsh(match);
228
229         InstructionsBuilder isb = OpenFlow13Utils.appendGotoTableInstruction(new InstructionsBuilder(),
230             NwConstants.EGRESS_SFC_CLASSIFIER_NEXTHOP_TABLE);
231         String flowIdStr = EGRESS_CLASSIFIER_FILTER_ETHNSH_FLOW_NAME + nodeId.getValue();
232
233         return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_FILTER_TABLE,
234             EGRESS_CLASSIFIER_FILTER_NSH_PRIORITY, EGRESS_CLASSIFIER_FILTER_COOKIE,
235             EGRESS_CLASSIFIER_FILTER_ETHNSH_FLOW_NAME, flowIdStr, match, isb).build();
236     }
237
238     /*
239      * Egress Classifier Filter No NSH flow:
240      *     Only allows NSH packets to proceed in the egress classifier
241      *     MatchAny (NSH not present), Resubmit to Egress Dispatcher
242      *     since the packet is not for SFC
243      */
244     public Flow createEgressClassifierFilterNoNshFlow(NodeId nodeId) {
245         MatchBuilder match = new MatchBuilder();
246
247         List<Action> actionList = new ArrayList<>();
248         actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE,
249             actionList.size()));
250
251         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
252         String flowIdStr = EGRESS_CLASSIFIER_FILTER_NONSH_FLOW_NAME + nodeId.getValue();
253
254         return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_FILTER_TABLE,
255             EGRESS_CLASSIFIER_FILTER_NONSH_PRIORITY, EGRESS_CLASSIFIER_FILTER_COOKIE,
256             EGRESS_CLASSIFIER_FILTER_NONSH_FLOW_NAME, flowIdStr, match, isb).build();
257     }
258
259     /*
260      * Egress Classifier NextHop No C1/C2 flow:
261      *     Set C1/C2 accordingly
262      *     Match [C1, C2] == [0, 0], Move [TunIpv4Dst, TunVnid] to [C1, C2],
263      *     Move Reg0 (SFF IP) to TunIpv4Dst, and goto Egress Classifier
264      *     Transport Egress table on match
265      */
266     public Flow createEgressClassifierNextHopNoC1C2Flow(NodeId nodeId) {
267         MatchBuilder match = new MatchBuilder();
268         OpenFlow13Utils.addMatchNshNsc1(match, DEFAULT_NSH_CONTEXT_VALUE);
269         OpenFlow13Utils.addMatchNshNsc2(match, DEFAULT_NSH_CONTEXT_VALUE);
270
271         List<Action> actionList = new ArrayList<>();
272         actionList.add(OpenFlow13Utils.createActionNxMoveTunIpv4DstToNsc1Register(actionList.size()));
273         actionList.add(OpenFlow13Utils.createActionNxMoveTunIdToNsc2Register(actionList.size()));
274         actionList.add(OpenFlow13Utils.createActionNxMoveReg0ToTunIpv4Dst(actionList.size()));
275
276         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
277         OpenFlow13Utils.appendGotoTableInstruction(isb, NwConstants.EGRESS_SFC_CLASSIFIER_EGRESS_TABLE);
278         String flowIdStr = EGRESS_CLASSIFIER_NEXTHOP_NOC1C2_FLOW_NAME + nodeId.getValue();
279
280         return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_NEXTHOP_TABLE,
281             EGRESS_CLASSIFIER_NEXTHOP_NOC1C2_PRIORITY, EGRESS_CLASSIFIER_NEXTHOP_COOKIE,
282             EGRESS_CLASSIFIER_NEXTHOP_NOC1C2_FLOW_NAME, flowIdStr, match, isb).build();
283     }
284
285     /*
286      * Egress Classifier NextHop with C1/C2 flow:
287      *     Set C1/C2 accordingly
288      *     MatchAny (C1, C2 already set) goto Egress Classifier
289      *     Transport Egress table
290      */
291     public Flow createEgressClassifierNextHopC1C2Flow(NodeId nodeId) {
292         MatchBuilder match = new MatchBuilder();
293
294         InstructionsBuilder isb = OpenFlow13Utils.appendGotoTableInstruction(new InstructionsBuilder(),
295             NwConstants.EGRESS_SFC_CLASSIFIER_EGRESS_TABLE);
296         String flowIdStr = EGRESS_CLASSIFIER_NEXTHOP_C1C2_FLOW_NAME + nodeId.getValue();
297
298         return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_NEXTHOP_TABLE,
299             EGRESS_CLASSIFIER_NEXTHOP_C1C2_PRIORITY, EGRESS_CLASSIFIER_NEXTHOP_COOKIE,
300             EGRESS_CLASSIFIER_NEXTHOP_C1C2_FLOW_NAME, flowIdStr, match, isb).build();
301     }
302
303     /*
304      * Egress Classifier TransportEgress Local Flow
305      *     Final egress processing and egress packets. Determines if the
306      *     packet should go to a local or remote SFF.
307      *     Match on [Nsp, TunIpv4Dst==thisNodeIp], Resubmit to Ingress
308      *     Dispatcher to be processed by SFC SFF on match
309      */
310     public Flow createEgressClassifierTransportEgressLocalFlow(NodeId nodeId, long nsp, String localIpStr) {
311         MatchBuilder match = OpenFlow13Utils.getNspMatch(nsp);
312         OpenFlow13Utils.addMatchTunIpv4Dst(match, localIpStr, DEFAULT_NETMASK);
313
314         List<Action> actionList = new ArrayList<>();
315         actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.SFC_TRANSPORT_INGRESS_TABLE,
316             actionList.size()));
317
318         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
319         String flowIdStr = EGRESS_CLASSIFIER_TPORTEGRESS_FLOW_NAME + nodeId.getValue() + "_" + nsp + "_" + localIpStr;
320
321         return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_EGRESS_TABLE,
322             EGRESS_CLASSIFIER_EGRESS_LOCAL_PRIORITY, EGRESS_CLASSIFIER_TPORTEGRESS_COOKIE,
323             EGRESS_CLASSIFIER_TPORTEGRESS_FLOW_NAME, flowIdStr, match, isb).build();
324     }
325
326     /*
327      * Egress Classifier TransportEgress Remote Flow
328      *     Final egress processing and egress packets. Determines if the
329      *     packet should go to a local or remote SFF.
330      *     Match on Nsp, Output to port to send to remote SFF on match.
331      */
332     public Flow createEgressClassifierTransportEgressRemoteFlow(NodeId nodeId, long nsp, long outport) {
333         MatchBuilder match = OpenFlow13Utils.getNspMatch(nsp);
334
335         List<Action> actionList = new ArrayList<>();
336         actionList.add(OpenFlow13Utils.createActionOutPort("output:" + outport, actionList.size()));
337
338         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
339         String flowIdStr = EGRESS_CLASSIFIER_TPORTEGRESS_FLOW_NAME + nodeId.getValue() + "_" + nsp;
340
341         return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_EGRESS_TABLE,
342             EGRESS_CLASSIFIER_EGRESS_REMOTE_PRIORITY, EGRESS_CLASSIFIER_TPORTEGRESS_COOKIE,
343             EGRESS_CLASSIFIER_TPORTEGRESS_FLOW_NAME, flowIdStr, match, isb).build();
344     }
345 }