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 java.math.BigInteger;;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.List;
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.genius.mdsalutil.MDSALDataStoreUtils;
19 import org.opendaylight.genius.mdsalutil.NwConstants;
20 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
21 import org.opendaylight.netvirt.cloudservicechain.utils.VpnServiceChainUtils;
22 import org.opendaylight.genius.mdsalutil.NWUtil;
23 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
24 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
25 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
26 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
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;
41 import com.google.common.base.Optional;
43 public class VPNServiceChainHandler implements AutoCloseable {
45 private static final Logger LOG = LoggerFactory.getLogger(VPNServiceChainHandler.class);
47 private final IMdsalApiManager mdsalManager;
48 private final DataBroker broker;
49 private final FibRpcService fibRpcService;
55 public VPNServiceChainHandler(final DataBroker db, IMdsalApiManager mdsalManager, FibRpcService fibRpcSrv) {
57 this.mdsalManager = mdsalManager;
58 this.fibRpcService = fibRpcSrv;
62 * Get RouterDistinguisher by VpnName
64 * @param vpnName Name of the VPN Instance
65 * @return the Route-Distinguisher
68 private String getRouteDistinguisher(String vpnName) {
69 InstanceIdentifier<VpnInstance> id = InstanceIdentifier.builder(VpnInstances.class)
70 .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
71 Optional<VpnInstance> vpnInstance = MDSALDataStoreUtils.read(broker, LogicalDatastoreType.CONFIGURATION, id);
73 if (vpnInstance.isPresent()) {
74 VpnInstance instance = vpnInstance.get();
75 VpnAfConfig config = instance.getIpv4Family();
76 rd = config.getRouteDistinguisher();
82 * Getting the VpnInstance from RouteDistinguisher
84 * @param rd the Route-Distinguisher
85 * @return an Object that holds the Operational info of the VPN
87 protected VpnInstanceOpDataEntry getVpnInstance(String rd) {
88 InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier.create(VpnInstanceOpData.class)
89 .child(VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd));
90 Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = MDSALDataStoreUtils.read(broker,
91 LogicalDatastoreType.OPERATIONAL, id);
92 if (vpnInstanceOpData.isPresent()) {
93 return vpnInstanceOpData.get();
99 * Returns the list of VrfEntries that belong to a VPN
101 * @param rd route distinguisher of the VPN
102 * @return the list of {@code VrfEntry}
104 private List<VrfEntry> getVrfEntries(String rd) {
106 InstanceIdentifier<VrfTables> id = VpnServiceChainUtils.buildVrfId(rd);
107 Optional<VrfTables> vrfTable = MDSALDataStoreUtils.read(broker, LogicalDatastoreType.CONFIGURATION, id);
108 if (vrfTable.isPresent()) {
109 return vrfTable.get().getVrfEntry();
111 } catch (Exception e) {
112 LOG.error("Exception: getVrfEntries", e);
118 * Programs the necessary flows in LFIB and LPortDispatcher table so that the packets coming from a
119 * given VPN are delivered to a given ServiceChain Pipeline
121 * @param vpnName Name of the VPN. Typically the UUID
122 * @param tableId Table to which the LPortDispatcher table sends the packet to (Uplink or Downlink Subsc table)
123 * @param scfTag Scf tag to the SCF to which the Vpn is linked to.
124 * @param lportTag VpnPseudo Port lportTag
125 * @param addOrRemove States if the VPN2SCF Pipeline must be installed or removed
127 public void programVpnToScfPipeline(String vpnName, short tableId, int scfTag, int lportTag, int addOrRemove) {
128 // This entries must be created in the DPN where the CGNAT is installed. Since it is not possible
129 // to know where CGNAT is located, this entries are installed in all the VPN footprint.
132 // - Match: cgnatLabel Instr: lportTag=vpnPseudoPortTag + SI=SCF + GOTO 17
134 // - Match: vpnPseudoPortTag + SI==SCF Instr: scfTag + GOTO 70
135 LOG.info("L3VPN: programSCFinVPNPipeline ({}) : Parameters VpnName:{} tableId:{} scftag:{} lportTag:{}",
136 (addOrRemove == NwConstants.ADD_FLOW) ? "Creation" : "Removal", vpnName, tableId, scfTag, lportTag);
137 VpnInstanceOpDataEntry vpnInstance = null;
139 String rd = getRouteDistinguisher(vpnName);
140 LOG.debug("Router distinguisher (rd):{}", rd);
142 vpnInstance = getVpnInstance(rd);
144 if ( vpnInstance == null ) {
145 LOG.warn("Could not find a suitable VpnInstance for Route-Distinguisher={}", rd);
149 // Find out the set of DPNs for the given VPN ID
150 Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
151 List<VrfEntry> vrfEntries = getVrfEntries(rd);
152 if (vrfEntries != null) {
153 VpnServiceChainUtils.updateVpnToLportTagMap(broker, rd, lportTag, addOrRemove);
155 for (VpnToDpnList dpnInVpn : vpnToDpnList) {
156 BigInteger dpnId = dpnInVpn.getDpnId();
157 VpnServiceChainUtils.programLFibEntriesForSCF(mdsalManager, dpnId, vrfEntries, lportTag,
160 VpnServiceChainUtils.programLPortDispatcherFlowForVpnToScf(mdsalManager, dpnId, lportTag, scfTag,
161 tableId, addOrRemove);
164 } catch (Exception ex) {
165 LOG.error("Exception: programSCFinVPNPipeline", ex);
171 * Get fake VPNPseudoPort interface name
173 * @param scfTag Service Function tag
174 * @param scsTag Service Chain tag
175 * @param lportTag Lport tag
176 * @return the fake VpnPseudoPort interface name
178 private String buildVpnPseudoPortIfName(Long dpId, int scfTag, int scsTag, int lportTag) {
179 return new StringBuilder("VpnPseudo.").append(dpId).append(NwConstants.FLOWID_SEPARATOR)
180 .append(lportTag).append(NwConstants.FLOWID_SEPARATOR)
181 .append(scfTag).append(NwConstants.FLOWID_SEPARATOR)
182 .append(scsTag).toString();
187 public void close() throws Exception {
193 * L3VPN Service chaining: It moves traffic from a ServiceChain to a L3VPN
195 * @param vpnName Vpn Instance Name. Typicall the UUID
196 * @param scfTag ServiceChainForwarding Tag
197 * @param servChainTag ServiceChain Tag
198 * @param dpnId DpnId in which the egress pseudo logical port belongs
199 * @param vpnPseudoLportTag VpnPseudo Logical port tag
200 * @param isLastServiceChain Flag stating if there is no other ServiceChain using this VpnPseudoPort
201 * @param addOrRemove States if pipeline must be installed or removed
203 public void programScfToVpnPipeline(String vpnName, int scfTag, int servChainTag, long dpnId, int vpnPseudoLportTag,
204 boolean isLastServiceChain, int addOrRemove) {
205 // These Flows must be installed in the DPN where the last SF in the ServiceChain is located
206 // + ScForwardingTable (75): (This one is created and maintained by ScHopManager)
207 // - Match: scfTag + servChainId + lportTagOfvVSF Instr: VpnPseudoPortTag + SI=L3VPN + GOTO LPortDisp
208 // And these 2 flows must be installed in all Dpns where the Vpn is present:
210 // - Match: VpnPseudoPortTag + SI==L3VPN Instr: setVpnTag + GOTO FIB
211 // + FIB (21): (one entry per VrfEntry, and it is maintained by FibManager)
212 // - Match: vrfTag==vpnTag + eth_type=IPv4 + dst_ip Instr: Output DC-GW
214 LOG.info("L3VPN: Service Chaining programScfToVpnPipeline [Started]: Parameters Vpn Name:{} ", vpnName);
215 VpnInstanceOpDataEntry vpnInstance = null;
217 String rd = getRouteDistinguisher(vpnName);
219 if (rd == null || rd.isEmpty()) {
220 LOG.debug("Router distinguisher (rd): {} associated to vpnName {} does not exists", rd, vpnName);
224 vpnInstance = getVpnInstance(rd);
225 LOG.debug("Router distinguisher (rd): {}, lportTag: {} ", rd, vpnPseudoLportTag);
226 // Find out the set of DPNs for the given VPN ID
227 if (vpnInstance != null) {
229 if ( addOrRemove == NwConstants.ADD_FLOW
230 || (addOrRemove == NwConstants.DEL_FLOW && isLastServiceChain) ) {
232 Long vpnId = vpnInstance.getVpnId();
233 List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
234 if ( vpnToDpnList != null ) {
235 List<BigInteger> dpns = new ArrayList<BigInteger>();
236 for (VpnToDpnList dpnInVpn : vpnToDpnList ) {
237 dpns.add(dpnInVpn.getDpnId());
239 if ( !dpns.contains(dpnId) ) {
240 LOG.debug("Dpn {} is not included in the current VPN Footprint", dpnId);
241 dpns.add(BigInteger.valueOf(dpnId));
243 for ( BigInteger dpn : dpns ) {
244 VpnServiceChainUtils.programLPortDispatcherFlowForScfToVpn(mdsalManager, vpnId, dpn,
245 vpnPseudoLportTag, addOrRemove);
248 LOG.debug("Could not find VpnToDpn list for VPN {} with rd {}", vpnName, rd);
252 // We need to keep a fake VpnInterface in the DPN where the last vSF (before the VpnPseudoPort) is
253 // located, because in case the last real VpnInterface is removed from that DPN, we still need
254 // the Fib table programmed there
255 String intfName = buildVpnPseudoPortIfName(dpnId, scfTag, servChainTag, vpnPseudoLportTag);
256 if (addOrRemove == NwConstants.ADD_FLOW ) {
257 // Including this DPN in the VPN footprint even if there is no VpnInterface here.
258 // This is needed so that FibManager maintains the FIB table in this DPN
259 VpnServiceChainUtils.updateMappingDbs(broker, fibRpcService, vpnInstance.getVpnId(),
260 BigInteger.valueOf(dpnId), intfName, vpnName);
262 VpnServiceChainUtils.removeFromMappingDbs(broker, fibRpcService, vpnInstance.getVpnId(),
263 BigInteger.valueOf(dpnId), intfName, vpnName);
266 } catch (Exception ex) {
267 LOG.error("Exception: programScfToVpnPipeline", ex);
269 LOG.info("L3VPN: Service Chaining programScfToVpnPipeline [End]");
273 * Removes all Flows in LFIB and LPortDispatcher that are related to this VpnPseudoLPort.
275 * @param vpnInstanceName vpn Instance name
276 * @param vpnPseudoLportTag vpnPseudoLPort tag
278 public void removeVpnPseudoPortFlows(String vpnInstanceName, int vpnPseudoLportTag) {
279 // At VpnPseudoPort removal time the current Vpn footprint could not be enough, so let's try to
280 // remove all possible entries in all DPNs.
281 // TODO: Study how this could be enhanced. It could be done at ServiceChain removal, but that
282 // could imply check all ServiceChains ending in all DPNs in Vpn footprint to decide that if the entries
283 // can be removed, and that sounds even costlier than this.
285 String rd = getRouteDistinguisher(vpnInstanceName);
286 List<VrfEntry> vrfEntries = null;
288 vrfEntries = VpnServiceChainUtils.getAllVrfEntries(broker, rd);
290 boolean cleanLFib = vrfEntries != null && !vrfEntries.isEmpty();
292 List<BigInteger> operativeDPNs = NWUtil.getOperativeDPNs(broker);
293 for (BigInteger dpnId : operativeDPNs) {
295 VpnServiceChainUtils.programLFibEntriesForSCF(mdsalManager, dpnId, vrfEntries, vpnPseudoLportTag,
296 NwConstants.DEL_FLOW);
299 String vpnToScfflowRef = VpnServiceChainUtils.getL3VpnToScfLportDispatcherFlowRef(vpnPseudoLportTag);
300 Flow vpnToScfFlow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
301 .setId(new FlowId(vpnToScfflowRef)).build();
302 mdsalManager.removeFlow(dpnId, vpnToScfFlow);
303 String scfToVpnFlowRef = VpnServiceChainUtils.getScfToL3VpnLportDispatcherFlowRef(vpnPseudoLportTag);
304 Flow scfToVpnFlow = new FlowBuilder().setTableId(NwConstants.LPORT_DISPATCHER_TABLE)
305 .setId(new FlowId(scfToVpnFlowRef)).build();
306 mdsalManager.removeFlow(dpnId, scfToVpnFlow);
309 VpnServiceChainUtils.updateVpnToLportTagMap(broker, rd, vpnPseudoLportTag, NwConstants.DEL_FLOW);