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.UpgradeState;
57 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
58 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
59 import org.opendaylight.genius.mdsalutil.actions.ActionPopMpls;
60 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
61 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
62 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
63 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
64 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
65 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
66 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
67 import org.opendaylight.genius.mdsalutil.matches.MatchMplsLabel;
68 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
69 import org.opendaylight.genius.utils.ServiceIndex;
70 import org.opendaylight.genius.utils.batching.SubTransaction;
71 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
72 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
73 import org.opendaylight.netvirt.elanmanager.api.IElanService;
74 import org.opendaylight.netvirt.fibmanager.NexthopManager.AdjacencyResult;
75 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
76 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
77 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
78 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
79 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
80 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
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 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, localNextHopIP);
755 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
756 vpnName, usedRds, localNextHopIP);
757 if (LOG.isDebugEnabled()) {
758 LOG.debug("Creating Local fib entry with vpnName {} usedRds {} localNextHopIP {} vpnExtraRoutes {}",
759 vpnName, usedRds, localNextHopIP, vpnExtraRoutes);
761 boolean localNextHopSeen = false;
762 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
763 for (Routes vpnExtraRoute : vpnExtraRoutes) {
765 if (isIpv4Address(vpnExtraRoute.getNexthopIpList().get(0))) {
766 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
768 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
770 Prefixes localNextHopInfoLocal = fibUtil.getPrefixToInterface(vpnId,
772 if (localNextHopInfoLocal != null) {
773 localNextHopSeen = true;
775 checkCreateLocalFibEntry(localNextHopInfoLocal, localNextHopInfoLocal.getIpAddress(),
776 vpnId, rd, vrfEntry, vpnExtraRoute, vpnExtraRoutes, etherType);
777 returnLocalDpnId.add(dpnId);
780 if (!localNextHopSeen && RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
781 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
782 if (optionalLabel.isPresent()) {
783 Long label = optionalLabel.get();
784 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
785 synchronized (label.toString().intern()) {
786 LabelRouteInfo lri = getLabelRouteInfo(label);
787 if (isPrefixAndNextHopPresentInLri(localNextHopIP, nextHopAddressList, lri)) {
788 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
789 fibUtil.getVpnInstanceOpData(rd);
790 if (vpnInstanceOpDataEntryOptional.isPresent()) {
791 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
792 if (lri.getVpnInstanceList().contains(vpnInstanceName)) {
793 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, true);
794 localNextHopIP = lri.getPrefix();
796 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, false);
797 localNextHopIP = lri.getPrefix();
800 if (localNextHopInfo != null) {
801 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
802 label, localNextHopInfo.getVpnInterfaceName(), lri.getDpnId());
803 if (vpnExtraRoutes.isEmpty()) {
804 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
805 vpnId, rd, vrfEntry, null, vpnExtraRoutes, etherType);
806 returnLocalDpnId.add(dpnId);
808 for (Routes extraRoutes : vpnExtraRoutes) {
809 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
810 vpnId, rd, vrfEntry, extraRoutes, vpnExtraRoutes, etherType);
811 returnLocalDpnId.add(dpnId);
819 if (returnLocalDpnId.isEmpty()) {
820 LOG.error("Local DPNID is empty for rd {}, vpnId {}, vrfEntry {}", rd, vpnId, vrfEntry);
823 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP, vpnId,
824 rd, vrfEntry, /*routes*/ null, /*vpnExtraRoutes*/ null, etherType);
826 returnLocalDpnId.add(dpnId);
829 return returnLocalDpnId;
832 private BigInteger checkCreateLocalFibEntry(Prefixes localNextHopInfo, String localNextHopIP,
833 final Long vpnId, final String rd,
834 final VrfEntry vrfEntry,
835 Routes routes, List<Routes> vpnExtraRoutes,
837 String vpnName = fibUtil.getVpnNameFromId(vpnId);
838 if (localNextHopInfo != null) {
841 final BigInteger dpnId = localNextHopInfo.getDpnId();
842 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
843 LOG.debug("checkCreateLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
844 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
847 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
848 LOG.debug("checkCreateLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
849 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
852 if (!isVpnPresentInDpn(rd, dpnId)) {
853 LOG.error("checkCreateLocalFibEntry: The VPN with id {} rd {} is not available on dpn {}",
854 vpnId, rd, dpnId.toString());
855 return BigInteger.ZERO;
857 String interfaceName = localNextHopInfo.getVpnInterfaceName();
858 String prefix = vrfEntry.getDestPrefix();
859 String gwMacAddress = vrfEntry.getGatewayMacAddress();
860 //The loadbalancing group is created only if the extra route has multiple nexthops
861 //to avoid loadbalancing the discovered routes
862 if (vpnExtraRoutes != null && routes != null) {
863 if (isIpv4Address(routes.getNexthopIpList().get(0))) {
864 localNextHopIP = routes.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
866 localNextHopIP = routes.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
868 if (vpnExtraRoutes.size() > 1) {
869 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
870 localGroupId = nextHopManager.getLocalSelectGroup(vpnId, vrfEntry.getDestPrefix());
871 } else if (routes.getNexthopIpList().size() > 1) {
872 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
873 localGroupId = groupId;
875 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP,
876 prefix, gwMacAddress);
877 localGroupId = groupId;
880 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP, prefix,
882 localGroupId = groupId;
884 if (groupId == FibConstants.INVALID_GROUP_ID) {
885 LOG.error("Unable to create Group for local prefix {} on rd {} for vpninterface {} on Node {}",
886 prefix, rd, interfaceName, dpnId.toString());
887 return BigInteger.ZERO;
889 final List<InstructionInfo> instructions = Collections.singletonList(
890 new InstructionApplyActions(
891 Collections.singletonList(new ActionGroup(groupId))));
892 final List<InstructionInfo> lfibinstructions = Collections.singletonList(
893 new InstructionApplyActions(
894 Arrays.asList(new ActionPopMpls(etherType), new ActionGroup(localGroupId))));
895 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
896 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
897 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
898 jobCoordinator.enqueueJob(jobKey,
899 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
900 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
901 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
902 if (FibUtil.isBgpVpn(vpnName, rd)) {
903 optLabel.ifPresent(label -> {
904 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
906 "Installing LFIB and tunnel table entry on dpn {} for interface {} with label "
907 + "{}, rd {}, prefix {}, nexthop {}", dpnId,
908 localNextHopInfo.getVpnInterfaceName(), optLabel, rd, vrfEntry.getDestPrefix(),
910 makeLFibTableEntry(dpnId, label, lfibinstructions, DEFAULT_FIB_FLOW_PRIORITY,
911 NwConstants.ADD_FLOW, tx);
912 makeTunnelTableEntry(dpnId, label, localGroupId, tx);
914 LOG.debug("Route with rd {} prefix {} label {} nexthop {} for vpn {} is an imported "
915 + "route. LFib and Terminating table entries will not be created.",
916 rd, vrfEntry.getDestPrefix(), optLabel, nextHopAddressList, vpnId);
923 LOG.error("localNextHopInfo received is null for prefix {} on rd {} on vpn {}", vrfEntry.getDestPrefix(), rd,
925 return BigInteger.ZERO;
928 private boolean isVpnPresentInDpn(String rd, BigInteger dpnId) {
929 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
930 Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
931 if (dpnInVpn.isPresent()) {
937 private LabelRouteInfo getLabelRouteInfo(Long label) {
938 InstanceIdentifier<LabelRouteInfo> lriIid = InstanceIdentifier.builder(LabelRouteMap.class)
939 .child(LabelRouteInfo.class, new LabelRouteInfoKey(label)).build();
940 Optional<LabelRouteInfo> opResult = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, lriIid);
941 if (opResult.isPresent()) {
942 return opResult.get();
947 private boolean deleteLabelRouteInfo(LabelRouteInfo lri, String vpnInstanceName,
948 TypedWriteTransaction<Operational> tx) {
953 LOG.debug("deleting LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
954 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
955 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
957 List<String> vpnInstancesList = lri.getVpnInstanceList() != null
958 ? lri.getVpnInstanceList() : new ArrayList<>();
959 if (vpnInstancesList.contains(vpnInstanceName)) {
960 LOG.debug("vpninstance {} name is present", vpnInstanceName);
961 vpnInstancesList.remove(vpnInstanceName);
963 if (vpnInstancesList.isEmpty()) {
964 LOG.debug("deleting LRI instance object for label {}", lri.getLabel());
968 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId);
972 LOG.debug("updating LRI instance object for label {}", lri.getLabel());
973 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri).setVpnInstanceList(vpnInstancesList);
974 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
979 void makeTunnelTableEntry(BigInteger dpId, long label, long groupId/*String egressInterfaceName*/,
980 TypedWriteTransaction<Configuration> tx) {
981 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionGroup(groupId));
983 createTerminatingServiceActions(dpId, (int) label, actionsInfos, tx);
985 LOG.debug("Terminating service Entry for dpID {} : label : {} egress : {} installed successfully",
986 dpId, label, groupId);
989 public void createTerminatingServiceActions(BigInteger destDpId, int label, List<ActionInfo> actionsInfos,
990 TypedWriteTransaction<Configuration> tx) {
991 List<MatchInfo> mkMatches = new ArrayList<>();
993 LOG.debug("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}",
994 destDpId, label, actionsInfos);
997 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
998 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
1000 List<InstructionInfo> mkInstructions = new ArrayList<>();
1001 mkInstructions.add(new InstructionApplyActions(actionsInfos));
1003 FlowEntity terminatingServiceTableFlowEntity =
1004 MDSALUtil.buildFlowEntity(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,
1005 getTableMissFlowRef(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE, label), 5,
1006 String.format("%s:%d", "TST Flow Entry ", label),
1007 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, mkInstructions);
1009 FlowKey flowKey = new FlowKey(new FlowId(terminatingServiceTableFlowEntity.getFlowId()));
1011 FlowBuilder flowbld = terminatingServiceTableFlowEntity.getFlowBuilder();
1013 Node nodeDpn = FibUtil.buildDpnNode(terminatingServiceTableFlowEntity.getDpnId());
1014 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1015 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1016 .child(Table.class, new TableKey(terminatingServiceTableFlowEntity.getTableId()))
1017 .child(Flow.class, flowKey).build();
1018 tx.put(flowInstanceId, flowbld.build(), CREATE_MISSING_PARENTS);
1021 private void removeTunnelTableEntry(BigInteger dpId, long label, TypedWriteTransaction<Configuration> tx) {
1022 FlowEntity flowEntity;
1023 LOG.debug("remove terminatingServiceActions called with DpnId = {} and label = {}", dpId, label);
1024 List<MatchInfo> mkMatches = new ArrayList<>();
1025 // Matching metadata
1026 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
1027 flowEntity = MDSALUtil.buildFlowEntity(dpId,
1028 NwConstants.INTERNAL_TUNNEL_TABLE,
1029 getTableMissFlowRef(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, (int) label),
1030 5, String.format("%s:%d", "TST Flow Entry ", label), 0, 0,
1031 COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, null);
1032 Node nodeDpn = FibUtil.buildDpnNode(flowEntity.getDpnId());
1033 FlowKey flowKey = new FlowKey(new FlowId(flowEntity.getFlowId()));
1034 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1035 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1036 .child(Table.class, new TableKey(flowEntity.getTableId())).child(Flow.class, flowKey).build();
1038 tx.delete(flowInstanceId);
1039 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully", dpId, label);
1042 public List<BigInteger> deleteLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
1043 List<BigInteger> returnLocalDpnId = new ArrayList<>();
1044 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1045 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1046 boolean shouldUpdateNonEcmpLocalNextHop = true;
1047 if (localNextHopInfo == null) {
1048 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1049 if (usedRds.size() > 1) {
1050 LOG.error("The extra route prefix {} is still present in some DPNs in vpn {} on rd {}",
1051 vrfEntry.getDestPrefix(), vpnName, rd);
1052 return returnLocalDpnId;
1054 String vpnRd = !usedRds.isEmpty() ? usedRds.get(0) : rd;
1055 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency
1057 Optional<Routes> extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1058 vpnName, vpnRd, vrfEntry.getDestPrefix());
1059 if (extraRouteOptional.isPresent()) {
1060 Routes extraRoute = extraRouteOptional.get();
1062 if (isIpv4Address(extraRoute.getNexthopIpList().get(0))) {
1063 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
1065 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
1067 if (extraRoute.getNexthopIpList().size() > 1) {
1068 shouldUpdateNonEcmpLocalNextHop = false;
1070 localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1071 if (localNextHopInfo != null) {
1072 String localNextHopIP = localNextHopInfo.getIpAddress();
1073 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP, vpnName, vpnId, rd,
1074 vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1075 if (!dpnId.equals(BigInteger.ZERO)) {
1076 LOG.trace("Deleting ECMP group for prefix {}, dpn {}", vrfEntry.getDestPrefix(), dpnId);
1077 nextHopManager.deleteLoadBalancingNextHop(vpnId, dpnId, vrfEntry.getDestPrefix());
1078 returnLocalDpnId.add(dpnId);
1081 LOG.error("localNextHopInfo unavailable while deleting prefix {} with rds {}, primary rd {} in "
1082 + "vpn {}", vrfEntry.getDestPrefix(), usedRds, rd, vpnName);
1086 if (localNextHopInfo == null) {
1087 /* Imported VRF entry */
1088 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1089 if (optionalLabel.isPresent()) {
1090 Long label = optionalLabel.get();
1091 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1092 LabelRouteInfo lri = getLabelRouteInfo(label);
1093 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1094 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1095 prefixBuilder.setDpnId(lri.getDpnId());
1096 BigInteger dpnId = checkDeleteLocalFibEntry(prefixBuilder.build(), nextHopAddressList.get(0),
1097 vpnName, vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1098 if (!dpnId.equals(BigInteger.ZERO)) {
1099 returnLocalDpnId.add(dpnId);
1106 LOG.debug("Obtained prefix to interface for rd {} prefix {}", rd, vrfEntry.getDestPrefix());
1107 String localNextHopIP = localNextHopInfo.getIpAddress();
1108 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP, vpnName, vpnId, rd, vrfEntry,
1109 shouldUpdateNonEcmpLocalNextHop);
1110 if (!dpnId.equals(BigInteger.ZERO)) {
1111 returnLocalDpnId.add(dpnId);
1115 return returnLocalDpnId;
1118 private BigInteger checkDeleteLocalFibEntry(Prefixes localNextHopInfo, final String localNextHopIP,
1119 final String vpnName, final Long vpnId, final String rd, final VrfEntry vrfEntry,
1120 boolean shouldUpdateNonEcmpLocalNextHop) {
1121 if (localNextHopInfo != null) {
1122 final BigInteger dpnId = localNextHopInfo.getDpnId();
1123 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
1124 LOG.debug("checkDeleteLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1125 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1128 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
1129 LOG.debug("checkDeleteLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1130 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1134 jobCoordinator.enqueueJob(FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix()),
1135 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1136 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1137 NwConstants.DEL_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1138 if (FibUtil.isBgpVpn(vpnName, rd)) {
1139 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1140 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1141 makeLFibTableEntry(dpnId, label, null /* instructions */, DEFAULT_FIB_FLOW_PRIORITY,
1142 NwConstants.DEL_FLOW, tx);
1143 removeTunnelTableEntry(dpnId, label, tx);
1148 //TODO: verify below adjacency call need to be optimized (?)
1149 //In case of the removal of the extra route, the loadbalancing group is updated
1150 if (shouldUpdateNonEcmpLocalNextHop) {
1151 baseVrfEntryHandler.deleteLocalAdjacency(dpnId, vpnId, localNextHopIP, vrfEntry.getDestPrefix());
1155 return BigInteger.ZERO;
1158 private void createRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, String rd,
1159 final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
1161 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1162 newTx -> createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx)), LOG,
1163 "Error creating remote FIB entry");
1167 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1168 LOG.debug("createremotefibentry: adding route {} for rd {} on remoteDpnId {}",
1169 vrfEntry.getDestPrefix(), rd, remoteDpnId);
1171 List<AdjacencyResult> adjacencyResults = baseVrfEntryHandler.resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
1172 if (adjacencyResults.isEmpty()) {
1173 LOG.error("Could not get interface for route-paths: {} in vpn {} on DPN {}",
1174 vrfEntry.getRoutePaths(), rd, remoteDpnId);
1175 LOG.error("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
1179 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1180 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
1181 vpnName, usedRds, vrfEntry.getDestPrefix());
1182 // create loadbalancing groups for extra routes only when the extra route is present behind
1184 if (!vpnExtraRoutes.isEmpty() && (vpnExtraRoutes.size() > 1
1185 || vpnExtraRoutes.get(0).getNexthopIpList().size() > 1)) {
1186 List<InstructionInfo> instructions = new ArrayList<>();
1187 // Obtain the local routes for this particular dpn.
1188 java.util.Optional<Routes> routes = vpnExtraRoutes
1191 Prefixes prefixToInterface = fibUtil.getPrefixToInterface(vpnId,
1192 fibUtil.getIpPrefix(route.getNexthopIpList().get(0)));
1193 if (prefixToInterface == null) {
1196 return remoteDpnId.equals(prefixToInterface.getDpnId());
1198 long groupId = nextHopManager.createNextHopGroups(vpnId, rd, remoteDpnId, vrfEntry,
1199 routes.isPresent() ? routes.get() : null, vpnExtraRoutes);
1200 if (groupId == FibConstants.INVALID_GROUP_ID) {
1201 LOG.error("Unable to create Group for local prefix {} on rd {} on Node {}",
1202 vrfEntry.getDestPrefix(), rd, remoteDpnId.toString());
1205 List<ActionInfo> actionInfos =
1206 Collections.singletonList(new ActionGroup(groupId));
1207 instructions.add(new InstructionApplyActions(actionInfos));
1208 baseVrfEntryHandler.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
1209 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1211 baseVrfEntryHandler.programRemoteFib(remoteDpnId, vpnId, vrfEntry,
1212 TransactionAdapter.toWriteTransaction(tx), rd, adjacencyResults, null);
1215 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
1218 protected void cleanUpOpDataForFib(Long vpnId, String primaryRd, final VrfEntry vrfEntry) {
1219 /* Get interface info from prefix to interface mapping;
1220 Use the interface info to get the corresponding vpn interface op DS entry,
1221 remove the adjacency corresponding to this fib entry.
1222 If adjacency removed is the last adjacency, clean up the following:
1223 - vpn interface from dpntovpn list, dpn if last vpn interface on dpn
1224 - prefix to interface entry
1225 - vpn interface op DS
1227 LOG.debug("Cleanup of prefix {} in VPN {}", vrfEntry.getDestPrefix(), vpnId);
1228 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1229 if (prefixInfo == null) {
1230 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1231 String usedRd = usedRds.isEmpty() ? primaryRd : usedRds.get(0);
1232 Routes extraRoute = baseVrfEntryHandler.getVpnToExtraroute(vpnId, usedRd, vrfEntry.getDestPrefix());
1233 if (extraRoute != null) {
1234 for (String nextHopIp : extraRoute.getNexthopIpList()) {
1235 LOG.debug("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
1236 if (nextHopIp != null) {
1238 if (isIpv4Address(nextHopIp)) {
1239 ipPrefix = nextHopIp + NwConstants.IPV4PREFIX;
1241 ipPrefix = nextHopIp + NwConstants.IPV6PREFIX;
1243 prefixInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1244 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1248 if (prefixInfo == null) {
1249 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1250 if (optionalLabel.isPresent()) {
1251 Long label = optionalLabel.get();
1252 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1253 LabelRouteInfo lri = getLabelRouteInfo(label);
1254 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1255 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1256 prefixBuilder.setDpnId(lri.getDpnId());
1257 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
1258 prefixBuilder.setIpAddress(lri.getPrefix());
1259 prefixInfo = prefixBuilder.build();
1260 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
1261 label, prefixInfo.getVpnInterfaceName(), lri.getDpnId());
1262 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1267 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, null /*Routes*/);
1271 private void checkCleanUpOpDataForFib(final Prefixes prefixInfo, final Long vpnId, final String rd,
1272 final VrfEntry vrfEntry, final Routes extraRoute) {
1274 if (prefixInfo == null) {
1275 LOG.error("Cleanup VPN Data Failed as unable to find prefix Info for prefix {} VpnId {} rd {}",
1276 vrfEntry.getDestPrefix(), vpnId, rd);
1277 return; //Don't have any info for this prefix (shouldn't happen); need to return
1280 if (Prefixes.PrefixCue.Nat.equals(prefixInfo.getPrefixCue())) {
1281 LOG.debug("NAT Prefix {} with vpnId {} rd {}. Skip FIB processing",
1282 vrfEntry.getDestPrefix(), vpnId, rd);
1286 String ifName = prefixInfo.getVpnInterfaceName();
1287 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName,
1288 new CleanupVpnInterfaceWorker(prefixInfo, vpnId, rd, vrfEntry, extraRoute));
1291 private class CleanupVpnInterfaceWorker implements Callable<List<ListenableFuture<Void>>> {
1292 Prefixes prefixInfo;
1298 CleanupVpnInterfaceWorker(final Prefixes prefixInfo, final Long vpnId, final String rd,
1299 final VrfEntry vrfEntry, final Routes extraRoute) {
1300 this.prefixInfo = prefixInfo;
1303 this.vrfEntry = vrfEntry;
1304 this.extraRoute = extraRoute;
1308 public List<ListenableFuture<Void>> call() {
1309 // If another renderer(for eg : CSS) needs to be supported, check can be performed here
1310 // to call the respective helpers.
1311 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
1312 //First Cleanup LabelRouteInfo
1313 //TODO(KIRAN) : Move the below block when addressing iRT/eRT for L3VPN Over VxLan
1314 LOG.debug("cleanupVpnInterfaceWorker: rd {} prefix {}", rd, prefixInfo.getIpAddress());
1315 if (VrfEntry.EncapType.Mplsgre.equals(vrfEntry.getEncapType())) {
1316 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1317 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1318 synchronized (label.toString().intern()) {
1319 LabelRouteInfo lri = getLabelRouteInfo(label);
1320 if (lri != null && lri.getPrefix().equals(vrfEntry.getDestPrefix())
1321 && nextHopAddressList.contains(lri.getNextHopIpList().get(0))) {
1322 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1323 fibUtil.getVpnInstanceOpData(rd);
1324 String vpnInstanceName = "";
1325 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1326 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1328 boolean lriRemoved = deleteLabelRouteInfo(lri, vpnInstanceName, tx);
1330 String parentRd = lri.getParentVpnRd();
1331 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1332 parentRd, vrfEntry.getDestPrefix()));
1335 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1336 rd, vrfEntry.getDestPrefix()));
1341 String ifName = prefixInfo.getVpnInterfaceName();
1342 Optional<String> optVpnName = fibUtil.getVpnNameFromRd(rd);
1343 String vpnName = null;
1345 if (Prefixes.PrefixCue.PhysNetFunc.equals(prefixInfo.getPrefixCue())) {
1346 // Get vpnId for rd = networkId since op vpnInterface will be pointing to rd = networkId
1347 Optional<String> vpnNameOpt = fibUtil.getVpnNameFromRd(vrfEntry.getParentVpnRd());
1348 if (vpnNameOpt.isPresent()) {
1349 vpnId = fibUtil.getVpnId(vpnNameOpt.get());
1352 if (optVpnName.isPresent()) {
1353 vpnName = optVpnName.get();
1354 Optional<VpnInterfaceOpDataEntry> opVpnInterface = tx
1355 .read(FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName)).get();
1356 if (opVpnInterface.isPresent()) {
1357 long associatedVpnId = fibUtil.getVpnId(vpnName);
1358 if (vpnId != associatedVpnId) {
1359 LOG.warn("Prefixes {} are associated with different vpn instance with id {} rather than {}",
1360 vrfEntry.getDestPrefix(), associatedVpnId, vpnId);
1361 LOG.warn("Not proceeding with Cleanup op data for prefix {}", vrfEntry.getDestPrefix());
1364 LOG.debug("Processing cleanup of prefix {} associated with vpn {}",
1365 vrfEntry.getDestPrefix(), associatedVpnId);
1369 if (extraRoute != null) {
1370 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1371 //Only one used Rd present in case of removal event
1372 String usedRd = usedRds.get(0);
1373 if (optVpnName.isPresent()) {
1374 tx.delete(BaseVrfEntryHandler.getVpnToExtrarouteIdentifier(vpnName, usedRd,
1375 vrfEntry.getDestPrefix()));
1376 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, configTx ->
1377 configTx.delete(VpnExtraRouteHelper.getUsedRdsIdentifier(vpnId, vrfEntry.getDestPrefix())));
1378 nextHopManager.removeNextHopPointer(nextHopManager
1379 .getRemoteSelectGroupKey(vpnId, vrfEntry.getDestPrefix()));
1380 nextHopManager.removeNextHopPointer(nextHopManager
1381 .getLocalSelectGroupKey(vpnId, vrfEntry.getDestPrefix()));
1384 handleAdjacencyAndVpnOpInterfaceDeletion(vrfEntry, ifName, vpnName, tx);
1390 * Check all the adjacency in VpnInterfaceOpData and decide whether to delete the entire interface or only adj.
1391 * Remove Adjacency from VPNInterfaceOpData.
1392 * if Adjacency != primary.
1393 * if Adjacency == primary , then mark it for deletion.
1394 * Remove entire VPNinterfaceOpData Entry.
1395 * if sie of Adjacency <= 2 and all are marked for deletion , delete the entire VPNinterface Op entry.
1396 * @param vrfEntry - VrfEntry removed
1397 * @param ifName - Interface name from VRFentry
1398 * @param vpnName - VPN name of corresponding VRF
1399 * @param tx - ReadWrite Tx
1401 private void handleAdjacencyAndVpnOpInterfaceDeletion(VrfEntry vrfEntry, String ifName, String vpnName,
1402 TypedReadWriteTransaction<Operational> tx)
1403 throws ExecutionException, InterruptedException {
1404 InstanceIdentifier<Adjacency> adjacencyIid =
1405 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix());
1406 Optional<Adjacency> adjacencyOptional = tx.read(adjacencyIid).get();
1407 if (adjacencyOptional.isPresent()) {
1408 if (adjacencyOptional.get().getAdjacencyType() != Adjacency.AdjacencyType.PrimaryAdjacency) {
1409 tx.delete(FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix()));
1411 tx.merge(adjacencyIid,
1412 new AdjacencyBuilder(adjacencyOptional.get()).setMarkedForDeletion(true).build());
1416 Optional<AdjacenciesOp> optAdjacencies = tx.read(FibUtil.getAdjListPathOp(ifName, vpnName)).get();
1418 if (!optAdjacencies.isPresent() || optAdjacencies.get().getAdjacency() == null) {
1422 if (optAdjacencies.get().getAdjacency().stream().count() <= 2
1423 && optAdjacencies.get().getAdjacency().stream().allMatch(adjacency ->
1424 adjacency.getAdjacencyType() == Adjacency.AdjacencyType.PrimaryAdjacency
1425 && adjacency.isMarkedForDeletion() != null
1426 && adjacency.isMarkedForDeletion()
1428 LOG.info("Clean up vpn interface {} to vpn {} list.", ifName, vpnName);
1429 tx.delete(FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1433 private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
1434 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
1435 final String rd = vrfTableKey.getRouteDistinguisher();
1436 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
1437 if (vpnInstance == null) {
1438 LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
1441 final Collection<VpnToDpnList> vpnToDpnList;
1442 if (vrfEntry.getParentVpnRd() != null
1443 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
1444 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
1445 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
1446 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
1447 vpnInstance.getVpnToDpnList();
1448 LOG.info("deleteFibEntries: Processing deletion of PNF FIB entry with rd {} prefix {}",
1449 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
1451 vpnToDpnList = vpnInstance.getVpnToDpnList();
1454 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1455 final java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1456 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1457 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1458 if (subnetRoute != null) {
1459 long elanTag = subnetRoute.getElantag();
1460 LOG.trace("SUBNETROUTE: deleteFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
1461 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
1462 if (vpnToDpnList != null) {
1463 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1464 () -> Collections.singletonList(
1465 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1466 for (final VpnToDpnList curDpn : vpnToDpnList) {
1468 baseVrfEntryHandler.makeConnectedRoute(curDpn.getDpnId(), vpnInstance.getVpnId(),
1469 vrfEntry, vrfTableKey.getRouteDistinguisher(), null, NwConstants.DEL_FLOW,
1470 TransactionAdapter.toWriteTransaction(tx), null);
1471 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1472 optionalLabel.ifPresent(label -> makeLFibTableEntry(curDpn.getDpnId(), label, null,
1473 DEFAULT_FIB_FLOW_PRIORITY, NwConstants.DEL_FLOW, tx));
1476 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnInstance.getVpnId(),
1477 vrfEntry, NwConstants.DEL_FLOW, tx);
1481 optionalLabel.ifPresent(label -> {
1482 synchronized (label.toString().intern()) {
1483 LabelRouteInfo lri = getLabelRouteInfo(label);
1484 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1485 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1486 fibUtil.getVpnInstanceOpData(rd);
1487 String vpnInstanceName = "";
1488 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1489 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1491 boolean lriRemoved = this.deleteLabelRouteInfo(lri, vpnInstanceName, null);
1493 String parentRd = lri.getParentVpnRd();
1494 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1495 parentRd, vrfEntry.getDestPrefix()));
1496 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}"
1497 + " as labelRouteInfo cleared", label, rd, vrfEntry.getDestPrefix());
1500 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1501 rd, vrfEntry.getDestPrefix()));
1502 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}",
1503 label, rd, vrfEntry.getDestPrefix());
1510 final List<BigInteger> localDpnIdList = deleteLocalFibEntry(vpnInstance.getVpnId(),
1511 vrfTableKey.getRouteDistinguisher(), vrfEntry);
1512 if (vpnToDpnList != null) {
1513 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1514 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1516 Optional<Routes> extraRouteOptional;
1517 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
1518 if (usedRds != null && !usedRds.isEmpty()) {
1519 jobKey = FibUtil.getJobKeyForRdPrefix(usedRds.get(0), vrfEntry.getDestPrefix());
1520 if (usedRds.size() > 1) {
1521 LOG.error("The extra route prefix is still present in some DPNs");
1524 // The first rd is retrieved from usedrds as Only 1 rd would be present as extra route prefix
1525 //is not present in any other DPN
1526 extraRouteOptional = VpnExtraRouteHelper
1527 .getVpnExtraroutes(dataBroker, vpnName, usedRds.get(0), vrfEntry.getDestPrefix());
1530 jobKey = FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix());
1531 extraRouteOptional = Optional.absent();
1534 jobCoordinator.enqueueJob(jobKey,
1535 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1536 if (localDpnIdList.size() <= 0) {
1537 for (VpnToDpnList curDpn : vpnToDpnList) {
1538 baseVrfEntryHandler.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
1539 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
1540 TransactionAdapter.toWriteTransaction(tx));
1543 for (BigInteger localDpnId : localDpnIdList) {
1544 for (VpnToDpnList curDpn : vpnToDpnList) {
1545 if (!curDpn.getDpnId().equals(localDpnId)) {
1546 baseVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
1547 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
1548 TransactionAdapter.toWriteTransaction(tx));
1556 //The flow/group entry has been deleted from config DS; need to clean up associated operational
1557 //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
1558 cleanUpOpDataForFib(vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(), vrfEntry);
1560 // Remove all fib entries configured due to interVpnLink, when nexthop is the opposite endPoint
1561 // of the interVpnLink.
1562 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
1563 if (optVpnUuid.isPresent()) {
1564 String vpnUuid = optVpnUuid.get();
1565 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
1566 Optional<InterVpnLinkDataComposite> optInterVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid);
1567 if (optInterVpnLink.isPresent()) {
1568 InterVpnLinkDataComposite interVpnLink = optInterVpnLink.get();
1569 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
1570 // This is route that points to the other endpoint of an InterVpnLink
1571 // In that case, we should look for the FIB table pointing to
1572 // LPortDispatcher table and remove it.
1573 removeInterVPNLinkRouteFlows(interVpnLink, vpnUuid, vrfEntry);
1581 private void makeLFibTableEntry(BigInteger dpId, long label, List<InstructionInfo> instructions, int priority,
1582 int addOrRemove, TypedWriteTransaction<Configuration> tx) {
1584 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1585 newTx -> makeLFibTableEntry(dpId, label, instructions, priority, addOrRemove, newTx)), LOG,
1586 "Error making LFIB table entry");
1590 List<MatchInfo> matches = new ArrayList<>();
1591 matches.add(MatchEthernetType.MPLS_UNICAST);
1592 matches.add(new MatchMplsLabel(label));
1594 // Install the flow entry in L3_LFIB_TABLE
1595 String flowRef = FibUtil.getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, label, priority);
1597 FlowEntity flowEntity;
1598 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef, priority, flowRef, 0, 0,
1599 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
1600 Flow flow = flowEntity.getFlowBuilder().build();
1601 String flowId = flowEntity.getFlowId();
1602 FlowKey flowKey = new FlowKey(new FlowId(flowId));
1603 Node nodeDpn = FibUtil.buildDpnNode(dpId);
1604 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1605 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1606 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
1608 if (addOrRemove == NwConstants.ADD_FLOW) {
1609 tx.put(flowInstanceId, flow, CREATE_MISSING_PARENTS);
1611 tx.delete(flowInstanceId);
1614 LOG.debug("LFIB Entry for dpID {} : label : {} instructions {} : key {} {} successfully",
1615 dpId, label, instructions, flowKey, NwConstants.ADD_FLOW == addOrRemove ? "ADDED" : "REMOVED");
1618 public void populateFibOnNewDpn(final BigInteger dpnId, final long vpnId, final String rd,
1619 final FutureCallback<List<Void>> callback) {
1620 LOG.trace("New dpn {} for vpn {} : populateFibOnNewDpn", dpnId, rd);
1621 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1623 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1624 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1625 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1626 LogicalDatastoreType.CONFIGURATION, id);
1627 List<ListenableFuture<Void>> futures = new ArrayList<>();
1628 if (!vrfTable.isPresent()) {
1629 LOG.info("populateFibOnNewDpn: dpn: {}: VRF Table not yet available for RD {}", dpnId, rd);
1630 if (callback != null) {
1631 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1632 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1636 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1637 futures.add(retryingTxRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
1638 for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
1639 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1640 if (subnetRoute != null) {
1641 long elanTag = subnetRoute.getElantag();
1642 installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, tx);
1643 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry, NwConstants.ADD_FLOW,
1647 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1648 if (routerInt != null) {
1649 LOG.trace("Router augmented vrfentry found rd:{}, uuid:{}, ip:{}, mac:{}",
1650 rd, routerInt.getUuid(), routerInt.getIpAddress(), routerInt.getMacAddress());
1651 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1652 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1653 NwConstants.ADD_FLOW);
1656 //Handle local flow creation for imports
1657 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1658 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1659 if (optionalLabel.isPresent()) {
1660 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1661 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1662 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList, lri)) {
1663 if (lri.getDpnId().equals(dpnId)) {
1665 int etherType = NWUtil.getEtherTypeFromIpPrefix(
1666 vrfEntry.getDestPrefix());
1667 createLocalFibEntry(vpnId, rd, vrfEntry, etherType);
1668 } catch (IllegalArgumentException ex) {
1669 LOG.warn("Unable to get etherType for IP Prefix {}",
1670 vrfEntry.getDestPrefix());
1677 boolean shouldCreateRemoteFibEntry = shouldCreateFibEntryForVrfAndVpnIdOnDpn(vpnId,
1679 if (shouldCreateRemoteFibEntry) {
1680 LOG.trace("Will create remote FIB entry for vrfEntry {} on DPN {}", vrfEntry, dpnId);
1681 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1682 List<SubTransaction> txnObjects = new ArrayList<>();
1683 bgpRouteVrfEntryHandler.createRemoteFibEntry(dpnId, vpnId,
1684 vrfTable.get().getRouteDistinguisher(), vrfEntry,
1685 TransactionAdapter.toWriteTransaction(tx), txnObjects);
1687 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
1693 if (callback != null) {
1694 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1695 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1702 public void populateExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1703 final String localNextHopIp, final String remoteNextHopIp) {
1704 LOG.trace("populateExternalRoutesOnDpn : dpn {}, vpn {}, rd {}, localNexthopIp {} , remoteNextHopIp {} ",
1705 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1706 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1707 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1708 List<SubTransaction> txnObjects = new ArrayList<>();
1709 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1710 if (vrfTable.isPresent()) {
1711 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1712 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1713 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1714 vrfTable.get().getVrfEntry().stream()
1715 .filter(vrfEntry -> RouteOrigin.BGP == RouteOrigin.value(vrfEntry.getOrigin()))
1716 .forEach(bgpRouteVrfEntryHandler.getConsumerForCreatingRemoteFib(dpnId, vpnId,
1717 rd, remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx), txnObjects));
1723 public void manageRemoteRouteOnDPN(final boolean action,
1724 final BigInteger localDpnId,
1727 final String destPrefix,
1728 final String destTepIp,
1730 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1732 if (vpnInstance == null) {
1733 LOG.error("VpnInstance for rd {} not present for prefix {}", rd, destPrefix);
1737 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, localDpnId),
1738 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1739 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1740 VrfTablesKey vrfTablesKey = new VrfTablesKey(rd);
1741 VrfEntry vrfEntry = getVrfEntry(dataBroker, rd, destPrefix);
1742 if (vrfEntry == null) {
1745 LOG.trace("manageRemoteRouteOnDPN :: action {}, DpnId {}, vpnId {}, rd {}, destPfx {}",
1746 action, localDpnId, vpnId, rd, destPrefix);
1747 List<RoutePaths> routePathList = vrfEntry.getRoutePaths();
1748 VrfEntry modVrfEntry;
1749 if (routePathList == null || routePathList.isEmpty()) {
1750 modVrfEntry = FibHelper.getVrfEntryBuilder(vrfEntry, label,
1751 Collections.singletonList(destTepIp),
1752 RouteOrigin.value(vrfEntry.getOrigin()), null /* parentVpnRd */).build();
1754 modVrfEntry = vrfEntry;
1758 LOG.trace("manageRemoteRouteOnDPN updated(add) vrfEntry :: {}", modVrfEntry);
1759 createRemoteFibEntry(localDpnId, vpnId, vrfTablesKey.getRouteDistinguisher(),
1762 LOG.trace("manageRemoteRouteOnDPN updated(remove) vrfEntry :: {}", modVrfEntry);
1763 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnInstance.getVpnId(),
1764 vrfEntry.getDestPrefix());
1765 if (usedRds.size() > 1) {
1766 LOG.debug("The extra route prefix is still present in some DPNs");
1769 //Is this fib route an extra route? If yes, get the nexthop which would be
1770 //an adjacency in the vpn
1771 Optional<Routes> extraRouteOptional = Optional.absent();
1772 if (usedRds.size() != 0) {
1773 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1774 fibUtil.getVpnNameFromId(vpnInstance.getVpnId()),
1775 usedRds.get(0), vrfEntry.getDestPrefix());
1777 baseVrfEntryHandler.deleteRemoteRoute(null, localDpnId, vpnId, vrfTablesKey, modVrfEntry,
1778 extraRouteOptional, TransactionAdapter.toWriteTransaction(tx));
1784 public void cleanUpDpnForVpn(final BigInteger dpnId, final long vpnId, final String rd,
1785 final FutureCallback<List<Void>> callback) {
1786 LOG.trace("cleanUpDpnForVpn: Remove dpn {} for vpn {} : cleanUpDpnForVpn", dpnId, rd);
1787 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1789 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1790 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1791 List<SubTransaction> txnObjects = new ArrayList<>();
1792 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1793 LogicalDatastoreType.CONFIGURATION, id);
1794 List<ListenableFuture<Void>> futures = new ArrayList<>();
1795 if (vrfTable.isPresent()) {
1796 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1797 futures.add(retryingTxRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1798 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1799 for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
1800 /* Handle subnet routes here */
1801 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1802 if (subnetRoute != null && !fibUtil
1803 .isInterfacePresentInDpn(vrfEntry.getParentVpnRd(), dpnId)) {
1804 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Cleaning subnetroute {} on dpn {}"
1805 + " for vpn {}", vrfEntry.getDestPrefix(), dpnId, rd);
1806 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1807 NwConstants.DEL_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1808 List<RoutePaths> routePaths = vrfEntry.getRoutePaths();
1809 if (routePaths != null) {
1810 for (RoutePaths routePath : routePaths) {
1811 makeLFibTableEntry(dpnId, routePath.getLabel(), null,
1812 DEFAULT_FIB_FLOW_PRIORITY,
1813 NwConstants.DEL_FLOW, tx);
1814 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Released subnetroute label {}"
1815 + " for rd {} prefix {}", routePath.getLabel(), rd,
1816 vrfEntry.getDestPrefix());
1819 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry,
1820 NwConstants.DEL_FLOW, tx);
1823 // ping responder for router interfaces
1824 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1825 if (routerInt != null) {
1826 LOG.trace("Router augmented vrfentry found for rd:{}, uuid:{}, ip:{}, mac:{}",
1827 rd, routerInt.getUuid(), routerInt.getIpAddress(),
1828 routerInt.getMacAddress());
1829 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1830 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1831 NwConstants.DEL_FLOW);
1835 //Handle local flow deletion for imports
1836 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1837 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1838 if (optionalLabel.isPresent()) {
1839 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1840 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1841 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList,
1842 lri) && lri.getDpnId().equals(dpnId)) {
1843 deleteLocalFibEntry(vpnId, rd, vrfEntry);
1848 // Passing null as we don't know the dpn
1849 // to which prefix is attached at this point
1850 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1851 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1852 Optional<Routes> extraRouteOptional;
1853 //Is this fib route an extra route? If yes, get the nexthop which would be
1854 //an adjacency in the vpn
1855 if (usedRds != null && !usedRds.isEmpty()) {
1856 if (usedRds.size() > 1) {
1857 LOG.error("The extra route prefix is still present in some DPNs");
1860 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
1861 usedRds.get(0), vrfEntry.getDestPrefix());
1865 extraRouteOptional = Optional.absent();
1867 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1868 bgpRouteVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1869 vrfTable.get().key(), vrfEntry, extraRouteOptional,
1870 TransactionAdapter.toWriteTransaction(tx), txnObjects);
1872 if (subnetRoute == null || !fibUtil
1873 .isInterfacePresentInDpn(vrfEntry.getParentVpnRd(), dpnId)) {
1874 baseVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1875 vrfTable.get().key(), vrfEntry, extraRouteOptional,
1876 TransactionAdapter.toWriteTransaction(tx));
1882 if (callback != null) {
1883 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1884 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1887 LOG.error("cleanUpDpnForVpn: No vrf table found for rd {} vpnId {} dpn {}", rd, vpnId, dpnId);
1894 public void cleanUpExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1895 final String localNextHopIp, final String remoteNextHopIp) {
1896 LOG.trace("cleanUpExternalRoutesOnDpn : cleanup remote routes on dpn {} for vpn {}, rd {}, "
1897 + " localNexthopIp {} , remoteNexhtHopIp {}",
1898 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1899 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1900 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1901 List<SubTransaction> txnObjects = new ArrayList<>();
1902 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1903 if (vrfTable.isPresent()) {
1904 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1906 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1907 return Collections.singletonList(
1908 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1909 tx -> vrfTable.get().getVrfEntry().stream()
1910 .filter(vrfEntry -> RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP)
1911 .forEach(bgpRouteVrfEntryHandler.getConsumerForDeletingRemoteFib(dpnId, vpnId,
1912 remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx),
1919 public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
1920 InstanceIdentifierBuilder<VrfTables> idBuilder =
1921 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
1922 return idBuilder.build();
1925 private String getInterVpnFibFlowRef(String interVpnLinkName, String prefix, String nextHop) {
1926 return FLOWID_PREFIX + interVpnLinkName + NwConstants.FLOWID_SEPARATOR + prefix + NwConstants
1927 .FLOWID_SEPARATOR + nextHop;
1930 private String getTableMissFlowRef(BigInteger dpnId, short tableId, int tableMiss) {
1931 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR
1932 + tableMiss + FLOWID_PREFIX;
1935 private VrfEntry getVrfEntry(DataBroker broker, String rd, String ipPrefix) {
1936 InstanceIdentifier<VrfEntry> vrfEntryId = InstanceIdentifier.builder(FibEntries.class)
1937 .child(VrfTables.class, new VrfTablesKey(rd))
1938 .child(VrfEntry.class, new VrfEntryKey(ipPrefix)).build();
1939 Optional<VrfEntry> vrfEntry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
1940 if (vrfEntry.isPresent()) {
1941 return vrfEntry.get();
1946 public void removeInterVPNLinkRouteFlows(final InterVpnLinkDataComposite interVpnLink,
1947 final String vpnName,
1948 final VrfEntry vrfEntry) {
1949 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null && vrfEntry.getRoutePaths().size() == 1);
1951 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
1952 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
1954 if (targetDpns.isEmpty()) {
1955 LOG.warn("Could not find DPNs for VPN {} in InterVpnLink {}", vpnName, interVpnLinkName);
1959 java.util.Optional<String> optNextHop = FibUtil.getFirstNextHopAddress(vrfEntry);
1960 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1964 optNextHop.ifPresent(nextHop -> {
1965 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, vrfEntry.getDestPrefix(), nextHop);
1966 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
1967 Flow flow = new FlowBuilder().withKey(flowKey).setId(new FlowId(flowRef))
1968 .setTableId(NwConstants.L3_FIB_TABLE).setFlowName(flowRef).build();
1970 LOG.trace("Removing flow in FIB table for interVpnLink {} key {}", interVpnLinkName, flowRef);
1971 for (BigInteger dpId : targetDpns) {
1972 LOG.debug("Removing flow: VrfEntry=[prefix={} nexthop={}] dpn {} for InterVpnLink {} in FIB",
1973 vrfEntry.getDestPrefix(), nextHop, dpId, interVpnLinkName);
1975 mdsalManager.removeFlow(dpId, flow);
1981 optLabel.ifPresent(label -> {
1982 LOG.trace("Removing flow in FIB table for interVpnLink {}", interVpnLinkName);
1984 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1985 for (BigInteger dpId : targetDpns) {
1986 LOG.debug("Removing flow: VrfEntry=[prefix={} label={}] dpn {} for InterVpnLink {} in LFIB",
1987 vrfEntry.getDestPrefix(), label, dpId, interVpnLinkName);
1988 makeLFibTableEntry(dpId, label, /*instructions*/null, LFIB_INTERVPN_PRIORITY, NwConstants.DEL_FLOW,
1991 }), LOG, "Error removing flows");
1995 private boolean isPrefixAndNextHopPresentInLri(String prefix,
1996 List<String> nextHopAddressList, LabelRouteInfo lri) {
1997 return lri != null && lri.getPrefix().equals(prefix)
1998 && nextHopAddressList.contains(lri.getNextHopIpList().get(0));
2001 private boolean shouldCreateFibEntryForVrfAndVpnIdOnDpn(Long vpnId, VrfEntry vrfEntry, BigInteger dpnId) {
2002 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
2006 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
2007 if (prefix != null) {
2008 BigInteger prefixDpnId = prefix.getDpnId();
2009 if (dpnId.equals(prefixDpnId)) {
2010 LOG.trace("Should not create remote FIB entry for vrfEntry {} on DPN {}",