2 * Copyright © 2015, 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netvirt.fibmanager;
10 import static org.opendaylight.controller.md.sal.binding.api.WriteTransaction.CREATE_MISSING_PARENTS;
11 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
13 import static org.opendaylight.genius.mdsalutil.NWUtil.isIpv4Address;
14 import static org.opendaylight.netvirt.fibmanager.FibUtil.nullToEmpty;
16 import com.google.common.base.Optional;
17 import com.google.common.base.Preconditions;
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture;
21 import com.google.common.util.concurrent.MoreExecutors;
22 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
23 import java.math.BigInteger;
24 import java.net.InetAddress;
25 import java.net.UnknownHostException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.Objects;
32 import java.util.concurrent.Callable;
33 import java.util.concurrent.CopyOnWriteArrayList;
34 import java.util.concurrent.ExecutionException;
35 import javax.annotation.Nullable;
36 import javax.annotation.PostConstruct;
37 import javax.inject.Inject;
38 import javax.inject.Singleton;
39 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
40 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
41 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
42 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
43 import org.opendaylight.genius.infra.Datastore.Configuration;
44 import org.opendaylight.genius.infra.Datastore.Operational;
45 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
46 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
47 import org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner;
48 import org.opendaylight.genius.infra.TransactionAdapter;
49 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
50 import org.opendaylight.genius.infra.TypedWriteTransaction;
51 import org.opendaylight.genius.mdsalutil.ActionInfo;
52 import org.opendaylight.genius.mdsalutil.FlowEntity;
53 import org.opendaylight.genius.mdsalutil.InstructionInfo;
54 import org.opendaylight.genius.mdsalutil.MDSALUtil;
55 import org.opendaylight.genius.mdsalutil.MatchInfo;
56 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
57 import org.opendaylight.genius.mdsalutil.NWUtil;
58 import org.opendaylight.genius.mdsalutil.NwConstants;
59 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
60 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
61 import org.opendaylight.genius.mdsalutil.actions.ActionPopMpls;
62 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
63 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
64 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
65 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
66 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
67 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
68 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
69 import org.opendaylight.genius.mdsalutil.matches.MatchMplsLabel;
70 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
71 import org.opendaylight.genius.utils.ServiceIndex;
72 import org.opendaylight.genius.utils.batching.SubTransaction;
73 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
74 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
75 import org.opendaylight.netvirt.elanmanager.api.IElanService;
76 import org.opendaylight.netvirt.fibmanager.NexthopManager.AdjacencyResult;
77 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
78 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
79 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
80 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
81 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
82 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
83 import org.opendaylight.serviceutils.upgrade.UpgradeState;
84 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.LabelRouteMap;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.RouterInterface;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRoute;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfo;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoBuilder;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoKey;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOp;
108 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
109 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
110 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
111 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.PrefixesBuilder;
112 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
113 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
114 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
115 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
116 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState.State;
117 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
118 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
119 import org.slf4j.Logger;
120 import org.slf4j.LoggerFactory;
124 public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry, VrfEntryListener> {
126 private static final Logger LOG = LoggerFactory.getLogger(VrfEntryListener.class);
127 private static final String FLOWID_PREFIX = "L3.";
128 private static final BigInteger COOKIE_VM_FIB_TABLE = new BigInteger("8000003", 16);
129 private static final int DEFAULT_FIB_FLOW_PRIORITY = 10;
130 private static final int IPV4_ADDR_PREFIX_LENGTH = 32;
131 private static final int LFIB_INTERVPN_PRIORITY = 15;
132 public static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
133 private static final int MAX_RETRIES = 3;
134 private static final BigInteger COOKIE_TABLE_MISS = new BigInteger("8000004", 16);
136 private final DataBroker dataBroker;
137 private final ManagedNewTransactionRunner txRunner;
138 private final RetryingManagedNewTransactionRunner retryingTxRunner;
139 private final IMdsalApiManager mdsalManager;
140 private final NexthopManager nextHopManager;
141 private final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler;
142 private final BaseVrfEntryHandler baseVrfEntryHandler;
143 private final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler;
144 private final JobCoordinator jobCoordinator;
145 private final IElanService elanManager;
146 private final FibUtil fibUtil;
147 private final InterVpnLinkCache interVpnLinkCache;
148 private final List<AutoCloseable> closeables = new CopyOnWriteArrayList<>();
149 private final UpgradeState upgradeState;
150 private final DataTreeEventCallbackRegistrar eventCallbacks;
153 public VrfEntryListener(final DataBroker dataBroker, final IMdsalApiManager mdsalApiManager,
154 final NexthopManager nexthopManager,
155 final IElanService elanManager,
156 final BaseVrfEntryHandler vrfEntryHandler,
157 final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler,
158 final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler,
159 final JobCoordinator jobCoordinator,
160 final FibUtil fibUtil,
161 final InterVpnLinkCache interVpnLinkCache,
162 final UpgradeState upgradeState,
163 final DataTreeEventCallbackRegistrar eventCallbacks) {
164 super(VrfEntry.class, VrfEntryListener.class);
165 this.dataBroker = dataBroker;
166 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
167 this.retryingTxRunner = new RetryingManagedNewTransactionRunner(dataBroker, MAX_RETRIES);
168 this.mdsalManager = mdsalApiManager;
169 this.nextHopManager = nexthopManager;
170 this.elanManager = elanManager;
171 this.baseVrfEntryHandler = vrfEntryHandler;
172 this.bgpRouteVrfEntryHandler = bgpRouteVrfEntryHandler;
173 this.routerInterfaceVrfEntryHandler = routerInterfaceVrfEntryHandler;
174 this.jobCoordinator = jobCoordinator;
175 this.fibUtil = fibUtil;
176 this.interVpnLinkCache = interVpnLinkCache;
177 this.upgradeState = upgradeState;
178 this.eventCallbacks = eventCallbacks;
184 LOG.info("{} init", getClass().getSimpleName());
185 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
189 @SuppressWarnings("checkstyle:IllegalCatch")
190 public void close() {
191 closeables.forEach(c -> {
194 } catch (Exception e) {
195 LOG.warn("Error closing {}", c, e);
201 protected VrfEntryListener getDataTreeChangeListener() {
202 return VrfEntryListener.this;
206 protected InstanceIdentifier<VrfEntry> getWildCardPath() {
207 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class).child(VrfEntry.class);
211 protected void add(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
212 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
213 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
214 LOG.debug("ADD: Adding Fib Entry rd {} prefix {} route-paths {}",
215 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
216 addFibEntries(identifier, vrfEntry, rd);
217 LOG.info("ADD: Added Fib Entry rd {} prefix {} route-paths {}",
218 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
221 //This method is temporary. Eventually Factory design pattern will be used to get
222 // right VrfEntryhandle and invoke its methods.
223 private void addFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
224 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
225 bgpRouteVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
228 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
229 LOG.info("EVPN flows need to be programmed.");
230 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
231 nextHopManager, jobCoordinator, elanManager, fibUtil, upgradeState, eventCallbacks);
232 evpnVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
233 closeables.add(evpnVrfEntryHandler);
236 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
237 if (routerInt != null) {
238 // ping responder for router interfaces
239 routerInterfaceVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
242 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
243 createFibEntries(identifier, vrfEntry);
249 protected void remove(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry) {
250 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
251 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
252 LOG.debug("REMOVE: Removing Fib Entry rd {} prefix {} route-paths {}",
253 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
254 removeFibEntries(identifier, vrfEntry, rd);
255 LOG.info("REMOVE: Removed Fib Entry rd {} prefix {} route-paths {}",
256 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
259 //This method is temporary. Eventually Factory design pattern will be used to get
260 // right VrfEntryhandle and invoke its methods.
261 private void removeFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
262 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
263 LOG.info("EVPN flows to be deleted");
264 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
265 nextHopManager, jobCoordinator, elanManager, fibUtil, upgradeState, eventCallbacks);
266 evpnVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
267 closeables.add(evpnVrfEntryHandler);
270 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
271 if (routerInt != null) {
272 // ping responder for router interfaces
273 routerInterfaceVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
276 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
277 deleteFibEntries(identifier, vrfEntry);
280 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
281 bgpRouteVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
287 // "Redundant nullcheck of originalRoutePath, which is known to be non-null" - the null checking for
288 // originalRoutePath is a little dicey - safest to keep the checking even if not needed.
289 @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE")
290 protected void update(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update) {
291 Preconditions.checkNotNull(update, "VrfEntry should not be null or empty.");
292 final String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
293 LOG.debug("UPDATE: Updating Fib Entries to rd {} prefix {} route-paths {} origin {} old-origin {}", rd,
294 update.getDestPrefix(), update.getRoutePaths(), update.getOrigin(), original.getOrigin());
295 // Handle BGP Routes first
296 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.BGP) {
297 bgpRouteVrfEntryHandler.updateFlows(identifier, original, update, rd);
298 LOG.info("UPDATE: Updated BGP advertised Fib Entry with rd {} prefix {} route-paths {}",
299 rd, update.getDestPrefix(), update.getRoutePaths());
303 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.STATIC) {
304 List<RoutePaths> originalRoutePath = original.getRoutePaths();
305 List<RoutePaths> updateRoutePath = update.getRoutePaths();
306 LOG.info("UPDATE: Original route-path {} update route-path {} ", originalRoutePath, updateRoutePath);
308 //Updates need to be handled for extraroute even if original vrf entry route path is null or
309 //updated vrf entry route path is null. This can happen during tunnel events.
310 Optional<VpnInstanceOpDataEntry> optVpnInstance = fibUtil.getVpnInstanceOpData(rd);
311 List<String> usedRds = new ArrayList<>();
312 if (optVpnInstance.isPresent()) {
313 usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,optVpnInstance.get().getVpnId(),
314 update.getDestPrefix());
316 // If original VRF Entry had nexthop null , but update VRF Entry
317 // has nexthop , route needs to be created on remote Dpns
318 if (originalRoutePath == null || originalRoutePath.isEmpty()
319 && updateRoutePath != null && !updateRoutePath.isEmpty() && usedRds.isEmpty()) {
320 // TODO(vivek): Though ugly, Not handling this code now, as each
321 // tep add event will invoke flow addition
322 LOG.trace("Original VRF entry NH is null for destprefix {}. And the prefix is not an extra route."
323 + " This event is IGNORED here.", update.getDestPrefix());
327 // If original VRF Entry had valid nexthop , but update VRF Entry
328 // has nexthop empty'ed out, route needs to be removed from remote Dpns
329 if (updateRoutePath == null || updateRoutePath.isEmpty()
330 && originalRoutePath != null && !originalRoutePath.isEmpty() && usedRds.isEmpty()) {
331 LOG.trace("Original VRF entry had valid NH for destprefix {}. And the prefix is not an extra route."
332 + "This event is IGNORED here.", update.getDestPrefix());
335 //Update the used rds and vpntoextraroute containers only for the deleted nextHops.
336 List<String> nextHopsRemoved = FibHelper.getNextHopListFromRoutePaths(original);
337 nextHopsRemoved.removeAll(FibHelper.getNextHopListFromRoutePaths(update));
338 List<ListenableFuture<Void>> futures = new ArrayList<>();
339 ListenableFuture<Void> configFuture =
340 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx ->
341 futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, operTx ->
342 nextHopsRemoved.parallelStream()
343 .forEach(nextHopRemoved -> {
345 fibUtil.updateUsedRdAndVpnToExtraRoute(
346 configTx, operTx, nextHopRemoved, rd, update.getDestPrefix());
347 } catch (ExecutionException | InterruptedException e) {
348 throw new RuntimeException(e);
351 futures.add(configFuture);
352 Futures.addCallback(configFuture, new FutureCallback<Void>() {
354 public void onSuccess(Void result) {
355 createFibEntries(identifier, update);
356 LOG.info("UPDATE: Updated static Fib Entry with rd {} prefix {} route-paths {}",
357 rd, update.getDestPrefix(), update.getRoutePaths());
361 public void onFailure(Throwable throwable) {
362 LOG.error("Exception encountered while submitting operational future for update vrfentry {}",
365 }, MoreExecutors.directExecutor());
369 //Handle all other routes only on a cluster reboot
370 if (original.equals(update)) {
372 createFibEntries(identifier, update);
373 LOG.info("UPDATE: Updated Non-static Fib Entry with rd {} prefix {} route-paths {}",
374 rd, update.getDestPrefix(), update.getRoutePaths());
378 LOG.info("UPDATE: Ignoring update for FIB entry with rd {} prefix {} route-origin {} route-paths {}",
379 rd, update.getDestPrefix(), update.getOrigin(), update.getRoutePaths());
382 private void createFibEntries(final InstanceIdentifier<VrfEntry> vrfEntryIid, final VrfEntry vrfEntry) {
383 final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
384 List<SubTransaction> txnObjects = new ArrayList<>();
385 final VpnInstanceOpDataEntry vpnInstance =
386 fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
387 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
388 Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId()
389 + " has null vpnId!");
390 final Collection<VpnToDpnList> vpnToDpnList;
391 if (vrfEntry.getParentVpnRd() != null
392 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
393 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
394 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
395 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
396 vpnInstance.getVpnToDpnList();
397 LOG.info("createFibEntries: Processing creation of PNF FIB entry with rd {} prefix {}",
398 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
400 vpnToDpnList = vpnInstance.getVpnToDpnList();
402 final Long vpnId = vpnInstance.getVpnId();
403 final String rd = vrfTableKey.getRouteDistinguisher();
404 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
405 if (subnetRoute != null) {
406 final long elanTag = subnetRoute.getElantag();
407 LOG.trace("SUBNETROUTE: createFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
408 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
409 if (vpnToDpnList != null) {
410 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
411 () -> Collections.singletonList(
412 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
413 for (final VpnToDpnList curDpn : vpnToDpnList) {
414 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
415 installSubnetRouteInFib(curDpn.getDpnId(), elanTag, rd, vpnId, vrfEntry, tx);
416 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnId.longValue(),
417 vrfEntry, NwConstants.ADD_FLOW, tx);
424 // Get etherType value based on the IpPrefix address family type
427 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
428 } catch (IllegalArgumentException ex) {
429 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
433 final List<BigInteger> localDpnIdList = createLocalFibEntry(vpnInstance.getVpnId(), rd, vrfEntry, etherType);
434 if (!localDpnIdList.isEmpty() && vpnToDpnList != null) {
435 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
436 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
437 synchronized (vpnInstance.getVpnInstanceName().intern()) {
438 for (VpnToDpnList vpnDpn : vpnToDpnList) {
439 if (!localDpnIdList.contains(vpnDpn.getDpnId())) {
440 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
442 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
443 bgpRouteVrfEntryHandler.createRemoteFibEntry(vpnDpn.getDpnId(),
444 vpnId, vrfTableKey.getRouteDistinguisher(), vrfEntry,
445 TransactionAdapter.toWriteTransaction(tx),
448 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
449 vrfTableKey.getRouteDistinguisher(), vrfEntry, tx);
451 } catch (NullPointerException e) {
452 LOG.error("Failed to get create remote fib flows for prefix {} ",
453 vrfEntry.getDestPrefix(), e);
462 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
463 if (optVpnUuid.isPresent()) {
464 String vpnUuid = optVpnUuid.get();
465 InterVpnLinkDataComposite interVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid).orNull();
466 if (interVpnLink != null) {
467 LOG.debug("InterVpnLink {} found in Cache linking Vpn {}", interVpnLink.getInterVpnLinkName(), vpnUuid);
468 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
469 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
470 // This is an static route that points to the other endpoint of an InterVpnLink
471 // In that case, we should add another entry in FIB table pointing to LPortDispatcher table.
472 installIVpnLinkSwitchingFlows(interVpnLink, vpnUuid, vrfEntry, vpnId);
473 installInterVpnRouteInLFib(interVpnLink, vpnUuid, vrfEntry, etherType);
480 void refreshFibTables(String rd, String prefix) {
481 InstanceIdentifier<VrfEntry> vrfEntryId =
482 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
483 .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
484 Optional<VrfEntry> vrfEntry = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
485 if (vrfEntry.isPresent()) {
486 createFibEntries(vrfEntryId, vrfEntry.get());
490 private Prefixes updateVpnReferencesInLri(LabelRouteInfo lri, String vpnInstanceName, boolean isPresentInList) {
491 LOG.debug("updating LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
492 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
493 prefixBuilder.setDpnId(lri.getDpnId());
494 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
495 prefixBuilder.setIpAddress(lri.getPrefix());
496 // Increment the refCount here
497 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
498 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
499 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri);
500 if (!isPresentInList) {
501 LOG.debug("vpnName {} is not present in LRI with label {}..", vpnInstanceName, lri.getLabel());
502 List<String> vpnInstanceNames = new ArrayList<>(nullToEmpty(lri.getVpnInstanceList()));
503 vpnInstanceNames.add(vpnInstanceName);
504 builder.setVpnInstanceList(vpnInstanceNames);
505 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
507 LOG.debug("vpnName {} is present in LRI with label {}..", vpnInstanceName, lri.getLabel());
509 return prefixBuilder.build();
512 void installSubnetRouteInFib(final BigInteger dpnId, final long elanTag, final String rd,
513 final long vpnId, final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
515 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
516 newTx -> installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, newTx)), LOG,
517 "Error installing subnet route in FIB");
522 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
523 } catch (IllegalArgumentException ex) {
524 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
527 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
528 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
529 synchronized (label.toString().intern()) {
530 LabelRouteInfo lri = getLabelRouteInfo(label);
531 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
533 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
534 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
535 fibUtil.getVpnInstanceOpData(rd);
536 if (vpnInstanceOpDataEntryOptional.isPresent()) {
537 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
538 if (!lri.getVpnInstanceList().contains(vpnInstanceName)) {
539 updateVpnReferencesInLri(lri, vpnInstanceName, false);
543 LOG.debug("SUBNETROUTE: installSubnetRouteInFib: Fetched labelRouteInfo for label {} interface {}"
544 + " and got dpn {}", label, lri.getVpnInterfaceName(), lri.getDpnId());
548 final List<InstructionInfo> instructions = new ArrayList<>();
549 BigInteger subnetRouteMeta = BigInteger.valueOf(elanTag).shiftLeft(24)
550 .or(BigInteger.valueOf(vpnId).shiftLeft(1));
551 instructions.add(new InstructionWriteMetadata(subnetRouteMeta, MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
552 instructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
553 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
554 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
555 if (vrfEntry.getRoutePaths() != null) {
556 for (RoutePaths routePath : vrfEntry.getRoutePaths()) {
557 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
558 List<ActionInfo> actionsInfos = new ArrayList<>();
559 // reinitialize instructions list for LFIB Table
560 final List<InstructionInfo> LFIBinstructions = new ArrayList<>();
561 actionsInfos.add(new ActionPopMpls(etherType));
562 LFIBinstructions.add(new InstructionApplyActions(actionsInfos));
563 LFIBinstructions.add(new InstructionWriteMetadata(subnetRouteMeta,
564 MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
565 LFIBinstructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
567 makeLFibTableEntry(dpnId, routePath.getLabel(), LFIBinstructions, DEFAULT_FIB_FLOW_PRIORITY,
568 NwConstants.ADD_FLOW, tx);
574 private void installSubnetBroadcastAddrDropRule(final BigInteger dpnId, final String rd, final long vpnId,
575 final VrfEntry vrfEntry, int addOrRemove, TypedWriteTransaction<Configuration> tx) {
576 List<MatchInfo> matches = new ArrayList<>();
578 LOG.debug("SUBNETROUTE: installSubnetBroadcastAddrDropRule: destPrefix {} rd {} vpnId {} dpnId {}",
579 vrfEntry.getDestPrefix(), rd, vpnId, dpnId);
580 String[] ipAddress = vrfEntry.getDestPrefix().split("/");
581 String subnetBroadcastAddr = FibUtil.getBroadcastAddressFromCidr(vrfEntry.getDestPrefix());
582 final int prefixLength = ipAddress.length == 1 ? 0 : Integer.parseInt(ipAddress[1]);
584 InetAddress destPrefix;
586 destPrefix = InetAddress.getByName(subnetBroadcastAddr);
587 } catch (UnknownHostException e) {
588 LOG.error("Failed to get destPrefix for prefix {} rd {} VpnId {} DPN {}",
589 vrfEntry.getDestPrefix(), rd, vpnId, dpnId, e);
593 // Match on VpnId and SubnetBroadCast IP address
594 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
595 matches.add(MatchEthernetType.IPV4);
597 if (prefixLength != 0) {
598 matches.add(new MatchIpv4Destination(subnetBroadcastAddr, Integer.toString(IPV4_ADDR_PREFIX_LENGTH)));
601 //Action is to drop the packet
602 List<InstructionInfo> dropInstructions = new ArrayList<>();
603 List<ActionInfo> actionsInfos = new ArrayList<>();
604 actionsInfos.add(new ActionDrop());
605 dropInstructions.add(new InstructionApplyActions(actionsInfos));
607 int priority = DEFAULT_FIB_FLOW_PRIORITY + IPV4_ADDR_PREFIX_LENGTH;
608 String flowRef = FibUtil.getFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, rd, priority, destPrefix);
609 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef, priority,
610 flowRef, 0, 0, COOKIE_TABLE_MISS, matches, dropInstructions);
612 Flow flow = flowEntity.getFlowBuilder().build();
613 String flowId = flowEntity.getFlowId();
614 FlowKey flowKey = new FlowKey(new FlowId(flowId));
615 Node nodeDpn = FibUtil.buildDpnNode(dpnId);
617 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
618 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
619 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
621 if (addOrRemove == NwConstants.ADD_FLOW) {
622 tx.put(flowInstanceId,flow, true);
624 tx.delete(flowInstanceId);
629 * For a given route, it installs a flow in LFIB that sets the lportTag of the other endpoint and sends to
630 * LportDispatcher table (via table 80)
632 private void installInterVpnRouteInLFib(final InterVpnLinkDataComposite interVpnLink, final String vpnName,
633 final VrfEntry vrfEntry, int etherType) {
634 // INTERVPN routes are routes in a Vpn1 that have been leaked to Vpn2. In DC-GW, this Vpn2 route is pointing
635 // to a list of DPNs where Vpn2's VpnLink was instantiated. In these DPNs LFIB must be programmed so that the
636 // packet is commuted from Vpn2 to Vpn1.
637 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
638 if (!interVpnLink.isActive()) {
639 LOG.warn("InterVpnLink {} is NOT ACTIVE. InterVpnLink flows for prefix={} wont be installed in LFIB",
640 interVpnLinkName, vrfEntry.getDestPrefix());
644 Optional<Long> optLportTag = interVpnLink.getEndpointLportTagByVpnName(vpnName);
645 if (!optLportTag.isPresent()) {
646 LOG.warn("Could not retrieve lportTag for VPN {} endpoint in InterVpnLink {}", vpnName, interVpnLinkName);
650 Long lportTag = optLportTag.get();
651 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).orElse(null);
653 LOG.error("Could not find label in vrfEntry=[prefix={} routePaths={}]. LFIB entry for InterVpnLink skipped",
654 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
657 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionPopMpls(etherType));
658 List<InstructionInfo> instructions = Arrays.asList(
659 new InstructionApplyActions(actionsInfos),
660 new InstructionWriteMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(lportTag.intValue(),
661 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
662 NwConstants.L3VPN_SERVICE_INDEX)),
663 MetaDataUtil.getMetaDataMaskForLPortDispatcher()),
664 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
665 List<String> interVpnNextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
666 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
668 for (BigInteger dpId : targetDpns) {
669 LOG.debug("Installing flow: VrfEntry=[prefix={} label={} nexthop={}] dpn {} for InterVpnLink {} in LFIB",
670 vrfEntry.getDestPrefix(), label, interVpnNextHopList, dpId, interVpnLink.getInterVpnLinkName());
672 makeLFibTableEntry(dpId, label, instructions, LFIB_INTERVPN_PRIORITY, NwConstants.ADD_FLOW,
679 * Installs the flows in FIB table that, for a given route, do the switching from one VPN to the other.
681 private void installIVpnLinkSwitchingFlows(final InterVpnLinkDataComposite interVpnLink, final String vpnUuid,
682 final VrfEntry vrfEntry, long vpnTag) {
683 Preconditions.checkNotNull(interVpnLink, "InterVpnLink cannot be null");
684 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null
685 && vrfEntry.getRoutePaths().size() == 1);
686 String destination = vrfEntry.getDestPrefix();
687 String nextHop = vrfEntry.getRoutePaths().get(0).getNexthopAddress();
688 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
690 // After having received a static route, we should check if the vpn is part of an inter-vpn-link.
691 // In that case, we should populate the FIB table of the VPN pointing to LPortDisptacher table
692 // using as metadata the LPortTag associated to that vpn in the inter-vpn-link.
693 if (interVpnLink.getState().or(State.Error) != State.Active) {
694 LOG.warn("Route to {} with nexthop={} cannot be installed because the interVpnLink {} is not active",
695 destination, nextHop, interVpnLinkName);
699 Optional<Long> optOtherEndpointLportTag = interVpnLink.getOtherEndpointLportTagByVpnName(vpnUuid);
700 if (!optOtherEndpointLportTag.isPresent()) {
701 LOG.warn("Could not find suitable LportTag for the endpoint opposite to vpn {} in interVpnLink {}",
702 vpnUuid, interVpnLinkName);
706 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnUuid);
707 if (targetDpns.isEmpty()) {
708 LOG.warn("Could not find DPNs for endpoint opposite to vpn {} in interVpnLink {}",
709 vpnUuid, interVpnLinkName);
713 String[] values = destination.split("/");
714 String destPrefixIpAddress = values[0];
715 int prefixLength = values.length == 1 ? 0 : Integer.parseInt(values[1]);
717 List<MatchInfo> matches = new ArrayList<>();
718 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnTag), MetaDataUtil.METADATA_MASK_VRFID));
719 matches.add(MatchEthernetType.IPV4);
721 if (prefixLength != 0) {
722 matches.add(new MatchIpv4Destination(destPrefixIpAddress, Integer.toString(prefixLength)));
725 List<Instruction> instructions =
726 Arrays.asList(new InstructionWriteMetadata(
727 MetaDataUtil.getMetaDataForLPortDispatcher(optOtherEndpointLportTag.get().intValue(),
728 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants
729 .L3VPN_SERVICE_INDEX)),
730 MetaDataUtil.getMetaDataMaskForLPortDispatcher()).buildInstruction(0),
731 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE).buildInstruction(1));
733 int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
734 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, destination, nextHop);
735 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef, priority, flowRef, 0, 0,
736 COOKIE_VM_FIB_TABLE, matches, instructions);
738 LOG.trace("Installing flow in FIB table for vpn {} interVpnLink {} nextHop {} key {}",
739 vpnUuid, interVpnLink.getInterVpnLinkName(), nextHop, flowRef);
741 for (BigInteger dpId : targetDpns) {
743 LOG.debug("Installing flow: VrfEntry=[prefix={} route-paths={}] dpn {} for InterVpnLink {} in FIB",
744 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(),
745 dpId, interVpnLink.getInterVpnLinkName());
747 mdsalManager.installFlow(dpId, flowEntity);
751 private List<BigInteger> createLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry, int etherType) {
752 List<BigInteger> returnLocalDpnId = new ArrayList<>();
753 String localNextHopIP = vrfEntry.getDestPrefix();
754 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
755 String vpnName = fibUtil.getVpnNameFromId(vpnId);
756 if (localNextHopInfo == null) {
757 boolean localNextHopSeen = false;
758 List<Routes> vpnExtraRoutes = null;
759 String rdPrefixKey = localNextHopIP + FibConstants.SEPARATOR + rd;
760 //Synchronized to prevent missing bucket action due to race condition between refreshFib and
761 // add/updateFib threads on missing nexthop in VpnToExtraroutes
762 synchronized (rdPrefixKey.intern()) {
763 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, localNextHopIP);
764 vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
765 vpnName, usedRds, localNextHopIP);
766 if (LOG.isDebugEnabled()) {
767 LOG.debug("Creating Local fib entry with vpnName {} usedRds {} localNextHopIP {} vpnExtraRoutes {}",
768 vpnName, usedRds, localNextHopIP, vpnExtraRoutes);
771 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
772 for (Routes vpnExtraRoute : vpnExtraRoutes) {
774 if (isIpv4Address(vpnExtraRoute.getNexthopIpList().get(0))) {
775 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
777 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
779 Prefixes localNextHopInfoLocal = fibUtil.getPrefixToInterface(vpnId,
781 if (localNextHopInfoLocal != null) {
782 localNextHopSeen = true;
784 checkCreateLocalFibEntry(localNextHopInfoLocal, localNextHopInfoLocal.getIpAddress(),
785 vpnId, rd, vrfEntry, vpnExtraRoute, vpnExtraRoutes, etherType);
786 returnLocalDpnId.add(dpnId);
790 if (!localNextHopSeen && RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
791 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
792 if (optionalLabel.isPresent()) {
793 Long label = optionalLabel.get();
794 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
795 synchronized (label.toString().intern()) {
796 LabelRouteInfo lri = getLabelRouteInfo(label);
797 if (isPrefixAndNextHopPresentInLri(localNextHopIP, nextHopAddressList, lri)) {
798 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
799 fibUtil.getVpnInstanceOpData(rd);
800 if (vpnInstanceOpDataEntryOptional.isPresent()) {
801 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
802 if (nullToEmpty(lri.getVpnInstanceList()).contains(vpnInstanceName)) {
803 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, true);
804 localNextHopIP = lri.getPrefix();
806 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, false);
807 localNextHopIP = lri.getPrefix();
810 if (localNextHopInfo != null) {
811 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
812 label, localNextHopInfo.getVpnInterfaceName(), lri.getDpnId());
813 if (vpnExtraRoutes.isEmpty()) {
814 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
815 vpnId, rd, vrfEntry, null, vpnExtraRoutes, etherType);
816 returnLocalDpnId.add(dpnId);
818 for (Routes extraRoutes : vpnExtraRoutes) {
819 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
820 vpnId, rd, vrfEntry, extraRoutes, vpnExtraRoutes, etherType);
821 returnLocalDpnId.add(dpnId);
829 if (returnLocalDpnId.isEmpty()) {
830 LOG.error("Local DPNID is empty for rd {}, vpnId {}, vrfEntry {}", rd, vpnId, vrfEntry);
833 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP, vpnId,
834 rd, vrfEntry, /*routes*/ null, /*vpnExtraRoutes*/ null, etherType);
836 returnLocalDpnId.add(dpnId);
839 return returnLocalDpnId;
842 private BigInteger checkCreateLocalFibEntry(Prefixes localNextHopInfo, String localNextHopIP,
843 final Long vpnId, final String rd,
844 final VrfEntry vrfEntry,
845 @Nullable Routes routes, @Nullable List<Routes> vpnExtraRoutes,
847 String vpnName = fibUtil.getVpnNameFromId(vpnId);
848 if (localNextHopInfo != null) {
851 final BigInteger dpnId = localNextHopInfo.getDpnId();
852 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
853 LOG.debug("checkCreateLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
854 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
857 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
858 LOG.debug("checkCreateLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
859 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
862 if (!isVpnPresentInDpn(rd, dpnId)) {
863 LOG.error("checkCreateLocalFibEntry: The VPN with id {} rd {} is not available on dpn {}",
864 vpnId, rd, dpnId.toString());
865 return BigInteger.ZERO;
867 String interfaceName = localNextHopInfo.getVpnInterfaceName();
868 String prefix = vrfEntry.getDestPrefix();
869 String gwMacAddress = vrfEntry.getGatewayMacAddress();
870 //The loadbalancing group is created only if the extra route has multiple nexthops
871 //to avoid loadbalancing the discovered routes
872 if (vpnExtraRoutes != null && routes != null) {
873 if (vpnExtraRoutes.size() > 1) {
874 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
875 localGroupId = nextHopManager.getLocalSelectGroup(vpnId, vrfEntry.getDestPrefix());
877 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
878 localGroupId = groupId;
881 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP, prefix,
883 localGroupId = groupId;
885 if (groupId == FibConstants.INVALID_GROUP_ID) {
886 LOG.error("Unable to create Group for local prefix {} on rd {} for vpninterface {} on Node {}",
887 prefix, rd, interfaceName, dpnId.toString());
888 return BigInteger.ZERO;
890 final List<InstructionInfo> instructions = Collections.singletonList(
891 new InstructionApplyActions(
892 Collections.singletonList(new ActionGroup(groupId))));
893 final List<InstructionInfo> lfibinstructions = Collections.singletonList(
894 new InstructionApplyActions(
895 Arrays.asList(new ActionPopMpls(etherType), new ActionGroup(localGroupId))));
896 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
897 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
898 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
899 jobCoordinator.enqueueJob(jobKey,
900 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
901 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
902 NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
903 if (FibUtil.isBgpVpn(vpnName, rd)) {
904 optLabel.ifPresent(label -> {
905 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
907 "Installing LFIB and tunnel table entry on dpn {} for interface {} with label "
908 + "{}, rd {}, prefix {}, nexthop {}", dpnId,
909 localNextHopInfo.getVpnInterfaceName(), optLabel, rd, vrfEntry.getDestPrefix(),
911 makeLFibTableEntry(dpnId, label, lfibinstructions, DEFAULT_FIB_FLOW_PRIORITY,
912 NwConstants.ADD_FLOW, tx);
913 makeTunnelTableEntry(dpnId, label, localGroupId, tx);
915 LOG.debug("Route with rd {} prefix {} label {} nexthop {} for vpn {} is an imported "
916 + "route. LFib and Terminating table entries will not be created.",
917 rd, vrfEntry.getDestPrefix(), optLabel, nextHopAddressList, vpnId);
924 LOG.error("localNextHopInfo received is null for prefix {} on rd {} on vpn {}", vrfEntry.getDestPrefix(), rd,
926 return BigInteger.ZERO;
929 private boolean isVpnPresentInDpn(String rd, BigInteger dpnId) {
930 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
931 Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
932 return dpnInVpn.isPresent();
936 private LabelRouteInfo getLabelRouteInfo(Long label) {
937 InstanceIdentifier<LabelRouteInfo> lriIid = InstanceIdentifier.builder(LabelRouteMap.class)
938 .child(LabelRouteInfo.class, new LabelRouteInfoKey(label)).build();
939 Optional<LabelRouteInfo> opResult = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, lriIid);
940 if (opResult.isPresent()) {
941 return opResult.get();
946 private boolean deleteLabelRouteInfo(LabelRouteInfo lri, String vpnInstanceName,
947 @Nullable TypedWriteTransaction<Operational> tx) {
952 LOG.debug("deleting LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
953 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
954 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
956 List<String> vpnInstancesList = lri.getVpnInstanceList() != null
957 ? lri.getVpnInstanceList() : new ArrayList<>();
958 if (vpnInstancesList.contains(vpnInstanceName)) {
959 LOG.debug("vpninstance {} name is present", vpnInstanceName);
960 vpnInstancesList.remove(vpnInstanceName);
962 if (vpnInstancesList.isEmpty()) {
963 LOG.debug("deleting LRI instance object for label {}", lri.getLabel());
967 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId);
971 LOG.debug("updating LRI instance object for label {}", lri.getLabel());
972 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri).setVpnInstanceList(vpnInstancesList);
973 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
978 void makeTunnelTableEntry(BigInteger dpId, long label, long groupId/*String egressInterfaceName*/,
979 TypedWriteTransaction<Configuration> tx) {
980 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionGroup(groupId));
982 createTerminatingServiceActions(dpId, (int) label, actionsInfos, tx);
984 LOG.debug("Terminating service Entry for dpID {} : label : {} egress : {} installed successfully",
985 dpId, label, groupId);
988 public void createTerminatingServiceActions(BigInteger destDpId, int label, List<ActionInfo> actionsInfos,
989 TypedWriteTransaction<Configuration> tx) {
990 List<MatchInfo> mkMatches = new ArrayList<>();
992 LOG.debug("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}",
993 destDpId, label, actionsInfos);
996 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
997 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
999 List<InstructionInfo> mkInstructions = new ArrayList<>();
1000 mkInstructions.add(new InstructionApplyActions(actionsInfos));
1002 FlowEntity terminatingServiceTableFlowEntity =
1003 MDSALUtil.buildFlowEntity(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,
1004 getTableMissFlowRef(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE, label), 5,
1005 String.format("%s:%d", "TST Flow Entry ", label),
1006 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, mkInstructions);
1008 FlowKey flowKey = new FlowKey(new FlowId(terminatingServiceTableFlowEntity.getFlowId()));
1010 FlowBuilder flowbld = terminatingServiceTableFlowEntity.getFlowBuilder();
1012 Node nodeDpn = FibUtil.buildDpnNode(terminatingServiceTableFlowEntity.getDpnId());
1013 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1014 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1015 .child(Table.class, new TableKey(terminatingServiceTableFlowEntity.getTableId()))
1016 .child(Flow.class, flowKey).build();
1017 tx.put(flowInstanceId, flowbld.build(), CREATE_MISSING_PARENTS);
1020 private void removeTunnelTableEntry(BigInteger dpId, long label, TypedWriteTransaction<Configuration> tx) {
1021 FlowEntity flowEntity;
1022 LOG.debug("remove terminatingServiceActions called with DpnId = {} and label = {}", dpId, label);
1023 List<MatchInfo> mkMatches = new ArrayList<>();
1024 // Matching metadata
1025 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
1026 flowEntity = MDSALUtil.buildFlowEntity(dpId,
1027 NwConstants.INTERNAL_TUNNEL_TABLE,
1028 getTableMissFlowRef(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, (int) label),
1029 5, String.format("%s:%d", "TST Flow Entry ", label), 0, 0,
1030 COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, null);
1031 Node nodeDpn = FibUtil.buildDpnNode(flowEntity.getDpnId());
1032 FlowKey flowKey = new FlowKey(new FlowId(flowEntity.getFlowId()));
1033 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1034 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1035 .child(Table.class, new TableKey(flowEntity.getTableId())).child(Flow.class, flowKey).build();
1037 tx.delete(flowInstanceId);
1038 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully", dpId, label);
1041 public List<BigInteger> deleteLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
1042 List<BigInteger> returnLocalDpnId = new ArrayList<>();
1043 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1044 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1045 boolean shouldUpdateNonEcmpLocalNextHop = true;
1046 if (localNextHopInfo == null) {
1047 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1048 if (usedRds.size() > 1) {
1049 LOG.error("The extra route prefix {} is still present in some DPNs in vpn {} on rd {}",
1050 vrfEntry.getDestPrefix(), vpnName, rd);
1051 return returnLocalDpnId;
1053 String vpnRd = !usedRds.isEmpty() ? usedRds.get(0) : rd;
1054 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency
1056 Optional<Routes> extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1057 vpnName, vpnRd, vrfEntry.getDestPrefix());
1058 if (extraRouteOptional.isPresent()) {
1059 Routes extraRoute = extraRouteOptional.get();
1061 if (isIpv4Address(extraRoute.getNexthopIpList().get(0))) {
1062 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
1064 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
1066 if (extraRoute.getNexthopIpList().size() > 1) {
1067 shouldUpdateNonEcmpLocalNextHop = false;
1069 localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1070 if (localNextHopInfo != null) {
1071 String localNextHopIP = localNextHopInfo.getIpAddress();
1072 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP, vpnName, vpnId, rd,
1073 vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1074 if (!dpnId.equals(BigInteger.ZERO)) {
1075 LOG.trace("Deleting ECMP group for prefix {}, dpn {}", vrfEntry.getDestPrefix(), dpnId);
1076 nextHopManager.deleteLoadBalancingNextHop(vpnId, dpnId, vrfEntry.getDestPrefix());
1077 returnLocalDpnId.add(dpnId);
1080 LOG.error("localNextHopInfo unavailable while deleting prefix {} with rds {}, primary rd {} in "
1081 + "vpn {}", vrfEntry.getDestPrefix(), usedRds, rd, vpnName);
1085 if (localNextHopInfo == null) {
1086 /* Imported VRF entry */
1087 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1088 if (optionalLabel.isPresent()) {
1089 Long label = optionalLabel.get();
1090 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1091 LabelRouteInfo lri = getLabelRouteInfo(label);
1092 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1093 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1094 prefixBuilder.setDpnId(lri.getDpnId());
1095 BigInteger dpnId = checkDeleteLocalFibEntry(prefixBuilder.build(), nextHopAddressList.get(0),
1096 vpnName, vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1097 if (!dpnId.equals(BigInteger.ZERO)) {
1098 returnLocalDpnId.add(dpnId);
1105 LOG.debug("Obtained prefix to interface for rd {} prefix {}", rd, vrfEntry.getDestPrefix());
1106 String localNextHopIP = localNextHopInfo.getIpAddress();
1107 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP, vpnName, vpnId, rd, vrfEntry,
1108 shouldUpdateNonEcmpLocalNextHop);
1109 if (!dpnId.equals(BigInteger.ZERO)) {
1110 returnLocalDpnId.add(dpnId);
1114 return returnLocalDpnId;
1117 private BigInteger checkDeleteLocalFibEntry(Prefixes localNextHopInfo, final String localNextHopIP,
1118 final String vpnName, final Long vpnId, final String rd, final VrfEntry vrfEntry,
1119 boolean shouldUpdateNonEcmpLocalNextHop) {
1120 if (localNextHopInfo != null) {
1121 final BigInteger dpnId = localNextHopInfo.getDpnId();
1122 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
1123 LOG.debug("checkDeleteLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1124 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1127 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
1128 LOG.debug("checkDeleteLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1129 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1133 jobCoordinator.enqueueJob(FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix()),
1134 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1135 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1136 NwConstants.DEL_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1137 if (FibUtil.isBgpVpn(vpnName, rd)) {
1138 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1139 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1140 makeLFibTableEntry(dpnId, label, null /* instructions */, DEFAULT_FIB_FLOW_PRIORITY,
1141 NwConstants.DEL_FLOW, tx);
1142 removeTunnelTableEntry(dpnId, label, tx);
1147 //TODO: verify below adjacency call need to be optimized (?)
1148 //In case of the removal of the extra route, the loadbalancing group is updated
1149 if (shouldUpdateNonEcmpLocalNextHop) {
1150 baseVrfEntryHandler.deleteLocalAdjacency(dpnId, vpnId, localNextHopIP, vrfEntry.getDestPrefix());
1154 return BigInteger.ZERO;
1157 private void createRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, String rd,
1158 final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
1160 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1161 newTx -> createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx)), LOG,
1162 "Error creating remote FIB entry");
1166 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1167 LOG.debug("createremotefibentry: adding route {} for rd {} on remoteDpnId {}",
1168 vrfEntry.getDestPrefix(), rd, remoteDpnId);
1170 List<AdjacencyResult> adjacencyResults = baseVrfEntryHandler.resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
1171 if (adjacencyResults.isEmpty()) {
1172 LOG.error("Could not get interface for route-paths: {} in vpn {} on DPN {}",
1173 vrfEntry.getRoutePaths(), rd, remoteDpnId);
1174 LOG.error("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
1178 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1179 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
1180 vpnName, usedRds, vrfEntry.getDestPrefix());
1181 // create loadbalancing groups for extra routes only when the extra route is present behind
1183 if (!vpnExtraRoutes.isEmpty()) {
1184 List<InstructionInfo> instructions = new ArrayList<>();
1185 // Obtain the local routes for this particular dpn.
1186 java.util.Optional<Routes> routes = vpnExtraRoutes
1189 Prefixes prefixToInterface = fibUtil.getPrefixToInterface(vpnId,
1190 fibUtil.getIpPrefix(route.getNexthopIpList().get(0)));
1191 if (prefixToInterface == null) {
1194 return remoteDpnId.equals(prefixToInterface.getDpnId());
1196 long groupId = nextHopManager.createNextHopGroups(vpnId, rd, remoteDpnId, vrfEntry,
1197 routes.isPresent() ? routes.get() : null, vpnExtraRoutes);
1198 if (groupId == FibConstants.INVALID_GROUP_ID) {
1199 LOG.error("Unable to create Group for local prefix {} on rd {} on Node {}",
1200 vrfEntry.getDestPrefix(), rd, remoteDpnId.toString());
1203 List<ActionInfo> actionInfos =
1204 Collections.singletonList(new ActionGroup(groupId));
1205 instructions.add(new InstructionApplyActions(actionInfos));
1206 String jobKey = FibUtil.getCreateRemoteNextHopJobKey(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
1207 jobCoordinator.enqueueJob(jobKey,
1208 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(txn -> {
1209 baseVrfEntryHandler.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
1210 NwConstants.ADD_FLOW, txn, null);
1213 baseVrfEntryHandler.programRemoteFib(remoteDpnId, vpnId, vrfEntry,
1214 TransactionAdapter.toWriteTransaction(tx), rd, adjacencyResults, null);
1217 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
1220 protected void cleanUpOpDataForFib(Long vpnId, String primaryRd, final VrfEntry vrfEntry) {
1221 /* Get interface info from prefix to interface mapping;
1222 Use the interface info to get the corresponding vpn interface op DS entry,
1223 remove the adjacency corresponding to this fib entry.
1224 If adjacency removed is the last adjacency, clean up the following:
1225 - vpn interface from dpntovpn list, dpn if last vpn interface on dpn
1226 - prefix to interface entry
1227 - vpn interface op DS
1229 LOG.debug("Cleanup of prefix {} in VPN {}", vrfEntry.getDestPrefix(), vpnId);
1230 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1231 if (prefixInfo == null) {
1232 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1233 String usedRd = usedRds.isEmpty() ? primaryRd : usedRds.get(0);
1234 Routes extraRoute = baseVrfEntryHandler.getVpnToExtraroute(vpnId, usedRd, vrfEntry.getDestPrefix());
1235 if (extraRoute != null) {
1236 for (String nextHopIp : nullToEmpty(extraRoute.getNexthopIpList())) {
1237 LOG.debug("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
1238 if (nextHopIp != null) {
1240 if (isIpv4Address(nextHopIp)) {
1241 ipPrefix = nextHopIp + NwConstants.IPV4PREFIX;
1243 ipPrefix = nextHopIp + NwConstants.IPV6PREFIX;
1245 prefixInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1246 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1250 if (prefixInfo == null) {
1251 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1252 if (optionalLabel.isPresent()) {
1253 Long label = optionalLabel.get();
1254 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1255 LabelRouteInfo lri = getLabelRouteInfo(label);
1256 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1257 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1258 prefixBuilder.setDpnId(lri.getDpnId());
1259 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
1260 prefixBuilder.setIpAddress(lri.getPrefix());
1261 prefixInfo = prefixBuilder.build();
1262 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
1263 label, prefixInfo.getVpnInterfaceName(), lri.getDpnId());
1264 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1269 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, null /*Routes*/);
1273 private void checkCleanUpOpDataForFib(final Prefixes prefixInfo, final Long vpnId, final String rd,
1274 final VrfEntry vrfEntry, @Nullable final Routes extraRoute) {
1276 if (prefixInfo == null) {
1277 LOG.error("Cleanup VPN Data Failed as unable to find prefix Info for prefix {} VpnId {} rd {}",
1278 vrfEntry.getDestPrefix(), vpnId, rd);
1279 return; //Don't have any info for this prefix (shouldn't happen); need to return
1282 if (Prefixes.PrefixCue.Nat.equals(prefixInfo.getPrefixCue())) {
1283 LOG.debug("NAT Prefix {} with vpnId {} rd {}. Skip FIB processing",
1284 vrfEntry.getDestPrefix(), vpnId, rd);
1288 String ifName = prefixInfo.getVpnInterfaceName();
1289 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName,
1290 new CleanupVpnInterfaceWorker(prefixInfo, vpnId, rd, vrfEntry, extraRoute));
1293 private class CleanupVpnInterfaceWorker implements Callable<List<ListenableFuture<Void>>> {
1294 Prefixes prefixInfo;
1300 CleanupVpnInterfaceWorker(final Prefixes prefixInfo, final Long vpnId, final String rd,
1301 final VrfEntry vrfEntry, final Routes extraRoute) {
1302 this.prefixInfo = prefixInfo;
1305 this.vrfEntry = vrfEntry;
1306 this.extraRoute = extraRoute;
1310 public List<ListenableFuture<Void>> call() {
1311 // If another renderer(for eg : CSS) needs to be supported, check can be performed here
1312 // to call the respective helpers.
1313 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
1314 //First Cleanup LabelRouteInfo
1315 //TODO(KIRAN) : Move the below block when addressing iRT/eRT for L3VPN Over VxLan
1316 LOG.debug("cleanupVpnInterfaceWorker: rd {} prefix {}", rd, prefixInfo.getIpAddress());
1317 if (VrfEntry.EncapType.Mplsgre.equals(vrfEntry.getEncapType())) {
1318 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1319 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1320 synchronized (label.toString().intern()) {
1321 LabelRouteInfo lri = getLabelRouteInfo(label);
1322 if (lri != null && Objects.equals(lri.getPrefix(), vrfEntry.getDestPrefix())
1323 && nextHopAddressList.contains(lri.getNextHopIpList().get(0))) {
1324 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1325 fibUtil.getVpnInstanceOpData(rd);
1326 String vpnInstanceName = "";
1327 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1328 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1330 boolean lriRemoved = deleteLabelRouteInfo(lri, vpnInstanceName, tx);
1332 String parentRd = lri.getParentVpnRd();
1333 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1334 parentRd, vrfEntry.getDestPrefix()));
1337 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1338 rd, vrfEntry.getDestPrefix()));
1343 String ifName = prefixInfo.getVpnInterfaceName();
1344 Optional<String> optVpnName = fibUtil.getVpnNameFromRd(rd);
1345 String vpnName = null;
1347 if (Prefixes.PrefixCue.PhysNetFunc.equals(prefixInfo.getPrefixCue())) {
1348 // Get vpnId for rd = networkId since op vpnInterface will be pointing to rd = networkId
1349 Optional<String> vpnNameOpt = fibUtil.getVpnNameFromRd(vrfEntry.getParentVpnRd());
1350 if (vpnNameOpt.isPresent()) {
1351 vpnId = fibUtil.getVpnId(vpnNameOpt.get());
1354 if (optVpnName.isPresent()) {
1355 vpnName = optVpnName.get();
1356 Optional<VpnInterfaceOpDataEntry> opVpnInterface = tx
1357 .read(FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName)).get();
1358 if (opVpnInterface.isPresent()) {
1359 long associatedVpnId = fibUtil.getVpnId(vpnName);
1360 if (vpnId != associatedVpnId) {
1361 LOG.warn("Prefixes {} are associated with different vpn instance with id {} rather than {}",
1362 vrfEntry.getDestPrefix(), associatedVpnId, vpnId);
1363 LOG.warn("Not proceeding with Cleanup op data for prefix {}", vrfEntry.getDestPrefix());
1366 LOG.debug("Processing cleanup of prefix {} associated with vpn {}",
1367 vrfEntry.getDestPrefix(), associatedVpnId);
1371 if (extraRoute != null) {
1372 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1373 //Only one used Rd present in case of removal event
1374 String usedRd = usedRds.get(0);
1375 if (optVpnName.isPresent()) {
1376 tx.delete(BaseVrfEntryHandler.getVpnToExtrarouteIdentifier(vpnName, usedRd,
1377 vrfEntry.getDestPrefix()));
1378 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, configTx ->
1379 configTx.delete(VpnExtraRouteHelper.getUsedRdsIdentifier(vpnId, vrfEntry.getDestPrefix())));
1382 handleAdjacencyAndVpnOpInterfaceDeletion(vrfEntry, ifName, vpnName, tx);
1388 * Check all the adjacency in VpnInterfaceOpData and decide whether to delete the entire interface or only adj.
1389 * Remove Adjacency from VPNInterfaceOpData.
1390 * if Adjacency != primary.
1391 * if Adjacency == primary , then mark it for deletion.
1392 * Remove entire VPNinterfaceOpData Entry.
1393 * if sie of Adjacency <= 2 and all are marked for deletion , delete the entire VPNinterface Op entry.
1394 * @param vrfEntry - VrfEntry removed
1395 * @param ifName - Interface name from VRFentry
1396 * @param vpnName - VPN name of corresponding VRF
1397 * @param tx - ReadWrite Tx
1399 private void handleAdjacencyAndVpnOpInterfaceDeletion(VrfEntry vrfEntry, String ifName, String vpnName,
1400 TypedReadWriteTransaction<Operational> tx)
1401 throws ExecutionException, InterruptedException {
1402 InstanceIdentifier<Adjacency> adjacencyIid =
1403 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix());
1404 Optional<Adjacency> adjacencyOptional = tx.read(adjacencyIid).get();
1405 if (adjacencyOptional.isPresent()) {
1406 if (adjacencyOptional.get().getAdjacencyType() != Adjacency.AdjacencyType.PrimaryAdjacency) {
1407 tx.delete(FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix()));
1409 tx.merge(adjacencyIid,
1410 new AdjacencyBuilder(adjacencyOptional.get()).setMarkedForDeletion(true).build());
1414 Optional<AdjacenciesOp> optAdjacencies = tx.read(FibUtil.getAdjListPathOp(ifName, vpnName)).get();
1416 if (!optAdjacencies.isPresent() || optAdjacencies.get().getAdjacency() == null) {
1420 if (nullToEmpty(optAdjacencies.get().getAdjacency()).size() <= 2
1421 && nullToEmpty(optAdjacencies.get().getAdjacency()).stream().allMatch(adjacency ->
1422 adjacency.getAdjacencyType() == Adjacency.AdjacencyType.PrimaryAdjacency
1423 && adjacency.isMarkedForDeletion() != null
1424 && adjacency.isMarkedForDeletion()
1426 LOG.info("Clean up vpn interface {} to vpn {} list.", ifName, vpnName);
1427 tx.delete(FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1431 private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
1432 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
1433 final String rd = vrfTableKey.getRouteDistinguisher();
1434 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
1435 if (vpnInstance == null) {
1436 LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
1439 final Collection<VpnToDpnList> vpnToDpnList;
1440 if (vrfEntry.getParentVpnRd() != null
1441 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
1442 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
1443 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
1444 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
1445 vpnInstance.getVpnToDpnList();
1446 LOG.info("deleteFibEntries: Processing deletion of PNF FIB entry with rd {} prefix {}",
1447 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
1449 vpnToDpnList = vpnInstance.getVpnToDpnList();
1452 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1453 final java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1454 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1455 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1456 if (subnetRoute != null) {
1457 long elanTag = subnetRoute.getElantag();
1458 LOG.trace("SUBNETROUTE: deleteFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
1459 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
1460 if (vpnToDpnList != null) {
1461 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1462 () -> Collections.singletonList(
1463 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1464 for (final VpnToDpnList curDpn : vpnToDpnList) {
1466 baseVrfEntryHandler.makeConnectedRoute(curDpn.getDpnId(), vpnInstance.getVpnId(),
1467 vrfEntry, vrfTableKey.getRouteDistinguisher(), null, NwConstants.DEL_FLOW,
1468 TransactionAdapter.toWriteTransaction(tx), null);
1469 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1470 optionalLabel.ifPresent(label -> makeLFibTableEntry(curDpn.getDpnId(), label, null,
1471 DEFAULT_FIB_FLOW_PRIORITY, NwConstants.DEL_FLOW, tx));
1474 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnInstance.getVpnId(),
1475 vrfEntry, NwConstants.DEL_FLOW, tx);
1479 optionalLabel.ifPresent(label -> {
1480 synchronized (label.toString().intern()) {
1481 LabelRouteInfo lri = getLabelRouteInfo(label);
1482 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1483 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1484 fibUtil.getVpnInstanceOpData(rd);
1485 String vpnInstanceName = "";
1486 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1487 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1489 boolean lriRemoved = this.deleteLabelRouteInfo(lri, vpnInstanceName, null);
1491 String parentRd = lri.getParentVpnRd();
1492 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1493 parentRd, vrfEntry.getDestPrefix()));
1494 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}"
1495 + " as labelRouteInfo cleared", label, rd, vrfEntry.getDestPrefix());
1498 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1499 rd, vrfEntry.getDestPrefix()));
1500 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}",
1501 label, rd, vrfEntry.getDestPrefix());
1508 final List<BigInteger> localDpnIdList = deleteLocalFibEntry(vpnInstance.getVpnId(),
1509 vrfTableKey.getRouteDistinguisher(), vrfEntry);
1510 if (vpnToDpnList != null) {
1511 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1512 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1514 Optional<Routes> extraRouteOptional;
1515 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
1516 if (usedRds != null && !usedRds.isEmpty()) {
1517 if (usedRds.size() > 1) {
1518 LOG.error("The extra route prefix is still present in some DPNs");
1521 // The first rd is retrieved from usedrds as Only 1 rd would be present as extra route prefix
1522 //is not present in any other DPN
1523 extraRouteOptional = VpnExtraRouteHelper
1524 .getVpnExtraroutes(dataBroker, vpnName, usedRds.get(0), vrfEntry.getDestPrefix());
1527 extraRouteOptional = Optional.absent();
1530 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1531 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1532 if (localDpnIdList.size() <= 0) {
1533 for (VpnToDpnList curDpn : vpnToDpnList) {
1534 baseVrfEntryHandler.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
1535 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
1536 TransactionAdapter.toWriteTransaction(tx));
1539 for (BigInteger localDpnId : localDpnIdList) {
1540 for (VpnToDpnList curDpn : vpnToDpnList) {
1541 if (!Objects.equals(curDpn.getDpnId(), localDpnId)) {
1542 baseVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
1543 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
1544 TransactionAdapter.toWriteTransaction(tx));
1549 nextHopManager.removeNextHopPointer(nextHopManager
1550 .getRemoteSelectGroupKey(vpnInstance.getVpnId(), vrfEntry.getDestPrefix()));
1551 nextHopManager.removeNextHopPointer(nextHopManager
1552 .getLocalSelectGroupKey(vpnInstance.getVpnId(), vrfEntry.getDestPrefix()));
1556 //The flow/group entry has been deleted from config DS; need to clean up associated operational
1557 //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
1558 cleanUpOpDataForFib(vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(), vrfEntry);
1560 // Remove all fib entries configured due to interVpnLink, when nexthop is the opposite endPoint
1561 // of the interVpnLink.
1562 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
1563 if (optVpnUuid.isPresent()) {
1564 String vpnUuid = optVpnUuid.get();
1565 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
1566 Optional<InterVpnLinkDataComposite> optInterVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid);
1567 if (optInterVpnLink.isPresent()) {
1568 InterVpnLinkDataComposite interVpnLink = optInterVpnLink.get();
1569 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
1570 // This is route that points to the other endpoint of an InterVpnLink
1571 // In that case, we should look for the FIB table pointing to
1572 // LPortDispatcher table and remove it.
1573 removeInterVPNLinkRouteFlows(interVpnLink, vpnUuid, vrfEntry);
1581 private void makeLFibTableEntry(BigInteger dpId, long label, @Nullable List<InstructionInfo> instructions,
1582 int priority, int addOrRemove, TypedWriteTransaction<Configuration> tx) {
1584 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1585 newTx -> makeLFibTableEntry(dpId, label, instructions, priority, addOrRemove, newTx)), LOG,
1586 "Error making LFIB table entry");
1590 List<MatchInfo> matches = new ArrayList<>();
1591 matches.add(MatchEthernetType.MPLS_UNICAST);
1592 matches.add(new MatchMplsLabel(label));
1594 // Install the flow entry in L3_LFIB_TABLE
1595 String flowRef = FibUtil.getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, label, priority);
1597 FlowEntity flowEntity;
1598 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef, priority, flowRef, 0, 0,
1599 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
1600 Flow flow = flowEntity.getFlowBuilder().build();
1601 String flowId = flowEntity.getFlowId();
1602 FlowKey flowKey = new FlowKey(new FlowId(flowId));
1603 Node nodeDpn = FibUtil.buildDpnNode(dpId);
1604 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1605 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1606 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
1608 if (addOrRemove == NwConstants.ADD_FLOW) {
1609 tx.put(flowInstanceId, flow, CREATE_MISSING_PARENTS);
1611 tx.delete(flowInstanceId);
1614 LOG.debug("LFIB Entry for dpID {} : label : {} instructions {} : key {} {} successfully",
1615 dpId, label, instructions, flowKey, NwConstants.ADD_FLOW == addOrRemove ? "ADDED" : "REMOVED");
1618 public void populateFibOnNewDpn(final BigInteger dpnId, final long vpnId, final String rd,
1619 final FutureCallback<List<Void>> callback) {
1620 LOG.trace("New dpn {} for vpn {} : populateFibOnNewDpn", dpnId, rd);
1621 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1623 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1624 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1625 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1626 LogicalDatastoreType.CONFIGURATION, id);
1627 List<ListenableFuture<Void>> futures = new ArrayList<>();
1628 if (!vrfTable.isPresent()) {
1629 LOG.info("populateFibOnNewDpn: dpn: {}: VRF Table not yet available for RD {}", dpnId, rd);
1630 if (callback != null) {
1631 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1632 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1636 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1637 futures.add(retryingTxRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
1638 for (final VrfEntry vrfEntry : nullToEmpty(vrfTable.get().getVrfEntry())) {
1639 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1640 if (subnetRoute != null) {
1641 long elanTag = subnetRoute.getElantag();
1642 installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, tx);
1643 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry, NwConstants.ADD_FLOW,
1647 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1648 if (routerInt != null) {
1649 LOG.trace("Router augmented vrfentry found rd:{}, uuid:{}, ip:{}, mac:{}",
1650 rd, routerInt.getUuid(), routerInt.getIpAddress(), routerInt.getMacAddress());
1651 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1652 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1653 NwConstants.ADD_FLOW);
1656 //Handle local flow creation for imports
1657 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1658 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1659 if (optionalLabel.isPresent()) {
1660 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1661 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1662 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList, lri)) {
1663 if (Objects.equals(lri.getDpnId(), dpnId)) {
1665 int etherType = NWUtil.getEtherTypeFromIpPrefix(
1666 vrfEntry.getDestPrefix());
1667 createLocalFibEntry(vpnId, rd, vrfEntry, etherType);
1668 } catch (IllegalArgumentException ex) {
1669 LOG.warn("Unable to get etherType for IP Prefix {}",
1670 vrfEntry.getDestPrefix());
1677 boolean shouldCreateRemoteFibEntry = shouldCreateFibEntryForVrfAndVpnIdOnDpn(vpnId,
1679 if (shouldCreateRemoteFibEntry) {
1680 LOG.trace("Will create remote FIB entry for vrfEntry {} on DPN {}", vrfEntry, dpnId);
1681 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1682 List<SubTransaction> txnObjects = new ArrayList<>();
1683 bgpRouteVrfEntryHandler.createRemoteFibEntry(dpnId, vpnId,
1684 vrfTable.get().getRouteDistinguisher(), vrfEntry,
1685 TransactionAdapter.toWriteTransaction(tx), txnObjects);
1687 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
1693 if (callback != null) {
1694 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1695 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1702 public void populateExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1703 final String localNextHopIp, final String remoteNextHopIp) {
1704 LOG.trace("populateExternalRoutesOnDpn : dpn {}, vpn {}, rd {}, localNexthopIp {} , remoteNextHopIp {} ",
1705 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1706 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1707 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1708 List<SubTransaction> txnObjects = new ArrayList<>();
1709 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1710 if (vrfTable.isPresent()) {
1711 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1712 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1713 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1714 nullToEmpty(vrfTable.get().getVrfEntry()).stream()
1715 .filter(vrfEntry -> RouteOrigin.BGP == RouteOrigin.value(vrfEntry.getOrigin()))
1716 .forEach(bgpRouteVrfEntryHandler.getConsumerForCreatingRemoteFib(dpnId, vpnId,
1717 rd, remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx), txnObjects));
1723 public void manageRemoteRouteOnDPN(final boolean action,
1724 final BigInteger localDpnId,
1727 final String destPrefix,
1728 final String destTepIp,
1730 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1732 if (vpnInstance == null) {
1733 LOG.error("VpnInstance for rd {} not present for prefix {}", rd, destPrefix);
1737 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, localDpnId),
1738 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1739 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1740 VrfTablesKey vrfTablesKey = new VrfTablesKey(rd);
1741 VrfEntry vrfEntry = getVrfEntry(dataBroker, rd, destPrefix);
1742 if (vrfEntry == null) {
1745 LOG.trace("manageRemoteRouteOnDPN :: action {}, DpnId {}, vpnId {}, rd {}, destPfx {}",
1746 action, localDpnId, vpnId, rd, destPrefix);
1747 List<RoutePaths> routePathList = vrfEntry.getRoutePaths();
1748 VrfEntry modVrfEntry;
1749 if (routePathList == null || routePathList.isEmpty()) {
1750 modVrfEntry = FibHelper.getVrfEntryBuilder(vrfEntry, label,
1751 Collections.singletonList(destTepIp),
1752 RouteOrigin.value(vrfEntry.getOrigin()), null /* parentVpnRd */).build();
1754 modVrfEntry = vrfEntry;
1758 LOG.trace("manageRemoteRouteOnDPN updated(add) vrfEntry :: {}", modVrfEntry);
1759 createRemoteFibEntry(localDpnId, vpnId, vrfTablesKey.getRouteDistinguisher(),
1762 LOG.trace("manageRemoteRouteOnDPN updated(remove) vrfEntry :: {}", modVrfEntry);
1763 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnInstance.getVpnId(),
1764 vrfEntry.getDestPrefix());
1765 if (usedRds.size() > 1) {
1766 LOG.debug("The extra route prefix is still present in some DPNs");
1769 //Is this fib route an extra route? If yes, get the nexthop which would be
1770 //an adjacency in the vpn
1771 Optional<Routes> extraRouteOptional = Optional.absent();
1772 if (usedRds.size() != 0) {
1773 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1774 fibUtil.getVpnNameFromId(vpnInstance.getVpnId()),
1775 usedRds.get(0), vrfEntry.getDestPrefix());
1777 baseVrfEntryHandler.deleteRemoteRoute(null, localDpnId, vpnId, vrfTablesKey, modVrfEntry,
1778 extraRouteOptional, TransactionAdapter.toWriteTransaction(tx));
1784 public void cleanUpDpnForVpn(final BigInteger dpnId, final long vpnId, final String rd,
1785 final FutureCallback<List<Void>> callback) {
1786 LOG.trace("cleanUpDpnForVpn: Remove dpn {} for vpn {} : cleanUpDpnForVpn", dpnId, rd);
1787 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1789 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1790 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1791 List<SubTransaction> txnObjects = new ArrayList<>();
1792 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1793 LogicalDatastoreType.CONFIGURATION, id);
1794 List<ListenableFuture<Void>> futures = new ArrayList<>();
1795 if (vrfTable.isPresent()) {
1796 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1797 futures.add(retryingTxRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1798 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1799 for (final VrfEntry vrfEntry : nullToEmpty(vrfTable.get().getVrfEntry())) {
1800 /* parentRd is only filled for external PNF cases where the interface on the external
1801 * network VPN are used to cleanup the flows. For all other cases, use "rd" for
1802 * #fibUtil.isInterfacePresentInDpn().
1804 String parentRd = vrfEntry.getParentVpnRd() != null ? vrfEntry.getParentVpnRd()
1806 /* Handle subnet routes here */
1807 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1808 if (subnetRoute != null && !fibUtil
1809 .isInterfacePresentInDpn(parentRd, dpnId)) {
1810 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Cleaning subnetroute {} on dpn {}"
1811 + " for vpn {}", vrfEntry.getDestPrefix(), dpnId, rd);
1812 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1813 NwConstants.DEL_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
1814 List<RoutePaths> routePaths = vrfEntry.getRoutePaths();
1815 if (routePaths != null) {
1816 for (RoutePaths routePath : routePaths) {
1817 makeLFibTableEntry(dpnId, routePath.getLabel(), null,
1818 DEFAULT_FIB_FLOW_PRIORITY,
1819 NwConstants.DEL_FLOW, tx);
1820 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Released subnetroute label {}"
1821 + " for rd {} prefix {}", routePath.getLabel(), rd,
1822 vrfEntry.getDestPrefix());
1825 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry,
1826 NwConstants.DEL_FLOW, tx);
1829 // ping responder for router interfaces
1830 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1831 if (routerInt != null) {
1832 LOG.trace("Router augmented vrfentry found for rd:{}, uuid:{}, ip:{}, mac:{}",
1833 rd, routerInt.getUuid(), routerInt.getIpAddress(),
1834 routerInt.getMacAddress());
1835 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1836 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1837 NwConstants.DEL_FLOW);
1841 //Handle local flow deletion for imports
1842 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1843 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1844 if (optionalLabel.isPresent()) {
1845 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1846 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1847 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList,
1848 lri) && Objects.equals(lri.getDpnId(), dpnId)) {
1849 deleteLocalFibEntry(vpnId, rd, vrfEntry);
1854 // Passing null as we don't know the dpn
1855 // to which prefix is attached at this point
1856 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1857 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1858 Optional<Routes> extraRouteOptional;
1859 //Is this fib route an extra route? If yes, get the nexthop which would be
1860 //an adjacency in the vpn
1861 if (usedRds != null && !usedRds.isEmpty()) {
1862 if (usedRds.size() > 1) {
1863 LOG.error("The extra route prefix is still present in some DPNs");
1866 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
1867 usedRds.get(0), vrfEntry.getDestPrefix());
1871 extraRouteOptional = Optional.absent();
1873 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1874 bgpRouteVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1875 vrfTable.get().key(), vrfEntry, extraRouteOptional,
1876 TransactionAdapter.toWriteTransaction(tx), txnObjects);
1878 if (subnetRoute == null || !fibUtil
1879 .isInterfacePresentInDpn(parentRd, dpnId)) {
1880 baseVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1881 vrfTable.get().key(), vrfEntry, extraRouteOptional,
1882 TransactionAdapter.toWriteTransaction(tx));
1888 if (callback != null) {
1889 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1890 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1893 LOG.error("cleanUpDpnForVpn: No vrf table found for rd {} vpnId {} dpn {}", rd, vpnId, dpnId);
1900 public void cleanUpExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1901 final String localNextHopIp, final String remoteNextHopIp) {
1902 LOG.trace("cleanUpExternalRoutesOnDpn : cleanup remote routes on dpn {} for vpn {}, rd {}, "
1903 + " localNexthopIp {} , remoteNexhtHopIp {}",
1904 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1905 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1906 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1907 List<SubTransaction> txnObjects = new ArrayList<>();
1908 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1909 if (vrfTable.isPresent()) {
1910 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1912 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1913 return Collections.singletonList(
1914 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
1915 tx -> nullToEmpty(vrfTable.get().getVrfEntry()).stream()
1916 .filter(vrfEntry -> RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP)
1917 .forEach(bgpRouteVrfEntryHandler.getConsumerForDeletingRemoteFib(dpnId, vpnId,
1918 remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx),
1925 public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
1926 InstanceIdentifierBuilder<VrfTables> idBuilder =
1927 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
1928 return idBuilder.build();
1931 private String getInterVpnFibFlowRef(String interVpnLinkName, String prefix, String nextHop) {
1932 return FLOWID_PREFIX + interVpnLinkName + NwConstants.FLOWID_SEPARATOR + prefix + NwConstants
1933 .FLOWID_SEPARATOR + nextHop;
1936 private String getTableMissFlowRef(BigInteger dpnId, short tableId, int tableMiss) {
1937 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR
1938 + tableMiss + FLOWID_PREFIX;
1942 private VrfEntry getVrfEntry(DataBroker broker, String rd, String ipPrefix) {
1943 InstanceIdentifier<VrfEntry> vrfEntryId = InstanceIdentifier.builder(FibEntries.class)
1944 .child(VrfTables.class, new VrfTablesKey(rd))
1945 .child(VrfEntry.class, new VrfEntryKey(ipPrefix)).build();
1946 Optional<VrfEntry> vrfEntry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
1947 if (vrfEntry.isPresent()) {
1948 return vrfEntry.get();
1953 public void removeInterVPNLinkRouteFlows(final InterVpnLinkDataComposite interVpnLink,
1954 final String vpnName,
1955 final VrfEntry vrfEntry) {
1956 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null && vrfEntry.getRoutePaths().size() == 1);
1958 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
1959 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
1961 if (targetDpns.isEmpty()) {
1962 LOG.warn("Could not find DPNs for VPN {} in InterVpnLink {}", vpnName, interVpnLinkName);
1966 java.util.Optional<String> optNextHop = FibUtil.getFirstNextHopAddress(vrfEntry);
1967 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1971 optNextHop.ifPresent(nextHop -> {
1972 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, vrfEntry.getDestPrefix(), nextHop);
1973 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
1974 Flow flow = new FlowBuilder().withKey(flowKey).setId(new FlowId(flowRef))
1975 .setTableId(NwConstants.L3_FIB_TABLE).setFlowName(flowRef).build();
1977 LOG.trace("Removing flow in FIB table for interVpnLink {} key {}", interVpnLinkName, flowRef);
1978 for (BigInteger dpId : targetDpns) {
1979 LOG.debug("Removing flow: VrfEntry=[prefix={} nexthop={}] dpn {} for InterVpnLink {} in FIB",
1980 vrfEntry.getDestPrefix(), nextHop, dpId, interVpnLinkName);
1982 mdsalManager.removeFlow(dpId, flow);
1988 optLabel.ifPresent(label -> {
1989 LOG.trace("Removing flow in FIB table for interVpnLink {}", interVpnLinkName);
1991 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
1992 for (BigInteger dpId : targetDpns) {
1993 LOG.debug("Removing flow: VrfEntry=[prefix={} label={}] dpn {} for InterVpnLink {} in LFIB",
1994 vrfEntry.getDestPrefix(), label, dpId, interVpnLinkName);
1995 makeLFibTableEntry(dpId, label, /*instructions*/null, LFIB_INTERVPN_PRIORITY, NwConstants.DEL_FLOW,
1998 }), LOG, "Error removing flows");
2002 private boolean isPrefixAndNextHopPresentInLri(String prefix,
2003 List<String> nextHopAddressList, LabelRouteInfo lri) {
2004 return lri != null && Objects.equals(lri.getPrefix(), prefix)
2005 && nextHopAddressList.contains(lri.getNextHopIpList().get(0));
2008 private boolean shouldCreateFibEntryForVrfAndVpnIdOnDpn(Long vpnId, VrfEntry vrfEntry, BigInteger dpnId) {
2009 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
2013 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
2014 if (prefix != null) {
2015 BigInteger prefixDpnId = prefix.getDpnId();
2016 if (dpnId.equals(prefixDpnId)) {
2017 LOG.trace("Should not create remote FIB entry for vrfEntry {} on DPN {}",