ELAN: skip remote unicast MACs
[netvirt.git] / cloud-servicechain / 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 com.google.common.base.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.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.controller.md.sal.common.api.data.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.MDSALDataStoreUtils;
24 import org.opendaylight.genius.mdsalutil.MDSALUtil;
25 import org.opendaylight.genius.mdsalutil.MatchInfo;
26 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
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         InstanceIdentifier<VpnToPseudoPortData> id = getVpnToPseudoPortTagIid(rd);
135         return MDSALDataStoreUtils.read(broker, LogicalDatastoreType.CONFIGURATION, id);
136     }
137
138     public static List<VpnToPseudoPortData> getAllVpnToPseudoPortData(DataBroker broker) {
139         InstanceIdentifier<VpnToPseudoPortList> path = InstanceIdentifier.builder(VpnToPseudoPortList.class).build();
140         Optional<VpnToPseudoPortList> all = MDSALDataStoreUtils.read(broker, LogicalDatastoreType.CONFIGURATION, path);
141
142         return all.isPresent() ? all.get().getVpnToPseudoPortData() : 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, String nextHop, int lportTag) {
318
319         List<MatchInfo> matches = new ArrayList<>();
320         matches.add(MatchEthernetType.MPLS_UNICAST);
321         matches.add(new MatchMplsLabel(label));
322
323         List<ActionInfo> actionsInfos = Collections.singletonList(new ActionPopMpls());
324         List<InstructionInfo> instructions = new ArrayList<>();
325         instructions.add(new InstructionWriteMetadata(
326                        MetaDataUtil.getMetaDataForLPortDispatcher(lportTag,
327                                                                   ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME,
328                                                                                         NwConstants.SCF_SERVICE_INDEX)),
329                        MetaDataUtil.getMetaDataMaskForLPortDispatcher()
330         ));
331
332         instructions.add(new InstructionApplyActions(actionsInfos));
333         instructions.add(new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
334         String flowRef = getLFibVpnPseudoPortFlowRef(lportTag, label, nextHop);
335         return MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef,
336                                          CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef, 0, 0,
337                                          NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
338     }
339
340
341     /**
342      * Modifies the LFIB table by adding/removing flows that redirects traffic
343      * from a VPN into the SCF via the VpnPseudoLport.
344      * Flows that match on the label and sets the VpnPseudoPort lportTag and
345      * sends to LPortDispatcher (via table 80)
346      *
347      */
348     public static void programLFibEntriesForSCF(IMdsalApiManager mdsalMgr, BigInteger dpId, List<VrfEntry> vrfEntries,
349             int lportTag, int addOrRemove) {
350         LOG.trace("programLFibEntriesForSCF:  dpId={}  lportTag={}  addOrRemove={}", dpId, lportTag, addOrRemove);
351         if (vrfEntries != null) {
352             vrfEntries.forEach(vrfEntry -> vrfEntry.getRoutePaths()
353                     .forEach(routePath -> {
354                         Long label = routePath.getLabel();
355                         String nextHop = routePath.getNexthopAddress();
356                         FlowEntity flowEntity = buildLFibVpnPseudoPortFlow(dpId, label, nextHop, lportTag);
357                         if (addOrRemove == NwConstants.ADD_FLOW) {
358                             mdsalMgr.installFlow(flowEntity);
359                         } else {
360                             mdsalMgr.removeFlow(flowEntity);
361                         }
362                         LOG.debug(
363                                 "LFIBEntry for label={}, destination={}, nexthop={} {} successfully in dpn={}",
364                                 label, vrfEntry.getDestPrefix(), nextHop,
365                                 addOrRemove == NwConstants.DEL_FLOW ? "removed" : "installed", dpId);
366                     }));
367         }
368     }
369
370     /**
371      * Installs/removes a flow in LPortDispatcher table that is in charge
372      * of sending the traffic to the SCF Pipeline.
373      *
374      */
375     public static void programLPortDispatcherFlowForVpnToScf(IMdsalApiManager mdsalManager, BigInteger dpId,
376                                                              int lportTag, long scfTag, short gotoTableId,
377                                                              int addOrRemove) {
378         LOG.trace("programLPortDispatcherFlowForVpnToScf: dpId={} lportTag={} scfTag={} gotoTableId={} addOrRemove={}",
379                   dpId, lportTag, scfTag, gotoTableId, addOrRemove);
380         FlowEntity flowEntity = VpnServiceChainUtils.buildLportFlowDispForVpnToScf(dpId, lportTag, scfTag, gotoTableId);
381         if (addOrRemove == NwConstants.ADD_FLOW) {
382             mdsalManager.installFlow(flowEntity);
383         } else {
384             mdsalManager.removeFlow(flowEntity);
385         }
386     }
387
388     /**
389      * Creates the flow that sends the packet from the VPN to the SCF pipeline.
390      * This usually happens when there is an ScHop whose ingressPort is a
391      * VpnPseudoPort.
392      * <ul>
393      * <li>Matches: lportTag = vpnPseudoLPortTag, SI = 1
394      * <li>Actions: setMetadata(scfTag), Go to: UpSubFilter table
395      * </ul>
396      */
397     public static FlowEntity buildLportFlowDispForVpnToScf(BigInteger dpId, int lportTag, long scfTag,
398                                                            short gotoTableId) {
399         List<InstructionInfo> instructions = new ArrayList<>();
400         List<ActionInfo> actionsInfos = new ArrayList<>();
401         actionsInfos.add(new ActionRegLoad(NxmNxReg2.class, 0, 31, scfTag));
402         instructions.add(new InstructionApplyActions(actionsInfos));
403         instructions.add(new InstructionGotoTable(gotoTableId));
404         String flowRef = getL3VpnToScfLportDispatcherFlowRef(lportTag);
405
406         List<MatchInfo> matches =
407                 buildMatchOnLportTagAndSI(lportTag, ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME,
408                         NwConstants.SCF_SERVICE_INDEX));
409         return MDSALUtil.buildFlowEntity(dpId, NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
410                                          CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY, flowRef, 0, 0,
411                                          getCookieSCHop(scfTag), matches, instructions);
412
413     }
414
415     public static Optional<Long> getVpnPseudoLportTag(DataBroker broker, String rd) {
416         InstanceIdentifier<VpnToPseudoPortData> path = getVpnToPseudoPortTagIid(rd);
417         return Optional.fromJavaUtil(MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, path).toJavaUtil().map(
418                 VpnToPseudoPortData::getVpnLportTag));
419     }
420
421     /**
422      * Creates a Flow that does the trick of moving the packets from one VPN to
423      * another VPN.
424      *
425      */
426     public static Flow buildLPortDispFlowForVpntoVpn(Integer dstLportTag, Integer vpnTag) {
427         List<MatchInfo> matches =
428             buildMatchOnLportTagAndSI(dstLportTag, ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
429                                                                          NwConstants.L3VPN_SERVICE_INDEX));
430         List<Instruction> instructions = buildSetVrfTagAndGotoFibInstructions(vpnTag);
431         String flowRef = getL3VpnToL3VpnLportDispFlowRef(dstLportTag, vpnTag);
432
433         return MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, flowRef,
434                                       CloudServiceChainConstants.DEFAULT_LPORT_DISPATCHER_FLOW_PRIORITY, flowRef,
435                                       0, 0, VpnServiceChainUtils.getCookieL3(vpnTag),
436                                       matches, instructions);
437     }
438
439
440     private static BigInteger getCookieSCHop(long scfInstanceTag) {
441         return CloudServiceChainConstants.COOKIE_SCF_BASE.add(new BigInteger("0610000", 16))
442                 .add(BigInteger.valueOf(scfInstanceTag));
443     }
444
445     /*
446      * Id for the Flow that is inserted in LFIB that is in charge of receiving packets coming from
447      * DC-GW and setting the VpnPseudoLport tag in metadata and send to LPortDispatcher. There is
448      * one of this entries per VrfEntry.
449      */
450     private static String getLFibVpnPseudoPortFlowRef(int vpnLportTag, long label, String nextHop) {
451         return new StringBuilder(64).append(CloudServiceChainConstants.VPN_PSEUDO_PORT_FLOWID_PREFIX)
452                                     .append(NwConstants.FLOWID_SEPARATOR).append(vpnLportTag)
453                                     .append(NwConstants.FLOWID_SEPARATOR).append(label)
454                                     .append(NwConstants.FLOWID_SEPARATOR).append(nextHop).toString();
455     }
456
457     private static String getL3VpnToL3VpnLportDispFlowRef(Integer lportTag, Integer vpnTag) {
458         return new StringBuilder().append(CloudServiceChainConstants.FLOWID_PREFIX_L3).append(lportTag)
459                                   .append(NwConstants.FLOWID_SEPARATOR).append(vpnTag)
460                                   .append(NwConstants.FLOWID_SEPARATOR)
461                                   .append(CloudServiceChainConstants.DEFAULT_LPORT_DISPATCHER_FLOW_PRIORITY).toString();
462     }
463
464     /**
465      * Id for the Flow that is inserted in LPortDispatcher table that is in
466      * charge of delivering packets from the L3VPN to the SCF Pipeline.
467      * The VpnPseudoLPort tag and the SCF_SERVICE_INDEX is enough to identify
468      * this kind of flows.
469      */
470     public static String getL3VpnToScfLportDispatcherFlowRef(Integer lportTag) {
471         return new StringBuffer(64).append(CloudServiceChainConstants.VPN_PSEUDO_VPN2SCF_FLOWID_PREFIX).append(lportTag)
472                                    .append(NwConstants.FLOWID_SEPARATOR)
473                                    .append(ServiceIndex.getIndex(NwConstants.SCF_SERVICE_NAME,
474                                                                  NwConstants.SCF_SERVICE_INDEX))
475                                    .append(NwConstants.FLOWID_SEPARATOR)
476                                    .append(CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY).toString();
477     }
478
479     /**
480      * Builds an identifier for the flow that is inserted in LPortDispatcher
481      * table and that is in charge of handling packets that are delivered from
482      * the SCF to the L3VPN Pipeline.
483      *
484      */
485     public static String getScfToL3VpnLportDispatcherFlowRef(Integer lportTag) {
486         return new StringBuffer().append(CloudServiceChainConstants.VPN_PSEUDO_SCF2VPN_FLOWID_PREFIX).append(lportTag)
487                                  .append(NwConstants.FLOWID_SEPARATOR)
488                                  .append(ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
489                                                                NwConstants.L3VPN_SERVICE_INDEX))
490                                  .append(NwConstants.FLOWID_SEPARATOR)
491                                  .append(CloudServiceChainConstants.DEFAULT_SCF_FLOW_PRIORITY).toString();
492     }
493
494     public static List<String> getAllVpnIfaceNames(DataBroker dataBroker, String vpnName) {
495
496         String vpnRd = getVpnRd(dataBroker, vpnName);
497         InstanceIdentifier<VpnInstanceOpDataEntry> vpnOpDataIid =
498             InstanceIdentifier.builder(VpnInstanceOpData.class)
499                               .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(vpnRd)).build();
500
501         try {
502             VpnInstanceOpDataEntry vpnOpData =
503                 SingleTransactionDataBroker.syncRead(dataBroker, LogicalDatastoreType.OPERATIONAL, vpnOpDataIid);
504
505             if (vpnOpData == null) {
506                 return Collections.emptyList();
507             }
508             List<VpnToDpnList> dpnToVpns = vpnOpData.getVpnToDpnList();
509             if (dpnToVpns == null) {
510                 return Collections.emptyList();
511             }
512
513             return dpnToVpns.stream()
514                             .filter(dpn -> dpn.getVpnInterfaces() != null)
515                             .flatMap(dpn -> dpn.getVpnInterfaces().stream())
516                             .map(VpnInterfaces::getInterfaceName)
517                             .collect(Collectors.toList());
518         } catch (ReadFailedException e) {
519             LOG.warn("getAllVpnInterfaces for vpn {}: Failure on read operation", vpnName, e);
520             return Collections.emptyList();
521         }
522     }
523 }