2 * Copyright © 2017 Ericsson, Inc. and others. All rights reserved.
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
9 package org.opendaylight.netvirt.sfc.classifier.providers;
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;
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);
44 // Priorities for each flow
45 public static final int INGRESS_CLASSIFIER_FILTER_NSH_PRIORITY = 510;
46 public static final int INGRESS_CLASSIFIER_FILTER_NONSH_PRIORITY = 500;
47 public static final int INGRESS_CLASSIFIER_ACL_PRIORITY = 500;
48 public static final int INGRESS_CLASSIFIER_ACL_NOMATCH_PRIORITY = 10;
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;
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_NSH_FLOW_NAME = "nvsfc_egr_class_filter_nsh";
62 public static final String EGRESS_CLASSIFIER_FILTER_NONSH_FLOW_NAME = "nvsfc_egr_class_filter_nonsh";
63 public static final String EGRESS_CLASSIFIER_NEXTHOP_C1C2_FLOW_NAME = "nvsfc_egr_class_nexthop_c1c2";
64 public static final String EGRESS_CLASSIFIER_NEXTHOP_NOC1C2_FLOW_NAME = "nvsfc_egr_class_nexthop_noc1c2";
65 public static final String EGRESS_CLASSIFIER_TPORTEGRESS_FLOW_NAME = "nvsfc_egr_class_ tport egress";
67 public static final short NSH_MDTYPE_ONE = 0x01;
68 public static final short NSH_NP_ETH = 0x3;
69 public static final long DEFAULT_NSH_CONTEXT_VALUE = 0L;
70 public static final long ACL_FLAG_CONTEXT_VALUE = 0xFFFFFFL;
71 public static final long SFC_TUNNEL_ID = 0L;
72 public static final String OF_URI_SEPARATOR = ":";
74 public MatchBuilder getMatchBuilderFromAceMatches(Matches matches) {
75 if (matches == null) {
79 return new AclMatches(matches).buildMatch();
82 public void appendFlowForCreate(NodeId node, Flow flow, WriteTransaction tx) {
83 NodeKey nodeKey = new NodeKey(node);
84 InstanceIdentifier<Flow> iidFlow = InstanceIdentifier.builder(Nodes.class)
85 .child(Node.class, nodeKey)
86 .augmentation(FlowCapableNode.class)
87 .child(Table.class, new TableKey(flow.getTableId()))
88 .child(Flow.class, flow.getKey())
91 tx.put(LogicalDatastoreType.CONFIGURATION, iidFlow, flow, WriteTransaction.CREATE_MISSING_PARENTS);
94 public void appendFlowForDelete(NodeId node, Flow flow, WriteTransaction tx) {
95 NodeKey nodeKey = new NodeKey(node);
96 InstanceIdentifier<Flow> iidFlow = InstanceIdentifier.builder(Nodes.class)
97 .child(Node.class, nodeKey)
98 .augmentation(FlowCapableNode.class)
99 .child(Table.class, new TableKey(flow.getTableId()))
100 .child(Flow.class, flow.getKey())
103 tx.delete(LogicalDatastoreType.CONFIGURATION, iidFlow);
107 * Ingress Classifier Filter Vxgpe NSH flow:
108 * Only allows Non-NSH packets to proceed in the classifier
109 * Match on NSP and resubmit to Ingress Dispatch on match
111 public Flow createIngressClassifierFilterVxgpeNshFlow(NodeId nodeId) {
112 MatchBuilder match = new MatchBuilder();
113 OpenFlow13Utils.addMatchVxgpeNsh(match);
115 List<Action> actionList = new ArrayList<>();
116 actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.LPORT_DISPATCHER_TABLE,
119 InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
120 String flowIdStr = INGRESS_CLASSIFIER_FILTER_VXGPENSH_FLOW_NAME + nodeId.getValue();
122 return OpenFlow13Utils.createFlowBuilder(NwConstants.INGRESS_SFC_CLASSIFIER_FILTER_TABLE,
123 INGRESS_CLASSIFIER_FILTER_NSH_PRIORITY, INGRESS_CLASSIFIER_FILTER_COOKIE,
124 INGRESS_CLASSIFIER_FILTER_VXGPENSH_FLOW_NAME, flowIdStr, match, isb).build();
128 * Ingress Classifier Filter Eth NSH flow:
129 * Only allows Non-NSH packets to proceed in the classifier
130 * Match on NSP and resubmit to Ingress Dispatch on match
132 public Flow createIngressClassifierFilterEthNshFlow(NodeId nodeId) {
133 MatchBuilder match = new MatchBuilder();
134 OpenFlow13Utils.addMatchEthNsh(match);
136 List<Action> actionList = new ArrayList<>();
137 actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.LPORT_DISPATCHER_TABLE,
140 InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
141 String flowIdStr = INGRESS_CLASSIFIER_FILTER_ETHNSH_FLOW_NAME + nodeId.getValue();
143 return OpenFlow13Utils.createFlowBuilder(NwConstants.INGRESS_SFC_CLASSIFIER_FILTER_TABLE,
144 INGRESS_CLASSIFIER_FILTER_NSH_PRIORITY, INGRESS_CLASSIFIER_FILTER_COOKIE,
145 INGRESS_CLASSIFIER_FILTER_ETHNSH_FLOW_NAME, flowIdStr, match, isb).build();
149 * Ingress Classifier Filter No NSH flow:
150 * Only allows Non-NSH packets to proceed in the classifier
151 * Match Any (NSH not present), Goto Classifier ACL table
153 public Flow createIngressClassifierFilterNoNshFlow(NodeId nodeId) {
155 MatchBuilder match = new MatchBuilder();
157 InstructionsBuilder isb = OpenFlow13Utils.appendGotoTableInstruction(new InstructionsBuilder(),
158 NwConstants.INGRESS_SFC_CLASSIFIER_ACL_TABLE);
159 String flowIdStr = INGRESS_CLASSIFIER_FILTER_NONSH_FLOW_NAME + nodeId.getValue();
161 return OpenFlow13Utils.createFlowBuilder(NwConstants.INGRESS_SFC_CLASSIFIER_FILTER_TABLE,
162 INGRESS_CLASSIFIER_FILTER_NONSH_PRIORITY, INGRESS_CLASSIFIER_FILTER_COOKIE,
163 INGRESS_CLASSIFIER_FILTER_NONSH_FLOW_NAME, flowIdStr, match, isb).build();
167 * Ingress Classifier ACL flow:
168 * Performs the ACL classification, and sends packets to Ingress Dispatcher
169 * Match on inport (corresponds to Neutron NW/tenant), Push NSH, init(nsp, nsi, C1, C2),
170 * and resubmit to Ingress Dispatcher to be sent down the rest of
173 public Flow createIngressClassifierAclFlow(NodeId nodeId, MatchBuilder match, Long port, long nsp, short nsi) {
174 OpenFlow13Utils.addMatchInPort(match, nodeId, port);
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.createActionNxLoadNp(NSH_NP_ETH, actionList.size()));
180 actionList.add(OpenFlow13Utils.createActionNxLoadNsp((int) nsp, actionList.size()));
181 actionList.add(OpenFlow13Utils.createActionNxLoadNsi(nsi, actionList.size()));
182 actionList.add(OpenFlow13Utils.createActionNxLoadNshc1(ACL_FLAG_CONTEXT_VALUE, actionList.size()));
183 actionList.add(OpenFlow13Utils.createActionNxLoadNshc2(DEFAULT_NSH_CONTEXT_VALUE, actionList.size()));
184 actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.LPORT_DISPATCHER_TABLE,
187 InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
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();
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();
198 * Ingress Classifier ACL NoMatch flow:
199 * If there are no ACL classification matches, then resubmit back to
200 * the Ingress Dispatcher to let other services handle the packet.
202 public Flow createIngressClassifierAclNoMatchFlow(NodeId nodeId) {
203 // This is a MatchAny flow
204 MatchBuilder match = new MatchBuilder();
206 List<Action> actionList = new ArrayList<>();
207 actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.LPORT_DISPATCHER_TABLE,
210 InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
212 String flowIdStr = INGRESS_CLASSIFIER_ACL_FLOW_NAME + "_" + nodeId.getValue();
214 return OpenFlow13Utils.createFlowBuilder(NwConstants.INGRESS_SFC_CLASSIFIER_ACL_TABLE,
215 INGRESS_CLASSIFIER_ACL_NOMATCH_PRIORITY, INGRESS_CLASSIFIER_ACL_COOKIE,
216 INGRESS_CLASSIFIER_ACL_FLOW_NAME, flowIdStr, match, isb).build();
220 // Internal EgressFlow util methods
224 * Egress Classifier Filter NSH flow:
225 * Only allows NSH packets to proceed in the egress classifier
226 * Match on NSH MdType=1, Goto table Egress Classifier NextHop on match
227 * Since we need to check if the packet has passed through the classifier and has been
228 * encapsulated with NSH. We cant check for Vxgpe+NSH or Eth+NSH yet, since the outer
229 * encapsulation wont be added until the packet egresses, so instead check for NSH MD-type,
230 * which was set in the classification flow.
232 public Flow createEgressClassifierFilterNshFlow(NodeId nodeId) {
233 MatchBuilder match = new MatchBuilder();
234 OpenFlow13Utils.addMatchNshNsc1(match, ACL_FLAG_CONTEXT_VALUE);
236 List<Action> actionList = new ArrayList<>();
237 actionList.add(OpenFlow13Utils.createActionNxLoadNshc1(DEFAULT_NSH_CONTEXT_VALUE, actionList.size()));
239 InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
240 isb = OpenFlow13Utils.appendGotoTableInstruction(isb, NwConstants.EGRESS_SFC_CLASSIFIER_NEXTHOP_TABLE);
241 String flowIdStr = EGRESS_CLASSIFIER_FILTER_NSH_FLOW_NAME + nodeId.getValue();
243 return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_FILTER_TABLE,
244 EGRESS_CLASSIFIER_FILTER_NSH_PRIORITY, EGRESS_CLASSIFIER_FILTER_COOKIE,
245 EGRESS_CLASSIFIER_FILTER_NSH_FLOW_NAME, flowIdStr, match, isb).build();
249 * Egress Classifier Filter No NSH flow:
250 * Only allows NSH packets to proceed in the egress classifier
251 * MatchAny (NSH not present), Resubmit to Egress Dispatcher
252 * since the packet is not for SFC
254 public Flow createEgressClassifierFilterNoNshFlow(NodeId nodeId) {
255 MatchBuilder match = new MatchBuilder();
257 List<Action> actionList = new ArrayList<>();
258 actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE,
261 InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
262 String flowIdStr = EGRESS_CLASSIFIER_FILTER_NONSH_FLOW_NAME + nodeId.getValue();
264 return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_FILTER_TABLE,
265 EGRESS_CLASSIFIER_FILTER_NONSH_PRIORITY, EGRESS_CLASSIFIER_FILTER_COOKIE,
266 EGRESS_CLASSIFIER_FILTER_NONSH_FLOW_NAME, flowIdStr, match, isb).build();
270 * Egress Classifier NextHop No C1/C2 flow:
271 * Set C1/C2 accordingly
272 * Match [C1, C2] == [0, 0], Move [TunIpv4Dst, TunVnid] to [C1, C2],
273 * Move Reg0 (SFF IP) to TunIpv4Dst, and goto Egress Classifier
274 * Transport Egress table on match
276 public Flow createEgressClassifierNextHopNoC1C2Flow(NodeId nodeId) {
277 MatchBuilder match = new MatchBuilder();
278 OpenFlow13Utils.addMatchNshNsc1(match, DEFAULT_NSH_CONTEXT_VALUE);
279 OpenFlow13Utils.addMatchNshNsc2(match, DEFAULT_NSH_CONTEXT_VALUE);
281 List<Action> actionList = new ArrayList<>();
282 actionList.add(OpenFlow13Utils.createActionNxMoveReg0ToNsc1Register(actionList.size()));
283 actionList.add(OpenFlow13Utils.createActionNxMoveTunIdToNsc2Register(actionList.size()));
284 actionList.add(OpenFlow13Utils.createActionNxLoadTunId(SFC_TUNNEL_ID, actionList.size()));
286 InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
287 OpenFlow13Utils.appendGotoTableInstruction(isb, NwConstants.EGRESS_SFC_CLASSIFIER_EGRESS_TABLE);
288 String flowIdStr = EGRESS_CLASSIFIER_NEXTHOP_NOC1C2_FLOW_NAME + nodeId.getValue();
290 return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_NEXTHOP_TABLE,
291 EGRESS_CLASSIFIER_NEXTHOP_NOC1C2_PRIORITY, EGRESS_CLASSIFIER_NEXTHOP_COOKIE,
292 EGRESS_CLASSIFIER_NEXTHOP_NOC1C2_FLOW_NAME, flowIdStr, match, isb).build();
296 * Egress Classifier NextHop with C1/C2 flow:
297 * Set C1/C2 accordingly
298 * MatchAny (C1, C2 already set) goto Egress Classifier
299 * Transport Egress table
301 public Flow createEgressClassifierNextHopC1C2Flow(NodeId nodeId) {
302 MatchBuilder match = new MatchBuilder();
304 InstructionsBuilder isb = OpenFlow13Utils.appendGotoTableInstruction(new InstructionsBuilder(),
305 NwConstants.EGRESS_SFC_CLASSIFIER_EGRESS_TABLE);
306 String flowIdStr = EGRESS_CLASSIFIER_NEXTHOP_C1C2_FLOW_NAME + nodeId.getValue();
308 return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_NEXTHOP_TABLE,
309 EGRESS_CLASSIFIER_NEXTHOP_C1C2_PRIORITY, EGRESS_CLASSIFIER_NEXTHOP_COOKIE,
310 EGRESS_CLASSIFIER_NEXTHOP_C1C2_FLOW_NAME, flowIdStr, match, isb).build();
314 * Egress Classifier TransportEgress Local Flow
315 * Final egress processing and egress packets. Resubmit to Ingress
316 * Dispatcher to be processed by SFC SFF on match
318 public Flow createEgressClassifierTransportEgressLocalFlow(NodeId nodeId, long nsp) {
319 MatchBuilder match = OpenFlow13Utils.getNspMatch(nsp);
321 List<Action> actionList = new ArrayList<>();
322 actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.SFC_TRANSPORT_INGRESS_TABLE,
325 InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
326 String flowIdStr = EGRESS_CLASSIFIER_TPORTEGRESS_FLOW_NAME + nodeId.getValue() + "_" + nsp;
328 return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_EGRESS_TABLE,
329 EGRESS_CLASSIFIER_EGRESS_LOCAL_PRIORITY, EGRESS_CLASSIFIER_TPORTEGRESS_COOKIE,
330 EGRESS_CLASSIFIER_TPORTEGRESS_FLOW_NAME, flowIdStr, match, isb).build();
334 * Egress Classifier TransportEgress Remote Flow
335 * Final egress processing and egress packets. Determines if the
336 * packet should go to a local or remote SFF.
337 * Match on Nsp, Output to port to send to remote SFF on match.
339 public Flow createEgressClassifierTransportEgressRemoteFlow(NodeId nodeId, long nsp, long outport,
341 MatchBuilder match = OpenFlow13Utils.getNspMatch(nsp);
343 Long ipl = InetAddresses.coerceToInteger(InetAddresses.forString(firstHopIp)) & 0xffffffffL;
344 List<Action> actionList = new ArrayList<>();
345 actionList.add(OpenFlow13Utils.createActionNxLoadTunIpv4Dst(ipl, actionList.size()));
346 actionList.add(OpenFlow13Utils.createActionOutPort("output:" + outport, actionList.size()));
348 InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
349 String flowIdStr = EGRESS_CLASSIFIER_TPORTEGRESS_FLOW_NAME + nodeId.getValue() + "_" + nsp;
351 return OpenFlow13Utils.createFlowBuilder(NwConstants.EGRESS_SFC_CLASSIFIER_EGRESS_TABLE,
352 EGRESS_CLASSIFIER_EGRESS_REMOTE_PRIORITY, EGRESS_CLASSIFIER_TPORTEGRESS_COOKIE,
353 EGRESS_CLASSIFIER_TPORTEGRESS_FLOW_NAME, flowIdStr, match, isb).build();
356 public static Long getPortNoFromNodeConnector(String connector) {
358 * NodeConnectorId is of the form 'openflow:dpnid:portnum'
360 return Long.valueOf(connector.split(OF_URI_SEPARATOR)[2]);
363 public static BigInteger getDpnIdFromNodeId(NodeId nodeId) {
365 * NodeId is of the form 'openflow:dpnid'
367 return BigInteger.valueOf(Long.valueOf(nodeId.getValue().split(OF_URI_SEPARATOR)[1]));