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.concurrent.Callable;
31 import java.util.concurrent.CopyOnWriteArrayList;
32 import java.util.concurrent.ExecutionException;
33 import javax.annotation.PostConstruct;
34 import javax.inject.Inject;
35 import javax.inject.Singleton;
36 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
37 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
38 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
39 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
40 import org.opendaylight.genius.infra.Datastore.Configuration;
41 import org.opendaylight.genius.infra.Datastore.Operational;
42 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
43 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
44 import org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner;
45 import org.opendaylight.genius.infra.TransactionAdapter;
46 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
47 import org.opendaylight.genius.infra.TypedWriteTransaction;
48 import org.opendaylight.genius.mdsalutil.ActionInfo;
49 import org.opendaylight.genius.mdsalutil.FlowEntity;
50 import org.opendaylight.genius.mdsalutil.InstructionInfo;
51 import org.opendaylight.genius.mdsalutil.MDSALUtil;
52 import org.opendaylight.genius.mdsalutil.MatchInfo;
53 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
54 import org.opendaylight.genius.mdsalutil.NWUtil;
55 import org.opendaylight.genius.mdsalutil.NwConstants;
56 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
57 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
58 import org.opendaylight.genius.mdsalutil.actions.ActionPopMpls;
59 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
60 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
61 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
62 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
63 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
64 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
65 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
66 import org.opendaylight.genius.mdsalutil.matches.MatchMplsLabel;
67 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
68 import org.opendaylight.genius.utils.ServiceIndex;
69 import org.opendaylight.genius.utils.batching.SubTransaction;
70 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
71 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
72 import org.opendaylight.netvirt.elanmanager.api.IElanService;
73 import org.opendaylight.netvirt.fibmanager.NexthopManager.AdjacencyResult;
74 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
75 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
76 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
77 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
78 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
79 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
80 import org.opendaylight.serviceutils.upgrade.UpgradeState;
81 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.LabelRouteMap;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.RouterInterface;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRoute;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfo;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoBuilder;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoKey;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOp;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.PrefixesBuilder;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState.State;
114 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
115 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
116 import org.slf4j.Logger;
117 import org.slf4j.LoggerFactory;
121 public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry, VrfEntryListener> {
123 private static final Logger LOG = LoggerFactory.getLogger(VrfEntryListener.class);
124 private static final String FLOWID_PREFIX = "L3.";
125 private static final BigInteger COOKIE_VM_FIB_TABLE = new BigInteger("8000003", 16);
126 private static final int DEFAULT_FIB_FLOW_PRIORITY = 10;
127 private static final int IPV4_ADDR_PREFIX_LENGTH = 32;
128 private static final int LFIB_INTERVPN_PRIORITY = 15;
129 public static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
130 private static final int MAX_RETRIES = 3;
131 private static final BigInteger COOKIE_TABLE_MISS = new BigInteger("8000004", 16);
133 private final DataBroker dataBroker;
134 private final ManagedNewTransactionRunner txRunner;
135 private final RetryingManagedNewTransactionRunner retryingTxRunner;
136 private final IMdsalApiManager mdsalManager;
137 private final NexthopManager nextHopManager;
138 private final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler;
139 private final BaseVrfEntryHandler baseVrfEntryHandler;
140 private final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler;
141 private final JobCoordinator jobCoordinator;
142 private final IElanService elanManager;
143 private final FibUtil fibUtil;
144 private final InterVpnLinkCache interVpnLinkCache;
145 private final List<AutoCloseable> closeables = new CopyOnWriteArrayList<>();
146 private final UpgradeState upgradeState;
147 private final DataTreeEventCallbackRegistrar eventCallbacks;
150 public VrfEntryListener(final DataBroker dataBroker, final IMdsalApiManager mdsalApiManager,
151 final NexthopManager nexthopManager,
152 final IElanService elanManager,
153 final BaseVrfEntryHandler vrfEntryHandler,
154 final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler,
155 final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler,
156 final JobCoordinator jobCoordinator,
157 final FibUtil fibUtil,
158 final InterVpnLinkCache interVpnLinkCache,
159 final UpgradeState upgradeState,
160 final DataTreeEventCallbackRegistrar eventCallbacks) {
161 super(VrfEntry.class, VrfEntryListener.class);
162 this.dataBroker = dataBroker;
163 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
164 this.retryingTxRunner = new RetryingManagedNewTransactionRunner(dataBroker, MAX_RETRIES);
165 this.mdsalManager = mdsalApiManager;
166 this.nextHopManager = nexthopManager;
167 this.elanManager = elanManager;
168 this.baseVrfEntryHandler = vrfEntryHandler;
169 this.bgpRouteVrfEntryHandler = bgpRouteVrfEntryHandler;
170 this.routerInterfaceVrfEntryHandler = routerInterfaceVrfEntryHandler;
171 this.jobCoordinator = jobCoordinator;
172 this.fibUtil = fibUtil;
173 this.interVpnLinkCache = interVpnLinkCache;
174 this.upgradeState = upgradeState;
175 this.eventCallbacks = eventCallbacks;
181 LOG.info("{} init", getClass().getSimpleName());
182 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
186 @SuppressWarnings("checkstyle:IllegalCatch")
187 public void close() {
188 closeables.forEach(c -> {
191 } catch (Exception e) {
192 LOG.warn("Error closing {}", c, e);
198 protected VrfEntryListener getDataTreeChangeListener() {
199 return VrfEntryListener.this;
203 protected InstanceIdentifier<VrfEntry> getWildCardPath() {
204 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class).child(VrfEntry.class);
208 protected void add(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
209 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
210 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
211 LOG.debug("ADD: Adding Fib Entry rd {} prefix {} route-paths {}",
212 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
213 addFibEntries(identifier, vrfEntry, rd);
214 LOG.info("ADD: Added Fib Entry rd {} prefix {} route-paths {}",
215 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
218 //This method is temporary. Eventually Factory design pattern will be used to get
219 // right VrfEntryhandle and invoke its methods.
220 private void addFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
221 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
222 bgpRouteVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
225 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
226 LOG.info("EVPN flows need to be programmed.");
227 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
228 nextHopManager, jobCoordinator, elanManager, fibUtil, upgradeState, eventCallbacks);
229 evpnVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
230 closeables.add(evpnVrfEntryHandler);
233 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
234 if (routerInt != null) {
235 // ping responder for router interfaces
236 routerInterfaceVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
239 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
240 createFibEntries(identifier, vrfEntry);
246 protected void remove(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry) {
247 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
248 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
249 LOG.debug("REMOVE: Removing Fib Entry rd {} prefix {} route-paths {}",
250 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
251 removeFibEntries(identifier, vrfEntry, rd);
252 LOG.info("REMOVE: Removed Fib Entry rd {} prefix {} route-paths {}",
253 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
256 //This method is temporary. Eventually Factory design pattern will be used to get
257 // right VrfEntryhandle and invoke its methods.
258 private void removeFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
259 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
260 LOG.info("EVPN flows to be deleted");
261 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
262 nextHopManager, jobCoordinator, elanManager, fibUtil, upgradeState, eventCallbacks);
263 evpnVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
264 closeables.add(evpnVrfEntryHandler);
267 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
268 if (routerInt != null) {
269 // ping responder for router interfaces
270 routerInterfaceVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
273 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
274 deleteFibEntries(identifier, vrfEntry);
277 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
278 bgpRouteVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
284 // "Redundant nullcheck of originalRoutePath, which is known to be non-null" - the null checking for
285 // originalRoutePath is a little dicey - safest to keep the checking even if not needed.
286 @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE")
287 protected void update(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update) {
288 Preconditions.checkNotNull(update, "VrfEntry should not be null or empty.");
289 final String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
290 LOG.debug("UPDATE: Updating Fib Entries to rd {} prefix {} route-paths {} origin {} old-origin {}", rd,
291 update.getDestPrefix(), update.getRoutePaths(), update.getOrigin(), original.getOrigin());
292 // Handle BGP Routes first
293 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.BGP) {
294 bgpRouteVrfEntryHandler.updateFlows(identifier, original, update, rd);
295 LOG.info("UPDATE: Updated BGP advertised Fib Entry with rd {} prefix {} route-paths {}",
296 rd, update.getDestPrefix(), update.getRoutePaths());
300 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.STATIC) {
301 List<RoutePaths> originalRoutePath = original.getRoutePaths();
302 List<RoutePaths> updateRoutePath = update.getRoutePaths();
303 LOG.info("UPDATE: Original route-path {} update route-path {} ", originalRoutePath, updateRoutePath);
305 //Updates need to be handled for extraroute even if original vrf entry route path is null or
306 //updated vrf entry route path is null. This can happen during tunnel events.
307 Optional<VpnInstanceOpDataEntry> optVpnInstance = fibUtil.getVpnInstanceOpData(rd);
308 List<String> usedRds = new ArrayList<>();
309 if (optVpnInstance.isPresent()) {
310 usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,optVpnInstance.get().getVpnId(),
311 update.getDestPrefix());
313 // If original VRF Entry had nexthop null , but update VRF Entry
314 // has nexthop , route needs to be created on remote Dpns
315 if (originalRoutePath == null || originalRoutePath.isEmpty()
316 && updateRoutePath != null && !updateRoutePath.isEmpty() && usedRds.isEmpty()) {
317 // TODO(vivek): Though ugly, Not handling this code now, as each
318 // tep add event will invoke flow addition
319 LOG.trace("Original VRF entry NH is null for destprefix {}. And the prefix is not an extra route."
320 + " This event is IGNORED here.", update.getDestPrefix());
324 // If original VRF Entry had valid nexthop , but update VRF Entry
325 // has nexthop empty'ed out, route needs to be removed from remote Dpns
326 if (updateRoutePath == null || updateRoutePath.isEmpty()
327 && originalRoutePath != null && !originalRoutePath.isEmpty() && usedRds.isEmpty()) {
328 LOG.trace("Original VRF entry had valid NH for destprefix {}. And the prefix is not an extra route."
329 + "This event is IGNORED here.", update.getDestPrefix());
332 //Update the used rds and vpntoextraroute containers only for the deleted nextHops.
333 List<String> nextHopsRemoved = FibHelper.getNextHopListFromRoutePaths(original);
334 nextHopsRemoved.removeAll(FibHelper.getNextHopListFromRoutePaths(update));
335 List<ListenableFuture<Void>> futures = new ArrayList<>();
336 ListenableFuture<Void> configFuture =
337 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx ->
338 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, operTx ->
339 nextHopsRemoved.parallelStream()
340 .forEach(nextHopRemoved -> {
342 fibUtil.updateUsedRdAndVpnToExtraRoute(
343 configTx, operTx, nextHopRemoved, rd, update.getDestPrefix());
344 } catch (ExecutionException | InterruptedException e) {
345 throw new RuntimeException(e);
348 futures.add(configFuture);
349 Futures.addCallback(configFuture, new FutureCallback<Void>() {
351 public void onSuccess(Void result) {
352 createFibEntries(identifier, update);
353 LOG.info("UPDATE: Updated static Fib Entry with rd {} prefix {} route-paths {}",
354 rd, update.getDestPrefix(), update.getRoutePaths());
358 public void onFailure(Throwable throwable) {
359 LOG.error("Exception encountered while submitting operational future for update vrfentry {}",
362 }, MoreExecutors.directExecutor());
366 //Handle all other routes only on a cluster reboot
367 if (original.equals(update)) {
369 createFibEntries(identifier, update);
370 LOG.info("UPDATE: Updated Non-static Fib Entry with rd {} prefix {} route-paths {}",
371 rd, update.getDestPrefix(), update.getRoutePaths());
375 LOG.info("UPDATE: Ignoring update for FIB entry with rd {} prefix {} route-origin {} route-paths {}",
376 rd, update.getDestPrefix(), update.getOrigin(), update.getRoutePaths());
379 private void createFibEntries(final InstanceIdentifier<VrfEntry> vrfEntryIid, final VrfEntry vrfEntry) {
380 final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
381 List<SubTransaction> txnObjects = new ArrayList<>();
382 final VpnInstanceOpDataEntry vpnInstance =
383 fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
384 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
385 Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId()
386 + " has null vpnId!");
387 final Collection<VpnToDpnList> vpnToDpnList;
388 if (vrfEntry.getParentVpnRd() != null
389 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
390 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
391 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
392 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
393 vpnInstance.getVpnToDpnList();
394 LOG.info("createFibEntries: Processing creation of PNF FIB entry with rd {} prefix {}",
395 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
397 vpnToDpnList = vpnInstance.getVpnToDpnList();
399 final Long vpnId = vpnInstance.getVpnId();
400 final String rd = vrfTableKey.getRouteDistinguisher();
401 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
402 if (subnetRoute != null) {
403 final long elanTag = subnetRoute.getElantag();
404 LOG.trace("SUBNETROUTE: createFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
405 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
406 if (vpnToDpnList != null) {
407 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
408 () -> Collections.singletonList(
409 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
410 for (final VpnToDpnList curDpn : vpnToDpnList) {
411 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
412 installSubnetRouteInFib(curDpn.getDpnId(), elanTag, rd, vpnId, vrfEntry, tx);
413 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnId.longValue(),
414 vrfEntry, NwConstants.ADD_FLOW, tx);
421 // Get etherType value based on the IpPrefix address family type
424 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
425 } catch (IllegalArgumentException ex) {
426 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
430 final List<BigInteger> localDpnIdList = createLocalFibEntry(vpnInstance.getVpnId(), rd, vrfEntry, etherType);
431 if (!localDpnIdList.isEmpty() && vpnToDpnList != null) {
432 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
433 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
434 synchronized (vpnInstance.getVpnInstanceName().intern()) {
435 for (VpnToDpnList vpnDpn : vpnToDpnList) {
436 if (!localDpnIdList.contains(vpnDpn.getDpnId())) {
437 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
439 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
440 bgpRouteVrfEntryHandler.createRemoteFibEntry(vpnDpn.getDpnId(),
441 vpnId, vrfTableKey.getRouteDistinguisher(), vrfEntry,
442 TransactionAdapter.toWriteTransaction(tx),
445 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
446 vrfTableKey.getRouteDistinguisher(), vrfEntry, tx);
448 } catch (NullPointerException e) {
449 LOG.error("Failed to get create remote fib flows for prefix {} ",
450 vrfEntry.getDestPrefix(), e);
459 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
460 if (optVpnUuid.isPresent()) {
461 String vpnUuid = optVpnUuid.get();
462 InterVpnLinkDataComposite interVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid).orNull();
463 if (interVpnLink != null) {
464 LOG.debug("InterVpnLink {} found in Cache linking Vpn {}", interVpnLink.getInterVpnLinkName(), vpnUuid);
465 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
466 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
467 // This is an static route that points to the other endpoint of an InterVpnLink
468 // In that case, we should add another entry in FIB table pointing to LPortDispatcher table.
469 installIVpnLinkSwitchingFlows(interVpnLink, vpnUuid, vrfEntry, vpnId);
470 installInterVpnRouteInLFib(interVpnLink, vpnUuid, vrfEntry, etherType);
477 void refreshFibTables(String rd, String prefix) {
478 InstanceIdentifier<VrfEntry> vrfEntryId =
479 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
480 .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
481 Optional<VrfEntry> vrfEntry = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
482 if (vrfEntry.isPresent()) {
483 createFibEntries(vrfEntryId, vrfEntry.get());
487 private Prefixes updateVpnReferencesInLri(LabelRouteInfo lri, String vpnInstanceName, boolean isPresentInList) {
488 LOG.debug("updating LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
489 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
490 prefixBuilder.setDpnId(lri.getDpnId());
491 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
492 prefixBuilder.setIpAddress(lri.getPrefix());
493 // Increment the refCount here
494 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
495 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
496 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri);
497 if (!isPresentInList) {
498 LOG.debug("vpnName {} is not present in LRI with label {}..", vpnInstanceName, lri.getLabel());
499 List<String> vpnInstanceNames = lri.getVpnInstanceList();
500 vpnInstanceNames.add(vpnInstanceName);
501 builder.setVpnInstanceList(vpnInstanceNames);
502 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
504 LOG.debug("vpnName {} is present in LRI with label {}..", vpnInstanceName, lri.getLabel());
506 return prefixBuilder.build();
509 void installSubnetRouteInFib(final BigInteger dpnId, final long elanTag, final String rd,
510 final long vpnId, final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
512 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
513 newTx -> installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, newTx)), LOG,
514 "Error installing subnet route in FIB");
519 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
520 } catch (IllegalArgumentException ex) {
521 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
524 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
525 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
526 synchronized (label.toString().intern()) {
527 LabelRouteInfo lri = getLabelRouteInfo(label);
528 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
530 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
531 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
532 fibUtil.getVpnInstanceOpData(rd);
533 if (vpnInstanceOpDataEntryOptional.isPresent()) {
534 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
535 if (!lri.getVpnInstanceList().contains(vpnInstanceName)) {
536 updateVpnReferencesInLri(lri, vpnInstanceName, false);
540 LOG.debug("SUBNETROUTE: installSubnetRouteInFib: Fetched labelRouteInfo for label {} interface {}"
541 + " and got dpn {}", label, lri.getVpnInterfaceName(), lri.getDpnId());
545 final List<InstructionInfo> instructions = new ArrayList<>();
546 BigInteger subnetRouteMeta = BigInteger.valueOf(elanTag).shiftLeft(24)
547 .or(BigInteger.valueOf(vpnId).shiftLeft(1));
548 instructions.add(new InstructionWriteMetadata(subnetRouteMeta, MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
549 instructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
550 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
551 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
552 if (vrfEntry.getRoutePaths() != null) {
553 for (RoutePaths routePath : vrfEntry.getRoutePaths()) {
554 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
555 List<ActionInfo> actionsInfos = new ArrayList<>();
556 // reinitialize instructions list for LFIB Table
557 final List<InstructionInfo> LFIBinstructions = new ArrayList<>();
558 actionsInfos.add(new ActionPopMpls(etherType));
559 LFIBinstructions.add(new InstructionApplyActions(actionsInfos));
560 LFIBinstructions.add(new InstructionWriteMetadata(subnetRouteMeta,
561 MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
562 LFIBinstructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
564 makeLFibTableEntry(dpnId, routePath.getLabel(), LFIBinstructions, DEFAULT_FIB_FLOW_PRIORITY,
565 NwConstants.ADD_FLOW, tx);
571 private void installSubnetBroadcastAddrDropRule(final BigInteger dpnId, final String rd, final long vpnId,
572 final VrfEntry vrfEntry, int addOrRemove, TypedWriteTransaction<Configuration> tx) {
573 List<MatchInfo> matches = new ArrayList<>();
575 LOG.debug("SUBNETROUTE: installSubnetBroadcastAddrDropRule: destPrefix {} rd {} vpnId {} dpnId {}",
576 vrfEntry.getDestPrefix(), rd, vpnId, dpnId);
577 String[] ipAddress = vrfEntry.getDestPrefix().split("/");
578 String subnetBroadcastAddr = FibUtil.getBroadcastAddressFromCidr(vrfEntry.getDestPrefix());
579 final int prefixLength = ipAddress.length == 1 ? 0 : Integer.parseInt(ipAddress[1]);
581 InetAddress destPrefix;
583 destPrefix = InetAddress.getByName(subnetBroadcastAddr);
584 } catch (UnknownHostException e) {
585 LOG.error("Failed to get destPrefix for prefix {} rd {} VpnId {} DPN {}",
586 vrfEntry.getDestPrefix(), rd, vpnId, dpnId, e);
590 // Match on VpnId and SubnetBroadCast IP address
591 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
592 matches.add(MatchEthernetType.IPV4);
594 if (prefixLength != 0) {
595 matches.add(new MatchIpv4Destination(subnetBroadcastAddr, Integer.toString(IPV4_ADDR_PREFIX_LENGTH)));
598 //Action is to drop the packet
599 List<InstructionInfo> dropInstructions = new ArrayList<>();
600 List<ActionInfo> actionsInfos = new ArrayList<>();
601 actionsInfos.add(new ActionDrop());
602 dropInstructions.add(new InstructionApplyActions(actionsInfos));
604 int priority = DEFAULT_FIB_FLOW_PRIORITY + IPV4_ADDR_PREFIX_LENGTH;
605 String flowRef = FibUtil.getFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, rd, priority, destPrefix);
606 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef, priority,
607 flowRef, 0, 0, COOKIE_TABLE_MISS, matches, dropInstructions);
609 Flow flow = flowEntity.getFlowBuilder().build();
610 String flowId = flowEntity.getFlowId();
611 FlowKey flowKey = new FlowKey(new FlowId(flowId));
612 Node nodeDpn = FibUtil.buildDpnNode(dpnId);
614 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
615 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
616 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
618 if (addOrRemove == NwConstants.ADD_FLOW) {
619 tx.put(flowInstanceId,flow, true);
621 tx.delete(flowInstanceId);
626 * For a given route, it installs a flow in LFIB that sets the lportTag of the other endpoint and sends to
627 * LportDispatcher table (via table 80)
629 private void installInterVpnRouteInLFib(final InterVpnLinkDataComposite interVpnLink, final String vpnName,
630 final VrfEntry vrfEntry, int etherType) {
631 // INTERVPN routes are routes in a Vpn1 that have been leaked to Vpn2. In DC-GW, this Vpn2 route is pointing
632 // to a list of DPNs where Vpn2's VpnLink was instantiated. In these DPNs LFIB must be programmed so that the
633 // packet is commuted from Vpn2 to Vpn1.
634 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
635 if (!interVpnLink.isActive()) {
636 LOG.warn("InterVpnLink {} is NOT ACTIVE. InterVpnLink flows for prefix={} wont be installed in LFIB",
637 interVpnLinkName, vrfEntry.getDestPrefix());
641 Optional<Long> optLportTag = interVpnLink.getEndpointLportTagByVpnName(vpnName);
642 if (!optLportTag.isPresent()) {
643 LOG.warn("Could not retrieve lportTag for VPN {} endpoint in InterVpnLink {}", vpnName, interVpnLinkName);
647 Long lportTag = optLportTag.get();
648 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).orElse(null);
650 LOG.error("Could not find label in vrfEntry=[prefix={} routePaths={}]. LFIB entry for InterVpnLink skipped",
651 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
654 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionPopMpls(etherType));
655 List<InstructionInfo> instructions = Arrays.asList(
656 new InstructionApplyActions(actionsInfos),
657 new InstructionWriteMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(lportTag.intValue(),
658 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
659 NwConstants.L3VPN_SERVICE_INDEX)),
660 MetaDataUtil.getMetaDataMaskForLPortDispatcher()),
661 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
662 List<String> interVpnNextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
663 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
665 for (BigInteger dpId : targetDpns) {
666 LOG.debug("Installing flow: VrfEntry=[prefix={} label={} nexthop={}] dpn {} for InterVpnLink {} in LFIB",
667 vrfEntry.getDestPrefix(), label, interVpnNextHopList, dpId, interVpnLink.getInterVpnLinkName());
669 makeLFibTableEntry(dpId, label, instructions, LFIB_INTERVPN_PRIORITY, NwConstants.ADD_FLOW,
676 * Installs the flows in FIB table that, for a given route, do the switching from one VPN to the other.
678 private void installIVpnLinkSwitchingFlows(final InterVpnLinkDataComposite interVpnLink, final String vpnUuid,
679 final VrfEntry vrfEntry, long vpnTag) {
680 Preconditions.checkNotNull(interVpnLink, "InterVpnLink cannot be null");
681 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null
682 && vrfEntry.getRoutePaths().size() == 1);
683 String destination = vrfEntry.getDestPrefix();
684 String nextHop = vrfEntry.getRoutePaths().get(0).getNexthopAddress();
685 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
687 // After having received a static route, we should check if the vpn is part of an inter-vpn-link.
688 // In that case, we should populate the FIB table of the VPN pointing to LPortDisptacher table
689 // using as metadata the LPortTag associated to that vpn in the inter-vpn-link.
690 if (interVpnLink.getState().or(State.Error) != State.Active) {
691 LOG.warn("Route to {} with nexthop={} cannot be installed because the interVpnLink {} is not active",
692 destination, nextHop, interVpnLinkName);
696 Optional<Long> optOtherEndpointLportTag = interVpnLink.getOtherEndpointLportTagByVpnName(vpnUuid);
697 if (!optOtherEndpointLportTag.isPresent()) {
698 LOG.warn("Could not find suitable LportTag for the endpoint opposite to vpn {} in interVpnLink {}",
699 vpnUuid, interVpnLinkName);
703 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnUuid);
704 if (targetDpns.isEmpty()) {
705 LOG.warn("Could not find DPNs for endpoint opposite to vpn {} in interVpnLink {}",
706 vpnUuid, interVpnLinkName);
710 String[] values = destination.split("/");
711 String destPrefixIpAddress = values[0];
712 int prefixLength = values.length == 1 ? 0 : Integer.parseInt(values[1]);
714 List<MatchInfo> matches = new ArrayList<>();
715 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnTag), MetaDataUtil.METADATA_MASK_VRFID));
716 matches.add(MatchEthernetType.IPV4);
718 if (prefixLength != 0) {
719 matches.add(new MatchIpv4Destination(destPrefixIpAddress, Integer.toString(prefixLength)));
722 List<Instruction> instructions =
723 Arrays.asList(new InstructionWriteMetadata(
724 MetaDataUtil.getMetaDataForLPortDispatcher(optOtherEndpointLportTag.get().intValue(),
725 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants
726 .L3VPN_SERVICE_INDEX)),
727 MetaDataUtil.getMetaDataMaskForLPortDispatcher()).buildInstruction(0),
728 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE).buildInstruction(1));
730 int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
731 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, destination, nextHop);
732 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef, priority, flowRef, 0, 0,
733 COOKIE_VM_FIB_TABLE, matches, instructions);
735 LOG.trace("Installing flow in FIB table for vpn {} interVpnLink {} nextHop {} key {}",
736 vpnUuid, interVpnLink.getInterVpnLinkName(), nextHop, flowRef);
738 for (BigInteger dpId : targetDpns) {
740 LOG.debug("Installing flow: VrfEntry=[prefix={} route-paths={}] dpn {} for InterVpnLink {} in FIB",
741 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(),
742 dpId, interVpnLink.getInterVpnLinkName());
744 mdsalManager.installFlow(dpId, flowEntity);
748 private List<BigInteger> createLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry, int etherType) {
749 List<BigInteger> returnLocalDpnId = new ArrayList<>();
750 String localNextHopIP = vrfEntry.getDestPrefix();
751 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
752 String vpnName = fibUtil.getVpnNameFromId(vpnId);
753 if (localNextHopInfo == null) {
754 boolean localNextHopSeen = false;
755 List<Routes> vpnExtraRoutes = null;
756 String rdPrefixKey = localNextHopIP + FibConstants.SEPARATOR + rd;
757 //Synchronized to prevent missing bucket action due to race condition between refreshFib and
758 // add/updateFib threads on missing nexthop in VpnToExtraroutes
759 synchronized (rdPrefixKey.intern()) {
760 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, localNextHopIP);
761 vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
762 vpnName, usedRds, localNextHopIP);
763 if (LOG.isDebugEnabled()) {
764 LOG.debug("Creating Local fib entry with vpnName {} usedRds {} localNextHopIP {} vpnExtraRoutes {}",
765 vpnName, usedRds, localNextHopIP, vpnExtraRoutes);
768 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
769 for (Routes vpnExtraRoute : vpnExtraRoutes) {
771 if (isIpv4Address(vpnExtraRoute.getNexthopIpList().get(0))) {
772 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
774 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
776 Prefixes localNextHopInfoLocal = fibUtil.getPrefixToInterface(vpnId,
778 if (localNextHopInfoLocal != null) {
779 localNextHopSeen = true;
781 checkCreateLocalFibEntry(localNextHopInfoLocal, localNextHopInfoLocal.getIpAddress(),
782 vpnId, rd, vrfEntry, vpnExtraRoute, vpnExtraRoutes, etherType);
783 returnLocalDpnId.add(dpnId);
787 if (!localNextHopSeen && RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
788 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
789 if (optionalLabel.isPresent()) {
790 Long label = optionalLabel.get();
791 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
792 synchronized (label.toString().intern()) {
793 LabelRouteInfo lri = getLabelRouteInfo(label);
794 if (isPrefixAndNextHopPresentInLri(localNextHopIP, nextHopAddressList, lri)) {
795 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
796 fibUtil.getVpnInstanceOpData(rd);
797 if (vpnInstanceOpDataEntryOptional.isPresent()) {
798 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
799 if (lri.getVpnInstanceList().contains(vpnInstanceName)) {
800 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, true);
801 localNextHopIP = lri.getPrefix();
803 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, false);
804 localNextHopIP = lri.getPrefix();
807 if (localNextHopInfo != null) {
808 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
809 label, localNextHopInfo.getVpnInterfaceName(), lri.getDpnId());
810 if (vpnExtraRoutes.isEmpty()) {
811 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
812 vpnId, rd, vrfEntry, null, vpnExtraRoutes, etherType);
813 returnLocalDpnId.add(dpnId);
815 for (Routes extraRoutes : vpnExtraRoutes) {
816 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
817 vpnId, rd, vrfEntry, extraRoutes, vpnExtraRoutes, etherType);
818 returnLocalDpnId.add(dpnId);
826 if (returnLocalDpnId.isEmpty()) {
827 LOG.error("Local DPNID is empty for rd {}, vpnId {}, vrfEntry {}", rd, vpnId, vrfEntry);
830 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP, vpnId,
831 rd, vrfEntry, /*routes*/ null, /*vpnExtraRoutes*/ null, etherType);
833 returnLocalDpnId.add(dpnId);
836 return returnLocalDpnId;
839 private BigInteger checkCreateLocalFibEntry(Prefixes localNextHopInfo, String localNextHopIP,
840 final Long vpnId, final String rd,
841 final VrfEntry vrfEntry,
842 Routes routes, List<Routes> vpnExtraRoutes,
844 String vpnName = fibUtil.getVpnNameFromId(vpnId);
845 if (localNextHopInfo != null) {
848 final BigInteger dpnId = localNextHopInfo.getDpnId();
849 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
850 LOG.debug("checkCreateLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
851 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
854 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
855 LOG.debug("checkCreateLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
856 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
859 if (!isVpnPresentInDpn(rd, dpnId)) {
860 LOG.error("checkCreateLocalFibEntry: The VPN with id {} rd {} is not available on dpn {}",
861 vpnId, rd, dpnId.toString());
862 return BigInteger.ZERO;
864 String interfaceName = localNextHopInfo.getVpnInterfaceName();
865 String prefix = vrfEntry.getDestPrefix();
866 String gwMacAddress = vrfEntry.getGatewayMacAddress();
867 //The loadbalancing group is created only if the extra route has multiple nexthops
868 //to avoid loadbalancing the discovered routes
869 if (vpnExtraRoutes != null && routes != null) {
870 if (vpnExtraRoutes.size() > 1) {
871 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
872 localGroupId = nextHopManager.getLocalSelectGroup(vpnId, vrfEntry.getDestPrefix());
874 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
875 localGroupId = groupId;
878 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP, prefix,
880 localGroupId = groupId;
882 if (groupId == FibConstants.INVALID_GROUP_ID) {
883 LOG.error("Unable to create Group for local prefix {} on rd {} for vpninterface {} on Node {}",
884 prefix, rd, interfaceName, dpnId.toString());
885 return BigInteger.ZERO;
887 final List<InstructionInfo> instructions = Collections.singletonList(
888 new InstructionApplyActions(
889 Collections.singletonList(new ActionGroup(groupId))));
890 final List<InstructionInfo> lfibinstructions = Collections.singletonList(
891 new InstructionApplyActions(
892 Arrays.asList(new ActionPopMpls(etherType), new ActionGroup(localGroupId))));
893 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
894 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
895 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
896 jobCoordinator.enqueueJob(jobKey,
897 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
898 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
899 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
900 if (FibUtil.isBgpVpn(vpnName, rd)) {
901 optLabel.ifPresent(label -> {
902 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
904 "Installing LFIB and tunnel table entry on dpn {} for interface {} with label "
905 + "{}, rd {}, prefix {}, nexthop {}", dpnId,
906 localNextHopInfo.getVpnInterfaceName(), optLabel, rd, vrfEntry.getDestPrefix(),
908 makeLFibTableEntry(dpnId, label, lfibinstructions, DEFAULT_FIB_FLOW_PRIORITY,
909 NwConstants.ADD_FLOW, tx);
910 makeTunnelTableEntry(dpnId, label, localGroupId, tx);
912 LOG.debug("Route with rd {} prefix {} label {} nexthop {} for vpn {} is an imported "
913 + "route. LFib and Terminating table entries will not be created.",
914 rd, vrfEntry.getDestPrefix(), optLabel, nextHopAddressList, vpnId);
921 LOG.error("localNextHopInfo received is null for prefix {} on rd {} on vpn {}", vrfEntry.getDestPrefix(), rd,
923 return BigInteger.ZERO;
926 private boolean isVpnPresentInDpn(String rd, BigInteger dpnId) {
927 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
928 Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
929 if (dpnInVpn.isPresent()) {
935 private LabelRouteInfo getLabelRouteInfo(Long label) {
936 InstanceIdentifier<LabelRouteInfo> lriIid = InstanceIdentifier.builder(LabelRouteMap.class)
937 .child(LabelRouteInfo.class, new LabelRouteInfoKey(label)).build();
938 Optional<LabelRouteInfo> opResult = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, lriIid);
939 if (opResult.isPresent()) {
940 return opResult.get();
945 private boolean deleteLabelRouteInfo(LabelRouteInfo lri, String vpnInstanceName,
946 TypedWriteTransaction<Operational> tx) {
951 LOG.debug("deleting LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
952 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
953 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
955 List<String> vpnInstancesList = lri.getVpnInstanceList() != null
956 ? lri.getVpnInstanceList() : new ArrayList<>();
957 if (vpnInstancesList.contains(vpnInstanceName)) {
958 LOG.debug("vpninstance {} name is present", vpnInstanceName);
959 vpnInstancesList.remove(vpnInstanceName);
961 if (vpnInstancesList.isEmpty()) {
962 LOG.debug("deleting LRI instance object for label {}", lri.getLabel());
966 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId);
970 LOG.debug("updating LRI instance object for label {}", lri.getLabel());
971 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri).setVpnInstanceList(vpnInstancesList);
972 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
977 void makeTunnelTableEntry(BigInteger dpId, long label, long groupId/*String egressInterfaceName*/,
978 TypedWriteTransaction<Configuration> tx) {
979 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionGroup(groupId));
981 createTerminatingServiceActions(dpId, (int) label, actionsInfos, tx);
983 LOG.debug("Terminating service Entry for dpID {} : label : {} egress : {} installed successfully",
984 dpId, label, groupId);
987 public void createTerminatingServiceActions(BigInteger destDpId, int label, List<ActionInfo> actionsInfos,
988 TypedWriteTransaction<Configuration> tx) {
989 List<MatchInfo> mkMatches = new ArrayList<>();
991 LOG.debug("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}",
992 destDpId, label, actionsInfos);
995 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
996 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
998 List<InstructionInfo> mkInstructions = new ArrayList<>();
999 mkInstructions.add(new InstructionApplyActions(actionsInfos));
1001 FlowEntity terminatingServiceTableFlowEntity =
1002 MDSALUtil.buildFlowEntity(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,
1003 getTableMissFlowRef(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE, label), 5,
1004 String.format("%s:%d", "TST Flow Entry ", label),
1005 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, mkInstructions);
1007 FlowKey flowKey = new FlowKey(new FlowId(terminatingServiceTableFlowEntity.getFlowId()));
1009 FlowBuilder flowbld = terminatingServiceTableFlowEntity.getFlowBuilder();
1011 Node nodeDpn = FibUtil.buildDpnNode(terminatingServiceTableFlowEntity.getDpnId());
1012 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1013 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1014 .child(Table.class, new TableKey(terminatingServiceTableFlowEntity.getTableId()))
1015 .child(Flow.class, flowKey).build();
1016 tx.put(flowInstanceId, flowbld.build(), CREATE_MISSING_PARENTS);
1019 private void removeTunnelTableEntry(BigInteger dpId, long label, TypedWriteTransaction<Configuration> tx) {
1020 FlowEntity flowEntity;
1021 LOG.debug("remove terminatingServiceActions called with DpnId = {} and label = {}", dpId, label);
1022 List<MatchInfo> mkMatches = new ArrayList<>();
1023 // Matching metadata
1024 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
1025 flowEntity = MDSALUtil.buildFlowEntity(dpId,
1026 NwConstants.INTERNAL_TUNNEL_TABLE,
1027 getTableMissFlowRef(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, (int) label),
1028 5, String.format("%s:%d", "TST Flow Entry ", label), 0, 0,
1029 COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, null);
1030 Node nodeDpn = FibUtil.buildDpnNode(flowEntity.getDpnId());
1031 FlowKey flowKey = new FlowKey(new FlowId(flowEntity.getFlowId()));
1032 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1033 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1034 .child(Table.class, new TableKey(flowEntity.getTableId())).child(Flow.class, flowKey).build();
1036 tx.delete(flowInstanceId);
1037 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully", dpId, label);
1040 public List<BigInteger> deleteLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
1041 List<BigInteger> returnLocalDpnId = new ArrayList<>();
1042 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1043 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1044 boolean shouldUpdateNonEcmpLocalNextHop = true;
1045 if (localNextHopInfo == null) {
1046 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1047 if (usedRds.size() > 1) {
1048 LOG.error("The extra route prefix {} is still present in some DPNs in vpn {} on rd {}",
1049 vrfEntry.getDestPrefix(), vpnName, rd);
1050 return returnLocalDpnId;
1052 String vpnRd = !usedRds.isEmpty() ? usedRds.get(0) : rd;
1053 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency
1055 Optional<Routes> extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1056 vpnName, vpnRd, vrfEntry.getDestPrefix());
1057 if (extraRouteOptional.isPresent()) {
1058 Routes extraRoute = extraRouteOptional.get();
1060 if (isIpv4Address(extraRoute.getNexthopIpList().get(0))) {
1061 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
1063 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
1065 if (extraRoute.getNexthopIpList().size() > 1) {
1066 shouldUpdateNonEcmpLocalNextHop = false;
1068 localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1069 if (localNextHopInfo != null) {
1070 String localNextHopIP = localNextHopInfo.getIpAddress();
1071 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP, vpnName, vpnId, rd,
1072 vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1073 if (!dpnId.equals(BigInteger.ZERO)) {
1074 LOG.trace("Deleting ECMP group for prefix {}, dpn {}", vrfEntry.getDestPrefix(), dpnId);
1075 nextHopManager.deleteLoadBalancingNextHop(vpnId, dpnId, vrfEntry.getDestPrefix());
1076 returnLocalDpnId.add(dpnId);
1079 LOG.error("localNextHopInfo unavailable while deleting prefix {} with rds {}, primary rd {} in "
1080 + "vpn {}", vrfEntry.getDestPrefix(), usedRds, rd, vpnName);
1084 if (localNextHopInfo == null) {
1085 /* Imported VRF entry */
1086 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1087 if (optionalLabel.isPresent()) {
1088 Long label = optionalLabel.get();
1089 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1090 LabelRouteInfo lri = getLabelRouteInfo(label);
1091 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1092 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1093 prefixBuilder.setDpnId(lri.getDpnId());
1094 BigInteger dpnId = checkDeleteLocalFibEntry(prefixBuilder.build(), nextHopAddressList.get(0),
1095 vpnName, vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1096 if (!dpnId.equals(BigInteger.ZERO)) {
1097 returnLocalDpnId.add(dpnId);
1104 LOG.debug("Obtained prefix to interface for rd {} prefix {}", rd, vrfEntry.getDestPrefix());
1105 String localNextHopIP = localNextHopInfo.getIpAddress();
1106 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP, vpnName, vpnId, rd, vrfEntry,
1107 shouldUpdateNonEcmpLocalNextHop);
1108 if (!dpnId.equals(BigInteger.ZERO)) {
1109 returnLocalDpnId.add(dpnId);
1113 return returnLocalDpnId;
1116 private BigInteger checkDeleteLocalFibEntry(Prefixes localNextHopInfo, final String localNextHopIP,
1117 final String vpnName, final Long vpnId, final String rd, final VrfEntry vrfEntry,
1118 boolean shouldUpdateNonEcmpLocalNextHop) {
1119 if (localNextHopInfo != null) {
1120 final BigInteger dpnId = localNextHopInfo.getDpnId();
1121 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
1122 LOG.debug("checkDeleteLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1123 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1126 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
1127 LOG.debug("checkDeleteLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1128 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1132 jobCoordinator.enqueueJob(FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix()),
1133 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1134 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1135 NwConstants.DEL_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1136 if (FibUtil.isBgpVpn(vpnName, rd)) {
1137 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1138 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1139 makeLFibTableEntry(dpnId, label, null /* instructions */, DEFAULT_FIB_FLOW_PRIORITY,
1140 NwConstants.DEL_FLOW, tx);
1141 removeTunnelTableEntry(dpnId, label, tx);
1146 //TODO: verify below adjacency call need to be optimized (?)
1147 //In case of the removal of the extra route, the loadbalancing group is updated
1148 if (shouldUpdateNonEcmpLocalNextHop) {
1149 baseVrfEntryHandler.deleteLocalAdjacency(dpnId, vpnId, localNextHopIP, vrfEntry.getDestPrefix());
1153 return BigInteger.ZERO;
1156 private void createRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, String rd,
1157 final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
1159 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1160 newTx -> createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx)), LOG,
1161 "Error creating remote FIB entry");
1165 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1166 LOG.debug("createremotefibentry: adding route {} for rd {} on remoteDpnId {}",
1167 vrfEntry.getDestPrefix(), rd, remoteDpnId);
1169 List<AdjacencyResult> adjacencyResults = baseVrfEntryHandler.resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
1170 if (adjacencyResults.isEmpty()) {
1171 LOG.error("Could not get interface for route-paths: {} in vpn {} on DPN {}",
1172 vrfEntry.getRoutePaths(), rd, remoteDpnId);
1173 LOG.error("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
1177 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1178 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
1179 vpnName, usedRds, vrfEntry.getDestPrefix());
1180 // create loadbalancing groups for extra routes only when the extra route is present behind
1182 if (!vpnExtraRoutes.isEmpty()) {
1183 List<InstructionInfo> instructions = new ArrayList<>();
1184 // Obtain the local routes for this particular dpn.
1185 java.util.Optional<Routes> routes = vpnExtraRoutes
1188 Prefixes prefixToInterface = fibUtil.getPrefixToInterface(vpnId,
1189 fibUtil.getIpPrefix(route.getNexthopIpList().get(0)));
1190 if (prefixToInterface == null) {
1193 return remoteDpnId.equals(prefixToInterface.getDpnId());
1195 long groupId = nextHopManager.createNextHopGroups(vpnId, rd, remoteDpnId, vrfEntry,
1196 routes.isPresent() ? routes.get() : null, vpnExtraRoutes);
1197 if (groupId == FibConstants.INVALID_GROUP_ID) {
1198 LOG.error("Unable to create Group for local prefix {} on rd {} on Node {}",
1199 vrfEntry.getDestPrefix(), rd, remoteDpnId.toString());
1202 List<ActionInfo> actionInfos =
1203 Collections.singletonList(new ActionGroup(groupId));
1204 instructions.add(new InstructionApplyActions(actionInfos));
1205 baseVrfEntryHandler.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
1206 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1208 baseVrfEntryHandler.programRemoteFib(remoteDpnId, vpnId, vrfEntry,
1209 TransactionAdapter.toWriteTransaction(tx), rd, adjacencyResults, null);
1212 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
1215 protected void cleanUpOpDataForFib(Long vpnId, String primaryRd, final VrfEntry vrfEntry) {
1216 /* Get interface info from prefix to interface mapping;
1217 Use the interface info to get the corresponding vpn interface op DS entry,
1218 remove the adjacency corresponding to this fib entry.
1219 If adjacency removed is the last adjacency, clean up the following:
1220 - vpn interface from dpntovpn list, dpn if last vpn interface on dpn
1221 - prefix to interface entry
1222 - vpn interface op DS
1224 LOG.debug("Cleanup of prefix {} in VPN {}", vrfEntry.getDestPrefix(), vpnId);
1225 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1226 if (prefixInfo == null) {
1227 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1228 String usedRd = usedRds.isEmpty() ? primaryRd : usedRds.get(0);
1229 Routes extraRoute = baseVrfEntryHandler.getVpnToExtraroute(vpnId, usedRd, vrfEntry.getDestPrefix());
1230 if (extraRoute != null) {
1231 for (String nextHopIp : extraRoute.getNexthopIpList()) {
1232 LOG.debug("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
1233 if (nextHopIp != null) {
1235 if (isIpv4Address(nextHopIp)) {
1236 ipPrefix = nextHopIp + NwConstants.IPV4PREFIX;
1238 ipPrefix = nextHopIp + NwConstants.IPV6PREFIX;
1240 prefixInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1241 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1245 if (prefixInfo == null) {
1246 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1247 if (optionalLabel.isPresent()) {
1248 Long label = optionalLabel.get();
1249 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1250 LabelRouteInfo lri = getLabelRouteInfo(label);
1251 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1252 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1253 prefixBuilder.setDpnId(lri.getDpnId());
1254 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
1255 prefixBuilder.setIpAddress(lri.getPrefix());
1256 prefixInfo = prefixBuilder.build();
1257 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
1258 label, prefixInfo.getVpnInterfaceName(), lri.getDpnId());
1259 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1264 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, null /*Routes*/);
1268 private void checkCleanUpOpDataForFib(final Prefixes prefixInfo, final Long vpnId, final String rd,
1269 final VrfEntry vrfEntry, final Routes extraRoute) {
1271 if (prefixInfo == null) {
1272 LOG.error("Cleanup VPN Data Failed as unable to find prefix Info for prefix {} VpnId {} rd {}",
1273 vrfEntry.getDestPrefix(), vpnId, rd);
1274 return; //Don't have any info for this prefix (shouldn't happen); need to return
1277 if (Prefixes.PrefixCue.Nat.equals(prefixInfo.getPrefixCue())) {
1278 LOG.debug("NAT Prefix {} with vpnId {} rd {}. Skip FIB processing",
1279 vrfEntry.getDestPrefix(), vpnId, rd);
1283 String ifName = prefixInfo.getVpnInterfaceName();
1284 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName,
1285 new CleanupVpnInterfaceWorker(prefixInfo, vpnId, rd, vrfEntry, extraRoute));
1288 private class CleanupVpnInterfaceWorker implements Callable<List<ListenableFuture<Void>>> {
1289 Prefixes prefixInfo;
1295 CleanupVpnInterfaceWorker(final Prefixes prefixInfo, final Long vpnId, final String rd,
1296 final VrfEntry vrfEntry, final Routes extraRoute) {
1297 this.prefixInfo = prefixInfo;
1300 this.vrfEntry = vrfEntry;
1301 this.extraRoute = extraRoute;
1305 public List<ListenableFuture<Void>> call() {
1306 // If another renderer(for eg : CSS) needs to be supported, check can be performed here
1307 // to call the respective helpers.
1308 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
1309 //First Cleanup LabelRouteInfo
1310 //TODO(KIRAN) : Move the below block when addressing iRT/eRT for L3VPN Over VxLan
1311 LOG.debug("cleanupVpnInterfaceWorker: rd {} prefix {}", rd, prefixInfo.getIpAddress());
1312 if (VrfEntry.EncapType.Mplsgre.equals(vrfEntry.getEncapType())) {
1313 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1314 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1315 synchronized (label.toString().intern()) {
1316 LabelRouteInfo lri = getLabelRouteInfo(label);
1317 if (lri != null && lri.getPrefix().equals(vrfEntry.getDestPrefix())
1318 && nextHopAddressList.contains(lri.getNextHopIpList().get(0))) {
1319 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1320 fibUtil.getVpnInstanceOpData(rd);
1321 String vpnInstanceName = "";
1322 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1323 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1325 boolean lriRemoved = deleteLabelRouteInfo(lri, vpnInstanceName, tx);
1327 String parentRd = lri.getParentVpnRd();
1328 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1329 parentRd, vrfEntry.getDestPrefix()));
1332 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1333 rd, vrfEntry.getDestPrefix()));
1338 String ifName = prefixInfo.getVpnInterfaceName();
1339 Optional<String> optVpnName = fibUtil.getVpnNameFromRd(rd);
1340 String vpnName = null;
1342 if (Prefixes.PrefixCue.PhysNetFunc.equals(prefixInfo.getPrefixCue())) {
1343 // Get vpnId for rd = networkId since op vpnInterface will be pointing to rd = networkId
1344 Optional<String> vpnNameOpt = fibUtil.getVpnNameFromRd(vrfEntry.getParentVpnRd());
1345 if (vpnNameOpt.isPresent()) {
1346 vpnId = fibUtil.getVpnId(vpnNameOpt.get());
1349 if (optVpnName.isPresent()) {
1350 vpnName = optVpnName.get();
1351 Optional<VpnInterfaceOpDataEntry> opVpnInterface = tx
1352 .read(FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName)).get();
1353 if (opVpnInterface.isPresent()) {
1354 long associatedVpnId = fibUtil.getVpnId(vpnName);
1355 if (vpnId != associatedVpnId) {
1356 LOG.warn("Prefixes {} are associated with different vpn instance with id {} rather than {}",
1357 vrfEntry.getDestPrefix(), associatedVpnId, vpnId);
1358 LOG.warn("Not proceeding with Cleanup op data for prefix {}", vrfEntry.getDestPrefix());
1361 LOG.debug("Processing cleanup of prefix {} associated with vpn {}",
1362 vrfEntry.getDestPrefix(), associatedVpnId);
1366 if (extraRoute != null) {
1367 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1368 //Only one used Rd present in case of removal event
1369 String usedRd = usedRds.get(0);
1370 if (optVpnName.isPresent()) {
1371 tx.delete(BaseVrfEntryHandler.getVpnToExtrarouteIdentifier(vpnName, usedRd,
1372 vrfEntry.getDestPrefix()));
1373 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, configTx ->
1374 configTx.delete(VpnExtraRouteHelper.getUsedRdsIdentifier(vpnId, vrfEntry.getDestPrefix())));
1377 handleAdjacencyAndVpnOpInterfaceDeletion(vrfEntry, ifName, vpnName, tx);
1383 * Check all the adjacency in VpnInterfaceOpData and decide whether to delete the entire interface or only adj.
1384 * Remove Adjacency from VPNInterfaceOpData.
1385 * if Adjacency != primary.
1386 * if Adjacency == primary , then mark it for deletion.
1387 * Remove entire VPNinterfaceOpData Entry.
1388 * if sie of Adjacency <= 2 and all are marked for deletion , delete the entire VPNinterface Op entry.
1389 * @param vrfEntry - VrfEntry removed
1390 * @param ifName - Interface name from VRFentry
1391 * @param vpnName - VPN name of corresponding VRF
1392 * @param tx - ReadWrite Tx
1394 private void handleAdjacencyAndVpnOpInterfaceDeletion(VrfEntry vrfEntry, String ifName, String vpnName,
1395 TypedReadWriteTransaction<Operational> tx)
1396 throws ExecutionException, InterruptedException {
1397 InstanceIdentifier<Adjacency> adjacencyIid =
1398 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix());
1399 Optional<Adjacency> adjacencyOptional = tx.read(adjacencyIid).get();
1400 if (adjacencyOptional.isPresent()) {
1401 if (adjacencyOptional.get().getAdjacencyType() != Adjacency.AdjacencyType.PrimaryAdjacency) {
1402 tx.delete(FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix()));
1404 tx.merge(adjacencyIid,
1405 new AdjacencyBuilder(adjacencyOptional.get()).setMarkedForDeletion(true).build());
1409 Optional<AdjacenciesOp> optAdjacencies = tx.read(FibUtil.getAdjListPathOp(ifName, vpnName)).get();
1411 if (!optAdjacencies.isPresent() || optAdjacencies.get().getAdjacency() == null) {
1415 if (optAdjacencies.get().getAdjacency().stream().count() <= 2
1416 && optAdjacencies.get().getAdjacency().stream().allMatch(adjacency ->
1417 adjacency.getAdjacencyType() == Adjacency.AdjacencyType.PrimaryAdjacency
1418 && adjacency.isMarkedForDeletion() != null
1419 && adjacency.isMarkedForDeletion()
1421 LOG.info("Clean up vpn interface {} to vpn {} list.", ifName, vpnName);
1422 tx.delete(FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1426 private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
1427 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
1428 final String rd = vrfTableKey.getRouteDistinguisher();
1429 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
1430 if (vpnInstance == null) {
1431 LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
1434 final Collection<VpnToDpnList> vpnToDpnList;
1435 if (vrfEntry.getParentVpnRd() != null
1436 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
1437 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
1438 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
1439 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
1440 vpnInstance.getVpnToDpnList();
1441 LOG.info("deleteFibEntries: Processing deletion of PNF FIB entry with rd {} prefix {}",
1442 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
1444 vpnToDpnList = vpnInstance.getVpnToDpnList();
1447 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1448 final java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1449 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1450 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1451 if (subnetRoute != null) {
1452 long elanTag = subnetRoute.getElantag();
1453 LOG.trace("SUBNETROUTE: deleteFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
1454 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
1455 if (vpnToDpnList != null) {
1456 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1457 () -> Collections.singletonList(
1458 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1459 for (final VpnToDpnList curDpn : vpnToDpnList) {
1461 baseVrfEntryHandler.makeConnectedRoute(curDpn.getDpnId(), vpnInstance.getVpnId(),
1462 vrfEntry, vrfTableKey.getRouteDistinguisher(), null, NwConstants.DEL_FLOW,
1463 TransactionAdapter.toWriteTransaction(tx), null);
1464 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1465 optionalLabel.ifPresent(label -> makeLFibTableEntry(curDpn.getDpnId(), label, null,
1466 DEFAULT_FIB_FLOW_PRIORITY, NwConstants.DEL_FLOW, tx));
1469 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnInstance.getVpnId(),
1470 vrfEntry, NwConstants.DEL_FLOW, tx);
1474 optionalLabel.ifPresent(label -> {
1475 synchronized (label.toString().intern()) {
1476 LabelRouteInfo lri = getLabelRouteInfo(label);
1477 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1478 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1479 fibUtil.getVpnInstanceOpData(rd);
1480 String vpnInstanceName = "";
1481 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1482 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1484 boolean lriRemoved = this.deleteLabelRouteInfo(lri, vpnInstanceName, null);
1486 String parentRd = lri.getParentVpnRd();
1487 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1488 parentRd, vrfEntry.getDestPrefix()));
1489 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}"
1490 + " as labelRouteInfo cleared", label, rd, vrfEntry.getDestPrefix());
1493 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1494 rd, vrfEntry.getDestPrefix()));
1495 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}",
1496 label, rd, vrfEntry.getDestPrefix());
1503 final List<BigInteger> localDpnIdList = deleteLocalFibEntry(vpnInstance.getVpnId(),
1504 vrfTableKey.getRouteDistinguisher(), vrfEntry);
1505 if (vpnToDpnList != null) {
1506 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1507 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1509 Optional<Routes> extraRouteOptional;
1510 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
1511 if (usedRds != null && !usedRds.isEmpty()) {
1512 if (usedRds.size() > 1) {
1513 LOG.error("The extra route prefix is still present in some DPNs");
1516 // The first rd is retrieved from usedrds as Only 1 rd would be present as extra route prefix
1517 //is not present in any other DPN
1518 extraRouteOptional = VpnExtraRouteHelper
1519 .getVpnExtraroutes(dataBroker, vpnName, usedRds.get(0), vrfEntry.getDestPrefix());
1522 extraRouteOptional = Optional.absent();
1525 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1526 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1527 if (localDpnIdList.size() <= 0) {
1528 for (VpnToDpnList curDpn : vpnToDpnList) {
1529 baseVrfEntryHandler.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
1530 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
1531 TransactionAdapter.toWriteTransaction(tx));
1534 for (BigInteger localDpnId : localDpnIdList) {
1535 for (VpnToDpnList curDpn : vpnToDpnList) {
1536 if (!curDpn.getDpnId().equals(localDpnId)) {
1537 baseVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
1538 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
1539 TransactionAdapter.toWriteTransaction(tx));
1544 nextHopManager.removeNextHopPointer(nextHopManager
1545 .getRemoteSelectGroupKey(vpnInstance.getVpnId(), vrfEntry.getDestPrefix()));
1546 nextHopManager.removeNextHopPointer(nextHopManager
1547 .getLocalSelectGroupKey(vpnInstance.getVpnId(), vrfEntry.getDestPrefix()));
1551 //The flow/group entry has been deleted from config DS; need to clean up associated operational
1552 //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
1553 cleanUpOpDataForFib(vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(), vrfEntry);
1555 // Remove all fib entries configured due to interVpnLink, when nexthop is the opposite endPoint
1556 // of the interVpnLink.
1557 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
1558 if (optVpnUuid.isPresent()) {
1559 String vpnUuid = optVpnUuid.get();
1560 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
1561 Optional<InterVpnLinkDataComposite> optInterVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid);
1562 if (optInterVpnLink.isPresent()) {
1563 InterVpnLinkDataComposite interVpnLink = optInterVpnLink.get();
1564 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
1565 // This is route that points to the other endpoint of an InterVpnLink
1566 // In that case, we should look for the FIB table pointing to
1567 // LPortDispatcher table and remove it.
1568 removeInterVPNLinkRouteFlows(interVpnLink, vpnUuid, vrfEntry);
1576 private void makeLFibTableEntry(BigInteger dpId, long label, List<InstructionInfo> instructions, int priority,
1577 int addOrRemove, TypedWriteTransaction<Configuration> tx) {
1579 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1580 newTx -> makeLFibTableEntry(dpId, label, instructions, priority, addOrRemove, newTx)), LOG,
1581 "Error making LFIB table entry");
1585 List<MatchInfo> matches = new ArrayList<>();
1586 matches.add(MatchEthernetType.MPLS_UNICAST);
1587 matches.add(new MatchMplsLabel(label));
1589 // Install the flow entry in L3_LFIB_TABLE
1590 String flowRef = FibUtil.getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, label, priority);
1592 FlowEntity flowEntity;
1593 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef, priority, flowRef, 0, 0,
1594 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
1595 Flow flow = flowEntity.getFlowBuilder().build();
1596 String flowId = flowEntity.getFlowId();
1597 FlowKey flowKey = new FlowKey(new FlowId(flowId));
1598 Node nodeDpn = FibUtil.buildDpnNode(dpId);
1599 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1600 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1601 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
1603 if (addOrRemove == NwConstants.ADD_FLOW) {
1604 tx.put(flowInstanceId, flow, CREATE_MISSING_PARENTS);
1606 tx.delete(flowInstanceId);
1609 LOG.debug("LFIB Entry for dpID {} : label : {} instructions {} : key {} {} successfully",
1610 dpId, label, instructions, flowKey, NwConstants.ADD_FLOW == addOrRemove ? "ADDED" : "REMOVED");
1613 public void populateFibOnNewDpn(final BigInteger dpnId, final long vpnId, final String rd,
1614 final FutureCallback<List<Void>> callback) {
1615 LOG.trace("New dpn {} for vpn {} : populateFibOnNewDpn", dpnId, rd);
1616 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1618 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1619 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1620 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1621 LogicalDatastoreType.CONFIGURATION, id);
1622 List<ListenableFuture<Void>> futures = new ArrayList<>();
1623 if (!vrfTable.isPresent()) {
1624 LOG.info("populateFibOnNewDpn: dpn: {}: VRF Table not yet available for RD {}", dpnId, rd);
1625 if (callback != null) {
1626 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1627 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1631 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1632 futures.add(retryingTxRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
1633 for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
1634 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1635 if (subnetRoute != null) {
1636 long elanTag = subnetRoute.getElantag();
1637 installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, tx);
1638 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry, NwConstants.ADD_FLOW,
1642 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1643 if (routerInt != null) {
1644 LOG.trace("Router augmented vrfentry found rd:{}, uuid:{}, ip:{}, mac:{}",
1645 rd, routerInt.getUuid(), routerInt.getIpAddress(), routerInt.getMacAddress());
1646 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1647 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1648 NwConstants.ADD_FLOW);
1651 //Handle local flow creation for imports
1652 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1653 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1654 if (optionalLabel.isPresent()) {
1655 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1656 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1657 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList, lri)) {
1658 if (lri.getDpnId().equals(dpnId)) {
1660 int etherType = NWUtil.getEtherTypeFromIpPrefix(
1661 vrfEntry.getDestPrefix());
1662 createLocalFibEntry(vpnId, rd, vrfEntry, etherType);
1663 } catch (IllegalArgumentException ex) {
1664 LOG.warn("Unable to get etherType for IP Prefix {}",
1665 vrfEntry.getDestPrefix());
1672 boolean shouldCreateRemoteFibEntry = shouldCreateFibEntryForVrfAndVpnIdOnDpn(vpnId,
1674 if (shouldCreateRemoteFibEntry) {
1675 LOG.trace("Will create remote FIB entry for vrfEntry {} on DPN {}", vrfEntry, dpnId);
1676 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1677 List<SubTransaction> txnObjects = new ArrayList<>();
1678 bgpRouteVrfEntryHandler.createRemoteFibEntry(dpnId, vpnId,
1679 vrfTable.get().getRouteDistinguisher(), vrfEntry,
1680 TransactionAdapter.toWriteTransaction(tx), txnObjects);
1682 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
1688 if (callback != null) {
1689 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1690 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1697 public void populateExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1698 final String localNextHopIp, final String remoteNextHopIp) {
1699 LOG.trace("populateExternalRoutesOnDpn : dpn {}, vpn {}, rd {}, localNexthopIp {} , remoteNextHopIp {} ",
1700 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1701 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1702 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1703 List<SubTransaction> txnObjects = new ArrayList<>();
1704 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1705 if (vrfTable.isPresent()) {
1706 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1707 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1708 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1709 vrfTable.get().getVrfEntry().stream()
1710 .filter(vrfEntry -> RouteOrigin.BGP == RouteOrigin.value(vrfEntry.getOrigin()))
1711 .forEach(bgpRouteVrfEntryHandler.getConsumerForCreatingRemoteFib(dpnId, vpnId,
1712 rd, remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx), txnObjects));
1718 public void manageRemoteRouteOnDPN(final boolean action,
1719 final BigInteger localDpnId,
1722 final String destPrefix,
1723 final String destTepIp,
1725 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1727 if (vpnInstance == null) {
1728 LOG.error("VpnInstance for rd {} not present for prefix {}", rd, destPrefix);
1732 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, localDpnId),
1733 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1734 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1735 VrfTablesKey vrfTablesKey = new VrfTablesKey(rd);
1736 VrfEntry vrfEntry = getVrfEntry(dataBroker, rd, destPrefix);
1737 if (vrfEntry == null) {
1740 LOG.trace("manageRemoteRouteOnDPN :: action {}, DpnId {}, vpnId {}, rd {}, destPfx {}",
1741 action, localDpnId, vpnId, rd, destPrefix);
1742 List<RoutePaths> routePathList = vrfEntry.getRoutePaths();
1743 VrfEntry modVrfEntry;
1744 if (routePathList == null || routePathList.isEmpty()) {
1745 modVrfEntry = FibHelper.getVrfEntryBuilder(vrfEntry, label,
1746 Collections.singletonList(destTepIp),
1747 RouteOrigin.value(vrfEntry.getOrigin()), null /* parentVpnRd */).build();
1749 modVrfEntry = vrfEntry;
1753 LOG.trace("manageRemoteRouteOnDPN updated(add) vrfEntry :: {}", modVrfEntry);
1754 createRemoteFibEntry(localDpnId, vpnId, vrfTablesKey.getRouteDistinguisher(),
1757 LOG.trace("manageRemoteRouteOnDPN updated(remove) vrfEntry :: {}", modVrfEntry);
1758 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnInstance.getVpnId(),
1759 vrfEntry.getDestPrefix());
1760 if (usedRds.size() > 1) {
1761 LOG.debug("The extra route prefix is still present in some DPNs");
1764 //Is this fib route an extra route? If yes, get the nexthop which would be
1765 //an adjacency in the vpn
1766 Optional<Routes> extraRouteOptional = Optional.absent();
1767 if (usedRds.size() != 0) {
1768 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1769 fibUtil.getVpnNameFromId(vpnInstance.getVpnId()),
1770 usedRds.get(0), vrfEntry.getDestPrefix());
1772 baseVrfEntryHandler.deleteRemoteRoute(null, localDpnId, vpnId, vrfTablesKey, modVrfEntry,
1773 extraRouteOptional, TransactionAdapter.toWriteTransaction(tx));
1779 public void cleanUpDpnForVpn(final BigInteger dpnId, final long vpnId, final String rd,
1780 final FutureCallback<List<Void>> callback) {
1781 LOG.trace("cleanUpDpnForVpn: Remove dpn {} for vpn {} : cleanUpDpnForVpn", dpnId, rd);
1782 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1784 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1785 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1786 List<SubTransaction> txnObjects = new ArrayList<>();
1787 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1788 LogicalDatastoreType.CONFIGURATION, id);
1789 List<ListenableFuture<Void>> futures = new ArrayList<>();
1790 if (vrfTable.isPresent()) {
1791 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1792 futures.add(retryingTxRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1793 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1794 for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
1795 /* Handle subnet routes here */
1796 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1797 if (subnetRoute != null && !fibUtil
1798 .isInterfacePresentInDpn(vrfEntry.getParentVpnRd(), dpnId)) {
1799 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Cleaning subnetroute {} on dpn {}"
1800 + " for vpn {}", vrfEntry.getDestPrefix(), dpnId, rd);
1801 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1802 NwConstants.DEL_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1803 List<RoutePaths> routePaths = vrfEntry.getRoutePaths();
1804 if (routePaths != null) {
1805 for (RoutePaths routePath : routePaths) {
1806 makeLFibTableEntry(dpnId, routePath.getLabel(), null,
1807 DEFAULT_FIB_FLOW_PRIORITY,
1808 NwConstants.DEL_FLOW, tx);
1809 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Released subnetroute label {}"
1810 + " for rd {} prefix {}", routePath.getLabel(), rd,
1811 vrfEntry.getDestPrefix());
1814 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry,
1815 NwConstants.DEL_FLOW, tx);
1818 // ping responder for router interfaces
1819 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1820 if (routerInt != null) {
1821 LOG.trace("Router augmented vrfentry found for rd:{}, uuid:{}, ip:{}, mac:{}",
1822 rd, routerInt.getUuid(), routerInt.getIpAddress(),
1823 routerInt.getMacAddress());
1824 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1825 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1826 NwConstants.DEL_FLOW);
1830 //Handle local flow deletion for imports
1831 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1832 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1833 if (optionalLabel.isPresent()) {
1834 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1835 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1836 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList,
1837 lri) && lri.getDpnId().equals(dpnId)) {
1838 deleteLocalFibEntry(vpnId, rd, vrfEntry);
1843 // Passing null as we don't know the dpn
1844 // to which prefix is attached at this point
1845 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1846 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1847 Optional<Routes> extraRouteOptional;
1848 //Is this fib route an extra route? If yes, get the nexthop which would be
1849 //an adjacency in the vpn
1850 if (usedRds != null && !usedRds.isEmpty()) {
1851 if (usedRds.size() > 1) {
1852 LOG.error("The extra route prefix is still present in some DPNs");
1855 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
1856 usedRds.get(0), vrfEntry.getDestPrefix());
1860 extraRouteOptional = Optional.absent();
1862 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1863 bgpRouteVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1864 vrfTable.get().key(), vrfEntry, extraRouteOptional,
1865 TransactionAdapter.toWriteTransaction(tx), txnObjects);
1867 if (subnetRoute == null || !fibUtil
1868 .isInterfacePresentInDpn(vrfEntry.getParentVpnRd(), dpnId)) {
1869 baseVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1870 vrfTable.get().key(), vrfEntry, extraRouteOptional,
1871 TransactionAdapter.toWriteTransaction(tx));
1877 if (callback != null) {
1878 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1879 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1882 LOG.error("cleanUpDpnForVpn: No vrf table found for rd {} vpnId {} dpn {}", rd, vpnId, dpnId);
1889 public void cleanUpExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1890 final String localNextHopIp, final String remoteNextHopIp) {
1891 LOG.trace("cleanUpExternalRoutesOnDpn : cleanup remote routes on dpn {} for vpn {}, rd {}, "
1892 + " localNexthopIp {} , remoteNexhtHopIp {}",
1893 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1894 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1895 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1896 List<SubTransaction> txnObjects = new ArrayList<>();
1897 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1898 if (vrfTable.isPresent()) {
1899 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1901 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1902 return Collections.singletonList(
1903 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1904 tx -> vrfTable.get().getVrfEntry().stream()
1905 .filter(vrfEntry -> RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP)
1906 .forEach(bgpRouteVrfEntryHandler.getConsumerForDeletingRemoteFib(dpnId, vpnId,
1907 remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx),
1914 public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
1915 InstanceIdentifierBuilder<VrfTables> idBuilder =
1916 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
1917 return idBuilder.build();
1920 private String getInterVpnFibFlowRef(String interVpnLinkName, String prefix, String nextHop) {
1921 return FLOWID_PREFIX + interVpnLinkName + NwConstants.FLOWID_SEPARATOR + prefix + NwConstants
1922 .FLOWID_SEPARATOR + nextHop;
1925 private String getTableMissFlowRef(BigInteger dpnId, short tableId, int tableMiss) {
1926 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR
1927 + tableMiss + FLOWID_PREFIX;
1930 private VrfEntry getVrfEntry(DataBroker broker, String rd, String ipPrefix) {
1931 InstanceIdentifier<VrfEntry> vrfEntryId = InstanceIdentifier.builder(FibEntries.class)
1932 .child(VrfTables.class, new VrfTablesKey(rd))
1933 .child(VrfEntry.class, new VrfEntryKey(ipPrefix)).build();
1934 Optional<VrfEntry> vrfEntry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
1935 if (vrfEntry.isPresent()) {
1936 return vrfEntry.get();
1941 public void removeInterVPNLinkRouteFlows(final InterVpnLinkDataComposite interVpnLink,
1942 final String vpnName,
1943 final VrfEntry vrfEntry) {
1944 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null && vrfEntry.getRoutePaths().size() == 1);
1946 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
1947 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
1949 if (targetDpns.isEmpty()) {
1950 LOG.warn("Could not find DPNs for VPN {} in InterVpnLink {}", vpnName, interVpnLinkName);
1954 java.util.Optional<String> optNextHop = FibUtil.getFirstNextHopAddress(vrfEntry);
1955 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1959 optNextHop.ifPresent(nextHop -> {
1960 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, vrfEntry.getDestPrefix(), nextHop);
1961 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
1962 Flow flow = new FlowBuilder().withKey(flowKey).setId(new FlowId(flowRef))
1963 .setTableId(NwConstants.L3_FIB_TABLE).setFlowName(flowRef).build();
1965 LOG.trace("Removing flow in FIB table for interVpnLink {} key {}", interVpnLinkName, flowRef);
1966 for (BigInteger dpId : targetDpns) {
1967 LOG.debug("Removing flow: VrfEntry=[prefix={} nexthop={}] dpn {} for InterVpnLink {} in FIB",
1968 vrfEntry.getDestPrefix(), nextHop, dpId, interVpnLinkName);
1970 mdsalManager.removeFlow(dpId, flow);
1976 optLabel.ifPresent(label -> {
1977 LOG.trace("Removing flow in FIB table for interVpnLink {}", interVpnLinkName);
1979 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1980 for (BigInteger dpId : targetDpns) {
1981 LOG.debug("Removing flow: VrfEntry=[prefix={} label={}] dpn {} for InterVpnLink {} in LFIB",
1982 vrfEntry.getDestPrefix(), label, dpId, interVpnLinkName);
1983 makeLFibTableEntry(dpId, label, /*instructions*/null, LFIB_INTERVPN_PRIORITY, NwConstants.DEL_FLOW,
1986 }), LOG, "Error removing flows");
1990 private boolean isPrefixAndNextHopPresentInLri(String prefix,
1991 List<String> nextHopAddressList, LabelRouteInfo lri) {
1992 return lri != null && lri.getPrefix().equals(prefix)
1993 && nextHopAddressList.contains(lri.getNextHopIpList().get(0));
1996 private boolean shouldCreateFibEntryForVrfAndVpnIdOnDpn(Long vpnId, VrfEntry vrfEntry, BigInteger dpnId) {
1997 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
2001 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
2002 if (prefix != null) {
2003 BigInteger prefixDpnId = prefix.getDpnId();
2004 if (dpnId.equals(prefixDpnId)) {
2005 LOG.trace("Should not create remote FIB entry for vrfEntry {} on DPN {}",