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.genius.mdsalutil.NWUtil.isIpv4Address;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.MoreExecutors;
18 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19 import java.math.BigInteger;
20 import java.net.InetAddress;
21 import java.net.UnknownHostException;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.concurrent.Callable;
28 import java.util.concurrent.CopyOnWriteArrayList;
29 import javax.annotation.PostConstruct;
30 import javax.inject.Inject;
31 import javax.inject.Singleton;
32 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
33 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
34 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
35 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
36 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
37 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
38 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
39 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
40 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
41 import org.opendaylight.genius.infra.RetryingManagedNewTransactionRunner;
42 import org.opendaylight.genius.mdsalutil.ActionInfo;
43 import org.opendaylight.genius.mdsalutil.FlowEntity;
44 import org.opendaylight.genius.mdsalutil.InstructionInfo;
45 import org.opendaylight.genius.mdsalutil.MDSALUtil;
46 import org.opendaylight.genius.mdsalutil.MatchInfo;
47 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
48 import org.opendaylight.genius.mdsalutil.NWUtil;
49 import org.opendaylight.genius.mdsalutil.NwConstants;
50 import org.opendaylight.genius.mdsalutil.UpgradeState;
51 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
52 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
53 import org.opendaylight.genius.mdsalutil.actions.ActionPopMpls;
54 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
55 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
56 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
57 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
58 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
59 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
60 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
61 import org.opendaylight.genius.mdsalutil.matches.MatchMplsLabel;
62 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
63 import org.opendaylight.genius.utils.ServiceIndex;
64 import org.opendaylight.genius.utils.batching.SubTransaction;
65 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
66 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
67 import org.opendaylight.netvirt.elanmanager.api.IElanService;
68 import org.opendaylight.netvirt.fibmanager.NexthopManager.AdjacencyResult;
69 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
70 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
71 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
72 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
73 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
74 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
75 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.LabelRouteMap;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.RouterInterface;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRoute;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfo;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoBuilder;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoKey;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOp;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.PrefixesBuilder;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
106 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
107 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.link.states.InterVpnLinkState.State;
108 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
109 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
110 import org.slf4j.Logger;
111 import org.slf4j.LoggerFactory;
115 public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry, VrfEntryListener> {
117 private static final Logger LOG = LoggerFactory.getLogger(VrfEntryListener.class);
118 private static final String FLOWID_PREFIX = "L3.";
119 private static final BigInteger COOKIE_VM_FIB_TABLE = new BigInteger("8000003", 16);
120 private static final int DEFAULT_FIB_FLOW_PRIORITY = 10;
121 private static final int IPV4_ADDR_PREFIX_LENGTH = 32;
122 private static final int LFIB_INTERVPN_PRIORITY = 15;
123 public static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
124 private static final int MAX_RETRIES = 3;
125 private static final BigInteger COOKIE_TABLE_MISS = new BigInteger("8000004", 16);
127 private final DataBroker dataBroker;
128 private final ManagedNewTransactionRunner txRunner;
129 private final RetryingManagedNewTransactionRunner retryingTxRunner;
130 private final IMdsalApiManager mdsalManager;
131 private final NexthopManager nextHopManager;
132 private final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler;
133 private final BaseVrfEntryHandler baseVrfEntryHandler;
134 private final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler;
135 private final JobCoordinator jobCoordinator;
136 private final IElanService elanManager;
137 private final FibUtil fibUtil;
138 private final InterVpnLinkCache interVpnLinkCache;
139 private final List<AutoCloseable> closeables = new CopyOnWriteArrayList<>();
140 private final UpgradeState upgradeState;
141 private final DataTreeEventCallbackRegistrar eventCallbacks;
144 public VrfEntryListener(final DataBroker dataBroker, final IMdsalApiManager mdsalApiManager,
145 final NexthopManager nexthopManager,
146 final IElanService elanManager,
147 final BaseVrfEntryHandler vrfEntryHandler,
148 final BgpRouteVrfEntryHandler bgpRouteVrfEntryHandler,
149 final RouterInterfaceVrfEntryHandler routerInterfaceVrfEntryHandler,
150 final JobCoordinator jobCoordinator,
151 final FibUtil fibUtil,
152 final InterVpnLinkCache interVpnLinkCache,
153 final UpgradeState upgradeState,
154 final DataTreeEventCallbackRegistrar eventCallbacks) {
155 super(VrfEntry.class, VrfEntryListener.class);
156 this.dataBroker = dataBroker;
157 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
158 this.retryingTxRunner = new RetryingManagedNewTransactionRunner(dataBroker, MAX_RETRIES);
159 this.mdsalManager = mdsalApiManager;
160 this.nextHopManager = nexthopManager;
161 this.elanManager = elanManager;
162 this.baseVrfEntryHandler = vrfEntryHandler;
163 this.bgpRouteVrfEntryHandler = bgpRouteVrfEntryHandler;
164 this.routerInterfaceVrfEntryHandler = routerInterfaceVrfEntryHandler;
165 this.jobCoordinator = jobCoordinator;
166 this.fibUtil = fibUtil;
167 this.interVpnLinkCache = interVpnLinkCache;
168 this.upgradeState = upgradeState;
169 this.eventCallbacks = eventCallbacks;
175 LOG.info("{} init", getClass().getSimpleName());
176 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
180 @SuppressWarnings("checkstyle:IllegalCatch")
181 public void close() {
182 closeables.forEach(c -> {
185 } catch (Exception e) {
186 LOG.warn("Error closing {}", c, e);
192 protected VrfEntryListener getDataTreeChangeListener() {
193 return VrfEntryListener.this;
197 protected InstanceIdentifier<VrfEntry> getWildCardPath() {
198 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class).child(VrfEntry.class);
202 protected void add(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
203 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
204 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
205 LOG.debug("ADD: Adding Fib Entry rd {} prefix {} route-paths {}",
206 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
207 addFibEntries(identifier, vrfEntry, rd);
208 LOG.info("ADD: Added Fib Entry rd {} prefix {} route-paths {}",
209 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
212 //This method is temporary. Eventually Factory design pattern will be used to get
213 // right VrfEntryhandle and invoke its methods.
214 private void addFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
215 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
216 bgpRouteVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
219 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
220 LOG.info("EVPN flows need to be programmed.");
221 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
222 nextHopManager, jobCoordinator, elanManager, fibUtil, upgradeState, eventCallbacks);
223 evpnVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
224 closeables.add(evpnVrfEntryHandler);
227 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
228 if (routerInt != null) {
229 // ping responder for router interfaces
230 routerInterfaceVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
233 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
234 createFibEntries(identifier, vrfEntry);
240 protected void remove(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry) {
241 Preconditions.checkNotNull(vrfEntry, "VrfEntry should not be null or empty.");
242 String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
243 LOG.debug("REMOVE: Removing Fib Entry rd {} prefix {} route-paths {}",
244 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
245 removeFibEntries(identifier, vrfEntry, rd);
246 LOG.info("REMOVE: Removed Fib Entry rd {} prefix {} route-paths {}",
247 rd, vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
250 //This method is temporary. Eventually Factory design pattern will be used to get
251 // right VrfEntryhandle and invoke its methods.
252 private void removeFibEntries(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
253 if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
254 LOG.info("EVPN flows to be deleted");
255 EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
256 nextHopManager, jobCoordinator, elanManager, fibUtil, upgradeState, eventCallbacks);
257 evpnVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
258 closeables.add(evpnVrfEntryHandler);
261 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
262 if (routerInt != null) {
263 // ping responder for router interfaces
264 routerInterfaceVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
267 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
268 deleteFibEntries(identifier, vrfEntry);
271 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
272 bgpRouteVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
278 // "Redundant nullcheck of originalRoutePath, which is known to be non-null" - the null checking for
279 // originalRoutePath is a little dicey - safest to keep the checking even if not needed.
280 @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE")
281 protected void update(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update) {
282 Preconditions.checkNotNull(update, "VrfEntry should not be null or empty.");
283 final String rd = identifier.firstKeyOf(VrfTables.class).getRouteDistinguisher();
284 LOG.debug("UPDATE: Updating Fib Entries to rd {} prefix {} route-paths {} origin {} old-origin {}", rd,
285 update.getDestPrefix(), update.getRoutePaths(), update.getOrigin(), original.getOrigin());
286 // Handle BGP Routes first
287 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.BGP) {
288 bgpRouteVrfEntryHandler.updateFlows(identifier, original, update, rd);
289 LOG.info("UPDATE: Updated BGP advertised Fib Entry with rd {} prefix {} route-paths {}",
290 rd, update.getDestPrefix(), update.getRoutePaths());
294 if (RouteOrigin.value(update.getOrigin()) == RouteOrigin.STATIC) {
295 List<RoutePaths> originalRoutePath = original.getRoutePaths();
296 List<RoutePaths> updateRoutePath = update.getRoutePaths();
297 LOG.info("UPDATE: Original route-path {} update route-path {} ", originalRoutePath, updateRoutePath);
299 //Updates need to be handled for extraroute even if original vrf entry route path is null or
300 //updated vrf entry route path is null. This can happen during tunnel events.
301 Optional<VpnInstanceOpDataEntry> optVpnInstance = fibUtil.getVpnInstanceOpData(rd);
302 List<String> usedRds = new ArrayList<>();
303 if (optVpnInstance.isPresent()) {
304 usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,optVpnInstance.get().getVpnId(),
305 update.getDestPrefix());
307 // If original VRF Entry had nexthop null , but update VRF Entry
308 // has nexthop , route needs to be created on remote Dpns
309 if (originalRoutePath == null || originalRoutePath.isEmpty()
310 && updateRoutePath != null && !updateRoutePath.isEmpty() && usedRds.isEmpty()) {
311 // TODO(vivek): Though ugly, Not handling this code now, as each
312 // tep add event will invoke flow addition
313 LOG.trace("Original VRF entry NH is null for destprefix {}. And the prefix is not an extra route."
314 + " This event is IGNORED here.", update.getDestPrefix());
318 // If original VRF Entry had valid nexthop , but update VRF Entry
319 // has nexthop empty'ed out, route needs to be removed from remote Dpns
320 if (updateRoutePath == null || updateRoutePath.isEmpty()
321 && originalRoutePath != null && !originalRoutePath.isEmpty() && usedRds.isEmpty()) {
322 LOG.trace("Original VRF entry had valid NH for destprefix {}. And the prefix is not an extra route."
323 + "This event is IGNORED here.", update.getDestPrefix());
326 //Update the used rds and vpntoextraroute containers only for the deleted nextHops.
327 List<String> nextHopsRemoved = FibHelper.getNextHopListFromRoutePaths(original);
328 nextHopsRemoved.removeAll(FibHelper.getNextHopListFromRoutePaths(update));
329 List<ListenableFuture<Void>> futures = new ArrayList<>();
330 ListenableFuture<Void> configFuture =
331 txRunner.callWithNewWriteOnlyTransactionAndSubmit(configTx ->
332 futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(operTx ->
333 nextHopsRemoved.parallelStream()
334 .forEach(nextHopRemoved -> fibUtil.updateUsedRdAndVpnToExtraRoute(
335 configTx, operTx, nextHopRemoved, rd, update.getDestPrefix())))));
336 futures.add(configFuture);
337 Futures.addCallback(configFuture, new FutureCallback<Void>() {
339 public void onSuccess(Void result) {
340 createFibEntries(identifier, update);
341 LOG.info("UPDATE: Updated static Fib Entry with rd {} prefix {} route-paths {}",
342 rd, update.getDestPrefix(), update.getRoutePaths());
346 public void onFailure(Throwable throwable) {
347 LOG.error("Exception encountered while submitting operational future for update vrfentry {}",
350 }, MoreExecutors.directExecutor());
354 //Handle all other routes only on a cluster reboot
355 if (original.equals(update)) {
357 createFibEntries(identifier, update);
358 LOG.info("UPDATE: Updated Non-static Fib Entry with rd {} prefix {} route-paths {}",
359 rd, update.getDestPrefix(), update.getRoutePaths());
363 LOG.info("UPDATE: Ignoring update for FIB entry with rd {} prefix {} route-origin {} route-paths {}",
364 rd, update.getDestPrefix(), update.getOrigin(), update.getRoutePaths());
367 private void createFibEntries(final InstanceIdentifier<VrfEntry> vrfEntryIid, final VrfEntry vrfEntry) {
368 final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
369 List<SubTransaction> txnObjects = new ArrayList<>();
370 final VpnInstanceOpDataEntry vpnInstance =
371 fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
372 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
373 Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId()
374 + " has null vpnId!");
375 final Collection<VpnToDpnList> vpnToDpnList;
376 if (vrfEntry.getParentVpnRd() != null
377 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
378 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
379 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
380 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
381 vpnInstance.getVpnToDpnList();
382 LOG.info("createFibEntries: Processing creation of PNF FIB entry with rd {} prefix {}",
383 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
385 vpnToDpnList = vpnInstance.getVpnToDpnList();
387 final Long vpnId = vpnInstance.getVpnId();
388 final String rd = vrfTableKey.getRouteDistinguisher();
389 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
390 if (subnetRoute != null) {
391 final long elanTag = subnetRoute.getElantag();
392 LOG.trace("SUBNETROUTE: createFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
393 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
394 if (vpnToDpnList != null) {
395 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
396 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
397 for (final VpnToDpnList curDpn : vpnToDpnList) {
398 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
399 installSubnetRouteInFib(curDpn.getDpnId(), elanTag, rd, vpnId, vrfEntry, tx);
400 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnId.longValue(),
401 vrfEntry, NwConstants.ADD_FLOW, tx);
408 // Get etherType value based on the IpPrefix address family type
411 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
412 } catch (IllegalArgumentException ex) {
413 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
417 final List<BigInteger> localDpnIdList = createLocalFibEntry(vpnInstance.getVpnId(), rd, vrfEntry, etherType);
418 if (!localDpnIdList.isEmpty() && vpnToDpnList != null) {
419 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
420 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
421 synchronized (vpnInstance.getVpnInstanceName().intern()) {
422 for (VpnToDpnList vpnDpn : vpnToDpnList) {
423 if (!localDpnIdList.contains(vpnDpn.getDpnId())) {
424 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
426 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
427 bgpRouteVrfEntryHandler.createRemoteFibEntry(vpnDpn.getDpnId(),
428 vpnId, vrfTableKey.getRouteDistinguisher(), vrfEntry, tx,
431 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
432 vrfTableKey.getRouteDistinguisher(), vrfEntry, tx);
434 } catch (NullPointerException e) {
435 LOG.error("Failed to get create remote fib flows for prefix {} ",
436 vrfEntry.getDestPrefix(), e);
445 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
446 if (optVpnUuid.isPresent()) {
447 String vpnUuid = optVpnUuid.get();
448 InterVpnLinkDataComposite interVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid).orNull();
449 if (interVpnLink != null) {
450 LOG.debug("InterVpnLink {} found in Cache linking Vpn {}", interVpnLink.getInterVpnLinkName(), vpnUuid);
451 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
452 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
453 // This is an static route that points to the other endpoint of an InterVpnLink
454 // In that case, we should add another entry in FIB table pointing to LPortDispatcher table.
455 installIVpnLinkSwitchingFlows(interVpnLink, vpnUuid, vrfEntry, vpnId);
456 installInterVpnRouteInLFib(interVpnLink, vpnUuid, vrfEntry, etherType);
463 void refreshFibTables(String rd, String prefix) {
464 InstanceIdentifier<VrfEntry> vrfEntryId =
465 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
466 .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
467 Optional<VrfEntry> vrfEntry = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
468 if (vrfEntry.isPresent()) {
469 createFibEntries(vrfEntryId, vrfEntry.get());
473 private Prefixes updateVpnReferencesInLri(LabelRouteInfo lri, String vpnInstanceName, boolean isPresentInList) {
474 LOG.debug("updating LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
475 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
476 prefixBuilder.setDpnId(lri.getDpnId());
477 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
478 prefixBuilder.setIpAddress(lri.getPrefix());
479 // Increment the refCount here
480 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
481 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
482 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri);
483 if (!isPresentInList) {
484 LOG.debug("vpnName {} is not present in LRI with label {}..", vpnInstanceName, lri.getLabel());
485 List<String> vpnInstanceNames = lri.getVpnInstanceList();
486 vpnInstanceNames.add(vpnInstanceName);
487 builder.setVpnInstanceList(vpnInstanceNames);
488 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
490 LOG.debug("vpnName {} is present in LRI with label {}..", vpnInstanceName, lri.getLabel());
492 return prefixBuilder.build();
495 void installSubnetRouteInFib(final BigInteger dpnId, final long elanTag, final String rd,
496 final long vpnId, final VrfEntry vrfEntry, WriteTransaction tx) {
498 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
499 newTx -> installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, newTx)), LOG,
500 "Error installing subnet route in FIB");
505 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
506 } catch (IllegalArgumentException ex) {
507 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
510 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
511 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
512 synchronized (label.toString().intern()) {
513 LabelRouteInfo lri = getLabelRouteInfo(label);
514 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
516 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
517 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
518 fibUtil.getVpnInstanceOpData(rd);
519 if (vpnInstanceOpDataEntryOptional.isPresent()) {
520 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
521 if (!lri.getVpnInstanceList().contains(vpnInstanceName)) {
522 updateVpnReferencesInLri(lri, vpnInstanceName, false);
526 LOG.debug("SUBNETROUTE: installSubnetRouteInFib: Fetched labelRouteInfo for label {} interface {}"
527 + " and got dpn {}", label, lri.getVpnInterfaceName(), lri.getDpnId());
531 final List<InstructionInfo> instructions = new ArrayList<>();
532 BigInteger subnetRouteMeta = BigInteger.valueOf(elanTag).shiftLeft(24)
533 .or(BigInteger.valueOf(vpnId).shiftLeft(1));
534 instructions.add(new InstructionWriteMetadata(subnetRouteMeta, MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
535 instructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
536 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
537 NwConstants.ADD_FLOW, tx, null);
538 if (vrfEntry.getRoutePaths() != null) {
539 for (RoutePaths routePath : vrfEntry.getRoutePaths()) {
540 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
541 List<ActionInfo> actionsInfos = new ArrayList<>();
542 // reinitialize instructions list for LFIB Table
543 final List<InstructionInfo> LFIBinstructions = new ArrayList<>();
544 actionsInfos.add(new ActionPopMpls(etherType));
545 LFIBinstructions.add(new InstructionApplyActions(actionsInfos));
546 LFIBinstructions.add(new InstructionWriteMetadata(subnetRouteMeta,
547 MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
548 LFIBinstructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
550 makeLFibTableEntry(dpnId, routePath.getLabel(), LFIBinstructions, DEFAULT_FIB_FLOW_PRIORITY,
551 NwConstants.ADD_FLOW, tx);
557 private void installSubnetBroadcastAddrDropRule(final BigInteger dpnId, final String rd, final long vpnId,
558 final VrfEntry vrfEntry, int addOrRemove, WriteTransaction tx) {
559 List<MatchInfo> matches = new ArrayList<>();
561 LOG.debug("SUBNETROUTE: installSubnetBroadcastAddrDropRule: destPrefix {} rd {} vpnId {} dpnId {}",
562 vrfEntry.getDestPrefix(), rd, vpnId, dpnId);
563 String[] ipAddress = vrfEntry.getDestPrefix().split("/");
564 String subnetBroadcastAddr = FibUtil.getBroadcastAddressFromCidr(vrfEntry.getDestPrefix());
565 final int prefixLength = ipAddress.length == 1 ? 0 : Integer.parseInt(ipAddress[1]);
567 InetAddress destPrefix;
569 destPrefix = InetAddress.getByName(subnetBroadcastAddr);
570 } catch (UnknownHostException e) {
571 LOG.error("Failed to get destPrefix for prefix {} rd {} VpnId {} DPN {}",
572 vrfEntry.getDestPrefix(), rd, vpnId, dpnId, e);
576 // Match on VpnId and SubnetBroadCast IP address
577 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
578 matches.add(MatchEthernetType.IPV4);
580 if (prefixLength != 0) {
581 matches.add(new MatchIpv4Destination(subnetBroadcastAddr, Integer.toString(IPV4_ADDR_PREFIX_LENGTH)));
584 //Action is to drop the packet
585 List<InstructionInfo> dropInstructions = new ArrayList<>();
586 List<ActionInfo> actionsInfos = new ArrayList<>();
587 actionsInfos.add(new ActionDrop());
588 dropInstructions.add(new InstructionApplyActions(actionsInfos));
590 int priority = DEFAULT_FIB_FLOW_PRIORITY + IPV4_ADDR_PREFIX_LENGTH;
591 String flowRef = FibUtil.getFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, rd, priority, destPrefix);
592 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef, priority,
593 flowRef, 0, 0, COOKIE_TABLE_MISS, matches, dropInstructions);
595 Flow flow = flowEntity.getFlowBuilder().build();
596 String flowId = flowEntity.getFlowId();
597 FlowKey flowKey = new FlowKey(new FlowId(flowId));
598 Node nodeDpn = FibUtil.buildDpnNode(dpnId);
600 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
601 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
602 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
604 if (addOrRemove == NwConstants.ADD_FLOW) {
605 tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId,flow, true);
607 tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
612 * For a given route, it installs a flow in LFIB that sets the lportTag of the other endpoint and sends to
613 * LportDispatcher table (via table 80)
615 private void installInterVpnRouteInLFib(final InterVpnLinkDataComposite interVpnLink, final String vpnName,
616 final VrfEntry vrfEntry, int etherType) {
617 // INTERVPN routes are routes in a Vpn1 that have been leaked to Vpn2. In DC-GW, this Vpn2 route is pointing
618 // to a list of DPNs where Vpn2's VpnLink was instantiated. In these DPNs LFIB must be programmed so that the
619 // packet is commuted from Vpn2 to Vpn1.
620 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
621 if (!interVpnLink.isActive()) {
622 LOG.warn("InterVpnLink {} is NOT ACTIVE. InterVpnLink flows for prefix={} wont be installed in LFIB",
623 interVpnLinkName, vrfEntry.getDestPrefix());
627 Optional<Long> optLportTag = interVpnLink.getEndpointLportTagByVpnName(vpnName);
628 if (!optLportTag.isPresent()) {
629 LOG.warn("Could not retrieve lportTag for VPN {} endpoint in InterVpnLink {}", vpnName, interVpnLinkName);
633 Long lportTag = optLportTag.get();
634 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).orElse(null);
636 LOG.error("Could not find label in vrfEntry=[prefix={} routePaths={}]. LFIB entry for InterVpnLink skipped",
637 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
640 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionPopMpls(etherType));
641 List<InstructionInfo> instructions = Arrays.asList(
642 new InstructionApplyActions(actionsInfos),
643 new InstructionWriteMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(lportTag.intValue(),
644 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
645 NwConstants.L3VPN_SERVICE_INDEX)),
646 MetaDataUtil.getMetaDataMaskForLPortDispatcher()),
647 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
648 List<String> interVpnNextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
649 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
651 for (BigInteger dpId : targetDpns) {
652 LOG.debug("Installing flow: VrfEntry=[prefix={} label={} nexthop={}] dpn {} for InterVpnLink {} in LFIB",
653 vrfEntry.getDestPrefix(), label, interVpnNextHopList, dpId, interVpnLink.getInterVpnLinkName());
655 makeLFibTableEntry(dpId, label, instructions, LFIB_INTERVPN_PRIORITY, NwConstants.ADD_FLOW,
662 * Installs the flows in FIB table that, for a given route, do the switching from one VPN to the other.
664 private void installIVpnLinkSwitchingFlows(final InterVpnLinkDataComposite interVpnLink, final String vpnUuid,
665 final VrfEntry vrfEntry, long vpnTag) {
666 Preconditions.checkNotNull(interVpnLink, "InterVpnLink cannot be null");
667 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null
668 && vrfEntry.getRoutePaths().size() == 1);
669 String destination = vrfEntry.getDestPrefix();
670 String nextHop = vrfEntry.getRoutePaths().get(0).getNexthopAddress();
671 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
673 // After having received a static route, we should check if the vpn is part of an inter-vpn-link.
674 // In that case, we should populate the FIB table of the VPN pointing to LPortDisptacher table
675 // using as metadata the LPortTag associated to that vpn in the inter-vpn-link.
676 if (interVpnLink.getState().or(State.Error) != State.Active) {
677 LOG.warn("Route to {} with nexthop={} cannot be installed because the interVpnLink {} is not active",
678 destination, nextHop, interVpnLinkName);
682 Optional<Long> optOtherEndpointLportTag = interVpnLink.getOtherEndpointLportTagByVpnName(vpnUuid);
683 if (!optOtherEndpointLportTag.isPresent()) {
684 LOG.warn("Could not find suitable LportTag for the endpoint opposite to vpn {} in interVpnLink {}",
685 vpnUuid, interVpnLinkName);
689 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnUuid);
690 if (targetDpns.isEmpty()) {
691 LOG.warn("Could not find DPNs for endpoint opposite to vpn {} in interVpnLink {}",
692 vpnUuid, interVpnLinkName);
696 String[] values = destination.split("/");
697 String destPrefixIpAddress = values[0];
698 int prefixLength = values.length == 1 ? 0 : Integer.parseInt(values[1]);
700 List<MatchInfo> matches = new ArrayList<>();
701 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnTag), MetaDataUtil.METADATA_MASK_VRFID));
702 matches.add(MatchEthernetType.IPV4);
704 if (prefixLength != 0) {
705 matches.add(new MatchIpv4Destination(destPrefixIpAddress, Integer.toString(prefixLength)));
708 List<Instruction> instructions =
709 Arrays.asList(new InstructionWriteMetadata(
710 MetaDataUtil.getMetaDataForLPortDispatcher(optOtherEndpointLportTag.get().intValue(),
711 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants
712 .L3VPN_SERVICE_INDEX)),
713 MetaDataUtil.getMetaDataMaskForLPortDispatcher()).buildInstruction(0),
714 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE).buildInstruction(1));
716 int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
717 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, destination, nextHop);
718 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef, priority, flowRef, 0, 0,
719 COOKIE_VM_FIB_TABLE, matches, instructions);
721 LOG.trace("Installing flow in FIB table for vpn {} interVpnLink {} nextHop {} key {}",
722 vpnUuid, interVpnLink.getInterVpnLinkName(), nextHop, flowRef);
724 for (BigInteger dpId : targetDpns) {
726 LOG.debug("Installing flow: VrfEntry=[prefix={} route-paths={}] dpn {} for InterVpnLink {} in FIB",
727 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(),
728 dpId, interVpnLink.getInterVpnLinkName());
730 mdsalManager.installFlow(dpId, flowEntity);
734 private List<BigInteger> createLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry, int etherType) {
735 List<BigInteger> returnLocalDpnId = new ArrayList<>();
736 String localNextHopIP = vrfEntry.getDestPrefix();
737 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
738 String vpnName = fibUtil.getVpnNameFromId(vpnId);
739 if (localNextHopInfo == null) {
740 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, localNextHopIP);
741 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
742 vpnName, usedRds, localNextHopIP);
743 if (LOG.isDebugEnabled()) {
744 LOG.debug("Creating Local fib entry with vpnName {} usedRds {} localNextHopIP {} vpnExtraRoutes {}",
745 vpnName, usedRds, localNextHopIP, vpnExtraRoutes);
747 boolean localNextHopSeen = false;
748 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
749 for (Routes vpnExtraRoute : vpnExtraRoutes) {
751 if (isIpv4Address(vpnExtraRoute.getNexthopIpList().get(0))) {
752 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
754 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
756 Prefixes localNextHopInfoLocal = fibUtil.getPrefixToInterface(vpnId,
758 if (localNextHopInfoLocal != null) {
759 localNextHopSeen = true;
761 checkCreateLocalFibEntry(localNextHopInfoLocal, localNextHopInfoLocal.getIpAddress(),
762 vpnId, rd, vrfEntry, vpnExtraRoute, vpnExtraRoutes, etherType);
763 returnLocalDpnId.add(dpnId);
766 if (!localNextHopSeen && RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
767 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
768 if (optionalLabel.isPresent()) {
769 Long label = optionalLabel.get();
770 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
771 synchronized (label.toString().intern()) {
772 LabelRouteInfo lri = getLabelRouteInfo(label);
773 if (isPrefixAndNextHopPresentInLri(localNextHopIP, nextHopAddressList, lri)) {
774 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
775 fibUtil.getVpnInstanceOpData(rd);
776 if (vpnInstanceOpDataEntryOptional.isPresent()) {
777 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
778 if (lri.getVpnInstanceList().contains(vpnInstanceName)) {
779 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, true);
780 localNextHopIP = lri.getPrefix();
782 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, false);
783 localNextHopIP = lri.getPrefix();
786 if (localNextHopInfo != null) {
787 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
788 label, localNextHopInfo.getVpnInterfaceName(), lri.getDpnId());
789 if (vpnExtraRoutes.isEmpty()) {
790 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
791 vpnId, rd, vrfEntry, null, vpnExtraRoutes, etherType);
792 returnLocalDpnId.add(dpnId);
794 for (Routes extraRoutes : vpnExtraRoutes) {
795 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
796 vpnId, rd, vrfEntry, extraRoutes, vpnExtraRoutes, etherType);
797 returnLocalDpnId.add(dpnId);
805 if (returnLocalDpnId.isEmpty()) {
806 LOG.error("Local DPNID is empty for rd {}, vpnId {}, vrfEntry {}", rd, vpnId, vrfEntry);
809 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP, vpnId,
810 rd, vrfEntry, /*routes*/ null, /*vpnExtraRoutes*/ null, etherType);
812 returnLocalDpnId.add(dpnId);
815 return returnLocalDpnId;
818 private BigInteger checkCreateLocalFibEntry(Prefixes localNextHopInfo, String localNextHopIP,
819 final Long vpnId, final String rd,
820 final VrfEntry vrfEntry,
821 Routes routes, List<Routes> vpnExtraRoutes,
823 String vpnName = fibUtil.getVpnNameFromId(vpnId);
824 if (localNextHopInfo != null) {
827 final BigInteger dpnId = localNextHopInfo.getDpnId();
828 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
829 LOG.debug("checkCreateLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
830 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
833 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
834 LOG.debug("checkCreateLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
835 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
838 if (!isVpnPresentInDpn(rd, dpnId)) {
839 LOG.error("checkCreateLocalFibEntry: The VPN with id {} rd {} is not available on dpn {}",
840 vpnId, rd, dpnId.toString());
841 return BigInteger.ZERO;
843 String interfaceName = localNextHopInfo.getVpnInterfaceName();
844 String prefix = vrfEntry.getDestPrefix();
845 String gwMacAddress = vrfEntry.getGatewayMacAddress();
846 //The loadbalancing group is created only if the extra route has multiple nexthops
847 //to avoid loadbalancing the discovered routes
848 if (vpnExtraRoutes != null && routes != null) {
849 if (isIpv4Address(routes.getNexthopIpList().get(0))) {
850 localNextHopIP = routes.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
852 localNextHopIP = routes.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
854 if (vpnExtraRoutes.size() > 1) {
855 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
856 localGroupId = nextHopManager.getLocalNextHopGroup(vpnId, localNextHopIP);
857 } else if (routes.getNexthopIpList().size() > 1) {
858 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
859 localGroupId = groupId;
861 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP,
862 prefix, gwMacAddress);
863 localGroupId = groupId;
866 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP, prefix,
868 localGroupId = groupId;
870 if (groupId == FibConstants.INVALID_GROUP_ID) {
871 LOG.error("Unable to create Group for local prefix {} on rd {} for vpninterface {} on Node {}",
872 prefix, rd, interfaceName, dpnId.toString());
873 return BigInteger.ZERO;
875 final List<InstructionInfo> instructions = Collections.singletonList(
876 new InstructionApplyActions(
877 Collections.singletonList(new ActionGroup(groupId))));
878 final List<InstructionInfo> lfibinstructions = Collections.singletonList(
879 new InstructionApplyActions(
880 Arrays.asList(new ActionPopMpls(etherType), new ActionGroup(groupId))));
881 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
882 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
883 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
884 jobCoordinator.enqueueJob(jobKey, () -> {
885 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
886 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
887 NwConstants.ADD_FLOW, tx, null);
888 if (!fibUtil.enforceVxlanDatapathSemanticsforInternalRouterVpn(localNextHopInfo.getSubnetId(),
890 optLabel.ifPresent(label -> {
891 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
893 "Installing LFIB and tunnel table entry on dpn {} for interface {} with label "
894 + "{}, rd {}, prefix {}, nexthop {}", dpnId,
895 localNextHopInfo.getVpnInterfaceName(), optLabel, rd, vrfEntry.getDestPrefix(),
897 makeLFibTableEntry(dpnId, label, lfibinstructions, DEFAULT_FIB_FLOW_PRIORITY,
898 NwConstants.ADD_FLOW, tx);
899 // If the extra-route is reachable from VMs attached to the same switch,
900 // then the tunnel table can point to the load balancing group.
901 // If it is reachable from VMs attached to different switches,
902 // then it should be pointing to one of the local group in order to avoid looping.
903 if (vrfEntry.getRoutePaths().size() == 1) {
904 makeTunnelTableEntry(dpnId, label, groupId, tx);
906 makeTunnelTableEntry(dpnId, label, localGroupId, tx);
909 LOG.debug("Route with rd {} prefix {} label {} nexthop {} for vpn {} is an imported "
910 + "route. LFib and Terminating table entries will not be created.",
911 rd, vrfEntry.getDestPrefix(), optLabel, nextHopAddressList, vpnId);
919 LOG.error("localNextHopInfo received is null for prefix {} on rd {} on vpn {}", vrfEntry.getDestPrefix(), rd,
921 return BigInteger.ZERO;
924 private boolean isVpnPresentInDpn(String rd, BigInteger dpnId) {
925 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
926 Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
927 if (dpnInVpn.isPresent()) {
933 private LabelRouteInfo getLabelRouteInfo(Long label) {
934 InstanceIdentifier<LabelRouteInfo> lriIid = InstanceIdentifier.builder(LabelRouteMap.class)
935 .child(LabelRouteInfo.class, new LabelRouteInfoKey(label)).build();
936 Optional<LabelRouteInfo> opResult = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, lriIid);
937 if (opResult.isPresent()) {
938 return opResult.get();
943 private boolean deleteLabelRouteInfo(LabelRouteInfo lri, String vpnInstanceName, WriteTransaction tx) {
948 LOG.debug("deleting LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
949 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
950 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
952 List<String> vpnInstancesList = lri.getVpnInstanceList() != null
953 ? lri.getVpnInstanceList() : new ArrayList<>();
954 if (vpnInstancesList.contains(vpnInstanceName)) {
955 LOG.debug("vpninstance {} name is present", vpnInstanceName);
956 vpnInstancesList.remove(vpnInstanceName);
958 if (vpnInstancesList.isEmpty()) {
959 LOG.debug("deleting LRI instance object for label {}", lri.getLabel());
961 tx.delete(LogicalDatastoreType.OPERATIONAL, lriId);
963 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId);
967 LOG.debug("updating LRI instance object for label {}", lri.getLabel());
968 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri).setVpnInstanceList(vpnInstancesList);
969 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
974 void makeTunnelTableEntry(BigInteger dpId, long label, long groupId/*String egressInterfaceName*/,
975 WriteTransaction tx) {
976 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionGroup(groupId));
978 createTerminatingServiceActions(dpId, (int) label, actionsInfos, tx);
980 LOG.debug("Terminating service Entry for dpID {} : label : {} egress : {} installed successfully",
981 dpId, label, groupId);
984 public void createTerminatingServiceActions(BigInteger destDpId, int label, List<ActionInfo> actionsInfos,
985 WriteTransaction tx) {
986 List<MatchInfo> mkMatches = new ArrayList<>();
988 LOG.debug("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}",
989 destDpId, label, actionsInfos);
992 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
993 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
995 List<InstructionInfo> mkInstructions = new ArrayList<>();
996 mkInstructions.add(new InstructionApplyActions(actionsInfos));
998 FlowEntity terminatingServiceTableFlowEntity =
999 MDSALUtil.buildFlowEntity(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,
1000 getTableMissFlowRef(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE, label), 5,
1001 String.format("%s:%d", "TST Flow Entry ", label),
1002 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, mkInstructions);
1004 FlowKey flowKey = new FlowKey(new FlowId(terminatingServiceTableFlowEntity.getFlowId()));
1006 FlowBuilder flowbld = terminatingServiceTableFlowEntity.getFlowBuilder();
1008 Node nodeDpn = FibUtil.buildDpnNode(terminatingServiceTableFlowEntity.getDpnId());
1009 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1010 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1011 .child(Table.class, new TableKey(terminatingServiceTableFlowEntity.getTableId()))
1012 .child(Flow.class, flowKey).build();
1013 tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId, flowbld.build(),
1014 WriteTransaction.CREATE_MISSING_PARENTS);
1017 private void removeTunnelTableEntry(BigInteger dpId, long label, WriteTransaction tx) {
1018 FlowEntity flowEntity;
1019 LOG.debug("remove terminatingServiceActions called with DpnId = {} and label = {}", dpId, label);
1020 List<MatchInfo> mkMatches = new ArrayList<>();
1021 // Matching metadata
1022 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
1023 flowEntity = MDSALUtil.buildFlowEntity(dpId,
1024 NwConstants.INTERNAL_TUNNEL_TABLE,
1025 getTableMissFlowRef(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, (int) label),
1026 5, String.format("%s:%d", "TST Flow Entry ", label), 0, 0,
1027 COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, null);
1028 Node nodeDpn = FibUtil.buildDpnNode(flowEntity.getDpnId());
1029 FlowKey flowKey = new FlowKey(new FlowId(flowEntity.getFlowId()));
1030 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1031 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1032 .child(Table.class, new TableKey(flowEntity.getTableId())).child(Flow.class, flowKey).build();
1034 tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
1035 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully", dpId, label);
1038 public List<BigInteger> deleteLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
1039 List<BigInteger> returnLocalDpnId = new ArrayList<>();
1040 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1041 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1042 boolean shouldUpdateNonEcmpLocalNextHop = true;
1043 if (localNextHopInfo == null) {
1044 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1045 if (usedRds.size() > 1) {
1046 LOG.error("The extra route prefix {} is still present in some DPNs in vpn {} on rd {}",
1047 vrfEntry.getDestPrefix(), vpnName, rd);
1048 return returnLocalDpnId;
1050 String vpnRd = (!usedRds.isEmpty()) ? usedRds.get(0) : rd;
1051 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency
1053 Optional<Routes> extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1054 vpnName, vpnRd, vrfEntry.getDestPrefix());
1055 if (extraRouteOptional.isPresent()) {
1056 Routes extraRoute = extraRouteOptional.get();
1058 if (isIpv4Address(extraRoute.getNexthopIpList().get(0))) {
1059 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
1061 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
1063 if (extraRoute.getNexthopIpList().size() > 1) {
1064 shouldUpdateNonEcmpLocalNextHop = false;
1066 localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1067 if (localNextHopInfo != null) {
1068 String localNextHopIP = localNextHopInfo.getIpAddress();
1069 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP,
1070 vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1071 if (!dpnId.equals(BigInteger.ZERO)) {
1072 LOG.trace("Deleting ECMP group for prefix {}, dpn {}", vrfEntry.getDestPrefix(), dpnId);
1073 nextHopManager.setupLoadBalancingNextHop(vpnId, dpnId,
1074 vrfEntry.getDestPrefix(), /*listBucketInfo*/ Collections.emptyList(),
1076 returnLocalDpnId.add(dpnId);
1079 LOG.error("localNextHopInfo unavailable while deleting prefix {} with rds {}, primary rd {} in "
1080 + "vpn {}", vrfEntry.getDestPrefix(), usedRds, rd, vpnName);
1084 if (localNextHopInfo == null) {
1085 /* Imported VRF entry */
1086 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1087 if (optionalLabel.isPresent()) {
1088 Long label = optionalLabel.get();
1089 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1090 LabelRouteInfo lri = getLabelRouteInfo(label);
1091 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1092 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1093 prefixBuilder.setDpnId(lri.getDpnId());
1094 BigInteger dpnId = checkDeleteLocalFibEntry(prefixBuilder.build(), nextHopAddressList.get(0),
1095 vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1096 if (!dpnId.equals(BigInteger.ZERO)) {
1097 returnLocalDpnId.add(dpnId);
1104 LOG.debug("Obtained prefix to interface for rd {} prefix {}", rd, vrfEntry.getDestPrefix());
1105 String localNextHopIP = localNextHopInfo.getIpAddress();
1106 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP,
1107 vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1108 if (!dpnId.equals(BigInteger.ZERO)) {
1109 returnLocalDpnId.add(dpnId);
1113 return returnLocalDpnId;
1116 private BigInteger checkDeleteLocalFibEntry(Prefixes localNextHopInfo, final String localNextHopIP,
1117 final Long vpnId, final String rd, final VrfEntry vrfEntry,
1118 boolean shouldUpdateNonEcmpLocalNextHop) {
1119 if (localNextHopInfo != null) {
1120 final BigInteger dpnId = localNextHopInfo.getDpnId();
1121 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
1122 LOG.debug("checkDeleteLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1123 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1126 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
1127 LOG.debug("checkDeleteLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1128 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1132 jobCoordinator.enqueueJob(FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix()),
1133 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1134 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1135 NwConstants.DEL_FLOW, tx, null);
1136 if (!fibUtil.enforceVxlanDatapathSemanticsforInternalRouterVpn(localNextHopInfo.getSubnetId(),
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, WriteTransaction tx) {
1160 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(newTx -> {
1161 createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx);
1162 }), LOG, "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() && (vpnExtraRoutes.size() > 1
1184 || vpnExtraRoutes.get(0).getNexthopIpList().size() > 1)) {
1185 List<InstructionInfo> instructions = new ArrayList<>();
1186 // Obtain the local routes for this particular dpn.
1187 java.util.Optional<Routes> routes = vpnExtraRoutes
1190 Prefixes prefixToInterface = fibUtil.getPrefixToInterface(vpnId,
1191 fibUtil.getIpPrefix(route.getNexthopIpList().get(0)));
1192 if (prefixToInterface == null) {
1195 return remoteDpnId.equals(prefixToInterface.getDpnId());
1197 long groupId = nextHopManager.createNextHopGroups(vpnId, rd, remoteDpnId, vrfEntry,
1198 routes.isPresent() ? routes.get() : null, vpnExtraRoutes);
1199 if (groupId == FibConstants.INVALID_GROUP_ID) {
1200 LOG.error("Unable to create Group for local prefix {} on rd {} on Node {}",
1201 vrfEntry.getDestPrefix(), rd, remoteDpnId.toString());
1204 List<ActionInfo> actionInfos =
1205 Collections.singletonList(new ActionGroup(groupId));
1206 instructions.add(new InstructionApplyActions(actionInfos));
1207 baseVrfEntryHandler.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
1208 NwConstants.ADD_FLOW, tx, null);
1210 baseVrfEntryHandler.programRemoteFib(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, null);
1213 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
1216 protected void cleanUpOpDataForFib(Long vpnId, String primaryRd, final VrfEntry vrfEntry) {
1217 /* Get interface info from prefix to interface mapping;
1218 Use the interface info to get the corresponding vpn interface op DS entry,
1219 remove the adjacency corresponding to this fib entry.
1220 If adjacency removed is the last adjacency, clean up the following:
1221 - vpn interface from dpntovpn list, dpn if last vpn interface on dpn
1222 - prefix to interface entry
1223 - vpn interface op DS
1225 LOG.debug("Cleanup of prefix {} in VPN {}", vrfEntry.getDestPrefix(), vpnId);
1226 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1227 if (prefixInfo == null) {
1228 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1229 String usedRd = usedRds.isEmpty() ? primaryRd : usedRds.get(0);
1230 Routes extraRoute = baseVrfEntryHandler.getVpnToExtraroute(vpnId, usedRd, vrfEntry.getDestPrefix());
1231 if (extraRoute != null) {
1232 for (String nextHopIp : extraRoute.getNexthopIpList()) {
1233 LOG.debug("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
1234 if (nextHopIp != null) {
1236 if (isIpv4Address(nextHopIp)) {
1237 ipPrefix = nextHopIp + NwConstants.IPV4PREFIX;
1239 ipPrefix = nextHopIp + NwConstants.IPV6PREFIX;
1241 prefixInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1242 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1246 if (prefixInfo == null) {
1247 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1248 if (optionalLabel.isPresent()) {
1249 Long label = optionalLabel.get();
1250 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1251 LabelRouteInfo lri = getLabelRouteInfo(label);
1252 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1253 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1254 prefixBuilder.setDpnId(lri.getDpnId());
1255 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
1256 prefixBuilder.setIpAddress(lri.getPrefix());
1257 prefixInfo = prefixBuilder.build();
1258 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
1259 label, prefixInfo.getVpnInterfaceName(), lri.getDpnId());
1260 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1265 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, null /*Routes*/);
1269 private void checkCleanUpOpDataForFib(final Prefixes prefixInfo, final Long vpnId, final String rd,
1270 final VrfEntry vrfEntry, final Routes extraRoute) {
1272 if (prefixInfo == null) {
1273 LOG.error("Cleanup VPN Data Failed as unable to find prefix Info for prefix {} VpnId {} rd {}",
1274 vrfEntry.getDestPrefix(), vpnId, rd);
1275 return; //Don't have any info for this prefix (shouldn't happen); need to return
1278 if (Prefixes.PrefixCue.Nat.equals(prefixInfo.getPrefixCue())) {
1279 LOG.debug("NAT Prefix {} with vpnId {} rd {}. Skip FIB processing",
1280 vrfEntry.getDestPrefix(), vpnId, rd);
1284 String ifName = prefixInfo.getVpnInterfaceName();
1285 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName,
1286 new CleanupVpnInterfaceWorker(prefixInfo, vpnId, rd, vrfEntry, extraRoute));
1289 private class CleanupVpnInterfaceWorker implements Callable<List<ListenableFuture<Void>>> {
1290 Prefixes prefixInfo;
1296 CleanupVpnInterfaceWorker(final Prefixes prefixInfo, final Long vpnId, final String rd,
1297 final VrfEntry vrfEntry, final Routes extraRoute) {
1298 this.prefixInfo = prefixInfo;
1301 this.vrfEntry = vrfEntry;
1302 this.extraRoute = extraRoute;
1306 public List<ListenableFuture<Void>> call() {
1307 // If another renderer(for eg : CSS) needs to be supported, check can be performed here
1308 // to call the respective helpers.
1309 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
1310 //First Cleanup LabelRouteInfo
1311 //TODO(KIRAN) : Move the below block when addressing iRT/eRT for L3VPN Over VxLan
1312 LOG.debug("cleanupVpnInterfaceWorker: rd {} prefix {}", rd, prefixInfo.getIpAddress());
1313 if (VrfEntry.EncapType.Mplsgre.equals(vrfEntry.getEncapType())) {
1314 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1315 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1316 synchronized (label.toString().intern()) {
1317 LabelRouteInfo lri = getLabelRouteInfo(label);
1318 if (lri != null && lri.getPrefix().equals(vrfEntry.getDestPrefix())
1319 && nextHopAddressList.contains(lri.getNextHopIpList().get(0))) {
1320 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1321 fibUtil.getVpnInstanceOpData(rd);
1322 String vpnInstanceName = "";
1323 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1324 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1326 boolean lriRemoved = deleteLabelRouteInfo(lri, vpnInstanceName, tx);
1328 String parentRd = lri.getParentVpnRd();
1329 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1330 parentRd, vrfEntry.getDestPrefix()));
1333 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1334 rd, vrfEntry.getDestPrefix()));
1339 String ifName = prefixInfo.getVpnInterfaceName();
1340 Optional<String> optVpnName = fibUtil.getVpnNameFromRd(rd);
1341 String vpnName = null;
1343 if (Prefixes.PrefixCue.PhysNetFunc.equals(prefixInfo.getPrefixCue())) {
1344 /*Get vpnId for rd = networkId since op vpnInterface will be pointing to rd = networkId
1346 Optional<String> vpnNameOpt = fibUtil.getVpnNameFromRd(vrfEntry.getParentVpnRd());
1347 if (vpnNameOpt.isPresent()) {
1348 vpnId = fibUtil.getVpnId(vpnNameOpt.get());
1351 if (optVpnName.isPresent()) {
1352 vpnName = optVpnName.get();
1353 Optional<VpnInterfaceOpDataEntry> opVpnInterface = MDSALUtil
1354 .read(dataBroker, LogicalDatastoreType.OPERATIONAL,
1355 fibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1356 if (opVpnInterface.isPresent()) {
1357 long associatedVpnId = fibUtil.getVpnId(vpnName);
1358 if (vpnId != associatedVpnId) {
1359 LOG.warn("Prefixes {} are associated with different vpn instance with id {} rather than {}",
1360 vrfEntry.getDestPrefix(), associatedVpnId, vpnId);
1361 LOG.warn("Not proceeding with Cleanup op data for prefix {}", vrfEntry.getDestPrefix());
1364 LOG.debug("Processing cleanup of prefix {} associated with vpn {}",
1365 vrfEntry.getDestPrefix(), associatedVpnId);
1369 if (extraRoute != null) {
1370 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1371 //Only one used Rd present in case of removal event
1372 String usedRd = usedRds.get(0);
1373 if (optVpnName.isPresent()) {
1374 tx.delete(LogicalDatastoreType.OPERATIONAL,
1375 baseVrfEntryHandler.getVpnToExtrarouteIdentifier(vpnName, usedRd,
1376 vrfEntry.getDestPrefix()));
1377 tx.delete(LogicalDatastoreType.CONFIGURATION,
1378 VpnExtraRouteHelper.getUsedRdsIdentifier(vpnId, vrfEntry.getDestPrefix()));
1381 handleAdjacencyAndVpnOpInterfaceDeletion(vrfEntry, ifName, vpnName, tx);
1387 * Check all the adjacency in VpnInterfaceOpData and decide whether to delete the entire interface or only adj.
1388 * Remove Adjacency from VPNInterfaceOpData.
1389 * if Adjacency != primary.
1390 * if Adjacency == primary , then mark it for deletion.
1391 * Remove entire VPNinterfaceOpData Entry.
1392 * if sie of Adjacency <= 2 and all are marked for deletion , delete the entire VPNinterface Op entry.
1393 * @param vrfEntry - VrfEntry removed
1394 * @param ifName - Interface name from VRFentry
1395 * @param vpnName - VPN name of corresponding VRF
1396 * @param tx - ReadWrite Tx
1397 * @throws ReadFailedException - Exception thrown in case of read failed
1399 private void handleAdjacencyAndVpnOpInterfaceDeletion(VrfEntry vrfEntry, String ifName, String vpnName,
1400 ReadWriteTransaction tx) throws ReadFailedException {
1401 InstanceIdentifier<Adjacency> adjacencyIid =
1402 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix());
1403 Optional<Adjacency> adjacencyOptional = tx.read(LogicalDatastoreType.OPERATIONAL, adjacencyIid).checkedGet();
1404 if (adjacencyOptional.isPresent()) {
1405 if (adjacencyOptional.get().getAdjacencyType() != Adjacency.AdjacencyType.PrimaryAdjacency) {
1406 tx.delete(LogicalDatastoreType.OPERATIONAL,
1407 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix()));
1409 tx.merge(LogicalDatastoreType.OPERATIONAL, adjacencyIid,
1410 new AdjacencyBuilder(adjacencyOptional.get()).setMarkedForDeletion(true).build());
1414 Optional<AdjacenciesOp> optAdjacencies =
1415 tx.read(LogicalDatastoreType.OPERATIONAL,
1416 FibUtil.getAdjListPathOp(ifName, vpnName)).checkedGet();
1418 if (!optAdjacencies.isPresent() || optAdjacencies.get().getAdjacency() == null) {
1422 if (optAdjacencies.get().getAdjacency().stream().count() <= 2
1423 && optAdjacencies.get().getAdjacency().stream().allMatch(adjacency ->
1424 adjacency.getAdjacencyType() == Adjacency.AdjacencyType.PrimaryAdjacency
1425 && adjacency.isMarkedForDeletion() != null
1426 && adjacency.isMarkedForDeletion()
1428 LOG.info("Clean up vpn interface {} to vpn {} list.", ifName, vpnName);
1429 tx.delete(LogicalDatastoreType.OPERATIONAL,
1430 FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1434 private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
1435 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
1436 final String rd = vrfTableKey.getRouteDistinguisher();
1437 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
1438 if (vpnInstance == null) {
1439 LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
1442 final Collection<VpnToDpnList> vpnToDpnList;
1443 if (vrfEntry.getParentVpnRd() != null
1444 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
1445 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
1446 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
1447 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
1448 vpnInstance.getVpnToDpnList();
1449 LOG.info("deleteFibEntries: Processing deletion of PNF FIB entry with rd {} prefix {}",
1450 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
1452 vpnToDpnList = vpnInstance.getVpnToDpnList();
1455 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1456 final java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1457 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1458 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1459 if (subnetRoute != null) {
1460 long elanTag = subnetRoute.getElantag();
1461 LOG.trace("SUBNETROUTE: deleteFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
1462 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
1463 if (vpnToDpnList != null) {
1464 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1465 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1466 for (final VpnToDpnList curDpn : vpnToDpnList) {
1468 baseVrfEntryHandler.makeConnectedRoute(curDpn.getDpnId(), vpnInstance.getVpnId(), vrfEntry,
1469 vrfTableKey.getRouteDistinguisher(), null, NwConstants.DEL_FLOW, tx, null);
1470 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1471 optionalLabel.ifPresent(label -> makeLFibTableEntry(curDpn.getDpnId(), label, null,
1472 DEFAULT_FIB_FLOW_PRIORITY, NwConstants.DEL_FLOW, tx));
1475 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnInstance.getVpnId(),
1476 vrfEntry, NwConstants.DEL_FLOW, tx);
1480 optionalLabel.ifPresent(label -> {
1481 synchronized (label.toString().intern()) {
1482 LabelRouteInfo lri = getLabelRouteInfo(label);
1483 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1484 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1485 fibUtil.getVpnInstanceOpData(rd);
1486 String vpnInstanceName = "";
1487 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1488 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1490 boolean lriRemoved = this.deleteLabelRouteInfo(lri, vpnInstanceName, null);
1492 String parentRd = lri.getParentVpnRd();
1493 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1494 parentRd, vrfEntry.getDestPrefix()));
1495 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}"
1496 + " as labelRouteInfo cleared", label, rd, vrfEntry.getDestPrefix());
1499 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1500 rd, vrfEntry.getDestPrefix()));
1501 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}",
1502 label, rd, vrfEntry.getDestPrefix());
1509 final List<BigInteger> localDpnIdList = deleteLocalFibEntry(vpnInstance.getVpnId(),
1510 vrfTableKey.getRouteDistinguisher(), vrfEntry);
1511 if (vpnToDpnList != null) {
1512 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1513 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1515 Optional<Routes> extraRouteOptional;
1516 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
1517 if (usedRds != null && !usedRds.isEmpty()) {
1518 jobKey = FibUtil.getJobKeyForRdPrefix(usedRds.get(0), vrfEntry.getDestPrefix());
1519 if (usedRds.size() > 1) {
1520 LOG.error("The extra route prefix is still present in some DPNs");
1523 // The first rd is retrieved from usedrds as Only 1 rd would be present as extra route prefix
1524 //is not present in any other DPN
1525 extraRouteOptional = VpnExtraRouteHelper
1526 .getVpnExtraroutes(dataBroker, vpnName, usedRds.get(0), vrfEntry.getDestPrefix());
1529 jobKey = FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix());
1530 extraRouteOptional = Optional.absent();
1533 jobCoordinator.enqueueJob(jobKey,
1534 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1535 if (localDpnIdList.size() <= 0) {
1536 for (VpnToDpnList curDpn : vpnToDpnList) {
1537 baseVrfEntryHandler.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
1538 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional, tx);
1541 for (BigInteger localDpnId : localDpnIdList) {
1542 for (VpnToDpnList curDpn : vpnToDpnList) {
1543 if (!curDpn.getDpnId().equals(localDpnId)) {
1544 baseVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
1545 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional, tx);
1553 //The flow/group entry has been deleted from config DS; need to clean up associated operational
1554 //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
1555 cleanUpOpDataForFib(vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(), vrfEntry);
1557 // Remove all fib entries configured due to interVpnLink, when nexthop is the opposite endPoint
1558 // of the interVpnLink.
1559 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
1560 if (optVpnUuid.isPresent()) {
1561 String vpnUuid = optVpnUuid.get();
1562 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
1563 Optional<InterVpnLinkDataComposite> optInterVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid);
1564 if (optInterVpnLink.isPresent()) {
1565 InterVpnLinkDataComposite interVpnLink = optInterVpnLink.get();
1566 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
1567 // This is route that points to the other endpoint of an InterVpnLink
1568 // In that case, we should look for the FIB table pointing to
1569 // LPortDispatcher table and remove it.
1570 removeInterVPNLinkRouteFlows(interVpnLink, vpnUuid, vrfEntry);
1578 private void makeLFibTableEntry(BigInteger dpId, long label, List<InstructionInfo> instructions, int priority,
1579 int addOrRemove, WriteTransaction tx) {
1581 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
1582 newTx -> makeLFibTableEntry(dpId, label, instructions, priority, addOrRemove, newTx)), LOG,
1583 "Error making LFIB table entry");
1587 List<MatchInfo> matches = new ArrayList<>();
1588 matches.add(MatchEthernetType.MPLS_UNICAST);
1589 matches.add(new MatchMplsLabel(label));
1591 // Install the flow entry in L3_LFIB_TABLE
1592 String flowRef = FibUtil.getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, label, priority);
1594 FlowEntity flowEntity;
1595 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef, priority, flowRef, 0, 0,
1596 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
1597 Flow flow = flowEntity.getFlowBuilder().build();
1598 String flowId = flowEntity.getFlowId();
1599 FlowKey flowKey = new FlowKey(new FlowId(flowId));
1600 Node nodeDpn = FibUtil.buildDpnNode(dpId);
1601 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1602 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1603 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
1605 if (addOrRemove == NwConstants.ADD_FLOW) {
1606 tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId, flow, WriteTransaction.CREATE_MISSING_PARENTS);
1608 tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
1611 LOG.debug("LFIB Entry for dpID {} : label : {} instructions {} : key {} {} successfully",
1612 dpId, label, instructions, flowKey, NwConstants.ADD_FLOW == addOrRemove ? "ADDED" : "REMOVED");
1615 public void populateFibOnNewDpn(final BigInteger dpnId, final long vpnId, final String rd,
1616 final FutureCallback<List<Void>> callback) {
1617 LOG.trace("New dpn {} for vpn {} : populateFibOnNewDpn", dpnId, rd);
1618 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1620 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1621 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1622 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1623 LogicalDatastoreType.CONFIGURATION, id);
1624 List<ListenableFuture<Void>> futures = new ArrayList<>();
1625 if (!vrfTable.isPresent()) {
1626 LOG.info("populateFibOnNewDpn: dpn: {}: VRF Table not yet available for RD {}", dpnId, rd);
1627 if (callback != null) {
1628 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1629 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1633 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1634 futures.add(retryingTxRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
1635 for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
1636 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1637 if (subnetRoute != null) {
1638 long elanTag = subnetRoute.getElantag();
1639 installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, tx);
1640 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry, NwConstants.ADD_FLOW,
1644 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1645 if (routerInt != null) {
1646 LOG.trace("Router augmented vrfentry found rd:{}, uuid:{}, ip:{}, mac:{}",
1647 rd, routerInt.getUuid(), routerInt.getIpAddress(), routerInt.getMacAddress());
1648 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1649 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1650 NwConstants.ADD_FLOW);
1653 //Handle local flow creation for imports
1654 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1655 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1656 if (optionalLabel.isPresent()) {
1657 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1658 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1659 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList, lri)) {
1660 if (lri.getDpnId().equals(dpnId)) {
1662 int etherType = NWUtil.getEtherTypeFromIpPrefix(
1663 vrfEntry.getDestPrefix());
1664 createLocalFibEntry(vpnId, rd, vrfEntry, etherType);
1665 } catch (IllegalArgumentException ex) {
1666 LOG.warn("Unable to get etherType for IP Prefix {}",
1667 vrfEntry.getDestPrefix());
1674 boolean shouldCreateRemoteFibEntry = shouldCreateFibEntryForVrfAndVpnIdOnDpn(vpnId,
1676 if (shouldCreateRemoteFibEntry) {
1677 LOG.trace("Will create remote FIB entry for vrfEntry {} on DPN {}", vrfEntry, dpnId);
1678 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1679 List<SubTransaction> txnObjects = new ArrayList<>();
1680 bgpRouteVrfEntryHandler.createRemoteFibEntry(dpnId, vpnId,
1681 vrfTable.get().getRouteDistinguisher(), vrfEntry, tx, txnObjects);
1683 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
1689 if (callback != null) {
1690 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1691 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1698 public void populateExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1699 final String localNextHopIp, final String remoteNextHopIp) {
1700 LOG.trace("populateExternalRoutesOnDpn : dpn {}, vpn {}, rd {}, localNexthopIp {} , remoteNextHopIp {} ",
1701 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1702 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1703 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1704 List<SubTransaction> txnObjects = new ArrayList<>();
1705 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1706 if (vrfTable.isPresent()) {
1707 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1708 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1709 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1710 vrfTable.get().getVrfEntry().stream()
1711 .filter(vrfEntry -> RouteOrigin.BGP == RouteOrigin.value(vrfEntry.getOrigin()))
1712 .forEach(bgpRouteVrfEntryHandler.getConsumerForCreatingRemoteFib(dpnId, vpnId,
1713 rd, remoteNextHopIp, vrfTable, tx, txnObjects));
1719 public void manageRemoteRouteOnDPN(final boolean action,
1720 final BigInteger localDpnId,
1723 final String destPrefix,
1724 final String destTepIp,
1726 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1728 if (vpnInstance == null) {
1729 LOG.error("VpnInstance for rd {} not present for prefix {}", rd, destPrefix);
1733 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, localDpnId),
1734 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1735 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1736 VrfTablesKey vrfTablesKey = new VrfTablesKey(rd);
1737 VrfEntry vrfEntry = getVrfEntry(dataBroker, rd, destPrefix);
1738 if (vrfEntry == null) {
1741 LOG.trace("manageRemoteRouteOnDPN :: action {}, DpnId {}, vpnId {}, rd {}, destPfx {}",
1742 action, localDpnId, vpnId, rd, destPrefix);
1743 List<RoutePaths> routePathList = vrfEntry.getRoutePaths();
1744 VrfEntry modVrfEntry;
1745 if (routePathList == null || routePathList.isEmpty()) {
1746 modVrfEntry = FibHelper.getVrfEntryBuilder(vrfEntry, label,
1747 Collections.singletonList(destTepIp),
1748 RouteOrigin.value(vrfEntry.getOrigin()), null /* parentVpnRd */).build();
1750 modVrfEntry = vrfEntry;
1754 LOG.trace("manageRemoteRouteOnDPN updated(add) vrfEntry :: {}", modVrfEntry);
1755 createRemoteFibEntry(localDpnId, vpnId, vrfTablesKey.getRouteDistinguisher(),
1758 LOG.trace("manageRemoteRouteOnDPN updated(remove) vrfEntry :: {}", modVrfEntry);
1759 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnInstance.getVpnId(),
1760 vrfEntry.getDestPrefix());
1761 if (usedRds.size() > 1) {
1762 LOG.debug("The extra route prefix is still present in some DPNs");
1765 //Is this fib route an extra route? If yes, get the nexthop which would be
1766 //an adjacency in the vpn
1767 Optional<Routes> extraRouteOptional = Optional.absent();
1768 if (usedRds.size() != 0) {
1769 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1770 fibUtil.getVpnNameFromId(vpnInstance.getVpnId()),
1771 usedRds.get(0), vrfEntry.getDestPrefix());
1773 baseVrfEntryHandler.deleteRemoteRoute(null, localDpnId, vpnId, vrfTablesKey, modVrfEntry,
1774 extraRouteOptional, tx);
1780 public void cleanUpDpnForVpn(final BigInteger dpnId, final long vpnId, final String rd,
1781 final FutureCallback<List<Void>> callback) {
1782 LOG.trace("cleanUpDpnForVpn: Remove dpn {} for vpn {} : cleanUpDpnForVpn", dpnId, rd);
1783 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1785 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1786 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1787 List<SubTransaction> txnObjects = new ArrayList<>();
1788 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1789 LogicalDatastoreType.CONFIGURATION, id);
1790 List<ListenableFuture<Void>> futures = new ArrayList<>();
1791 if (vrfTable.isPresent()) {
1792 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1793 futures.add(retryingTxRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1794 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1795 for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
1796 /* Handle subnet routes here */
1797 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1798 if (subnetRoute != null && !fibUtil
1799 .isInterfacePresentInDpn(vrfEntry.getParentVpnRd(), dpnId)) {
1800 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Cleaning subnetroute {} on dpn {}"
1801 + " for vpn {}", vrfEntry.getDestPrefix(), dpnId, rd);
1802 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1803 NwConstants.DEL_FLOW, tx, null);
1804 List<RoutePaths> routePaths = vrfEntry.getRoutePaths();
1805 if (routePaths != null) {
1806 for (RoutePaths routePath : routePaths) {
1807 makeLFibTableEntry(dpnId, routePath.getLabel(), null,
1808 DEFAULT_FIB_FLOW_PRIORITY,
1809 NwConstants.DEL_FLOW, tx);
1810 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Released subnetroute label {}"
1811 + " for rd {} prefix {}", routePath.getLabel(), rd,
1812 vrfEntry.getDestPrefix());
1815 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry,
1816 NwConstants.DEL_FLOW, tx);
1819 // ping responder for router interfaces
1820 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1821 if (routerInt != null) {
1822 LOG.trace("Router augmented vrfentry found for rd:{}, uuid:{}, ip:{}, mac:{}",
1823 rd, routerInt.getUuid(), routerInt.getIpAddress(),
1824 routerInt.getMacAddress());
1825 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1826 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1827 NwConstants.DEL_FLOW);
1831 //Handle local flow deletion for imports
1832 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1833 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1834 if (optionalLabel.isPresent()) {
1835 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1836 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1837 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList,
1838 lri) && lri.getDpnId().equals(dpnId)) {
1839 deleteLocalFibEntry(vpnId, rd, vrfEntry);
1844 // Passing null as we don't know the dpn
1845 // to which prefix is attached at this point
1846 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1847 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1848 Optional<Routes> extraRouteOptional;
1849 //Is this fib route an extra route? If yes, get the nexthop which would be
1850 //an adjacency in the vpn
1851 if (usedRds != null && !usedRds.isEmpty()) {
1852 if (usedRds.size() > 1) {
1853 LOG.error("The extra route prefix is still present in some DPNs");
1856 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
1857 usedRds.get(0), vrfEntry.getDestPrefix());
1861 extraRouteOptional = Optional.absent();
1863 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1864 bgpRouteVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1865 vrfTable.get().key(), vrfEntry, extraRouteOptional, tx, txnObjects);
1867 if (subnetRoute == null || !fibUtil
1868 .isInterfacePresentInDpn(vrfEntry.getParentVpnRd(), dpnId)) {
1869 baseVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1870 vrfTable.get().key(), vrfEntry, extraRouteOptional, tx);
1876 if (callback != null) {
1877 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1878 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1881 LOG.error("cleanUpDpnForVpn: No vrf table found for rd {} vpnId {} dpn {}", rd, vpnId, dpnId);
1888 public void cleanUpExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1889 final String localNextHopIp, final String remoteNextHopIp) {
1890 LOG.trace("cleanUpExternalRoutesOnDpn : cleanup remote routes on dpn {} for vpn {}, rd {}, "
1891 + " localNexthopIp {} , remoteNexhtHopIp {}",
1892 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1893 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1894 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1895 List<SubTransaction> txnObjects = new ArrayList<>();
1896 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1897 if (vrfTable.isPresent()) {
1898 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1900 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1901 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
1902 tx -> vrfTable.get().getVrfEntry().stream()
1903 .filter(vrfEntry -> RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP)
1904 .forEach(bgpRouteVrfEntryHandler.getConsumerForDeletingRemoteFib(dpnId, vpnId,
1905 remoteNextHopIp, vrfTable, tx, txnObjects))));
1911 public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
1912 InstanceIdentifierBuilder<VrfTables> idBuilder =
1913 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
1914 return idBuilder.build();
1917 private String getInterVpnFibFlowRef(String interVpnLinkName, String prefix, String nextHop) {
1918 return FLOWID_PREFIX + interVpnLinkName + NwConstants.FLOWID_SEPARATOR + prefix + NwConstants
1919 .FLOWID_SEPARATOR + nextHop;
1922 private String getTableMissFlowRef(BigInteger dpnId, short tableId, int tableMiss) {
1923 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR
1924 + tableMiss + FLOWID_PREFIX;
1927 private VrfEntry getVrfEntry(DataBroker broker, String rd, String ipPrefix) {
1928 InstanceIdentifier<VrfEntry> vrfEntryId = InstanceIdentifier.builder(FibEntries.class)
1929 .child(VrfTables.class, new VrfTablesKey(rd))
1930 .child(VrfEntry.class, new VrfEntryKey(ipPrefix)).build();
1931 Optional<VrfEntry> vrfEntry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
1932 if (vrfEntry.isPresent()) {
1933 return vrfEntry.get();
1938 public void removeInterVPNLinkRouteFlows(final InterVpnLinkDataComposite interVpnLink,
1939 final String vpnName,
1940 final VrfEntry vrfEntry) {
1941 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null && vrfEntry.getRoutePaths().size() == 1);
1943 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
1944 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
1946 if (targetDpns.isEmpty()) {
1947 LOG.warn("Could not find DPNs for VPN {} in InterVpnLink {}", vpnName, interVpnLinkName);
1951 java.util.Optional<String> optNextHop = FibUtil.getFirstNextHopAddress(vrfEntry);
1952 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1956 optNextHop.ifPresent(nextHop -> {
1957 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, vrfEntry.getDestPrefix(), nextHop);
1958 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
1959 Flow flow = new FlowBuilder().withKey(flowKey).setId(new FlowId(flowRef))
1960 .setTableId(NwConstants.L3_FIB_TABLE).setFlowName(flowRef).build();
1962 LOG.trace("Removing flow in FIB table for interVpnLink {} key {}", interVpnLinkName, flowRef);
1963 for (BigInteger dpId : targetDpns) {
1964 LOG.debug("Removing flow: VrfEntry=[prefix={} nexthop={}] dpn {} for InterVpnLink {} in FIB",
1965 vrfEntry.getDestPrefix(), nextHop, dpId, interVpnLinkName);
1967 mdsalManager.removeFlow(dpId, flow);
1973 optLabel.ifPresent(label -> {
1974 LOG.trace("Removing flow in FIB table for interVpnLink {}", interVpnLinkName);
1976 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1977 for (BigInteger dpId : targetDpns) {
1978 LOG.debug("Removing flow: VrfEntry=[prefix={} label={}] dpn {} for InterVpnLink {} in LFIB",
1979 vrfEntry.getDestPrefix(), label, dpId, interVpnLinkName);
1980 makeLFibTableEntry(dpId, label, /*instructions*/null, LFIB_INTERVPN_PRIORITY, NwConstants.DEL_FLOW,
1983 }), LOG, "Error removing flows");
1987 private boolean isPrefixAndNextHopPresentInLri(String prefix,
1988 List<String> nextHopAddressList, LabelRouteInfo lri) {
1989 return lri != null && lri.getPrefix().equals(prefix)
1990 && nextHopAddressList.contains(lri.getNextHopIpList().get(0));
1993 private boolean shouldCreateFibEntryForVrfAndVpnIdOnDpn(Long vpnId, VrfEntry vrfEntry, BigInteger dpnId) {
1994 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
1998 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1999 if (prefix != null) {
2000 BigInteger prefixDpnId = prefix.getDpnId();
2001 if (dpnId.equals(prefixDpnId)) {
2002 LOG.trace("Should not create remote FIB entry for vrfEntry {} on DPN {}",