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.controller.md.sal.binding.api.WriteTransaction.CREATE_MISSING_PARENTS;
11 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
13 import static org.opendaylight.genius.mdsalutil.NWUtil.isIpv4Address;
14 import static org.opendaylight.netvirt.fibmanager.FibUtil.nullToEmpty;
16 import com.google.common.base.Optional;
17 import com.google.common.base.Preconditions;
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture;
21 import com.google.common.util.concurrent.MoreExecutors;
22 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
23 import java.math.BigInteger;
24 import java.net.InetAddress;
25 import java.net.UnknownHostException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.Objects;
32 import java.util.concurrent.Callable;
33 import java.util.concurrent.CopyOnWriteArrayList;
34 import java.util.concurrent.ExecutionException;
35 import java.util.concurrent.locks.ReentrantLock;
36 import javax.annotation.Nullable;
37 import javax.annotation.PostConstruct;
38 import javax.inject.Inject;
39 import javax.inject.Singleton;
40 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
41 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
42 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
43 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
44 import org.opendaylight.genius.infra.Datastore.Configuration;
45 import org.opendaylight.genius.infra.Datastore.Operational;
46 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
47 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
48 import org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner;
49 import org.opendaylight.genius.infra.TransactionAdapter;
50 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
51 import org.opendaylight.genius.infra.TypedWriteTransaction;
52 import org.opendaylight.genius.mdsalutil.ActionInfo;
53 import org.opendaylight.genius.mdsalutil.FlowEntity;
54 import org.opendaylight.genius.mdsalutil.InstructionInfo;
55 import org.opendaylight.genius.mdsalutil.MDSALUtil;
56 import org.opendaylight.genius.mdsalutil.MatchInfo;
57 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
58 import org.opendaylight.genius.mdsalutil.NWUtil;
59 import org.opendaylight.genius.mdsalutil.NwConstants;
60 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
61 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
62 import org.opendaylight.genius.mdsalutil.actions.ActionPopMpls;
63 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
64 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
65 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
66 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
67 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
68 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
69 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
70 import org.opendaylight.genius.mdsalutil.matches.MatchMplsLabel;
71 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
72 import org.opendaylight.genius.utils.JvmGlobalLocks;
73 import org.opendaylight.genius.utils.ServiceIndex;
74 import org.opendaylight.genius.utils.batching.SubTransaction;
75 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
76 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
77 import org.opendaylight.netvirt.elanmanager.api.IElanService;
78 import org.opendaylight.netvirt.fibmanager.NexthopManager.AdjacencyResult;
79 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
80 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
81 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
82 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
83 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
84 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
85 import org.opendaylight.serviceutils.upgrade.UpgradeState;
86 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.LabelRouteMap;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.RouterInterface;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRoute;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfo;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoBuilder;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoKey;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOp;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.PrefixesBuilder;
114 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
115 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
116 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
117 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
118 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState.State;
119 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
120 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
121 import org.slf4j.Logger;
122 import org.slf4j.LoggerFactory;
126 public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry, VrfEntryListener> {
128 private static final Logger LOG = LoggerFactory.getLogger(VrfEntryListener.class);
129 private static final String FLOWID_PREFIX = "L3.";
130 private static final BigInteger COOKIE_VM_FIB_TABLE = new BigInteger("8000003", 16);
131 private static final int DEFAULT_FIB_FLOW_PRIORITY = 10;
132 private static final int IPV4_ADDR_PREFIX_LENGTH = 32;
133 private static final int LFIB_INTERVPN_PRIORITY = 15;
134 public static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
135 private static final int MAX_RETRIES = 3;
136 private static final BigInteger COOKIE_TABLE_MISS = new BigInteger("8000004", 16);
138 private final DataBroker dataBroker;
139 private final ManagedNewTransactionRunner txRunner;
140 private final RetryingManagedNewTransactionRunner retryingTxRunner;
141 private final IMdsalApiManager mdsalManager;
142 private final NexthopManager nextHopManager;
143 private final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler;
144 private final BaseVrfEntryHandler baseVrfEntryHandler;
145 private final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler;
146 private final JobCoordinator jobCoordinator;
147 private final IElanService elanManager;
148 private final FibUtil fibUtil;
149 private final InterVpnLinkCache interVpnLinkCache;
150 private final List<AutoCloseable> closeables = new CopyOnWriteArrayList<>();
151 private final UpgradeState upgradeState;
152 private final DataTreeEventCallbackRegistrar eventCallbacks;
155 public VrfEntryListener(final DataBroker dataBroker, final IMdsalApiManager mdsalApiManager,
156 final NexthopManager nexthopManager,
157 final IElanService elanManager,
158 final BaseVrfEntryHandler vrfEntryHandler,
159 final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler,
160 final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler,
161 final JobCoordinator jobCoordinator,
162 final FibUtil fibUtil,
163 final InterVpnLinkCache interVpnLinkCache,
164 final UpgradeState upgradeState,
165 final DataTreeEventCallbackRegistrar eventCallbacks) {
166 super(VrfEntry.class, VrfEntryListener.class);
167 this.dataBroker = dataBroker;
168 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
169 this.retryingTxRunner = new RetryingManagedNewTransactionRunner(dataBroker, MAX_RETRIES);
170 this.mdsalManager = mdsalApiManager;
171 this.nextHopManager = nexthopManager;
172 this.elanManager = elanManager;
173 this.baseVrfEntryHandler = vrfEntryHandler;
174 this.bgpRouteVrfEntryHandler = bgpRouteVrfEntryHandler;
175 this.routerInterfaceVrfEntryHandler = routerInterfaceVrfEntryHandler;
176 this.jobCoordinator = jobCoordinator;
177 this.fibUtil = fibUtil;
178 this.interVpnLinkCache = interVpnLinkCache;
179 this.upgradeState = upgradeState;
180 this.eventCallbacks = eventCallbacks;
186 LOG.info("{} init", getClass().getSimpleName());
187 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
191 @SuppressWarnings("checkstyle:IllegalCatch")
192 public void close() {
193 closeables.forEach(c -> {
196 } catch (Exception e) {
197 LOG.warn("Error closing {}", c, e);
203 protected VrfEntryListener getDataTreeChangeListener() {
204 return VrfEntryListener.this;
208 protected InstanceIdentifier<VrfEntry> getWildCardPath() {
209 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class).child(VrfEntry.class);
213 protected void add(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
214 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
215 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
216 LOG.debug("ADD: Adding Fib Entry rd {} prefix {} route-paths {}",
217 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
218 addFibEntries(identifier, vrfEntry, rd);
219 LOG.info("ADD: Added Fib Entry rd {} prefix {} route-paths {}",
220 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
223 //This method is temporary. Eventually Factory design pattern will be used to get
224 // right VrfEntryhandle and invoke its methods.
225 private void addFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
226 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
227 bgpRouteVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
230 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
231 LOG.info("EVPN flows need to be programmed.");
232 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
233 nextHopManager, jobCoordinator, elanManager, fibUtil, upgradeState, eventCallbacks);
234 evpnVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
235 closeables.add(evpnVrfEntryHandler);
238 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
239 if (routerInt != null) {
240 // ping responder for router interfaces
241 routerInterfaceVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
244 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
245 createFibEntries(identifier, vrfEntry);
251 protected void remove(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry) {
252 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
253 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
254 LOG.debug("REMOVE: Removing Fib Entry rd {} prefix {} route-paths {}",
255 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
256 removeFibEntries(identifier, vrfEntry, rd);
257 LOG.info("REMOVE: Removed Fib Entry rd {} prefix {} route-paths {}",
258 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
261 //This method is temporary. Eventually Factory design pattern will be used to get
262 // right VrfEntryhandle and invoke its methods.
263 private void removeFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
264 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
265 LOG.info("EVPN flows to be deleted");
266 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
267 nextHopManager, jobCoordinator, elanManager, fibUtil, upgradeState, eventCallbacks);
268 evpnVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
269 closeables.add(evpnVrfEntryHandler);
272 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
273 if (routerInt != null) {
274 // ping responder for router interfaces
275 routerInterfaceVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
278 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
279 deleteFibEntries(identifier, vrfEntry);
282 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
283 bgpRouteVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
289 // "Redundant nullcheck of originalRoutePath, which is known to be non-null" - the null checking for
290 // originalRoutePath is a little dicey - safest to keep the checking even if not needed.
291 @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE")
292 protected void update(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update) {
293 Preconditions.checkNotNull(update, "VrfEntry should not be null or empty.");
294 final String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
295 LOG.debug("UPDATE: Updating Fib Entries to rd {} prefix {} route-paths {} origin {} old-origin {}", rd,
296 update.getDestPrefix(), update.getRoutePaths(), update.getOrigin(), original.getOrigin());
297 // Handle BGP Routes first
298 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.BGP) {
299 bgpRouteVrfEntryHandler.updateFlows(identifier, original, update, rd);
300 LOG.info("UPDATE: Updated BGP advertised Fib Entry with rd {} prefix {} route-paths {}",
301 rd, update.getDestPrefix(), update.getRoutePaths());
305 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.STATIC) {
306 List<RoutePaths> originalRoutePath = original.getRoutePaths();
307 List<RoutePaths> updateRoutePath = update.getRoutePaths();
308 LOG.info("UPDATE: Original route-path {} update route-path {} ", originalRoutePath, updateRoutePath);
310 //Updates need to be handled for extraroute even if original vrf entry route path is null or
311 //updated vrf entry route path is null. This can happen during tunnel events.
312 Optional<VpnInstanceOpDataEntry> optVpnInstance = fibUtil.getVpnInstanceOpData(rd);
313 List<String> usedRds = new ArrayList<>();
314 if (optVpnInstance.isPresent()) {
315 usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,optVpnInstance.get().getVpnId(),
316 update.getDestPrefix());
318 // If original VRF Entry had nexthop null , but update VRF Entry
319 // has nexthop , route needs to be created on remote Dpns
320 if (originalRoutePath == null || originalRoutePath.isEmpty()
321 && updateRoutePath != null && !updateRoutePath.isEmpty() && usedRds.isEmpty()) {
322 // TODO(vivek): Though ugly, Not handling this code now, as each
323 // tep add event will invoke flow addition
324 LOG.trace("Original VRF entry NH is null for destprefix {}. And the prefix is not an extra route."
325 + " This event is IGNORED here.", update.getDestPrefix());
329 // If original VRF Entry had valid nexthop , but update VRF Entry
330 // has nexthop empty'ed out, route needs to be removed from remote Dpns
331 if (updateRoutePath == null || updateRoutePath.isEmpty()
332 && originalRoutePath != null && !originalRoutePath.isEmpty() && usedRds.isEmpty()) {
333 LOG.trace("Original VRF entry had valid NH for destprefix {}. And the prefix is not an extra route."
334 + "This event is IGNORED here.", update.getDestPrefix());
337 //Update the used rds and vpntoextraroute containers only for the deleted nextHops.
338 List<String> nextHopsRemoved = FibHelper.getNextHopListFromRoutePaths(original);
339 nextHopsRemoved.removeAll(FibHelper.getNextHopListFromRoutePaths(update));
340 List<ListenableFuture<Void>> futures = new ArrayList<>();
341 ListenableFuture<Void> configFuture =
342 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx ->
343 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, operTx ->
344 nextHopsRemoved.parallelStream()
345 .forEach(nextHopRemoved -> {
347 fibUtil.updateUsedRdAndVpnToExtraRoute(
348 configTx, operTx, nextHopRemoved, rd, update.getDestPrefix());
349 } catch (ExecutionException | InterruptedException e) {
350 throw new RuntimeException(e);
353 futures.add(configFuture);
354 Futures.addCallback(configFuture, new FutureCallback<Void>() {
356 public void onSuccess(Void result) {
357 createFibEntries(identifier, update);
358 LOG.info("UPDATE: Updated static Fib Entry with rd {} prefix {} route-paths {}",
359 rd, update.getDestPrefix(), update.getRoutePaths());
363 public void onFailure(Throwable throwable) {
364 LOG.error("Exception encountered while submitting operational future for update vrfentry {}",
367 }, MoreExecutors.directExecutor());
371 //Handle all other routes only on a cluster reboot
372 if (original.equals(update)) {
374 createFibEntries(identifier, update);
375 LOG.info("UPDATE: Updated Non-static Fib Entry with rd {} prefix {} route-paths {}",
376 rd, update.getDestPrefix(), update.getRoutePaths());
380 LOG.info("UPDATE: Ignoring update for FIB entry with rd {} prefix {} route-origin {} route-paths {}",
381 rd, update.getDestPrefix(), update.getOrigin(), update.getRoutePaths());
384 private void createFibEntries(final InstanceIdentifier<VrfEntry> vrfEntryIid, final VrfEntry vrfEntry) {
385 final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
386 List<SubTransaction> txnObjects = new ArrayList<>();
387 final VpnInstanceOpDataEntry vpnInstance =
388 fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
389 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
390 Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId()
391 + " has null vpnId!");
392 final Collection<VpnToDpnList> vpnToDpnList;
393 if (vrfEntry.getParentVpnRd() != null
394 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
395 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
396 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
397 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
398 vpnInstance.getVpnToDpnList();
399 LOG.info("createFibEntries: Processing creation of PNF FIB entry with rd {} prefix {}",
400 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
402 vpnToDpnList = vpnInstance.getVpnToDpnList();
404 final Long vpnId = vpnInstance.getVpnId();
405 final String rd = vrfTableKey.getRouteDistinguisher();
406 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
407 if (subnetRoute != null) {
408 final long elanTag = subnetRoute.getElantag();
409 LOG.trace("SUBNETROUTE: createFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
410 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
411 if (vpnToDpnList != null) {
412 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
413 () -> Collections.singletonList(
414 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
415 for (final VpnToDpnList curDpn : vpnToDpnList) {
416 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
417 installSubnetRouteInFib(curDpn.getDpnId(), elanTag, rd, vpnId, vrfEntry, tx);
418 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnId.longValue(),
419 vrfEntry, NwConstants.ADD_FLOW, tx);
426 // Get etherType value based on the IpPrefix address family type
429 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
430 } catch (IllegalArgumentException ex) {
431 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
435 final List<BigInteger> localDpnIdList = createLocalFibEntry(vpnInstance.getVpnId(), rd, vrfEntry, etherType);
436 if (!localDpnIdList.isEmpty() && vpnToDpnList != null) {
437 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
438 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
439 final ReentrantLock lock = lockFor(vpnInstance);
442 for (VpnToDpnList vpnDpn : vpnToDpnList) {
443 if (!localDpnIdList.contains(vpnDpn.getDpnId())) {
444 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
446 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
447 bgpRouteVrfEntryHandler.createRemoteFibEntry(vpnDpn.getDpnId(),
448 vpnId, vrfTableKey.getRouteDistinguisher(), vrfEntry,
449 TransactionAdapter.toWriteTransaction(tx),
452 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
453 vrfTableKey.getRouteDistinguisher(), vrfEntry, tx);
455 } catch (NullPointerException e) {
456 LOG.error("Failed to get create remote fib flows for prefix {} ",
457 vrfEntry.getDestPrefix(), e);
468 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
469 if (optVpnUuid.isPresent()) {
470 String vpnUuid = optVpnUuid.get();
471 InterVpnLinkDataComposite interVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid).orNull();
472 if (interVpnLink != null) {
473 LOG.debug("InterVpnLink {} found in Cache linking Vpn {}", interVpnLink.getInterVpnLinkName(), vpnUuid);
474 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
475 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
476 // This is an static route that points to the other endpoint of an InterVpnLink
477 // In that case, we should add another entry in FIB table pointing to LPortDispatcher table.
478 installIVpnLinkSwitchingFlows(interVpnLink, vpnUuid, vrfEntry, vpnId);
479 installInterVpnRouteInLFib(interVpnLink, vpnUuid, vrfEntry, etherType);
486 void refreshFibTables(String rd, String prefix) {
487 InstanceIdentifier<VrfEntry> vrfEntryId =
488 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
489 .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
490 Optional<VrfEntry> vrfEntry = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
491 if (vrfEntry.isPresent()) {
492 createFibEntries(vrfEntryId, vrfEntry.get());
496 private Prefixes updateVpnReferencesInLri(LabelRouteInfo lri, String vpnInstanceName, boolean isPresentInList) {
497 LOG.debug("updating LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
498 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
499 prefixBuilder.setDpnId(lri.getDpnId());
500 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
501 prefixBuilder.setIpAddress(lri.getPrefix());
502 // Increment the refCount here
503 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
504 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
505 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri);
506 if (!isPresentInList) {
507 LOG.debug("vpnName {} is not present in LRI with label {}..", vpnInstanceName, lri.getLabel());
508 List<String> vpnInstanceNames = new ArrayList<>(nullToEmpty(lri.getVpnInstanceList()));
509 vpnInstanceNames.add(vpnInstanceName);
510 builder.setVpnInstanceList(vpnInstanceNames);
511 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
513 LOG.debug("vpnName {} is present in LRI with label {}..", vpnInstanceName, lri.getLabel());
515 return prefixBuilder.build();
518 void installSubnetRouteInFib(final BigInteger dpnId, final long elanTag, final String rd,
519 final long vpnId, final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
521 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
522 newTx -> installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, newTx)), LOG,
523 "Error installing subnet route in FIB");
528 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
529 } catch (IllegalArgumentException ex) {
530 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
533 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
534 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
535 final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
536 final ReentrantLock lock = lockFor(lriKey);
539 LabelRouteInfo lri = getLabelRouteInfo(lriKey);
540 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
542 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
543 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
544 fibUtil.getVpnInstanceOpData(rd);
545 if (vpnInstanceOpDataEntryOptional.isPresent()) {
546 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
547 if (!lri.getVpnInstanceList().contains(vpnInstanceName)) {
548 updateVpnReferencesInLri(lri, vpnInstanceName, false);
552 LOG.debug("SUBNETROUTE: installSubnetRouteInFib: Fetched labelRouteInfo for label {} interface {}"
553 + " and got dpn {}", label, lri.getVpnInterfaceName(), lri.getDpnId());
559 final List<InstructionInfo> instructions = new ArrayList<>();
560 BigInteger subnetRouteMeta = BigInteger.valueOf(elanTag).shiftLeft(24)
561 .or(BigInteger.valueOf(vpnId).shiftLeft(1));
562 instructions.add(new InstructionWriteMetadata(subnetRouteMeta, MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
563 instructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
564 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
565 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
566 if (vrfEntry.getRoutePaths() != null) {
567 for (RoutePaths routePath : vrfEntry.getRoutePaths()) {
568 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
569 List<ActionInfo> actionsInfos = new ArrayList<>();
570 // reinitialize instructions list for LFIB Table
571 final List<InstructionInfo> LFIBinstructions = new ArrayList<>();
572 actionsInfos.add(new ActionPopMpls(etherType));
573 LFIBinstructions.add(new InstructionApplyActions(actionsInfos));
574 LFIBinstructions.add(new InstructionWriteMetadata(subnetRouteMeta,
575 MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
576 LFIBinstructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
578 makeLFibTableEntry(dpnId, routePath.getLabel(), LFIBinstructions, DEFAULT_FIB_FLOW_PRIORITY,
579 NwConstants.ADD_FLOW, tx);
585 private void installSubnetBroadcastAddrDropRule(final BigInteger dpnId, final String rd, final long vpnId,
586 final VrfEntry vrfEntry, int addOrRemove, TypedWriteTransaction<Configuration> tx) {
587 List<MatchInfo> matches = new ArrayList<>();
589 LOG.debug("SUBNETROUTE: installSubnetBroadcastAddrDropRule: destPrefix {} rd {} vpnId {} dpnId {}",
590 vrfEntry.getDestPrefix(), rd, vpnId, dpnId);
591 String[] ipAddress = vrfEntry.getDestPrefix().split("/");
592 String subnetBroadcastAddr = FibUtil.getBroadcastAddressFromCidr(vrfEntry.getDestPrefix());
593 final int prefixLength = ipAddress.length == 1 ? 0 : Integer.parseInt(ipAddress[1]);
595 InetAddress destPrefix;
597 destPrefix = InetAddress.getByName(subnetBroadcastAddr);
598 } catch (UnknownHostException e) {
599 LOG.error("Failed to get destPrefix for prefix {} rd {} VpnId {} DPN {}",
600 vrfEntry.getDestPrefix(), rd, vpnId, dpnId, e);
604 // Match on VpnId and SubnetBroadCast IP address
605 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
606 matches.add(MatchEthernetType.IPV4);
608 if (prefixLength != 0) {
609 matches.add(new MatchIpv4Destination(subnetBroadcastAddr, Integer.toString(IPV4_ADDR_PREFIX_LENGTH)));
612 //Action is to drop the packet
613 List<InstructionInfo> dropInstructions = new ArrayList<>();
614 List<ActionInfo> actionsInfos = new ArrayList<>();
615 actionsInfos.add(new ActionDrop());
616 dropInstructions.add(new InstructionApplyActions(actionsInfos));
618 int priority = DEFAULT_FIB_FLOW_PRIORITY + IPV4_ADDR_PREFIX_LENGTH;
619 String flowRef = FibUtil.getFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, rd, priority, destPrefix);
620 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef, priority,
621 flowRef, 0, 0, COOKIE_TABLE_MISS, matches, dropInstructions);
623 Flow flow = flowEntity.getFlowBuilder().build();
624 String flowId = flowEntity.getFlowId();
625 FlowKey flowKey = new FlowKey(new FlowId(flowId));
626 Node nodeDpn = FibUtil.buildDpnNode(dpnId);
628 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
629 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
630 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
632 if (addOrRemove == NwConstants.ADD_FLOW) {
633 tx.put(flowInstanceId,flow, true);
635 tx.delete(flowInstanceId);
640 * For a given route, it installs a flow in LFIB that sets the lportTag of the other endpoint and sends to
641 * LportDispatcher table (via table 80)
643 private void installInterVpnRouteInLFib(final InterVpnLinkDataComposite interVpnLink, final String vpnName,
644 final VrfEntry vrfEntry, int etherType) {
645 // INTERVPN routes are routes in a Vpn1 that have been leaked to Vpn2. In DC-GW, this Vpn2 route is pointing
646 // to a list of DPNs where Vpn2's VpnLink was instantiated. In these DPNs LFIB must be programmed so that the
647 // packet is commuted from Vpn2 to Vpn1.
648 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
649 if (!interVpnLink.isActive()) {
650 LOG.warn("InterVpnLink {} is NOT ACTIVE. InterVpnLink flows for prefix={} wont be installed in LFIB",
651 interVpnLinkName, vrfEntry.getDestPrefix());
655 Optional<Long> optLportTag = interVpnLink.getEndpointLportTagByVpnName(vpnName);
656 if (!optLportTag.isPresent()) {
657 LOG.warn("Could not retrieve lportTag for VPN {} endpoint in InterVpnLink {}", vpnName, interVpnLinkName);
661 Long lportTag = optLportTag.get();
662 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).orElse(null);
664 LOG.error("Could not find label in vrfEntry=[prefix={} routePaths={}]. LFIB entry for InterVpnLink skipped",
665 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
668 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionPopMpls(etherType));
669 List<InstructionInfo> instructions = Arrays.asList(
670 new InstructionApplyActions(actionsInfos),
671 new InstructionWriteMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(lportTag.intValue(),
672 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
673 NwConstants.L3VPN_SERVICE_INDEX)),
674 MetaDataUtil.getMetaDataMaskForLPortDispatcher()),
675 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
676 List<String> interVpnNextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
677 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
679 for (BigInteger dpId : targetDpns) {
680 LOG.debug("Installing flow: VrfEntry=[prefix={} label={} nexthop={}] dpn {} for InterVpnLink {} in LFIB",
681 vrfEntry.getDestPrefix(), label, interVpnNextHopList, dpId, interVpnLink.getInterVpnLinkName());
683 makeLFibTableEntry(dpId, label, instructions, LFIB_INTERVPN_PRIORITY, NwConstants.ADD_FLOW,
690 * Installs the flows in FIB table that, for a given route, do the switching from one VPN to the other.
692 private void installIVpnLinkSwitchingFlows(final InterVpnLinkDataComposite interVpnLink, final String vpnUuid,
693 final VrfEntry vrfEntry, long vpnTag) {
694 Preconditions.checkNotNull(interVpnLink, "InterVpnLink cannot be null");
695 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null
696 && vrfEntry.getRoutePaths().size() == 1);
697 String destination = vrfEntry.getDestPrefix();
698 String nextHop = vrfEntry.getRoutePaths().get(0).getNexthopAddress();
699 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
701 // After having received a static route, we should check if the vpn is part of an inter-vpn-link.
702 // In that case, we should populate the FIB table of the VPN pointing to LPortDisptacher table
703 // using as metadata the LPortTag associated to that vpn in the inter-vpn-link.
704 if (interVpnLink.getState().or(State.Error) != State.Active) {
705 LOG.warn("Route to {} with nexthop={} cannot be installed because the interVpnLink {} is not active",
706 destination, nextHop, interVpnLinkName);
710 Optional<Long> optOtherEndpointLportTag = interVpnLink.getOtherEndpointLportTagByVpnName(vpnUuid);
711 if (!optOtherEndpointLportTag.isPresent()) {
712 LOG.warn("Could not find suitable LportTag for the endpoint opposite to vpn {} in interVpnLink {}",
713 vpnUuid, interVpnLinkName);
717 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnUuid);
718 if (targetDpns.isEmpty()) {
719 LOG.warn("Could not find DPNs for endpoint opposite to vpn {} in interVpnLink {}",
720 vpnUuid, interVpnLinkName);
724 String[] values = destination.split("/");
725 String destPrefixIpAddress = values[0];
726 int prefixLength = values.length == 1 ? 0 : Integer.parseInt(values[1]);
728 List<MatchInfo> matches = new ArrayList<>();
729 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnTag), MetaDataUtil.METADATA_MASK_VRFID));
730 matches.add(MatchEthernetType.IPV4);
732 if (prefixLength != 0) {
733 matches.add(new MatchIpv4Destination(destPrefixIpAddress, Integer.toString(prefixLength)));
736 List<Instruction> instructions =
737 Arrays.asList(new InstructionWriteMetadata(
738 MetaDataUtil.getMetaDataForLPortDispatcher(optOtherEndpointLportTag.get().intValue(),
739 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants
740 .L3VPN_SERVICE_INDEX)),
741 MetaDataUtil.getMetaDataMaskForLPortDispatcher()).buildInstruction(0),
742 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE).buildInstruction(1));
744 int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
745 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, destination, nextHop);
746 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef, priority, flowRef, 0, 0,
747 COOKIE_VM_FIB_TABLE, matches, instructions);
749 LOG.trace("Installing flow in FIB table for vpn {} interVpnLink {} nextHop {} key {}",
750 vpnUuid, interVpnLink.getInterVpnLinkName(), nextHop, flowRef);
752 for (BigInteger dpId : targetDpns) {
754 LOG.debug("Installing flow: VrfEntry=[prefix={} route-paths={}] dpn {} for InterVpnLink {} in FIB",
755 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(),
756 dpId, interVpnLink.getInterVpnLinkName());
758 mdsalManager.installFlow(dpId, flowEntity);
762 private List<BigInteger> createLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry, int etherType) {
763 List<BigInteger> returnLocalDpnId = new ArrayList<>();
764 String localNextHopIP = vrfEntry.getDestPrefix();
765 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
766 String vpnName = fibUtil.getVpnNameFromId(vpnId);
767 if (localNextHopInfo == null) {
768 boolean localNextHopSeen = false;
769 List<Routes> vpnExtraRoutes = null;
770 //Synchronized to prevent missing bucket action due to race condition between refreshFib and
771 // add/updateFib threads on missing nexthop in VpnToExtraroutes
772 // FIXME: use an Identifier structure?
773 final ReentrantLock lock = JvmGlobalLocks.getLockForString(localNextHopIP + FibConstants.SEPARATOR + rd);
776 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, localNextHopIP);
777 vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
778 vpnName, usedRds, localNextHopIP);
779 if (LOG.isDebugEnabled()) {
780 LOG.debug("Creating Local fib entry with vpnName {} usedRds {} localNextHopIP {} vpnExtraRoutes {}",
781 vpnName, usedRds, localNextHopIP, vpnExtraRoutes);
784 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
785 for (Routes vpnExtraRoute : vpnExtraRoutes) {
787 if (isIpv4Address(vpnExtraRoute.getNexthopIpList().get(0))) {
788 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
790 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
792 Prefixes localNextHopInfoLocal = fibUtil.getPrefixToInterface(vpnId,
794 if (localNextHopInfoLocal != null) {
795 localNextHopSeen = true;
797 checkCreateLocalFibEntry(localNextHopInfoLocal, localNextHopInfoLocal.getIpAddress(),
798 vpnId, rd, vrfEntry, vpnExtraRoute, vpnExtraRoutes, etherType);
799 returnLocalDpnId.add(dpnId);
805 if (!localNextHopSeen && RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
806 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
807 if (optionalLabel.isPresent()) {
808 Long label = optionalLabel.get();
809 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
810 final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
811 final ReentrantLock labelLock = lockFor(lriKey);
814 LabelRouteInfo lri = getLabelRouteInfo(lriKey);
815 if (isPrefixAndNextHopPresentInLri(localNextHopIP, nextHopAddressList, lri)) {
816 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
817 fibUtil.getVpnInstanceOpData(rd);
818 if (vpnInstanceOpDataEntryOptional.isPresent()) {
819 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
820 if (nullToEmpty(lri.getVpnInstanceList()).contains(vpnInstanceName)) {
821 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, true);
822 localNextHopIP = lri.getPrefix();
824 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, false);
825 localNextHopIP = lri.getPrefix();
828 if (localNextHopInfo != null) {
829 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
830 label, localNextHopInfo.getVpnInterfaceName(), lri.getDpnId());
831 if (vpnExtraRoutes.isEmpty()) {
832 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
833 vpnId, rd, vrfEntry, null, vpnExtraRoutes, etherType);
834 returnLocalDpnId.add(dpnId);
836 for (Routes extraRoutes : vpnExtraRoutes) {
837 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
838 vpnId, rd, vrfEntry, extraRoutes, vpnExtraRoutes, etherType);
839 returnLocalDpnId.add(dpnId);
849 if (returnLocalDpnId.isEmpty()) {
850 LOG.error("Local DPNID is empty for rd {}, vpnId {}, vrfEntry {}", rd, vpnId, vrfEntry);
853 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP, vpnId,
854 rd, vrfEntry, /*routes*/ null, /*vpnExtraRoutes*/ null, etherType);
856 returnLocalDpnId.add(dpnId);
859 return returnLocalDpnId;
862 private BigInteger checkCreateLocalFibEntry(Prefixes localNextHopInfo, String localNextHopIP,
863 final Long vpnId, final String rd,
864 final VrfEntry vrfEntry,
865 @Nullable Routes routes, @Nullable List<Routes> vpnExtraRoutes,
867 String vpnName = fibUtil.getVpnNameFromId(vpnId);
868 if (localNextHopInfo != null) {
871 final BigInteger dpnId = localNextHopInfo.getDpnId();
872 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
873 LOG.debug("checkCreateLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
874 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
877 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
878 LOG.debug("checkCreateLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
879 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
882 if (!isVpnPresentInDpn(rd, dpnId)) {
883 LOG.error("checkCreateLocalFibEntry: The VPN with id {} rd {} is not available on dpn {}",
884 vpnId, rd, dpnId.toString());
885 return BigInteger.ZERO;
887 String interfaceName = localNextHopInfo.getVpnInterfaceName();
888 String prefix = vrfEntry.getDestPrefix();
889 String gwMacAddress = vrfEntry.getGatewayMacAddress();
890 //The loadbalancing group is created only if the extra route has multiple nexthops
891 //to avoid loadbalancing the discovered routes
892 if (RouteOrigin.STATIC.getValue().equals(vrfEntry.getOrigin()) && vpnExtraRoutes != null
894 if (vpnExtraRoutes.size() > 1) {
895 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
896 localGroupId = nextHopManager.getLocalSelectGroup(vpnId, vrfEntry.getDestPrefix());
898 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
899 localGroupId = groupId;
902 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP, prefix,
904 localGroupId = groupId;
906 if (groupId == FibConstants.INVALID_GROUP_ID) {
907 LOG.error("Unable to create Group for local prefix {} on rd {} for vpninterface {} on Node {}",
908 prefix, rd, interfaceName, dpnId.toString());
909 return BigInteger.ZERO;
911 final List<InstructionInfo> instructions = Collections.singletonList(
912 new InstructionApplyActions(
913 Collections.singletonList(new ActionGroup(groupId))));
914 final List<InstructionInfo> lfibinstructions = Collections.singletonList(
915 new InstructionApplyActions(
916 Arrays.asList(new ActionPopMpls(etherType), new ActionGroup(localGroupId))));
917 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
918 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
919 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
920 jobCoordinator.enqueueJob(jobKey,
921 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
922 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
923 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
924 if (FibUtil.isBgpVpn(vpnName, rd)) {
925 optLabel.ifPresent(label -> {
926 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
928 "Installing LFIB and tunnel table entry on dpn {} for interface {} with label "
929 + "{}, rd {}, prefix {}, nexthop {}", dpnId,
930 localNextHopInfo.getVpnInterfaceName(), optLabel, rd, vrfEntry.getDestPrefix(),
932 makeLFibTableEntry(dpnId, label, lfibinstructions, DEFAULT_FIB_FLOW_PRIORITY,
933 NwConstants.ADD_FLOW, tx);
934 makeTunnelTableEntry(dpnId, label, localGroupId, tx);
936 LOG.debug("Route with rd {} prefix {} label {} nexthop {} for vpn {} is an imported "
937 + "route. LFib and Terminating table entries will not be created.",
938 rd, vrfEntry.getDestPrefix(), optLabel, nextHopAddressList, vpnId);
945 LOG.error("localNextHopInfo received is null for prefix {} on rd {} on vpn {}", vrfEntry.getDestPrefix(), rd,
947 return BigInteger.ZERO;
950 private boolean isVpnPresentInDpn(String rd, BigInteger dpnId) {
951 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
952 Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
953 return dpnInVpn.isPresent();
957 private LabelRouteInfo getLabelRouteInfo(Long label) {
958 return getLabelRouteInfo(new LabelRouteInfoKey(label));
962 private LabelRouteInfo getLabelRouteInfo(LabelRouteInfoKey label) {
963 InstanceIdentifier<LabelRouteInfo> lriIid = InstanceIdentifier.builder(LabelRouteMap.class)
964 .child(LabelRouteInfo.class, label).build();
965 Optional<LabelRouteInfo> opResult = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, lriIid);
966 if (opResult.isPresent()) {
967 return opResult.get();
972 private boolean deleteLabelRouteInfo(LabelRouteInfo lri, String vpnInstanceName,
973 @Nullable TypedWriteTransaction<Operational> tx) {
978 LOG.debug("deleting LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
979 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
980 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
982 List<String> vpnInstancesList = lri.getVpnInstanceList() != null
983 ? lri.getVpnInstanceList() : new ArrayList<>();
984 if (vpnInstancesList.contains(vpnInstanceName)) {
985 LOG.debug("vpninstance {} name is present", vpnInstanceName);
986 vpnInstancesList.remove(vpnInstanceName);
988 if (vpnInstancesList.isEmpty()) {
989 LOG.debug("deleting LRI instance object for label {}", lri.getLabel());
993 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId);
997 LOG.debug("updating LRI instance object for label {}", lri.getLabel());
998 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri).setVpnInstanceList(vpnInstancesList);
999 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
1004 void makeTunnelTableEntry(BigInteger dpId, long label, long groupId/*String egressInterfaceName*/,
1005 TypedWriteTransaction<Configuration> tx) {
1006 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionGroup(groupId));
1008 createTerminatingServiceActions(dpId, (int) label, actionsInfos, tx);
1010 LOG.debug("Terminating service Entry for dpID {} : label : {} egress : {} installed successfully",
1011 dpId, label, groupId);
1014 public void createTerminatingServiceActions(BigInteger destDpId, int label, List<ActionInfo> actionsInfos,
1015 TypedWriteTransaction<Configuration> tx) {
1016 List<MatchInfo> mkMatches = new ArrayList<>();
1018 LOG.debug("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}",
1019 destDpId, label, actionsInfos);
1021 // Matching metadata
1022 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
1023 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
1025 List<InstructionInfo> mkInstructions = new ArrayList<>();
1026 mkInstructions.add(new InstructionApplyActions(actionsInfos));
1028 FlowEntity terminatingServiceTableFlowEntity =
1029 MDSALUtil.buildFlowEntity(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,
1030 getTableMissFlowRef(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE, label), 5,
1031 String.format("%s:%d", "TST Flow Entry ", label),
1032 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, mkInstructions);
1034 FlowKey flowKey = new FlowKey(new FlowId(terminatingServiceTableFlowEntity.getFlowId()));
1036 FlowBuilder flowbld = terminatingServiceTableFlowEntity.getFlowBuilder();
1038 Node nodeDpn = FibUtil.buildDpnNode(terminatingServiceTableFlowEntity.getDpnId());
1039 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1040 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1041 .child(Table.class, new TableKey(terminatingServiceTableFlowEntity.getTableId()))
1042 .child(Flow.class, flowKey).build();
1043 tx.put(flowInstanceId, flowbld.build(), CREATE_MISSING_PARENTS);
1046 private void removeTunnelTableEntry(BigInteger dpId, long label, TypedWriteTransaction<Configuration> tx) {
1047 FlowEntity flowEntity;
1048 LOG.debug("remove terminatingServiceActions called with DpnId = {} and label = {}", dpId, label);
1049 List<MatchInfo> mkMatches = new ArrayList<>();
1050 // Matching metadata
1051 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
1052 flowEntity = MDSALUtil.buildFlowEntity(dpId,
1053 NwConstants.INTERNAL_TUNNEL_TABLE,
1054 getTableMissFlowRef(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, (int) label),
1055 5, String.format("%s:%d", "TST Flow Entry ", label), 0, 0,
1056 COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, null);
1057 Node nodeDpn = FibUtil.buildDpnNode(flowEntity.getDpnId());
1058 FlowKey flowKey = new FlowKey(new FlowId(flowEntity.getFlowId()));
1059 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1060 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1061 .child(Table.class, new TableKey(flowEntity.getTableId())).child(Flow.class, flowKey).build();
1063 tx.delete(flowInstanceId);
1064 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully", dpId, label);
1067 public List<BigInteger> deleteLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
1068 List<BigInteger> returnLocalDpnId = new ArrayList<>();
1069 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1070 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1071 boolean shouldUpdateNonEcmpLocalNextHop = true;
1072 if (localNextHopInfo == null) {
1073 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1074 if (usedRds.size() > 1) {
1075 LOG.error("The extra route prefix {} is still present in some DPNs in vpn {} on rd {}",
1076 vrfEntry.getDestPrefix(), vpnName, rd);
1077 return returnLocalDpnId;
1079 String vpnRd = !usedRds.isEmpty() ? usedRds.get(0) : rd;
1080 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency
1082 Optional<Routes> extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1083 vpnName, vpnRd, vrfEntry.getDestPrefix());
1084 if (extraRouteOptional.isPresent()) {
1085 Routes extraRoute = extraRouteOptional.get();
1087 if (isIpv4Address(extraRoute.getNexthopIpList().get(0))) {
1088 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
1090 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
1092 if (extraRoute.getNexthopIpList().size() > 1) {
1093 shouldUpdateNonEcmpLocalNextHop = false;
1095 localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1096 if (localNextHopInfo != null) {
1097 String localNextHopIP = localNextHopInfo.getIpAddress();
1098 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP, vpnName, vpnId, rd,
1099 vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1100 if (!dpnId.equals(BigInteger.ZERO)) {
1101 LOG.trace("Deleting ECMP group for prefix {}, dpn {}", vrfEntry.getDestPrefix(), dpnId);
1102 nextHopManager.deleteLoadBalancingNextHop(vpnId, dpnId, vrfEntry.getDestPrefix());
1103 returnLocalDpnId.add(dpnId);
1106 LOG.error("localNextHopInfo unavailable while deleting prefix {} with rds {}, primary rd {} in "
1107 + "vpn {}", vrfEntry.getDestPrefix(), usedRds, rd, vpnName);
1111 if (localNextHopInfo == null) {
1112 /* Imported VRF entry */
1113 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1114 if (optionalLabel.isPresent()) {
1115 Long label = optionalLabel.get();
1116 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1117 LabelRouteInfo lri = getLabelRouteInfo(label);
1118 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1119 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1120 prefixBuilder.setDpnId(lri.getDpnId());
1121 BigInteger dpnId = checkDeleteLocalFibEntry(prefixBuilder.build(), nextHopAddressList.get(0),
1122 vpnName, vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1123 if (!dpnId.equals(BigInteger.ZERO)) {
1124 returnLocalDpnId.add(dpnId);
1131 LOG.debug("Obtained prefix to interface for rd {} prefix {}", rd, vrfEntry.getDestPrefix());
1132 String localNextHopIP = localNextHopInfo.getIpAddress();
1133 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP, vpnName, vpnId, rd, vrfEntry,
1134 shouldUpdateNonEcmpLocalNextHop);
1135 if (!dpnId.equals(BigInteger.ZERO)) {
1136 returnLocalDpnId.add(dpnId);
1140 return returnLocalDpnId;
1143 private BigInteger checkDeleteLocalFibEntry(Prefixes localNextHopInfo, final String localNextHopIP,
1144 final String vpnName, final Long vpnId, final String rd, final VrfEntry vrfEntry,
1145 boolean shouldUpdateNonEcmpLocalNextHop) {
1146 if (localNextHopInfo != null) {
1147 final BigInteger dpnId = localNextHopInfo.getDpnId();
1148 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
1149 LOG.debug("checkDeleteLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1150 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1153 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
1154 LOG.debug("checkDeleteLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1155 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1159 jobCoordinator.enqueueJob(FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix()),
1160 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1161 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1162 NwConstants.DEL_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1163 if (FibUtil.isBgpVpn(vpnName, rd)) {
1164 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1165 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1166 makeLFibTableEntry(dpnId, label, null /* instructions */, DEFAULT_FIB_FLOW_PRIORITY,
1167 NwConstants.DEL_FLOW, tx);
1168 removeTunnelTableEntry(dpnId, label, tx);
1173 //TODO: verify below adjacency call need to be optimized (?)
1174 //In case of the removal of the extra route, the loadbalancing group is updated
1175 if (shouldUpdateNonEcmpLocalNextHop) {
1176 baseVrfEntryHandler.deleteLocalAdjacency(dpnId, vpnId, localNextHopIP, vrfEntry.getDestPrefix());
1180 return BigInteger.ZERO;
1183 private void createRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, String rd,
1184 final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
1186 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1187 newTx -> createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx)), LOG,
1188 "Error creating remote FIB entry");
1192 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1193 LOG.debug("createremotefibentry: adding route {} for rd {} on remoteDpnId {}",
1194 vrfEntry.getDestPrefix(), rd, remoteDpnId);
1196 List<AdjacencyResult> adjacencyResults = baseVrfEntryHandler.resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
1197 if (adjacencyResults.isEmpty()) {
1198 LOG.error("Could not get interface for route-paths: {} in vpn {} on DPN {}",
1199 vrfEntry.getRoutePaths(), rd, remoteDpnId);
1200 LOG.error("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
1204 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1205 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
1206 vpnName, usedRds, vrfEntry.getDestPrefix());
1207 // create loadbalancing groups for extra routes only when the extra route is present behind
1209 if (!vpnExtraRoutes.isEmpty()) {
1210 List<InstructionInfo> instructions = new ArrayList<>();
1211 // Obtain the local routes for this particular dpn.
1212 java.util.Optional<Routes> routes = vpnExtraRoutes
1215 Prefixes prefixToInterface = fibUtil.getPrefixToInterface(vpnId,
1216 FibUtil.getIpPrefix(route.getNexthopIpList().get(0)));
1217 if (prefixToInterface == null) {
1220 return remoteDpnId.equals(prefixToInterface.getDpnId());
1222 long groupId = nextHopManager.createNextHopGroups(vpnId, rd, remoteDpnId, vrfEntry,
1223 routes.isPresent() ? routes.get() : null, vpnExtraRoutes);
1224 if (groupId == FibConstants.INVALID_GROUP_ID) {
1225 LOG.error("Unable to create Group for local prefix {} on rd {} on Node {}",
1226 vrfEntry.getDestPrefix(), rd, remoteDpnId.toString());
1229 List<ActionInfo> actionInfos =
1230 Collections.singletonList(new ActionGroup(groupId));
1231 instructions.add(new InstructionApplyActions(actionInfos));
1232 String jobKey = FibUtil.getCreateRemoteNextHopJobKey(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
1233 jobCoordinator.enqueueJob(jobKey,
1234 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(txn -> {
1235 baseVrfEntryHandler.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
1236 NwConstants.ADD_FLOW, txn, null);
1239 baseVrfEntryHandler.programRemoteFib(remoteDpnId, vpnId, vrfEntry,
1240 TransactionAdapter.toWriteTransaction(tx), rd, adjacencyResults, null);
1243 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
1246 protected void cleanUpOpDataForFib(Long vpnId, String primaryRd, final VrfEntry vrfEntry) {
1247 /* Get interface info from prefix to interface mapping;
1248 Use the interface info to get the corresponding vpn interface op DS entry,
1249 remove the adjacency corresponding to this fib entry.
1250 If adjacency removed is the last adjacency, clean up the following:
1251 - vpn interface from dpntovpn list, dpn if last vpn interface on dpn
1252 - prefix to interface entry
1253 - vpn interface op DS
1255 LOG.debug("Cleanup of prefix {} in VPN {}", vrfEntry.getDestPrefix(), vpnId);
1256 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1257 if (prefixInfo == null) {
1258 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1259 String usedRd = usedRds.isEmpty() ? primaryRd : usedRds.get(0);
1260 Routes extraRoute = baseVrfEntryHandler.getVpnToExtraroute(vpnId, usedRd, vrfEntry.getDestPrefix());
1261 if (extraRoute != null) {
1262 for (String nextHopIp : nullToEmpty(extraRoute.getNexthopIpList())) {
1263 LOG.debug("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
1264 if (nextHopIp != null) {
1266 if (isIpv4Address(nextHopIp)) {
1267 ipPrefix = nextHopIp + NwConstants.IPV4PREFIX;
1269 ipPrefix = nextHopIp + NwConstants.IPV6PREFIX;
1271 prefixInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1272 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1276 if (prefixInfo == null) {
1277 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1278 if (optionalLabel.isPresent()) {
1279 Long label = optionalLabel.get();
1280 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1281 LabelRouteInfo lri = getLabelRouteInfo(label);
1282 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1283 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1284 prefixBuilder.setDpnId(lri.getDpnId());
1285 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
1286 prefixBuilder.setIpAddress(lri.getPrefix());
1287 prefixInfo = prefixBuilder.build();
1288 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
1289 label, prefixInfo.getVpnInterfaceName(), lri.getDpnId());
1290 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1295 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, null /*Routes*/);
1299 private void checkCleanUpOpDataForFib(final Prefixes prefixInfo, final Long vpnId, final String rd,
1300 final VrfEntry vrfEntry, @Nullable final Routes extraRoute) {
1302 if (prefixInfo == null) {
1303 LOG.error("Cleanup VPN Data Failed as unable to find prefix Info for prefix {} VpnId {} rd {}",
1304 vrfEntry.getDestPrefix(), vpnId, rd);
1305 return; //Don't have any info for this prefix (shouldn't happen); need to return
1308 if (Prefixes.PrefixCue.Nat.equals(prefixInfo.getPrefixCue())) {
1309 LOG.debug("NAT Prefix {} with vpnId {} rd {}. Skip FIB processing",
1310 vrfEntry.getDestPrefix(), vpnId, rd);
1314 String ifName = prefixInfo.getVpnInterfaceName();
1315 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName,
1316 new CleanupVpnInterfaceWorker(prefixInfo, vpnId, rd, vrfEntry, extraRoute));
1319 private class CleanupVpnInterfaceWorker implements Callable<List<ListenableFuture<Void>>> {
1320 Prefixes prefixInfo;
1326 CleanupVpnInterfaceWorker(final Prefixes prefixInfo, final Long vpnId, final String rd,
1327 final VrfEntry vrfEntry, final Routes extraRoute) {
1328 this.prefixInfo = prefixInfo;
1331 this.vrfEntry = vrfEntry;
1332 this.extraRoute = extraRoute;
1336 public List<ListenableFuture<Void>> call() {
1337 // If another renderer(for eg : CSS) needs to be supported, check can be performed here
1338 // to call the respective helpers.
1339 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
1340 //First Cleanup LabelRouteInfo
1341 //TODO(KIRAN) : Move the below block when addressing iRT/eRT for L3VPN Over VxLan
1342 LOG.debug("cleanupVpnInterfaceWorker: rd {} prefix {}", rd, prefixInfo.getIpAddress());
1343 if (VrfEntry.EncapType.Mplsgre.equals(vrfEntry.getEncapType())) {
1344 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1345 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1346 final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
1347 final ReentrantLock lock = lockFor(lriKey);
1350 LabelRouteInfo lri = getLabelRouteInfo(lriKey);
1351 if (lri != null && Objects.equals(lri.getPrefix(), vrfEntry.getDestPrefix())
1352 && nextHopAddressList.contains(lri.getNextHopIpList().get(0))) {
1353 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1354 fibUtil.getVpnInstanceOpData(rd);
1355 String vpnInstanceName = "";
1356 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1357 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1359 boolean lriRemoved = deleteLabelRouteInfo(lri, vpnInstanceName, tx);
1361 String parentRd = lri.getParentVpnRd();
1362 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1363 parentRd, vrfEntry.getDestPrefix()));
1366 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1367 rd, vrfEntry.getDestPrefix()));
1374 String ifName = prefixInfo.getVpnInterfaceName();
1375 Optional<String> optVpnName = fibUtil.getVpnNameFromRd(rd);
1376 String vpnName = null;
1378 if (Prefixes.PrefixCue.PhysNetFunc.equals(prefixInfo.getPrefixCue())) {
1379 // Get vpnId for rd = networkId since op vpnInterface will be pointing to rd = networkId
1380 Optional<String> vpnNameOpt = fibUtil.getVpnNameFromRd(vrfEntry.getParentVpnRd());
1381 if (vpnNameOpt.isPresent()) {
1382 vpnId = fibUtil.getVpnId(vpnNameOpt.get());
1385 if (optVpnName.isPresent()) {
1386 vpnName = optVpnName.get();
1387 Optional<VpnInterfaceOpDataEntry> opVpnInterface = tx
1388 .read(FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName)).get();
1389 if (opVpnInterface.isPresent()) {
1390 long associatedVpnId = fibUtil.getVpnId(vpnName);
1391 if (vpnId != associatedVpnId) {
1392 LOG.warn("Prefixes {} are associated with different vpn instance with id {} rather than {}",
1393 vrfEntry.getDestPrefix(), associatedVpnId, vpnId);
1394 LOG.warn("Not proceeding with Cleanup op data for prefix {}", vrfEntry.getDestPrefix());
1397 LOG.debug("Processing cleanup of prefix {} associated with vpn {}",
1398 vrfEntry.getDestPrefix(), associatedVpnId);
1402 if (extraRoute != null) {
1403 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1404 //Only one used Rd present in case of removal event
1405 String usedRd = usedRds.get(0);
1406 if (optVpnName.isPresent()) {
1407 tx.delete(BaseVrfEntryHandler.getVpnToExtrarouteIdentifier(vpnName, usedRd,
1408 vrfEntry.getDestPrefix()));
1409 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, configTx ->
1410 configTx.delete(VpnExtraRouteHelper.getUsedRdsIdentifier(vpnId, vrfEntry.getDestPrefix())));
1413 handleAdjacencyAndVpnOpInterfaceDeletion(vrfEntry, ifName, vpnName, tx);
1419 * Check all the adjacency in VpnInterfaceOpData and decide whether to delete the entire interface or only adj.
1420 * Remove Adjacency from VPNInterfaceOpData.
1421 * if Adjacency != primary.
1422 * if Adjacency == primary , then mark it for deletion.
1423 * Remove entire VPNinterfaceOpData Entry.
1424 * if sie of Adjacency <= 2 and all are marked for deletion , delete the entire VPNinterface Op entry.
1425 * @param vrfEntry - VrfEntry removed
1426 * @param ifName - Interface name from VRFentry
1427 * @param vpnName - VPN name of corresponding VRF
1428 * @param tx - ReadWrite Tx
1430 private void handleAdjacencyAndVpnOpInterfaceDeletion(VrfEntry vrfEntry, String ifName, String vpnName,
1431 TypedReadWriteTransaction<Operational> tx)
1432 throws ExecutionException, InterruptedException {
1433 InstanceIdentifier<Adjacency> adjacencyIid =
1434 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix());
1435 Optional<Adjacency> adjacencyOptional = tx.read(adjacencyIid).get();
1436 if (adjacencyOptional.isPresent()) {
1437 if (adjacencyOptional.get().getAdjacencyType() != Adjacency.AdjacencyType.PrimaryAdjacency) {
1438 tx.delete(FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix()));
1440 tx.merge(adjacencyIid,
1441 new AdjacencyBuilder(adjacencyOptional.get()).setMarkedForDeletion(true).build());
1445 Optional<AdjacenciesOp> optAdjacencies = tx.read(FibUtil.getAdjListPathOp(ifName, vpnName)).get();
1447 if (!optAdjacencies.isPresent() || optAdjacencies.get().getAdjacency() == null) {
1451 if (nullToEmpty(optAdjacencies.get().getAdjacency()).size() <= 2
1452 && nullToEmpty(optAdjacencies.get().getAdjacency()).stream().allMatch(adjacency ->
1453 adjacency.getAdjacencyType() == Adjacency.AdjacencyType.PrimaryAdjacency
1454 && adjacency.isMarkedForDeletion() != null
1455 && adjacency.isMarkedForDeletion()
1457 LOG.info("Clean up vpn interface {} to vpn {} list.", ifName, vpnName);
1458 tx.delete(FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1462 private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
1463 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
1464 final String rd = vrfTableKey.getRouteDistinguisher();
1465 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
1466 if (vpnInstance == null) {
1467 LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
1470 final Collection<VpnToDpnList> vpnToDpnList;
1471 if (vrfEntry.getParentVpnRd() != null
1472 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
1473 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
1474 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
1475 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
1476 vpnInstance.getVpnToDpnList();
1477 LOG.info("deleteFibEntries: Processing deletion of PNF FIB entry with rd {} prefix {}",
1478 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
1480 vpnToDpnList = vpnInstance.getVpnToDpnList();
1483 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1484 final java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1485 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1486 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1487 if (subnetRoute != null) {
1488 long elanTag = subnetRoute.getElantag();
1489 LOG.trace("SUBNETROUTE: deleteFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
1490 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
1491 if (vpnToDpnList != null) {
1492 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1493 () -> Collections.singletonList(
1494 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1495 for (final VpnToDpnList curDpn : vpnToDpnList) {
1497 baseVrfEntryHandler.makeConnectedRoute(curDpn.getDpnId(), vpnInstance.getVpnId(),
1498 vrfEntry, vrfTableKey.getRouteDistinguisher(), null, NwConstants.DEL_FLOW,
1499 TransactionAdapter.toWriteTransaction(tx), null);
1500 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1501 optionalLabel.ifPresent(label -> makeLFibTableEntry(curDpn.getDpnId(), label, null,
1502 DEFAULT_FIB_FLOW_PRIORITY, NwConstants.DEL_FLOW, tx));
1505 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnInstance.getVpnId(),
1506 vrfEntry, NwConstants.DEL_FLOW, tx);
1510 optionalLabel.ifPresent(label -> {
1511 final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
1512 final ReentrantLock lock = lockFor(lriKey);
1515 LabelRouteInfo lri = getLabelRouteInfo(lriKey);
1516 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1517 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1518 fibUtil.getVpnInstanceOpData(rd);
1519 String vpnInstanceName = "";
1520 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1521 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1523 boolean lriRemoved = this.deleteLabelRouteInfo(lri, vpnInstanceName, null);
1525 String parentRd = lri.getParentVpnRd();
1526 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1527 parentRd, vrfEntry.getDestPrefix()));
1528 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}"
1529 + " as labelRouteInfo cleared", label, rd, vrfEntry.getDestPrefix());
1532 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1533 rd, vrfEntry.getDestPrefix()));
1534 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}",
1535 label, rd, vrfEntry.getDestPrefix());
1544 final List<BigInteger> localDpnIdList = deleteLocalFibEntry(vpnInstance.getVpnId(),
1545 vrfTableKey.getRouteDistinguisher(), vrfEntry);
1546 if (vpnToDpnList != null) {
1547 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1548 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1550 Optional<Routes> extraRouteOptional;
1551 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
1552 if (usedRds != null && !usedRds.isEmpty()) {
1553 if (usedRds.size() > 1) {
1554 LOG.error("The extra route prefix is still present in some DPNs");
1557 // The first rd is retrieved from usedrds as Only 1 rd would be present as extra route prefix
1558 //is not present in any other DPN
1559 extraRouteOptional = VpnExtraRouteHelper
1560 .getVpnExtraroutes(dataBroker, vpnName, usedRds.get(0), vrfEntry.getDestPrefix());
1563 extraRouteOptional = Optional.absent();
1566 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1567 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1568 if (localDpnIdList.size() <= 0) {
1569 for (VpnToDpnList curDpn : vpnToDpnList) {
1570 baseVrfEntryHandler.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
1571 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
1572 TransactionAdapter.toWriteTransaction(tx));
1575 for (BigInteger localDpnId : localDpnIdList) {
1576 for (VpnToDpnList curDpn : vpnToDpnList) {
1577 if (!Objects.equals(curDpn.getDpnId(), localDpnId)) {
1578 baseVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
1579 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
1580 TransactionAdapter.toWriteTransaction(tx));
1585 if (extraRouteOptional.isPresent()) {
1586 //Remove select groups only for extra-routes
1587 nextHopManager.removeNextHopPointer(nextHopManager
1588 .getRemoteSelectGroupKey(vpnInstance.getVpnId(), vrfEntry.getDestPrefix()));
1589 nextHopManager.removeNextHopPointer(nextHopManager
1590 .getLocalSelectGroupKey(vpnInstance.getVpnId(), vrfEntry.getDestPrefix()));
1595 //The flow/group entry has been deleted from config DS; need to clean up associated operational
1596 //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
1597 cleanUpOpDataForFib(vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(), vrfEntry);
1599 // Remove all fib entries configured due to interVpnLink, when nexthop is the opposite endPoint
1600 // of the interVpnLink.
1601 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
1602 if (optVpnUuid.isPresent()) {
1603 String vpnUuid = optVpnUuid.get();
1604 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
1605 Optional<InterVpnLinkDataComposite> optInterVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid);
1606 if (optInterVpnLink.isPresent()) {
1607 InterVpnLinkDataComposite interVpnLink = optInterVpnLink.get();
1608 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
1609 // This is route that points to the other endpoint of an InterVpnLink
1610 // In that case, we should look for the FIB table pointing to
1611 // LPortDispatcher table and remove it.
1612 removeInterVPNLinkRouteFlows(interVpnLink, vpnUuid, vrfEntry);
1620 private void makeLFibTableEntry(BigInteger dpId, long label, @Nullable List<InstructionInfo> instructions,
1621 int priority, int addOrRemove, TypedWriteTransaction<Configuration> tx) {
1623 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1624 newTx -> makeLFibTableEntry(dpId, label, instructions, priority, addOrRemove, newTx)), LOG,
1625 "Error making LFIB table entry");
1629 List<MatchInfo> matches = new ArrayList<>();
1630 matches.add(MatchEthernetType.MPLS_UNICAST);
1631 matches.add(new MatchMplsLabel(label));
1633 // Install the flow entry in L3_LFIB_TABLE
1634 String flowRef = FibUtil.getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, label, priority);
1636 FlowEntity flowEntity;
1637 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef, priority, flowRef, 0, 0,
1638 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
1639 Flow flow = flowEntity.getFlowBuilder().build();
1640 String flowId = flowEntity.getFlowId();
1641 FlowKey flowKey = new FlowKey(new FlowId(flowId));
1642 Node nodeDpn = FibUtil.buildDpnNode(dpId);
1643 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1644 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1645 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
1647 if (addOrRemove == NwConstants.ADD_FLOW) {
1648 tx.put(flowInstanceId, flow, CREATE_MISSING_PARENTS);
1650 tx.delete(flowInstanceId);
1653 LOG.debug("LFIB Entry for dpID {} : label : {} instructions {} : key {} {} successfully",
1654 dpId, label, instructions, flowKey, NwConstants.ADD_FLOW == addOrRemove ? "ADDED" : "REMOVED");
1657 public void populateFibOnNewDpn(final BigInteger dpnId, final long vpnId, final String rd,
1658 final FutureCallback<List<Void>> callback) {
1659 LOG.trace("New dpn {} for vpn {} : populateFibOnNewDpn", dpnId, rd);
1660 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1662 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1663 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1664 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1665 LogicalDatastoreType.CONFIGURATION, id);
1666 List<ListenableFuture<Void>> futures = new ArrayList<>();
1667 if (!vrfTable.isPresent()) {
1668 LOG.info("populateFibOnNewDpn: dpn: {}: VRF Table not yet available for RD {}", dpnId, rd);
1669 if (callback != null) {
1670 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1671 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1676 final ReentrantLock lock = lockFor(vpnInstance);
1679 futures.add(retryingTxRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
1680 for (final VrfEntry vrfEntry : nullToEmpty(vrfTable.get().getVrfEntry())) {
1681 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1682 if (subnetRoute != null) {
1683 long elanTag = subnetRoute.getElantag();
1684 installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, tx);
1685 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry, NwConstants.ADD_FLOW,
1689 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1690 if (routerInt != null) {
1691 LOG.trace("Router augmented vrfentry found rd:{}, uuid:{}, ip:{}, mac:{}",
1692 rd, routerInt.getUuid(), routerInt.getIpAddress(), routerInt.getMacAddress());
1693 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1694 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1695 NwConstants.ADD_FLOW);
1698 //Handle local flow creation for imports
1699 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1700 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1701 if (optionalLabel.isPresent()) {
1702 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1703 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1704 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList, lri)) {
1705 if (Objects.equals(lri.getDpnId(), dpnId)) {
1707 int etherType = NWUtil.getEtherTypeFromIpPrefix(
1708 vrfEntry.getDestPrefix());
1709 createLocalFibEntry(vpnId, rd, vrfEntry, etherType);
1710 } catch (IllegalArgumentException ex) {
1711 LOG.warn("Unable to get etherType for IP Prefix {}",
1712 vrfEntry.getDestPrefix());
1719 boolean shouldCreateRemoteFibEntry = shouldCreateFibEntryForVrfAndVpnIdOnDpn(vpnId,
1721 if (shouldCreateRemoteFibEntry) {
1722 LOG.trace("Will create remote FIB entry for vrfEntry {} on DPN {}", vrfEntry, dpnId);
1723 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1724 List<SubTransaction> txnObjects = new ArrayList<>();
1725 bgpRouteVrfEntryHandler.createRemoteFibEntry(dpnId, vpnId,
1726 vrfTable.get().getRouteDistinguisher(), vrfEntry,
1727 TransactionAdapter.toWriteTransaction(tx), txnObjects);
1729 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
1735 if (callback != null) {
1736 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1737 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1746 public void populateExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1747 final String localNextHopIp, final String remoteNextHopIp) {
1748 LOG.trace("populateExternalRoutesOnDpn : dpn {}, vpn {}, rd {}, localNexthopIp {} , remoteNextHopIp {} ",
1749 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1750 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1751 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1752 List<SubTransaction> txnObjects = new ArrayList<>();
1753 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1754 if (vrfTable.isPresent()) {
1755 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1756 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1757 final ReentrantLock lock = lockFor(vpnInstance);
1760 nullToEmpty(vrfTable.get().getVrfEntry()).stream()
1761 .filter(vrfEntry -> RouteOrigin.BGP == RouteOrigin.value(vrfEntry.getOrigin()))
1762 .forEach(bgpRouteVrfEntryHandler.getConsumerForCreatingRemoteFib(dpnId, vpnId,
1763 rd, remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx), txnObjects));
1771 public void manageRemoteRouteOnDPN(final boolean action,
1772 final BigInteger localDpnId,
1775 final String destPrefix,
1776 final String destTepIp,
1778 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1780 if (vpnInstance == null) {
1781 LOG.error("VpnInstance for rd {} not present for prefix {}", rd, destPrefix);
1785 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, localDpnId),
1786 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1787 final ReentrantLock lock = lockFor(vpnInstance);
1790 VrfTablesKey vrfTablesKey = new VrfTablesKey(rd);
1791 VrfEntry vrfEntry = getVrfEntry(dataBroker, rd, destPrefix);
1792 if (vrfEntry == null) {
1795 LOG.trace("manageRemoteRouteOnDPN :: action {}, DpnId {}, vpnId {}, rd {}, destPfx {}",
1796 action, localDpnId, vpnId, rd, destPrefix);
1797 List<RoutePaths> routePathList = vrfEntry.getRoutePaths();
1798 VrfEntry modVrfEntry;
1799 if (routePathList == null || routePathList.isEmpty()) {
1800 modVrfEntry = FibHelper.getVrfEntryBuilder(vrfEntry, label,
1801 Collections.singletonList(destTepIp),
1802 RouteOrigin.value(vrfEntry.getOrigin()), null /* parentVpnRd */).build();
1804 modVrfEntry = vrfEntry;
1808 LOG.trace("manageRemoteRouteOnDPN updated(add) vrfEntry :: {}", modVrfEntry);
1809 createRemoteFibEntry(localDpnId, vpnId, vrfTablesKey.getRouteDistinguisher(),
1812 LOG.trace("manageRemoteRouteOnDPN updated(remove) vrfEntry :: {}", modVrfEntry);
1813 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnInstance.getVpnId(),
1814 vrfEntry.getDestPrefix());
1815 if (usedRds.size() > 1) {
1816 LOG.debug("The extra route prefix is still present in some DPNs");
1819 //Is this fib route an extra route? If yes, get the nexthop which would be
1820 //an adjacency in the vpn
1821 Optional<Routes> extraRouteOptional = Optional.absent();
1822 if (usedRds.size() != 0) {
1823 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1824 fibUtil.getVpnNameFromId(vpnInstance.getVpnId()),
1825 usedRds.get(0), vrfEntry.getDestPrefix());
1827 baseVrfEntryHandler.deleteRemoteRoute(null, localDpnId, vpnId, vrfTablesKey, modVrfEntry,
1828 extraRouteOptional, TransactionAdapter.toWriteTransaction(tx));
1836 public void cleanUpDpnForVpn(final BigInteger dpnId, final long vpnId, final String rd,
1837 final FutureCallback<List<Void>> callback) {
1838 LOG.trace("cleanUpDpnForVpn: Remove dpn {} for vpn {} : cleanUpDpnForVpn", dpnId, rd);
1839 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1841 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1842 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1843 List<SubTransaction> txnObjects = new ArrayList<>();
1844 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1845 LogicalDatastoreType.CONFIGURATION, id);
1846 List<ListenableFuture<Void>> futures = new ArrayList<>();
1847 if (vrfTable.isPresent()) {
1848 final ReentrantLock lock = lockFor(vpnInstance);
1851 futures.add(retryingTxRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1852 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1853 for (final VrfEntry vrfEntry : nullToEmpty(vrfTable.get().getVrfEntry())) {
1854 /* parentRd is only filled for external PNF cases where the interface on the external
1855 * network VPN are used to cleanup the flows. For all other cases, use "rd" for
1856 * #fibUtil.isInterfacePresentInDpn().
1858 String parentRd = vrfEntry.getParentVpnRd() != null ? vrfEntry.getParentVpnRd()
1860 /* Handle subnet routes here */
1861 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1862 if (subnetRoute != null && !fibUtil
1863 .isInterfacePresentInDpn(parentRd, dpnId)) {
1864 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Cleaning subnetroute {} on dpn {}"
1865 + " for vpn {}", vrfEntry.getDestPrefix(), dpnId, rd);
1866 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1867 NwConstants.DEL_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1868 List<RoutePaths> routePaths = vrfEntry.getRoutePaths();
1869 if (routePaths != null) {
1870 for (RoutePaths routePath : routePaths) {
1871 makeLFibTableEntry(dpnId, routePath.getLabel(), null,
1872 DEFAULT_FIB_FLOW_PRIORITY,
1873 NwConstants.DEL_FLOW, tx);
1874 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Released subnetroute label {}"
1875 + " for rd {} prefix {}", routePath.getLabel(), rd,
1876 vrfEntry.getDestPrefix());
1879 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry,
1880 NwConstants.DEL_FLOW, tx);
1883 // ping responder for router interfaces
1884 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1885 if (routerInt != null) {
1886 LOG.trace("Router augmented vrfentry found for rd:{}, uuid:{}, ip:{}, mac:{}",
1887 rd, routerInt.getUuid(), routerInt.getIpAddress(),
1888 routerInt.getMacAddress());
1889 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1890 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1891 NwConstants.DEL_FLOW);
1895 //Handle local flow deletion for imports
1896 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1897 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1898 if (optionalLabel.isPresent()) {
1899 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1900 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1901 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList,
1902 lri) && Objects.equals(lri.getDpnId(), dpnId)) {
1903 deleteLocalFibEntry(vpnId, rd, vrfEntry);
1908 // Passing null as we don't know the dpn
1909 // to which prefix is attached at this point
1910 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1911 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1912 Optional<Routes> extraRouteOptional;
1913 //Is this fib route an extra route? If yes, get the nexthop which would be
1914 //an adjacency in the vpn
1915 if (usedRds != null && !usedRds.isEmpty()) {
1916 if (usedRds.size() > 1) {
1917 LOG.error("The extra route prefix is still present in some DPNs");
1920 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
1921 usedRds.get(0), vrfEntry.getDestPrefix());
1925 extraRouteOptional = Optional.absent();
1927 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1928 bgpRouteVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1929 vrfTable.get().key(), vrfEntry, extraRouteOptional,
1930 TransactionAdapter.toWriteTransaction(tx), txnObjects);
1932 if (subnetRoute == null || !fibUtil
1933 .isInterfacePresentInDpn(parentRd, dpnId)) {
1934 baseVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1935 vrfTable.get().key(), vrfEntry, extraRouteOptional,
1936 TransactionAdapter.toWriteTransaction(tx));
1944 if (callback != null) {
1945 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1946 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1949 LOG.error("cleanUpDpnForVpn: No vrf table found for rd {} vpnId {} dpn {}", rd, vpnId, dpnId);
1956 public void cleanUpExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1957 final String localNextHopIp, final String remoteNextHopIp) {
1958 LOG.trace("cleanUpExternalRoutesOnDpn : cleanup remote routes on dpn {} for vpn {}, rd {}, "
1959 + " localNexthopIp {} , remoteNexhtHopIp {}",
1960 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1961 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1962 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1963 List<SubTransaction> txnObjects = new ArrayList<>();
1964 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1965 if (vrfTable.isPresent()) {
1966 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1968 final ReentrantLock lock = lockFor(vpnInstance);
1971 return Collections.singletonList(
1972 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1973 tx -> nullToEmpty(vrfTable.get().getVrfEntry()).stream()
1974 .filter(vrfEntry -> RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP)
1975 .forEach(bgpRouteVrfEntryHandler.getConsumerForDeletingRemoteFib(dpnId, vpnId,
1976 remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx),
1985 public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
1986 InstanceIdentifierBuilder<VrfTables> idBuilder =
1987 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
1988 return idBuilder.build();
1991 private String getInterVpnFibFlowRef(String interVpnLinkName, String prefix, String nextHop) {
1992 return FLOWID_PREFIX + interVpnLinkName + NwConstants.FLOWID_SEPARATOR + prefix + NwConstants
1993 .FLOWID_SEPARATOR + nextHop;
1996 private String getTableMissFlowRef(BigInteger dpnId, short tableId, int tableMiss) {
1997 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR
1998 + tableMiss + FLOWID_PREFIX;
2002 private VrfEntry getVrfEntry(DataBroker broker, String rd, String ipPrefix) {
2003 InstanceIdentifier<VrfEntry> vrfEntryId = InstanceIdentifier.builder(FibEntries.class)
2004 .child(VrfTables.class, new VrfTablesKey(rd))
2005 .child(VrfEntry.class, new VrfEntryKey(ipPrefix)).build();
2006 Optional<VrfEntry> vrfEntry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
2007 if (vrfEntry.isPresent()) {
2008 return vrfEntry.get();
2013 public void removeInterVPNLinkRouteFlows(final InterVpnLinkDataComposite interVpnLink,
2014 final String vpnName,
2015 final VrfEntry vrfEntry) {
2016 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null && vrfEntry.getRoutePaths().size() == 1);
2018 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
2019 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
2021 if (targetDpns.isEmpty()) {
2022 LOG.warn("Could not find DPNs for VPN {} in InterVpnLink {}", vpnName, interVpnLinkName);
2026 java.util.Optional<String> optNextHop = FibUtil.getFirstNextHopAddress(vrfEntry);
2027 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
2031 optNextHop.ifPresent(nextHop -> {
2032 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, vrfEntry.getDestPrefix(), nextHop);
2033 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
2034 Flow flow = new FlowBuilder().withKey(flowKey).setId(new FlowId(flowRef))
2035 .setTableId(NwConstants.L3_FIB_TABLE).setFlowName(flowRef).build();
2037 LOG.trace("Removing flow in FIB table for interVpnLink {} key {}", interVpnLinkName, flowRef);
2038 for (BigInteger dpId : targetDpns) {
2039 LOG.debug("Removing flow: VrfEntry=[prefix={} nexthop={}] dpn {} for InterVpnLink {} in FIB",
2040 vrfEntry.getDestPrefix(), nextHop, dpId, interVpnLinkName);
2042 mdsalManager.removeFlow(dpId, flow);
2048 optLabel.ifPresent(label -> {
2049 LOG.trace("Removing flow in FIB table for interVpnLink {}", interVpnLinkName);
2051 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
2052 for (BigInteger dpId : targetDpns) {
2053 LOG.debug("Removing flow: VrfEntry=[prefix={} label={}] dpn {} for InterVpnLink {} in LFIB",
2054 vrfEntry.getDestPrefix(), label, dpId, interVpnLinkName);
2055 makeLFibTableEntry(dpId, label, /*instructions*/null, LFIB_INTERVPN_PRIORITY, NwConstants.DEL_FLOW,
2058 }), LOG, "Error removing flows");
2062 private static boolean isPrefixAndNextHopPresentInLri(String prefix,
2063 List<String> nextHopAddressList, LabelRouteInfo lri) {
2064 return lri != null && Objects.equals(lri.getPrefix(), prefix)
2065 && nextHopAddressList.contains(lri.getNextHopIpList().get(0));
2068 private boolean shouldCreateFibEntryForVrfAndVpnIdOnDpn(Long vpnId, VrfEntry vrfEntry, BigInteger dpnId) {
2069 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
2073 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
2074 if (prefix != null) {
2075 BigInteger prefixDpnId = prefix.getDpnId();
2076 if (dpnId.equals(prefixDpnId)) {
2077 LOG.trace("Should not create remote FIB entry for vrfEntry {} on DPN {}",
2085 private static ReentrantLock lockFor(final VpnInstanceOpDataEntry vpnInstance) {
2086 // FIXME: use vpnInstance.key() instead?
2087 return JvmGlobalLocks.getLockForString(vpnInstance.getVpnInstanceName());
2090 private static ReentrantLock lockFor(LabelRouteInfoKey label) {
2091 return JvmGlobalLocks.getLockFor(label);