3422953598c6ebdba0ad65f2a4d1cac7b66f6018
[netvirt.git] /
1 /*
2  * Copyright (c) 2015 - 2016 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
9 package org.opendaylight.netvirt.cloudservicechain;
10
11 import java.math.BigInteger;;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.List;
15
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;
40
41 import com.google.common.base.Optional;
42
43 public class VPNServiceChainHandler implements AutoCloseable {
44
45     private static final Logger LOG = LoggerFactory.getLogger(VPNServiceChainHandler.class);
46
47     private final IMdsalApiManager mdsalManager;
48     private final DataBroker broker;
49     private final FibRpcService fibRpcService;
50
51     /**
52      * @param mdsalManager
53      * @param db
54      */
55     public VPNServiceChainHandler(final DataBroker db, IMdsalApiManager mdsalManager, FibRpcService fibRpcSrv) {
56         this.broker = db;
57         this.mdsalManager = mdsalManager;
58         this.fibRpcService = fibRpcSrv;
59     }
60
61     /**
62      * Get RouterDistinguisher by VpnName
63      *
64      * @param vpnName Name of the VPN Instance
65      * @return the Route-Distinguisher
66      */
67
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);
72         String rd = "";
73         if (vpnInstance.isPresent()) {
74             VpnInstance instance = vpnInstance.get();
75             VpnAfConfig config = instance.getIpv4Family();
76             rd = config.getRouteDistinguisher();
77         }
78         return rd;
79     }
80
81     /**
82      * Getting the VpnInstance from RouteDistinguisher
83      *
84      * @param rd the Route-Distinguisher
85      * @return an Object that holds the Operational info of the VPN
86      */
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();
94         }
95         return null;
96     }
97
98     /**
99      * Returns the list of VrfEntries that belong to a VPN
100      *
101      * @param rd route distinguisher of the VPN
102      * @return the list of {@code VrfEntry}
103      */
104     private List<VrfEntry> getVrfEntries(String rd) {
105         try {
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();
110             }
111         } catch (Exception e) {
112             LOG.error("Exception: getVrfEntries", e);
113         }
114         return null;
115     }
116
117     /**
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
120      *
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
126      */
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.
130
131         //   LFIB:
132         //     - Match: cgnatLabel   Instr: lportTag=vpnPseudoPortTag + SI=SCF  +  GOTO 17
133         //   LportDisp:
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;
138         try {
139             String rd = getRouteDistinguisher(vpnName);
140             LOG.debug("Router distinguisher (rd):{}", rd);
141             if (!rd.isEmpty()) {
142                 vpnInstance = getVpnInstance(rd);
143             }
144             if ( vpnInstance == null ) {
145                 LOG.warn("Could not find a suitable VpnInstance for Route-Distinguisher={}", rd);
146                 return;
147             }
148
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);
154
155                 for (VpnToDpnList dpnInVpn : vpnToDpnList) {
156                     BigInteger dpnId = dpnInVpn.getDpnId();
157                     VpnServiceChainUtils.programLFibEntriesForSCF(mdsalManager, dpnId, vrfEntries, lportTag,
158                                                                   addOrRemove);
159
160                     VpnServiceChainUtils.programLPortDispatcherFlowForVpnToScf(mdsalManager, dpnId, lportTag, scfTag,
161                                                                                tableId, addOrRemove);
162                 }
163             }
164         } catch (Exception ex) {
165             LOG.error("Exception: programSCFinVPNPipeline", ex);
166         }
167     }
168
169
170     /**
171      * Get fake VPNPseudoPort interface name
172      * @param dpId Dpn Id
173      * @param scfTag Service Function tag
174      * @param scsTag Service Chain tag
175      * @param lportTag Lport tag
176      * @return the fake VpnPseudoPort interface name
177      */
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();
183     }
184
185
186     @Override
187     public void close() throws Exception {
188         // add
189     }
190
191
192     /**
193      * L3VPN Service chaining: It moves traffic from a ServiceChain to a L3VPN
194      *
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
202      */
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:
209         //   + LPortDisp (17):
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
213         //
214         LOG.info("L3VPN: Service Chaining programScfToVpnPipeline [Started]: Parameters Vpn Name:{} ", vpnName);
215         VpnInstanceOpDataEntry vpnInstance = null;
216         try {
217             String rd = getRouteDistinguisher(vpnName);
218
219             if (rd == null || rd.isEmpty()) {
220                 LOG.debug("Router distinguisher (rd): {} associated to vpnName {} does not exists", rd, vpnName);
221                 return;
222             }
223
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) {
228
229                 if ( addOrRemove == NwConstants.ADD_FLOW
230                         || (addOrRemove == NwConstants.DEL_FLOW && isLastServiceChain) ) {
231
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());
238                         }
239                         if ( !dpns.contains(dpnId) ) {
240                             LOG.debug("Dpn {} is not included in the current VPN Footprint", dpnId);
241                             dpns.add(BigInteger.valueOf(dpnId));
242                         }
243                         for ( BigInteger dpn : dpns ) {
244                             VpnServiceChainUtils.programLPortDispatcherFlowForScfToVpn(mdsalManager, vpnId, dpn,
245                                     vpnPseudoLportTag, addOrRemove);
246                         }
247                     } else {
248                         LOG.debug("Could not find VpnToDpn list for VPN {} with rd {}", vpnName, rd);
249                     }
250                 }
251
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);
261                 } else {
262                     VpnServiceChainUtils.removeFromMappingDbs(broker, fibRpcService, vpnInstance.getVpnId(),
263                                                               BigInteger.valueOf(dpnId), intfName, vpnName);
264                 }
265             }
266         } catch (Exception ex) {
267             LOG.error("Exception: programScfToVpnPipeline", ex);
268         }
269         LOG.info("L3VPN: Service Chaining programScfToVpnPipeline [End]");
270     }
271
272     /**
273      * Removes all Flows in LFIB and LPortDispatcher that are related to this VpnPseudoLPort.
274      *
275      * @param vpnInstanceName vpn Instance name
276      * @param vpnPseudoLportTag vpnPseudoLPort tag
277      */
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.
284
285         String rd = getRouteDistinguisher(vpnInstanceName);
286         List<VrfEntry> vrfEntries = null;
287         if ( rd != null ) {
288             vrfEntries = VpnServiceChainUtils.getAllVrfEntries(broker, rd);
289         }
290         boolean cleanLFib = vrfEntries != null && !vrfEntries.isEmpty();
291
292         List<BigInteger> operativeDPNs = NWUtil.getOperativeDPNs(broker);
293         for (BigInteger dpnId : operativeDPNs) {
294             if ( cleanLFib ) {
295                 VpnServiceChainUtils.programLFibEntriesForSCF(mdsalManager, dpnId, vrfEntries, vpnPseudoLportTag,
296                                                               NwConstants.DEL_FLOW);
297             }
298
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);
307         }
308
309         VpnServiceChainUtils.updateVpnToLportTagMap(broker, rd, vpnPseudoLportTag, NwConstants.DEL_FLOW);
310     }
311
312 }