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.collect.Lists;
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture;
21 import com.google.common.util.concurrent.MoreExecutors;
22 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
23 import java.math.BigInteger;
24 import java.net.InetAddress;
25 import java.net.UnknownHostException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.Objects;
32 import java.util.concurrent.Callable;
33 import java.util.concurrent.CopyOnWriteArrayList;
34 import java.util.concurrent.ExecutionException;
35 import java.util.concurrent.locks.ReentrantLock;
36 import javax.annotation.PostConstruct;
37 import javax.inject.Inject;
38 import javax.inject.Singleton;
39 import org.eclipse.jdt.annotation.NonNull;
40 import org.eclipse.jdt.annotation.Nullable;
41 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
42 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
43 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
44 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
45 import org.opendaylight.genius.infra.Datastore.Configuration;
46 import org.opendaylight.genius.infra.Datastore.Operational;
47 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
48 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
49 import org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner;
50 import org.opendaylight.genius.infra.TransactionAdapter;
51 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
52 import org.opendaylight.genius.infra.TypedWriteTransaction;
53 import org.opendaylight.genius.mdsalutil.ActionInfo;
54 import org.opendaylight.genius.mdsalutil.FlowEntity;
55 import org.opendaylight.genius.mdsalutil.InstructionInfo;
56 import org.opendaylight.genius.mdsalutil.MDSALUtil;
57 import org.opendaylight.genius.mdsalutil.MatchInfo;
58 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
59 import org.opendaylight.genius.mdsalutil.NWUtil;
60 import org.opendaylight.genius.mdsalutil.NwConstants;
61 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
62 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
63 import org.opendaylight.genius.mdsalutil.actions.ActionPopMpls;
64 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
65 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
66 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
67 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
68 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
69 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
70 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
71 import org.opendaylight.genius.mdsalutil.matches.MatchMplsLabel;
72 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
73 import org.opendaylight.genius.utils.JvmGlobalLocks;
74 import org.opendaylight.genius.utils.ServiceIndex;
75 import org.opendaylight.genius.utils.batching.SubTransaction;
76 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
77 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
78 import org.opendaylight.netvirt.elanmanager.api.IElanService;
79 import org.opendaylight.netvirt.fibmanager.NexthopManager.AdjacencyResult;
80 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
81 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
82 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
83 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
84 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
85 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
86 import org.opendaylight.serviceutils.upgrade.UpgradeState;
87 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.LabelRouteMap;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.RouterInterface;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRoute;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfo;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoBuilder;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoKey;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOp;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
114 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.PrefixesBuilder;
115 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
116 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
117 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
118 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
119 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState.State;
120 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
121 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
122 import org.opendaylight.yangtools.yang.common.Uint32;
123 import org.opendaylight.yangtools.yang.common.Uint64;
124 import org.slf4j.Logger;
125 import org.slf4j.LoggerFactory;
129 public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry, VrfEntryListener> {
131 private static final Logger LOG = LoggerFactory.getLogger(VrfEntryListener.class);
132 private static final String FLOWID_PREFIX = "L3.";
133 private static final Uint64 COOKIE_VM_FIB_TABLE = Uint64.valueOf("8000003", 16).intern();
134 private static final int DEFAULT_FIB_FLOW_PRIORITY = 10;
135 private static final int IPV4_ADDR_PREFIX_LENGTH = 32;
136 private static final int LFIB_INTERVPN_PRIORITY = 15;
137 public static final Uint64 COOKIE_TUNNEL = Uint64.valueOf("9000000", 16).intern();
138 private static final int MAX_RETRIES = 3;
139 private static final Uint64 COOKIE_TABLE_MISS = Uint64.valueOf("8000004", 16).intern();
141 private final DataBroker dataBroker;
142 private final ManagedNewTransactionRunner txRunner;
143 private final RetryingManagedNewTransactionRunner retryingTxRunner;
144 private final IMdsalApiManager mdsalManager;
145 private final NexthopManager nextHopManager;
146 private final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler;
147 private final BaseVrfEntryHandler baseVrfEntryHandler;
148 private final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler;
149 private final JobCoordinator jobCoordinator;
150 private final IElanService elanManager;
151 private final FibUtil fibUtil;
152 private final InterVpnLinkCache interVpnLinkCache;
153 private final List<AutoCloseable> closeables = new CopyOnWriteArrayList<>();
154 private final UpgradeState upgradeState;
155 private final DataTreeEventCallbackRegistrar eventCallbacks;
158 public VrfEntryListener(final DataBroker dataBroker, final IMdsalApiManager mdsalApiManager,
159 final NexthopManager nexthopManager,
160 final IElanService elanManager,
161 final BaseVrfEntryHandler vrfEntryHandler,
162 final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler,
163 final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler,
164 final JobCoordinator jobCoordinator,
165 final FibUtil fibUtil,
166 final InterVpnLinkCache interVpnLinkCache,
167 final UpgradeState upgradeState,
168 final DataTreeEventCallbackRegistrar eventCallbacks) {
169 super(VrfEntry.class, VrfEntryListener.class);
170 this.dataBroker = dataBroker;
171 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
172 this.retryingTxRunner = new RetryingManagedNewTransactionRunner(dataBroker, MAX_RETRIES);
173 this.mdsalManager = mdsalApiManager;
174 this.nextHopManager = nexthopManager;
175 this.elanManager = elanManager;
176 this.baseVrfEntryHandler = vrfEntryHandler;
177 this.bgpRouteVrfEntryHandler = bgpRouteVrfEntryHandler;
178 this.routerInterfaceVrfEntryHandler = routerInterfaceVrfEntryHandler;
179 this.jobCoordinator = jobCoordinator;
180 this.fibUtil = fibUtil;
181 this.interVpnLinkCache = interVpnLinkCache;
182 this.upgradeState = upgradeState;
183 this.eventCallbacks = eventCallbacks;
189 LOG.info("{} init", getClass().getSimpleName());
190 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
194 @SuppressWarnings("checkstyle:IllegalCatch")
195 public void close() {
196 closeables.forEach(c -> {
199 } catch (Exception e) {
200 LOG.warn("Error closing {}", c, e);
206 protected VrfEntryListener getDataTreeChangeListener() {
207 return VrfEntryListener.this;
211 protected InstanceIdentifier<VrfEntry> getWildCardPath() {
212 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class).child(VrfEntry.class);
216 protected void add(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
217 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
218 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
219 LOG.debug("ADD: Adding Fib Entry rd {} prefix {} route-paths {}",
220 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
221 addFibEntries(identifier, vrfEntry, rd);
222 LOG.info("ADD: Added Fib Entry rd {} prefix {} route-paths {}",
223 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
226 //This method is temporary. Eventually Factory design pattern will be used to get
227 // right VrfEntryhandle and invoke its methods.
228 private void addFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
229 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
230 bgpRouteVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
233 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
234 LOG.info("EVPN flows need to be programmed.");
235 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
236 nextHopManager, jobCoordinator, fibUtil, upgradeState, eventCallbacks);
237 evpnVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
238 closeables.add(evpnVrfEntryHandler);
241 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
242 if (routerInt != null) {
243 // ping responder for router interfaces
244 routerInterfaceVrfEntryHandler.createFlows(vrfEntry, rd);
247 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
248 createFibEntries(identifier, vrfEntry);
254 protected void remove(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry) {
255 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
256 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
257 LOG.debug("REMOVE: Removing Fib Entry rd {} prefix {} route-paths {}",
258 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
259 removeFibEntries(identifier, vrfEntry, rd);
260 LOG.info("REMOVE: Removed Fib Entry rd {} prefix {} route-paths {}",
261 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
264 //This method is temporary. Eventually Factory design pattern will be used to get
265 // right VrfEntryhandle and invoke its methods.
266 private void removeFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
267 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
268 LOG.info("EVPN flows to be deleted");
269 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
270 nextHopManager, jobCoordinator, fibUtil, upgradeState, eventCallbacks);
271 evpnVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
272 closeables.add(evpnVrfEntryHandler);
275 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
276 if (routerInt != null) {
277 // ping responder for router interfaces
278 routerInterfaceVrfEntryHandler.removeFlows(vrfEntry, rd);
281 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
282 deleteFibEntries(identifier, vrfEntry);
285 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
286 bgpRouteVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
292 // "Redundant nullcheck of originalRoutePath, which is known to be non-null" - the null checking for
293 // originalRoutePath is a little dicey - safest to keep the checking even if not needed.
294 @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE")
295 protected void update(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update) {
296 Preconditions.checkNotNull(update, "VrfEntry should not be null or empty.");
297 final String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
298 LOG.debug("UPDATE: Updating Fib Entries to rd {} prefix {} route-paths {} origin {} old-origin {}", rd,
299 update.getDestPrefix(), update.getRoutePaths(), update.getOrigin(), original.getOrigin());
300 // Handle BGP Routes first
301 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.BGP) {
302 bgpRouteVrfEntryHandler.updateFlows(identifier, original, update, rd);
303 LOG.info("UPDATE: Updated BGP advertised Fib Entry with rd {} prefix {} route-paths {}",
304 rd, update.getDestPrefix(), update.getRoutePaths());
308 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.STATIC) {
309 List<RoutePaths> originalRoutePath = original.getRoutePaths();
310 List<RoutePaths> updateRoutePath = update.getRoutePaths();
311 LOG.info("UPDATE: Original route-path {} update route-path {} ", originalRoutePath, updateRoutePath);
313 //Updates need to be handled for extraroute even if original vrf entry route path is null or
314 //updated vrf entry route path is null. This can happen during tunnel events.
315 Optional<VpnInstanceOpDataEntry> optVpnInstance = fibUtil.getVpnInstanceOpData(rd);
316 List<String> usedRds = new ArrayList<>();
317 if (optVpnInstance.isPresent()) {
318 usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,optVpnInstance.get().getVpnId(),
319 update.getDestPrefix());
321 // If original VRF Entry had nexthop null , but update VRF Entry
322 // has nexthop , route needs to be created on remote Dpns
323 if (originalRoutePath == null || originalRoutePath.isEmpty()
324 && updateRoutePath != null && !updateRoutePath.isEmpty() && usedRds.isEmpty()) {
325 // TODO(vivek): Though ugly, Not handling this code now, as each
326 // tep add event will invoke flow addition
327 LOG.trace("Original VRF entry NH is null for destprefix {}. And the prefix is not an extra route."
328 + " This event is IGNORED here.", update.getDestPrefix());
332 // If original VRF Entry had valid nexthop , but update VRF Entry
333 // has nexthop empty'ed out, route needs to be removed from remote Dpns
334 if (updateRoutePath == null || updateRoutePath.isEmpty()
335 && originalRoutePath != null && !originalRoutePath.isEmpty() && usedRds.isEmpty()) {
336 LOG.trace("Original VRF entry had valid NH for destprefix {}. And the prefix is not an extra route."
337 + "This event is IGNORED here.", update.getDestPrefix());
340 //Update the used rds and vpntoextraroute containers only for the deleted nextHops.
341 List<String> nextHopsRemoved = FibHelper.getNextHopListFromRoutePaths(original);
342 nextHopsRemoved.removeAll(FibHelper.getNextHopListFromRoutePaths(update));
343 List<ListenableFuture<Void>> futures = new ArrayList<>();
344 ListenableFuture<Void> configFuture =
345 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx ->
346 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, operTx ->
347 nextHopsRemoved.parallelStream()
348 .forEach(nextHopRemoved -> {
350 fibUtil.updateUsedRdAndVpnToExtraRoute(
351 configTx, operTx, nextHopRemoved, rd, update.getDestPrefix());
352 } catch (ExecutionException | InterruptedException e) {
353 throw new RuntimeException(e);
356 futures.add(configFuture);
357 Futures.addCallback(configFuture, new FutureCallback<Void>() {
359 public void onSuccess(Void result) {
360 createFibEntries(identifier, update);
361 LOG.info("UPDATE: Updated static Fib Entry with rd {} prefix {} route-paths {}",
362 rd, update.getDestPrefix(), update.getRoutePaths());
366 public void onFailure(Throwable throwable) {
367 LOG.error("Exception encountered while submitting operational future for update vrfentry {}",
370 }, MoreExecutors.directExecutor());
374 //Handle all other routes only on a cluster reboot
375 if (original.equals(update)) {
377 createFibEntries(identifier, update);
378 LOG.info("UPDATE: Updated Non-static Fib Entry with rd {} prefix {} route-paths {}",
379 rd, update.getDestPrefix(), update.getRoutePaths());
383 LOG.info("UPDATE: Ignoring update for FIB entry with rd {} prefix {} route-origin {} route-paths {}",
384 rd, update.getDestPrefix(), update.getOrigin(), update.getRoutePaths());
387 private void createFibEntries(final InstanceIdentifier<VrfEntry> vrfEntryIid, final VrfEntry vrfEntry) {
388 final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
389 List<SubTransaction> txnObjects = new ArrayList<>();
390 final VpnInstanceOpDataEntry vpnInstance =
391 fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
392 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
393 Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId()
394 + " has null vpnId!");
395 final Collection<VpnToDpnList> vpnToDpnList;
396 if (vrfEntry.getParentVpnRd() != null
397 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
398 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
399 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
400 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
401 vpnInstance.getVpnToDpnList();
402 LOG.info("createFibEntries: Processing creation of PNF FIB entry with rd {} prefix {}",
403 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
405 vpnToDpnList = vpnInstance.getVpnToDpnList();
407 final Uint32 vpnId = vpnInstance.getVpnId();
408 final String rd = vrfTableKey.getRouteDistinguisher();
409 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
410 if (subnetRoute != null) {
411 final long elanTag = subnetRoute.getElantag().toJava();
412 LOG.trace("SUBNETROUTE: createFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
413 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
414 if (vpnToDpnList != null) {
415 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
416 () -> Collections.singletonList(
417 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
418 for (final VpnToDpnList curDpn : vpnToDpnList) {
419 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
420 installSubnetRouteInFib(curDpn.getDpnId(),
421 elanTag, rd, vpnId, vrfEntry, tx);
422 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd,
423 vpnId, vrfEntry, NwConstants.ADD_FLOW, tx);
430 // Get etherType value based on the IpPrefix address family type
433 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
434 } catch (IllegalArgumentException ex) {
435 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
439 final List<Uint64> localDpnIdList = createLocalFibEntry(vpnInstance.getVpnId(),
440 rd, vrfEntry, etherType);
441 if (!localDpnIdList.isEmpty() && vpnToDpnList != null) {
442 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
443 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
444 final ReentrantLock lock = lockFor(vpnInstance);
447 for (VpnToDpnList vpnDpn : vpnToDpnList) {
448 if (!localDpnIdList.contains(vpnDpn.getDpnId())) {
449 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
451 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
452 bgpRouteVrfEntryHandler.createRemoteFibEntry(vpnDpn.getDpnId(),
453 vpnId, vrfTableKey.getRouteDistinguisher(), vrfEntry,
454 TransactionAdapter.toWriteTransaction(tx),
457 createRemoteFibEntry(vpnDpn.getDpnId(),
458 vpnInstance.getVpnId(),
459 vrfTableKey.getRouteDistinguisher(), vrfEntry, tx);
461 } catch (NullPointerException e) {
462 LOG.error("Failed to get create remote fib flows for prefix {} ",
463 vrfEntry.getDestPrefix(), e);
474 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
475 if (optVpnUuid.isPresent()) {
476 String vpnUuid = optVpnUuid.get();
477 InterVpnLinkDataComposite interVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid).orNull();
478 if (interVpnLink != null) {
479 LOG.debug("InterVpnLink {} found in Cache linking Vpn {}", interVpnLink.getInterVpnLinkName(), vpnUuid);
480 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
481 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
482 // This is an static route that points to the other endpoint of an InterVpnLink
483 // In that case, we should add another entry in FIB table pointing to LPortDispatcher table.
484 installIVpnLinkSwitchingFlows(interVpnLink, vpnUuid, vrfEntry, vpnId);
485 installInterVpnRouteInLFib(interVpnLink, vpnUuid, vrfEntry, etherType);
492 void refreshFibTables(String rd, String prefix) {
493 InstanceIdentifier<VrfEntry> vrfEntryId =
494 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
495 .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
496 Optional<VrfEntry> vrfEntry = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
497 if (vrfEntry.isPresent()) {
498 createFibEntries(vrfEntryId, vrfEntry.get());
502 private Prefixes updateVpnReferencesInLri(LabelRouteInfo lri, String vpnInstanceName, boolean isPresentInList) {
503 LOG.debug("updating LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
504 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
505 prefixBuilder.setDpnId(lri.getDpnId());
506 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
507 prefixBuilder.setIpAddress(lri.getPrefix());
508 // Increment the refCount here
509 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
510 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
511 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri);
512 if (!isPresentInList) {
513 LOG.debug("vpnName {} is not present in LRI with label {}..", vpnInstanceName, lri.getLabel());
514 List<String> vpnInstanceNames =
515 lri.getVpnInstanceList() != null ? new ArrayList<>(lri.getVpnInstanceList()) : new ArrayList<>();
516 vpnInstanceNames.add(vpnInstanceName);
517 builder.setVpnInstanceList(vpnInstanceNames);
518 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
520 LOG.debug("vpnName {} is present in LRI with label {}..", vpnInstanceName, lri.getLabel());
522 return prefixBuilder.build();
525 void installSubnetRouteInFib(final Uint64 dpnId, final long elanTag, final String rd,
526 final Uint32 vpnId, final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
528 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
529 newTx -> installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, newTx)), LOG,
530 "Error installing subnet route in FIB");
535 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
536 } catch (IllegalArgumentException ex) {
537 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
540 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
541 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
542 final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
543 final ReentrantLock lock = lockFor(lriKey);
546 LabelRouteInfo lri = getLabelRouteInfo(lriKey);
547 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
549 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
550 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
551 fibUtil.getVpnInstanceOpData(rd);
552 if (vpnInstanceOpDataEntryOptional.isPresent()) {
553 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
554 if (!lri.getVpnInstanceList().contains(vpnInstanceName)) {
555 updateVpnReferencesInLri(lri, vpnInstanceName, false);
559 LOG.debug("SUBNETROUTE: installSubnetRouteInFib: Fetched labelRouteInfo for label {} interface {}"
560 + " and got dpn {}", label, lri.getVpnInterfaceName(), lri.getDpnId());
566 final List<InstructionInfo> instructions = new ArrayList<>();
567 Uint64 subnetRouteMeta = Uint64.valueOf(BigInteger.valueOf(elanTag).shiftLeft(24)
568 .or(BigInteger.valueOf(vpnId.longValue()).shiftLeft(1)));
569 instructions.add(new InstructionWriteMetadata(subnetRouteMeta, MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
570 instructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
571 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
572 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
573 if (vrfEntry.getRoutePaths() != null) {
574 for (RoutePaths routePath : vrfEntry.getRoutePaths()) {
575 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
576 List<ActionInfo> actionsInfos = new ArrayList<>();
577 // reinitialize instructions list for LFIB Table
578 final List<InstructionInfo> LFIBinstructions = new ArrayList<>();
579 actionsInfos.add(new ActionPopMpls(etherType));
580 LFIBinstructions.add(new InstructionApplyActions(actionsInfos));
581 LFIBinstructions.add(new InstructionWriteMetadata(subnetRouteMeta,
582 MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
583 LFIBinstructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
585 makeLFibTableEntry(dpnId, routePath.getLabel(), LFIBinstructions,
586 DEFAULT_FIB_FLOW_PRIORITY, NwConstants.ADD_FLOW, tx);
592 private void installSubnetBroadcastAddrDropRule(final Uint64 dpnId, final String rd, final Uint32 vpnId,
593 final VrfEntry vrfEntry, int addOrRemove, TypedWriteTransaction<Configuration> tx) {
594 List<MatchInfo> matches = new ArrayList<>();
596 LOG.debug("SUBNETROUTE: installSubnetBroadcastAddrDropRule: destPrefix {} rd {} vpnId {} dpnId {}",
597 vrfEntry.getDestPrefix(), rd, vpnId, dpnId);
598 String[] ipAddress = vrfEntry.getDestPrefix().split("/");
599 String subnetBroadcastAddr = FibUtil.getBroadcastAddressFromCidr(vrfEntry.getDestPrefix());
600 final int prefixLength = ipAddress.length == 1 ? 0 : Integer.parseInt(ipAddress[1]);
602 InetAddress destPrefix;
604 destPrefix = InetAddress.getByName(subnetBroadcastAddr);
605 } catch (UnknownHostException e) {
606 LOG.error("Failed to get destPrefix for prefix {} rd {} VpnId {} DPN {}",
607 vrfEntry.getDestPrefix(), rd, vpnId, dpnId, e);
611 // Match on VpnId and SubnetBroadCast IP address
612 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId.longValue()),
613 MetaDataUtil.METADATA_MASK_VRFID));
614 matches.add(MatchEthernetType.IPV4);
616 if (prefixLength != 0) {
617 matches.add(new MatchIpv4Destination(subnetBroadcastAddr, Integer.toString(IPV4_ADDR_PREFIX_LENGTH)));
620 //Action is to drop the packet
621 List<InstructionInfo> dropInstructions = new ArrayList<>();
622 List<ActionInfo> actionsInfos = new ArrayList<>();
623 actionsInfos.add(new ActionDrop());
624 dropInstructions.add(new InstructionApplyActions(actionsInfos));
626 int priority = DEFAULT_FIB_FLOW_PRIORITY + IPV4_ADDR_PREFIX_LENGTH;
627 String flowRef = FibUtil.getFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, rd, priority, destPrefix);
628 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef, priority,
629 flowRef, 0, 0, COOKIE_TABLE_MISS, matches, dropInstructions);
631 Flow flow = flowEntity.getFlowBuilder().build();
632 String flowId = flowEntity.getFlowId();
633 FlowKey flowKey = new FlowKey(new FlowId(flowId));
634 Node nodeDpn = FibUtil.buildDpnNode(dpnId);
636 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
637 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
638 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
640 if (addOrRemove == NwConstants.ADD_FLOW) {
641 tx.put(flowInstanceId,flow, true);
643 tx.delete(flowInstanceId);
648 * For a given route, it installs a flow in LFIB that sets the lportTag of the other endpoint and sends to
649 * LportDispatcher table (via table 80)
651 private void installInterVpnRouteInLFib(final InterVpnLinkDataComposite interVpnLink, final String vpnName,
652 final VrfEntry vrfEntry, int etherType) {
653 // INTERVPN routes are routes in a Vpn1 that have been leaked to Vpn2. In DC-GW, this Vpn2 route is pointing
654 // to a list of DPNs where Vpn2's VpnLink was instantiated. In these DPNs LFIB must be programmed so that the
655 // packet is commuted from Vpn2 to Vpn1.
656 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
657 if (!interVpnLink.isActive()) {
658 LOG.warn("InterVpnLink {} is NOT ACTIVE. InterVpnLink flows for prefix={} wont be installed in LFIB",
659 interVpnLinkName, vrfEntry.getDestPrefix());
663 Optional<Uint32> optLportTag = interVpnLink.getEndpointLportTagByVpnName(vpnName);
664 if (!optLportTag.isPresent()) {
665 LOG.warn("Could not retrieve lportTag for VPN {} endpoint in InterVpnLink {}", vpnName, interVpnLinkName);
669 Long lportTag = optLportTag.get().toJava();
670 Uint32 label = FibUtil.getLabelFromRoutePaths(vrfEntry).orElse(null);
672 LOG.error("Could not find label in vrfEntry=[prefix={} routePaths={}]. LFIB entry for InterVpnLink skipped",
673 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
676 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionPopMpls(etherType));
677 List<InstructionInfo> instructions = Arrays.asList(
678 new InstructionApplyActions(actionsInfos),
679 new InstructionWriteMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(lportTag.intValue(),
680 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
681 NwConstants.L3VPN_SERVICE_INDEX)),
682 MetaDataUtil.getMetaDataMaskForLPortDispatcher()),
683 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
684 List<String> interVpnNextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
685 List<Uint64> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
687 for (Uint64 dpId : targetDpns) {
688 LOG.debug("Installing flow: VrfEntry=[prefix={} label={} nexthop={}] dpn {} for InterVpnLink {} in LFIB",
689 vrfEntry.getDestPrefix(), label, interVpnNextHopList, dpId, interVpnLink.getInterVpnLinkName());
691 makeLFibTableEntry(dpId, label, instructions, LFIB_INTERVPN_PRIORITY, NwConstants.ADD_FLOW,
698 * Installs the flows in FIB table that, for a given route, do the switching from one VPN to the other.
700 private void installIVpnLinkSwitchingFlows(final InterVpnLinkDataComposite interVpnLink, final String vpnUuid,
701 final VrfEntry vrfEntry, Uint32 vpnTag) {
702 Preconditions.checkNotNull(interVpnLink, "InterVpnLink cannot be null");
703 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null
704 && vrfEntry.getRoutePaths().size() == 1);
705 String destination = vrfEntry.getDestPrefix();
706 String nextHop = vrfEntry.getRoutePaths().get(0).getNexthopAddress();
707 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
709 // After having received a static route, we should check if the vpn is part of an inter-vpn-link.
710 // In that case, we should populate the FIB table of the VPN pointing to LPortDisptacher table
711 // using as metadata the LPortTag associated to that vpn in the inter-vpn-link.
712 if (interVpnLink.getState().or(State.Error) != State.Active) {
713 LOG.warn("Route to {} with nexthop={} cannot be installed because the interVpnLink {} is not active",
714 destination, nextHop, interVpnLinkName);
718 Optional<Uint32> optOtherEndpointLportTag = interVpnLink.getOtherEndpointLportTagByVpnName(vpnUuid);
719 if (!optOtherEndpointLportTag.isPresent()) {
720 LOG.warn("Could not find suitable LportTag for the endpoint opposite to vpn {} in interVpnLink {}",
721 vpnUuid, interVpnLinkName);
725 List<Uint64> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnUuid);
726 if (targetDpns.isEmpty()) {
727 LOG.warn("Could not find DPNs for endpoint opposite to vpn {} in interVpnLink {}",
728 vpnUuid, interVpnLinkName);
732 String[] values = destination.split("/");
733 String destPrefixIpAddress = values[0];
734 int prefixLength = values.length == 1 ? 0 : Integer.parseInt(values[1]);
736 List<MatchInfo> matches = new ArrayList<>();
737 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnTag.longValue()),
738 MetaDataUtil.METADATA_MASK_VRFID));
739 matches.add(MatchEthernetType.IPV4);
741 if (prefixLength != 0) {
742 matches.add(new MatchIpv4Destination(destPrefixIpAddress, Integer.toString(prefixLength)));
745 List<Instruction> instructions =
746 Arrays.asList(new InstructionWriteMetadata(
747 MetaDataUtil.getMetaDataForLPortDispatcher(optOtherEndpointLportTag.get().intValue(),
748 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants
749 .L3VPN_SERVICE_INDEX)),
750 MetaDataUtil.getMetaDataMaskForLPortDispatcher()).buildInstruction(0),
751 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE).buildInstruction(1));
753 int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
754 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, destination, nextHop);
755 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef, priority, flowRef, 0, 0,
756 COOKIE_VM_FIB_TABLE, matches, instructions);
758 LOG.trace("Installing flow in FIB table for vpn {} interVpnLink {} nextHop {} key {}",
759 vpnUuid, interVpnLink.getInterVpnLinkName(), nextHop, flowRef);
761 for (Uint64 dpId : targetDpns) {
763 LOG.debug("Installing flow: VrfEntry=[prefix={} route-paths={}] dpn {} for InterVpnLink {} in FIB",
764 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(),
765 dpId, interVpnLink.getInterVpnLinkName());
767 mdsalManager.installFlow(dpId, flowEntity);
771 private List<Uint64> createLocalFibEntry(Uint32 vpnId, String rd, VrfEntry vrfEntry, int etherType) {
772 List<Uint64> returnLocalDpnId = new ArrayList<>();
773 String localNextHopIP = vrfEntry.getDestPrefix();
774 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
775 String vpnName = fibUtil.getVpnNameFromId(vpnId);
776 if (localNextHopInfo == null) {
777 boolean localNextHopSeen = false;
778 List<Routes> vpnExtraRoutes = null;
779 //Synchronized to prevent missing bucket action due to race condition between refreshFib and
780 // add/updateFib threads on missing nexthop in VpnToExtraroutes
781 // FIXME: use an Identifier structure?
782 final ReentrantLock lock = JvmGlobalLocks.getLockForString(localNextHopIP + FibConstants.SEPARATOR + rd);
785 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, localNextHopIP);
786 vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
787 vpnName, usedRds, localNextHopIP);
788 if (LOG.isDebugEnabled()) {
789 LOG.debug("Creating Local fib entry with vpnName {} usedRds {} localNextHopIP {} vpnExtraRoutes {}",
790 vpnName, usedRds, localNextHopIP, vpnExtraRoutes);
793 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
794 for (Routes vpnExtraRoute : vpnExtraRoutes) {
796 if (isIpv4Address(vpnExtraRoute.getNexthopIpList().get(0))) {
797 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
799 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
801 Prefixes localNextHopInfoLocal = fibUtil.getPrefixToInterface(vpnId,
803 if (localNextHopInfoLocal != null) {
804 localNextHopSeen = true;
806 checkCreateLocalFibEntry(localNextHopInfoLocal, localNextHopInfoLocal.getIpAddress(),
807 vpnId, rd, vrfEntry, vpnExtraRoute, vpnExtraRoutes, etherType,
808 /*parentVpnId*/ null);
809 returnLocalDpnId.add(dpnId);
815 if (!localNextHopSeen && RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
816 java.util.Optional<Uint32> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
817 if (optionalLabel.isPresent()) {
818 Uint32 label = optionalLabel.get();
819 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
820 final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
821 final ReentrantLock labelLock = lockFor(lriKey);
824 LabelRouteInfo lri = getLabelRouteInfo(lriKey);
825 Uint32 parentVpnId = lri.getParentVpnid();
826 if (isPrefixAndNextHopPresentInLri(localNextHopIP, nextHopAddressList, lri)) {
827 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
828 fibUtil.getVpnInstanceOpData(rd);
829 if (vpnInstanceOpDataEntryOptional.isPresent()) {
830 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
831 if (lri.getVpnInstanceList() != null && lri.getVpnInstanceList().contains(
833 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, true);
834 localNextHopIP = lri.getPrefix();
836 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, false);
837 localNextHopIP = lri.getPrefix();
840 if (localNextHopInfo != null) {
841 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
842 label, localNextHopInfo.getVpnInterfaceName(), lri.getDpnId());
843 if (vpnExtraRoutes.isEmpty()) {
844 Uint64 dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
845 vpnId, rd, vrfEntry, null, vpnExtraRoutes, etherType, parentVpnId);
846 returnLocalDpnId.add(dpnId);
848 for (Routes extraRoutes : vpnExtraRoutes) {
849 Uint64 dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
850 vpnId, rd, vrfEntry, extraRoutes, vpnExtraRoutes, etherType,
852 returnLocalDpnId.add(dpnId);
862 if (returnLocalDpnId.isEmpty()) {
863 LOG.error("Local DPNID is empty for rd {}, vpnId {}, vrfEntry {}", rd, vpnId, vrfEntry);
866 Uint64 dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP, vpnId,
867 rd, vrfEntry, /*routes*/ null, /*vpnExtraRoutes*/ null, etherType,
868 /*parentVpnId*/ null);
870 returnLocalDpnId.add(dpnId);
873 return returnLocalDpnId;
876 private Uint64 checkCreateLocalFibEntry(Prefixes localNextHopInfo, String localNextHopIP,
877 final Uint32 vpnId, final String rd,
878 final VrfEntry vrfEntry,
879 @Nullable Routes routes, @Nullable List<Routes> vpnExtraRoutes,
880 int etherType, Uint32 parentVpnId) {
881 String vpnName = fibUtil.getVpnNameFromId(vpnId);
882 if (localNextHopInfo != null) {
885 final Uint64 dpnId = localNextHopInfo.getDpnId();
886 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
887 LOG.debug("checkCreateLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
888 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
891 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
892 LOG.debug("checkCreateLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
893 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
896 if (!isVpnPresentInDpn(rd, dpnId)) {
897 LOG.error("checkCreateLocalFibEntry: The VPN with id {} rd {} is not available on dpn {}",
898 vpnId, rd, dpnId.toString());
901 String interfaceName = localNextHopInfo.getVpnInterfaceName();
902 String prefix = vrfEntry.getDestPrefix();
903 String gwMacAddress = vrfEntry.getGatewayMacAddress();
904 //The loadbalancing group is created only if the extra route has multiple nexthops
905 //to avoid loadbalancing the discovered routes
906 if (RouteOrigin.STATIC.getValue().equals(vrfEntry.getOrigin()) && vpnExtraRoutes != null
908 if (vpnExtraRoutes.size() > 1) {
909 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
910 localGroupId = nextHopManager.getLocalSelectGroup(vpnId, vrfEntry.getDestPrefix());
912 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
913 localGroupId = groupId;
916 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP, prefix,
917 gwMacAddress, parentVpnId);
918 localGroupId = groupId;
920 if (groupId == FibConstants.INVALID_GROUP_ID) {
921 LOG.error("Unable to create Group for local prefix {} on rd {} for vpninterface {} on Node {}",
922 prefix, rd, interfaceName, dpnId.toString());
925 final List<InstructionInfo> instructions = Collections.singletonList(
926 new InstructionApplyActions(
927 Collections.singletonList(new ActionGroup(groupId))));
928 final List<InstructionInfo> lfibinstructions = Collections.singletonList(
929 new InstructionApplyActions(
930 Arrays.asList(new ActionPopMpls(etherType), new ActionGroup(localGroupId))));
931 java.util.Optional<Uint32> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
932 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
933 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
934 jobCoordinator.enqueueJob(jobKey,
935 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
936 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
937 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
938 if (FibUtil.isBgpVpn(vpnName, rd)) {
939 optLabel.ifPresent(label -> {
940 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
942 "Installing LFIB and tunnel table entry on dpn {} for interface {} with label "
943 + "{}, rd {}, prefix {}, nexthop {}", dpnId,
944 localNextHopInfo.getVpnInterfaceName(), optLabel, rd, vrfEntry.getDestPrefix(),
946 makeLFibTableEntry(dpnId, label, lfibinstructions, DEFAULT_FIB_FLOW_PRIORITY,
947 NwConstants.ADD_FLOW, tx);
948 makeTunnelTableEntry(dpnId, label, localGroupId, tx);
950 LOG.debug("Route with rd {} prefix {} label {} nexthop {} for vpn {} is an imported "
951 + "route. LFib and Terminating table entries will not be created.",
952 rd, vrfEntry.getDestPrefix(), optLabel, nextHopAddressList, vpnId);
959 LOG.error("localNextHopInfo received is null for prefix {} on rd {} on vpn {}", vrfEntry.getDestPrefix(), rd,
964 private boolean isVpnPresentInDpn(String rd, Uint64 dpnId) {
965 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
966 Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
967 return dpnInVpn.isPresent();
971 private LabelRouteInfo getLabelRouteInfo(Uint32 label) {
972 return getLabelRouteInfo(new LabelRouteInfoKey(label));
976 private LabelRouteInfo getLabelRouteInfo(LabelRouteInfoKey label) {
977 InstanceIdentifier<LabelRouteInfo> lriIid = InstanceIdentifier.builder(LabelRouteMap.class)
978 .child(LabelRouteInfo.class, label).build();
979 Optional<LabelRouteInfo> opResult = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, lriIid);
980 if (opResult.isPresent()) {
981 return opResult.get();
986 private boolean deleteLabelRouteInfo(LabelRouteInfo lri, String vpnInstanceName,
987 @Nullable TypedWriteTransaction<Operational> tx) {
992 LOG.debug("deleting LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
993 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
994 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
996 List<String> vpnInstancesList = lri.getVpnInstanceList() != null
997 ? new ArrayList<>(lri.getVpnInstanceList()) : new ArrayList<>();
998 if (vpnInstancesList.contains(vpnInstanceName)) {
999 LOG.debug("vpninstance {} name is present", vpnInstanceName);
1000 vpnInstancesList.remove(vpnInstanceName);
1002 if (vpnInstancesList.isEmpty()) {
1003 LOG.debug("deleting LRI instance object for label {}", lri.getLabel());
1007 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId);
1011 LOG.debug("updating LRI instance object for label {}", lri.getLabel());
1012 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri).setVpnInstanceList(vpnInstancesList);
1013 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
1018 void makeTunnelTableEntry(Uint64 dpId, Uint32 label, long groupId/*String egressInterfaceName*/,
1019 TypedWriteTransaction<Configuration> tx) {
1020 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionGroup(groupId));
1022 createTerminatingServiceActions(dpId, label, actionsInfos, tx);
1024 LOG.debug("Terminating service Entry for dpID {} : label : {} egress : {} installed successfully",
1025 dpId, label, groupId);
1028 public void createTerminatingServiceActions(Uint64 destDpId, Uint32 label, List<ActionInfo> actionsInfos,
1029 TypedWriteTransaction<Configuration> tx) {
1030 List<MatchInfo> mkMatches = new ArrayList<>();
1032 LOG.debug("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}",
1033 destDpId, label, actionsInfos);
1035 // Matching metadata
1036 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
1037 mkMatches.add(new MatchTunnelId(Uint64.valueOf(label.longValue())));
1039 List<InstructionInfo> mkInstructions = new ArrayList<>();
1040 mkInstructions.add(new InstructionApplyActions(actionsInfos));
1042 FlowEntity terminatingServiceTableFlowEntity =
1043 MDSALUtil.buildFlowEntity(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,
1044 getTableMissFlowRef(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE, label),
1045 FibConstants.DEFAULT_VPN_INTERNAL_TUNNEL_TABLE_PRIORITY,
1046 String.format("%s:%s", "TST Flow Entry ", label), 0, 0,
1047 Uint64.valueOf(COOKIE_TUNNEL.longValue() + label.longValue()),
1048 mkMatches, mkInstructions);
1050 FlowKey flowKey = new FlowKey(new FlowId(terminatingServiceTableFlowEntity.getFlowId()));
1052 FlowBuilder flowbld = terminatingServiceTableFlowEntity.getFlowBuilder();
1054 Node nodeDpn = FibUtil.buildDpnNode(terminatingServiceTableFlowEntity.getDpnId());
1055 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1056 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1057 .child(Table.class, new TableKey(terminatingServiceTableFlowEntity.getTableId()))
1058 .child(Flow.class, flowKey).build();
1059 tx.put(flowInstanceId, flowbld.build(), CREATE_MISSING_PARENTS);
1062 private void removeTunnelTableEntry(Uint64 dpId, Uint32 label, TypedWriteTransaction<Configuration> tx) {
1063 FlowEntity flowEntity;
1064 LOG.debug("remove terminatingServiceActions called with DpnId = {} and label = {}", dpId, label);
1065 List<MatchInfo> mkMatches = new ArrayList<>();
1066 // Matching metadata
1067 mkMatches.add(new MatchTunnelId(Uint64.valueOf(label.longValue())));
1068 flowEntity = MDSALUtil.buildFlowEntity(dpId,
1069 NwConstants.INTERNAL_TUNNEL_TABLE,
1070 getTableMissFlowRef(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, label),
1071 FibConstants.DEFAULT_VPN_INTERNAL_TUNNEL_TABLE_PRIORITY,
1072 String.format("%s:%s", "TST Flow Entry ", label), 0, 0,
1073 Uint64.valueOf(COOKIE_TUNNEL.longValue() + label.longValue()), mkMatches, null);
1074 Node nodeDpn = FibUtil.buildDpnNode(flowEntity.getDpnId());
1075 FlowKey flowKey = new FlowKey(new FlowId(flowEntity.getFlowId()));
1076 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1077 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1078 .child(Table.class, new TableKey(flowEntity.getTableId())).child(Flow.class, flowKey).build();
1080 tx.delete(flowInstanceId);
1081 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully", dpId, label);
1084 public List<Uint64> deleteLocalFibEntry(Uint32 vpnId, String rd, VrfEntry vrfEntry) {
1085 List<Uint64> returnLocalDpnId = new ArrayList<>();
1086 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1087 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1088 boolean shouldUpdateNonEcmpLocalNextHop = true;
1089 if (localNextHopInfo == null) {
1090 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1091 if (usedRds.size() > 1) {
1092 LOG.error("The extra route prefix {} is still present in some DPNs in vpn {} on rd {}",
1093 vrfEntry.getDestPrefix(), vpnName, rd);
1094 return returnLocalDpnId;
1096 String vpnRd = !usedRds.isEmpty() ? usedRds.get(0) : rd;
1097 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency
1099 Optional<Routes> extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1100 vpnName, vpnRd, vrfEntry.getDestPrefix());
1101 if (extraRouteOptional.isPresent()) {
1102 Routes extraRoute = extraRouteOptional.get();
1104 if (isIpv4Address(extraRoute.getNexthopIpList().get(0))) {
1105 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
1107 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
1109 if (extraRoute.getNexthopIpList().size() > 1) {
1110 shouldUpdateNonEcmpLocalNextHop = false;
1112 localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1113 if (localNextHopInfo != null) {
1114 String localNextHopIP = localNextHopInfo.getIpAddress();
1115 Uint64 dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP, vpnName, vpnId, rd,
1116 vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1117 if (!dpnId.equals(Uint64.ZERO)) {
1118 LOG.trace("Deleting ECMP group for prefix {}, dpn {}", vrfEntry.getDestPrefix(), dpnId);
1119 nextHopManager.deleteLoadBalancingNextHop(vpnId, dpnId, vrfEntry.getDestPrefix());
1120 returnLocalDpnId.add(dpnId);
1123 LOG.error("localNextHopInfo unavailable while deleting prefix {} with rds {}, primary rd {} in "
1124 + "vpn {}", vrfEntry.getDestPrefix(), usedRds, rd, vpnName);
1128 if (localNextHopInfo == null) {
1129 /* Imported VRF entry */
1130 java.util.Optional<Uint32> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1131 if (optionalLabel.isPresent()) {
1132 Uint32 label = optionalLabel.get();
1133 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1134 LabelRouteInfo lri = getLabelRouteInfo(label);
1135 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1136 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1137 prefixBuilder.setDpnId(lri.getDpnId());
1138 Uint64 dpnId = checkDeleteLocalFibEntry(prefixBuilder.build(), nextHopAddressList.get(0),
1139 vpnName, vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1140 if (!dpnId.equals(Uint64.ZERO)) {
1141 returnLocalDpnId.add(dpnId);
1148 LOG.debug("Obtained prefix to interface for rd {} prefix {}", rd, vrfEntry.getDestPrefix());
1149 String localNextHopIP = localNextHopInfo.getIpAddress();
1150 Uint64 dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP, vpnName, vpnId, rd, vrfEntry,
1151 shouldUpdateNonEcmpLocalNextHop);
1152 if (!dpnId.equals(Uint64.ZERO)) {
1153 returnLocalDpnId.add(dpnId);
1157 return returnLocalDpnId;
1160 private Uint64 checkDeleteLocalFibEntry(Prefixes localNextHopInfo, final String localNextHopIP,
1161 final String vpnName, final Uint32 vpnId, final String rd, final VrfEntry vrfEntry,
1162 boolean shouldUpdateNonEcmpLocalNextHop) {
1163 if (localNextHopInfo != null) {
1164 final Uint64 dpnId = localNextHopInfo.getDpnId();
1165 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
1166 LOG.debug("checkDeleteLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1167 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1170 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
1171 LOG.debug("checkDeleteLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1172 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1176 jobCoordinator.enqueueJob(FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix()),
1177 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1178 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1179 NwConstants.DEL_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1180 if (FibUtil.isBgpVpn(vpnName, rd)) {
1181 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1182 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1183 makeLFibTableEntry(dpnId, label, null /* instructions */, DEFAULT_FIB_FLOW_PRIORITY,
1184 NwConstants.DEL_FLOW, tx);
1185 removeTunnelTableEntry(dpnId, label, tx);
1190 //TODO: verify below adjacency call need to be optimized (?)
1191 //In case of the removal of the extra route, the loadbalancing group is updated
1192 if (shouldUpdateNonEcmpLocalNextHop) {
1193 baseVrfEntryHandler.deleteLocalAdjacency(dpnId, vpnId, localNextHopIP, vrfEntry.getDestPrefix());
1200 private void createRemoteFibEntry(final Uint64 remoteDpnId, final Uint32 vpnId, String rd,
1201 final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
1203 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1204 newTx -> createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx)), LOG,
1205 "Error creating remote FIB entry");
1209 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1210 LOG.debug("createremotefibentry: adding route {} for rd {} on remoteDpnId {}", vrfEntry.getDestPrefix(), rd,
1213 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.STATIC) {
1214 programRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, tx);
1217 // Handling static VRF entries
1218 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1219 List<Routes> vpnExtraRoutes =
1220 VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker, vpnName, usedRds, vrfEntry.getDestPrefix());
1221 if (!vpnExtraRoutes.isEmpty()) {
1222 programRemoteFibWithLoadBalancingGroups(remoteDpnId, vpnId, rd, vrfEntry, vpnExtraRoutes);
1224 // Program in case of other static VRF entries like floating IPs
1225 programRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, tx);
1229 // Allow deprecated TransactionRunner calls for now
1230 @SuppressWarnings("ForbidCertainMethod")
1231 private void programRemoteFibWithLoadBalancingGroups(final Uint64 remoteDpnId, final Uint32 vpnId, String rd,
1232 final VrfEntry vrfEntry, List<Routes> vpnExtraRoutes) {
1233 // create loadbalancing groups for extra routes only when the extra route is
1234 // present behind multiple VMs
1235 // Obtain the local routes for this particular dpn.
1236 java.util.Optional<Routes> routes = vpnExtraRoutes.stream().filter(route -> {
1237 Prefixes prefixToInterface =
1238 fibUtil.getPrefixToInterface(vpnId, FibUtil.getIpPrefix(route.getNexthopIpList().get(0)));
1239 if (prefixToInterface == null) {
1242 return remoteDpnId.equals(prefixToInterface.getDpnId());
1244 long groupId = nextHopManager.createNextHopGroups(vpnId, rd, remoteDpnId, vrfEntry,
1245 routes.isPresent() ? routes.get() : null, vpnExtraRoutes);
1246 if (groupId == FibConstants.INVALID_GROUP_ID) {
1247 LOG.error("Unable to create Group for local prefix {} on rd {} on Node {}", vrfEntry.getDestPrefix(), rd,
1251 List<ActionInfo> actionInfos = Collections.singletonList(new ActionGroup(groupId));
1252 List<InstructionInfo> instructions = Lists.newArrayList(new InstructionApplyActions(actionInfos));
1253 String jobKey = FibUtil.getCreateRemoteNextHopJobKey(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
1254 jobCoordinator.enqueueJob(jobKey,
1255 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(txn -> {
1256 baseVrfEntryHandler.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
1257 NwConstants.ADD_FLOW, txn, null);
1260 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
1263 private void programRemoteFibEntry(final Uint64 remoteDpnId, final Uint32 vpnId, String rd,
1264 final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
1265 List<AdjacencyResult> adjacencyResults = baseVrfEntryHandler.resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
1266 if (adjacencyResults.isEmpty()) {
1267 LOG.error("Could not get interface for route-paths: {} in vpn {} on DPN {}", vrfEntry.getRoutePaths(), rd,
1269 LOG.error("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
1272 baseVrfEntryHandler.programRemoteFib(remoteDpnId, vpnId, vrfEntry, TransactionAdapter.toWriteTransaction(tx),
1273 rd, adjacencyResults, null);
1274 LOG.debug("Successfully programmed FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
1277 protected void cleanUpOpDataForFib(Uint32 vpnId, String primaryRd, final VrfEntry vrfEntry) {
1278 /* Get interface info from prefix to interface mapping;
1279 Use the interface info to get the corresponding vpn interface op DS entry,
1280 remove the adjacency corresponding to this fib entry.
1281 If adjacency removed is the last adjacency, clean up the following:
1282 - vpn interface from dpntovpn list, dpn if last vpn interface on dpn
1283 - prefix to interface entry
1284 - vpn interface op DS
1286 LOG.debug("Cleanup of prefix {} in VPN {}", vrfEntry.getDestPrefix(), vpnId);
1287 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1288 if (prefixInfo == null) {
1289 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1290 String usedRd = usedRds.isEmpty() ? primaryRd : usedRds.get(0);
1291 Routes extraRoute = baseVrfEntryHandler.getVpnToExtraroute(vpnId, usedRd, vrfEntry.getDestPrefix());
1292 if (extraRoute != null && extraRoute.getNexthopIpList() != null) {
1293 for (String nextHopIp : extraRoute.getNexthopIpList()) {
1294 LOG.debug("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
1295 if (nextHopIp != null) {
1297 if (isIpv4Address(nextHopIp)) {
1298 ipPrefix = nextHopIp + NwConstants.IPV4PREFIX;
1300 ipPrefix = nextHopIp + NwConstants.IPV6PREFIX;
1302 prefixInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1303 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1307 if (prefixInfo == null) {
1308 java.util.Optional<Uint32> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1309 if (optionalLabel.isPresent()) {
1310 Uint32 label = optionalLabel.get();
1311 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1312 LabelRouteInfo lri = getLabelRouteInfo(label);
1313 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1314 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1315 prefixBuilder.setDpnId(lri.getDpnId());
1316 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
1317 prefixBuilder.setIpAddress(lri.getPrefix());
1318 prefixInfo = prefixBuilder.build();
1319 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
1320 label, prefixInfo.getVpnInterfaceName(), lri.getDpnId());
1321 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1326 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, null /*Routes*/);
1330 private void checkCleanUpOpDataForFib(final Prefixes prefixInfo, final Uint32 vpnId, final String rd,
1331 final VrfEntry vrfEntry, @Nullable final Routes extraRoute) {
1333 if (prefixInfo == null) {
1334 LOG.error("Cleanup VPN Data Failed as unable to find prefix Info for prefix {} VpnId {} rd {}",
1335 vrfEntry.getDestPrefix(), vpnId, rd);
1336 return; //Don't have any info for this prefix (shouldn't happen); need to return
1339 if (Prefixes.PrefixCue.Nat.equals(prefixInfo.getPrefixCue())) {
1340 LOG.debug("NAT Prefix {} with vpnId {} rd {}. Skip FIB processing",
1341 vrfEntry.getDestPrefix(), vpnId, rd);
1345 String ifName = prefixInfo.getVpnInterfaceName();
1346 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName,
1347 new CleanupVpnInterfaceWorker(prefixInfo, vpnId, rd, vrfEntry, extraRoute));
1350 private class CleanupVpnInterfaceWorker implements Callable<List<ListenableFuture<Void>>> {
1351 Prefixes prefixInfo;
1357 CleanupVpnInterfaceWorker(final Prefixes prefixInfo, final Uint32 vpnId, final String rd,
1358 final VrfEntry vrfEntry, final Routes extraRoute) {
1359 this.prefixInfo = prefixInfo;
1362 this.vrfEntry = vrfEntry;
1363 this.extraRoute = extraRoute;
1367 public List<ListenableFuture<Void>> call() {
1368 // If another renderer(for eg : CSS) needs to be supported, check can be performed here
1369 // to call the respective helpers.
1370 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
1371 //First Cleanup LabelRouteInfo
1372 //TODO(KIRAN) : Move the below block when addressing iRT/eRT for L3VPN Over VxLan
1373 LOG.debug("cleanupVpnInterfaceWorker: rd {} prefix {}", rd, prefixInfo.getIpAddress());
1374 if (VrfEntry.EncapType.Mplsgre.equals(vrfEntry.getEncapType())) {
1375 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1376 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1377 final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
1378 final ReentrantLock lock = lockFor(lriKey);
1381 LabelRouteInfo lri = getLabelRouteInfo(lriKey);
1382 if (lri != null && Objects.equals(lri.getPrefix(), vrfEntry.getDestPrefix())
1383 && nextHopAddressList.contains(lri.getNextHopIpList().get(0))) {
1384 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1385 fibUtil.getVpnInstanceOpData(rd);
1386 String vpnInstanceName = "";
1387 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1388 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1390 boolean lriRemoved = deleteLabelRouteInfo(lri, vpnInstanceName, tx);
1392 String parentRd = lri.getParentVpnRd();
1393 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1394 parentRd, vrfEntry.getDestPrefix()));
1397 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1398 rd, vrfEntry.getDestPrefix()));
1405 String ifName = prefixInfo.getVpnInterfaceName();
1406 Optional<String> optVpnName = fibUtil.getVpnNameFromRd(rd);
1407 String vpnName = null;
1409 if (Prefixes.PrefixCue.PhysNetFunc.equals(prefixInfo.getPrefixCue())) {
1410 // Get vpnId for rd = networkId since op vpnInterface will be pointing to rd = networkId
1411 Optional<String> vpnNameOpt = fibUtil.getVpnNameFromRd(vrfEntry.getParentVpnRd());
1412 if (vpnNameOpt.isPresent()) {
1413 vpnId = fibUtil.getVpnId(vpnNameOpt.get());
1416 if (optVpnName.isPresent()) {
1417 vpnName = optVpnName.get();
1418 Optional<VpnInterfaceOpDataEntry> opVpnInterface = tx
1419 .read(FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName)).get();
1420 if (opVpnInterface.isPresent()) {
1421 Uint32 associatedVpnId = fibUtil.getVpnId(vpnName);
1422 if (!Objects.equals(vpnId, associatedVpnId)) {
1423 LOG.warn("Prefixes {} are associated with different vpn instance with id {} rather than {}",
1424 vrfEntry.getDestPrefix(), associatedVpnId, vpnId);
1425 LOG.warn("Not proceeding with Cleanup op data for prefix {}", vrfEntry.getDestPrefix());
1428 LOG.debug("Processing cleanup of prefix {} associated with vpn {}",
1429 vrfEntry.getDestPrefix(), associatedVpnId);
1433 if (extraRoute != null) {
1434 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1435 //Only one used Rd present in case of removal event
1436 String usedRd = usedRds.get(0);
1437 if (optVpnName.isPresent()) {
1438 tx.delete(BaseVrfEntryHandler.getVpnToExtrarouteIdentifier(vpnName, usedRd,
1439 vrfEntry.getDestPrefix()));
1440 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, configTx ->
1441 configTx.delete(VpnExtraRouteHelper.getUsedRdsIdentifier(vpnId, vrfEntry.getDestPrefix())));
1444 handleAdjacencyAndVpnOpInterfaceDeletion(vrfEntry, ifName, vpnName, tx);
1450 * Check all the adjacency in VpnInterfaceOpData and decide whether to delete the entire interface or only adj.
1451 * Remove Adjacency from VPNInterfaceOpData.
1452 * if Adjacency != primary.
1453 * if Adjacency == primary , then mark it for deletion.
1454 * Remove entire VPNinterfaceOpData Entry.
1455 * if sie of Adjacency <= 2 and all are marked for deletion , delete the entire VPNinterface Op entry.
1456 * @param vrfEntry - VrfEntry removed
1457 * @param ifName - Interface name from VRFentry
1458 * @param vpnName - VPN name of corresponding VRF
1459 * @param tx - ReadWrite Tx
1461 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
1462 justification = "https://github.com/spotbugs/spotbugs/issues/811")
1463 private void handleAdjacencyAndVpnOpInterfaceDeletion(VrfEntry vrfEntry, String ifName, String vpnName,
1464 TypedReadWriteTransaction<Operational> tx)
1465 throws ExecutionException, InterruptedException {
1466 InstanceIdentifier<Adjacency> adjacencyIid =
1467 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix());
1468 Optional<Adjacency> adjacencyOptional = tx.read(adjacencyIid).get();
1469 if (adjacencyOptional.isPresent()) {
1470 if (adjacencyOptional.get().getAdjacencyType() != Adjacency.AdjacencyType.PrimaryAdjacency) {
1471 tx.delete(FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix()));
1473 tx.merge(adjacencyIid,
1474 new AdjacencyBuilder(adjacencyOptional.get()).setMarkedForDeletion(true).build());
1478 Optional<AdjacenciesOp> optAdjacencies = tx.read(FibUtil.getAdjListPathOp(ifName, vpnName)).get();
1480 if (!optAdjacencies.isPresent() || optAdjacencies.get().getAdjacency() == null) {
1484 @NonNull List<Adjacency> adjacencies = optAdjacencies.get().nonnullAdjacency();
1485 if (adjacencies.size() <= 2
1486 && adjacencies.stream().allMatch(adjacency ->
1487 adjacency.getAdjacencyType() == Adjacency.AdjacencyType.PrimaryAdjacency
1488 && adjacency.isMarkedForDeletion() != null
1489 && adjacency.isMarkedForDeletion()
1491 LOG.info("Clean up vpn interface {} to vpn {} list.", ifName, vpnName);
1492 tx.delete(FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1496 private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
1497 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
1498 final String rd = vrfTableKey.getRouteDistinguisher();
1499 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
1500 if (vpnInstance == null) {
1501 LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
1504 final Collection<VpnToDpnList> vpnToDpnList;
1505 if (vrfEntry.getParentVpnRd() != null
1506 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
1507 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
1508 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
1509 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
1510 vpnInstance.getVpnToDpnList();
1511 LOG.info("deleteFibEntries: Processing deletion of PNF FIB entry with rd {} prefix {}",
1512 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
1514 vpnToDpnList = vpnInstance.getVpnToDpnList();
1517 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1518 final java.util.Optional<Uint32> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1519 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1520 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1521 if (subnetRoute != null) {
1522 long elanTag = subnetRoute.getElantag().toJava();
1523 LOG.trace("SUBNETROUTE: deleteFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
1524 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
1525 if (vpnToDpnList != null) {
1526 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1527 () -> Collections.singletonList(
1528 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1529 for (final VpnToDpnList curDpn : vpnToDpnList) {
1531 baseVrfEntryHandler.makeConnectedRoute(curDpn.getDpnId(),
1532 vpnInstance.getVpnId(),
1533 vrfEntry, vrfTableKey.getRouteDistinguisher(), null, NwConstants.DEL_FLOW,
1534 TransactionAdapter.toWriteTransaction(tx), null);
1535 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1536 optionalLabel.ifPresent(label -> makeLFibTableEntry(curDpn.getDpnId(),
1537 label, null, DEFAULT_FIB_FLOW_PRIORITY, NwConstants.DEL_FLOW, tx));
1540 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd,
1541 vpnInstance.getVpnId(),
1542 vrfEntry, NwConstants.DEL_FLOW, tx);
1546 optionalLabel.ifPresent(label -> {
1547 final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
1548 final ReentrantLock lock = lockFor(lriKey);
1551 LabelRouteInfo lri = getLabelRouteInfo(lriKey);
1552 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1553 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1554 fibUtil.getVpnInstanceOpData(rd);
1555 String vpnInstanceName = "";
1556 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1557 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1559 boolean lriRemoved = this.deleteLabelRouteInfo(lri, vpnInstanceName, null);
1561 String parentRd = lri.getParentVpnRd();
1562 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1563 parentRd, vrfEntry.getDestPrefix()));
1564 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}"
1565 + " as labelRouteInfo cleared", label, rd, vrfEntry.getDestPrefix());
1568 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1569 rd, vrfEntry.getDestPrefix()));
1570 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}",
1571 label, rd, vrfEntry.getDestPrefix());
1580 final List<Uint64> localDpnIdList = deleteLocalFibEntry(vpnInstance.getVpnId(),
1581 vrfTableKey.getRouteDistinguisher(), vrfEntry);
1582 if (vpnToDpnList != null) {
1583 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1584 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1586 Optional<Routes> extraRouteOptional;
1587 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
1588 if (usedRds != null && !usedRds.isEmpty()) {
1589 if (usedRds.size() > 1) {
1590 LOG.error("The extra route prefix is still present in some DPNs");
1593 // The first rd is retrieved from usedrds as Only 1 rd would be present as extra route prefix
1594 //is not present in any other DPN
1595 extraRouteOptional = VpnExtraRouteHelper
1596 .getVpnExtraroutes(dataBroker, vpnName, usedRds.get(0), vrfEntry.getDestPrefix());
1599 extraRouteOptional = Optional.absent();
1602 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1603 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1604 if (localDpnIdList.size() <= 0) {
1605 for (VpnToDpnList curDpn : vpnToDpnList) {
1606 baseVrfEntryHandler.deleteRemoteRoute(Uint64.ZERO, curDpn.getDpnId(),
1607 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
1608 TransactionAdapter.toWriteTransaction(tx));
1611 for (Uint64 localDpnId : localDpnIdList) {
1612 for (VpnToDpnList curDpn : vpnToDpnList) {
1613 if (!Objects.equals(curDpn.getDpnId(), localDpnId)) {
1614 baseVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
1615 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
1616 TransactionAdapter.toWriteTransaction(tx));
1621 if (extraRouteOptional.isPresent()) {
1622 //Remove select groups only for extra-routes
1623 nextHopManager.removeNextHopPointer(nextHopManager
1624 .getRemoteSelectGroupKey(vpnInstance.getVpnId(), vrfEntry.getDestPrefix()));
1625 nextHopManager.removeNextHopPointer(nextHopManager
1626 .getLocalSelectGroupKey(vpnInstance.getVpnId(), vrfEntry.getDestPrefix()));
1631 //The flow/group entry has been deleted from config DS; need to clean up associated operational
1632 //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
1633 cleanUpOpDataForFib(vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(), vrfEntry);
1635 // Remove all fib entries configured due to interVpnLink, when nexthop is the opposite endPoint
1636 // of the interVpnLink.
1637 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
1638 if (optVpnUuid.isPresent()) {
1639 String vpnUuid = optVpnUuid.get();
1640 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
1641 Optional<InterVpnLinkDataComposite> optInterVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid);
1642 if (optInterVpnLink.isPresent()) {
1643 InterVpnLinkDataComposite interVpnLink = optInterVpnLink.get();
1644 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
1645 // This is route that points to the other endpoint of an InterVpnLink
1646 // In that case, we should look for the FIB table pointing to
1647 // LPortDispatcher table and remove it.
1648 removeInterVPNLinkRouteFlows(interVpnLink, vpnUuid, vrfEntry);
1656 private void makeLFibTableEntry(Uint64 dpId, Uint32 label, @Nullable List<InstructionInfo> instructions,
1657 int priority, int addOrRemove, TypedWriteTransaction<Configuration> tx) {
1659 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1660 newTx -> makeLFibTableEntry(dpId, label, instructions, priority, addOrRemove, newTx)), LOG,
1661 "Error making LFIB table entry");
1665 List<MatchInfo> matches = new ArrayList<>();
1666 matches.add(MatchEthernetType.MPLS_UNICAST);
1667 matches.add(new MatchMplsLabel(label.longValue()));
1669 // Install the flow entry in L3_LFIB_TABLE
1670 String flowRef = FibUtil.getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, label, priority);
1672 FlowEntity flowEntity;
1673 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef, priority, flowRef, 0, 0,
1674 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
1675 Flow flow = flowEntity.getFlowBuilder().build();
1676 String flowId = flowEntity.getFlowId();
1677 FlowKey flowKey = new FlowKey(new FlowId(flowId));
1678 Node nodeDpn = FibUtil.buildDpnNode(dpId);
1679 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1680 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1681 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
1683 if (addOrRemove == NwConstants.ADD_FLOW) {
1684 tx.put(flowInstanceId, flow, CREATE_MISSING_PARENTS);
1686 tx.delete(flowInstanceId);
1689 LOG.debug("LFIB Entry for dpID {} : label : {} instructions {} : key {} {} successfully",
1690 dpId, label, instructions, flowKey, NwConstants.ADD_FLOW == addOrRemove ? "ADDED" : "REMOVED");
1693 public void populateFibOnNewDpn(final Uint64 dpnId, final Uint32 vpnId, final String rd,
1694 final FutureCallback<List<Void>> callback) {
1695 LOG.trace("New dpn {} for vpn {} : populateFibOnNewDpn", dpnId, rd);
1696 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1698 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1699 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1700 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1701 LogicalDatastoreType.CONFIGURATION, id);
1702 List<ListenableFuture<Void>> futures = new ArrayList<>();
1703 if (!vrfTable.isPresent()) {
1704 LOG.info("populateFibOnNewDpn: dpn: {}: VRF Table not yet available for RD {}", dpnId, rd);
1705 if (callback != null) {
1706 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1707 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1712 final ReentrantLock lock = lockFor(vpnInstance);
1715 futures.add(retryingTxRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
1716 for (final VrfEntry vrfEntry : vrfTable.get().nonnullVrfEntry()) {
1717 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1718 if (subnetRoute != null) {
1719 long elanTag = subnetRoute.getElantag().toJava();
1720 installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, tx);
1721 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry, NwConstants.ADD_FLOW,
1725 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1726 if (routerInt != null) {
1727 LOG.trace("Router augmented vrfentry found rd:{}, uuid:{}, ip:{}, mac:{}",
1728 rd, routerInt.getUuid(), routerInt.getIpAddress(), routerInt.getMacAddress());
1729 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1730 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1731 NwConstants.ADD_FLOW);
1734 //Handle local flow creation for imports
1735 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1736 java.util.Optional<Uint32> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1737 if (optionalLabel.isPresent()) {
1738 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1739 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1740 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList, lri)) {
1741 if (Objects.equals(lri.getDpnId(), dpnId)) {
1743 int etherType = NWUtil.getEtherTypeFromIpPrefix(
1744 vrfEntry.getDestPrefix());
1745 createLocalFibEntry(vpnId, rd, vrfEntry, etherType);
1746 } catch (IllegalArgumentException ex) {
1747 LOG.warn("Unable to get etherType for IP Prefix {}",
1748 vrfEntry.getDestPrefix());
1755 boolean shouldCreateRemoteFibEntry = shouldCreateFibEntryForVrfAndVpnIdOnDpn(vpnId,
1757 if (shouldCreateRemoteFibEntry) {
1758 LOG.trace("Will create remote FIB entry for vrfEntry {} on DPN {}", vrfEntry, dpnId);
1759 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1760 List<SubTransaction> txnObjects = new ArrayList<>();
1761 bgpRouteVrfEntryHandler.createRemoteFibEntry(dpnId, vpnId,
1762 vrfTable.get().getRouteDistinguisher(), vrfEntry,
1763 TransactionAdapter.toWriteTransaction(tx), txnObjects);
1765 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
1771 if (callback != null) {
1772 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1773 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1782 public void populateExternalRoutesOnDpn(final Uint64 dpnId, final Uint32 vpnId, final String rd,
1783 final String localNextHopIp, final String remoteNextHopIp) {
1784 LOG.trace("populateExternalRoutesOnDpn : dpn {}, vpn {}, rd {}, localNexthopIp {} , remoteNextHopIp {} ",
1785 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1786 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1787 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1788 List<SubTransaction> txnObjects = new ArrayList<>();
1789 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1790 if (vrfTable.isPresent()) {
1791 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1792 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1793 final ReentrantLock lock = lockFor(vpnInstance);
1796 vrfTable.get().nonnullVrfEntry().stream()
1797 .filter(vrfEntry -> RouteOrigin.BGP == RouteOrigin.value(vrfEntry.getOrigin()))
1798 .forEach(bgpRouteVrfEntryHandler.getConsumerForCreatingRemoteFib(dpnId, vpnId,
1799 rd, remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx), txnObjects));
1807 public void manageRemoteRouteOnDPN(final boolean action,
1808 final Uint64 localDpnId,
1811 final String destPrefix,
1812 final String destTepIp,
1813 final Uint32 label) {
1814 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1816 if (vpnInstance == null) {
1817 LOG.error("VpnInstance for rd {} not present for prefix {}", rd, destPrefix);
1821 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, localDpnId),
1822 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1823 final ReentrantLock lock = lockFor(vpnInstance);
1826 VrfTablesKey vrfTablesKey = new VrfTablesKey(rd);
1827 VrfEntry vrfEntry = getVrfEntry(dataBroker, rd, destPrefix);
1828 if (vrfEntry == null) {
1831 LOG.trace("manageRemoteRouteOnDPN :: action {}, DpnId {}, vpnId {}, rd {}, destPfx {}",
1832 action, localDpnId, vpnId, rd, destPrefix);
1833 List<RoutePaths> routePathList = vrfEntry.getRoutePaths();
1834 VrfEntry modVrfEntry;
1835 if (routePathList == null || routePathList.isEmpty()) {
1836 modVrfEntry = FibHelper.getVrfEntryBuilder(vrfEntry, label,
1837 Collections.singletonList(destTepIp),
1838 RouteOrigin.value(vrfEntry.getOrigin()), null /* parentVpnRd */).build();
1840 modVrfEntry = vrfEntry;
1844 LOG.trace("manageRemoteRouteOnDPN updated(add) vrfEntry :: {}", modVrfEntry);
1845 createRemoteFibEntry(localDpnId, vpnId, vrfTablesKey.getRouteDistinguisher(),
1848 LOG.trace("manageRemoteRouteOnDPN updated(remove) vrfEntry :: {}", modVrfEntry);
1849 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1850 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1851 if (usedRds.size() > 1) {
1852 LOG.debug("The extra route prefix is still present in some DPNs");
1855 //Is this fib route an extra route? If yes, get the nexthop which would be
1856 //an adjacency in the vpn
1857 Optional<Routes> extraRouteOptional = Optional.absent();
1858 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.STATIC && usedRds.size() != 0) {
1859 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1860 fibUtil.getVpnNameFromId(vpnInstance.getVpnId()),
1861 usedRds.get(0), vrfEntry.getDestPrefix());
1863 baseVrfEntryHandler.deleteRemoteRoute(null, localDpnId, vpnId, vrfTablesKey, modVrfEntry,
1864 extraRouteOptional, TransactionAdapter.toWriteTransaction(tx));
1872 public void cleanUpDpnForVpn(final Uint64 dpnId, final Uint32 vpnId, final String rd,
1873 final FutureCallback<List<Void>> callback) {
1874 LOG.trace("cleanUpDpnForVpn: Remove dpn {} for vpn {} : cleanUpDpnForVpn", dpnId, rd);
1875 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1877 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1878 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1879 List<SubTransaction> txnObjects = new ArrayList<>();
1880 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1881 LogicalDatastoreType.CONFIGURATION, id);
1882 List<ListenableFuture<Void>> futures = new ArrayList<>();
1883 if (!vrfTable.isPresent()) {
1884 LOG.error("cleanUpDpnForVpn: VRF Table not available for RD {}", rd);
1885 if (callback != null) {
1886 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1887 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1891 final ReentrantLock lock = lockFor(vpnInstance);
1894 futures.add(retryingTxRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1895 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1896 for (final VrfEntry vrfEntry : vrfTable.get().nonnullVrfEntry()) {
1897 /* parentRd is only filled for external PNF cases where the interface on the external
1898 * network VPN are used to cleanup the flows. For all other cases, use "rd" for
1899 * #fibUtil.isInterfacePresentInDpn().
1901 String parentRd = vrfEntry.getParentVpnRd() != null ? vrfEntry.getParentVpnRd()
1903 /* Handle subnet routes here */
1904 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1905 if (subnetRoute != null && !fibUtil
1906 .isInterfacePresentInDpn(parentRd, dpnId)) {
1907 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Cleaning subnetroute {} on dpn {}"
1908 + " for vpn {}", vrfEntry.getDestPrefix(), dpnId, rd);
1909 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1910 NwConstants.DEL_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1911 List<RoutePaths> routePaths = vrfEntry.getRoutePaths();
1912 if (routePaths != null) {
1913 for (RoutePaths routePath : routePaths) {
1914 makeLFibTableEntry(dpnId, routePath.getLabel(), null,
1915 DEFAULT_FIB_FLOW_PRIORITY,
1916 NwConstants.DEL_FLOW, tx);
1917 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Released subnetroute label {}"
1918 + " for rd {} prefix {}", routePath.getLabel(), rd,
1919 vrfEntry.getDestPrefix());
1922 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry,
1923 NwConstants.DEL_FLOW, tx);
1926 // ping responder for router interfaces
1927 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1928 if (routerInt != null) {
1929 LOG.trace("Router augmented vrfentry found for rd:{}, uuid:{}, ip:{}, mac:{}",
1930 rd, routerInt.getUuid(), routerInt.getIpAddress(),
1931 routerInt.getMacAddress());
1932 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1933 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1934 NwConstants.DEL_FLOW);
1937 //Handle local flow deletion for imports
1938 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1939 java.util.Optional<Uint32> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1940 if (optionalLabel.isPresent()) {
1941 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1942 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1943 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList,
1944 lri) && Objects.equals(lri.getDpnId(), dpnId)) {
1945 deleteLocalFibEntry(vpnId, rd, vrfEntry);
1949 // Passing null as we don't know the dpn
1950 // to which prefix is attached at this point
1951 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1952 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1953 Optional<Routes> extraRouteOptional;
1954 //Is this fib route an extra route? If yes, get the nexthop which would be
1955 //an adjacency in the vpn
1956 if (usedRds != null && !usedRds.isEmpty()) {
1957 if (usedRds.size() > 1) {
1958 LOG.error("The extra route prefix is still present in some DPNs");
1961 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
1962 usedRds.get(0), vrfEntry.getDestPrefix());
1966 extraRouteOptional = Optional.absent();
1968 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1969 bgpRouteVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1970 vrfTable.get().key(), vrfEntry, extraRouteOptional,
1971 TransactionAdapter.toWriteTransaction(tx), txnObjects);
1973 if (subnetRoute == null || !fibUtil
1974 .isInterfacePresentInDpn(parentRd, dpnId)) {
1975 baseVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1976 vrfTable.get().key(), vrfEntry, extraRouteOptional,
1977 TransactionAdapter.toWriteTransaction(tx));
1985 if (callback != null) {
1986 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1987 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1993 public void cleanUpExternalRoutesOnDpn(final Uint64 dpnId, final Uint32 vpnId, final String rd,
1994 final String localNextHopIp, final String remoteNextHopIp) {
1995 LOG.trace("cleanUpExternalRoutesOnDpn : cleanup remote routes on dpn {} for vpn {}, rd {}, "
1996 + " localNexthopIp {} , remoteNexhtHopIp {}",
1997 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1998 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1999 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
2000 List<SubTransaction> txnObjects = new ArrayList<>();
2001 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
2002 if (vrfTable.isPresent()) {
2003 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
2005 final ReentrantLock lock = lockFor(vpnInstance);
2008 return Collections.singletonList(
2009 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
2010 tx -> vrfTable.get().nonnullVrfEntry().stream()
2011 .filter(vrfEntry -> RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP)
2012 .forEach(bgpRouteVrfEntryHandler.getConsumerForDeletingRemoteFib(dpnId, vpnId,
2013 remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx),
2022 public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
2023 InstanceIdentifierBuilder<VrfTables> idBuilder =
2024 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
2025 return idBuilder.build();
2028 private String getInterVpnFibFlowRef(String interVpnLinkName, String prefix, String nextHop) {
2029 return FLOWID_PREFIX + interVpnLinkName + NwConstants.FLOWID_SEPARATOR + prefix + NwConstants
2030 .FLOWID_SEPARATOR + nextHop;
2033 private String getTableMissFlowRef(Uint64 dpnId, short tableId, Uint32 tableMiss) {
2034 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR
2035 + tableMiss + FLOWID_PREFIX;
2039 private VrfEntry getVrfEntry(DataBroker broker, String rd, String ipPrefix) {
2040 InstanceIdentifier<VrfEntry> vrfEntryId = InstanceIdentifier.builder(FibEntries.class)
2041 .child(VrfTables.class, new VrfTablesKey(rd))
2042 .child(VrfEntry.class, new VrfEntryKey(ipPrefix)).build();
2043 Optional<VrfEntry> vrfEntry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
2044 if (vrfEntry.isPresent()) {
2045 return vrfEntry.get();
2050 public void removeInterVPNLinkRouteFlows(final InterVpnLinkDataComposite interVpnLink,
2051 final String vpnName,
2052 final VrfEntry vrfEntry) {
2053 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null && vrfEntry.getRoutePaths().size() == 1);
2055 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
2056 List<Uint64> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
2058 if (targetDpns.isEmpty()) {
2059 LOG.warn("Could not find DPNs for VPN {} in InterVpnLink {}", vpnName, interVpnLinkName);
2063 java.util.Optional<String> optNextHop = FibUtil.getFirstNextHopAddress(vrfEntry);
2064 java.util.Optional<Uint32> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
2068 optNextHop.ifPresent(nextHop -> {
2069 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, vrfEntry.getDestPrefix(), nextHop);
2070 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
2071 Flow flow = new FlowBuilder().withKey(flowKey).setId(new FlowId(flowRef))
2072 .setTableId(NwConstants.L3_FIB_TABLE).setFlowName(flowRef).build();
2074 LOG.trace("Removing flow in FIB table for interVpnLink {} key {}", interVpnLinkName, flowRef);
2075 for (Uint64 dpId : targetDpns) {
2076 LOG.debug("Removing flow: VrfEntry=[prefix={} nexthop={}] dpn {} for InterVpnLink {} in FIB",
2077 vrfEntry.getDestPrefix(), nextHop, dpId, interVpnLinkName);
2079 mdsalManager.removeFlow(dpId, flow);
2085 optLabel.ifPresent(label -> {
2086 LOG.trace("Removing flow in FIB table for interVpnLink {}", interVpnLinkName);
2088 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
2089 for (Uint64 dpId : targetDpns) {
2090 LOG.debug("Removing flow: VrfEntry=[prefix={} label={}] dpn {} for InterVpnLink {} in LFIB",
2091 vrfEntry.getDestPrefix(), label, dpId, interVpnLinkName);
2092 makeLFibTableEntry(dpId, label, /*instructions*/null, LFIB_INTERVPN_PRIORITY,
2093 NwConstants.DEL_FLOW, tx);
2095 }), LOG, "Error removing flows");
2099 private static boolean isPrefixAndNextHopPresentInLri(String prefix,
2100 List<String> nextHopAddressList, LabelRouteInfo lri) {
2101 return lri != null && Objects.equals(lri.getPrefix(), prefix)
2102 && nextHopAddressList.contains(lri.getNextHopIpList().get(0));
2105 private boolean shouldCreateFibEntryForVrfAndVpnIdOnDpn(Uint32 vpnId, VrfEntry vrfEntry, Uint64 dpnId) {
2106 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
2110 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
2111 if (prefix != null) {
2112 Uint64 prefixDpnId = prefix.getDpnId();
2113 if (dpnId.equals(prefixDpnId)) {
2114 LOG.trace("Should not create remote FIB entry for vrfEntry {} on DPN {}",
2122 private static ReentrantLock lockFor(final VpnInstanceOpDataEntry vpnInstance) {
2123 // FIXME: use vpnInstance.key() instead?
2124 return JvmGlobalLocks.getLockForString(vpnInstance.getVpnInstanceName());
2127 private static ReentrantLock lockFor(LabelRouteInfoKey label) {
2128 return JvmGlobalLocks.getLockFor(label);