2 * Copyright (c) 2017 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
8 package org.opendaylight.netvirt.vpnmanager.intervpnlink;
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
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;
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.infra.ManagedNewTransactionRunner;
28 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
29 import org.opendaylight.genius.mdsalutil.NwConstants;
30 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
31 import org.opendaylight.mdsal.binding.api.DataBroker;
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;
70 public class IVpnLinkServiceImpl implements IVpnLinkService, AutoCloseable {
72 private static final Logger LOG = LoggerFactory.getLogger(IVpnLinkServiceImpl.class);
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;
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;
98 LOG.info("{} start", getClass().getSimpleName());
103 public void close() {
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);
114 leakRoute(optIVpnLink.get(), vpnName, prefix, nextHopList, label, addOrRemove);
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) {
122 String dstVpnName = interVpnLink.getOtherVpnName(vpnName);
124 LOG.trace("leakingRoute: from VPN={} to VPN={}: prefix={} nhList={} label={}",
125 vpnName, dstVpnName, prefix, nextHopList, label);
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());
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*/);
147 List<String> ivlNexthops =
148 interVpnLink.getEndpointDpnsByVpnName(dstVpnName).stream()
149 .map(dpnId -> InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId))
150 .collect(Collectors.toList());
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);
159 LOG.debug("Removing leaked route to {} from VPN {}", prefix, dstVpnName);
160 fibManager.removeFibEntry(dstVpnRd, prefix, null, null /*writeConfigTxn*/);
161 bgpManager.withdrawPrefix(dstVpnRd, prefix);
165 // TODO Clean up the exception handling
166 @SuppressWarnings("checkstyle:IllegalCatch")
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);
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)
186 Collections.singletonList(FibHelper.buildRoutePath(endpointIp, label)))
187 .setOrigin(leakedOrigin).build();
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()))
195 LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx ->
196 tx.put(newVrfEntryIid, newVrfEntry)), LOG, "Error adding VRF entry {}", newVrfEntry);
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());
206 LOG.debug("Advertising route in VPN={} [prefix={} label={} nexthops={}] to DC-GW",
207 dstVpnRd, newVrfEntry.getDestPrefix(), label.intValue(), nexthops);
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);
218 public void leakRouteIfNeeded(String vpnName, String prefix, List<String> nextHopList, Uint32 label,
219 RouteOrigin origin, int addOrRemove) {
221 Optional<InterVpnLinkDataComposite> optIVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnName);
222 if (!optIVpnLink.isPresent()) {
223 LOG.debug("Vpn {} not involved in any InterVpnLink", vpnName);
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());
236 if (!ivpnLink.isBgpRoutesLeaking()) {
237 LOG.debug("BGP route to {} not leaked because bgp-routes-leaking is disabled", prefix);
240 leakRoute(vpnName, prefix, nextHopList, label, addOrRemove);
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);
251 leakRoute(vpnName, prefix, nextHopList, label, addOrRemove);
254 if (!ivpnLink.isConnectedRoutesLeaking()) {
255 LOG.debug("Connected route to {} not leaked because connected-routes-leaking is disabled", prefix);
258 leakRoute(vpnName, prefix, nextHopList, label, addOrRemove);
261 LOG.warn("origin {} not considered in Route-leaking", origin.getValue());
267 public void exchangeRoutes(InterVpnLinkDataComposite ivpnLink) {
268 if (!ivpnLink.isComplete()) {
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);
277 if (ivpnLink.isStaticRoutesLeaking()) {
278 originsToConsider.add(RouteOrigin.STATIC);
280 if (ivpnLink.isConnectedRoutesLeaking()) {
281 originsToConsider.add(RouteOrigin.CONNECTED);
284 String vpn1Uuid = ivpnLink.getFirstEndpointVpnUuid().get();
285 String vpn2Uuid = ivpnLink.getSecondEndpointVpnUuid().get();
287 if (! originsToConsider.isEmpty()) {
288 // 1st Endpoint ==> 2nd endpoint
289 leakRoutes(ivpnLink, vpn1Uuid, vpn2Uuid, originsToConsider);
292 // 2nd Endpoint ==> 1st endpoint
293 leakRoutes(ivpnLink, vpn2Uuid, vpn1Uuid, originsToConsider);
296 // Static routes in Vpn1 pointing to Vpn2's endpoint
297 leakExtraRoutesToVpnEndpoint(ivpnLink, vpn1Uuid, vpn2Uuid);
299 // Static routes in Vpn2 pointing to Vpn1's endpoint
300 leakExtraRoutesToVpnEndpoint(ivpnLink, vpn2Uuid, vpn1Uuid);
304 * Checks if there are static routes in Vpn1 whose nexthop is Vpn2's endpoint.
305 * Those routes must be leaked to Vpn1.
311 private void leakExtraRoutesToVpnEndpoint(InterVpnLinkDataComposite vpnLink, String vpn1Uuid, String vpn2Uuid) {
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());
329 leakRoute(vpnLink, vpn2Uuid, vpn1Uuid, vrfEntry.getDestPrefix(), label,
330 RouteOrigin.value(vrfEntry.getOrigin()));
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 {} "
348 vpnLink.getInterVpnLinkName(), dstVpnRd, vrfEntry.getDestPrefix());
351 leakRoute(vpnLink, srcVpnUuid, dstVpnUuid, vrfEntry.getDestPrefix(), label, null /*NotForcedOrigin*/);
355 private Map<String, String> buildRouterXL3VPNMap() {
356 InstanceIdentifier<VpnMaps> vpnMapsIdentifier = InstanceIdentifier.builder(VpnMaps.class).build();
357 Optional<VpnMaps> optVpnMaps = Optional.empty();
359 optVpnMaps = SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
361 } catch (ExecutionException | InterruptedException e) {
362 LOG.error("buildRouterXL3VPNMap: Exception while reading VpnMaps DS", e);
364 if (!optVpnMaps.isPresent()) {
365 LOG.info("Could not retrieve VpnMaps object from Configurational DS");
366 return new HashMap<>();
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) {
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
380 vmap.put(routerId.getValue(), map.getVpnId().getValue());
388 public void handleStaticRoutes(InterVpnLinkDataComposite ivpnLink) {
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
395 // Map that corresponds a routerId with the L3VPN that it's been assigned to.
396 Map<String, String> routerXL3VpnMap = buildRouterXL3VPNMap();
398 // Retrieving all Routers
399 InstanceIdentifier<Routers> routersIid = InstanceIdentifier.builder(Neutron.class)
400 .child(Routers.class).build();
401 Optional<Routers> routerOpData = Optional.empty();
403 routerOpData = SingleTransactionDataBroker.syncReadOptional(dataBroker,
404 LogicalDatastoreType.CONFIGURATION, routersIid);
405 } catch (ExecutionException | InterruptedException e) {
406 LOG.error("handleStaticRoutes: Exception while reading routers DS", e);
408 if (!routerOpData.isPresent()) {
411 Map<RouterKey, Router> keyRouterMap = routerOpData.get().nonnullRouter();
412 for (Router router : keyRouterMap.values()) {
413 String vpnId = routerXL3VpnMap.get(router.getUuid().getValue());
415 LOG.warn("Could not find suitable VPN for router {}", router.getUuid());
416 continue; // with next router
418 Map<RoutesKey, Routes> routesKeyRoutesMap = router.nonnullRoutes();
419 if (routesKeyRoutesMap != null) {
420 for (Routes route : routesKeyRoutesMap.values()) {
421 handleStaticRoute(vpnId, route, ivpnLink);
428 * Takes care of an static route to see if flows related to interVpnLink
429 * must be installed in tables 20 and 17
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
436 @SuppressWarnings("checkstyle:IllegalCatch")
437 private void handleStaticRoute(String vpnId, Routes route, InterVpnLinkDataComposite ivpnLink) {
439 IpAddress nhIpAddr = route.getNexthop();
440 String routeNextHop = nhIpAddr.getIpv4Address() != null ? nhIpAddr.getIpv4Address().getValue()
441 : nhIpAddr.getIpv6Address().getValue();
442 String destination = route.getDestination().stringValue();
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);
453 // Lets work: 1) write Fibentry, 2) advertise to BGP and 3) check if it must be leaked
454 String vpnRd = vpnUtil.getVpnRd(vpnId);
456 LOG.warn("Could not find Route-Distinguisher for VpnName {}", vpnId);
460 Uint32 label = vpnUtil.getUniqueId(VpnConstants.VPN_IDPOOL_NAME,
461 VpnUtil.getNextHopLabelKey(vpnId, destination));
464 interVpnLinkUtil.handleStaticRoute(ivpnLink, vpnId, destination, routeNextHop, label);
465 } catch (Exception e) {
466 LOG.error("Exception while advertising prefix for intervpn link", e);