f0d35c907e29bdc5687dd42c88d4217c030f2d85
[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
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;
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_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";
66
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 = ":";
73
74     public MatchBuilder getMatchBuilderFromAceMatches(Matches matches) {
75         if (matches == null) {
76             return null;
77         }
78
79         return new AclMatches(matches).buildMatch();
80     }
81
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())
89             .build();
90
91         tx.put(LogicalDatastoreType.CONFIGURATION, iidFlow, flow, WriteTransaction.CREATE_MISSING_PARENTS);
92     }
93
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())
101             .build();
102
103         tx.delete(LogicalDatastoreType.CONFIGURATION, iidFlow);
104     }
105
106     /*
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
110      */
111     public Flow createIngressClassifierFilterVxgpeNshFlow(NodeId nodeId) {
112         MatchBuilder match = new MatchBuilder();
113         OpenFlow13Utils.addMatchVxgpeNsh(match);
114
115         List<Action> actionList = new ArrayList<>();
116         actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.LPORT_DISPATCHER_TABLE,
117             actionList.size()));
118
119         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
120         String flowIdStr = INGRESS_CLASSIFIER_FILTER_VXGPENSH_FLOW_NAME + nodeId.getValue();
121
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();
125     }
126
127     /*
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
131      */
132     public Flow createIngressClassifierFilterEthNshFlow(NodeId nodeId) {
133         MatchBuilder match = new MatchBuilder();
134         OpenFlow13Utils.addMatchEthNsh(match);
135
136         List<Action> actionList = new ArrayList<>();
137         actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.LPORT_DISPATCHER_TABLE,
138             actionList.size()));
139
140         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
141         String flowIdStr = INGRESS_CLASSIFIER_FILTER_ETHNSH_FLOW_NAME + nodeId.getValue();
142
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();
146     }
147
148     /*
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
152      */
153     public Flow createIngressClassifierFilterNoNshFlow(NodeId nodeId) {
154         // MatchAny
155         MatchBuilder match = new MatchBuilder();
156
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();
160
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();
164     }
165
166     /*
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
171      *     the pipeline
172      */
173     public Flow createIngressClassifierAclFlow(NodeId nodeId, MatchBuilder match, Long port, long nsp, short nsi) {
174         OpenFlow13Utils.addMatchInPort(match, nodeId, port);
175
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,
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      * 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.
201      */
202     public Flow createIngressClassifierAclNoMatchFlow(NodeId nodeId) {
203         // This is a MatchAny flow
204         MatchBuilder match = new MatchBuilder();
205
206         List<Action> actionList = new ArrayList<>();
207         actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.LPORT_DISPATCHER_TABLE,
208                 actionList.size()));
209
210         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
211
212         String flowIdStr = INGRESS_CLASSIFIER_ACL_FLOW_NAME + "_" + nodeId.getValue();
213
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();
217     }
218
219     //
220     // Internal EgressFlow util methods
221     //
222
223     /*
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.
231      */
232     public Flow createEgressClassifierFilterNshFlow(NodeId nodeId) {
233         MatchBuilder match = new MatchBuilder();
234         OpenFlow13Utils.addMatchNshNsc1(match, ACL_FLAG_CONTEXT_VALUE);
235
236         List<Action> actionList = new ArrayList<>();
237         actionList.add(OpenFlow13Utils.createActionNxLoadNshc1(DEFAULT_NSH_CONTEXT_VALUE, actionList.size()));
238
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();
242
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();
246     }
247
248     /*
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
253      */
254     public Flow createEgressClassifierFilterNoNshFlow(NodeId nodeId) {
255         MatchBuilder match = new MatchBuilder();
256
257         List<Action> actionList = new ArrayList<>();
258         actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.EGRESS_LPORT_DISPATCHER_TABLE,
259             actionList.size()));
260
261         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
262         String flowIdStr = EGRESS_CLASSIFIER_FILTER_NONSH_FLOW_NAME + nodeId.getValue();
263
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();
267     }
268
269     /*
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
275      */
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);
280
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()));
285
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();
289
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();
293     }
294
295     /*
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
300      */
301     public Flow createEgressClassifierNextHopC1C2Flow(NodeId nodeId) {
302         MatchBuilder match = new MatchBuilder();
303
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();
307
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();
311     }
312
313     /*
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
317      */
318     public Flow createEgressClassifierTransportEgressLocalFlow(NodeId nodeId, long nsp) {
319         MatchBuilder match = OpenFlow13Utils.getNspMatch(nsp);
320
321         List<Action> actionList = new ArrayList<>();
322         actionList.add(OpenFlow13Utils.createActionResubmitTable(NwConstants.SFC_TRANSPORT_INGRESS_TABLE,
323             actionList.size()));
324
325         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
326         String flowIdStr = EGRESS_CLASSIFIER_TPORTEGRESS_FLOW_NAME + nodeId.getValue() + "_" + nsp;
327
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();
331     }
332
333     /*
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.
338      */
339     public Flow createEgressClassifierTransportEgressRemoteFlow(NodeId nodeId, long nsp, long outport,
340                                                                 String firstHopIp) {
341         MatchBuilder match = OpenFlow13Utils.getNspMatch(nsp);
342
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()));
347
348         InstructionsBuilder isb = OpenFlow13Utils.wrapActionsIntoApplyActionsInstruction(actionList);
349         String flowIdStr = EGRESS_CLASSIFIER_TPORTEGRESS_FLOW_NAME + nodeId.getValue() + "_" + nsp;
350
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();
354     }
355
356     public static Long getPortNoFromNodeConnector(String connector) {
357         /*
358          * NodeConnectorId is of the form 'openflow:dpnid:portnum'
359          */
360         return Long.valueOf(connector.split(OF_URI_SEPARATOR)[2]);
361     }
362
363     public static BigInteger getDpnIdFromNodeId(NodeId nodeId) {
364         /*
365          * NodeId is of the form 'openflow:dpnid'
366          */
367         return BigInteger.valueOf(Long.valueOf(nodeId.getValue().split(OF_URI_SEPARATOR)[1]));
368     }
369
370 }