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.NWUtil;
48 import org.opendaylight.genius.mdsalutil.NwConstants;
49 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
50 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
51 import org.opendaylight.genius.mdsalutil.actions.ActionPopMpls;
52 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
53 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
54 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
55 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
56 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
57 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
58 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
59 import org.opendaylight.genius.mdsalutil.matches.MatchMplsLabel;
60 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
61 import org.opendaylight.genius.utils.ServiceIndex;
62 import org.opendaylight.genius.utils.batching.SubTransaction;
63 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
64 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
65 import org.opendaylight.netvirt.elanmanager.api.IElanService;
66 import org.opendaylight.netvirt.fibmanager.NexthopManager.AdjacencyResult;
67 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
68 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
69 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
70 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
71 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
72 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
73 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.LabelRouteMap;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.RouterInterface;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRoute;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfo;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoBuilder;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoKey;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOp;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.PrefixesBuilder;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState.State;
106 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
107 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
108 import org.slf4j.Logger;
109 import org.slf4j.LoggerFactory;
113 public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry, VrfEntryListener> {
115 private static final Logger LOG = LoggerFactory.getLogger(VrfEntryListener.class);
116 private static final String FLOWID_PREFIX = "L3.";
117 private static final BigInteger COOKIE_VM_FIB_TABLE = new BigInteger("8000003", 16);
118 private static final int DEFAULT_FIB_FLOW_PRIORITY = 10;
119 private static final int IPV4_ADDR_PREFIX_LENGTH = 32;
120 private static final int LFIB_INTERVPN_PRIORITY = 15;
121 public static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
122 private static final int MAX_RETRIES = 3;
123 private static final BigInteger COOKIE_TABLE_MISS = new BigInteger("8000004", 16);
125 private final DataBroker dataBroker;
126 private final ManagedNewTransactionRunner txRunner;
127 private final RetryingManagedNewTransactionRunner retryingTxRunner;
128 private final IMdsalApiManager mdsalManager;
129 private final NexthopManager nextHopManager;
130 private final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler;
131 private final BaseVrfEntryHandler baseVrfEntryHandler;
132 private final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler;
133 private final JobCoordinator jobCoordinator;
134 private final IElanService elanManager;
135 private final FibUtil fibUtil;
136 private final InterVpnLinkCache interVpnLinkCache;
137 private final List<AutoCloseable> closeables = new CopyOnWriteArrayList<>();
140 public VrfEntryListener(final DataBroker dataBroker, final IMdsalApiManager mdsalApiManager,
141 final NexthopManager nexthopManager,
142 final IElanService elanManager,
143 final BaseVrfEntryHandler vrfEntryHandler,
144 final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler,
145 final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler,
146 final JobCoordinator jobCoordinator,
147 final FibUtil fibUtil,
148 final InterVpnLinkCache interVpnLinkCache) {
149 super(VrfEntry.class, VrfEntryListener.class);
150 this.dataBroker = dataBroker;
151 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
152 this.retryingTxRunner = new RetryingManagedNewTransactionRunner(dataBroker, MAX_RETRIES);
153 this.mdsalManager = mdsalApiManager;
154 this.nextHopManager = nexthopManager;
155 this.elanManager = elanManager;
156 this.baseVrfEntryHandler = vrfEntryHandler;
157 this.bgpRouteVrfEntryHandler = bgpRouteVrfEntryHandler;
158 this.routerInterfaceVrfEntryHandler = routerInterfaceVrfEntryHandler;
159 this.jobCoordinator = jobCoordinator;
160 this.fibUtil = fibUtil;
161 this.interVpnLinkCache = interVpnLinkCache;
167 LOG.info("{} init", getClass().getSimpleName());
168 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
172 @SuppressWarnings("checkstyle:IllegalCatch")
173 public void close() {
174 closeables.forEach(c -> {
177 } catch (Exception e) {
178 LOG.warn("Error closing {}", c, e);
184 protected VrfEntryListener getDataTreeChangeListener() {
185 return VrfEntryListener.this;
189 protected InstanceIdentifier<VrfEntry> getWildCardPath() {
190 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class).child(VrfEntry.class);
194 protected void add(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
195 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
196 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
197 LOG.debug("ADD: Adding Fib Entry rd {} prefix {} route-paths {}",
198 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
199 addFibEntries(identifier, vrfEntry, rd);
200 LOG.info("ADD: Added Fib Entry rd {} prefix {} route-paths {}",
201 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
204 //This method is temporary. Eventually Factory design pattern will be used to get
205 // right VrfEntryhandle and invoke its methods.
206 private void addFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
207 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
208 bgpRouteVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
211 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
212 LOG.info("EVPN flows need to be programmed.");
213 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
214 nextHopManager, jobCoordinator, elanManager, fibUtil);
215 evpnVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
216 closeables.add(evpnVrfEntryHandler);
219 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
220 if (routerInt != null) {
221 // ping responder for router interfaces
222 routerInterfaceVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
225 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
226 createFibEntries(identifier, vrfEntry);
232 protected void remove(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry) {
233 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
234 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
235 LOG.debug("REMOVE: Removing Fib Entry rd {} prefix {} route-paths {}",
236 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
237 removeFibEntries(identifier, vrfEntry, rd);
238 LOG.info("REMOVE: Removed Fib Entry rd {} prefix {} route-paths {}",
239 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
242 //This method is temporary. Eventually Factory design pattern will be used to get
243 // right VrfEntryhandle and invoke its methods.
244 private void removeFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
245 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
246 LOG.info("EVPN flows to be deleted");
247 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
248 nextHopManager, jobCoordinator, elanManager, fibUtil);
249 evpnVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
250 closeables.add(evpnVrfEntryHandler);
253 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
254 if (routerInt != null) {
255 // ping responder for router interfaces
256 routerInterfaceVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
259 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
260 deleteFibEntries(identifier, vrfEntry);
263 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
264 bgpRouteVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
270 // "Redundant nullcheck of originalRoutePath, which is known to be non-null" - the null checking for
271 // originalRoutePath is a little dicey - safest to keep the checking even if not needed.
272 @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE")
273 protected void update(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update) {
274 Preconditions.checkNotNull(update, "VrfEntry should not be null or empty.");
275 final String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
276 LOG.debug("UPDATE: Updating Fib Entries to rd {} prefix {} route-paths {} origin {} old-origin {}", rd,
277 update.getDestPrefix(), update.getRoutePaths(), update.getOrigin(), original.getOrigin());
278 // Handle BGP Routes first
279 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.BGP) {
280 bgpRouteVrfEntryHandler.updateFlows(identifier, original, update, rd);
281 LOG.info("UPDATE: Updated BGP advertised Fib Entry with rd {} prefix {} route-paths {}",
282 rd, update.getDestPrefix(), update.getRoutePaths());
286 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.STATIC) {
287 List<RoutePaths> originalRoutePath = original.getRoutePaths();
288 List<RoutePaths> updateRoutePath = update.getRoutePaths();
289 LOG.info("UPDATE: Original route-path {} update route-path {} ", originalRoutePath, updateRoutePath);
291 //Updates need to be handled for extraroute even if original vrf entry route path is null or
292 //updated vrf entry route path is null. This can happen during tunnel events.
293 Optional<VpnInstanceOpDataEntry> optVpnInstance = fibUtil.getVpnInstanceOpData(rd);
294 List<String> usedRds = new ArrayList<>();
295 if (optVpnInstance.isPresent()) {
296 usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,optVpnInstance.get().getVpnId(),
297 update.getDestPrefix());
299 // If original VRF Entry had nexthop null , but update VRF Entry
300 // has nexthop , route needs to be created on remote Dpns
301 if (originalRoutePath == null || originalRoutePath.isEmpty()
302 && updateRoutePath != null && !updateRoutePath.isEmpty() && usedRds.isEmpty()) {
303 // TODO(vivek): Though ugly, Not handling this code now, as each
304 // tep add event will invoke flow addition
305 LOG.trace("Original VRF entry NH is null for destprefix {}. And the prefix is not an extra route."
306 + " This event is IGNORED here.", update.getDestPrefix());
310 // If original VRF Entry had valid nexthop , but update VRF Entry
311 // has nexthop empty'ed out, route needs to be removed from remote Dpns
312 if (updateRoutePath == null || updateRoutePath.isEmpty()
313 && originalRoutePath != null && !originalRoutePath.isEmpty() && usedRds.isEmpty()) {
314 LOG.trace("Original VRF entry had valid NH for destprefix {}. And the prefix is not an extra route."
315 + "This event is IGNORED here.", update.getDestPrefix());
318 //Update the used rds and vpntoextraroute containers only for the deleted nextHops.
319 List<String> nextHopsRemoved = FibHelper.getNextHopListFromRoutePaths(original);
320 nextHopsRemoved.removeAll(FibHelper.getNextHopListFromRoutePaths(update));
321 ListenableFuture<Void> future =
322 txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> nextHopsRemoved.parallelStream()
323 .forEach(nextHopRemoved -> fibUtil.updateUsedRdAndVpnToExtraRoute(
324 tx, nextHopRemoved, rd, update.getDestPrefix())));
325 Futures.addCallback(future, new FutureCallback<Void>() {
327 public void onSuccess(Void result) {
328 createFibEntries(identifier, update);
329 LOG.info("UPDATE: Updated static Fib Entry with rd {} prefix {} route-paths {}",
330 rd, update.getDestPrefix(), update.getRoutePaths());
334 public void onFailure(Throwable throwable) {
335 LOG.error("Exception encountered while submitting operational future for update vrfentry {}",
338 }, MoreExecutors.directExecutor());
342 //Handle all other routes only on a cluster reboot
343 if (original.equals(update)) {
345 createFibEntries(identifier, update);
346 LOG.info("UPDATE: Updated Non-static Fib Entry with rd {} prefix {} route-paths {}",
347 rd, update.getDestPrefix(), update.getRoutePaths());
351 LOG.info("UPDATE: Ignoring update for FIB entry with rd {} prefix {} route-origin {} route-paths {}",
352 rd, update.getDestPrefix(), update.getOrigin(), update.getRoutePaths());
355 private void createFibEntries(final InstanceIdentifier<VrfEntry> vrfEntryIid, final VrfEntry vrfEntry) {
356 final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
357 List<SubTransaction> txnObjects = new ArrayList<>();
358 final VpnInstanceOpDataEntry vpnInstance =
359 fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
360 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
361 Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId()
362 + " has null vpnId!");
363 final Collection<VpnToDpnList> vpnToDpnList;
364 if (vrfEntry.getParentVpnRd() != null
365 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
366 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
367 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
368 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
369 vpnInstance.getVpnToDpnList();
370 LOG.info("createFibEntries: Processing creation of PNF FIB entry with rd {} prefix {}",
371 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
373 vpnToDpnList = vpnInstance.getVpnToDpnList();
375 final Long vpnId = vpnInstance.getVpnId();
376 final String rd = vrfTableKey.getRouteDistinguisher();
377 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
378 if (subnetRoute != null) {
379 final long elanTag = subnetRoute.getElantag();
380 LOG.trace("SUBNETROUTE: createFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
381 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
382 if (vpnToDpnList != null) {
383 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
384 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
385 for (final VpnToDpnList curDpn : vpnToDpnList) {
386 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
387 installSubnetRouteInFib(curDpn.getDpnId(), elanTag, rd, vpnId, vrfEntry, tx);
388 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnId.longValue(),
389 vrfEntry, NwConstants.ADD_FLOW, tx);
396 // Get etherType value based on the IpPrefix address family type
399 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
400 } catch (IllegalArgumentException ex) {
401 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
405 final List<BigInteger> localDpnIdList = createLocalFibEntry(vpnInstance.getVpnId(), rd, vrfEntry, etherType);
406 if (!localDpnIdList.isEmpty() && vpnToDpnList != null) {
407 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
408 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
409 synchronized (vpnInstance.getVpnInstanceName().intern()) {
410 for (VpnToDpnList vpnDpn : vpnToDpnList) {
411 if (!localDpnIdList.contains(vpnDpn.getDpnId())) {
412 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
414 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
415 bgpRouteVrfEntryHandler.createRemoteFibEntry(vpnDpn.getDpnId(),
416 vpnId, vrfTableKey.getRouteDistinguisher(), vrfEntry, tx,
419 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
420 vrfTableKey.getRouteDistinguisher(), vrfEntry, tx);
422 } catch (NullPointerException e) {
423 LOG.error("Failed to get create remote fib flows for prefix {} ",
424 vrfEntry.getDestPrefix(), e);
433 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
434 if (optVpnUuid.isPresent()) {
435 String vpnUuid = optVpnUuid.get();
436 InterVpnLinkDataComposite interVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid).orNull();
437 if (interVpnLink != null) {
438 LOG.debug("InterVpnLink {} found in Cache linking Vpn {}", interVpnLink.getInterVpnLinkName(), vpnUuid);
439 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
440 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
441 // This is an static route that points to the other endpoint of an InterVpnLink
442 // In that case, we should add another entry in FIB table pointing to LPortDispatcher table.
443 installIVpnLinkSwitchingFlows(interVpnLink, vpnUuid, vrfEntry, vpnId);
444 installInterVpnRouteInLFib(interVpnLink, vpnUuid, vrfEntry, etherType);
451 void refreshFibTables(String rd, String prefix) {
452 InstanceIdentifier<VrfEntry> vrfEntryId =
453 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
454 .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
455 Optional<VrfEntry> vrfEntry = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
456 if (vrfEntry.isPresent()) {
457 createFibEntries(vrfEntryId, vrfEntry.get());
461 private Prefixes updateVpnReferencesInLri(LabelRouteInfo lri, String vpnInstanceName, boolean isPresentInList) {
462 LOG.debug("updating LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
463 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
464 prefixBuilder.setDpnId(lri.getDpnId());
465 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
466 prefixBuilder.setIpAddress(lri.getPrefix());
467 // Increment the refCount here
468 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
469 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
470 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri);
471 if (!isPresentInList) {
472 LOG.debug("vpnName {} is not present in LRI with label {}..", vpnInstanceName, lri.getLabel());
473 List<String> vpnInstanceNames = lri.getVpnInstanceList();
474 vpnInstanceNames.add(vpnInstanceName);
475 builder.setVpnInstanceList(vpnInstanceNames);
476 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
478 LOG.debug("vpnName {} is present in LRI with label {}..", vpnInstanceName, lri.getLabel());
480 return prefixBuilder.build();
483 void installSubnetRouteInFib(final BigInteger dpnId, final long elanTag, final String rd,
484 final long vpnId, final VrfEntry vrfEntry, WriteTransaction tx) {
486 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
487 newTx -> installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, newTx)), LOG,
488 "Error installing subnet route in FIB");
493 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
494 } catch (IllegalArgumentException ex) {
495 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
498 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
499 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
500 synchronized (label.toString().intern()) {
501 LabelRouteInfo lri = getLabelRouteInfo(label);
502 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
504 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
505 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
506 fibUtil.getVpnInstanceOpData(rd);
507 if (vpnInstanceOpDataEntryOptional.isPresent()) {
508 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
509 if (!lri.getVpnInstanceList().contains(vpnInstanceName)) {
510 updateVpnReferencesInLri(lri, vpnInstanceName, false);
514 LOG.debug("SUBNETROUTE: installSubnetRouteInFib: Fetched labelRouteInfo for label {} interface {}"
515 + " and got dpn {}", label, lri.getVpnInterfaceName(), lri.getDpnId());
519 final List<InstructionInfo> instructions = new ArrayList<>();
520 BigInteger subnetRouteMeta = BigInteger.valueOf(elanTag).shiftLeft(24)
521 .or(BigInteger.valueOf(vpnId).shiftLeft(1));
522 instructions.add(new InstructionWriteMetadata(subnetRouteMeta, MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
523 instructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
524 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
525 NwConstants.ADD_FLOW, tx, null);
526 if (vrfEntry.getRoutePaths() != null) {
527 for (RoutePaths routePath : vrfEntry.getRoutePaths()) {
528 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
529 List<ActionInfo> actionsInfos = new ArrayList<>();
530 // reinitialize instructions list for LFIB Table
531 final List<InstructionInfo> LFIBinstructions = new ArrayList<>();
532 actionsInfos.add(new ActionPopMpls(etherType));
533 LFIBinstructions.add(new InstructionApplyActions(actionsInfos));
534 LFIBinstructions.add(new InstructionWriteMetadata(subnetRouteMeta,
535 MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
536 LFIBinstructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
538 makeLFibTableEntry(dpnId, routePath.getLabel(), LFIBinstructions, DEFAULT_FIB_FLOW_PRIORITY,
539 NwConstants.ADD_FLOW, tx);
545 private void installSubnetBroadcastAddrDropRule(final BigInteger dpnId, final String rd, final long vpnId,
546 final VrfEntry vrfEntry, int addOrRemove, WriteTransaction tx) {
547 List<MatchInfo> matches = new ArrayList<>();
549 LOG.debug("SUBNETROUTE: installSubnetBroadcastAddrDropRule: destPrefix {} rd {} vpnId {} dpnId {}",
550 vrfEntry.getDestPrefix(), rd, vpnId, dpnId);
551 String[] ipAddress = vrfEntry.getDestPrefix().split("/");
552 String subnetBroadcastAddr = FibUtil.getBroadcastAddressFromCidr(vrfEntry.getDestPrefix());
553 final int prefixLength = ipAddress.length == 1 ? 0 : Integer.parseInt(ipAddress[1]);
555 InetAddress destPrefix;
557 destPrefix = InetAddress.getByName(subnetBroadcastAddr);
558 } catch (UnknownHostException e) {
559 LOG.error("Failed to get destPrefix for prefix {} rd {} VpnId {} DPN {}",
560 vrfEntry.getDestPrefix(), rd, vpnId, dpnId, e);
564 // Match on VpnId and SubnetBroadCast IP address
565 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
566 matches.add(MatchEthernetType.IPV4);
568 if (prefixLength != 0) {
569 matches.add(new MatchIpv4Destination(subnetBroadcastAddr, Integer.toString(IPV4_ADDR_PREFIX_LENGTH)));
572 //Action is to drop the packet
573 List<InstructionInfo> dropInstructions = new ArrayList<>();
574 List<ActionInfo> actionsInfos = new ArrayList<>();
575 actionsInfos.add(new ActionDrop());
576 dropInstructions.add(new InstructionApplyActions(actionsInfos));
578 int priority = DEFAULT_FIB_FLOW_PRIORITY + IPV4_ADDR_PREFIX_LENGTH;
579 String flowRef = FibUtil.getFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, rd, priority, destPrefix);
580 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef, priority,
581 flowRef, 0, 0, COOKIE_TABLE_MISS, matches, dropInstructions);
583 Flow flow = flowEntity.getFlowBuilder().build();
584 String flowId = flowEntity.getFlowId();
585 FlowKey flowKey = new FlowKey(new FlowId(flowId));
586 Node nodeDpn = FibUtil.buildDpnNode(dpnId);
588 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
589 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
590 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
592 if (addOrRemove == NwConstants.ADD_FLOW) {
593 tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId,flow, true);
595 tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
600 * For a given route, it installs a flow in LFIB that sets the lportTag of the other endpoint and sends to
601 * LportDispatcher table (via table 80)
603 private void installInterVpnRouteInLFib(final InterVpnLinkDataComposite interVpnLink, final String vpnName,
604 final VrfEntry vrfEntry, int etherType) {
605 // INTERVPN routes are routes in a Vpn1 that have been leaked to Vpn2. In DC-GW, this Vpn2 route is pointing
606 // to a list of DPNs where Vpn2's VpnLink was instantiated. In these DPNs LFIB must be programmed so that the
607 // packet is commuted from Vpn2 to Vpn1.
608 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
609 if (!interVpnLink.isActive()) {
610 LOG.warn("InterVpnLink {} is NOT ACTIVE. InterVpnLink flows for prefix={} wont be installed in LFIB",
611 interVpnLinkName, vrfEntry.getDestPrefix());
615 Optional<Long> optLportTag = interVpnLink.getEndpointLportTagByVpnName(vpnName);
616 if (!optLportTag.isPresent()) {
617 LOG.warn("Could not retrieve lportTag for VPN {} endpoint in InterVpnLink {}", vpnName, interVpnLinkName);
621 Long lportTag = optLportTag.get();
622 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).orElse(null);
624 LOG.error("Could not find label in vrfEntry=[prefix={} routePaths={}]. LFIB entry for InterVpnLink skipped",
625 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
628 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionPopMpls(etherType));
629 List<InstructionInfo> instructions = Arrays.asList(
630 new InstructionApplyActions(actionsInfos),
631 new InstructionWriteMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(lportTag.intValue(),
632 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
633 NwConstants.L3VPN_SERVICE_INDEX)),
634 MetaDataUtil.getMetaDataMaskForLPortDispatcher()),
635 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
636 List<String> interVpnNextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
637 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
639 for (BigInteger dpId : targetDpns) {
640 LOG.debug("Installing flow: VrfEntry=[prefix={} label={} nexthop={}] dpn {} for InterVpnLink {} in LFIB",
641 vrfEntry.getDestPrefix(), label, interVpnNextHopList, dpId, interVpnLink.getInterVpnLinkName());
643 makeLFibTableEntry(dpId, label, instructions, LFIB_INTERVPN_PRIORITY, NwConstants.ADD_FLOW,
650 * Installs the flows in FIB table that, for a given route, do the switching from one VPN to the other.
652 private void installIVpnLinkSwitchingFlows(final InterVpnLinkDataComposite interVpnLink, final String vpnUuid,
653 final VrfEntry vrfEntry, long vpnTag) {
654 Preconditions.checkNotNull(interVpnLink, "InterVpnLink cannot be null");
655 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null
656 && vrfEntry.getRoutePaths().size() == 1);
657 String destination = vrfEntry.getDestPrefix();
658 String nextHop = vrfEntry.getRoutePaths().get(0).getNexthopAddress();
659 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
661 // After having received a static route, we should check if the vpn is part of an inter-vpn-link.
662 // In that case, we should populate the FIB table of the VPN pointing to LPortDisptacher table
663 // using as metadata the LPortTag associated to that vpn in the inter-vpn-link.
664 if (interVpnLink.getState().or(State.Error) != State.Active) {
665 LOG.warn("Route to {} with nexthop={} cannot be installed because the interVpnLink {} is not active",
666 destination, nextHop, interVpnLinkName);
670 Optional<Long> optOtherEndpointLportTag = interVpnLink.getOtherEndpointLportTagByVpnName(vpnUuid);
671 if (!optOtherEndpointLportTag.isPresent()) {
672 LOG.warn("Could not find suitable LportTag for the endpoint opposite to vpn {} in interVpnLink {}",
673 vpnUuid, interVpnLinkName);
677 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnUuid);
678 if (targetDpns.isEmpty()) {
679 LOG.warn("Could not find DPNs for endpoint opposite to vpn {} in interVpnLink {}",
680 vpnUuid, interVpnLinkName);
684 String[] values = destination.split("/");
685 String destPrefixIpAddress = values[0];
686 int prefixLength = values.length == 1 ? 0 : Integer.parseInt(values[1]);
688 List<MatchInfo> matches = new ArrayList<>();
689 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnTag), MetaDataUtil.METADATA_MASK_VRFID));
690 matches.add(MatchEthernetType.IPV4);
692 if (prefixLength != 0) {
693 matches.add(new MatchIpv4Destination(destPrefixIpAddress, Integer.toString(prefixLength)));
696 List<Instruction> instructions =
697 Arrays.asList(new InstructionWriteMetadata(
698 MetaDataUtil.getMetaDataForLPortDispatcher(optOtherEndpointLportTag.get().intValue(),
699 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants
700 .L3VPN_SERVICE_INDEX)),
701 MetaDataUtil.getMetaDataMaskForLPortDispatcher()).buildInstruction(0),
702 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE).buildInstruction(1));
704 int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
705 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, destination, nextHop);
706 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef, priority, flowRef, 0, 0,
707 COOKIE_VM_FIB_TABLE, matches, instructions);
709 LOG.trace("Installing flow in FIB table for vpn {} interVpnLink {} nextHop {} key {}",
710 vpnUuid, interVpnLink.getInterVpnLinkName(), nextHop, flowRef);
712 for (BigInteger dpId : targetDpns) {
714 LOG.debug("Installing flow: VrfEntry=[prefix={} route-paths={}] dpn {} for InterVpnLink {} in FIB",
715 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(),
716 dpId, interVpnLink.getInterVpnLinkName());
718 mdsalManager.installFlow(dpId, flowEntity);
722 private List<BigInteger> createLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry, int etherType) {
723 List<BigInteger> returnLocalDpnId = new ArrayList<>();
724 String localNextHopIP = vrfEntry.getDestPrefix();
725 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
726 String vpnName = fibUtil.getVpnNameFromId(vpnId);
727 if (localNextHopInfo == null) {
728 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, localNextHopIP);
729 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
730 vpnName, usedRds, localNextHopIP);
731 boolean localNextHopSeen = false;
732 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
733 for (Routes vpnExtraRoute : vpnExtraRoutes) {
735 if (isIpv4Address(vpnExtraRoute.getNexthopIpList().get(0))) {
736 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
738 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
740 Prefixes localNextHopInfoLocal = fibUtil.getPrefixToInterface(vpnId,
742 if (localNextHopInfoLocal != null) {
743 localNextHopSeen = true;
745 checkCreateLocalFibEntry(localNextHopInfoLocal, localNextHopInfoLocal.getIpAddress(),
746 vpnId, rd, vrfEntry, vpnExtraRoute, vpnExtraRoutes, etherType);
747 returnLocalDpnId.add(dpnId);
750 if (!localNextHopSeen && RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
751 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
752 if (optionalLabel.isPresent()) {
753 Long label = optionalLabel.get();
754 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
755 synchronized (label.toString().intern()) {
756 LabelRouteInfo lri = getLabelRouteInfo(label);
757 if (isPrefixAndNextHopPresentInLri(localNextHopIP, nextHopAddressList, lri)) {
758 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
759 fibUtil.getVpnInstanceOpData(rd);
760 if (vpnInstanceOpDataEntryOptional.isPresent()) {
761 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
762 if (lri.getVpnInstanceList().contains(vpnInstanceName)) {
763 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, true);
764 localNextHopIP = lri.getPrefix();
766 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, false);
767 localNextHopIP = lri.getPrefix();
770 if (localNextHopInfo != null) {
771 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
772 label, localNextHopInfo.getVpnInterfaceName(), lri.getDpnId());
773 if (vpnExtraRoutes.isEmpty()) {
774 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
775 vpnId, rd, vrfEntry, null, vpnExtraRoutes, etherType);
776 returnLocalDpnId.add(dpnId);
778 for (Routes extraRoutes : vpnExtraRoutes) {
779 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
780 vpnId, rd, vrfEntry, extraRoutes, vpnExtraRoutes, etherType);
781 returnLocalDpnId.add(dpnId);
789 if (returnLocalDpnId.isEmpty()) {
790 LOG.error("Local DPNID is empty for rd {}, vpnId {}, vrfEntry {}", rd, vpnId, vrfEntry);
793 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP, vpnId,
794 rd, vrfEntry, /*routes*/ null, /*vpnExtraRoutes*/ null, etherType);
796 returnLocalDpnId.add(dpnId);
799 return returnLocalDpnId;
802 private BigInteger checkCreateLocalFibEntry(Prefixes localNextHopInfo, String localNextHopIP,
803 final Long vpnId, final String rd,
804 final VrfEntry vrfEntry,
805 Routes routes, List<Routes> vpnExtraRoutes,
807 String vpnName = fibUtil.getVpnNameFromId(vpnId);
808 if (localNextHopInfo != null) {
811 final BigInteger dpnId = localNextHopInfo.getDpnId();
812 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
813 LOG.debug("checkCreateLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
814 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
817 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
818 LOG.debug("checkCreateLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
819 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
822 if (!isVpnPresentInDpn(rd, dpnId)) {
823 LOG.error("checkCreateLocalFibEntry: The VPN with id {} rd {} is not available on dpn {}",
824 vpnId, rd, dpnId.toString());
825 return BigInteger.ZERO;
827 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
828 String interfaceName = localNextHopInfo.getVpnInterfaceName();
829 String prefix = vrfEntry.getDestPrefix();
830 String gwMacAddress = vrfEntry.getGatewayMacAddress();
831 //The loadbalancing group is created only if the extra route has multiple nexthops
832 //to avoid loadbalancing the discovered routes
833 if (vpnExtraRoutes != null && routes != null) {
834 if (isIpv4Address(routes.getNexthopIpList().get(0))) {
835 localNextHopIP = routes.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
837 localNextHopIP = routes.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
839 if (vpnExtraRoutes.size() > 1) {
840 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes,
842 localGroupId = nextHopManager.getLocalNextHopGroup(vpnId, localNextHopIP);
843 } else if (routes.getNexthopIpList().size() > 1) {
844 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes,
846 localGroupId = groupId;
848 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP,
849 prefix, gwMacAddress, jobKey);
850 localGroupId = groupId;
853 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP, prefix,
854 gwMacAddress, jobKey);
855 localGroupId = groupId;
857 if (groupId == FibConstants.INVALID_GROUP_ID) {
858 LOG.error("Unable to create Group for local prefix {} on rd {} for vpninterface {} on Node {}",
859 prefix, rd, interfaceName, dpnId.toString());
860 return BigInteger.ZERO;
862 final List<InstructionInfo> instructions = Collections.singletonList(
863 new InstructionApplyActions(
864 Collections.singletonList(new ActionGroup(groupId))));
865 final List<InstructionInfo> lfibinstructions = Collections.singletonList(
866 new InstructionApplyActions(
867 Arrays.asList(new ActionPopMpls(etherType), new ActionGroup(groupId))));
868 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
869 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
870 jobCoordinator.enqueueJob(jobKey, () -> {
871 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
872 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
873 NwConstants.ADD_FLOW, tx, null);
874 if (!fibUtil.enforceVxlanDatapathSemanticsforInternalRouterVpn(localNextHopInfo.getSubnetId(),
876 optLabel.ifPresent(label -> {
877 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
879 "Installing LFIB and tunnel table entry on dpn {} for interface {} with label "
880 + "{}, rd {}, prefix {}, nexthop {}", dpnId,
881 localNextHopInfo.getVpnInterfaceName(), optLabel, rd, vrfEntry.getDestPrefix(),
883 makeLFibTableEntry(dpnId, label, lfibinstructions, DEFAULT_FIB_FLOW_PRIORITY,
884 NwConstants.ADD_FLOW, tx);
885 // If the extra-route is reachable from VMs attached to the same switch,
886 // then the tunnel table can point to the load balancing group.
887 // If it is reachable from VMs attached to different switches,
888 // then it should be pointing to one of the local group in order to avoid looping.
889 if (vrfEntry.getRoutePaths().size() == 1) {
890 makeTunnelTableEntry(dpnId, label, groupId, tx);
892 makeTunnelTableEntry(dpnId, label, localGroupId, tx);
895 LOG.debug("Route with rd {} prefix {} label {} nexthop {} for vpn {} is an imported "
896 + "route. LFib and Terminating table entries will not be created.",
897 rd, vrfEntry.getDestPrefix(), optLabel, nextHopAddressList, vpnId);
905 LOG.error("localNextHopInfo received is null for prefix {} on rd {} on vpn {}", vrfEntry.getDestPrefix(), rd,
907 return BigInteger.ZERO;
910 private boolean isVpnPresentInDpn(String rd, BigInteger dpnId) {
911 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
912 Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
913 if (dpnInVpn.isPresent()) {
919 private LabelRouteInfo getLabelRouteInfo(Long label) {
920 InstanceIdentifier<LabelRouteInfo> lriIid = InstanceIdentifier.builder(LabelRouteMap.class)
921 .child(LabelRouteInfo.class, new LabelRouteInfoKey(label)).build();
922 Optional<LabelRouteInfo> opResult = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, lriIid);
923 if (opResult.isPresent()) {
924 return opResult.get();
929 private boolean deleteLabelRouteInfo(LabelRouteInfo lri, String vpnInstanceName, WriteTransaction tx) {
934 LOG.debug("deleting LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
935 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
936 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
938 List<String> vpnInstancesList = lri.getVpnInstanceList() != null
939 ? lri.getVpnInstanceList() : new ArrayList<>();
940 if (vpnInstancesList.contains(vpnInstanceName)) {
941 LOG.debug("vpninstance {} name is present", vpnInstanceName);
942 vpnInstancesList.remove(vpnInstanceName);
944 if (vpnInstancesList.isEmpty()) {
945 LOG.debug("deleting LRI instance object for label {}", lri.getLabel());
947 tx.delete(LogicalDatastoreType.OPERATIONAL, lriId);
949 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId);
953 LOG.debug("updating LRI instance object for label {}", lri.getLabel());
954 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri).setVpnInstanceList(vpnInstancesList);
955 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
960 void makeTunnelTableEntry(BigInteger dpId, long label, long groupId/*String egressInterfaceName*/,
961 WriteTransaction tx) {
962 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionGroup(groupId));
964 createTerminatingServiceActions(dpId, (int) label, actionsInfos, tx);
966 LOG.debug("Terminating service Entry for dpID {} : label : {} egress : {} installed successfully",
967 dpId, label, groupId);
970 public void createTerminatingServiceActions(BigInteger destDpId, int label, List<ActionInfo> actionsInfos,
971 WriteTransaction tx) {
972 List<MatchInfo> mkMatches = new ArrayList<>();
974 LOG.debug("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}",
975 destDpId, label, actionsInfos);
978 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
979 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
981 List<InstructionInfo> mkInstructions = new ArrayList<>();
982 mkInstructions.add(new InstructionApplyActions(actionsInfos));
984 FlowEntity terminatingServiceTableFlowEntity =
985 MDSALUtil.buildFlowEntity(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,
986 getTableMissFlowRef(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE, label), 5,
987 String.format("%s:%d", "TST Flow Entry ", label),
988 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, mkInstructions);
990 FlowKey flowKey = new FlowKey(new FlowId(terminatingServiceTableFlowEntity.getFlowId()));
992 FlowBuilder flowbld = terminatingServiceTableFlowEntity.getFlowBuilder();
994 Node nodeDpn = FibUtil.buildDpnNode(terminatingServiceTableFlowEntity.getDpnId());
995 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
996 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
997 .child(Table.class, new TableKey(terminatingServiceTableFlowEntity.getTableId()))
998 .child(Flow.class, flowKey).build();
999 tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId, flowbld.build(),
1000 WriteTransaction.CREATE_MISSING_PARENTS);
1003 private void removeTunnelTableEntry(BigInteger dpId, long label, WriteTransaction tx) {
1004 FlowEntity flowEntity;
1005 LOG.debug("remove terminatingServiceActions called with DpnId = {} and label = {}", dpId, label);
1006 List<MatchInfo> mkMatches = new ArrayList<>();
1007 // Matching metadata
1008 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
1009 flowEntity = MDSALUtil.buildFlowEntity(dpId,
1010 NwConstants.INTERNAL_TUNNEL_TABLE,
1011 getTableMissFlowRef(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, (int) label),
1012 5, String.format("%s:%d", "TST Flow Entry ", label), 0, 0,
1013 COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, null);
1014 Node nodeDpn = FibUtil.buildDpnNode(flowEntity.getDpnId());
1015 FlowKey flowKey = new FlowKey(new FlowId(flowEntity.getFlowId()));
1016 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1017 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1018 .child(Table.class, new TableKey(flowEntity.getTableId())).child(Flow.class, flowKey).build();
1020 tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
1021 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully", dpId, label);
1024 public List<BigInteger> deleteLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
1025 List<BigInteger> returnLocalDpnId = new ArrayList<>();
1026 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1027 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1028 boolean shouldUpdateNonEcmpLocalNextHop = true;
1029 if (localNextHopInfo == null) {
1030 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1031 if (usedRds.size() > 1) {
1032 LOG.error("The extra route prefix {} is still present in some DPNs in vpn {} on rd {}",
1033 vrfEntry.getDestPrefix(), vpnName, rd);
1034 return returnLocalDpnId;
1036 String vpnRd = (!usedRds.isEmpty()) ? usedRds.get(0) : rd;
1037 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency
1039 Optional<Routes> extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1040 vpnName, vpnRd, vrfEntry.getDestPrefix());
1041 if (extraRouteOptional.isPresent()) {
1042 Routes extraRoute = extraRouteOptional.get();
1044 if (isIpv4Address(extraRoute.getNexthopIpList().get(0))) {
1045 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
1047 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
1049 if (extraRoute.getNexthopIpList().size() > 1) {
1050 shouldUpdateNonEcmpLocalNextHop = false;
1052 localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1053 if (localNextHopInfo != null) {
1054 String localNextHopIP = localNextHopInfo.getIpAddress();
1055 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP,
1056 vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1057 if (!dpnId.equals(BigInteger.ZERO)) {
1058 LOG.trace("Deleting ECMP group for prefix {}, dpn {}", vrfEntry.getDestPrefix(), dpnId);
1059 nextHopManager.setupLoadBalancingNextHop(vpnId, dpnId,
1060 vrfEntry.getDestPrefix(), /*listBucketInfo*/ Collections.emptyList(),
1062 returnLocalDpnId.add(dpnId);
1065 LOG.error("localNextHopInfo unavailable while deleting prefix {} with rds {}, primary rd {} in "
1066 + "vpn {}", vrfEntry.getDestPrefix(), usedRds, rd, vpnName);
1070 if (localNextHopInfo == null) {
1071 /* Imported VRF entry */
1072 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1073 if (optionalLabel.isPresent()) {
1074 Long label = optionalLabel.get();
1075 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1076 LabelRouteInfo lri = getLabelRouteInfo(label);
1077 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1078 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1079 prefixBuilder.setDpnId(lri.getDpnId());
1080 BigInteger dpnId = checkDeleteLocalFibEntry(prefixBuilder.build(), nextHopAddressList.get(0),
1081 vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1082 if (!dpnId.equals(BigInteger.ZERO)) {
1083 returnLocalDpnId.add(dpnId);
1090 LOG.debug("Obtained prefix to interface for rd {} prefix {}", rd, vrfEntry.getDestPrefix());
1091 String localNextHopIP = localNextHopInfo.getIpAddress();
1092 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP,
1093 vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1094 if (!dpnId.equals(BigInteger.ZERO)) {
1095 returnLocalDpnId.add(dpnId);
1099 return returnLocalDpnId;
1102 private BigInteger checkDeleteLocalFibEntry(Prefixes localNextHopInfo, final String localNextHopIP,
1103 final Long vpnId, final String rd, final VrfEntry vrfEntry,
1104 boolean shouldUpdateNonEcmpLocalNextHop) {
1105 if (localNextHopInfo != null) {
1106 final BigInteger dpnId = localNextHopInfo.getDpnId();
1107 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
1108 LOG.debug("checkDeleteLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1109 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1112 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
1113 LOG.debug("checkDeleteLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1114 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1118 jobCoordinator.enqueueJob(FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix()),
1119 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1120 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1121 NwConstants.DEL_FLOW, tx, null);
1122 if (!fibUtil.enforceVxlanDatapathSemanticsforInternalRouterVpn(localNextHopInfo.getSubnetId(),
1124 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1125 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1126 makeLFibTableEntry(dpnId, label, null /* instructions */, DEFAULT_FIB_FLOW_PRIORITY,
1127 NwConstants.DEL_FLOW, tx);
1128 removeTunnelTableEntry(dpnId, label, tx);
1133 //TODO: verify below adjacency call need to be optimized (?)
1134 //In case of the removal of the extra route, the loadbalancing group is updated
1135 if (shouldUpdateNonEcmpLocalNextHop) {
1136 baseVrfEntryHandler.deleteLocalAdjacency(dpnId, vpnId, localNextHopIP, vrfEntry.getDestPrefix());
1140 return BigInteger.ZERO;
1143 private void createRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, String rd,
1144 final VrfEntry vrfEntry, WriteTransaction tx) {
1146 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(newTx -> {
1147 createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx);
1148 }), LOG, "Error creating remote FIB entry");
1152 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1153 LOG.debug("createremotefibentry: adding route {} for rd {} on remoteDpnId {}",
1154 vrfEntry.getDestPrefix(), rd, remoteDpnId);
1156 List<AdjacencyResult> adjacencyResults = baseVrfEntryHandler.resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
1157 if (adjacencyResults.isEmpty()) {
1158 LOG.error("Could not get interface for route-paths: {} in vpn {} on DPN {}",
1159 vrfEntry.getRoutePaths(), rd, remoteDpnId);
1160 LOG.error("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
1164 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1165 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
1166 vpnName, usedRds, vrfEntry.getDestPrefix());
1167 // create loadbalancing groups for extra routes only when the extra route is present behind
1169 if (!vpnExtraRoutes.isEmpty() && (vpnExtraRoutes.size() > 1
1170 || vpnExtraRoutes.get(0).getNexthopIpList().size() > 1)) {
1171 List<InstructionInfo> instructions = new ArrayList<>();
1172 // Obtain the local routes for this particular dpn.
1173 java.util.Optional<Routes> routes = vpnExtraRoutes
1176 Prefixes prefixToInterface = fibUtil.getPrefixToInterface(vpnId,
1177 fibUtil.getIpPrefix(route.getNexthopIpList().get(0)));
1178 if (prefixToInterface == null) {
1181 return remoteDpnId.equals(prefixToInterface.getDpnId());
1183 long groupId = nextHopManager.createNextHopGroups(vpnId, rd, remoteDpnId, vrfEntry,
1184 routes.isPresent() ? routes.get() : null, vpnExtraRoutes);
1185 if (groupId == FibConstants.INVALID_GROUP_ID) {
1186 LOG.error("Unable to create Group for local prefix {} on rd {} on Node {}",
1187 vrfEntry.getDestPrefix(), rd, remoteDpnId.toString());
1190 List<ActionInfo> actionInfos =
1191 Collections.singletonList(new ActionGroup(groupId));
1192 instructions.add(new InstructionApplyActions(actionInfos));
1193 baseVrfEntryHandler.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
1194 NwConstants.ADD_FLOW, tx, null);
1196 baseVrfEntryHandler.programRemoteFib(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, null);
1199 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
1202 protected void cleanUpOpDataForFib(Long vpnId, String primaryRd, final VrfEntry vrfEntry) {
1203 /* Get interface info from prefix to interface mapping;
1204 Use the interface info to get the corresponding vpn interface op DS entry,
1205 remove the adjacency corresponding to this fib entry.
1206 If adjacency removed is the last adjacency, clean up the following:
1207 - vpn interface from dpntovpn list, dpn if last vpn interface on dpn
1208 - prefix to interface entry
1209 - vpn interface op DS
1211 LOG.debug("Cleanup of prefix {} in VPN {}", vrfEntry.getDestPrefix(), vpnId);
1212 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1213 if (prefixInfo == null) {
1214 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1215 String usedRd = usedRds.isEmpty() ? primaryRd : usedRds.get(0);
1216 Routes extraRoute = baseVrfEntryHandler.getVpnToExtraroute(vpnId, usedRd, vrfEntry.getDestPrefix());
1217 if (extraRoute != null) {
1218 for (String nextHopIp : extraRoute.getNexthopIpList()) {
1219 LOG.debug("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
1220 if (nextHopIp != null) {
1222 if (isIpv4Address(nextHopIp)) {
1223 ipPrefix = nextHopIp + NwConstants.IPV4PREFIX;
1225 ipPrefix = nextHopIp + NwConstants.IPV6PREFIX;
1227 prefixInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1228 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1232 if (prefixInfo == null) {
1233 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1234 if (optionalLabel.isPresent()) {
1235 Long label = optionalLabel.get();
1236 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1237 LabelRouteInfo lri = getLabelRouteInfo(label);
1238 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1239 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1240 prefixBuilder.setDpnId(lri.getDpnId());
1241 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
1242 prefixBuilder.setIpAddress(lri.getPrefix());
1243 prefixInfo = prefixBuilder.build();
1244 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
1245 label, prefixInfo.getVpnInterfaceName(), lri.getDpnId());
1246 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1251 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, null /*Routes*/);
1255 private void checkCleanUpOpDataForFib(final Prefixes prefixInfo, final Long vpnId, final String rd,
1256 final VrfEntry vrfEntry, final Routes extraRoute) {
1258 if (prefixInfo == null) {
1259 LOG.error("Cleanup VPN Data Failed as unable to find prefix Info for prefix {} VpnId {} rd {}",
1260 vrfEntry.getDestPrefix(), vpnId, rd);
1261 return; //Don't have any info for this prefix (shouldn't happen); need to return
1264 if (Prefixes.PrefixCue.Nat.equals(prefixInfo.getPrefixCue())) {
1265 LOG.debug("NAT Prefix {} with vpnId {} rd {}. Skip FIB processing",
1266 vrfEntry.getDestPrefix(), vpnId, rd);
1270 String ifName = prefixInfo.getVpnInterfaceName();
1271 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName,
1272 new CleanupVpnInterfaceWorker(prefixInfo, vpnId, rd, vrfEntry, extraRoute));
1275 private class CleanupVpnInterfaceWorker implements Callable<List<ListenableFuture<Void>>> {
1276 Prefixes prefixInfo;
1282 CleanupVpnInterfaceWorker(final Prefixes prefixInfo, final Long vpnId, final String rd,
1283 final VrfEntry vrfEntry, final Routes extraRoute) {
1284 this.prefixInfo = prefixInfo;
1287 this.vrfEntry = vrfEntry;
1288 this.extraRoute = extraRoute;
1292 public List<ListenableFuture<Void>> call() {
1293 // If another renderer(for eg : CSS) needs to be supported, check can be performed here
1294 // to call the respective helpers.
1295 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
1296 //First Cleanup LabelRouteInfo
1297 //TODO(KIRAN) : Move the below block when addressing iRT/eRT for L3VPN Over VxLan
1298 LOG.debug("cleanupVpnInterfaceWorker: rd {} prefix {}", rd, prefixInfo.getIpAddress());
1299 if (VrfEntry.EncapType.Mplsgre.equals(vrfEntry.getEncapType())) {
1300 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1301 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1302 synchronized (label.toString().intern()) {
1303 LabelRouteInfo lri = getLabelRouteInfo(label);
1304 if (lri != null && lri.getPrefix().equals(vrfEntry.getDestPrefix())
1305 && nextHopAddressList.contains(lri.getNextHopIpList().get(0))) {
1306 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1307 fibUtil.getVpnInstanceOpData(rd);
1308 String vpnInstanceName = "";
1309 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1310 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1312 boolean lriRemoved = deleteLabelRouteInfo(lri, vpnInstanceName, tx);
1314 String parentRd = lri.getParentVpnRd();
1315 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1316 parentRd, vrfEntry.getDestPrefix()));
1319 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1320 rd, vrfEntry.getDestPrefix()));
1325 String ifName = prefixInfo.getVpnInterfaceName();
1326 Optional<String> optVpnName = fibUtil.getVpnNameFromRd(rd);
1327 String vpnName = null;
1329 if (Prefixes.PrefixCue.PhysNetFunc.equals(prefixInfo.getPrefixCue())) {
1330 /*Get vpnId for rd = networkId since op vpnInterface will be pointing to rd = networkId
1332 Optional<String> vpnNameOpt = fibUtil.getVpnNameFromRd(vrfEntry.getParentVpnRd());
1333 if (vpnNameOpt.isPresent()) {
1334 vpnId = fibUtil.getVpnId(vpnNameOpt.get());
1337 if (optVpnName.isPresent()) {
1338 vpnName = optVpnName.get();
1339 Optional<VpnInterfaceOpDataEntry> opVpnInterface = MDSALUtil
1340 .read(dataBroker, LogicalDatastoreType.OPERATIONAL,
1341 fibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1342 if (opVpnInterface.isPresent()) {
1343 long associatedVpnId = fibUtil.getVpnId(vpnName);
1344 if (vpnId != associatedVpnId) {
1345 LOG.warn("Prefixes {} are associated with different vpn instance with id {} rather than {}",
1346 vrfEntry.getDestPrefix(), associatedVpnId, vpnId);
1347 LOG.warn("Not proceeding with Cleanup op data for prefix {}", vrfEntry.getDestPrefix());
1350 LOG.debug("Processing cleanup of prefix {} associated with vpn {}",
1351 vrfEntry.getDestPrefix(), associatedVpnId);
1355 if (extraRoute != null) {
1356 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1357 //Only one used Rd present in case of removal event
1358 String usedRd = usedRds.get(0);
1359 if (optVpnName.isPresent()) {
1360 tx.delete(LogicalDatastoreType.OPERATIONAL,
1361 baseVrfEntryHandler.getVpnToExtrarouteIdentifier(vpnName, usedRd,
1362 vrfEntry.getDestPrefix()));
1363 tx.delete(LogicalDatastoreType.CONFIGURATION,
1364 VpnExtraRouteHelper.getUsedRdsIdentifier(vpnId, vrfEntry.getDestPrefix()));
1367 handleAdjacencyAndVpnOpInterfaceDeletion(vrfEntry, ifName, vpnName, tx);
1373 * Check all the adjacency in VpnInterfaceOpData and decide whether to delete the entire interface or only adj.
1374 * Remove Adjacency from VPNInterfaceOpData.
1375 * if Adjacency != primary.
1376 * if Adjacency == primary , then mark it for deletion.
1377 * Remove entire VPNinterfaceOpData Entry.
1378 * if sie of Adjacency <= 2 and all are marked for deletion , delete the entire VPNinterface Op entry.
1379 * @param vrfEntry - VrfEntry removed
1380 * @param ifName - Interface name from VRFentry
1381 * @param vpnName - VPN name of corresponding VRF
1382 * @param tx - ReadWrite Tx
1383 * @throws ReadFailedException - Exception thrown in case of read failed
1385 private void handleAdjacencyAndVpnOpInterfaceDeletion(VrfEntry vrfEntry, String ifName, String vpnName,
1386 ReadWriteTransaction tx) throws ReadFailedException {
1387 InstanceIdentifier<Adjacency> adjacencyIid =
1388 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix());
1389 Optional<Adjacency> adjacencyOptional = tx.read(LogicalDatastoreType.OPERATIONAL, adjacencyIid).checkedGet();
1390 if (adjacencyOptional.isPresent()) {
1391 if (adjacencyOptional.get().getAdjacencyType() != Adjacency.AdjacencyType.PrimaryAdjacency) {
1392 tx.delete(LogicalDatastoreType.OPERATIONAL,
1393 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix()));
1395 tx.merge(LogicalDatastoreType.OPERATIONAL, adjacencyIid,
1396 new AdjacencyBuilder(adjacencyOptional.get()).setMarkedForDeletion(true).build());
1400 Optional<AdjacenciesOp> optAdjacencies =
1401 tx.read(LogicalDatastoreType.OPERATIONAL,
1402 FibUtil.getAdjListPathOp(ifName, vpnName)).checkedGet();
1404 if (!optAdjacencies.isPresent() || optAdjacencies.get().getAdjacency() == null) {
1408 if (optAdjacencies.get().getAdjacency().stream().count() <= 2
1409 && optAdjacencies.get().getAdjacency().stream().allMatch(adjacency ->
1410 adjacency.getAdjacencyType() == Adjacency.AdjacencyType.PrimaryAdjacency
1411 && adjacency.isMarkedForDeletion() != null
1412 && adjacency.isMarkedForDeletion()
1414 LOG.info("Clean up vpn interface {} to vpn {} list.", ifName, vpnName);
1415 tx.delete(LogicalDatastoreType.OPERATIONAL,
1416 FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1420 private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
1421 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
1422 final String rd = vrfTableKey.getRouteDistinguisher();
1423 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
1424 if (vpnInstance == null) {
1425 LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
1428 final Collection<VpnToDpnList> vpnToDpnList;
1429 if (vrfEntry.getParentVpnRd() != null
1430 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
1431 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
1432 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
1433 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
1434 vpnInstance.getVpnToDpnList();
1435 LOG.info("deleteFibEntries: Processing deletion of PNF FIB entry with rd {} prefix {}",
1436 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
1438 vpnToDpnList = vpnInstance.getVpnToDpnList();
1441 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1442 final java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1443 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1444 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1445 if (subnetRoute != null) {
1446 long elanTag = subnetRoute.getElantag();
1447 LOG.trace("SUBNETROUTE: deleteFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
1448 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
1449 if (vpnToDpnList != null) {
1450 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1451 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1452 for (final VpnToDpnList curDpn : vpnToDpnList) {
1454 baseVrfEntryHandler.makeConnectedRoute(curDpn.getDpnId(), vpnInstance.getVpnId(), vrfEntry,
1455 vrfTableKey.getRouteDistinguisher(), null, NwConstants.DEL_FLOW, tx, null);
1456 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1457 optionalLabel.ifPresent(label -> makeLFibTableEntry(curDpn.getDpnId(), label, null,
1458 DEFAULT_FIB_FLOW_PRIORITY, NwConstants.DEL_FLOW, tx));
1461 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnInstance.getVpnId(),
1462 vrfEntry, NwConstants.DEL_FLOW, tx);
1466 optionalLabel.ifPresent(label -> {
1467 synchronized (label.toString().intern()) {
1468 LabelRouteInfo lri = getLabelRouteInfo(label);
1469 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1470 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1471 fibUtil.getVpnInstanceOpData(rd);
1472 String vpnInstanceName = "";
1473 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1474 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1476 boolean lriRemoved = this.deleteLabelRouteInfo(lri, vpnInstanceName, null);
1478 String parentRd = lri.getParentVpnRd();
1479 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1480 parentRd, vrfEntry.getDestPrefix()));
1481 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}"
1482 + " as labelRouteInfo cleared", label, rd, vrfEntry.getDestPrefix());
1485 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1486 rd, vrfEntry.getDestPrefix()));
1487 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}",
1488 label, rd, vrfEntry.getDestPrefix());
1495 final List<BigInteger> localDpnIdList = deleteLocalFibEntry(vpnInstance.getVpnId(),
1496 vrfTableKey.getRouteDistinguisher(), vrfEntry);
1497 if (vpnToDpnList != null) {
1498 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1499 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1501 Optional<Routes> extraRouteOptional;
1502 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
1503 if (usedRds != null && !usedRds.isEmpty()) {
1504 jobKey = FibUtil.getJobKeyForRdPrefix(usedRds.get(0), vrfEntry.getDestPrefix());
1505 if (usedRds.size() > 1) {
1506 LOG.error("The extra route prefix is still present in some DPNs");
1509 // The first rd is retrieved from usedrds as Only 1 rd would be present as extra route prefix
1510 //is not present in any other DPN
1511 extraRouteOptional = VpnExtraRouteHelper
1512 .getVpnExtraroutes(dataBroker, vpnName, usedRds.get(0), vrfEntry.getDestPrefix());
1515 jobKey = FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix());
1516 extraRouteOptional = Optional.absent();
1519 jobCoordinator.enqueueJob(jobKey,
1520 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1521 if (localDpnIdList.size() <= 0) {
1522 for (VpnToDpnList curDpn : vpnToDpnList) {
1523 baseVrfEntryHandler.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
1524 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional, tx);
1527 for (BigInteger localDpnId : localDpnIdList) {
1528 for (VpnToDpnList curDpn : vpnToDpnList) {
1529 if (!curDpn.getDpnId().equals(localDpnId)) {
1530 baseVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
1531 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional, tx);
1539 //The flow/group entry has been deleted from config DS; need to clean up associated operational
1540 //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
1541 cleanUpOpDataForFib(vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(), vrfEntry);
1543 // Remove all fib entries configured due to interVpnLink, when nexthop is the opposite endPoint
1544 // of the interVpnLink.
1545 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
1546 if (optVpnUuid.isPresent()) {
1547 String vpnUuid = optVpnUuid.get();
1548 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
1549 Optional<InterVpnLinkDataComposite> optInterVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid);
1550 if (optInterVpnLink.isPresent()) {
1551 InterVpnLinkDataComposite interVpnLink = optInterVpnLink.get();
1552 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
1553 // This is route that points to the other endpoint of an InterVpnLink
1554 // In that case, we should look for the FIB table pointing to
1555 // LPortDispatcher table and remove it.
1556 removeInterVPNLinkRouteFlows(interVpnLink, vpnUuid, vrfEntry);
1564 private void makeLFibTableEntry(BigInteger dpId, long label, List<InstructionInfo> instructions, int priority,
1565 int addOrRemove, WriteTransaction tx) {
1567 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
1568 newTx -> makeLFibTableEntry(dpId, label, instructions, priority, addOrRemove, newTx)), LOG,
1569 "Error making LFIB table entry");
1573 List<MatchInfo> matches = new ArrayList<>();
1574 matches.add(MatchEthernetType.MPLS_UNICAST);
1575 matches.add(new MatchMplsLabel(label));
1577 // Install the flow entry in L3_LFIB_TABLE
1578 String flowRef = FibUtil.getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, label, priority);
1580 FlowEntity flowEntity;
1581 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef, priority, flowRef, 0, 0,
1582 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
1583 Flow flow = flowEntity.getFlowBuilder().build();
1584 String flowId = flowEntity.getFlowId();
1585 FlowKey flowKey = new FlowKey(new FlowId(flowId));
1586 Node nodeDpn = FibUtil.buildDpnNode(dpId);
1587 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1588 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1589 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
1591 if (addOrRemove == NwConstants.ADD_FLOW) {
1592 tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId, flow, WriteTransaction.CREATE_MISSING_PARENTS);
1594 tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
1597 LOG.debug("LFIB Entry for dpID {} : label : {} instructions {} : key {} {} successfully",
1598 dpId, label, instructions, flowKey, NwConstants.ADD_FLOW == addOrRemove ? "ADDED" : "REMOVED");
1601 public void populateFibOnNewDpn(final BigInteger dpnId, final long vpnId, final String rd,
1602 final FutureCallback<List<Void>> callback) {
1603 LOG.trace("New dpn {} for vpn {} : populateFibOnNewDpn", dpnId, rd);
1604 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1606 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1607 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1608 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1609 LogicalDatastoreType.CONFIGURATION, id);
1610 List<ListenableFuture<Void>> futures = new ArrayList<>();
1611 if (!vrfTable.isPresent()) {
1612 LOG.info("populateFibOnNewDpn: dpn: {}: VRF Table not yet available for RD {}", dpnId, rd);
1613 if (callback != null) {
1614 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1615 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1619 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1620 futures.add(retryingTxRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
1621 for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
1622 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1623 if (subnetRoute != null) {
1624 long elanTag = subnetRoute.getElantag();
1625 installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, tx);
1626 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry, NwConstants.ADD_FLOW,
1630 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1631 if (routerInt != null) {
1632 LOG.trace("Router augmented vrfentry found rd:{}, uuid:{}, ip:{}, mac:{}",
1633 rd, routerInt.getUuid(), routerInt.getIpAddress(), routerInt.getMacAddress());
1634 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1635 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1636 NwConstants.ADD_FLOW);
1639 //Handle local flow creation for imports
1640 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1641 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1642 if (optionalLabel.isPresent()) {
1643 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1644 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1645 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList, lri)) {
1646 if (lri.getDpnId().equals(dpnId)) {
1648 int etherType = NWUtil.getEtherTypeFromIpPrefix(
1649 vrfEntry.getDestPrefix());
1650 createLocalFibEntry(vpnId, rd, vrfEntry, etherType);
1651 } catch (IllegalArgumentException ex) {
1652 LOG.warn("Unable to get etherType for IP Prefix {}",
1653 vrfEntry.getDestPrefix());
1660 boolean shouldCreateRemoteFibEntry = shouldCreateFibEntryForVrfAndVpnIdOnDpn(vpnId,
1662 if (shouldCreateRemoteFibEntry) {
1663 LOG.trace("Will create remote FIB entry for vrfEntry {} on DPN {}", vrfEntry, dpnId);
1664 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1665 List<SubTransaction> txnObjects = new ArrayList<>();
1666 bgpRouteVrfEntryHandler.createRemoteFibEntry(dpnId, vpnId,
1667 vrfTable.get().getRouteDistinguisher(), vrfEntry, tx, txnObjects);
1669 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
1675 if (callback != null) {
1676 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1677 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1684 public void populateExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1685 final String localNextHopIp, final String remoteNextHopIp) {
1686 LOG.trace("populateExternalRoutesOnDpn : dpn {}, vpn {}, rd {}, localNexthopIp {} , remoteNextHopIp {} ",
1687 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1688 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1689 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1690 List<SubTransaction> txnObjects = new ArrayList<>();
1691 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1692 if (vrfTable.isPresent()) {
1693 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1694 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1695 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1696 vrfTable.get().getVrfEntry().stream()
1697 .filter(vrfEntry -> RouteOrigin.BGP == RouteOrigin.value(vrfEntry.getOrigin()))
1698 .forEach(bgpRouteVrfEntryHandler.getConsumerForCreatingRemoteFib(dpnId, vpnId,
1699 rd, remoteNextHopIp, vrfTable, tx, txnObjects));
1705 public void manageRemoteRouteOnDPN(final boolean action,
1706 final BigInteger localDpnId,
1709 final String destPrefix,
1710 final String destTepIp,
1712 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1714 if (vpnInstance == null) {
1715 LOG.error("VpnInstance for rd {} not present for prefix {}", rd, destPrefix);
1719 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, localDpnId),
1720 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1721 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1722 VrfTablesKey vrfTablesKey = new VrfTablesKey(rd);
1723 VrfEntry vrfEntry = getVrfEntry(dataBroker, rd, destPrefix);
1724 if (vrfEntry == null) {
1727 LOG.trace("manageRemoteRouteOnDPN :: action {}, DpnId {}, vpnId {}, rd {}, destPfx {}",
1728 action, localDpnId, vpnId, rd, destPrefix);
1729 List<RoutePaths> routePathList = vrfEntry.getRoutePaths();
1730 VrfEntry modVrfEntry;
1731 if (routePathList == null || routePathList.isEmpty()) {
1732 modVrfEntry = FibHelper.getVrfEntryBuilder(vrfEntry, label,
1733 Collections.singletonList(destTepIp),
1734 RouteOrigin.value(vrfEntry.getOrigin()), null /* parentVpnRd */).build();
1736 modVrfEntry = vrfEntry;
1740 LOG.trace("manageRemoteRouteOnDPN updated(add) vrfEntry :: {}", modVrfEntry);
1741 createRemoteFibEntry(localDpnId, vpnId, vrfTablesKey.getRouteDistinguisher(),
1744 LOG.trace("manageRemoteRouteOnDPN updated(remove) vrfEntry :: {}", modVrfEntry);
1745 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnInstance.getVpnId(),
1746 vrfEntry.getDestPrefix());
1747 if (usedRds.size() > 1) {
1748 LOG.debug("The extra route prefix is still present in some DPNs");
1751 //Is this fib route an extra route? If yes, get the nexthop which would be
1752 //an adjacency in the vpn
1753 Optional<Routes> extraRouteOptional = Optional.absent();
1754 if (usedRds.size() != 0) {
1755 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1756 fibUtil.getVpnNameFromId(vpnInstance.getVpnId()),
1757 usedRds.get(0), vrfEntry.getDestPrefix());
1759 baseVrfEntryHandler.deleteRemoteRoute(null, localDpnId, vpnId, vrfTablesKey, modVrfEntry,
1760 extraRouteOptional, tx);
1766 public void cleanUpDpnForVpn(final BigInteger dpnId, final long vpnId, final String rd,
1767 final FutureCallback<List<Void>> callback) {
1768 LOG.trace("cleanUpDpnForVpn: Remove dpn {} for vpn {} : cleanUpDpnForVpn", dpnId, rd);
1769 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1771 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1772 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1773 List<SubTransaction> txnObjects = new ArrayList<>();
1774 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1775 LogicalDatastoreType.CONFIGURATION, id);
1776 List<ListenableFuture<Void>> futures = new ArrayList<>();
1777 if (vrfTable.isPresent()) {
1778 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1779 futures.add(retryingTxRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1780 for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
1781 /* Handle subnet routes here */
1782 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1783 if (subnetRoute != null) {
1784 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Cleaning subnetroute {} on dpn {}"
1785 + " for vpn {}", vrfEntry.getDestPrefix(), dpnId, rd);
1786 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1787 NwConstants.DEL_FLOW, tx, null);
1788 List<RoutePaths> routePaths = vrfEntry.getRoutePaths();
1789 if (routePaths != null) {
1790 for (RoutePaths routePath : routePaths) {
1791 makeLFibTableEntry(dpnId, routePath.getLabel(), null,
1792 DEFAULT_FIB_FLOW_PRIORITY,
1793 NwConstants.DEL_FLOW, tx);
1794 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Released subnetroute label {}"
1795 + " for rd {} prefix {}", routePath.getLabel(), rd,
1796 vrfEntry.getDestPrefix());
1799 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry,
1800 NwConstants.DEL_FLOW, tx);
1803 // ping responder for router interfaces
1804 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1805 if (routerInt != null) {
1806 LOG.trace("Router augmented vrfentry found for rd:{}, uuid:{}, ip:{}, mac:{}",
1807 rd, routerInt.getUuid(), routerInt.getIpAddress(),
1808 routerInt.getMacAddress());
1809 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1810 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1811 NwConstants.DEL_FLOW);
1815 //Handle local flow deletion for imports
1816 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1817 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1818 if (optionalLabel.isPresent()) {
1819 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1820 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1821 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList,
1822 lri) && lri.getDpnId().equals(dpnId)) {
1823 deleteLocalFibEntry(vpnId, rd, vrfEntry);
1828 // Passing null as we don't know the dpn
1829 // to which prefix is attached at this point
1830 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1831 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1832 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1833 Optional<Routes> extraRouteOptional;
1834 //Is this fib route an extra route? If yes, get the nexthop which would be
1835 //an adjacency in the vpn
1836 if (usedRds != null && !usedRds.isEmpty()) {
1837 if (usedRds.size() > 1) {
1838 LOG.error("The extra route prefix is still present in some DPNs");
1841 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
1842 usedRds.get(0), vrfEntry.getDestPrefix());
1846 extraRouteOptional = Optional.absent();
1848 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1849 bgpRouteVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1850 vrfTable.get().key(), vrfEntry, extraRouteOptional, tx, txnObjects);
1852 baseVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId, vrfTable.get().key(),
1853 vrfEntry, extraRouteOptional, tx);
1858 if (callback != null) {
1859 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1860 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1863 LOG.error("cleanUpDpnForVpn: No vrf table found for rd {} vpnId {} dpn {}", rd, vpnId, dpnId);
1870 public void cleanUpExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1871 final String localNextHopIp, final String remoteNextHopIp) {
1872 LOG.trace("cleanUpExternalRoutesOnDpn : cleanup remote routes on dpn {} for vpn {}, rd {}, "
1873 + " localNexthopIp {} , remoteNexhtHopIp {}",
1874 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1875 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1876 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1877 List<SubTransaction> txnObjects = new ArrayList<>();
1878 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1879 if (vrfTable.isPresent()) {
1880 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1882 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1883 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
1884 tx -> vrfTable.get().getVrfEntry().stream()
1885 .filter(vrfEntry -> RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP)
1886 .forEach(bgpRouteVrfEntryHandler.getConsumerForDeletingRemoteFib(dpnId, vpnId,
1887 remoteNextHopIp, vrfTable, tx, txnObjects))));
1893 public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
1894 InstanceIdentifierBuilder<VrfTables> idBuilder =
1895 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
1896 return idBuilder.build();
1899 private String getInterVpnFibFlowRef(String interVpnLinkName, String prefix, String nextHop) {
1900 return FLOWID_PREFIX + interVpnLinkName + NwConstants.FLOWID_SEPARATOR + prefix + NwConstants
1901 .FLOWID_SEPARATOR + nextHop;
1904 private String getTableMissFlowRef(BigInteger dpnId, short tableId, int tableMiss) {
1905 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR
1906 + tableMiss + FLOWID_PREFIX;
1909 private VrfEntry getVrfEntry(DataBroker broker, String rd, String ipPrefix) {
1910 InstanceIdentifier<VrfEntry> vrfEntryId = InstanceIdentifier.builder(FibEntries.class)
1911 .child(VrfTables.class, new VrfTablesKey(rd))
1912 .child(VrfEntry.class, new VrfEntryKey(ipPrefix)).build();
1913 Optional<VrfEntry> vrfEntry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
1914 if (vrfEntry.isPresent()) {
1915 return vrfEntry.get();
1920 public void removeInterVPNLinkRouteFlows(final InterVpnLinkDataComposite interVpnLink,
1921 final String vpnName,
1922 final VrfEntry vrfEntry) {
1923 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null && vrfEntry.getRoutePaths().size() == 1);
1925 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
1926 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
1928 if (targetDpns.isEmpty()) {
1929 LOG.warn("Could not find DPNs for VPN {} in InterVpnLink {}", vpnName, interVpnLinkName);
1933 java.util.Optional<String> optNextHop = FibUtil.getFirstNextHopAddress(vrfEntry);
1934 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1938 optNextHop.ifPresent(nextHop -> {
1939 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, vrfEntry.getDestPrefix(), nextHop);
1940 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
1941 Flow flow = new FlowBuilder().withKey(flowKey).setId(new FlowId(flowRef))
1942 .setTableId(NwConstants.L3_FIB_TABLE).setFlowName(flowRef).build();
1944 LOG.trace("Removing flow in FIB table for interVpnLink {} key {}", interVpnLinkName, flowRef);
1945 for (BigInteger dpId : targetDpns) {
1946 LOG.debug("Removing flow: VrfEntry=[prefix={} nexthop={}] dpn {} for InterVpnLink {} in FIB",
1947 vrfEntry.getDestPrefix(), nextHop, dpId, interVpnLinkName);
1949 mdsalManager.removeFlow(dpId, flow);
1955 optLabel.ifPresent(label -> {
1956 LOG.trace("Removing flow in FIB table for interVpnLink {}", interVpnLinkName);
1958 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1959 for (BigInteger dpId : targetDpns) {
1960 LOG.debug("Removing flow: VrfEntry=[prefix={} label={}] dpn {} for InterVpnLink {} in LFIB",
1961 vrfEntry.getDestPrefix(), label, dpId, interVpnLinkName);
1962 makeLFibTableEntry(dpId, label, /*instructions*/null, LFIB_INTERVPN_PRIORITY, NwConstants.DEL_FLOW,
1965 }), LOG, "Error removing flows");
1969 private boolean isPrefixAndNextHopPresentInLri(String prefix,
1970 List<String> nextHopAddressList, LabelRouteInfo lri) {
1971 return lri != null && lri.getPrefix().equals(prefix)
1972 && nextHopAddressList.contains(lri.getNextHopIpList().get(0));
1975 private boolean shouldCreateFibEntryForVrfAndVpnIdOnDpn(Long vpnId, VrfEntry vrfEntry, BigInteger dpnId) {
1976 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
1980 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1981 if (prefix != null) {
1982 BigInteger prefixDpnId = prefix.getDpnId();
1983 if (dpnId.equals(prefixDpnId)) {
1984 LOG.trace("Should not create remote FIB entry for vrfEntry {} on DPN {}",