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;
15 import com.google.common.base.Optional;
16 import com.google.common.base.Preconditions;
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.ListenableFuture;
20 import com.google.common.util.concurrent.MoreExecutors;
21 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
22 import java.math.BigInteger;
23 import java.net.InetAddress;
24 import java.net.UnknownHostException;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Objects;
31 import java.util.concurrent.Callable;
32 import java.util.concurrent.CopyOnWriteArrayList;
33 import java.util.concurrent.ExecutionException;
34 import java.util.concurrent.locks.ReentrantLock;
35 import javax.annotation.Nullable;
36 import javax.annotation.PostConstruct;
37 import javax.inject.Inject;
38 import javax.inject.Singleton;
39 import org.eclipse.jdt.annotation.NonNull;
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 =
509 lri.getVpnInstanceList() != null ? new ArrayList<>(lri.getVpnInstanceList()) : new ArrayList<>();
510 vpnInstanceNames.add(vpnInstanceName);
511 builder.setVpnInstanceList(vpnInstanceNames);
512 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
514 LOG.debug("vpnName {} is present in LRI with label {}..", vpnInstanceName, lri.getLabel());
516 return prefixBuilder.build();
519 void installSubnetRouteInFib(final BigInteger dpnId, final long elanTag, final String rd,
520 final long vpnId, final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
522 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
523 newTx -> installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, newTx)), LOG,
524 "Error installing subnet route in FIB");
529 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
530 } catch (IllegalArgumentException ex) {
531 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
534 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
535 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
536 final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
537 final ReentrantLock lock = lockFor(lriKey);
540 LabelRouteInfo lri = getLabelRouteInfo(lriKey);
541 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
543 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
544 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
545 fibUtil.getVpnInstanceOpData(rd);
546 if (vpnInstanceOpDataEntryOptional.isPresent()) {
547 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
548 if (!lri.getVpnInstanceList().contains(vpnInstanceName)) {
549 updateVpnReferencesInLri(lri, vpnInstanceName, false);
553 LOG.debug("SUBNETROUTE: installSubnetRouteInFib: Fetched labelRouteInfo for label {} interface {}"
554 + " and got dpn {}", label, lri.getVpnInterfaceName(), lri.getDpnId());
560 final List<InstructionInfo> instructions = new ArrayList<>();
561 BigInteger subnetRouteMeta = BigInteger.valueOf(elanTag).shiftLeft(24)
562 .or(BigInteger.valueOf(vpnId).shiftLeft(1));
563 instructions.add(new InstructionWriteMetadata(subnetRouteMeta, MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
564 instructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
565 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
566 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
567 if (vrfEntry.getRoutePaths() != null) {
568 for (RoutePaths routePath : vrfEntry.getRoutePaths()) {
569 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
570 List<ActionInfo> actionsInfos = new ArrayList<>();
571 // reinitialize instructions list for LFIB Table
572 final List<InstructionInfo> LFIBinstructions = new ArrayList<>();
573 actionsInfos.add(new ActionPopMpls(etherType));
574 LFIBinstructions.add(new InstructionApplyActions(actionsInfos));
575 LFIBinstructions.add(new InstructionWriteMetadata(subnetRouteMeta,
576 MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
577 LFIBinstructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
579 makeLFibTableEntry(dpnId, routePath.getLabel(), LFIBinstructions, DEFAULT_FIB_FLOW_PRIORITY,
580 NwConstants.ADD_FLOW, tx);
586 private void installSubnetBroadcastAddrDropRule(final BigInteger dpnId, final String rd, final long vpnId,
587 final VrfEntry vrfEntry, int addOrRemove, TypedWriteTransaction<Configuration> tx) {
588 List<MatchInfo> matches = new ArrayList<>();
590 LOG.debug("SUBNETROUTE: installSubnetBroadcastAddrDropRule: destPrefix {} rd {} vpnId {} dpnId {}",
591 vrfEntry.getDestPrefix(), rd, vpnId, dpnId);
592 String[] ipAddress = vrfEntry.getDestPrefix().split("/");
593 String subnetBroadcastAddr = FibUtil.getBroadcastAddressFromCidr(vrfEntry.getDestPrefix());
594 final int prefixLength = ipAddress.length == 1 ? 0 : Integer.parseInt(ipAddress[1]);
596 InetAddress destPrefix;
598 destPrefix = InetAddress.getByName(subnetBroadcastAddr);
599 } catch (UnknownHostException e) {
600 LOG.error("Failed to get destPrefix for prefix {} rd {} VpnId {} DPN {}",
601 vrfEntry.getDestPrefix(), rd, vpnId, dpnId, e);
605 // Match on VpnId and SubnetBroadCast IP address
606 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
607 matches.add(MatchEthernetType.IPV4);
609 if (prefixLength != 0) {
610 matches.add(new MatchIpv4Destination(subnetBroadcastAddr, Integer.toString(IPV4_ADDR_PREFIX_LENGTH)));
613 //Action is to drop the packet
614 List<InstructionInfo> dropInstructions = new ArrayList<>();
615 List<ActionInfo> actionsInfos = new ArrayList<>();
616 actionsInfos.add(new ActionDrop());
617 dropInstructions.add(new InstructionApplyActions(actionsInfos));
619 int priority = DEFAULT_FIB_FLOW_PRIORITY + IPV4_ADDR_PREFIX_LENGTH;
620 String flowRef = FibUtil.getFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, rd, priority, destPrefix);
621 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef, priority,
622 flowRef, 0, 0, COOKIE_TABLE_MISS, matches, dropInstructions);
624 Flow flow = flowEntity.getFlowBuilder().build();
625 String flowId = flowEntity.getFlowId();
626 FlowKey flowKey = new FlowKey(new FlowId(flowId));
627 Node nodeDpn = FibUtil.buildDpnNode(dpnId);
629 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
630 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
631 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
633 if (addOrRemove == NwConstants.ADD_FLOW) {
634 tx.put(flowInstanceId,flow, true);
636 tx.delete(flowInstanceId);
641 * For a given route, it installs a flow in LFIB that sets the lportTag of the other endpoint and sends to
642 * LportDispatcher table (via table 80)
644 private void installInterVpnRouteInLFib(final InterVpnLinkDataComposite interVpnLink, final String vpnName,
645 final VrfEntry vrfEntry, int etherType) {
646 // INTERVPN routes are routes in a Vpn1 that have been leaked to Vpn2. In DC-GW, this Vpn2 route is pointing
647 // to a list of DPNs where Vpn2's VpnLink was instantiated. In these DPNs LFIB must be programmed so that the
648 // packet is commuted from Vpn2 to Vpn1.
649 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
650 if (!interVpnLink.isActive()) {
651 LOG.warn("InterVpnLink {} is NOT ACTIVE. InterVpnLink flows for prefix={} wont be installed in LFIB",
652 interVpnLinkName, vrfEntry.getDestPrefix());
656 Optional<Long> optLportTag = interVpnLink.getEndpointLportTagByVpnName(vpnName);
657 if (!optLportTag.isPresent()) {
658 LOG.warn("Could not retrieve lportTag for VPN {} endpoint in InterVpnLink {}", vpnName, interVpnLinkName);
662 Long lportTag = optLportTag.get();
663 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).orElse(null);
665 LOG.error("Could not find label in vrfEntry=[prefix={} routePaths={}]. LFIB entry for InterVpnLink skipped",
666 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
669 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionPopMpls(etherType));
670 List<InstructionInfo> instructions = Arrays.asList(
671 new InstructionApplyActions(actionsInfos),
672 new InstructionWriteMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(lportTag.intValue(),
673 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
674 NwConstants.L3VPN_SERVICE_INDEX)),
675 MetaDataUtil.getMetaDataMaskForLPortDispatcher()),
676 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
677 List<String> interVpnNextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
678 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
680 for (BigInteger dpId : targetDpns) {
681 LOG.debug("Installing flow: VrfEntry=[prefix={} label={} nexthop={}] dpn {} for InterVpnLink {} in LFIB",
682 vrfEntry.getDestPrefix(), label, interVpnNextHopList, dpId, interVpnLink.getInterVpnLinkName());
684 makeLFibTableEntry(dpId, label, instructions, LFIB_INTERVPN_PRIORITY, NwConstants.ADD_FLOW,
691 * Installs the flows in FIB table that, for a given route, do the switching from one VPN to the other.
693 private void installIVpnLinkSwitchingFlows(final InterVpnLinkDataComposite interVpnLink, final String vpnUuid,
694 final VrfEntry vrfEntry, long vpnTag) {
695 Preconditions.checkNotNull(interVpnLink, "InterVpnLink cannot be null");
696 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null
697 && vrfEntry.getRoutePaths().size() == 1);
698 String destination = vrfEntry.getDestPrefix();
699 String nextHop = vrfEntry.getRoutePaths().get(0).getNexthopAddress();
700 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
702 // After having received a static route, we should check if the vpn is part of an inter-vpn-link.
703 // In that case, we should populate the FIB table of the VPN pointing to LPortDisptacher table
704 // using as metadata the LPortTag associated to that vpn in the inter-vpn-link.
705 if (interVpnLink.getState().or(State.Error) != State.Active) {
706 LOG.warn("Route to {} with nexthop={} cannot be installed because the interVpnLink {} is not active",
707 destination, nextHop, interVpnLinkName);
711 Optional<Long> optOtherEndpointLportTag = interVpnLink.getOtherEndpointLportTagByVpnName(vpnUuid);
712 if (!optOtherEndpointLportTag.isPresent()) {
713 LOG.warn("Could not find suitable LportTag for the endpoint opposite to vpn {} in interVpnLink {}",
714 vpnUuid, interVpnLinkName);
718 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnUuid);
719 if (targetDpns.isEmpty()) {
720 LOG.warn("Could not find DPNs for endpoint opposite to vpn {} in interVpnLink {}",
721 vpnUuid, interVpnLinkName);
725 String[] values = destination.split("/");
726 String destPrefixIpAddress = values[0];
727 int prefixLength = values.length == 1 ? 0 : Integer.parseInt(values[1]);
729 List<MatchInfo> matches = new ArrayList<>();
730 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnTag), MetaDataUtil.METADATA_MASK_VRFID));
731 matches.add(MatchEthernetType.IPV4);
733 if (prefixLength != 0) {
734 matches.add(new MatchIpv4Destination(destPrefixIpAddress, Integer.toString(prefixLength)));
737 List<Instruction> instructions =
738 Arrays.asList(new InstructionWriteMetadata(
739 MetaDataUtil.getMetaDataForLPortDispatcher(optOtherEndpointLportTag.get().intValue(),
740 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants
741 .L3VPN_SERVICE_INDEX)),
742 MetaDataUtil.getMetaDataMaskForLPortDispatcher()).buildInstruction(0),
743 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE).buildInstruction(1));
745 int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
746 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, destination, nextHop);
747 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef, priority, flowRef, 0, 0,
748 COOKIE_VM_FIB_TABLE, matches, instructions);
750 LOG.trace("Installing flow in FIB table for vpn {} interVpnLink {} nextHop {} key {}",
751 vpnUuid, interVpnLink.getInterVpnLinkName(), nextHop, flowRef);
753 for (BigInteger dpId : targetDpns) {
755 LOG.debug("Installing flow: VrfEntry=[prefix={} route-paths={}] dpn {} for InterVpnLink {} in FIB",
756 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(),
757 dpId, interVpnLink.getInterVpnLinkName());
759 mdsalManager.installFlow(dpId, flowEntity);
763 private List<BigInteger> createLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry, int etherType) {
764 List<BigInteger> returnLocalDpnId = new ArrayList<>();
765 String localNextHopIP = vrfEntry.getDestPrefix();
766 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
767 String vpnName = fibUtil.getVpnNameFromId(vpnId);
768 if (localNextHopInfo == null) {
769 boolean localNextHopSeen = false;
770 List<Routes> vpnExtraRoutes = null;
771 //Synchronized to prevent missing bucket action due to race condition between refreshFib and
772 // add/updateFib threads on missing nexthop in VpnToExtraroutes
773 // FIXME: use an Identifier structure?
774 final ReentrantLock lock = JvmGlobalLocks.getLockForString(localNextHopIP + FibConstants.SEPARATOR + rd);
777 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, localNextHopIP);
778 vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
779 vpnName, usedRds, localNextHopIP);
780 if (LOG.isDebugEnabled()) {
781 LOG.debug("Creating Local fib entry with vpnName {} usedRds {} localNextHopIP {} vpnExtraRoutes {}",
782 vpnName, usedRds, localNextHopIP, vpnExtraRoutes);
785 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
786 for (Routes vpnExtraRoute : vpnExtraRoutes) {
788 if (isIpv4Address(vpnExtraRoute.getNexthopIpList().get(0))) {
789 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
791 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
793 Prefixes localNextHopInfoLocal = fibUtil.getPrefixToInterface(vpnId,
795 if (localNextHopInfoLocal != null) {
796 localNextHopSeen = true;
798 checkCreateLocalFibEntry(localNextHopInfoLocal, localNextHopInfoLocal.getIpAddress(),
799 vpnId, rd, vrfEntry, vpnExtraRoute, vpnExtraRoutes, etherType);
800 returnLocalDpnId.add(dpnId);
806 if (!localNextHopSeen && RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
807 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
808 if (optionalLabel.isPresent()) {
809 Long label = optionalLabel.get();
810 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
811 final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
812 final ReentrantLock labelLock = lockFor(lriKey);
815 LabelRouteInfo lri = getLabelRouteInfo(lriKey);
816 if (isPrefixAndNextHopPresentInLri(localNextHopIP, nextHopAddressList, lri)) {
817 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
818 fibUtil.getVpnInstanceOpData(rd);
819 if (vpnInstanceOpDataEntryOptional.isPresent()) {
820 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
821 if (lri.getVpnInstanceList() != null && lri.getVpnInstanceList().contains(
823 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, true);
824 localNextHopIP = lri.getPrefix();
826 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, false);
827 localNextHopIP = lri.getPrefix();
830 if (localNextHopInfo != null) {
831 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
832 label, localNextHopInfo.getVpnInterfaceName(), lri.getDpnId());
833 if (vpnExtraRoutes.isEmpty()) {
834 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
835 vpnId, rd, vrfEntry, null, vpnExtraRoutes, etherType);
836 returnLocalDpnId.add(dpnId);
838 for (Routes extraRoutes : vpnExtraRoutes) {
839 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
840 vpnId, rd, vrfEntry, extraRoutes, vpnExtraRoutes, etherType);
841 returnLocalDpnId.add(dpnId);
851 if (returnLocalDpnId.isEmpty()) {
852 LOG.error("Local DPNID is empty for rd {}, vpnId {}, vrfEntry {}", rd, vpnId, vrfEntry);
855 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP, vpnId,
856 rd, vrfEntry, /*routes*/ null, /*vpnExtraRoutes*/ null, etherType);
858 returnLocalDpnId.add(dpnId);
861 return returnLocalDpnId;
864 private BigInteger checkCreateLocalFibEntry(Prefixes localNextHopInfo, String localNextHopIP,
865 final Long vpnId, final String rd,
866 final VrfEntry vrfEntry,
867 @Nullable Routes routes, @Nullable List<Routes> vpnExtraRoutes,
869 String vpnName = fibUtil.getVpnNameFromId(vpnId);
870 if (localNextHopInfo != null) {
873 final BigInteger dpnId = localNextHopInfo.getDpnId();
874 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
875 LOG.debug("checkCreateLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
876 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
879 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
880 LOG.debug("checkCreateLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
881 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
884 if (!isVpnPresentInDpn(rd, dpnId)) {
885 LOG.error("checkCreateLocalFibEntry: The VPN with id {} rd {} is not available on dpn {}",
886 vpnId, rd, dpnId.toString());
887 return BigInteger.ZERO;
889 String interfaceName = localNextHopInfo.getVpnInterfaceName();
890 String prefix = vrfEntry.getDestPrefix();
891 String gwMacAddress = vrfEntry.getGatewayMacAddress();
892 //The loadbalancing group is created only if the extra route has multiple nexthops
893 //to avoid loadbalancing the discovered routes
894 if (RouteOrigin.STATIC.getValue().equals(vrfEntry.getOrigin()) && vpnExtraRoutes != null
896 if (vpnExtraRoutes.size() > 1) {
897 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
898 localGroupId = nextHopManager.getLocalSelectGroup(vpnId, vrfEntry.getDestPrefix());
900 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
901 localGroupId = groupId;
904 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP, prefix,
906 localGroupId = groupId;
908 if (groupId == FibConstants.INVALID_GROUP_ID) {
909 LOG.error("Unable to create Group for local prefix {} on rd {} for vpninterface {} on Node {}",
910 prefix, rd, interfaceName, dpnId.toString());
911 return BigInteger.ZERO;
913 final List<InstructionInfo> instructions = Collections.singletonList(
914 new InstructionApplyActions(
915 Collections.singletonList(new ActionGroup(groupId))));
916 final List<InstructionInfo> lfibinstructions = Collections.singletonList(
917 new InstructionApplyActions(
918 Arrays.asList(new ActionPopMpls(etherType), new ActionGroup(localGroupId))));
919 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
920 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
921 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
922 jobCoordinator.enqueueJob(jobKey,
923 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
924 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
925 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
926 if (FibUtil.isBgpVpn(vpnName, rd)) {
927 optLabel.ifPresent(label -> {
928 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
930 "Installing LFIB and tunnel table entry on dpn {} for interface {} with label "
931 + "{}, rd {}, prefix {}, nexthop {}", dpnId,
932 localNextHopInfo.getVpnInterfaceName(), optLabel, rd, vrfEntry.getDestPrefix(),
934 makeLFibTableEntry(dpnId, label, lfibinstructions, DEFAULT_FIB_FLOW_PRIORITY,
935 NwConstants.ADD_FLOW, tx);
936 makeTunnelTableEntry(dpnId, label, localGroupId, tx);
938 LOG.debug("Route with rd {} prefix {} label {} nexthop {} for vpn {} is an imported "
939 + "route. LFib and Terminating table entries will not be created.",
940 rd, vrfEntry.getDestPrefix(), optLabel, nextHopAddressList, vpnId);
947 LOG.error("localNextHopInfo received is null for prefix {} on rd {} on vpn {}", vrfEntry.getDestPrefix(), rd,
949 return BigInteger.ZERO;
952 private boolean isVpnPresentInDpn(String rd, BigInteger dpnId) {
953 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
954 Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
955 return dpnInVpn.isPresent();
959 private LabelRouteInfo getLabelRouteInfo(Long label) {
960 return getLabelRouteInfo(new LabelRouteInfoKey(label));
964 private LabelRouteInfo getLabelRouteInfo(LabelRouteInfoKey label) {
965 InstanceIdentifier<LabelRouteInfo> lriIid = InstanceIdentifier.builder(LabelRouteMap.class)
966 .child(LabelRouteInfo.class, label).build();
967 Optional<LabelRouteInfo> opResult = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, lriIid);
968 if (opResult.isPresent()) {
969 return opResult.get();
974 private boolean deleteLabelRouteInfo(LabelRouteInfo lri, String vpnInstanceName,
975 @Nullable TypedWriteTransaction<Operational> tx) {
980 LOG.debug("deleting LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
981 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
982 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
984 List<String> vpnInstancesList = lri.getVpnInstanceList() != null
985 ? lri.getVpnInstanceList() : new ArrayList<>();
986 if (vpnInstancesList.contains(vpnInstanceName)) {
987 LOG.debug("vpninstance {} name is present", vpnInstanceName);
988 vpnInstancesList.remove(vpnInstanceName);
990 if (vpnInstancesList.isEmpty()) {
991 LOG.debug("deleting LRI instance object for label {}", lri.getLabel());
995 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId);
999 LOG.debug("updating LRI instance object for label {}", lri.getLabel());
1000 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri).setVpnInstanceList(vpnInstancesList);
1001 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
1006 void makeTunnelTableEntry(BigInteger dpId, long label, long groupId/*String egressInterfaceName*/,
1007 TypedWriteTransaction<Configuration> tx) {
1008 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionGroup(groupId));
1010 createTerminatingServiceActions(dpId, (int) label, actionsInfos, tx);
1012 LOG.debug("Terminating service Entry for dpID {} : label : {} egress : {} installed successfully",
1013 dpId, label, groupId);
1016 public void createTerminatingServiceActions(BigInteger destDpId, int label, List<ActionInfo> actionsInfos,
1017 TypedWriteTransaction<Configuration> tx) {
1018 List<MatchInfo> mkMatches = new ArrayList<>();
1020 LOG.debug("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}",
1021 destDpId, label, actionsInfos);
1023 // Matching metadata
1024 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
1025 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
1027 List<InstructionInfo> mkInstructions = new ArrayList<>();
1028 mkInstructions.add(new InstructionApplyActions(actionsInfos));
1030 FlowEntity terminatingServiceTableFlowEntity =
1031 MDSALUtil.buildFlowEntity(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,
1032 getTableMissFlowRef(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE, label), 5,
1033 String.format("%s:%d", "TST Flow Entry ", label),
1034 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, mkInstructions);
1036 FlowKey flowKey = new FlowKey(new FlowId(terminatingServiceTableFlowEntity.getFlowId()));
1038 FlowBuilder flowbld = terminatingServiceTableFlowEntity.getFlowBuilder();
1040 Node nodeDpn = FibUtil.buildDpnNode(terminatingServiceTableFlowEntity.getDpnId());
1041 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1042 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1043 .child(Table.class, new TableKey(terminatingServiceTableFlowEntity.getTableId()))
1044 .child(Flow.class, flowKey).build();
1045 tx.put(flowInstanceId, flowbld.build(), CREATE_MISSING_PARENTS);
1048 private void removeTunnelTableEntry(BigInteger dpId, long label, TypedWriteTransaction<Configuration> tx) {
1049 FlowEntity flowEntity;
1050 LOG.debug("remove terminatingServiceActions called with DpnId = {} and label = {}", dpId, label);
1051 List<MatchInfo> mkMatches = new ArrayList<>();
1052 // Matching metadata
1053 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
1054 flowEntity = MDSALUtil.buildFlowEntity(dpId,
1055 NwConstants.INTERNAL_TUNNEL_TABLE,
1056 getTableMissFlowRef(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, (int) label),
1057 5, String.format("%s:%d", "TST Flow Entry ", label), 0, 0,
1058 COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, null);
1059 Node nodeDpn = FibUtil.buildDpnNode(flowEntity.getDpnId());
1060 FlowKey flowKey = new FlowKey(new FlowId(flowEntity.getFlowId()));
1061 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1062 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1063 .child(Table.class, new TableKey(flowEntity.getTableId())).child(Flow.class, flowKey).build();
1065 tx.delete(flowInstanceId);
1066 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully", dpId, label);
1069 public List<BigInteger> deleteLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
1070 List<BigInteger> returnLocalDpnId = new ArrayList<>();
1071 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1072 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1073 boolean shouldUpdateNonEcmpLocalNextHop = true;
1074 if (localNextHopInfo == null) {
1075 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1076 if (usedRds.size() > 1) {
1077 LOG.error("The extra route prefix {} is still present in some DPNs in vpn {} on rd {}",
1078 vrfEntry.getDestPrefix(), vpnName, rd);
1079 return returnLocalDpnId;
1081 String vpnRd = !usedRds.isEmpty() ? usedRds.get(0) : rd;
1082 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency
1084 Optional<Routes> extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1085 vpnName, vpnRd, vrfEntry.getDestPrefix());
1086 if (extraRouteOptional.isPresent()) {
1087 Routes extraRoute = extraRouteOptional.get();
1089 if (isIpv4Address(extraRoute.getNexthopIpList().get(0))) {
1090 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
1092 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
1094 if (extraRoute.getNexthopIpList().size() > 1) {
1095 shouldUpdateNonEcmpLocalNextHop = false;
1097 localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1098 if (localNextHopInfo != null) {
1099 String localNextHopIP = localNextHopInfo.getIpAddress();
1100 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP, vpnName, vpnId, rd,
1101 vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1102 if (!dpnId.equals(BigInteger.ZERO)) {
1103 LOG.trace("Deleting ECMP group for prefix {}, dpn {}", vrfEntry.getDestPrefix(), dpnId);
1104 nextHopManager.deleteLoadBalancingNextHop(vpnId, dpnId, vrfEntry.getDestPrefix());
1105 returnLocalDpnId.add(dpnId);
1108 LOG.error("localNextHopInfo unavailable while deleting prefix {} with rds {}, primary rd {} in "
1109 + "vpn {}", vrfEntry.getDestPrefix(), usedRds, rd, vpnName);
1113 if (localNextHopInfo == null) {
1114 /* Imported VRF entry */
1115 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1116 if (optionalLabel.isPresent()) {
1117 Long label = optionalLabel.get();
1118 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1119 LabelRouteInfo lri = getLabelRouteInfo(label);
1120 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1121 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1122 prefixBuilder.setDpnId(lri.getDpnId());
1123 BigInteger dpnId = checkDeleteLocalFibEntry(prefixBuilder.build(), nextHopAddressList.get(0),
1124 vpnName, vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1125 if (!dpnId.equals(BigInteger.ZERO)) {
1126 returnLocalDpnId.add(dpnId);
1133 LOG.debug("Obtained prefix to interface for rd {} prefix {}", rd, vrfEntry.getDestPrefix());
1134 String localNextHopIP = localNextHopInfo.getIpAddress();
1135 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP, vpnName, vpnId, rd, vrfEntry,
1136 shouldUpdateNonEcmpLocalNextHop);
1137 if (!dpnId.equals(BigInteger.ZERO)) {
1138 returnLocalDpnId.add(dpnId);
1142 return returnLocalDpnId;
1145 private BigInteger checkDeleteLocalFibEntry(Prefixes localNextHopInfo, final String localNextHopIP,
1146 final String vpnName, final Long vpnId, final String rd, final VrfEntry vrfEntry,
1147 boolean shouldUpdateNonEcmpLocalNextHop) {
1148 if (localNextHopInfo != null) {
1149 final BigInteger dpnId = localNextHopInfo.getDpnId();
1150 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
1151 LOG.debug("checkDeleteLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1152 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1155 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
1156 LOG.debug("checkDeleteLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1157 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1161 jobCoordinator.enqueueJob(FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix()),
1162 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1163 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1164 NwConstants.DEL_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1165 if (FibUtil.isBgpVpn(vpnName, rd)) {
1166 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1167 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1168 makeLFibTableEntry(dpnId, label, null /* instructions */, DEFAULT_FIB_FLOW_PRIORITY,
1169 NwConstants.DEL_FLOW, tx);
1170 removeTunnelTableEntry(dpnId, label, tx);
1175 //TODO: verify below adjacency call need to be optimized (?)
1176 //In case of the removal of the extra route, the loadbalancing group is updated
1177 if (shouldUpdateNonEcmpLocalNextHop) {
1178 baseVrfEntryHandler.deleteLocalAdjacency(dpnId, vpnId, localNextHopIP, vrfEntry.getDestPrefix());
1182 return BigInteger.ZERO;
1185 private void createRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, String rd,
1186 final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
1188 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1189 newTx -> createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx)), LOG,
1190 "Error creating remote FIB entry");
1194 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1195 LOG.debug("createremotefibentry: adding route {} for rd {} on remoteDpnId {}",
1196 vrfEntry.getDestPrefix(), rd, remoteDpnId);
1198 List<AdjacencyResult> adjacencyResults = baseVrfEntryHandler.resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
1199 if (adjacencyResults.isEmpty()) {
1200 LOG.error("Could not get interface for route-paths: {} in vpn {} on DPN {}",
1201 vrfEntry.getRoutePaths(), rd, remoteDpnId);
1202 LOG.error("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
1206 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1207 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
1208 vpnName, usedRds, vrfEntry.getDestPrefix());
1209 // create loadbalancing groups for extra routes only when the extra route is present behind
1211 if (!vpnExtraRoutes.isEmpty()) {
1212 List<InstructionInfo> instructions = new ArrayList<>();
1213 // Obtain the local routes for this particular dpn.
1214 java.util.Optional<Routes> routes = vpnExtraRoutes
1217 Prefixes prefixToInterface = fibUtil.getPrefixToInterface(vpnId,
1218 FibUtil.getIpPrefix(route.getNexthopIpList().get(0)));
1219 if (prefixToInterface == null) {
1222 return remoteDpnId.equals(prefixToInterface.getDpnId());
1224 long groupId = nextHopManager.createNextHopGroups(vpnId, rd, remoteDpnId, vrfEntry,
1225 routes.isPresent() ? routes.get() : null, vpnExtraRoutes);
1226 if (groupId == FibConstants.INVALID_GROUP_ID) {
1227 LOG.error("Unable to create Group for local prefix {} on rd {} on Node {}",
1228 vrfEntry.getDestPrefix(), rd, remoteDpnId.toString());
1231 List<ActionInfo> actionInfos =
1232 Collections.singletonList(new ActionGroup(groupId));
1233 instructions.add(new InstructionApplyActions(actionInfos));
1234 String jobKey = FibUtil.getCreateRemoteNextHopJobKey(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
1235 jobCoordinator.enqueueJob(jobKey,
1236 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(txn -> {
1237 baseVrfEntryHandler.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
1238 NwConstants.ADD_FLOW, txn, null);
1241 baseVrfEntryHandler.programRemoteFib(remoteDpnId, vpnId, vrfEntry,
1242 TransactionAdapter.toWriteTransaction(tx), rd, adjacencyResults, null);
1245 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
1248 protected void cleanUpOpDataForFib(Long vpnId, String primaryRd, final VrfEntry vrfEntry) {
1249 /* Get interface info from prefix to interface mapping;
1250 Use the interface info to get the corresponding vpn interface op DS entry,
1251 remove the adjacency corresponding to this fib entry.
1252 If adjacency removed is the last adjacency, clean up the following:
1253 - vpn interface from dpntovpn list, dpn if last vpn interface on dpn
1254 - prefix to interface entry
1255 - vpn interface op DS
1257 LOG.debug("Cleanup of prefix {} in VPN {}", vrfEntry.getDestPrefix(), vpnId);
1258 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1259 if (prefixInfo == null) {
1260 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1261 String usedRd = usedRds.isEmpty() ? primaryRd : usedRds.get(0);
1262 Routes extraRoute = baseVrfEntryHandler.getVpnToExtraroute(vpnId, usedRd, vrfEntry.getDestPrefix());
1263 if (extraRoute != null && extraRoute.getNexthopIpList() != null) {
1264 for (String nextHopIp : extraRoute.getNexthopIpList()) {
1265 LOG.debug("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
1266 if (nextHopIp != null) {
1268 if (isIpv4Address(nextHopIp)) {
1269 ipPrefix = nextHopIp + NwConstants.IPV4PREFIX;
1271 ipPrefix = nextHopIp + NwConstants.IPV6PREFIX;
1273 prefixInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1274 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1278 if (prefixInfo == null) {
1279 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1280 if (optionalLabel.isPresent()) {
1281 Long label = optionalLabel.get();
1282 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1283 LabelRouteInfo lri = getLabelRouteInfo(label);
1284 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1285 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1286 prefixBuilder.setDpnId(lri.getDpnId());
1287 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
1288 prefixBuilder.setIpAddress(lri.getPrefix());
1289 prefixInfo = prefixBuilder.build();
1290 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
1291 label, prefixInfo.getVpnInterfaceName(), lri.getDpnId());
1292 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1297 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, null /*Routes*/);
1301 private void checkCleanUpOpDataForFib(final Prefixes prefixInfo, final Long vpnId, final String rd,
1302 final VrfEntry vrfEntry, @Nullable final Routes extraRoute) {
1304 if (prefixInfo == null) {
1305 LOG.error("Cleanup VPN Data Failed as unable to find prefix Info for prefix {} VpnId {} rd {}",
1306 vrfEntry.getDestPrefix(), vpnId, rd);
1307 return; //Don't have any info for this prefix (shouldn't happen); need to return
1310 if (Prefixes.PrefixCue.Nat.equals(prefixInfo.getPrefixCue())) {
1311 LOG.debug("NAT Prefix {} with vpnId {} rd {}. Skip FIB processing",
1312 vrfEntry.getDestPrefix(), vpnId, rd);
1316 String ifName = prefixInfo.getVpnInterfaceName();
1317 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName,
1318 new CleanupVpnInterfaceWorker(prefixInfo, vpnId, rd, vrfEntry, extraRoute));
1321 private class CleanupVpnInterfaceWorker implements Callable<List<ListenableFuture<Void>>> {
1322 Prefixes prefixInfo;
1328 CleanupVpnInterfaceWorker(final Prefixes prefixInfo, final Long vpnId, final String rd,
1329 final VrfEntry vrfEntry, final Routes extraRoute) {
1330 this.prefixInfo = prefixInfo;
1333 this.vrfEntry = vrfEntry;
1334 this.extraRoute = extraRoute;
1338 public List<ListenableFuture<Void>> call() {
1339 // If another renderer(for eg : CSS) needs to be supported, check can be performed here
1340 // to call the respective helpers.
1341 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
1342 //First Cleanup LabelRouteInfo
1343 //TODO(KIRAN) : Move the below block when addressing iRT/eRT for L3VPN Over VxLan
1344 LOG.debug("cleanupVpnInterfaceWorker: rd {} prefix {}", rd, prefixInfo.getIpAddress());
1345 if (VrfEntry.EncapType.Mplsgre.equals(vrfEntry.getEncapType())) {
1346 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1347 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1348 final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
1349 final ReentrantLock lock = lockFor(lriKey);
1352 LabelRouteInfo lri = getLabelRouteInfo(lriKey);
1353 if (lri != null && Objects.equals(lri.getPrefix(), vrfEntry.getDestPrefix())
1354 && nextHopAddressList.contains(lri.getNextHopIpList().get(0))) {
1355 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1356 fibUtil.getVpnInstanceOpData(rd);
1357 String vpnInstanceName = "";
1358 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1359 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1361 boolean lriRemoved = deleteLabelRouteInfo(lri, vpnInstanceName, tx);
1363 String parentRd = lri.getParentVpnRd();
1364 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1365 parentRd, vrfEntry.getDestPrefix()));
1368 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1369 rd, vrfEntry.getDestPrefix()));
1376 String ifName = prefixInfo.getVpnInterfaceName();
1377 Optional<String> optVpnName = fibUtil.getVpnNameFromRd(rd);
1378 String vpnName = null;
1380 if (Prefixes.PrefixCue.PhysNetFunc.equals(prefixInfo.getPrefixCue())) {
1381 // Get vpnId for rd = networkId since op vpnInterface will be pointing to rd = networkId
1382 Optional<String> vpnNameOpt = fibUtil.getVpnNameFromRd(vrfEntry.getParentVpnRd());
1383 if (vpnNameOpt.isPresent()) {
1384 vpnId = fibUtil.getVpnId(vpnNameOpt.get());
1387 if (optVpnName.isPresent()) {
1388 vpnName = optVpnName.get();
1389 Optional<VpnInterfaceOpDataEntry> opVpnInterface = tx
1390 .read(FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName)).get();
1391 if (opVpnInterface.isPresent()) {
1392 long associatedVpnId = fibUtil.getVpnId(vpnName);
1393 if (vpnId != associatedVpnId) {
1394 LOG.warn("Prefixes {} are associated with different vpn instance with id {} rather than {}",
1395 vrfEntry.getDestPrefix(), associatedVpnId, vpnId);
1396 LOG.warn("Not proceeding with Cleanup op data for prefix {}", vrfEntry.getDestPrefix());
1399 LOG.debug("Processing cleanup of prefix {} associated with vpn {}",
1400 vrfEntry.getDestPrefix(), associatedVpnId);
1404 if (extraRoute != null) {
1405 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1406 //Only one used Rd present in case of removal event
1407 String usedRd = usedRds.get(0);
1408 if (optVpnName.isPresent()) {
1409 tx.delete(BaseVrfEntryHandler.getVpnToExtrarouteIdentifier(vpnName, usedRd,
1410 vrfEntry.getDestPrefix()));
1411 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, configTx ->
1412 configTx.delete(VpnExtraRouteHelper.getUsedRdsIdentifier(vpnId, vrfEntry.getDestPrefix())));
1415 handleAdjacencyAndVpnOpInterfaceDeletion(vrfEntry, ifName, vpnName, tx);
1421 * Check all the adjacency in VpnInterfaceOpData and decide whether to delete the entire interface or only adj.
1422 * Remove Adjacency from VPNInterfaceOpData.
1423 * if Adjacency != primary.
1424 * if Adjacency == primary , then mark it for deletion.
1425 * Remove entire VPNinterfaceOpData Entry.
1426 * if sie of Adjacency <= 2 and all are marked for deletion , delete the entire VPNinterface Op entry.
1427 * @param vrfEntry - VrfEntry removed
1428 * @param ifName - Interface name from VRFentry
1429 * @param vpnName - VPN name of corresponding VRF
1430 * @param tx - ReadWrite Tx
1432 private void handleAdjacencyAndVpnOpInterfaceDeletion(VrfEntry vrfEntry, String ifName, String vpnName,
1433 TypedReadWriteTransaction<Operational> tx)
1434 throws ExecutionException, InterruptedException {
1435 InstanceIdentifier<Adjacency> adjacencyIid =
1436 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix());
1437 Optional<Adjacency> adjacencyOptional = tx.read(adjacencyIid).get();
1438 if (adjacencyOptional.isPresent()) {
1439 if (adjacencyOptional.get().getAdjacencyType() != Adjacency.AdjacencyType.PrimaryAdjacency) {
1440 tx.delete(FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix()));
1442 tx.merge(adjacencyIid,
1443 new AdjacencyBuilder(adjacencyOptional.get()).setMarkedForDeletion(true).build());
1447 Optional<AdjacenciesOp> optAdjacencies = tx.read(FibUtil.getAdjListPathOp(ifName, vpnName)).get();
1449 if (!optAdjacencies.isPresent() || optAdjacencies.get().getAdjacency() == null) {
1453 @NonNull List<Adjacency> adjacencies = optAdjacencies.get().nonnullAdjacency();
1454 if (adjacencies.size() <= 2
1455 && adjacencies.stream().allMatch(adjacency ->
1456 adjacency.getAdjacencyType() == Adjacency.AdjacencyType.PrimaryAdjacency
1457 && adjacency.isMarkedForDeletion() != null
1458 && adjacency.isMarkedForDeletion()
1460 LOG.info("Clean up vpn interface {} to vpn {} list.", ifName, vpnName);
1461 tx.delete(FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1465 private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
1466 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
1467 final String rd = vrfTableKey.getRouteDistinguisher();
1468 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
1469 if (vpnInstance == null) {
1470 LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
1473 final Collection<VpnToDpnList> vpnToDpnList;
1474 if (vrfEntry.getParentVpnRd() != null
1475 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
1476 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
1477 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
1478 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
1479 vpnInstance.getVpnToDpnList();
1480 LOG.info("deleteFibEntries: Processing deletion of PNF FIB entry with rd {} prefix {}",
1481 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
1483 vpnToDpnList = vpnInstance.getVpnToDpnList();
1486 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1487 final java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1488 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1489 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1490 if (subnetRoute != null) {
1491 long elanTag = subnetRoute.getElantag();
1492 LOG.trace("SUBNETROUTE: deleteFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
1493 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
1494 if (vpnToDpnList != null) {
1495 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1496 () -> Collections.singletonList(
1497 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1498 for (final VpnToDpnList curDpn : vpnToDpnList) {
1500 baseVrfEntryHandler.makeConnectedRoute(curDpn.getDpnId(), vpnInstance.getVpnId(),
1501 vrfEntry, vrfTableKey.getRouteDistinguisher(), null, NwConstants.DEL_FLOW,
1502 TransactionAdapter.toWriteTransaction(tx), null);
1503 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1504 optionalLabel.ifPresent(label -> makeLFibTableEntry(curDpn.getDpnId(), label, null,
1505 DEFAULT_FIB_FLOW_PRIORITY, NwConstants.DEL_FLOW, tx));
1508 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnInstance.getVpnId(),
1509 vrfEntry, NwConstants.DEL_FLOW, tx);
1513 optionalLabel.ifPresent(label -> {
1514 final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
1515 final ReentrantLock lock = lockFor(lriKey);
1518 LabelRouteInfo lri = getLabelRouteInfo(lriKey);
1519 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1520 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1521 fibUtil.getVpnInstanceOpData(rd);
1522 String vpnInstanceName = "";
1523 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1524 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1526 boolean lriRemoved = this.deleteLabelRouteInfo(lri, vpnInstanceName, null);
1528 String parentRd = lri.getParentVpnRd();
1529 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1530 parentRd, vrfEntry.getDestPrefix()));
1531 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}"
1532 + " as labelRouteInfo cleared", label, rd, vrfEntry.getDestPrefix());
1535 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1536 rd, vrfEntry.getDestPrefix()));
1537 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}",
1538 label, rd, vrfEntry.getDestPrefix());
1547 final List<BigInteger> localDpnIdList = deleteLocalFibEntry(vpnInstance.getVpnId(),
1548 vrfTableKey.getRouteDistinguisher(), vrfEntry);
1549 if (vpnToDpnList != null) {
1550 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1551 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1553 Optional<Routes> extraRouteOptional;
1554 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
1555 if (usedRds != null && !usedRds.isEmpty()) {
1556 if (usedRds.size() > 1) {
1557 LOG.error("The extra route prefix is still present in some DPNs");
1560 // The first rd is retrieved from usedrds as Only 1 rd would be present as extra route prefix
1561 //is not present in any other DPN
1562 extraRouteOptional = VpnExtraRouteHelper
1563 .getVpnExtraroutes(dataBroker, vpnName, usedRds.get(0), vrfEntry.getDestPrefix());
1566 extraRouteOptional = Optional.absent();
1569 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1570 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1571 if (localDpnIdList.size() <= 0) {
1572 for (VpnToDpnList curDpn : vpnToDpnList) {
1573 baseVrfEntryHandler.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
1574 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
1575 TransactionAdapter.toWriteTransaction(tx));
1578 for (BigInteger localDpnId : localDpnIdList) {
1579 for (VpnToDpnList curDpn : vpnToDpnList) {
1580 if (!Objects.equals(curDpn.getDpnId(), localDpnId)) {
1581 baseVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
1582 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
1583 TransactionAdapter.toWriteTransaction(tx));
1588 if (extraRouteOptional.isPresent()) {
1589 //Remove select groups only for extra-routes
1590 nextHopManager.removeNextHopPointer(nextHopManager
1591 .getRemoteSelectGroupKey(vpnInstance.getVpnId(), vrfEntry.getDestPrefix()));
1592 nextHopManager.removeNextHopPointer(nextHopManager
1593 .getLocalSelectGroupKey(vpnInstance.getVpnId(), vrfEntry.getDestPrefix()));
1598 //The flow/group entry has been deleted from config DS; need to clean up associated operational
1599 //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
1600 cleanUpOpDataForFib(vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(), vrfEntry);
1602 // Remove all fib entries configured due to interVpnLink, when nexthop is the opposite endPoint
1603 // of the interVpnLink.
1604 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
1605 if (optVpnUuid.isPresent()) {
1606 String vpnUuid = optVpnUuid.get();
1607 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
1608 Optional<InterVpnLinkDataComposite> optInterVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid);
1609 if (optInterVpnLink.isPresent()) {
1610 InterVpnLinkDataComposite interVpnLink = optInterVpnLink.get();
1611 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
1612 // This is route that points to the other endpoint of an InterVpnLink
1613 // In that case, we should look for the FIB table pointing to
1614 // LPortDispatcher table and remove it.
1615 removeInterVPNLinkRouteFlows(interVpnLink, vpnUuid, vrfEntry);
1623 private void makeLFibTableEntry(BigInteger dpId, long label, @Nullable List<InstructionInfo> instructions,
1624 int priority, int addOrRemove, TypedWriteTransaction<Configuration> tx) {
1626 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1627 newTx -> makeLFibTableEntry(dpId, label, instructions, priority, addOrRemove, newTx)), LOG,
1628 "Error making LFIB table entry");
1632 List<MatchInfo> matches = new ArrayList<>();
1633 matches.add(MatchEthernetType.MPLS_UNICAST);
1634 matches.add(new MatchMplsLabel(label));
1636 // Install the flow entry in L3_LFIB_TABLE
1637 String flowRef = FibUtil.getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, label, priority);
1639 FlowEntity flowEntity;
1640 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef, priority, flowRef, 0, 0,
1641 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
1642 Flow flow = flowEntity.getFlowBuilder().build();
1643 String flowId = flowEntity.getFlowId();
1644 FlowKey flowKey = new FlowKey(new FlowId(flowId));
1645 Node nodeDpn = FibUtil.buildDpnNode(dpId);
1646 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1647 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1648 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
1650 if (addOrRemove == NwConstants.ADD_FLOW) {
1651 tx.put(flowInstanceId, flow, CREATE_MISSING_PARENTS);
1653 tx.delete(flowInstanceId);
1656 LOG.debug("LFIB Entry for dpID {} : label : {} instructions {} : key {} {} successfully",
1657 dpId, label, instructions, flowKey, NwConstants.ADD_FLOW == addOrRemove ? "ADDED" : "REMOVED");
1660 public void populateFibOnNewDpn(final BigInteger dpnId, final long vpnId, final String rd,
1661 final FutureCallback<List<Void>> callback) {
1662 LOG.trace("New dpn {} for vpn {} : populateFibOnNewDpn", dpnId, rd);
1663 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1665 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1666 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1667 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1668 LogicalDatastoreType.CONFIGURATION, id);
1669 List<ListenableFuture<Void>> futures = new ArrayList<>();
1670 if (!vrfTable.isPresent()) {
1671 LOG.info("populateFibOnNewDpn: dpn: {}: VRF Table not yet available for RD {}", dpnId, rd);
1672 if (callback != null) {
1673 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1674 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1679 final ReentrantLock lock = lockFor(vpnInstance);
1682 futures.add(retryingTxRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
1683 for (final VrfEntry vrfEntry : vrfTable.get().nonnullVrfEntry()) {
1684 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1685 if (subnetRoute != null) {
1686 long elanTag = subnetRoute.getElantag();
1687 installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, tx);
1688 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry, NwConstants.ADD_FLOW,
1692 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1693 if (routerInt != null) {
1694 LOG.trace("Router augmented vrfentry found rd:{}, uuid:{}, ip:{}, mac:{}",
1695 rd, routerInt.getUuid(), routerInt.getIpAddress(), routerInt.getMacAddress());
1696 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1697 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1698 NwConstants.ADD_FLOW);
1701 //Handle local flow creation for imports
1702 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1703 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1704 if (optionalLabel.isPresent()) {
1705 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1706 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1707 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList, lri)) {
1708 if (Objects.equals(lri.getDpnId(), dpnId)) {
1710 int etherType = NWUtil.getEtherTypeFromIpPrefix(
1711 vrfEntry.getDestPrefix());
1712 createLocalFibEntry(vpnId, rd, vrfEntry, etherType);
1713 } catch (IllegalArgumentException ex) {
1714 LOG.warn("Unable to get etherType for IP Prefix {}",
1715 vrfEntry.getDestPrefix());
1722 boolean shouldCreateRemoteFibEntry = shouldCreateFibEntryForVrfAndVpnIdOnDpn(vpnId,
1724 if (shouldCreateRemoteFibEntry) {
1725 LOG.trace("Will create remote FIB entry for vrfEntry {} on DPN {}", vrfEntry, dpnId);
1726 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1727 List<SubTransaction> txnObjects = new ArrayList<>();
1728 bgpRouteVrfEntryHandler.createRemoteFibEntry(dpnId, vpnId,
1729 vrfTable.get().getRouteDistinguisher(), vrfEntry,
1730 TransactionAdapter.toWriteTransaction(tx), txnObjects);
1732 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
1738 if (callback != null) {
1739 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1740 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1749 public void populateExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1750 final String localNextHopIp, final String remoteNextHopIp) {
1751 LOG.trace("populateExternalRoutesOnDpn : dpn {}, vpn {}, rd {}, localNexthopIp {} , remoteNextHopIp {} ",
1752 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1753 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1754 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1755 List<SubTransaction> txnObjects = new ArrayList<>();
1756 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1757 if (vrfTable.isPresent()) {
1758 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1759 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1760 final ReentrantLock lock = lockFor(vpnInstance);
1763 vrfTable.get().nonnullVrfEntry().stream()
1764 .filter(vrfEntry -> RouteOrigin.BGP == RouteOrigin.value(vrfEntry.getOrigin()))
1765 .forEach(bgpRouteVrfEntryHandler.getConsumerForCreatingRemoteFib(dpnId, vpnId,
1766 rd, remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx), txnObjects));
1774 public void manageRemoteRouteOnDPN(final boolean action,
1775 final BigInteger localDpnId,
1778 final String destPrefix,
1779 final String destTepIp,
1781 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1783 if (vpnInstance == null) {
1784 LOG.error("VpnInstance for rd {} not present for prefix {}", rd, destPrefix);
1788 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, localDpnId),
1789 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1790 final ReentrantLock lock = lockFor(vpnInstance);
1793 VrfTablesKey vrfTablesKey = new VrfTablesKey(rd);
1794 VrfEntry vrfEntry = getVrfEntry(dataBroker, rd, destPrefix);
1795 if (vrfEntry == null) {
1798 LOG.trace("manageRemoteRouteOnDPN :: action {}, DpnId {}, vpnId {}, rd {}, destPfx {}",
1799 action, localDpnId, vpnId, rd, destPrefix);
1800 List<RoutePaths> routePathList = vrfEntry.getRoutePaths();
1801 VrfEntry modVrfEntry;
1802 if (routePathList == null || routePathList.isEmpty()) {
1803 modVrfEntry = FibHelper.getVrfEntryBuilder(vrfEntry, label,
1804 Collections.singletonList(destTepIp),
1805 RouteOrigin.value(vrfEntry.getOrigin()), null /* parentVpnRd */).build();
1807 modVrfEntry = vrfEntry;
1811 LOG.trace("manageRemoteRouteOnDPN updated(add) vrfEntry :: {}", modVrfEntry);
1812 createRemoteFibEntry(localDpnId, vpnId, vrfTablesKey.getRouteDistinguisher(),
1815 LOG.trace("manageRemoteRouteOnDPN updated(remove) vrfEntry :: {}", modVrfEntry);
1816 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnInstance.getVpnId(),
1817 vrfEntry.getDestPrefix());
1818 if (usedRds.size() > 1) {
1819 LOG.debug("The extra route prefix is still present in some DPNs");
1822 //Is this fib route an extra route? If yes, get the nexthop which would be
1823 //an adjacency in the vpn
1824 Optional<Routes> extraRouteOptional = Optional.absent();
1825 if (usedRds.size() != 0) {
1826 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1827 fibUtil.getVpnNameFromId(vpnInstance.getVpnId()),
1828 usedRds.get(0), vrfEntry.getDestPrefix());
1830 baseVrfEntryHandler.deleteRemoteRoute(null, localDpnId, vpnId, vrfTablesKey, modVrfEntry,
1831 extraRouteOptional, TransactionAdapter.toWriteTransaction(tx));
1839 public void cleanUpDpnForVpn(final BigInteger dpnId, final long vpnId, final String rd,
1840 final FutureCallback<List<Void>> callback) {
1841 LOG.trace("cleanUpDpnForVpn: Remove dpn {} for vpn {} : cleanUpDpnForVpn", dpnId, rd);
1842 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1844 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1845 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1846 List<SubTransaction> txnObjects = new ArrayList<>();
1847 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1848 LogicalDatastoreType.CONFIGURATION, id);
1849 List<ListenableFuture<Void>> futures = new ArrayList<>();
1850 if (vrfTable.isPresent()) {
1851 final ReentrantLock lock = lockFor(vpnInstance);
1854 futures.add(retryingTxRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1855 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1856 for (final VrfEntry vrfEntry : vrfTable.get().nonnullVrfEntry()) {
1857 /* parentRd is only filled for external PNF cases where the interface on the external
1858 * network VPN are used to cleanup the flows. For all other cases, use "rd" for
1859 * #fibUtil.isInterfacePresentInDpn().
1861 String parentRd = vrfEntry.getParentVpnRd() != null ? vrfEntry.getParentVpnRd()
1863 /* Handle subnet routes here */
1864 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1865 if (subnetRoute != null && !fibUtil
1866 .isInterfacePresentInDpn(parentRd, dpnId)) {
1867 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Cleaning subnetroute {} on dpn {}"
1868 + " for vpn {}", vrfEntry.getDestPrefix(), dpnId, rd);
1869 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1870 NwConstants.DEL_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1871 List<RoutePaths> routePaths = vrfEntry.getRoutePaths();
1872 if (routePaths != null) {
1873 for (RoutePaths routePath : routePaths) {
1874 makeLFibTableEntry(dpnId, routePath.getLabel(), null,
1875 DEFAULT_FIB_FLOW_PRIORITY,
1876 NwConstants.DEL_FLOW, tx);
1877 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Released subnetroute label {}"
1878 + " for rd {} prefix {}", routePath.getLabel(), rd,
1879 vrfEntry.getDestPrefix());
1882 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry,
1883 NwConstants.DEL_FLOW, tx);
1886 // ping responder for router interfaces
1887 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1888 if (routerInt != null) {
1889 LOG.trace("Router augmented vrfentry found for rd:{}, uuid:{}, ip:{}, mac:{}",
1890 rd, routerInt.getUuid(), routerInt.getIpAddress(),
1891 routerInt.getMacAddress());
1892 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1893 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1894 NwConstants.DEL_FLOW);
1898 //Handle local flow deletion for imports
1899 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1900 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1901 if (optionalLabel.isPresent()) {
1902 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1903 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1904 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList,
1905 lri) && Objects.equals(lri.getDpnId(), dpnId)) {
1906 deleteLocalFibEntry(vpnId, rd, vrfEntry);
1911 // Passing null as we don't know the dpn
1912 // to which prefix is attached at this point
1913 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1914 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1915 Optional<Routes> extraRouteOptional;
1916 //Is this fib route an extra route? If yes, get the nexthop which would be
1917 //an adjacency in the vpn
1918 if (usedRds != null && !usedRds.isEmpty()) {
1919 if (usedRds.size() > 1) {
1920 LOG.error("The extra route prefix is still present in some DPNs");
1923 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
1924 usedRds.get(0), vrfEntry.getDestPrefix());
1928 extraRouteOptional = Optional.absent();
1930 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1931 bgpRouteVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1932 vrfTable.get().key(), vrfEntry, extraRouteOptional,
1933 TransactionAdapter.toWriteTransaction(tx), txnObjects);
1935 if (subnetRoute == null || !fibUtil
1936 .isInterfacePresentInDpn(parentRd, dpnId)) {
1937 baseVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1938 vrfTable.get().key(), vrfEntry, extraRouteOptional,
1939 TransactionAdapter.toWriteTransaction(tx));
1947 if (callback != null) {
1948 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1949 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1952 LOG.error("cleanUpDpnForVpn: No vrf table found for rd {} vpnId {} dpn {}", rd, vpnId, dpnId);
1959 public void cleanUpExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1960 final String localNextHopIp, final String remoteNextHopIp) {
1961 LOG.trace("cleanUpExternalRoutesOnDpn : cleanup remote routes on dpn {} for vpn {}, rd {}, "
1962 + " localNexthopIp {} , remoteNexhtHopIp {}",
1963 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1964 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1965 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1966 List<SubTransaction> txnObjects = new ArrayList<>();
1967 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1968 if (vrfTable.isPresent()) {
1969 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1971 final ReentrantLock lock = lockFor(vpnInstance);
1974 return Collections.singletonList(
1975 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1976 tx -> vrfTable.get().nonnullVrfEntry().stream()
1977 .filter(vrfEntry -> RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP)
1978 .forEach(bgpRouteVrfEntryHandler.getConsumerForDeletingRemoteFib(dpnId, vpnId,
1979 remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx),
1988 public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
1989 InstanceIdentifierBuilder<VrfTables> idBuilder =
1990 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
1991 return idBuilder.build();
1994 private String getInterVpnFibFlowRef(String interVpnLinkName, String prefix, String nextHop) {
1995 return FLOWID_PREFIX + interVpnLinkName + NwConstants.FLOWID_SEPARATOR + prefix + NwConstants
1996 .FLOWID_SEPARATOR + nextHop;
1999 private String getTableMissFlowRef(BigInteger dpnId, short tableId, int tableMiss) {
2000 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR
2001 + tableMiss + FLOWID_PREFIX;
2005 private VrfEntry getVrfEntry(DataBroker broker, String rd, String ipPrefix) {
2006 InstanceIdentifier<VrfEntry> vrfEntryId = InstanceIdentifier.builder(FibEntries.class)
2007 .child(VrfTables.class, new VrfTablesKey(rd))
2008 .child(VrfEntry.class, new VrfEntryKey(ipPrefix)).build();
2009 Optional<VrfEntry> vrfEntry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
2010 if (vrfEntry.isPresent()) {
2011 return vrfEntry.get();
2016 public void removeInterVPNLinkRouteFlows(final InterVpnLinkDataComposite interVpnLink,
2017 final String vpnName,
2018 final VrfEntry vrfEntry) {
2019 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null && vrfEntry.getRoutePaths().size() == 1);
2021 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
2022 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
2024 if (targetDpns.isEmpty()) {
2025 LOG.warn("Could not find DPNs for VPN {} in InterVpnLink {}", vpnName, interVpnLinkName);
2029 java.util.Optional<String> optNextHop = FibUtil.getFirstNextHopAddress(vrfEntry);
2030 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
2034 optNextHop.ifPresent(nextHop -> {
2035 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, vrfEntry.getDestPrefix(), nextHop);
2036 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
2037 Flow flow = new FlowBuilder().withKey(flowKey).setId(new FlowId(flowRef))
2038 .setTableId(NwConstants.L3_FIB_TABLE).setFlowName(flowRef).build();
2040 LOG.trace("Removing flow in FIB table for interVpnLink {} key {}", interVpnLinkName, flowRef);
2041 for (BigInteger dpId : targetDpns) {
2042 LOG.debug("Removing flow: VrfEntry=[prefix={} nexthop={}] dpn {} for InterVpnLink {} in FIB",
2043 vrfEntry.getDestPrefix(), nextHop, dpId, interVpnLinkName);
2045 mdsalManager.removeFlow(dpId, flow);
2051 optLabel.ifPresent(label -> {
2052 LOG.trace("Removing flow in FIB table for interVpnLink {}", interVpnLinkName);
2054 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
2055 for (BigInteger dpId : targetDpns) {
2056 LOG.debug("Removing flow: VrfEntry=[prefix={} label={}] dpn {} for InterVpnLink {} in LFIB",
2057 vrfEntry.getDestPrefix(), label, dpId, interVpnLinkName);
2058 makeLFibTableEntry(dpId, label, /*instructions*/null, LFIB_INTERVPN_PRIORITY, NwConstants.DEL_FLOW,
2061 }), LOG, "Error removing flows");
2065 private static boolean isPrefixAndNextHopPresentInLri(String prefix,
2066 List<String> nextHopAddressList, LabelRouteInfo lri) {
2067 return lri != null && Objects.equals(lri.getPrefix(), prefix)
2068 && nextHopAddressList.contains(lri.getNextHopIpList().get(0));
2071 private boolean shouldCreateFibEntryForVrfAndVpnIdOnDpn(Long vpnId, VrfEntry vrfEntry, BigInteger dpnId) {
2072 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
2076 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
2077 if (prefix != null) {
2078 BigInteger prefixDpnId = prefix.getDpnId();
2079 if (dpnId.equals(prefixDpnId)) {
2080 LOG.trace("Should not create remote FIB entry for vrfEntry {} on DPN {}",
2088 private static ReentrantLock lockFor(final VpnInstanceOpDataEntry vpnInstance) {
2089 // FIXME: use vpnInstance.key() instead?
2090 return JvmGlobalLocks.getLockForString(vpnInstance.getVpnInstanceName());
2093 private static ReentrantLock lockFor(LabelRouteInfoKey label) {
2094 return JvmGlobalLocks.getLockFor(label);