2 * Copyright © 2015, 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.fibmanager;
10 import static org.opendaylight.genius.mdsalutil.NWUtil.isIpv4Address;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.MoreExecutors;
18 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19 import java.math.BigInteger;
20 import java.net.InetAddress;
21 import java.net.UnknownHostException;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.concurrent.Callable;
28 import java.util.concurrent.CopyOnWriteArrayList;
29 import javax.annotation.PostConstruct;
30 import javax.inject.Inject;
31 import javax.inject.Singleton;
32 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
33 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
34 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
35 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
36 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
37 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
38 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
39 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
40 import org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner;
41 import org.opendaylight.genius.mdsalutil.ActionInfo;
42 import org.opendaylight.genius.mdsalutil.FlowEntity;
43 import org.opendaylight.genius.mdsalutil.InstructionInfo;
44 import org.opendaylight.genius.mdsalutil.MDSALUtil;
45 import org.opendaylight.genius.mdsalutil.MatchInfo;
46 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
47 import org.opendaylight.genius.mdsalutil.NwConstants;
48 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
49 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
50 import org.opendaylight.genius.mdsalutil.actions.ActionPopMpls;
51 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
52 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
53 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
54 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
55 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
56 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
57 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
58 import org.opendaylight.genius.mdsalutil.matches.MatchMplsLabel;
59 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
60 import org.opendaylight.genius.utils.ServiceIndex;
61 import org.opendaylight.genius.utils.batching.SubTransaction;
62 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
63 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
64 import org.opendaylight.netvirt.elanmanager.api.IElanService;
65 import org.opendaylight.netvirt.fibmanager.NexthopManager.AdjacencyResult;
66 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
67 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
68 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
69 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
70 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
71 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
72 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.LabelRouteMap;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.RouterInterface;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRoute;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfo;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoBuilder;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoKey;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOp;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.PrefixesBuilder;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState.State;
105 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
106 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
107 import org.slf4j.Logger;
108 import org.slf4j.LoggerFactory;
112 public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry, VrfEntryListener> {
114 private static final Logger LOG = LoggerFactory.getLogger(VrfEntryListener.class);
115 private static final String FLOWID_PREFIX = "L3.";
116 private static final BigInteger COOKIE_VM_FIB_TABLE = new BigInteger("8000003", 16);
117 private static final int DEFAULT_FIB_FLOW_PRIORITY = 10;
118 private static final int IPV4_ADDR_PREFIX_LENGTH = 32;
119 private static final int LFIB_INTERVPN_PRIORITY = 15;
120 public static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
121 private static final int MAX_RETRIES = 3;
122 private static final BigInteger COOKIE_TABLE_MISS = new BigInteger("8000004", 16);
124 private final DataBroker dataBroker;
125 private final ManagedNewTransactionRunner txRunner;
126 private final RetryingManagedNewTransactionRunner retryingTxRunner;
127 private final IMdsalApiManager mdsalManager;
128 private final NexthopManager nextHopManager;
129 private final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler;
130 private final BaseVrfEntryHandler baseVrfEntryHandler;
131 private final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler;
132 private final JobCoordinator jobCoordinator;
133 private final IElanService elanManager;
134 private final FibUtil fibUtil;
135 private final InterVpnLinkCache interVpnLinkCache;
136 private final List<AutoCloseable> closeables = new CopyOnWriteArrayList<>();
139 public VrfEntryListener(final DataBroker dataBroker, final IMdsalApiManager mdsalApiManager,
140 final NexthopManager nexthopManager,
141 final IElanService elanManager,
142 final BaseVrfEntryHandler vrfEntryHandler,
143 final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler,
144 final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler,
145 final JobCoordinator jobCoordinator,
146 final FibUtil fibUtil,
147 final InterVpnLinkCache interVpnLinkCache) {
148 super(VrfEntry.class, VrfEntryListener.class);
149 this.dataBroker = dataBroker;
150 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
151 this.retryingTxRunner = new RetryingManagedNewTransactionRunner(dataBroker, MAX_RETRIES);
152 this.mdsalManager = mdsalApiManager;
153 this.nextHopManager = nexthopManager;
154 this.elanManager = elanManager;
155 this.baseVrfEntryHandler = vrfEntryHandler;
156 this.bgpRouteVrfEntryHandler = bgpRouteVrfEntryHandler;
157 this.routerInterfaceVrfEntryHandler = routerInterfaceVrfEntryHandler;
158 this.jobCoordinator = jobCoordinator;
159 this.fibUtil = fibUtil;
160 this.interVpnLinkCache = interVpnLinkCache;
166 LOG.info("{} init", getClass().getSimpleName());
167 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
171 @SuppressWarnings("checkstyle:IllegalCatch")
172 public void close() {
173 closeables.forEach(c -> {
176 } catch (Exception e) {
177 LOG.warn("Error closing {}", c, e);
183 protected VrfEntryListener getDataTreeChangeListener() {
184 return VrfEntryListener.this;
188 protected InstanceIdentifier<VrfEntry> getWildCardPath() {
189 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class).child(VrfEntry.class);
193 protected void add(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
194 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
195 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
196 LOG.debug("ADD: Adding Fib Entry rd {} prefix {} route-paths {}",
197 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
198 addFibEntries(identifier, vrfEntry, rd);
199 LOG.info("ADD: Added Fib Entry rd {} prefix {} route-paths {}",
200 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
203 //This method is temporary. Eventually Factory design pattern will be used to get
204 // right VrfEntryhandle and invoke its methods.
205 private void addFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
206 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
207 bgpRouteVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
210 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
211 LOG.info("EVPN flows need to be programmed.");
212 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
213 nextHopManager, jobCoordinator, elanManager, fibUtil);
214 evpnVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
215 closeables.add(evpnVrfEntryHandler);
218 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
219 if (routerInt != null) {
220 // ping responder for router interfaces
221 routerInterfaceVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
224 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
225 createFibEntries(identifier, vrfEntry);
231 protected void remove(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry) {
232 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
233 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
234 LOG.debug("REMOVE: Removing Fib Entry rd {} prefix {} route-paths {}",
235 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
236 removeFibEntries(identifier, vrfEntry, rd);
237 LOG.info("REMOVE: Removed Fib Entry rd {} prefix {} route-paths {}",
238 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
241 //This method is temporary. Eventually Factory design pattern will be used to get
242 // right VrfEntryhandle and invoke its methods.
243 private void removeFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
244 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
245 LOG.info("EVPN flows to be deleted");
246 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
247 nextHopManager, jobCoordinator, elanManager, fibUtil);
248 evpnVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
249 closeables.add(evpnVrfEntryHandler);
252 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
253 if (routerInt != null) {
254 // ping responder for router interfaces
255 routerInterfaceVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
258 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
259 deleteFibEntries(identifier, vrfEntry);
262 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
263 bgpRouteVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
269 // "Redundant nullcheck of originalRoutePath, which is known to be non-null" - the null checking for
270 // originalRoutePath is a little dicey - safest to keep the checking even if not needed.
271 @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE")
272 protected void update(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update) {
273 Preconditions.checkNotNull(update, "VrfEntry should not be null or empty.");
274 final String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
275 LOG.debug("UPDATE: Updating Fib Entries to rd {} prefix {} route-paths {} origin {} old-origin {}", rd,
276 update.getDestPrefix(), update.getRoutePaths(), update.getOrigin(), original.getOrigin());
277 // Handle BGP Routes first
278 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.BGP) {
279 bgpRouteVrfEntryHandler.updateFlows(identifier, original, update, rd);
280 LOG.info("UPDATE: Updated BGP advertised Fib Entry with rd {} prefix {} route-paths {}",
281 rd, update.getDestPrefix(), update.getRoutePaths());
285 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.STATIC) {
286 List<RoutePaths> originalRoutePath = original.getRoutePaths();
287 List<RoutePaths> updateRoutePath = update.getRoutePaths();
288 LOG.info("UPDATE: Original route-path {} update route-path {} ", originalRoutePath, updateRoutePath);
290 //Updates need to be handled for extraroute even if original vrf entry route path is null or
291 //updated vrf entry route path is null. This can happen during tunnel events.
292 Optional<VpnInstanceOpDataEntry> optVpnInstance = fibUtil.getVpnInstanceOpData(rd);
293 List<String> usedRds = new ArrayList<>();
294 if (optVpnInstance.isPresent()) {
295 usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,optVpnInstance.get().getVpnId(),
296 update.getDestPrefix());
298 // If original VRF Entry had nexthop null , but update VRF Entry
299 // has nexthop , route needs to be created on remote Dpns
300 if (originalRoutePath == null || originalRoutePath.isEmpty()
301 && updateRoutePath != null && !updateRoutePath.isEmpty() && usedRds.isEmpty()) {
302 // TODO(vivek): Though ugly, Not handling this code now, as each
303 // tep add event will invoke flow addition
304 LOG.trace("Original VRF entry NH is null for destprefix {}. And the prefix is not an extra route."
305 + " This event is IGNORED here.", update.getDestPrefix());
309 // If original VRF Entry had valid nexthop , but update VRF Entry
310 // has nexthop empty'ed out, route needs to be removed from remote Dpns
311 if (updateRoutePath == null || updateRoutePath.isEmpty()
312 && originalRoutePath != null && !originalRoutePath.isEmpty() && usedRds.isEmpty()) {
313 LOG.trace("Original VRF entry had valid NH for destprefix {}. And the prefix is not an extra route."
314 + "This event is IGNORED here.", update.getDestPrefix());
317 //Update the used rds and vpntoextraroute containers only for the deleted nextHops.
318 List<String> nextHopsRemoved = FibHelper.getNextHopListFromRoutePaths(original);
319 nextHopsRemoved.removeAll(FibHelper.getNextHopListFromRoutePaths(update));
320 ListenableFuture<Void> future =
321 txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> nextHopsRemoved.parallelStream()
322 .forEach(nextHopRemoved -> fibUtil.updateUsedRdAndVpnToExtraRoute(
323 tx, nextHopRemoved, rd, update.getDestPrefix())));
324 Futures.addCallback(future, new FutureCallback<Void>() {
326 public void onSuccess(Void result) {
327 createFibEntries(identifier, update);
328 LOG.info("UPDATE: Updated static Fib Entry with rd {} prefix {} route-paths {}",
329 rd, update.getDestPrefix(), update.getRoutePaths());
333 public void onFailure(Throwable throwable) {
334 LOG.error("Exception encountered while submitting operational future for update vrfentry {}",
337 }, MoreExecutors.directExecutor());
340 //Handle all other routes only on a cluster reboot
341 if (original.equals(update)) {
343 createFibEntries(identifier, update);
344 LOG.info("UPDATE: Updated Non-static Fib Entry with rd {} prefix {} route-paths {}",
345 rd, update.getDestPrefix(), update.getRoutePaths());
349 LOG.info("UPDATE: Ignoring update for FIB entry with rd {} prefix {} route-origin {} route-paths {}",
350 rd, update.getDestPrefix(), update.getOrigin(), update.getRoutePaths());
353 private void createFibEntries(final InstanceIdentifier<VrfEntry> vrfEntryIid, final VrfEntry vrfEntry) {
354 final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
355 List<SubTransaction> txnObjects = new ArrayList<>();
356 final VpnInstanceOpDataEntry vpnInstance =
357 fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
358 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
359 Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId()
360 + " has null vpnId!");
361 final Collection<VpnToDpnList> vpnToDpnList;
362 if (vrfEntry.getParentVpnRd() != null
363 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
364 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
365 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
366 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
367 vpnInstance.getVpnToDpnList();
368 LOG.info("createFibEntries: Processing creation of PNF FIB entry with rd {} prefix {}",
369 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
371 vpnToDpnList = vpnInstance.getVpnToDpnList();
373 final Long vpnId = vpnInstance.getVpnId();
374 final String rd = vrfTableKey.getRouteDistinguisher();
375 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
376 if (subnetRoute != null) {
377 final long elanTag = subnetRoute.getElantag();
378 LOG.trace("SUBNETROUTE: createFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
379 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
380 if (vpnToDpnList != null) {
381 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
382 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
383 for (final VpnToDpnList curDpn : vpnToDpnList) {
384 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
385 installSubnetRouteInFib(curDpn.getDpnId(), elanTag, rd, vpnId, vrfEntry, tx);
386 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnId.longValue(),
387 vrfEntry, NwConstants.ADD_FLOW, tx);
395 final List<BigInteger> localDpnIdList = createLocalFibEntry(vpnInstance.getVpnId(), rd, vrfEntry);
396 if (!localDpnIdList.isEmpty() && vpnToDpnList != null) {
397 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
398 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
399 synchronized (vpnInstance.getVpnInstanceName().intern()) {
400 for (VpnToDpnList vpnDpn : vpnToDpnList) {
401 if (!localDpnIdList.contains(vpnDpn.getDpnId())) {
402 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
404 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
405 bgpRouteVrfEntryHandler.createRemoteFibEntry(vpnDpn.getDpnId(),
406 vpnId, vrfTableKey.getRouteDistinguisher(), vrfEntry, tx,
409 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
410 vrfTableKey.getRouteDistinguisher(), vrfEntry, tx);
412 } catch (NullPointerException e) {
413 LOG.error("Failed to get create remote fib flows for prefix {} ",
414 vrfEntry.getDestPrefix(), e);
423 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
424 if (optVpnUuid.isPresent()) {
425 String vpnUuid = optVpnUuid.get();
426 InterVpnLinkDataComposite interVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid).orNull();
427 if (interVpnLink != null) {
428 LOG.debug("InterVpnLink {} found in Cache linking Vpn {}", interVpnLink.getInterVpnLinkName(), vpnUuid);
429 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
430 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
431 // This is an static route that points to the other endpoint of an InterVpnLink
432 // In that case, we should add another entry in FIB table pointing to LPortDispatcher table.
433 installIVpnLinkSwitchingFlows(interVpnLink, vpnUuid, vrfEntry, vpnId);
434 installInterVpnRouteInLFib(interVpnLink, vpnUuid, vrfEntry);
441 void refreshFibTables(String rd, String prefix) {
442 InstanceIdentifier<VrfEntry> vrfEntryId =
443 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
444 .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
445 Optional<VrfEntry> vrfEntry = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
446 if (vrfEntry.isPresent()) {
447 createFibEntries(vrfEntryId, vrfEntry.get());
451 private Prefixes updateVpnReferencesInLri(LabelRouteInfo lri, String vpnInstanceName, boolean isPresentInList) {
452 LOG.debug("updating LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
453 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
454 prefixBuilder.setDpnId(lri.getDpnId());
455 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
456 prefixBuilder.setIpAddress(lri.getPrefix());
457 // Increment the refCount here
458 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
459 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
460 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri);
461 if (!isPresentInList) {
462 LOG.debug("vpnName {} is not present in LRI with label {}..", vpnInstanceName, lri.getLabel());
463 List<String> vpnInstanceNames = lri.getVpnInstanceList();
464 vpnInstanceNames.add(vpnInstanceName);
465 builder.setVpnInstanceList(vpnInstanceNames);
466 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
468 LOG.debug("vpnName {} is present in LRI with label {}..", vpnInstanceName, lri.getLabel());
470 return prefixBuilder.build();
473 void installSubnetRouteInFib(final BigInteger dpnId, final long elanTag, final String rd,
474 final long vpnId, final VrfEntry vrfEntry, WriteTransaction tx) {
476 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
477 newTx -> installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, newTx)), LOG,
478 "Error installing subnet route in FIB");
481 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
482 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
483 synchronized (label.toString().intern()) {
484 LabelRouteInfo lri = getLabelRouteInfo(label);
485 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
487 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
488 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
489 fibUtil.getVpnInstanceOpData(rd);
490 if (vpnInstanceOpDataEntryOptional.isPresent()) {
491 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
492 if (!lri.getVpnInstanceList().contains(vpnInstanceName)) {
493 updateVpnReferencesInLri(lri, vpnInstanceName, false);
497 LOG.debug("SUBNETROUTE: installSubnetRouteInFib: Fetched labelRouteInfo for label {} interface {}"
498 + " and got dpn {}", label, lri.getVpnInterfaceName(), lri.getDpnId());
502 final List<InstructionInfo> instructions = new ArrayList<>();
503 BigInteger subnetRouteMeta = BigInteger.valueOf(elanTag).shiftLeft(24)
504 .or(BigInteger.valueOf(vpnId).shiftLeft(1));
505 instructions.add(new InstructionWriteMetadata(subnetRouteMeta, MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
506 instructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
507 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
508 NwConstants.ADD_FLOW, tx, null);
510 if (vrfEntry.getRoutePaths() != null) {
511 for (RoutePaths routePath : vrfEntry.getRoutePaths()) {
512 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
513 List<ActionInfo> actionsInfos = new ArrayList<>();
514 // reinitialize instructions list for LFIB Table
515 final List<InstructionInfo> LFIBinstructions = new ArrayList<>();
517 actionsInfos.add(new ActionPopMpls());
518 LFIBinstructions.add(new InstructionApplyActions(actionsInfos));
519 LFIBinstructions.add(new InstructionWriteMetadata(subnetRouteMeta,
520 MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
521 LFIBinstructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
523 makeLFibTableEntry(dpnId, routePath.getLabel(), LFIBinstructions, DEFAULT_FIB_FLOW_PRIORITY,
524 NwConstants.ADD_FLOW, tx);
530 private void installSubnetBroadcastAddrDropRule(final BigInteger dpnId, final String rd, final long vpnId,
531 final VrfEntry vrfEntry, int addOrRemove, WriteTransaction tx) {
532 List<MatchInfo> matches = new ArrayList<>();
534 LOG.debug("SUBNETROUTE: installSubnetBroadcastAddrDropRule: destPrefix {} rd {} vpnId {} dpnId {}",
535 vrfEntry.getDestPrefix(), rd, vpnId, dpnId);
536 String[] ipAddress = vrfEntry.getDestPrefix().split("/");
537 String subnetBroadcastAddr = FibUtil.getBroadcastAddressFromCidr(vrfEntry.getDestPrefix());
538 final int prefixLength = ipAddress.length == 1 ? 0 : Integer.parseInt(ipAddress[1]);
540 InetAddress destPrefix;
542 destPrefix = InetAddress.getByName(subnetBroadcastAddr);
543 } catch (UnknownHostException e) {
544 LOG.error("Failed to get destPrefix for prefix {} rd {} VpnId {} DPN {}",
545 vrfEntry.getDestPrefix(), rd, vpnId, dpnId, e);
549 // Match on VpnId and SubnetBroadCast IP address
550 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
551 matches.add(MatchEthernetType.IPV4);
553 if (prefixLength != 0) {
554 matches.add(new MatchIpv4Destination(subnetBroadcastAddr, Integer.toString(IPV4_ADDR_PREFIX_LENGTH)));
557 //Action is to drop the packet
558 List<InstructionInfo> dropInstructions = new ArrayList<>();
559 List<ActionInfo> actionsInfos = new ArrayList<>();
560 actionsInfos.add(new ActionDrop());
561 dropInstructions.add(new InstructionApplyActions(actionsInfos));
563 int priority = DEFAULT_FIB_FLOW_PRIORITY + IPV4_ADDR_PREFIX_LENGTH;
564 String flowRef = FibUtil.getFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, rd, priority, destPrefix);
565 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef, priority,
566 flowRef, 0, 0, COOKIE_TABLE_MISS, matches, dropInstructions);
568 Flow flow = flowEntity.getFlowBuilder().build();
569 String flowId = flowEntity.getFlowId();
570 FlowKey flowKey = new FlowKey(new FlowId(flowId));
571 Node nodeDpn = FibUtil.buildDpnNode(dpnId);
573 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
574 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
575 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
577 if (addOrRemove == NwConstants.ADD_FLOW) {
578 tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId,flow, true);
580 tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
585 * For a given route, it installs a flow in LFIB that sets the lportTag of the other endpoint and sends to
586 * LportDispatcher table (via table 80)
588 private void installInterVpnRouteInLFib(final InterVpnLinkDataComposite interVpnLink, final String vpnName,
589 final VrfEntry vrfEntry) {
590 // INTERVPN routes are routes in a Vpn1 that have been leaked to Vpn2. In DC-GW, this Vpn2 route is pointing
591 // to a list of DPNs where Vpn2's VpnLink was instantiated. In these DPNs LFIB must be programmed so that the
592 // packet is commuted from Vpn2 to Vpn1.
593 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
594 if (!interVpnLink.isActive()) {
595 LOG.warn("InterVpnLink {} is NOT ACTIVE. InterVpnLink flows for prefix={} wont be installed in LFIB",
596 interVpnLinkName, vrfEntry.getDestPrefix());
600 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
601 Optional<Long> optLportTag = interVpnLink.getEndpointLportTagByVpnName(vpnName);
602 if (!optLportTag.isPresent()) {
603 LOG.warn("Could not retrieve lportTag for VPN {} endpoint in InterVpnLink {}", vpnName, interVpnLinkName);
607 Long lportTag = optLportTag.get();
608 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).orElse(null);
610 LOG.error("Could not find label in vrfEntry=[prefix={} routePaths={}]. LFIB entry for InterVpnLink skipped",
611 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
614 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionPopMpls());
615 List<InstructionInfo> instructions = Arrays.asList(
616 new InstructionApplyActions(actionsInfos),
617 new InstructionWriteMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(lportTag.intValue(),
618 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
619 NwConstants.L3VPN_SERVICE_INDEX)),
620 MetaDataUtil.getMetaDataMaskForLPortDispatcher()),
621 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
622 List<String> interVpnNextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
624 for (BigInteger dpId : targetDpns) {
625 LOG.debug("Installing flow: VrfEntry=[prefix={} label={} nexthop={}] dpn {} for InterVpnLink {} in LFIB",
626 vrfEntry.getDestPrefix(), label, interVpnNextHopList, dpId, interVpnLink.getInterVpnLinkName());
628 makeLFibTableEntry(dpId, label, instructions, LFIB_INTERVPN_PRIORITY, NwConstants.ADD_FLOW,
635 * Installs the flows in FIB table that, for a given route, do the switching from one VPN to the other.
637 private void installIVpnLinkSwitchingFlows(final InterVpnLinkDataComposite interVpnLink, final String vpnUuid,
638 final VrfEntry vrfEntry, long vpnTag) {
639 Preconditions.checkNotNull(interVpnLink, "InterVpnLink cannot be null");
640 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null
641 && vrfEntry.getRoutePaths().size() == 1);
642 String destination = vrfEntry.getDestPrefix();
643 String nextHop = vrfEntry.getRoutePaths().get(0).getNexthopAddress();
644 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
646 // After having received a static route, we should check if the vpn is part of an inter-vpn-link.
647 // In that case, we should populate the FIB table of the VPN pointing to LPortDisptacher table
648 // using as metadata the LPortTag associated to that vpn in the inter-vpn-link.
649 if (interVpnLink.getState().or(State.Error) != State.Active) {
650 LOG.warn("Route to {} with nexthop={} cannot be installed because the interVpnLink {} is not active",
651 destination, nextHop, interVpnLinkName);
655 Optional<Long> optOtherEndpointLportTag = interVpnLink.getOtherEndpointLportTagByVpnName(vpnUuid);
656 if (!optOtherEndpointLportTag.isPresent()) {
657 LOG.warn("Could not find suitable LportTag for the endpoint opposite to vpn {} in interVpnLink {}",
658 vpnUuid, interVpnLinkName);
662 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnUuid);
663 if (targetDpns.isEmpty()) {
664 LOG.warn("Could not find DPNs for endpoint opposite to vpn {} in interVpnLink {}",
665 vpnUuid, interVpnLinkName);
669 String[] values = destination.split("/");
670 String destPrefixIpAddress = values[0];
671 int prefixLength = values.length == 1 ? 0 : Integer.parseInt(values[1]);
673 List<MatchInfo> matches = new ArrayList<>();
674 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnTag), MetaDataUtil.METADATA_MASK_VRFID));
675 matches.add(MatchEthernetType.IPV4);
677 if (prefixLength != 0) {
678 matches.add(new MatchIpv4Destination(destPrefixIpAddress, Integer.toString(prefixLength)));
681 List<Instruction> instructions =
682 Arrays.asList(new InstructionWriteMetadata(
683 MetaDataUtil.getMetaDataForLPortDispatcher(optOtherEndpointLportTag.get().intValue(),
684 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants
685 .L3VPN_SERVICE_INDEX)),
686 MetaDataUtil.getMetaDataMaskForLPortDispatcher()).buildInstruction(0),
687 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE).buildInstruction(1));
689 int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
690 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, destination, nextHop);
691 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef, priority, flowRef, 0, 0,
692 COOKIE_VM_FIB_TABLE, matches, instructions);
694 LOG.trace("Installing flow in FIB table for vpn {} interVpnLink {} nextHop {} key {}",
695 vpnUuid, interVpnLink.getInterVpnLinkName(), nextHop, flowRef);
697 for (BigInteger dpId : targetDpns) {
699 LOG.debug("Installing flow: VrfEntry=[prefix={} route-paths={}] dpn {} for InterVpnLink {} in FIB",
700 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(),
701 dpId, interVpnLink.getInterVpnLinkName());
703 mdsalManager.installFlow(dpId, flowEntity);
707 private List<BigInteger> createLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
708 List<BigInteger> returnLocalDpnId = new ArrayList<>();
709 String localNextHopIP = vrfEntry.getDestPrefix();
710 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
711 String vpnName = fibUtil.getVpnNameFromId(vpnId);
712 if (localNextHopInfo == null) {
713 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, localNextHopIP);
714 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
715 vpnName, usedRds, localNextHopIP);
716 boolean localNextHopSeen = false;
717 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
718 for (Routes vpnExtraRoute : vpnExtraRoutes) {
720 if (isIpv4Address(vpnExtraRoute.getNexthopIpList().get(0))) {
721 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
723 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
725 Prefixes localNextHopInfoLocal = fibUtil.getPrefixToInterface(vpnId,
727 if (localNextHopInfoLocal != null) {
728 localNextHopSeen = true;
730 checkCreateLocalFibEntry(localNextHopInfoLocal, localNextHopInfoLocal.getIpAddress(),
731 vpnId, rd, vrfEntry, vpnExtraRoute, vpnExtraRoutes);
732 returnLocalDpnId.add(dpnId);
735 if (!localNextHopSeen && RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
736 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
737 if (optionalLabel.isPresent()) {
738 Long label = optionalLabel.get();
739 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
740 synchronized (label.toString().intern()) {
741 LabelRouteInfo lri = getLabelRouteInfo(label);
742 if (isPrefixAndNextHopPresentInLri(localNextHopIP, nextHopAddressList, lri)) {
743 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
744 fibUtil.getVpnInstanceOpData(rd);
745 if (vpnInstanceOpDataEntryOptional.isPresent()) {
746 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
747 if (lri.getVpnInstanceList().contains(vpnInstanceName)) {
748 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, true);
749 localNextHopIP = lri.getPrefix();
751 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, false);
752 localNextHopIP = lri.getPrefix();
755 if (localNextHopInfo != null) {
756 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
757 label, localNextHopInfo.getVpnInterfaceName(), lri.getDpnId());
758 if (vpnExtraRoutes.isEmpty()) {
759 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
760 vpnId, rd, vrfEntry, null, vpnExtraRoutes);
761 returnLocalDpnId.add(dpnId);
763 for (Routes extraRoutes : vpnExtraRoutes) {
764 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
765 vpnId, rd, vrfEntry, extraRoutes, vpnExtraRoutes);
766 returnLocalDpnId.add(dpnId);
774 if (returnLocalDpnId.isEmpty()) {
775 LOG.error("Local DPNID is empty for rd {}, vpnId {}, vrfEntry {}", rd, vpnId, vrfEntry);
778 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP, vpnId,
779 rd, vrfEntry, /*routes*/ null, /*vpnExtraRoutes*/ null);
781 returnLocalDpnId.add(dpnId);
784 return returnLocalDpnId;
787 private BigInteger checkCreateLocalFibEntry(Prefixes localNextHopInfo, String localNextHopIP,
788 final Long vpnId, final String rd,
789 final VrfEntry vrfEntry,
790 Routes routes, List<Routes> vpnExtraRoutes) {
791 String vpnName = fibUtil.getVpnNameFromId(vpnId);
792 if (localNextHopInfo != null) {
795 final BigInteger dpnId = localNextHopInfo.getDpnId();
796 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
797 LOG.debug("checkCreateLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
798 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
801 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
802 LOG.debug("checkCreateLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
803 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
806 if (!isVpnPresentInDpn(rd, dpnId)) {
807 LOG.error("checkCreateLocalFibEntry: The VPN with id {} rd {} is not available on dpn {}",
808 vpnId, rd, dpnId.toString());
809 return BigInteger.ZERO;
811 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
812 String interfaceName = localNextHopInfo.getVpnInterfaceName();
813 String prefix = vrfEntry.getDestPrefix();
814 String gwMacAddress = vrfEntry.getGatewayMacAddress();
815 //The loadbalancing group is created only if the extra route has multiple nexthops
816 //to avoid loadbalancing the discovered routes
817 if (vpnExtraRoutes != null && routes != null) {
818 if (isIpv4Address(routes.getNexthopIpList().get(0))) {
819 localNextHopIP = routes.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
821 localNextHopIP = routes.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
823 if (vpnExtraRoutes.size() > 1) {
824 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes,
826 localGroupId = nextHopManager.getLocalNextHopGroup(vpnId, localNextHopIP);
827 } else if (routes.getNexthopIpList().size() > 1) {
828 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes,
830 localGroupId = groupId;
832 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP,
833 prefix, gwMacAddress, jobKey);
834 localGroupId = groupId;
837 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP, prefix,
838 gwMacAddress, jobKey);
839 localGroupId = groupId;
841 if (groupId == FibConstants.INVALID_GROUP_ID) {
842 LOG.error("Unable to create Group for local prefix {} on rd {} for vpninterface {} on Node {}",
843 prefix, rd, interfaceName, dpnId.toString());
844 return BigInteger.ZERO;
846 final List<InstructionInfo> instructions = Collections.singletonList(
847 new InstructionApplyActions(
848 Collections.singletonList(new ActionGroup(groupId))));
849 final List<InstructionInfo> lfibinstructions = Collections.singletonList(
850 new InstructionApplyActions(
851 Arrays.asList(new ActionPopMpls(), new ActionGroup(groupId))));
852 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
853 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
854 jobCoordinator.enqueueJob(jobKey, () -> {
855 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
856 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
857 NwConstants.ADD_FLOW, tx, null);
858 if (!fibUtil.enforceVxlanDatapathSemanticsforInternalRouterVpn(localNextHopInfo.getSubnetId(),
860 optLabel.ifPresent(label -> {
861 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
863 "Installing LFIB and tunnel table entry on dpn {} for interface {} with label "
864 + "{}, rd {}, prefix {}, nexthop {}", dpnId,
865 localNextHopInfo.getVpnInterfaceName(), optLabel, rd, vrfEntry.getDestPrefix(),
867 makeLFibTableEntry(dpnId, label, lfibinstructions, DEFAULT_FIB_FLOW_PRIORITY,
868 NwConstants.ADD_FLOW, tx);
869 // If the extra-route is reachable from VMs attached to the same switch,
870 // then the tunnel table can point to the load balancing group.
871 // If it is reachable from VMs attached to different switches,
872 // then it should be pointing to one of the local group in order to avoid looping.
873 if (vrfEntry.getRoutePaths().size() == 1) {
874 makeTunnelTableEntry(dpnId, label, groupId, tx);
876 makeTunnelTableEntry(dpnId, label, localGroupId, tx);
879 LOG.debug("Route with rd {} prefix {} label {} nexthop {} for vpn {} is an imported "
880 + "route. LFib and Terminating table entries will not be created.",
881 rd, vrfEntry.getDestPrefix(), optLabel, nextHopAddressList, vpnId);
889 LOG.error("localNextHopInfo received is null for prefix {} on rd {} on vpn {}", vrfEntry.getDestPrefix(), rd,
891 return BigInteger.ZERO;
894 private boolean isVpnPresentInDpn(String rd, BigInteger dpnId) {
895 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
896 Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
897 if (dpnInVpn.isPresent()) {
903 private LabelRouteInfo getLabelRouteInfo(Long label) {
904 InstanceIdentifier<LabelRouteInfo> lriIid = InstanceIdentifier.builder(LabelRouteMap.class)
905 .child(LabelRouteInfo.class, new LabelRouteInfoKey(label)).build();
906 Optional<LabelRouteInfo> opResult = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, lriIid);
907 if (opResult.isPresent()) {
908 return opResult.get();
913 private boolean deleteLabelRouteInfo(LabelRouteInfo lri, String vpnInstanceName, WriteTransaction tx) {
918 LOG.debug("deleting LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
919 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
920 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
922 List<String> vpnInstancesList = lri.getVpnInstanceList() != null
923 ? lri.getVpnInstanceList() : new ArrayList<>();
924 if (vpnInstancesList.contains(vpnInstanceName)) {
925 LOG.debug("vpninstance {} name is present", vpnInstanceName);
926 vpnInstancesList.remove(vpnInstanceName);
928 if (vpnInstancesList.isEmpty()) {
929 LOG.debug("deleting LRI instance object for label {}", lri.getLabel());
931 tx.delete(LogicalDatastoreType.OPERATIONAL, lriId);
933 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId);
937 LOG.debug("updating LRI instance object for label {}", lri.getLabel());
938 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri).setVpnInstanceList(vpnInstancesList);
939 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
944 void makeTunnelTableEntry(BigInteger dpId, long label, long groupId/*String egressInterfaceName*/,
945 WriteTransaction tx) {
946 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionGroup(groupId));
948 createTerminatingServiceActions(dpId, (int) label, actionsInfos, tx);
950 LOG.debug("Terminating service Entry for dpID {} : label : {} egress : {} installed successfully",
951 dpId, label, groupId);
954 public void createTerminatingServiceActions(BigInteger destDpId, int label, List<ActionInfo> actionsInfos,
955 WriteTransaction tx) {
956 List<MatchInfo> mkMatches = new ArrayList<>();
958 LOG.debug("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}",
959 destDpId, label, actionsInfos);
962 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
963 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
965 List<InstructionInfo> mkInstructions = new ArrayList<>();
966 mkInstructions.add(new InstructionApplyActions(actionsInfos));
968 FlowEntity terminatingServiceTableFlowEntity =
969 MDSALUtil.buildFlowEntity(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,
970 getTableMissFlowRef(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE, label), 5,
971 String.format("%s:%d", "TST Flow Entry ", label),
972 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, mkInstructions);
974 FlowKey flowKey = new FlowKey(new FlowId(terminatingServiceTableFlowEntity.getFlowId()));
976 FlowBuilder flowbld = terminatingServiceTableFlowEntity.getFlowBuilder();
978 Node nodeDpn = FibUtil.buildDpnNode(terminatingServiceTableFlowEntity.getDpnId());
979 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
980 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
981 .child(Table.class, new TableKey(terminatingServiceTableFlowEntity.getTableId()))
982 .child(Flow.class, flowKey).build();
983 tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId, flowbld.build(),
984 WriteTransaction.CREATE_MISSING_PARENTS);
987 private void removeTunnelTableEntry(BigInteger dpId, long label, WriteTransaction tx) {
988 FlowEntity flowEntity;
989 LOG.debug("remove terminatingServiceActions called with DpnId = {} and label = {}", dpId, label);
990 List<MatchInfo> mkMatches = new ArrayList<>();
992 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
993 flowEntity = MDSALUtil.buildFlowEntity(dpId,
994 NwConstants.INTERNAL_TUNNEL_TABLE,
995 getTableMissFlowRef(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, (int) label),
996 5, String.format("%s:%d", "TST Flow Entry ", label), 0, 0,
997 COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, null);
998 Node nodeDpn = FibUtil.buildDpnNode(flowEntity.getDpnId());
999 FlowKey flowKey = new FlowKey(new FlowId(flowEntity.getFlowId()));
1000 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1001 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1002 .child(Table.class, new TableKey(flowEntity.getTableId())).child(Flow.class, flowKey).build();
1004 tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
1005 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully", dpId, label);
1008 public List<BigInteger> deleteLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
1009 List<BigInteger> returnLocalDpnId = new ArrayList<>();
1010 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1011 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1012 boolean shouldUpdateNonEcmpLocalNextHop = true;
1013 if (localNextHopInfo == null) {
1014 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1015 if (usedRds.size() > 1) {
1016 LOG.error("The extra route prefix {} is still present in some DPNs in vpn {} on rd {}",
1017 vrfEntry.getDestPrefix(), vpnName, rd);
1018 return returnLocalDpnId;
1020 String vpnRd = (!usedRds.isEmpty()) ? usedRds.get(0) : rd;
1021 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency
1023 Optional<Routes> extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1024 vpnName, vpnRd, vrfEntry.getDestPrefix());
1025 if (extraRouteOptional.isPresent()) {
1026 Routes extraRoute = extraRouteOptional.get();
1028 if (isIpv4Address(extraRoute.getNexthopIpList().get(0))) {
1029 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
1031 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
1033 if (extraRoute.getNexthopIpList().size() > 1) {
1034 shouldUpdateNonEcmpLocalNextHop = false;
1036 localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1037 if (localNextHopInfo != null) {
1038 String localNextHopIP = localNextHopInfo.getIpAddress();
1039 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP,
1040 vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1041 if (!dpnId.equals(BigInteger.ZERO)) {
1042 LOG.trace("Deleting ECMP group for prefix {}, dpn {}", vrfEntry.getDestPrefix(), dpnId);
1043 nextHopManager.setupLoadBalancingNextHop(vpnId, dpnId,
1044 vrfEntry.getDestPrefix(), /*listBucketInfo*/ Collections.emptyList(),
1046 returnLocalDpnId.add(dpnId);
1049 LOG.error("localNextHopInfo unavailable while deleting prefix {} with rds {}, primary rd {} in "
1050 + "vpn {}", vrfEntry.getDestPrefix(), usedRds, rd, vpnName);
1054 if (localNextHopInfo == null) {
1055 /* Imported VRF entry */
1056 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1057 if (optionalLabel.isPresent()) {
1058 Long label = optionalLabel.get();
1059 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1060 LabelRouteInfo lri = getLabelRouteInfo(label);
1061 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1062 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1063 prefixBuilder.setDpnId(lri.getDpnId());
1064 BigInteger dpnId = checkDeleteLocalFibEntry(prefixBuilder.build(), nextHopAddressList.get(0),
1065 vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1066 if (!dpnId.equals(BigInteger.ZERO)) {
1067 returnLocalDpnId.add(dpnId);
1074 LOG.debug("Obtained prefix to interface for rd {} prefix {}", rd, vrfEntry.getDestPrefix());
1075 String localNextHopIP = localNextHopInfo.getIpAddress();
1076 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP,
1077 vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1078 if (!dpnId.equals(BigInteger.ZERO)) {
1079 returnLocalDpnId.add(dpnId);
1083 return returnLocalDpnId;
1086 private BigInteger checkDeleteLocalFibEntry(Prefixes localNextHopInfo, final String localNextHopIP,
1087 final Long vpnId, final String rd, final VrfEntry vrfEntry,
1088 boolean shouldUpdateNonEcmpLocalNextHop) {
1089 if (localNextHopInfo != null) {
1090 final BigInteger dpnId = localNextHopInfo.getDpnId();
1091 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
1092 LOG.debug("checkDeleteLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1093 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1096 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
1097 LOG.debug("checkDeleteLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1098 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1102 jobCoordinator.enqueueJob(FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix()),
1103 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1104 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1105 NwConstants.DEL_FLOW, tx, null);
1106 if (!fibUtil.enforceVxlanDatapathSemanticsforInternalRouterVpn(localNextHopInfo.getSubnetId(),
1108 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1109 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1110 makeLFibTableEntry(dpnId, label, null /* instructions */, DEFAULT_FIB_FLOW_PRIORITY,
1111 NwConstants.DEL_FLOW, tx);
1112 removeTunnelTableEntry(dpnId, label, tx);
1117 //TODO: verify below adjacency call need to be optimized (?)
1118 //In case of the removal of the extra route, the loadbalancing group is updated
1119 if (shouldUpdateNonEcmpLocalNextHop) {
1120 baseVrfEntryHandler.deleteLocalAdjacency(dpnId, vpnId, localNextHopIP, vrfEntry.getDestPrefix());
1124 return BigInteger.ZERO;
1127 private void createRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, String rd,
1128 final VrfEntry vrfEntry, WriteTransaction tx) {
1130 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(newTx -> {
1131 createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx);
1132 }), LOG, "Error creating remote FIB entry");
1136 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1137 LOG.debug("createremotefibentry: adding route {} for rd {} on remoteDpnId {}",
1138 vrfEntry.getDestPrefix(), rd, remoteDpnId);
1140 List<AdjacencyResult> adjacencyResults = baseVrfEntryHandler.resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
1141 if (adjacencyResults.isEmpty()) {
1142 LOG.error("Could not get interface for route-paths: {} in vpn {} on DPN {}",
1143 vrfEntry.getRoutePaths(), rd, remoteDpnId);
1144 LOG.error("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
1148 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1149 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
1150 vpnName, usedRds, vrfEntry.getDestPrefix());
1151 // create loadbalancing groups for extra routes only when the extra route is present behind
1153 if (!vpnExtraRoutes.isEmpty() && (vpnExtraRoutes.size() > 1
1154 || vpnExtraRoutes.get(0).getNexthopIpList().size() > 1)) {
1155 List<InstructionInfo> instructions = new ArrayList<>();
1156 // Obtain the local routes for this particular dpn.
1157 java.util.Optional<Routes> routes = vpnExtraRoutes
1160 Prefixes prefixToInterface = fibUtil.getPrefixToInterface(vpnId,
1161 fibUtil.getIpPrefix(route.getNexthopIpList().get(0)));
1162 if (prefixToInterface == null) {
1165 return remoteDpnId.equals(prefixToInterface.getDpnId());
1167 long groupId = nextHopManager.createNextHopGroups(vpnId, rd, remoteDpnId, vrfEntry,
1168 routes.isPresent() ? routes.get() : null, vpnExtraRoutes);
1169 if (groupId == FibConstants.INVALID_GROUP_ID) {
1170 LOG.error("Unable to create Group for local prefix {} on rd {} on Node {}",
1171 vrfEntry.getDestPrefix(), rd, remoteDpnId.toString());
1174 List<ActionInfo> actionInfos =
1175 Collections.singletonList(new ActionGroup(groupId));
1176 instructions.add(new InstructionApplyActions(actionInfos));
1177 baseVrfEntryHandler.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
1178 NwConstants.ADD_FLOW, tx, null);
1180 baseVrfEntryHandler.programRemoteFib(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, null);
1183 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
1186 protected void cleanUpOpDataForFib(Long vpnId, String primaryRd, final VrfEntry vrfEntry) {
1187 /* Get interface info from prefix to interface mapping;
1188 Use the interface info to get the corresponding vpn interface op DS entry,
1189 remove the adjacency corresponding to this fib entry.
1190 If adjacency removed is the last adjacency, clean up the following:
1191 - vpn interface from dpntovpn list, dpn if last vpn interface on dpn
1192 - prefix to interface entry
1193 - vpn interface op DS
1195 LOG.debug("Cleanup of prefix {} in VPN {}", vrfEntry.getDestPrefix(), vpnId);
1196 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1197 if (prefixInfo == null) {
1198 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1199 String usedRd = usedRds.isEmpty() ? primaryRd : usedRds.get(0);
1200 Routes extraRoute = baseVrfEntryHandler.getVpnToExtraroute(vpnId, usedRd, vrfEntry.getDestPrefix());
1201 if (extraRoute != null) {
1202 for (String nextHopIp : extraRoute.getNexthopIpList()) {
1203 LOG.debug("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
1204 if (nextHopIp != null) {
1206 if (isIpv4Address(nextHopIp)) {
1207 ipPrefix = nextHopIp + NwConstants.IPV4PREFIX;
1209 ipPrefix = nextHopIp + NwConstants.IPV6PREFIX;
1211 prefixInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1212 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1216 if (prefixInfo == null) {
1217 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1218 if (optionalLabel.isPresent()) {
1219 Long label = optionalLabel.get();
1220 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1221 LabelRouteInfo lri = getLabelRouteInfo(label);
1222 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1223 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1224 prefixBuilder.setDpnId(lri.getDpnId());
1225 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
1226 prefixBuilder.setIpAddress(lri.getPrefix());
1227 prefixInfo = prefixBuilder.build();
1228 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
1229 label, prefixInfo.getVpnInterfaceName(), lri.getDpnId());
1230 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1235 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, null /*Routes*/);
1239 private void checkCleanUpOpDataForFib(final Prefixes prefixInfo, final Long vpnId, final String rd,
1240 final VrfEntry vrfEntry, final Routes extraRoute) {
1242 if (prefixInfo == null) {
1243 LOG.error("Cleanup VPN Data Failed as unable to find prefix Info for prefix {} VpnId {} rd {}",
1244 vrfEntry.getDestPrefix(), vpnId, rd);
1245 return; //Don't have any info for this prefix (shouldn't happen); need to return
1248 if (Prefixes.PrefixCue.Nat.equals(prefixInfo.getPrefixCue())) {
1249 LOG.debug("NAT Prefix {} with vpnId {} rd {}. Skip FIB processing",
1250 vrfEntry.getDestPrefix(), vpnId, rd);
1254 String ifName = prefixInfo.getVpnInterfaceName();
1255 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName,
1256 new CleanupVpnInterfaceWorker(prefixInfo, vpnId, rd, vrfEntry, extraRoute));
1259 private class CleanupVpnInterfaceWorker implements Callable<List<ListenableFuture<Void>>> {
1260 Prefixes prefixInfo;
1266 CleanupVpnInterfaceWorker(final Prefixes prefixInfo, final Long vpnId, final String rd,
1267 final VrfEntry vrfEntry, final Routes extraRoute) {
1268 this.prefixInfo = prefixInfo;
1271 this.vrfEntry = vrfEntry;
1272 this.extraRoute = extraRoute;
1276 public List<ListenableFuture<Void>> call() {
1277 // If another renderer(for eg : CSS) needs to be supported, check can be performed here
1278 // to call the respective helpers.
1279 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
1280 //First Cleanup LabelRouteInfo
1281 //TODO(KIRAN) : Move the below block when addressing iRT/eRT for L3VPN Over VxLan
1282 LOG.debug("cleanupVpnInterfaceWorker: rd {} prefix {}", rd, prefixInfo.getIpAddress());
1283 if (VrfEntry.EncapType.Mplsgre.equals(vrfEntry.getEncapType())) {
1284 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1285 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1286 synchronized (label.toString().intern()) {
1287 LabelRouteInfo lri = getLabelRouteInfo(label);
1288 if (lri != null && lri.getPrefix().equals(vrfEntry.getDestPrefix())
1289 && nextHopAddressList.contains(lri.getNextHopIpList().get(0))) {
1290 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1291 fibUtil.getVpnInstanceOpData(rd);
1292 String vpnInstanceName = "";
1293 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1294 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1296 boolean lriRemoved = deleteLabelRouteInfo(lri, vpnInstanceName, tx);
1298 String parentRd = lri.getParentVpnRd();
1299 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1300 parentRd, vrfEntry.getDestPrefix()));
1303 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1304 rd, vrfEntry.getDestPrefix()));
1309 String ifName = prefixInfo.getVpnInterfaceName();
1310 Optional<String> optVpnName = fibUtil.getVpnNameFromRd(rd);
1311 String vpnName = null;
1313 if (Prefixes.PrefixCue.PhysNetFunc.equals(prefixInfo.getPrefixCue())) {
1314 /*Get vpnId for rd = networkId since op vpnInterface will be pointing to rd = networkId
1316 Optional<String> vpnNameOpt = fibUtil.getVpnNameFromRd(vrfEntry.getParentVpnRd());
1317 if (vpnNameOpt.isPresent()) {
1318 vpnId = fibUtil.getVpnId(vpnNameOpt.get());
1321 if (optVpnName.isPresent()) {
1322 vpnName = optVpnName.get();
1323 Optional<VpnInterfaceOpDataEntry> opVpnInterface = MDSALUtil
1324 .read(dataBroker, LogicalDatastoreType.OPERATIONAL,
1325 fibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1326 if (opVpnInterface.isPresent()) {
1327 long associatedVpnId = fibUtil.getVpnId(vpnName);
1328 if (vpnId != associatedVpnId) {
1329 LOG.warn("Prefixes {} are associated with different vpn instance with id {} rather than {}",
1330 vrfEntry.getDestPrefix(), associatedVpnId, vpnId);
1331 LOG.warn("Not proceeding with Cleanup op data for prefix {}", vrfEntry.getDestPrefix());
1334 LOG.debug("Processing cleanup of prefix {} associated with vpn {}",
1335 vrfEntry.getDestPrefix(), associatedVpnId);
1339 if (extraRoute != null) {
1340 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1341 //Only one used Rd present in case of removal event
1342 String usedRd = usedRds.get(0);
1343 if (optVpnName.isPresent()) {
1344 tx.delete(LogicalDatastoreType.OPERATIONAL,
1345 baseVrfEntryHandler.getVpnToExtrarouteIdentifier(vpnName, usedRd,
1346 vrfEntry.getDestPrefix()));
1347 tx.delete(LogicalDatastoreType.CONFIGURATION,
1348 VpnExtraRouteHelper.getUsedRdsIdentifier(vpnId, vrfEntry.getDestPrefix()));
1351 handleAdjacencyAndVpnOpInterfaceDeletion(vrfEntry, ifName, vpnName, tx);
1357 * Check all the adjacency in VpnInterfaceOpData and decide whether to delete the entire interface or only adj.
1358 * Remove Adjacency from VPNInterfaceOpData.
1359 * if Adjacency != primary.
1360 * if Adjacency == primary , then mark it for deletion.
1361 * Remove entire VPNinterfaceOpData Entry.
1362 * if sie of Adjacency <= 2 and all are marked for deletion , delete the entire VPNinterface Op entry.
1363 * @param vrfEntry - VrfEntry removed
1364 * @param ifName - Interface name from VRFentry
1365 * @param vpnName - VPN name of corresponding VRF
1366 * @param tx - ReadWrite Tx
1367 * @throws ReadFailedException - Exception thrown in case of read failed
1369 private void handleAdjacencyAndVpnOpInterfaceDeletion(VrfEntry vrfEntry, String ifName, String vpnName,
1370 ReadWriteTransaction tx) throws ReadFailedException {
1371 InstanceIdentifier<Adjacency> adjacencyIid =
1372 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix());
1373 Optional<Adjacency> adjacencyOptional = tx.read(LogicalDatastoreType.OPERATIONAL, adjacencyIid).checkedGet();
1374 if (adjacencyOptional.isPresent()) {
1375 if (adjacencyOptional.get().getAdjacencyType() != Adjacency.AdjacencyType.PrimaryAdjacency) {
1376 tx.delete(LogicalDatastoreType.OPERATIONAL,
1377 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix()));
1379 tx.merge(LogicalDatastoreType.OPERATIONAL, adjacencyIid,
1380 new AdjacencyBuilder(adjacencyOptional.get()).setMarkedForDeletion(true).build());
1384 Optional<AdjacenciesOp> optAdjacencies =
1385 tx.read(LogicalDatastoreType.OPERATIONAL,
1386 FibUtil.getAdjListPathOp(ifName, vpnName)).checkedGet();
1388 if (!optAdjacencies.isPresent() || optAdjacencies.get().getAdjacency() == null) {
1392 if (optAdjacencies.get().getAdjacency().stream().count() <= 2
1393 && optAdjacencies.get().getAdjacency().stream().allMatch(adjacency ->
1394 adjacency.getAdjacencyType() == Adjacency.AdjacencyType.PrimaryAdjacency
1395 && adjacency.isMarkedForDeletion() != null
1396 && adjacency.isMarkedForDeletion()
1398 LOG.info("Clean up vpn interface {} to vpn {} list.", ifName, vpnName);
1399 tx.delete(LogicalDatastoreType.OPERATIONAL,
1400 FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1404 private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
1405 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
1406 final String rd = vrfTableKey.getRouteDistinguisher();
1407 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
1408 if (vpnInstance == null) {
1409 LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
1412 final Collection<VpnToDpnList> vpnToDpnList;
1413 if (vrfEntry.getParentVpnRd() != null
1414 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
1415 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
1416 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
1417 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
1418 vpnInstance.getVpnToDpnList();
1419 LOG.info("deleteFibEntries: Processing deletion of PNF FIB entry with rd {} prefix {}",
1420 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
1422 vpnToDpnList = vpnInstance.getVpnToDpnList();
1425 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1426 final java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1427 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1428 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1429 if (subnetRoute != null) {
1430 long elanTag = subnetRoute.getElantag();
1431 LOG.trace("SUBNETROUTE: deleteFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
1432 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
1433 if (vpnToDpnList != null) {
1434 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1435 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1436 for (final VpnToDpnList curDpn : vpnToDpnList) {
1438 baseVrfEntryHandler.makeConnectedRoute(curDpn.getDpnId(), vpnInstance.getVpnId(), vrfEntry,
1439 vrfTableKey.getRouteDistinguisher(), null, NwConstants.DEL_FLOW, tx, null);
1440 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1441 optionalLabel.ifPresent(label -> makeLFibTableEntry(curDpn.getDpnId(), label, null,
1442 DEFAULT_FIB_FLOW_PRIORITY, NwConstants.DEL_FLOW, tx));
1445 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnInstance.getVpnId(),
1446 vrfEntry, NwConstants.DEL_FLOW, tx);
1450 optionalLabel.ifPresent(label -> {
1451 synchronized (label.toString().intern()) {
1452 LabelRouteInfo lri = getLabelRouteInfo(label);
1453 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1454 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1455 fibUtil.getVpnInstanceOpData(rd);
1456 String vpnInstanceName = "";
1457 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1458 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1460 boolean lriRemoved = this.deleteLabelRouteInfo(lri, vpnInstanceName, null);
1462 String parentRd = lri.getParentVpnRd();
1463 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1464 parentRd, vrfEntry.getDestPrefix()));
1465 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}"
1466 + " as labelRouteInfo cleared", label, rd, vrfEntry.getDestPrefix());
1469 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1470 rd, vrfEntry.getDestPrefix()));
1471 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}",
1472 label, rd, vrfEntry.getDestPrefix());
1479 final List<BigInteger> localDpnIdList = deleteLocalFibEntry(vpnInstance.getVpnId(),
1480 vrfTableKey.getRouteDistinguisher(), vrfEntry);
1481 if (vpnToDpnList != null) {
1482 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1483 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1485 Optional<Routes> extraRouteOptional;
1486 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
1487 if (usedRds != null && !usedRds.isEmpty()) {
1488 jobKey = FibUtil.getJobKeyForRdPrefix(usedRds.get(0), vrfEntry.getDestPrefix());
1489 if (usedRds.size() > 1) {
1490 LOG.error("The extra route prefix is still present in some DPNs");
1493 // The first rd is retrieved from usedrds as Only 1 rd would be present as extra route prefix
1494 //is not present in any other DPN
1495 extraRouteOptional = VpnExtraRouteHelper
1496 .getVpnExtraroutes(dataBroker, vpnName, usedRds.get(0), vrfEntry.getDestPrefix());
1499 jobKey = FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix());
1500 extraRouteOptional = Optional.absent();
1503 jobCoordinator.enqueueJob(jobKey,
1504 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1505 if (localDpnIdList.size() <= 0) {
1506 for (VpnToDpnList curDpn : vpnToDpnList) {
1507 baseVrfEntryHandler.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
1508 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional, tx);
1511 for (BigInteger localDpnId : localDpnIdList) {
1512 for (VpnToDpnList curDpn : vpnToDpnList) {
1513 if (!curDpn.getDpnId().equals(localDpnId)) {
1514 baseVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
1515 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional, tx);
1523 //The flow/group entry has been deleted from config DS; need to clean up associated operational
1524 //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
1525 cleanUpOpDataForFib(vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(), vrfEntry);
1527 // Remove all fib entries configured due to interVpnLink, when nexthop is the opposite endPoint
1528 // of the interVpnLink.
1529 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
1530 if (optVpnUuid.isPresent()) {
1531 String vpnUuid = optVpnUuid.get();
1532 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
1533 Optional<InterVpnLinkDataComposite> optInterVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid);
1534 if (optInterVpnLink.isPresent()) {
1535 InterVpnLinkDataComposite interVpnLink = optInterVpnLink.get();
1536 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
1537 // This is route that points to the other endpoint of an InterVpnLink
1538 // In that case, we should look for the FIB table pointing to
1539 // LPortDispatcher table and remove it.
1540 removeInterVPNLinkRouteFlows(interVpnLink, vpnUuid, vrfEntry);
1548 private void makeLFibTableEntry(BigInteger dpId, long label, List<InstructionInfo> instructions, int priority,
1549 int addOrRemove, WriteTransaction tx) {
1551 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
1552 newTx -> makeLFibTableEntry(dpId, label, instructions, priority, addOrRemove, newTx)), LOG,
1553 "Error making LFIB table entry");
1557 List<MatchInfo> matches = new ArrayList<>();
1558 matches.add(MatchEthernetType.MPLS_UNICAST);
1559 matches.add(new MatchMplsLabel(label));
1561 // Install the flow entry in L3_LFIB_TABLE
1562 String flowRef = FibUtil.getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, label, priority);
1564 FlowEntity flowEntity;
1565 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef, priority, flowRef, 0, 0,
1566 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
1567 Flow flow = flowEntity.getFlowBuilder().build();
1568 String flowId = flowEntity.getFlowId();
1569 FlowKey flowKey = new FlowKey(new FlowId(flowId));
1570 Node nodeDpn = FibUtil.buildDpnNode(dpId);
1571 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1572 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1573 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
1575 if (addOrRemove == NwConstants.ADD_FLOW) {
1576 tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId, flow, WriteTransaction.CREATE_MISSING_PARENTS);
1578 tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
1581 LOG.debug("LFIB Entry for dpID {} : label : {} instructions {} : key {} {} successfully",
1582 dpId, label, instructions, flowKey, NwConstants.ADD_FLOW == addOrRemove ? "ADDED" : "REMOVED");
1585 public void populateFibOnNewDpn(final BigInteger dpnId, final long vpnId, final String rd,
1586 final FutureCallback<List<Void>> callback) {
1587 LOG.trace("New dpn {} for vpn {} : populateFibOnNewDpn", dpnId, rd);
1588 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1590 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1591 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1592 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1593 LogicalDatastoreType.CONFIGURATION, id);
1594 List<ListenableFuture<Void>> futures = new ArrayList<>();
1595 if (!vrfTable.isPresent()) {
1596 LOG.info("populateFibOnNewDpn: dpn: {}: VRF Table not yet available for RD {}", dpnId, rd);
1597 if (callback != null) {
1598 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1599 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1603 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1604 futures.add(retryingTxRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
1605 for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
1606 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1607 if (subnetRoute != null) {
1608 long elanTag = subnetRoute.getElantag();
1609 installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, tx);
1610 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry, NwConstants.ADD_FLOW,
1614 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1615 if (routerInt != null) {
1616 LOG.trace("Router augmented vrfentry found rd:{}, uuid:{}, ip:{}, mac:{}",
1617 rd, routerInt.getUuid(), routerInt.getIpAddress(), routerInt.getMacAddress());
1618 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1619 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1620 NwConstants.ADD_FLOW);
1623 //Handle local flow creation for imports
1624 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1625 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1626 if (optionalLabel.isPresent()) {
1627 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1628 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1629 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList, lri)) {
1630 if (lri.getDpnId().equals(dpnId)) {
1631 createLocalFibEntry(vpnId, rd, vrfEntry);
1637 boolean shouldCreateRemoteFibEntry = shouldCreateFibEntryForVrfAndVpnIdOnDpn(vpnId,
1639 if (shouldCreateRemoteFibEntry) {
1640 LOG.trace("Will create remote FIB entry for vrfEntry {} on DPN {}", vrfEntry, dpnId);
1641 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1642 List<SubTransaction> txnObjects = new ArrayList<>();
1643 bgpRouteVrfEntryHandler.createRemoteFibEntry(dpnId, vpnId,
1644 vrfTable.get().getRouteDistinguisher(), vrfEntry, tx, txnObjects);
1646 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
1652 if (callback != null) {
1653 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1654 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1661 public void populateExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1662 final String localNextHopIp, final String remoteNextHopIp) {
1663 LOG.trace("populateExternalRoutesOnDpn : dpn {}, vpn {}, rd {}, localNexthopIp {} , remoteNextHopIp {} ",
1664 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1665 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1666 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1667 List<SubTransaction> txnObjects = new ArrayList<>();
1668 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1669 if (vrfTable.isPresent()) {
1670 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1671 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1672 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1673 vrfTable.get().getVrfEntry().stream()
1674 .filter(vrfEntry -> RouteOrigin.BGP == RouteOrigin.value(vrfEntry.getOrigin()))
1675 .forEach(bgpRouteVrfEntryHandler.getConsumerForCreatingRemoteFib(dpnId, vpnId,
1676 rd, remoteNextHopIp, vrfTable, tx, txnObjects));
1682 public void manageRemoteRouteOnDPN(final boolean action,
1683 final BigInteger localDpnId,
1686 final String destPrefix,
1687 final String destTepIp,
1689 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1691 if (vpnInstance == null) {
1692 LOG.error("VpnInstance for rd {} not present for prefix {}", rd, destPrefix);
1696 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, localDpnId),
1697 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1698 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1699 VrfTablesKey vrfTablesKey = new VrfTablesKey(rd);
1700 VrfEntry vrfEntry = getVrfEntry(dataBroker, rd, destPrefix);
1701 if (vrfEntry == null) {
1704 LOG.trace("manageRemoteRouteOnDPN :: action {}, DpnId {}, vpnId {}, rd {}, destPfx {}",
1705 action, localDpnId, vpnId, rd, destPrefix);
1706 List<RoutePaths> routePathList = vrfEntry.getRoutePaths();
1707 VrfEntry modVrfEntry;
1708 if (routePathList == null || routePathList.isEmpty()) {
1709 modVrfEntry = FibHelper.getVrfEntryBuilder(vrfEntry, label,
1710 Collections.singletonList(destTepIp),
1711 RouteOrigin.value(vrfEntry.getOrigin()), null /* parentVpnRd */).build();
1713 modVrfEntry = vrfEntry;
1717 LOG.trace("manageRemoteRouteOnDPN updated(add) vrfEntry :: {}", modVrfEntry);
1718 createRemoteFibEntry(localDpnId, vpnId, vrfTablesKey.getRouteDistinguisher(),
1721 LOG.trace("manageRemoteRouteOnDPN updated(remove) vrfEntry :: {}", modVrfEntry);
1722 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnInstance.getVpnId(),
1723 vrfEntry.getDestPrefix());
1724 if (usedRds.size() > 1) {
1725 LOG.debug("The extra route prefix is still present in some DPNs");
1728 //Is this fib route an extra route? If yes, get the nexthop which would be
1729 //an adjacency in the vpn
1730 Optional<Routes> extraRouteOptional = Optional.absent();
1731 if (usedRds.size() != 0) {
1732 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1733 fibUtil.getVpnNameFromId(vpnInstance.getVpnId()),
1734 usedRds.get(0), vrfEntry.getDestPrefix());
1736 baseVrfEntryHandler.deleteRemoteRoute(null, localDpnId, vpnId, vrfTablesKey, modVrfEntry,
1737 extraRouteOptional, tx);
1743 public void cleanUpDpnForVpn(final BigInteger dpnId, final long vpnId, final String rd,
1744 final FutureCallback<List<Void>> callback) {
1745 LOG.trace("cleanUpDpnForVpn: Remove dpn {} for vpn {} : cleanUpDpnForVpn", dpnId, rd);
1746 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1748 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1749 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1750 List<SubTransaction> txnObjects = new ArrayList<>();
1751 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1752 LogicalDatastoreType.CONFIGURATION, id);
1753 List<ListenableFuture<Void>> futures = new ArrayList<>();
1754 if (vrfTable.isPresent()) {
1755 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1756 futures.add(retryingTxRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1757 for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
1758 /* Handle subnet routes here */
1759 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1760 if (subnetRoute != null) {
1761 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Cleaning subnetroute {} on dpn {}"
1762 + " for vpn {}", vrfEntry.getDestPrefix(), dpnId, rd);
1763 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1764 NwConstants.DEL_FLOW, tx, null);
1765 List<RoutePaths> routePaths = vrfEntry.getRoutePaths();
1766 if (routePaths != null) {
1767 for (RoutePaths routePath : routePaths) {
1768 makeLFibTableEntry(dpnId, routePath.getLabel(), null,
1769 DEFAULT_FIB_FLOW_PRIORITY,
1770 NwConstants.DEL_FLOW, tx);
1771 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Released subnetroute label {}"
1772 + " for rd {} prefix {}", routePath.getLabel(), rd,
1773 vrfEntry.getDestPrefix());
1776 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry,
1777 NwConstants.DEL_FLOW, tx);
1780 // ping responder for router interfaces
1781 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1782 if (routerInt != null) {
1783 LOG.trace("Router augmented vrfentry found for rd:{}, uuid:{}, ip:{}, mac:{}",
1784 rd, routerInt.getUuid(), routerInt.getIpAddress(),
1785 routerInt.getMacAddress());
1786 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1787 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1788 NwConstants.DEL_FLOW);
1792 //Handle local flow deletion for imports
1793 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1794 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1795 if (optionalLabel.isPresent()) {
1796 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1797 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1798 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList,
1799 lri) && lri.getDpnId().equals(dpnId)) {
1800 deleteLocalFibEntry(vpnId, rd, vrfEntry);
1805 // Passing null as we don't know the dpn
1806 // to which prefix is attached at this point
1807 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1808 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1809 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1810 Optional<Routes> extraRouteOptional;
1811 //Is this fib route an extra route? If yes, get the nexthop which would be
1812 //an adjacency in the vpn
1813 if (usedRds != null && !usedRds.isEmpty()) {
1814 if (usedRds.size() > 1) {
1815 LOG.error("The extra route prefix is still present in some DPNs");
1818 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
1819 usedRds.get(0), vrfEntry.getDestPrefix());
1823 extraRouteOptional = Optional.absent();
1825 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1826 bgpRouteVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1827 vrfTable.get().key(), vrfEntry, extraRouteOptional, tx, txnObjects);
1829 baseVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId, vrfTable.get().key(),
1830 vrfEntry, extraRouteOptional, tx);
1835 if (callback != null) {
1836 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1837 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1840 LOG.error("cleanUpDpnForVpn: No vrf table found for rd {} vpnId {} dpn {}", rd, vpnId, dpnId);
1847 public void cleanUpExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1848 final String localNextHopIp, final String remoteNextHopIp) {
1849 LOG.trace("cleanUpExternalRoutesOnDpn : cleanup remote routes on dpn {} for vpn {}, rd {}, "
1850 + " localNexthopIp {} , remoteNexhtHopIp {}",
1851 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1852 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1853 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1854 List<SubTransaction> txnObjects = new ArrayList<>();
1855 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1856 if (vrfTable.isPresent()) {
1857 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1859 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1860 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
1861 tx -> vrfTable.get().getVrfEntry().stream()
1862 .filter(vrfEntry -> RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP)
1863 .forEach(bgpRouteVrfEntryHandler.getConsumerForDeletingRemoteFib(dpnId, vpnId,
1864 remoteNextHopIp, vrfTable, tx, txnObjects))));
1870 public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
1871 InstanceIdentifierBuilder<VrfTables> idBuilder =
1872 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
1873 return idBuilder.build();
1876 private String getInterVpnFibFlowRef(String interVpnLinkName, String prefix, String nextHop) {
1877 return FLOWID_PREFIX + interVpnLinkName + NwConstants.FLOWID_SEPARATOR + prefix + NwConstants
1878 .FLOWID_SEPARATOR + nextHop;
1881 private String getTableMissFlowRef(BigInteger dpnId, short tableId, int tableMiss) {
1882 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR
1883 + tableMiss + FLOWID_PREFIX;
1886 private VrfEntry getVrfEntry(DataBroker broker, String rd, String ipPrefix) {
1887 InstanceIdentifier<VrfEntry> vrfEntryId = InstanceIdentifier.builder(FibEntries.class)
1888 .child(VrfTables.class, new VrfTablesKey(rd))
1889 .child(VrfEntry.class, new VrfEntryKey(ipPrefix)).build();
1890 Optional<VrfEntry> vrfEntry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
1891 if (vrfEntry.isPresent()) {
1892 return vrfEntry.get();
1897 public void removeInterVPNLinkRouteFlows(final InterVpnLinkDataComposite interVpnLink,
1898 final String vpnName,
1899 final VrfEntry vrfEntry) {
1900 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null && vrfEntry.getRoutePaths().size() == 1);
1902 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
1903 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
1905 if (targetDpns.isEmpty()) {
1906 LOG.warn("Could not find DPNs for VPN {} in InterVpnLink {}", vpnName, interVpnLinkName);
1910 java.util.Optional<String> optNextHop = FibUtil.getFirstNextHopAddress(vrfEntry);
1911 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1915 optNextHop.ifPresent(nextHop -> {
1916 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, vrfEntry.getDestPrefix(), nextHop);
1917 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
1918 Flow flow = new FlowBuilder().withKey(flowKey).setId(new FlowId(flowRef))
1919 .setTableId(NwConstants.L3_FIB_TABLE).setFlowName(flowRef).build();
1921 LOG.trace("Removing flow in FIB table for interVpnLink {} key {}", interVpnLinkName, flowRef);
1922 for (BigInteger dpId : targetDpns) {
1923 LOG.debug("Removing flow: VrfEntry=[prefix={} nexthop={}] dpn {} for InterVpnLink {} in FIB",
1924 vrfEntry.getDestPrefix(), nextHop, dpId, interVpnLinkName);
1926 mdsalManager.removeFlow(dpId, flow);
1932 optLabel.ifPresent(label -> {
1933 LOG.trace("Removing flow in FIB table for interVpnLink {}", interVpnLinkName);
1935 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1936 for (BigInteger dpId : targetDpns) {
1937 LOG.debug("Removing flow: VrfEntry=[prefix={} label={}] dpn {} for InterVpnLink {} in LFIB",
1938 vrfEntry.getDestPrefix(), label, dpId, interVpnLinkName);
1939 makeLFibTableEntry(dpId, label, /*instructions*/null, LFIB_INTERVPN_PRIORITY, NwConstants.DEL_FLOW,
1942 }), LOG, "Error removing flows");
1946 private boolean isPrefixAndNextHopPresentInLri(String prefix,
1947 List<String> nextHopAddressList, LabelRouteInfo lri) {
1948 return lri != null && lri.getPrefix().equals(prefix)
1949 && nextHopAddressList.contains(lri.getNextHopIpList().get(0));
1952 private boolean shouldCreateFibEntryForVrfAndVpnIdOnDpn(Long vpnId, VrfEntry vrfEntry, BigInteger dpnId) {
1953 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
1957 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1958 if (prefix != null) {
1959 BigInteger prefixDpnId = prefix.getDpnId();
1960 if (dpnId.equals(prefixDpnId)) {
1961 LOG.trace("Should not create remote FIB entry for vrfEntry {} on DPN {}",