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