2 * Copyright (c) 2015 - 2016 Ericsson India Global Services Pvt Ltd. 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.cloudservicechain;
11 import com.google.common.base.Optional;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.List;
17 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
18 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
19 import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
20 import org.opendaylight.genius.mdsalutil.MDSALUtil;
21 import org.opendaylight.genius.mdsalutil.NWUtil;
22 import org.opendaylight.genius.mdsalutil.NwConstants;
23 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
24 import org.opendaylight.netvirt.cloudservicechain.jobs.AddVpnPseudoPortDataJob;
25 import org.opendaylight.netvirt.cloudservicechain.jobs.RemoveVpnPseudoPortDataJob;
26 import org.opendaylight.netvirt.cloudservicechain.utils.VpnPseudoPortCache;
27 import org.opendaylight.netvirt.cloudservicechain.utils.VpnServiceChainUtils;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInstanceOpData;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
42 public class VPNServiceChainHandler implements AutoCloseable {
44 private static final Logger LOG = LoggerFactory.getLogger(VPNServiceChainHandler.class);
46 private final IMdsalApiManager mdsalManager;
47 private final DataBroker broker;
48 private final FibRpcService fibRpcService;
50 public VPNServiceChainHandler(final DataBroker db, IMdsalApiManager mdsalManager, FibRpcService fibRpcSrv) {
52 this.mdsalManager = mdsalManager;
53 this.fibRpcService = fibRpcSrv;
57 VpnPseudoPortCache.createVpnPseudoPortCache(broker);
61 public void close() throws Exception {
62 VpnPseudoPortCache.destroyVpnPseudoPortCache();
66 * Getting the VpnInstance from RouteDistinguisher.
68 * @param rd the Route-Distinguisher
69 * @return an Object that holds the Operational info of the VPN
71 protected VpnInstanceOpDataEntry getVpnInstance(String rd) {
72 InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier.create(VpnInstanceOpData.class)
73 .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd));
74 Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = MDSALUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
76 if (vpnInstanceOpData.isPresent()) {
77 return vpnInstanceOpData.get();
83 * Programs the necessary flows in LFIB and LPortDispatcher table so that
84 * the packets coming from a given VPN are delivered to a given
85 * ServiceChain Pipeline.
87 * @param vpnName Name of the VPN. Typically the UUID
88 * @param tableId Table to which the LPortDispatcher table sends the packet
89 * to (Uplink or Downlink Subsc table)
90 * @param scfTag Scf tag to the SCF to which the Vpn is linked to.
91 * @param lportTag VpnPseudo Port lportTag
92 * @param addOrRemove States if the VPN2SCF Pipeline must be installed or
95 public void programVpnToScfPipeline(String vpnName, short tableId, long scfTag, int lportTag, int addOrRemove) {
96 // This entries must be created in the DPN where the CGNAT is installed. Since it is not possible
97 // to know where CGNAT is located, this entries are installed in all the VPN footprint.
100 // - Match: cgnatLabel Instr: lportTag=vpnPseudoPortTag + SI=SCF + GOTO 17
102 // - Match: vpnPseudoPortTag + SI==SCF Instr: scfTag + GOTO 70
103 LOG.info("programVpnToScfPipeline ({}) : Parameters VpnName:{} tableId:{} scftag:{} lportTag:{}",
104 addOrRemove == NwConstants.ADD_FLOW ? "Creation" : "Removal", vpnName, tableId, scfTag, lportTag);
105 String rd = VpnServiceChainUtils.getVpnRd(broker, vpnName);
106 LOG.debug("Router distinguisher (rd):{}", rd);
107 if (rd == null || rd.isEmpty()) {
108 LOG.warn("programVpnToScfPipeline: Could not find Router-distinguisher for VPN {}. No further actions",
112 VpnInstanceOpDataEntry vpnInstance = getVpnInstance(rd);
113 if ( vpnInstance == null ) {
114 LOG.warn("Could not find a suitable VpnInstance for Route-Distinguisher={}", rd);
118 // Find out the set of DPNs for the given VPN ID
119 Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
120 List<VrfEntry> vrfEntries = VpnServiceChainUtils.getAllVrfEntries(broker, rd);
121 if (vrfEntries != null) {
122 AddVpnPseudoPortDataJob updateVpnToPseudoPortTask =
123 new AddVpnPseudoPortDataJob(broker, rd, lportTag, tableId, (int) scfTag);
124 DataStoreJobCoordinator.getInstance().enqueueJob(updateVpnToPseudoPortTask.getDsJobCoordinatorKey(),
125 updateVpnToPseudoPortTask);
127 for (VpnToDpnList dpnInVpn : vpnToDpnList) {
128 BigInteger dpnId = dpnInVpn.getDpnId();
129 programVpnToScfPipelineOnDpn(dpnId, vrfEntries, tableId, (int) scfTag, lportTag, addOrRemove);
134 public void programVpnToScfPipelineOnDpn(BigInteger dpnId, List<VrfEntry> vpnVrfEntries, short tableIdToGoTo,
135 int scfTag, int lportTag, int addOrRemove) {
136 VpnServiceChainUtils.programLFibEntriesForSCF(mdsalManager, dpnId, vpnVrfEntries, lportTag,
139 VpnServiceChainUtils.programLPortDispatcherFlowForVpnToScf(mdsalManager, dpnId, lportTag, scfTag,
140 tableIdToGoTo, addOrRemove);
144 * Get fake VPNPseudoPort interface name.
147 * @param scfTag Service Function tag
148 * @param scsTag Service Chain tag
149 * @param lportTag Lport tag
150 * @return the fake VpnPseudoPort interface name
152 private String buildVpnPseudoPortIfName(Long dpId, long scfTag, int scsTag, int lportTag) {
153 return new StringBuilder("VpnPseudo.").append(dpId).append(NwConstants.FLOWID_SEPARATOR)
154 .append(lportTag).append(NwConstants.FLOWID_SEPARATOR)
155 .append(scfTag).append(NwConstants.FLOWID_SEPARATOR)
156 .append(scsTag).toString();
160 * L3VPN Service chaining: It moves traffic from a ServiceChain to a L3VPN.
162 * @param vpnName Vpn Instance Name. Typicall the UUID
163 * @param scfTag ServiceChainForwarding Tag
164 * @param servChainTag ServiceChain Tag
165 * @param dpnId DpnId in which the egress pseudo logical port belongs
166 * @param vpnPseudoLportTag VpnPseudo Logical port tag
167 * @param isLastServiceChain Flag stating if there is no other ServiceChain
168 * using this VpnPseudoPort
169 * @param addOrRemove States if pipeline must be installed or removed
171 public void programScfToVpnPipeline(String vpnName, long scfTag, int servChainTag, long dpnId,
172 int vpnPseudoLportTag, boolean isLastServiceChain, int addOrRemove) {
173 // These Flows must be installed in the DPN where the last SF in the ServiceChain is located
174 // + ScForwardingTable (75): (This one is created and maintained by ScHopManager)
175 // - Match: scfTag + servChainId + lportTagOfvVSF Instr: VpnPseudoPortTag + SI=L3VPN + GOTO LPortDisp
176 // And these 2 flows must be installed in all Dpns where the Vpn is present:
178 // - Match: VpnPseudoPortTag + SI==L3VPN Instr: setVpnTag + GOTO FIB
179 // + FIB (21): (one entry per VrfEntry, and it is maintained by FibManager)
180 // - Match: vrfTag==vpnTag + eth_type=IPv4 + dst_ip Instr: Output DC-GW
182 LOG.info("L3VPN: Service Chaining programScfToVpnPipeline [Started]: Parameters Vpn Name: {} ", vpnName);
183 String rd = VpnServiceChainUtils.getVpnRd(broker, vpnName);
185 if (rd == null || rd.isEmpty()) {
186 LOG.warn("programScfToVpnPipeline: Could not find Router-distinguisher for VPN {}. No further actions",
191 VpnInstanceOpDataEntry vpnInstance = getVpnInstance(rd);
192 LOG.debug("programScfToVpnPipeline: rd={}, lportTag={} ", rd, vpnPseudoLportTag);
193 // Find out the set of DPNs for the given VPN ID
194 if (vpnInstance != null) {
196 if ( addOrRemove == NwConstants.ADD_FLOW
197 || (addOrRemove == NwConstants.DEL_FLOW && isLastServiceChain) ) {
199 Long vpnId = vpnInstance.getVpnId();
200 List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
201 if ( vpnToDpnList != null ) {
202 List<BigInteger> dpns = new ArrayList<>();
203 for (VpnToDpnList dpnInVpn : vpnToDpnList ) {
204 dpns.add(dpnInVpn.getDpnId());
206 if ( !dpns.contains(dpnId) ) {
207 LOG.debug("Dpn {} is not included in the current VPN Footprint", dpnId);
208 dpns.add(BigInteger.valueOf(dpnId));
210 for ( BigInteger dpn : dpns ) {
211 VpnServiceChainUtils.programLPortDispatcherFlowForScfToVpn(mdsalManager, vpnId, dpn,
212 vpnPseudoLportTag, addOrRemove);
215 LOG.debug("Could not find VpnToDpn list for VPN {} with rd {}", vpnName, rd);
219 // We need to keep a fake VpnInterface in the DPN where the last vSF (before the VpnPseudoPort) is
220 // located, because in case the last real VpnInterface is removed from that DPN, we still need
221 // the Fib table programmed there
222 String intfName = buildVpnPseudoPortIfName(dpnId, scfTag, servChainTag, vpnPseudoLportTag);
223 if (addOrRemove == NwConstants.ADD_FLOW ) {
224 // Including this DPN in the VPN footprint even if there is no VpnInterface here.
225 // This is needed so that FibManager maintains the FIB table in this DPN
226 VpnServiceChainUtils.updateMappingDbs(broker, fibRpcService, vpnInstance.getVpnId(),
227 BigInteger.valueOf(dpnId), intfName, vpnName);
229 VpnServiceChainUtils.removeFromMappingDbs(broker, fibRpcService, vpnInstance.getVpnId(),
230 BigInteger.valueOf(dpnId), intfName, vpnName);
233 LOG.info("L3VPN: Service Chaining programScfToVpnPipeline [End]");
237 * Removes all Flows in LFIB and LPortDispatcher that are related to this VpnPseudoLPort.
239 * @param vpnInstanceName vpn Instance name
240 * @param vpnPseudoLportTag vpnPseudoLPort tag
242 public void removeVpnPseudoPortFlows(String vpnInstanceName, int vpnPseudoLportTag) {
243 // At VpnPseudoPort removal time the current Vpn footprint could not be enough, so let's try to
244 // remove all possible entries in all DPNs.
245 // TODO: Study how this could be enhanced. It could be done at ServiceChain removal, but that
246 // could imply check all ServiceChains ending in all DPNs in Vpn footprint to decide that if the entries
247 // can be removed, and that sounds even costlier than this.
249 String rd = VpnServiceChainUtils.getVpnRd(broker, vpnInstanceName);
250 List<VrfEntry> vrfEntries = null;
252 vrfEntries = VpnServiceChainUtils.getAllVrfEntries(broker, rd);
254 boolean cleanLFib = vrfEntries != null && !vrfEntries.isEmpty();
256 List<BigInteger> operativeDPNs = NWUtil.getOperativeDPNs(broker);
257 for (BigInteger dpnId : operativeDPNs) {
259 VpnServiceChainUtils.programLFibEntriesForSCF(mdsalManager, dpnId, vrfEntries, vpnPseudoLportTag,
260 NwConstants.DEL_FLOW);
263 String vpnToScfflowRef = VpnServiceChainUtils.getL3VpnToScfLportDispatcherFlowRef(vpnPseudoLportTag);
264 Flow vpnToScfFlow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
265 .setId(new FlowId(vpnToScfflowRef)).build();
266 mdsalManager.removeFlow(dpnId, vpnToScfFlow);
267 String scfToVpnFlowRef = VpnServiceChainUtils.getScfToL3VpnLportDispatcherFlowRef(vpnPseudoLportTag);
268 Flow scfToVpnFlow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
269 .setId(new FlowId(scfToVpnFlowRef)).build();
270 mdsalManager.removeFlow(dpnId, scfToVpnFlow);
274 RemoveVpnPseudoPortDataJob removeVpnPseudoPortDataTask = new RemoveVpnPseudoPortDataJob(broker, rd);
275 DataStoreJobCoordinator.getInstance().enqueueJob(removeVpnPseudoPortDataTask.getDsJobCoordinatorKey(),
276 removeVpnPseudoPortDataTask);