Freeze upstream versions
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / intervpnlink / IVpnLinkServiceImpl.java
1 /*
2  * Copyright (c) 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.vpnmanager.intervpnlink;
9
10 import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
11
12 import com.google.common.base.Preconditions;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Objects;
19 import java.util.Optional;
20 import java.util.concurrent.ExecutionException;
21 import java.util.stream.Collectors;
22 import javax.annotation.PreDestroy;
23 import javax.inject.Inject;
24 import javax.inject.Singleton;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
27 import org.opendaylight.genius.mdsalutil.NwConstants;
28 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
29 import org.opendaylight.mdsal.binding.api.DataBroker;
30 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
31 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
32 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
33 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
34 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
35 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
36 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
37 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronUtils;
38 import org.opendaylight.netvirt.vpnmanager.VpnConstants;
39 import org.opendaylight.netvirt.vpnmanager.VpnUtil;
40 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
41 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.IVpnLinkService;
42 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
43 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.VpnMaps;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMap;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.VpnMapKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.vpnmaps.vpnmap.RouterIds;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.l3.attributes.Routes;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.l3.attributes.RoutesKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.RouterKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
63 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
64 import org.opendaylight.yangtools.yang.common.Uint32;
65 import org.opendaylight.yangtools.yang.common.Uint64;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 @Singleton
70 public class IVpnLinkServiceImpl implements IVpnLinkService, AutoCloseable {
71
72     private static final Logger LOG = LoggerFactory.getLogger(IVpnLinkServiceImpl.class);
73
74     private final DataBroker dataBroker;
75     private final ManagedNewTransactionRunner txRunner;
76     private final IdManagerService idManager;
77     private final IBgpManager bgpManager;
78     private final IFibManager fibManager;
79     private final InterVpnLinkCache interVpnLinkCache;
80     private final VpnUtil vpnUtil;
81     private final InterVpnLinkUtil interVpnLinkUtil;
82
83     @Inject
84     public IVpnLinkServiceImpl(final DataBroker dataBroker, final IdManagerService idMgr, final IBgpManager bgpMgr,
85                                final IFibManager fibMgr, final InterVpnLinkCache interVpnLinkCache,
86                                VpnUtil vpnUtil, InterVpnLinkUtil interVpnLinkUtil) {
87         this.dataBroker = dataBroker;
88         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
89         this.idManager = idMgr;
90         this.bgpManager = bgpMgr;
91         this.fibManager = fibMgr;
92         this.interVpnLinkCache = interVpnLinkCache;
93         this.vpnUtil = vpnUtil;
94         this.interVpnLinkUtil = interVpnLinkUtil;
95     }
96
97     public void start() {
98         LOG.info("{} start", getClass().getSimpleName());
99     }
100
101     @Override
102     @PreDestroy
103     public void close() {
104     }
105
106     @Override
107     public void leakRoute(String vpnName, String prefix, List<String> nextHopList, Uint32 label, int addOrRemove) {
108         LOG.trace("leakRoute: vpnName={}  prefix={}  nhList={}  label={}", vpnName, prefix, nextHopList, label);
109         Optional<InterVpnLinkDataComposite> optIVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnName);
110         if (!optIVpnLink.isPresent()) {
111             LOG.debug("Vpn {} not involved in any InterVpnLink", vpnName);
112             return;
113         }
114         leakRoute(optIVpnLink.get(), vpnName, prefix, nextHopList, label, addOrRemove);
115     }
116
117     // TODO Clean up the exception handling
118     @SuppressWarnings("checkstyle:IllegalCatch")
119     private void leakRoute(InterVpnLinkDataComposite interVpnLink, String vpnName, String prefix,
120                            List<String> nextHopList, Uint32 label, int addOrRemove) {
121
122         String dstVpnName = interVpnLink.getOtherVpnName(vpnName);
123
124         LOG.trace("leakingRoute: from VPN={} to VPN={}: prefix={}  nhList={}  label={}",
125                   vpnName, dstVpnName, prefix, nextHopList, label);
126
127         // For leaking, we need the InterVpnLink to be active.
128         if (addOrRemove == NwConstants.ADD_FLOW && !interVpnLink.isActive()) {
129             LOG.warn("Cannot leak route [prefix={}, label={}] from VPN {} to VPN {} because "
130                      + "InterVpnLink {} is not active",
131                      prefix, label, vpnName, dstVpnName, interVpnLink.getInterVpnLinkName());
132             return;
133         }
134
135         String dstVpnRd = vpnUtil.getVpnRd(dstVpnName);
136         if (addOrRemove == NwConstants.ADD_FLOW) {
137             LOG.debug("Leaking route (prefix={}, nexthop={}) from Vpn={} to Vpn={} (RD={})",
138                       prefix, nextHopList, vpnName, dstVpnName, dstVpnRd);
139             String key = dstVpnRd + VpnConstants.SEPARATOR + prefix;
140             Uint32 leakedLabel = vpnUtil.getUniqueId(VpnConstants.VPN_IDPOOL_NAME, key);
141             String leakedNexthop = interVpnLink.getEndpointIpAddr(vpnName);
142             fibManager.addOrUpdateFibEntry(dstVpnRd, null /*macAddress*/, prefix,
143                                            Collections.singletonList(leakedNexthop), VrfEntry.EncapType.Mplsgre,
144                                            leakedLabel, Uint32.ZERO /*l3vni*/, null /*gatewayMacAddress*/,
145                                            null /*parentVpnRd*/, RouteOrigin.INTERVPN, null /*writeConfigTxn*/);
146
147             List<String> ivlNexthops =
148                 interVpnLink.getEndpointDpnsByVpnName(dstVpnName).stream()
149                             .map(dpnId -> InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId))
150                             .collect(Collectors.toList());
151             try {
152                 bgpManager.advertisePrefix(dstVpnRd, null /*macAddress*/, prefix, ivlNexthops,
153                                            VrfEntry.EncapType.Mplsgre, leakedLabel, Uint32.ZERO /*l3vni*/,
154                                            Uint32.ZERO /*l2vni*/, null /*gwMacAddress*/);
155             } catch (Exception e) {
156                 LOG.error("Exception while advertising prefix {} on vpnRd {} for intervpn link", prefix, dstVpnRd, e);
157             }
158         } else {
159             LOG.debug("Removing leaked route to {} from VPN {}", prefix, dstVpnName);
160             fibManager.removeFibEntry(dstVpnRd, prefix, null, null /*writeConfigTxn*/);
161             bgpManager.withdrawPrefix(dstVpnRd, prefix);
162         }
163     }
164
165     // TODO Clean up the exception handling
166     @SuppressWarnings("checkstyle:IllegalCatch")
167     @Override
168     public void leakRoute(InterVpnLinkDataComposite interVpnLink, String srcVpnUuid, String dstVpnUuid,
169                           String prefix, Uint32 label, @Nullable RouteOrigin forcedOrigin) {
170         String ivpnLinkName = interVpnLink.getInterVpnLinkName();
171         // The source VPN must participate in the InterVpnLink
172         Preconditions.checkArgument(interVpnLink.isVpnLinked(srcVpnUuid),
173                                     "The source VPN {} does not participate in the interVpnLink {}",
174                                     srcVpnUuid, ivpnLinkName);
175         // The destination VPN must participate in the InterVpnLink
176         Preconditions.checkArgument(interVpnLink.isVpnLinked(dstVpnUuid),
177                                     "The destination VPN {} does not participate in the interVpnLink {}",
178                                     dstVpnUuid, ivpnLinkName);
179
180         String endpointIp = interVpnLink.getOtherEndpointIpAddr(dstVpnUuid);
181         String leakedOrigin = forcedOrigin != null ? forcedOrigin.getValue() : RouteOrigin.INTERVPN.getValue();
182         FibHelper.buildRoutePath(endpointIp, label);
183         VrfEntry newVrfEntry =
184             new VrfEntryBuilder().withKey(new VrfEntryKey(prefix)).setDestPrefix(prefix)
185                                  .setRoutePaths(
186                                      Collections.singletonList(FibHelper.buildRoutePath(endpointIp, label)))
187                                  .setOrigin(leakedOrigin).build();
188
189         String dstVpnRd = vpnUtil.getVpnRd(dstVpnUuid);
190         InstanceIdentifier<VrfEntry> newVrfEntryIid =
191             InstanceIdentifier.builder(FibEntries.class)
192                               .child(VrfTables.class, new VrfTablesKey(dstVpnRd))
193                               .child(VrfEntry.class, new VrfEntryKey(newVrfEntry.getDestPrefix()))
194                               .build();
195         LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx ->
196             tx.put(newVrfEntryIid, newVrfEntry)), LOG, "Error adding VRF entry {}", newVrfEntry);
197
198         // Finally, route is advertised it to the DC-GW. But while in the FibEntries the nexthop is the other
199         // endpoint's IP, in the DC-GW the nexthop for those prefixes are the IPs of those DPNs where the target
200         // VPN has been instantiated
201         List<Uint64> srcDpnList = interVpnLink.getEndpointDpnsByVpnName(srcVpnUuid);
202         List<String> nexthops =
203             srcDpnList.stream().map(dpnId -> InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId))
204                                .collect(Collectors.toList());
205
206         LOG.debug("Advertising route in VPN={} [prefix={} label={}  nexthops={}] to DC-GW",
207                   dstVpnRd, newVrfEntry.getDestPrefix(), label.intValue(), nexthops);
208         try {
209             bgpManager.advertisePrefix(dstVpnRd, null /*macAddress*/, prefix, nexthops,
210                                        VrfEntry.EncapType.Mplsgre, label, Uint32.ZERO /*l3vni*/, Uint32.ZERO /*l2vni*/,
211                                        null /*gwMacAddress*/);
212         } catch (Exception e) {
213             LOG.error("Exception while advertising prefix {} on vpnRd {} for intervpn link", prefix, dstVpnRd, e);
214         }
215     }
216
217     @Override
218     public void leakRouteIfNeeded(String vpnName, String prefix, List<String> nextHopList, Uint32 label,
219                                   RouteOrigin origin, int addOrRemove) {
220
221         Optional<InterVpnLinkDataComposite> optIVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnName);
222         if (!optIVpnLink.isPresent()) {
223             LOG.debug("Vpn {} not involved in any InterVpnLink", vpnName);
224             return;
225         }
226         InterVpnLinkDataComposite ivpnLink = optIVpnLink.get();
227         if (addOrRemove == NwConstants.ADD_FLOW && !ivpnLink.isActive()) {
228             // Note: for the removal case it is not necessary that ivpnlink is ACTIVE
229             LOG.debug("Route to {} in VPN {} cannot be leaked because InterVpnLink {} is not ACTIVE",
230                       prefix, vpnName, ivpnLink.getInterVpnLinkName());
231             return;
232         }
233
234         switch (origin) {
235             case BGP:
236                 if (!ivpnLink.isBgpRoutesLeaking()) {
237                     LOG.debug("BGP route to {} not leaked because bgp-routes-leaking is disabled", prefix);
238                     return;
239                 }
240                 leakRoute(vpnName, prefix, nextHopList, label, addOrRemove);
241                 break;
242             case STATIC:
243                 /* NOTE: There are 2 types of static routes depending on the next hop:
244                     + static route when next hop is a VM, the DC-GW or a DPNIP
245                     + static route when next hop is an Inter-VPN Link
246                  Only the 1st type should be considered since the 2nd has a special treatment */
247                 if (!ivpnLink.isStaticRoutesLeaking()) {
248                     LOG.debug("Static route to {} not leaked because static-routes-leaking is disabled", prefix);
249                     return;
250                 }
251                 leakRoute(vpnName, prefix, nextHopList, label, addOrRemove);
252                 break;
253             case CONNECTED:
254                 if (!ivpnLink.isConnectedRoutesLeaking()) {
255                     LOG.debug("Connected route to {} not leaked because connected-routes-leaking is disabled", prefix);
256                     return;
257                 }
258                 leakRoute(vpnName, prefix, nextHopList, label, addOrRemove);
259                 break;
260             default:
261                 LOG.warn("origin {} not considered in Route-leaking", origin.getValue());
262         }
263
264     }
265
266     @Override
267     public void exchangeRoutes(InterVpnLinkDataComposite ivpnLink) {
268         if (!ivpnLink.isComplete()) {
269             return;
270         }
271
272         // The type of routes to exchange depend on the leaking flags that have been activated
273         List<RouteOrigin> originsToConsider = new ArrayList<>();
274         if (ivpnLink.isBgpRoutesLeaking()) {
275             originsToConsider.add(RouteOrigin.BGP);
276         }
277         if (ivpnLink.isStaticRoutesLeaking()) {
278             originsToConsider.add(RouteOrigin.STATIC);
279         }
280         if (ivpnLink.isConnectedRoutesLeaking()) {
281             originsToConsider.add(RouteOrigin.CONNECTED);
282         }
283
284         String vpn1Uuid = ivpnLink.getFirstEndpointVpnUuid().get();
285         String vpn2Uuid = ivpnLink.getSecondEndpointVpnUuid().get();
286
287         if (! originsToConsider.isEmpty()) {
288             // 1st Endpoint ==> 2nd endpoint
289             leakRoutes(ivpnLink, vpn1Uuid, vpn2Uuid, originsToConsider);
290
291
292             // 2nd Endpoint ==> 1st endpoint
293             leakRoutes(ivpnLink, vpn2Uuid, vpn1Uuid, originsToConsider);
294         }
295
296         // Static routes in Vpn1 pointing to Vpn2's endpoint
297         leakExtraRoutesToVpnEndpoint(ivpnLink, vpn1Uuid, vpn2Uuid);
298
299         // Static routes in Vpn2 pointing to Vpn1's endpoint
300         leakExtraRoutesToVpnEndpoint(ivpnLink, vpn2Uuid, vpn1Uuid);
301     }
302
303     /*
304      * Checks if there are static routes in Vpn1 whose nexthop is Vpn2's endpoint.
305      * Those routes must be leaked to Vpn1.
306      *
307      * @param vpnLink
308      * @param vpn1Uuid
309      * @param vpn2Uuid
310      */
311     private void leakExtraRoutesToVpnEndpoint(InterVpnLinkDataComposite vpnLink, String vpn1Uuid, String vpn2Uuid) {
312
313         String vpn1Rd = vpnUtil.getVpnRd(vpn1Uuid);
314         String vpn2Endpoint = vpnLink.getOtherEndpointIpAddr(vpn2Uuid);
315         List<VrfEntry> allVpnVrfEntries = vpnUtil.getAllVrfEntries(vpn1Rd);
316         for (VrfEntry vrfEntry : allVpnVrfEntries) {
317             vrfEntry.nonnullRoutePaths().values().stream()
318                     .filter(routePath -> Objects.equals(routePath.getNexthopAddress(), vpn2Endpoint))
319                     .forEach(routePath -> {
320                         // Vpn1 has a route pointing to Vpn2's endpoint. Forcing the leaking of the route will update
321                         // the BGP accordingly
322                         Uint32 label = vpnUtil.getUniqueId(VpnConstants.VPN_IDPOOL_NAME,
323                                                          VpnUtil.getNextHopLabelKey(vpn1Rd, vrfEntry.getDestPrefix()));
324                         if (label.longValue() == VpnConstants.INVALID_LABEL) {
325                             LOG.error("Unable to fetch label from Id Manager. Bailing out of leaking extra routes for "
326                                       + "InterVpnLink {} rd {} prefix {}",
327                                       vpnLink.getInterVpnLinkName(), vpn1Rd, vrfEntry.getDestPrefix());
328                         } else {
329                             leakRoute(vpnLink, vpn2Uuid, vpn1Uuid, vrfEntry.getDestPrefix(), label,
330                                       RouteOrigin.value(vrfEntry.getOrigin()));
331                         }
332                     });
333
334         }
335     }
336
337     private void leakRoutes(InterVpnLinkDataComposite vpnLink, String srcVpnUuid, String dstVpnUuid,
338                             List<RouteOrigin> originsToConsider) {
339         String srcVpnRd = vpnUtil.getVpnRd(srcVpnUuid);
340         String dstVpnRd = vpnUtil.getVpnRd(dstVpnUuid);
341         List<VrfEntry> srcVpnRemoteVrfEntries = vpnUtil.getVrfEntriesByOrigin(srcVpnRd, originsToConsider);
342         for (VrfEntry vrfEntry : srcVpnRemoteVrfEntries) {
343             Uint32 label = vpnUtil.getUniqueId(VpnConstants.VPN_IDPOOL_NAME,
344                                              VpnUtil.getNextHopLabelKey(dstVpnRd, vrfEntry.getDestPrefix()));
345             if (label.longValue() == VpnConstants.INVALID_LABEL) {
346                 LOG.error("Unable to fetch label from Id Manager. Bailing out of leaking routes for InterVpnLink {} "
347                           + "rd {} prefix {}",
348                         vpnLink.getInterVpnLinkName(), dstVpnRd, vrfEntry.getDestPrefix());
349                 continue;
350             }
351             leakRoute(vpnLink, srcVpnUuid, dstVpnUuid, vrfEntry.getDestPrefix(), label, null /*NotForcedOrigin*/);
352         }
353     }
354
355     private Map<String, String> buildRouterXL3VPNMap() {
356         InstanceIdentifier<VpnMaps> vpnMapsIdentifier = InstanceIdentifier.builder(VpnMaps.class).build();
357         Optional<VpnMaps> optVpnMaps = Optional.empty();
358         try {
359             optVpnMaps = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
360                     vpnMapsIdentifier);
361         } catch (ExecutionException | InterruptedException e) {
362             LOG.error("buildRouterXL3VPNMap: Exception while reading VpnMaps DS", e);
363         }
364         if (!optVpnMaps.isPresent()) {
365             LOG.info("Could not retrieve VpnMaps object from Configurational DS");
366             return new HashMap<>();
367         }
368         Map<String,String> vmap = new HashMap<>();
369         final Map<VpnMapKey, VpnMap> keyVpnMapMap = optVpnMaps.get().nonnullVpnMap();
370         for (VpnMap map : keyVpnMapMap.values()) {
371             if (map.getRouterIds() == null) {
372                 continue;
373             }
374             final List<Uuid> vpnRouterIds = NeutronUtils.getVpnMapRouterIdsListUuid(
375                     new ArrayList<RouterIds>(map.nonnullRouterIds().values()));
376             for (Uuid routerId : vpnRouterIds) {
377                 if (map.getVpnId().getValue().equalsIgnoreCase(routerId.getValue())) {
378                     break; // VPN is internal
379                 }
380                 vmap.put(routerId.getValue(), map.getVpnId().getValue());
381             }
382         }
383         return vmap;
384     }
385
386
387     @Override
388     public void handleStaticRoutes(InterVpnLinkDataComposite ivpnLink) {
389         /*
390          * Checks if there are any static routes pointing to any of both
391          * InterVpnLink's endpoints. Goes through all routers checking if they have
392          * a route whose nexthop is an InterVpnLink endpoint
393          */
394
395         // Map that corresponds a routerId with the L3VPN that it's been assigned to.
396         Map<String, String> routerXL3VpnMap = buildRouterXL3VPNMap();
397
398         // Retrieving all Routers
399         InstanceIdentifier<Routers> routersIid = InstanceIdentifier.builder(Neutron.class)
400                 .child(Routers.class).build();
401         Optional<Routers> routerOpData = Optional.empty();
402         try {
403             routerOpData = SingleTransactionDataBroker.syncReadOptional(dataBroker,
404                     LogicalDatastoreType.CONFIGURATION, routersIid);
405         } catch (ExecutionException | InterruptedException e) {
406             LOG.error("handleStaticRoutes: Exception while reading routers DS", e);
407         }
408         if (!routerOpData.isPresent()) {
409             return;
410         }
411         Map<RouterKey, Router> keyRouterMap = routerOpData.get().nonnullRouter();
412         for (Router router : keyRouterMap.values()) {
413             String vpnId = routerXL3VpnMap.get(router.getUuid().getValue());
414             if (vpnId == null) {
415                 LOG.warn("Could not find suitable VPN for router {}", router.getUuid());
416                 continue; // with next router
417             }
418             Map<RoutesKey, Routes> routesKeyRoutesMap = router.nonnullRoutes();
419             if (routesKeyRoutesMap != null) {
420                 for (Routes route : routesKeyRoutesMap.values()) {
421                     handleStaticRoute(vpnId, route, ivpnLink);
422                 }
423             }
424         }
425     }
426
427     /*
428      * Takes care of an static route to see if flows related to interVpnLink
429      * must be installed in tables 20 and 17
430      *
431      * @param vpnId Vpn to which the route belongs
432      * @param route Route to handle. Will only be considered if its nexthop is the VPN's endpoint IpAddress
433      *              at the other side of the InterVpnLink
434      * @param iVpnLink
435      */
436     @SuppressWarnings("checkstyle:IllegalCatch")
437     private void handleStaticRoute(String vpnId, Routes route, InterVpnLinkDataComposite ivpnLink) {
438
439         IpAddress nhIpAddr = route.getNexthop();
440         String routeNextHop = nhIpAddr.getIpv4Address() != null ? nhIpAddr.getIpv4Address().getValue()
441                                                                   : nhIpAddr.getIpv6Address().getValue();
442         String destination = route.getDestination().stringValue();
443
444         // is nexthop the other endpoint's IP
445         String otherEndpoint = ivpnLink.getOtherEndpoint(vpnId);
446         if (!routeNextHop.equals(otherEndpoint)) {
447             LOG.debug("VPN {}: Route to {} nexthop={} points to an InterVpnLink endpoint, but its not "
448                       + "the other endpoint. Other endpoint is {}",
449                       vpnId, destination, routeNextHop, otherEndpoint);
450             return;
451         }
452
453         // Lets work: 1) write Fibentry, 2) advertise to BGP and 3) check if it must be leaked
454         String vpnRd = vpnUtil.getVpnRd(vpnId);
455         if (vpnRd == null) {
456             LOG.warn("Could not find Route-Distinguisher for VpnName {}", vpnId);
457             return;
458         }
459
460         Uint32 label = vpnUtil.getUniqueId(VpnConstants.VPN_IDPOOL_NAME,
461                                 VpnUtil.getNextHopLabelKey(vpnId, destination));
462
463         try {
464             interVpnLinkUtil.handleStaticRoute(ivpnLink, vpnId, destination, routeNextHop, label);
465         } catch (Exception e) {
466             LOG.error("Exception while advertising prefix for intervpn link", e);
467         }
468     }
469 }