NETVIRT-1630 migrate to md-sal APIs
[netvirt.git] / cloud-servicechain / impl / src / main / java / org / opendaylight / netvirt / cloudservicechain / utils / VpnServiceChainUtils.java
1 /*
2  * Copyright © 2016, 2017 Ericsson India Global Services Pvt Ltd. 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 package org.opendaylight.netvirt.cloudservicechain.utils;
9
10 import java.util.Optional;
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.stream.Collectors;
16 import org.opendaylight.mdsal.binding.api.DataBroker;
17 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
18 import org.opendaylight.mdsal.common.api.ReadFailedException;
19 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
20 import org.opendaylight.genius.mdsalutil.ActionInfo;
21 import org.opendaylight.genius.mdsalutil.FlowEntity;
22 import org.opendaylight.genius.mdsalutil.InstructionInfo;
23 import org.opendaylight.genius.mdsalutil.MDSALUtil;
24 import org.opendaylight.genius.mdsalutil.MatchInfo;
25 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
26 import org.opendaylight.genius.mdsalutil.NWUtil;
27 import org.opendaylight.genius.mdsalutil.NwConstants;
28 import org.opendaylight.genius.mdsalutil.actions.ActionPopMpls;
29 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
30 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
31 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
32 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
33 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
34 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
35 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
36 import org.opendaylight.genius.mdsalutil.matches.MatchMplsLabel;
37 import org.opendaylight.genius.utils.ServiceIndex;
38 import org.opendaylight.netvirt.cloudservicechain.CloudServiceChainConstants;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev160711.VpnToPseudoPortList;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev160711.vpn.to.pseudo.port.list.VpnToPseudoPortData;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.cloud.servicechain.state.rev160711.vpn.to.pseudo.port.list.VpnToPseudoPortDataKey;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInstanceOpData;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInstanceToVpnId;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstanceKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
66 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 public final class VpnServiceChainUtils {
71
72     private static final Logger LOG = LoggerFactory.getLogger(VpnServiceChainUtils.class);
73
74     private VpnServiceChainUtils() { }
75
76     public static BigInteger getMetadataSCF(long scfTag) { // TODO: Move to a common place
77         return new BigInteger("FF", 16).and(BigInteger.valueOf(scfTag)).shiftLeft(32);
78     }
79
80     public static BigInteger getCookieL3(int vpnId) {
81         return CloudServiceChainConstants.COOKIE_L3_BASE.add(new BigInteger("0610000", 16))
82                                                       .add(BigInteger.valueOf(vpnId));
83     }
84
85
86     public static InstanceIdentifier<VpnInstanceOpDataEntry> getVpnInstanceOpDataIdentifier(String rd) {
87         return InstanceIdentifier.builder(VpnInstanceOpData.class)
88                 .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd)).build();
89     }
90
91     public static InstanceIdentifier<BoundServices> buildBoundServicesIid(short servicePrio, String ifaceName) {
92         return InstanceIdentifier.builder(ServiceBindings.class)
93                                  .child(ServicesInfo.class, new ServicesInfoKey(ifaceName, ServiceModeIngress.class))
94                                  .child(BoundServices.class, new BoundServicesKey(servicePrio))
95                                  .build();
96     }
97
98     /**
99      * Retrieves from MDSAL the Operational Data of the VPN specified by its
100      * Route-Distinguisher.
101      *
102      * @param rd Route-Distinguisher of the VPN
103      *
104      * @return the Operational Data of the VPN or absent if no VPN could be
105      *         found for the given RD or if the VPN does not have operational
106      *         data.
107      */
108     public static Optional<VpnInstanceOpDataEntry> getVpnInstanceOpData(DataBroker broker, String rd) {
109         InstanceIdentifier<VpnInstanceOpDataEntry> id = VpnServiceChainUtils.getVpnInstanceOpDataIdentifier(rd);
110         return MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
111     }
112
113     public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
114         return InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd)).build();
115     }
116
117     public static InstanceIdentifier<VpnToDpnList> getVpnToDpnListIdentifier(String rd, BigInteger dpnId) {
118         return InstanceIdentifier.builder(VpnInstanceOpData.class)
119             .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd))
120             .child(VpnToDpnList.class, new VpnToDpnListKey(dpnId)).build();
121     }
122
123     public static InstanceIdentifier<VpnInstance> getVpnInstanceToVpnIdIdentifier(String vpnName) {
124         return InstanceIdentifier.builder(VpnInstanceToVpnId.class)
125                                  .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
126     }
127
128     public static InstanceIdentifier<VpnToPseudoPortData> getVpnToPseudoPortTagIid(String rd) {
129         VpnToPseudoPortDataKey key = new VpnToPseudoPortDataKey(rd);
130         return InstanceIdentifier.builder(VpnToPseudoPortList.class).child(VpnToPseudoPortData.class, key).build();
131     }
132
133     public static Optional<VpnToPseudoPortData> getVpnPseudoPortData(DataBroker broker, String rd)
134             throws ReadFailedException {
135         InstanceIdentifier<VpnToPseudoPortData> id = getVpnToPseudoPortTagIid(rd);
136         return SingleTransactionDataBroker.syncReadOptional(broker, LogicalDatastoreType.CONFIGURATION, id);
137     }
138
139     public static List<VpnToPseudoPortData> getAllVpnToPseudoPortData(DataBroker broker) throws ReadFailedException {
140         InstanceIdentifier<VpnToPseudoPortList> path = InstanceIdentifier.builder(VpnToPseudoPortList.class).build();
141         return SingleTransactionDataBroker.syncReadOptional(broker, LogicalDatastoreType.CONFIGURATION, path)
142                 .toJavaUtil().map(VpnToPseudoPortList::getVpnToPseudoPortData).orElse(new ArrayList<>());
143     }
144
145     /**
146      * Get fake VPNPseudoPort interface name.
147      *
148      * @param dpId Dpn Id
149      * @param scfTag Service Function tag
150      * @param scsTag Service Chain tag
151      * @param lportTag Lport tag
152      * @return the fake VpnPseudoPort interface name
153      */
154     public static String buildVpnPseudoPortIfName(Long dpId, long scfTag, int scsTag, int lportTag) {
155         return new StringBuilder("VpnPseudo.").append(dpId).append(NwConstants.FLOWID_SEPARATOR)
156                                               .append(lportTag).append(NwConstants.FLOWID_SEPARATOR)
157                                               .append(scfTag).append(NwConstants.FLOWID_SEPARATOR)
158                                               .append(scsTag).toString();
159     }
160
161     /**
162      * Retrieves the VpnId (datapath id) searching by VpnInstanceName.
163      *
164      * @param broker Reference to the MDSAL Databroker service
165      * @param vpnName The Vpn instance name
166      * @return the datapath identifier for the specified VPN
167      */
168     public static long getVpnId(DataBroker broker, String vpnName) {
169
170         InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt
171                 .l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> id =
172                 getVpnInstanceToVpnIdIdentifier(vpnName);
173         Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt
174                 .l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> vpnInstance =
175                 MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
176
177         return vpnInstance.isPresent() ? vpnInstance.get().getVpnId() : CloudServiceChainConstants.INVALID_VPN_TAG;
178     }
179
180     /**
181      * Retrieves the VPN's Route Distinguisher out from the VpnName.
182      *
183      * @param broker Reference to the MDSAL Databroker service
184      * @param vpnName The Vpn Instance Name. Typically the UUID.
185      * @return the RouteDistinguiser for the specified VPN
186      */
187     public static String getVpnRd(DataBroker broker, String vpnName) {
188         InstanceIdentifier<VpnInstance> id = getVpnInstanceToVpnIdIdentifier(vpnName);
189         return MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id).toJavaUtil().map(
190                 VpnInstance::getVrfId).orElse(null);
191     }
192
193     /**
194      * Returns all the VrfEntries that belong to a given VPN.
195      *
196      * @param broker Reference to the MDSAL Databroker service
197      * @param rd Route-distinguisher of the VPN
198      * @return the list of the matching VrfEntries
199      */
200     public static List<VrfEntry> getAllVrfEntries(DataBroker broker, String rd) {
201         InstanceIdentifier<VrfTables> vpnVrfTables =
202             InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd)).build();
203         return MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vpnVrfTables).toJavaUtil().map(
204                 VrfTables::getVrfEntry).orElse(new ArrayList<>());
205     }
206
207     /**
208      * Installs/removes a flow in LPortDispatcher table that is in charge of
209      * handling packets that falls back from SCF Pipeline to L3Vpn.
210      *
211      * @param mdsalManager  MDSAL Util API accessor
212      * @param vpnId Dataplane identifier of the VPN, the Vrf Tag.
213      * @param dpId The DPN where the flow must be installed/removed
214      * @param vpnPseudoLportTag Dataplane identifier for the VpnPseudoPort
215      * @param addOrRemove States if the flow must be created or removed
216      */
217     public static void programLPortDispatcherFlowForScfToVpn(IMdsalApiManager mdsalManager, long vpnId, BigInteger dpId,
218                                                              Integer vpnPseudoLportTag, int addOrRemove) {
219         LOG.trace("programLPortDispatcherFlowForScfToVpn: vpnId={} dpId={}, vpnPseudoLportTag={} addOrRemove={}",
220                   vpnId, dpId, vpnPseudoLportTag, addOrRemove);
221         Flow flow = buildLPortDispFromScfToL3VpnFlow(vpnId, dpId, vpnPseudoLportTag, addOrRemove);
222         if (addOrRemove == NwConstants.ADD_FLOW) {
223             mdsalManager.installFlow(dpId, flow);
224         } else {
225             mdsalManager.removeFlow(dpId, flow);
226         }
227     }
228
229     /**
230      * Build the flow that must be inserted when there is a ScHop whose
231      * egressPort is a VPN Pseudo Port. In that case, packets must be moved
232      * from the SCF to VPN Pipeline.
233      * <p>
234      * Flow matches:  VpnPseudo port lPortTag + SI=L3VPN
235      * Actions: Write vrfTag in Metadata + goto FIB Table
236      * </p>
237      * @param vpnId Dataplane identifier of the VPN, the Vrf Tag.
238      * @param dpId The DPN where the flow must be installed/removed
239      * @param lportTag Dataplane identifier for the VpnPseudoPort
240      * @param addOrRemove States if it must build a Flow to be created or
241      *     removed
242      *
243      * @return the Flow object
244      */
245     public static Flow buildLPortDispFromScfToL3VpnFlow(Long vpnId, BigInteger dpId, Integer lportTag,
246                                                         int addOrRemove) {
247         LOG.info("buildLPortDispFlowForScf vpnId={} dpId={} lportTag={} addOrRemove={} ",
248                  vpnId, dpId, lportTag, addOrRemove);
249         List<MatchInfo> matches = buildMatchOnLportTagAndSI(lportTag,
250                                                             ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
251                                                                                   NwConstants.L3VPN_SERVICE_INDEX));
252         List<Instruction> instructions = buildSetVrfTagAndGotoFibInstructions(vpnId.intValue());
253
254         String flowRef = getScfToL3VpnLportDispatcherFlowRef(lportTag);
255
256         Flow result;
257         if (addOrRemove == NwConstants.ADD_FLOW) {
258             result = MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
259                                             CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef,
260                                             0, 0, VpnServiceChainUtils.getCookieL3(vpnId.intValue()),
261                                             matches, instructions);
262
263         } else {
264             result = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
265                                       .setId(new FlowId(flowRef))
266                                       .build();
267         }
268         return result;
269     }
270
271
272     /**
273      * Builds the Match for flows that must match on a given lportTag and
274      * serviceIndex.
275      *
276      * @return the Match as a list.
277      */
278     public static List<MatchInfo> buildMatchOnLportTagAndSI(Integer lportTag, short serviceIndex) {
279         return Collections.singletonList(
280                 new MatchMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(lportTag, serviceIndex),
281                         MetaDataUtil.getMetaDataMaskForLPortDispatcher()));
282     }
283
284     /**
285      * Builds a The Instructions that sets the VpnTag in metadata and sends to
286      * FIB table.
287      *
288      * @param vpnTag Dataplane identifier of the VPN.
289      * @return the list of Instructions
290      */
291     public static List<Instruction> buildSetVrfTagAndGotoFibInstructions(Integer vpnTag) {
292         List<Instruction> result = new ArrayList<>();
293         int instructionKey = 0;
294         result.add(MDSALUtil.buildAndGetWriteMetadaInstruction(MetaDataUtil.getVpnIdMetadata(vpnTag),
295                                                                MetaDataUtil.METADATA_MASK_VRFID,
296                                                                ++instructionKey));
297         result.add(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.L3_FIB_TABLE, ++instructionKey));
298         return result;
299     }
300
301
302     /**
303      * Builds a Flow for the LFIB table that sets the LPortTag of the
304      * VpnPseudoPort and sends to LPortDispatcher table.
305      * <ul>
306      * <li>Matching: eth_type = MPLS, mpls_label = VPN MPLS label
307      * <li>Actions: setMetadata LportTag and SI=2, pop MPLS, Go to
308      *              LPortDispacherTable
309      * </ul>
310      *
311      * @param dpId  DpnId
312      * @param label MPLS label
313      * @param nextHop Next Hop IP
314      * @param lportTag Pseudo Logical Port tag
315      * @return the FlowEntity
316      */
317     public static FlowEntity buildLFibVpnPseudoPortFlow(BigInteger dpId, Long label, int etherType, String nextHop,
318                                                         int lportTag) {
319
320         List<MatchInfo> matches = new ArrayList<>();
321         matches.add(MatchEthernetType.MPLS_UNICAST);
322         matches.add(new MatchMplsLabel(label));
323
324         List<ActionInfo> actionsInfos = Collections.singletonList(new ActionPopMpls(etherType));
325         List<InstructionInfo> instructions = new ArrayList<>();
326         instructions.add(new InstructionWriteMetadata(
327                        MetaDataUtil.getMetaDataForLPortDispatcher(lportTag,
328                                                                   ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME,
329                                                                                         NwConstants.SCF_SERVICE_INDEX)),
330                        MetaDataUtil.getMetaDataMaskForLPortDispatcher()
331         ));
332
333         instructions.add(new InstructionApplyActions(actionsInfos));
334         instructions.add(new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
335         String flowRef = getLFibVpnPseudoPortFlowRef(lportTag, label, nextHop);
336         return MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef,
337                                          CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef, 0, 0,
338                                          NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
339     }
340
341
342     /**
343      * Modifies the LFIB table by adding/removing flows that redirects traffic
344      * from a VPN into the SCF via the VpnPseudoLport.
345      * Flows that match on the label and sets the VpnPseudoPort lportTag and
346      * sends to LPortDispatcher (via table 80)
347      *
348      */
349     public static void programLFibEntriesForSCF(IMdsalApiManager mdsalMgr, BigInteger dpId, List<VrfEntry> vrfEntries,
350             int lportTag, int addOrRemove) {
351         LOG.trace("programLFibEntriesForSCF:  dpId={}  lportTag={}  addOrRemove={}", dpId, lportTag, addOrRemove);
352         if (vrfEntries != null) {
353             vrfEntries.forEach(vrfEntry -> vrfEntry.getRoutePaths()
354                     .forEach(routePath -> {
355                         Long label = routePath.getLabel();
356                         String nextHop = routePath.getNexthopAddress();
357                         try {
358                             int etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
359                             FlowEntity flowEntity = buildLFibVpnPseudoPortFlow(dpId, label, etherType, nextHop,
360                                     lportTag);
361                             if (addOrRemove == NwConstants.ADD_FLOW) {
362                                 mdsalMgr.installFlow(flowEntity);
363                             } else {
364                                 mdsalMgr.removeFlow(flowEntity);
365                             }
366                             LOG.debug(
367                                     "LFIBEntry for label={}, destination={}, nexthop={} {} successfully in dpn={}",
368                                     label, vrfEntry.getDestPrefix(), nextHop,
369                                     addOrRemove == NwConstants.DEL_FLOW ? "removed" : "installed", dpId);
370                         } catch (IllegalArgumentException ex) {
371                             LOG.warn("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
372                         }
373                     }));
374         }
375     }
376
377     /**
378      * Installs/removes a flow in LPortDispatcher table that is in charge
379      * of sending the traffic to the SCF Pipeline.
380      *
381      */
382     public static void programLPortDispatcherFlowForVpnToScf(IMdsalApiManager mdsalManager, BigInteger dpId,
383                                                              int lportTag, long scfTag, short gotoTableId,
384                                                              int addOrRemove) {
385         LOG.trace("programLPortDispatcherFlowForVpnToScf: dpId={} lportTag={} scfTag={} gotoTableId={} addOrRemove={}",
386                   dpId, lportTag, scfTag, gotoTableId, addOrRemove);
387         FlowEntity flowEntity = VpnServiceChainUtils.buildLportFlowDispForVpnToScf(dpId, lportTag, scfTag, gotoTableId);
388         if (addOrRemove == NwConstants.ADD_FLOW) {
389             mdsalManager.installFlow(flowEntity);
390         } else {
391             mdsalManager.removeFlow(flowEntity);
392         }
393     }
394
395     /**
396      * Creates the flow that sends the packet from the VPN to the SCF pipeline.
397      * This usually happens when there is an ScHop whose ingressPort is a
398      * VpnPseudoPort.
399      * <ul>
400      * <li>Matches: lportTag = vpnPseudoLPortTag, SI = 1
401      * <li>Actions: setMetadata(scfTag), Go to: UpSubFilter table
402      * </ul>
403      */
404     public static FlowEntity buildLportFlowDispForVpnToScf(BigInteger dpId, int lportTag, long scfTag,
405                                                            short gotoTableId) {
406         List<InstructionInfo> instructions = new ArrayList<>();
407         List<ActionInfo> actionsInfos = new ArrayList<>();
408         actionsInfos.add(new ActionRegLoad(NxmNxReg2.class, 0, 31, scfTag));
409         instructions.add(new InstructionApplyActions(actionsInfos));
410         instructions.add(new InstructionGotoTable(gotoTableId));
411         String flowRef = getL3VpnToScfLportDispatcherFlowRef(lportTag);
412
413         List<MatchInfo> matches =
414                 buildMatchOnLportTagAndSI(lportTag, ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME,
415                         NwConstants.SCF_SERVICE_INDEX));
416         return MDSALUtil.buildFlowEntity(dpId, NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
417                                          CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef, 0, 0,
418                                          getCookieSCHop(scfTag), matches, instructions);
419
420     }
421
422     public static Optional<Long> getVpnPseudoLportTag(DataBroker broker, String rd) {
423         InstanceIdentifier<VpnToPseudoPortData> path = getVpnToPseudoPortTagIid(rd);
424         return Optional.fromJavaUtil(MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, path).toJavaUtil().map(
425                 VpnToPseudoPortData::getVpnLportTag));
426     }
427
428     /**
429      * Creates a Flow that does the trick of moving the packets from one VPN to
430      * another VPN.
431      *
432      */
433     public static Flow buildLPortDispFlowForVpntoVpn(Integer dstLportTag, Integer vpnTag) {
434         List<MatchInfo> matches =
435             buildMatchOnLportTagAndSI(dstLportTag, ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
436                                                                          NwConstants.L3VPN_SERVICE_INDEX));
437         List<Instruction> instructions = buildSetVrfTagAndGotoFibInstructions(vpnTag);
438         String flowRef = getL3VpnToL3VpnLportDispFlowRef(dstLportTag, vpnTag);
439
440         return MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
441                                       CloudServiceChainConstants.DEFAULT_LPORT_DISPATCHER_FLOW_PRIORITY, flowRef,
442                                       0, 0, VpnServiceChainUtils.getCookieL3(vpnTag),
443                                       matches, instructions);
444     }
445
446
447     private static BigInteger getCookieSCHop(long scfInstanceTag) {
448         return CloudServiceChainConstants.COOKIE_SCF_BASE.add(new BigInteger("0610000", 16))
449                 .add(BigInteger.valueOf(scfInstanceTag));
450     }
451
452     /*
453      * Id for the Flow that is inserted in LFIB that is in charge of receiving packets coming from
454      * DC-GW and setting the VpnPseudoLport tag in metadata and send to LPortDispatcher. There is
455      * one of this entries per VrfEntry.
456      */
457     private static String getLFibVpnPseudoPortFlowRef(int vpnLportTag, long label, String nextHop) {
458         return new StringBuilder(64).append(CloudServiceChainConstants.VPN_PSEUDO_PORT_FLOWID_PREFIX)
459                                     .append(NwConstants.FLOWID_SEPARATOR).append(vpnLportTag)
460                                     .append(NwConstants.FLOWID_SEPARATOR).append(label)
461                                     .append(NwConstants.FLOWID_SEPARATOR).append(nextHop).toString();
462     }
463
464     private static String getL3VpnToL3VpnLportDispFlowRef(Integer lportTag, Integer vpnTag) {
465         return new StringBuilder().append(CloudServiceChainConstants.FLOWID_PREFIX_L3).append(lportTag)
466                                   .append(NwConstants.FLOWID_SEPARATOR).append(vpnTag)
467                                   .append(NwConstants.FLOWID_SEPARATOR)
468                                   .append(CloudServiceChainConstants.DEFAULT_LPORT_DISPATCHER_FLOW_PRIORITY).toString();
469     }
470
471     /**
472      * Id for the Flow that is inserted in LPortDispatcher table that is in
473      * charge of delivering packets from the L3VPN to the SCF Pipeline.
474      * The VpnPseudoLPort tag and the SCF_SERVICE_INDEX is enough to identify
475      * this kind of flows.
476      */
477     public static String getL3VpnToScfLportDispatcherFlowRef(Integer lportTag) {
478         return new StringBuilder(64).append(CloudServiceChainConstants.VPN_PSEUDO_VPN2SCF_FLOWID_PREFIX)
479                                    .append(lportTag)
480                                    .append(NwConstants.FLOWID_SEPARATOR)
481                                    .append(ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME,
482                                                                  NwConstants.SCF_SERVICE_INDEX))
483                                    .append(NwConstants.FLOWID_SEPARATOR)
484                                    .append(CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY).toString();
485     }
486
487     /**
488      * Builds an identifier for the flow that is inserted in LPortDispatcher
489      * table and that is in charge of handling packets that are delivered from
490      * the SCF to the L3VPN Pipeline.
491      *
492      */
493     public static String getScfToL3VpnLportDispatcherFlowRef(Integer lportTag) {
494         return new StringBuilder().append(CloudServiceChainConstants.VPN_PSEUDO_SCF2VPN_FLOWID_PREFIX).append(lportTag)
495                                  .append(NwConstants.FLOWID_SEPARATOR)
496                                  .append(ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
497                                                                NwConstants.L3VPN_SERVICE_INDEX))
498                                  .append(NwConstants.FLOWID_SEPARATOR)
499                                  .append(CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY).toString();
500     }
501
502     public static List<String> getAllVpnIfaceNames(DataBroker dataBroker, String vpnName) {
503
504         String vpnRd = getVpnRd(dataBroker, vpnName);
505         InstanceIdentifier<VpnInstanceOpDataEntry> vpnOpDataIid =
506             InstanceIdentifier.builder(VpnInstanceOpData.class)
507                               .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(vpnRd)).build();
508
509         try {
510             VpnInstanceOpDataEntry vpnOpData =
511                 SingleTransactionDataBroker.syncRead(dataBroker, LogicalDatastoreType.OPERATIONAL, vpnOpDataIid);
512
513             if (vpnOpData == null) {
514                 return Collections.emptyList();
515             }
516             List<VpnToDpnList> dpnToVpns = vpnOpData.getVpnToDpnList();
517             if (dpnToVpns == null) {
518                 return Collections.emptyList();
519             }
520
521             return dpnToVpns.stream()
522                             .filter(dpn -> dpn.getVpnInterfaces() != null)
523                             .flatMap(dpn -> dpn.getVpnInterfaces().stream())
524                             .map(VpnInterfaces::getInterfaceName)
525                             .collect(Collectors.toList());
526         } catch (InterruptedException | ExecutionException e) {
527             LOG.warn("getAllVpnInterfaces for vpn {}: Failure on read operation", vpnName, e);
528             return Collections.emptyList();
529         }
530     }
531 }