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 ListenableFuture<Void> future =
330 txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> nextHopsRemoved.parallelStream()
331 .forEach(nextHopRemoved -> fibUtil.updateUsedRdAndVpnToExtraRoute(
332 tx, nextHopRemoved, rd, update.getDestPrefix())));
333 Futures.addCallback(future, new FutureCallback<Void>() {
335 public void onSuccess(Void result) {
336 createFibEntries(identifier, update);
337 LOG.info("UPDATE: Updated static Fib Entry with rd {} prefix {} route-paths {}",
338 rd, update.getDestPrefix(), update.getRoutePaths());
342 public void onFailure(Throwable throwable) {
343 LOG.error("Exception encountered while submitting operational future for update vrfentry {}",
346 }, MoreExecutors.directExecutor());
350 //Handle all other routes only on a cluster reboot
351 if (original.equals(update)) {
353 createFibEntries(identifier, update);
354 LOG.info("UPDATE: Updated Non-static Fib Entry with rd {} prefix {} route-paths {}",
355 rd, update.getDestPrefix(), update.getRoutePaths());
359 LOG.info("UPDATE: Ignoring update for FIB entry with rd {} prefix {} route-origin {} route-paths {}",
360 rd, update.getDestPrefix(), update.getOrigin(), update.getRoutePaths());
363 private void createFibEntries(final InstanceIdentifier<VrfEntry> vrfEntryIid, final VrfEntry vrfEntry) {
364 final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
365 List<SubTransaction> txnObjects = new ArrayList<>();
366 final VpnInstanceOpDataEntry vpnInstance =
367 fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
368 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available " + vrfTableKey.getRouteDistinguisher());
369 Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId()
370 + " has null vpnId!");
371 final Collection<VpnToDpnList> vpnToDpnList;
372 if (vrfEntry.getParentVpnRd() != null
373 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
374 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
375 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
376 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
377 vpnInstance.getVpnToDpnList();
378 LOG.info("createFibEntries: Processing creation of PNF FIB entry with rd {} prefix {}",
379 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
381 vpnToDpnList = vpnInstance.getVpnToDpnList();
383 final Long vpnId = vpnInstance.getVpnId();
384 final String rd = vrfTableKey.getRouteDistinguisher();
385 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
386 if (subnetRoute != null) {
387 final long elanTag = subnetRoute.getElantag();
388 LOG.trace("SUBNETROUTE: createFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
389 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
390 if (vpnToDpnList != null) {
391 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
392 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
393 for (final VpnToDpnList curDpn : vpnToDpnList) {
394 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
395 installSubnetRouteInFib(curDpn.getDpnId(), elanTag, rd, vpnId, vrfEntry, tx);
396 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnId.longValue(),
397 vrfEntry, NwConstants.ADD_FLOW, tx);
404 // Get etherType value based on the IpPrefix address family type
407 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
408 } catch (IllegalArgumentException ex) {
409 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
413 final List<BigInteger> localDpnIdList = createLocalFibEntry(vpnInstance.getVpnId(), rd, vrfEntry, etherType);
414 if (!localDpnIdList.isEmpty() && vpnToDpnList != null) {
415 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
416 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
417 synchronized (vpnInstance.getVpnInstanceName().intern()) {
418 for (VpnToDpnList vpnDpn : vpnToDpnList) {
419 if (!localDpnIdList.contains(vpnDpn.getDpnId())) {
420 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
422 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
423 bgpRouteVrfEntryHandler.createRemoteFibEntry(vpnDpn.getDpnId(),
424 vpnId, vrfTableKey.getRouteDistinguisher(), vrfEntry, tx,
427 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
428 vrfTableKey.getRouteDistinguisher(), vrfEntry, tx);
430 } catch (NullPointerException e) {
431 LOG.error("Failed to get create remote fib flows for prefix {} ",
432 vrfEntry.getDestPrefix(), e);
441 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
442 if (optVpnUuid.isPresent()) {
443 String vpnUuid = optVpnUuid.get();
444 InterVpnLinkDataComposite interVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid).orNull();
445 if (interVpnLink != null) {
446 LOG.debug("InterVpnLink {} found in Cache linking Vpn {}", interVpnLink.getInterVpnLinkName(), vpnUuid);
447 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
448 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
449 // This is an static route that points to the other endpoint of an InterVpnLink
450 // In that case, we should add another entry in FIB table pointing to LPortDispatcher table.
451 installIVpnLinkSwitchingFlows(interVpnLink, vpnUuid, vrfEntry, vpnId);
452 installInterVpnRouteInLFib(interVpnLink, vpnUuid, vrfEntry, etherType);
459 void refreshFibTables(String rd, String prefix) {
460 InstanceIdentifier<VrfEntry> vrfEntryId =
461 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
462 .child(VrfEntry.class, new VrfEntryKey(prefix)).build();
463 Optional<VrfEntry> vrfEntry = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
464 if (vrfEntry.isPresent()) {
465 createFibEntries(vrfEntryId, vrfEntry.get());
469 private Prefixes updateVpnReferencesInLri(LabelRouteInfo lri, String vpnInstanceName, boolean isPresentInList) {
470 LOG.debug("updating LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
471 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
472 prefixBuilder.setDpnId(lri.getDpnId());
473 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
474 prefixBuilder.setIpAddress(lri.getPrefix());
475 // Increment the refCount here
476 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
477 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
478 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri);
479 if (!isPresentInList) {
480 LOG.debug("vpnName {} is not present in LRI with label {}..", vpnInstanceName, lri.getLabel());
481 List<String> vpnInstanceNames = lri.getVpnInstanceList();
482 vpnInstanceNames.add(vpnInstanceName);
483 builder.setVpnInstanceList(vpnInstanceNames);
484 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
486 LOG.debug("vpnName {} is present in LRI with label {}..", vpnInstanceName, lri.getLabel());
488 return prefixBuilder.build();
491 void installSubnetRouteInFib(final BigInteger dpnId, final long elanTag, final String rd,
492 final long vpnId, final VrfEntry vrfEntry, WriteTransaction tx) {
494 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
495 newTx -> installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, newTx)), LOG,
496 "Error installing subnet route in FIB");
501 etherType = NWUtil.getEtherTypeFromIpPrefix(vrfEntry.getDestPrefix());
502 } catch (IllegalArgumentException ex) {
503 LOG.error("Unable to get etherType for IP Prefix {}", vrfEntry.getDestPrefix());
506 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
507 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
508 synchronized (label.toString().intern()) {
509 LabelRouteInfo lri = getLabelRouteInfo(label);
510 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
512 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
513 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
514 fibUtil.getVpnInstanceOpData(rd);
515 if (vpnInstanceOpDataEntryOptional.isPresent()) {
516 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
517 if (!lri.getVpnInstanceList().contains(vpnInstanceName)) {
518 updateVpnReferencesInLri(lri, vpnInstanceName, false);
522 LOG.debug("SUBNETROUTE: installSubnetRouteInFib: Fetched labelRouteInfo for label {} interface {}"
523 + " and got dpn {}", label, lri.getVpnInterfaceName(), lri.getDpnId());
527 final List<InstructionInfo> instructions = new ArrayList<>();
528 BigInteger subnetRouteMeta = BigInteger.valueOf(elanTag).shiftLeft(24)
529 .or(BigInteger.valueOf(vpnId).shiftLeft(1));
530 instructions.add(new InstructionWriteMetadata(subnetRouteMeta, MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
531 instructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
532 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
533 NwConstants.ADD_FLOW, tx, null);
534 if (vrfEntry.getRoutePaths() != null) {
535 for (RoutePaths routePath : vrfEntry.getRoutePaths()) {
536 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
537 List<ActionInfo> actionsInfos = new ArrayList<>();
538 // reinitialize instructions list for LFIB Table
539 final List<InstructionInfo> LFIBinstructions = new ArrayList<>();
540 actionsInfos.add(new ActionPopMpls(etherType));
541 LFIBinstructions.add(new InstructionApplyActions(actionsInfos));
542 LFIBinstructions.add(new InstructionWriteMetadata(subnetRouteMeta,
543 MetaDataUtil.METADATA_MASK_SUBNET_ROUTE));
544 LFIBinstructions.add(new InstructionGotoTable(NwConstants.L3_SUBNET_ROUTE_TABLE));
546 makeLFibTableEntry(dpnId, routePath.getLabel(), LFIBinstructions, DEFAULT_FIB_FLOW_PRIORITY,
547 NwConstants.ADD_FLOW, tx);
553 private void installSubnetBroadcastAddrDropRule(final BigInteger dpnId, final String rd, final long vpnId,
554 final VrfEntry vrfEntry, int addOrRemove, WriteTransaction tx) {
555 List<MatchInfo> matches = new ArrayList<>();
557 LOG.debug("SUBNETROUTE: installSubnetBroadcastAddrDropRule: destPrefix {} rd {} vpnId {} dpnId {}",
558 vrfEntry.getDestPrefix(), rd, vpnId, dpnId);
559 String[] ipAddress = vrfEntry.getDestPrefix().split("/");
560 String subnetBroadcastAddr = FibUtil.getBroadcastAddressFromCidr(vrfEntry.getDestPrefix());
561 final int prefixLength = ipAddress.length == 1 ? 0 : Integer.parseInt(ipAddress[1]);
563 InetAddress destPrefix;
565 destPrefix = InetAddress.getByName(subnetBroadcastAddr);
566 } catch (UnknownHostException e) {
567 LOG.error("Failed to get destPrefix for prefix {} rd {} VpnId {} DPN {}",
568 vrfEntry.getDestPrefix(), rd, vpnId, dpnId, e);
572 // Match on VpnId and SubnetBroadCast IP address
573 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
574 matches.add(MatchEthernetType.IPV4);
576 if (prefixLength != 0) {
577 matches.add(new MatchIpv4Destination(subnetBroadcastAddr, Integer.toString(IPV4_ADDR_PREFIX_LENGTH)));
580 //Action is to drop the packet
581 List<InstructionInfo> dropInstructions = new ArrayList<>();
582 List<ActionInfo> actionsInfos = new ArrayList<>();
583 actionsInfos.add(new ActionDrop());
584 dropInstructions.add(new InstructionApplyActions(actionsInfos));
586 int priority = DEFAULT_FIB_FLOW_PRIORITY + IPV4_ADDR_PREFIX_LENGTH;
587 String flowRef = FibUtil.getFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, rd, priority, destPrefix);
588 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef, priority,
589 flowRef, 0, 0, COOKIE_TABLE_MISS, matches, dropInstructions);
591 Flow flow = flowEntity.getFlowBuilder().build();
592 String flowId = flowEntity.getFlowId();
593 FlowKey flowKey = new FlowKey(new FlowId(flowId));
594 Node nodeDpn = FibUtil.buildDpnNode(dpnId);
596 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
597 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
598 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
600 if (addOrRemove == NwConstants.ADD_FLOW) {
601 tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId,flow, true);
603 tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
608 * For a given route, it installs a flow in LFIB that sets the lportTag of the other endpoint and sends to
609 * LportDispatcher table (via table 80)
611 private void installInterVpnRouteInLFib(final InterVpnLinkDataComposite interVpnLink, final String vpnName,
612 final VrfEntry vrfEntry, int etherType) {
613 // INTERVPN routes are routes in a Vpn1 that have been leaked to Vpn2. In DC-GW, this Vpn2 route is pointing
614 // to a list of DPNs where Vpn2's VpnLink was instantiated. In these DPNs LFIB must be programmed so that the
615 // packet is commuted from Vpn2 to Vpn1.
616 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
617 if (!interVpnLink.isActive()) {
618 LOG.warn("InterVpnLink {} is NOT ACTIVE. InterVpnLink flows for prefix={} wont be installed in LFIB",
619 interVpnLinkName, vrfEntry.getDestPrefix());
623 Optional<Long> optLportTag = interVpnLink.getEndpointLportTagByVpnName(vpnName);
624 if (!optLportTag.isPresent()) {
625 LOG.warn("Could not retrieve lportTag for VPN {} endpoint in InterVpnLink {}", vpnName, interVpnLinkName);
629 Long lportTag = optLportTag.get();
630 Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).orElse(null);
632 LOG.error("Could not find label in vrfEntry=[prefix={} routePaths={}]. LFIB entry for InterVpnLink skipped",
633 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths());
636 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionPopMpls(etherType));
637 List<InstructionInfo> instructions = Arrays.asList(
638 new InstructionApplyActions(actionsInfos),
639 new InstructionWriteMetadata(MetaDataUtil.getMetaDataForLPortDispatcher(lportTag.intValue(),
640 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME,
641 NwConstants.L3VPN_SERVICE_INDEX)),
642 MetaDataUtil.getMetaDataMaskForLPortDispatcher()),
643 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
644 List<String> interVpnNextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
645 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
647 for (BigInteger dpId : targetDpns) {
648 LOG.debug("Installing flow: VrfEntry=[prefix={} label={} nexthop={}] dpn {} for InterVpnLink {} in LFIB",
649 vrfEntry.getDestPrefix(), label, interVpnNextHopList, dpId, interVpnLink.getInterVpnLinkName());
651 makeLFibTableEntry(dpId, label, instructions, LFIB_INTERVPN_PRIORITY, NwConstants.ADD_FLOW,
658 * Installs the flows in FIB table that, for a given route, do the switching from one VPN to the other.
660 private void installIVpnLinkSwitchingFlows(final InterVpnLinkDataComposite interVpnLink, final String vpnUuid,
661 final VrfEntry vrfEntry, long vpnTag) {
662 Preconditions.checkNotNull(interVpnLink, "InterVpnLink cannot be null");
663 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null
664 && vrfEntry.getRoutePaths().size() == 1);
665 String destination = vrfEntry.getDestPrefix();
666 String nextHop = vrfEntry.getRoutePaths().get(0).getNexthopAddress();
667 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
669 // After having received a static route, we should check if the vpn is part of an inter-vpn-link.
670 // In that case, we should populate the FIB table of the VPN pointing to LPortDisptacher table
671 // using as metadata the LPortTag associated to that vpn in the inter-vpn-link.
672 if (interVpnLink.getState().or(State.Error) != State.Active) {
673 LOG.warn("Route to {} with nexthop={} cannot be installed because the interVpnLink {} is not active",
674 destination, nextHop, interVpnLinkName);
678 Optional<Long> optOtherEndpointLportTag = interVpnLink.getOtherEndpointLportTagByVpnName(vpnUuid);
679 if (!optOtherEndpointLportTag.isPresent()) {
680 LOG.warn("Could not find suitable LportTag for the endpoint opposite to vpn {} in interVpnLink {}",
681 vpnUuid, interVpnLinkName);
685 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnUuid);
686 if (targetDpns.isEmpty()) {
687 LOG.warn("Could not find DPNs for endpoint opposite to vpn {} in interVpnLink {}",
688 vpnUuid, interVpnLinkName);
692 String[] values = destination.split("/");
693 String destPrefixIpAddress = values[0];
694 int prefixLength = values.length == 1 ? 0 : Integer.parseInt(values[1]);
696 List<MatchInfo> matches = new ArrayList<>();
697 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnTag), MetaDataUtil.METADATA_MASK_VRFID));
698 matches.add(MatchEthernetType.IPV4);
700 if (prefixLength != 0) {
701 matches.add(new MatchIpv4Destination(destPrefixIpAddress, Integer.toString(prefixLength)));
704 List<Instruction> instructions =
705 Arrays.asList(new InstructionWriteMetadata(
706 MetaDataUtil.getMetaDataForLPortDispatcher(optOtherEndpointLportTag.get().intValue(),
707 ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants
708 .L3VPN_SERVICE_INDEX)),
709 MetaDataUtil.getMetaDataMaskForLPortDispatcher()).buildInstruction(0),
710 new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE).buildInstruction(1));
712 int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
713 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, destination, nextHop);
714 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_FIB_TABLE, flowRef, priority, flowRef, 0, 0,
715 COOKIE_VM_FIB_TABLE, matches, instructions);
717 LOG.trace("Installing flow in FIB table for vpn {} interVpnLink {} nextHop {} key {}",
718 vpnUuid, interVpnLink.getInterVpnLinkName(), nextHop, flowRef);
720 for (BigInteger dpId : targetDpns) {
722 LOG.debug("Installing flow: VrfEntry=[prefix={} route-paths={}] dpn {} for InterVpnLink {} in FIB",
723 vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(),
724 dpId, interVpnLink.getInterVpnLinkName());
726 mdsalManager.installFlow(dpId, flowEntity);
730 private List<BigInteger> createLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry, int etherType) {
731 List<BigInteger> returnLocalDpnId = new ArrayList<>();
732 String localNextHopIP = vrfEntry.getDestPrefix();
733 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
734 String vpnName = fibUtil.getVpnNameFromId(vpnId);
735 if (localNextHopInfo == null) {
736 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, localNextHopIP);
737 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
738 vpnName, usedRds, localNextHopIP);
739 boolean localNextHopSeen = false;
740 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
741 for (Routes vpnExtraRoute : vpnExtraRoutes) {
743 if (isIpv4Address(vpnExtraRoute.getNexthopIpList().get(0))) {
744 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
746 ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
748 Prefixes localNextHopInfoLocal = fibUtil.getPrefixToInterface(vpnId,
750 if (localNextHopInfoLocal != null) {
751 localNextHopSeen = true;
753 checkCreateLocalFibEntry(localNextHopInfoLocal, localNextHopInfoLocal.getIpAddress(),
754 vpnId, rd, vrfEntry, vpnExtraRoute, vpnExtraRoutes, etherType);
755 returnLocalDpnId.add(dpnId);
758 if (!localNextHopSeen && RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
759 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
760 if (optionalLabel.isPresent()) {
761 Long label = optionalLabel.get();
762 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
763 synchronized (label.toString().intern()) {
764 LabelRouteInfo lri = getLabelRouteInfo(label);
765 if (isPrefixAndNextHopPresentInLri(localNextHopIP, nextHopAddressList, lri)) {
766 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
767 fibUtil.getVpnInstanceOpData(rd);
768 if (vpnInstanceOpDataEntryOptional.isPresent()) {
769 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
770 if (lri.getVpnInstanceList().contains(vpnInstanceName)) {
771 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, true);
772 localNextHopIP = lri.getPrefix();
774 localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, false);
775 localNextHopIP = lri.getPrefix();
778 if (localNextHopInfo != null) {
779 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
780 label, localNextHopInfo.getVpnInterfaceName(), lri.getDpnId());
781 if (vpnExtraRoutes.isEmpty()) {
782 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
783 vpnId, rd, vrfEntry, null, vpnExtraRoutes, etherType);
784 returnLocalDpnId.add(dpnId);
786 for (Routes extraRoutes : vpnExtraRoutes) {
787 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP,
788 vpnId, rd, vrfEntry, extraRoutes, vpnExtraRoutes, etherType);
789 returnLocalDpnId.add(dpnId);
797 if (returnLocalDpnId.isEmpty()) {
798 LOG.error("Local DPNID is empty for rd {}, vpnId {}, vrfEntry {}", rd, vpnId, vrfEntry);
801 BigInteger dpnId = checkCreateLocalFibEntry(localNextHopInfo, localNextHopIP, vpnId,
802 rd, vrfEntry, /*routes*/ null, /*vpnExtraRoutes*/ null, etherType);
804 returnLocalDpnId.add(dpnId);
807 return returnLocalDpnId;
810 private BigInteger checkCreateLocalFibEntry(Prefixes localNextHopInfo, String localNextHopIP,
811 final Long vpnId, final String rd,
812 final VrfEntry vrfEntry,
813 Routes routes, List<Routes> vpnExtraRoutes,
815 String vpnName = fibUtil.getVpnNameFromId(vpnId);
816 if (localNextHopInfo != null) {
819 final BigInteger dpnId = localNextHopInfo.getDpnId();
820 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
821 LOG.debug("checkCreateLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
822 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
825 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
826 LOG.debug("checkCreateLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
827 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
830 if (!isVpnPresentInDpn(rd, dpnId)) {
831 LOG.error("checkCreateLocalFibEntry: The VPN with id {} rd {} is not available on dpn {}",
832 vpnId, rd, dpnId.toString());
833 return BigInteger.ZERO;
835 String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix());
836 String interfaceName = localNextHopInfo.getVpnInterfaceName();
837 String prefix = vrfEntry.getDestPrefix();
838 String gwMacAddress = vrfEntry.getGatewayMacAddress();
839 //The loadbalancing group is created only if the extra route has multiple nexthops
840 //to avoid loadbalancing the discovered routes
841 if (vpnExtraRoutes != null && routes != null) {
842 if (isIpv4Address(routes.getNexthopIpList().get(0))) {
843 localNextHopIP = routes.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
845 localNextHopIP = routes.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
847 if (vpnExtraRoutes.size() > 1) {
848 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes,
850 localGroupId = nextHopManager.getLocalNextHopGroup(vpnId, localNextHopIP);
851 } else if (routes.getNexthopIpList().size() > 1) {
852 groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes,
854 localGroupId = groupId;
856 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP,
857 prefix, gwMacAddress, jobKey);
858 localGroupId = groupId;
861 groupId = nextHopManager.createLocalNextHop(vpnId, dpnId, interfaceName, localNextHopIP, prefix,
862 gwMacAddress, jobKey);
863 localGroupId = groupId;
865 if (groupId == FibConstants.INVALID_GROUP_ID) {
866 LOG.error("Unable to create Group for local prefix {} on rd {} for vpninterface {} on Node {}",
867 prefix, rd, interfaceName, dpnId.toString());
868 return BigInteger.ZERO;
870 final List<InstructionInfo> instructions = Collections.singletonList(
871 new InstructionApplyActions(
872 Collections.singletonList(new ActionGroup(groupId))));
873 final List<InstructionInfo> lfibinstructions = Collections.singletonList(
874 new InstructionApplyActions(
875 Arrays.asList(new ActionPopMpls(etherType), new ActionGroup(groupId))));
876 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
877 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
878 jobCoordinator.enqueueJob(jobKey, () -> {
879 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
880 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, instructions,
881 NwConstants.ADD_FLOW, tx, null);
882 if (!fibUtil.enforceVxlanDatapathSemanticsforInternalRouterVpn(localNextHopInfo.getSubnetId(),
884 optLabel.ifPresent(label -> {
885 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
887 "Installing LFIB and tunnel table entry on dpn {} for interface {} with label "
888 + "{}, rd {}, prefix {}, nexthop {}", dpnId,
889 localNextHopInfo.getVpnInterfaceName(), optLabel, rd, vrfEntry.getDestPrefix(),
891 makeLFibTableEntry(dpnId, label, lfibinstructions, DEFAULT_FIB_FLOW_PRIORITY,
892 NwConstants.ADD_FLOW, tx);
893 // If the extra-route is reachable from VMs attached to the same switch,
894 // then the tunnel table can point to the load balancing group.
895 // If it is reachable from VMs attached to different switches,
896 // then it should be pointing to one of the local group in order to avoid looping.
897 if (vrfEntry.getRoutePaths().size() == 1) {
898 makeTunnelTableEntry(dpnId, label, groupId, tx);
900 makeTunnelTableEntry(dpnId, label, localGroupId, tx);
903 LOG.debug("Route with rd {} prefix {} label {} nexthop {} for vpn {} is an imported "
904 + "route. LFib and Terminating table entries will not be created.",
905 rd, vrfEntry.getDestPrefix(), optLabel, nextHopAddressList, vpnId);
913 LOG.error("localNextHopInfo received is null for prefix {} on rd {} on vpn {}", vrfEntry.getDestPrefix(), rd,
915 return BigInteger.ZERO;
918 private boolean isVpnPresentInDpn(String rd, BigInteger dpnId) {
919 InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
920 Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
921 if (dpnInVpn.isPresent()) {
927 private LabelRouteInfo getLabelRouteInfo(Long label) {
928 InstanceIdentifier<LabelRouteInfo> lriIid = InstanceIdentifier.builder(LabelRouteMap.class)
929 .child(LabelRouteInfo.class, new LabelRouteInfoKey(label)).build();
930 Optional<LabelRouteInfo> opResult = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, lriIid);
931 if (opResult.isPresent()) {
932 return opResult.get();
937 private boolean deleteLabelRouteInfo(LabelRouteInfo lri, String vpnInstanceName, WriteTransaction tx) {
942 LOG.debug("deleting LRI : for label {} vpninstancename {}", lri.getLabel(), vpnInstanceName);
943 InstanceIdentifier<LabelRouteInfo> lriId = InstanceIdentifier.builder(LabelRouteMap.class)
944 .child(LabelRouteInfo.class, new LabelRouteInfoKey(lri.getLabel())).build();
946 List<String> vpnInstancesList = lri.getVpnInstanceList() != null
947 ? lri.getVpnInstanceList() : new ArrayList<>();
948 if (vpnInstancesList.contains(vpnInstanceName)) {
949 LOG.debug("vpninstance {} name is present", vpnInstanceName);
950 vpnInstancesList.remove(vpnInstanceName);
952 if (vpnInstancesList.isEmpty()) {
953 LOG.debug("deleting LRI instance object for label {}", lri.getLabel());
955 tx.delete(LogicalDatastoreType.OPERATIONAL, lriId);
957 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId);
961 LOG.debug("updating LRI instance object for label {}", lri.getLabel());
962 LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri).setVpnInstanceList(vpnInstancesList);
963 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
968 void makeTunnelTableEntry(BigInteger dpId, long label, long groupId/*String egressInterfaceName*/,
969 WriteTransaction tx) {
970 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionGroup(groupId));
972 createTerminatingServiceActions(dpId, (int) label, actionsInfos, tx);
974 LOG.debug("Terminating service Entry for dpID {} : label : {} egress : {} installed successfully",
975 dpId, label, groupId);
978 public void createTerminatingServiceActions(BigInteger destDpId, int label, List<ActionInfo> actionsInfos,
979 WriteTransaction tx) {
980 List<MatchInfo> mkMatches = new ArrayList<>();
982 LOG.debug("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}",
983 destDpId, label, actionsInfos);
986 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
987 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
989 List<InstructionInfo> mkInstructions = new ArrayList<>();
990 mkInstructions.add(new InstructionApplyActions(actionsInfos));
992 FlowEntity terminatingServiceTableFlowEntity =
993 MDSALUtil.buildFlowEntity(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,
994 getTableMissFlowRef(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE, label), 5,
995 String.format("%s:%d", "TST Flow Entry ", label),
996 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, mkInstructions);
998 FlowKey flowKey = new FlowKey(new FlowId(terminatingServiceTableFlowEntity.getFlowId()));
1000 FlowBuilder flowbld = terminatingServiceTableFlowEntity.getFlowBuilder();
1002 Node nodeDpn = FibUtil.buildDpnNode(terminatingServiceTableFlowEntity.getDpnId());
1003 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1004 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1005 .child(Table.class, new TableKey(terminatingServiceTableFlowEntity.getTableId()))
1006 .child(Flow.class, flowKey).build();
1007 tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId, flowbld.build(),
1008 WriteTransaction.CREATE_MISSING_PARENTS);
1011 private void removeTunnelTableEntry(BigInteger dpId, long label, WriteTransaction tx) {
1012 FlowEntity flowEntity;
1013 LOG.debug("remove terminatingServiceActions called with DpnId = {} and label = {}", dpId, label);
1014 List<MatchInfo> mkMatches = new ArrayList<>();
1015 // Matching metadata
1016 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(label)));
1017 flowEntity = MDSALUtil.buildFlowEntity(dpId,
1018 NwConstants.INTERNAL_TUNNEL_TABLE,
1019 getTableMissFlowRef(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, (int) label),
1020 5, String.format("%s:%d", "TST Flow Entry ", label), 0, 0,
1021 COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, null);
1022 Node nodeDpn = FibUtil.buildDpnNode(flowEntity.getDpnId());
1023 FlowKey flowKey = new FlowKey(new FlowId(flowEntity.getFlowId()));
1024 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1025 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1026 .child(Table.class, new TableKey(flowEntity.getTableId())).child(Flow.class, flowKey).build();
1028 tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
1029 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully", dpId, label);
1032 public List<BigInteger> deleteLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
1033 List<BigInteger> returnLocalDpnId = new ArrayList<>();
1034 Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1035 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1036 boolean shouldUpdateNonEcmpLocalNextHop = true;
1037 if (localNextHopInfo == null) {
1038 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1039 if (usedRds.size() > 1) {
1040 LOG.error("The extra route prefix {} is still present in some DPNs in vpn {} on rd {}",
1041 vrfEntry.getDestPrefix(), vpnName, rd);
1042 return returnLocalDpnId;
1044 String vpnRd = (!usedRds.isEmpty()) ? usedRds.get(0) : rd;
1045 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency
1047 Optional<Routes> extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1048 vpnName, vpnRd, vrfEntry.getDestPrefix());
1049 if (extraRouteOptional.isPresent()) {
1050 Routes extraRoute = extraRouteOptional.get();
1052 if (isIpv4Address(extraRoute.getNexthopIpList().get(0))) {
1053 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
1055 ipPrefix = extraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
1057 if (extraRoute.getNexthopIpList().size() > 1) {
1058 shouldUpdateNonEcmpLocalNextHop = false;
1060 localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1061 if (localNextHopInfo != null) {
1062 String localNextHopIP = localNextHopInfo.getIpAddress();
1063 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP,
1064 vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1065 if (!dpnId.equals(BigInteger.ZERO)) {
1066 LOG.trace("Deleting ECMP group for prefix {}, dpn {}", vrfEntry.getDestPrefix(), dpnId);
1067 nextHopManager.setupLoadBalancingNextHop(vpnId, dpnId,
1068 vrfEntry.getDestPrefix(), /*listBucketInfo*/ Collections.emptyList(),
1070 returnLocalDpnId.add(dpnId);
1073 LOG.error("localNextHopInfo unavailable while deleting prefix {} with rds {}, primary rd {} in "
1074 + "vpn {}", vrfEntry.getDestPrefix(), usedRds, rd, vpnName);
1078 if (localNextHopInfo == null) {
1079 /* Imported VRF entry */
1080 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1081 if (optionalLabel.isPresent()) {
1082 Long label = optionalLabel.get();
1083 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1084 LabelRouteInfo lri = getLabelRouteInfo(label);
1085 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1086 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1087 prefixBuilder.setDpnId(lri.getDpnId());
1088 BigInteger dpnId = checkDeleteLocalFibEntry(prefixBuilder.build(), nextHopAddressList.get(0),
1089 vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1090 if (!dpnId.equals(BigInteger.ZERO)) {
1091 returnLocalDpnId.add(dpnId);
1098 LOG.debug("Obtained prefix to interface for rd {} prefix {}", rd, vrfEntry.getDestPrefix());
1099 String localNextHopIP = localNextHopInfo.getIpAddress();
1100 BigInteger dpnId = checkDeleteLocalFibEntry(localNextHopInfo, localNextHopIP,
1101 vpnId, rd, vrfEntry, shouldUpdateNonEcmpLocalNextHop);
1102 if (!dpnId.equals(BigInteger.ZERO)) {
1103 returnLocalDpnId.add(dpnId);
1107 return returnLocalDpnId;
1110 private BigInteger checkDeleteLocalFibEntry(Prefixes localNextHopInfo, final String localNextHopIP,
1111 final Long vpnId, final String rd, final VrfEntry vrfEntry,
1112 boolean shouldUpdateNonEcmpLocalNextHop) {
1113 if (localNextHopInfo != null) {
1114 final BigInteger dpnId = localNextHopInfo.getDpnId();
1115 if (Prefixes.PrefixCue.Nat.equals(localNextHopInfo.getPrefixCue())) {
1116 LOG.debug("checkDeleteLocalFibEntry: NAT Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1117 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1120 if (Prefixes.PrefixCue.PhysNetFunc.equals(localNextHopInfo.getPrefixCue())) {
1121 LOG.debug("checkDeleteLocalFibEntry: PNF Prefix {} with vpnId {} rd {}. Skip local dpn {}"
1122 + " FIB processing", vrfEntry.getDestPrefix(), vpnId, rd, dpnId);
1126 jobCoordinator.enqueueJob(FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, vrfEntry.getDestPrefix()),
1127 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1128 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1129 NwConstants.DEL_FLOW, tx, null);
1130 if (!fibUtil.enforceVxlanDatapathSemanticsforInternalRouterVpn(localNextHopInfo.getSubnetId(),
1132 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1133 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1134 makeLFibTableEntry(dpnId, label, null /* instructions */, DEFAULT_FIB_FLOW_PRIORITY,
1135 NwConstants.DEL_FLOW, tx);
1136 removeTunnelTableEntry(dpnId, label, tx);
1141 //TODO: verify below adjacency call need to be optimized (?)
1142 //In case of the removal of the extra route, the loadbalancing group is updated
1143 if (shouldUpdateNonEcmpLocalNextHop) {
1144 baseVrfEntryHandler.deleteLocalAdjacency(dpnId, vpnId, localNextHopIP, vrfEntry.getDestPrefix());
1148 return BigInteger.ZERO;
1151 private void createRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, String rd,
1152 final VrfEntry vrfEntry, WriteTransaction tx) {
1154 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(newTx -> {
1155 createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx);
1156 }), LOG, "Error creating remote FIB entry");
1160 String vpnName = fibUtil.getVpnNameFromId(vpnId);
1161 LOG.debug("createremotefibentry: adding route {} for rd {} on remoteDpnId {}",
1162 vrfEntry.getDestPrefix(), rd, remoteDpnId);
1164 List<AdjacencyResult> adjacencyResults = baseVrfEntryHandler.resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
1165 if (adjacencyResults.isEmpty()) {
1166 LOG.error("Could not get interface for route-paths: {} in vpn {} on DPN {}",
1167 vrfEntry.getRoutePaths(), rd, remoteDpnId);
1168 LOG.error("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
1172 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1173 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
1174 vpnName, usedRds, vrfEntry.getDestPrefix());
1175 // create loadbalancing groups for extra routes only when the extra route is present behind
1177 if (!vpnExtraRoutes.isEmpty() && (vpnExtraRoutes.size() > 1
1178 || vpnExtraRoutes.get(0).getNexthopIpList().size() > 1)) {
1179 List<InstructionInfo> instructions = new ArrayList<>();
1180 // Obtain the local routes for this particular dpn.
1181 java.util.Optional<Routes> routes = vpnExtraRoutes
1184 Prefixes prefixToInterface = fibUtil.getPrefixToInterface(vpnId,
1185 fibUtil.getIpPrefix(route.getNexthopIpList().get(0)));
1186 if (prefixToInterface == null) {
1189 return remoteDpnId.equals(prefixToInterface.getDpnId());
1191 long groupId = nextHopManager.createNextHopGroups(vpnId, rd, remoteDpnId, vrfEntry,
1192 routes.isPresent() ? routes.get() : null, vpnExtraRoutes);
1193 if (groupId == FibConstants.INVALID_GROUP_ID) {
1194 LOG.error("Unable to create Group for local prefix {} on rd {} on Node {}",
1195 vrfEntry.getDestPrefix(), rd, remoteDpnId.toString());
1198 List<ActionInfo> actionInfos =
1199 Collections.singletonList(new ActionGroup(groupId));
1200 instructions.add(new InstructionApplyActions(actionInfos));
1201 baseVrfEntryHandler.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
1202 NwConstants.ADD_FLOW, tx, null);
1204 baseVrfEntryHandler.programRemoteFib(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, null);
1207 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
1210 protected void cleanUpOpDataForFib(Long vpnId, String primaryRd, final VrfEntry vrfEntry) {
1211 /* Get interface info from prefix to interface mapping;
1212 Use the interface info to get the corresponding vpn interface op DS entry,
1213 remove the adjacency corresponding to this fib entry.
1214 If adjacency removed is the last adjacency, clean up the following:
1215 - vpn interface from dpntovpn list, dpn if last vpn interface on dpn
1216 - prefix to interface entry
1217 - vpn interface op DS
1219 LOG.debug("Cleanup of prefix {} in VPN {}", vrfEntry.getDestPrefix(), vpnId);
1220 Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1221 if (prefixInfo == null) {
1222 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1223 String usedRd = usedRds.isEmpty() ? primaryRd : usedRds.get(0);
1224 Routes extraRoute = baseVrfEntryHandler.getVpnToExtraroute(vpnId, usedRd, vrfEntry.getDestPrefix());
1225 if (extraRoute != null) {
1226 for (String nextHopIp : extraRoute.getNexthopIpList()) {
1227 LOG.debug("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
1228 if (nextHopIp != null) {
1230 if (isIpv4Address(nextHopIp)) {
1231 ipPrefix = nextHopIp + NwConstants.IPV4PREFIX;
1233 ipPrefix = nextHopIp + NwConstants.IPV6PREFIX;
1235 prefixInfo = fibUtil.getPrefixToInterface(vpnId, ipPrefix);
1236 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1240 if (prefixInfo == null) {
1241 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1242 if (optionalLabel.isPresent()) {
1243 Long label = optionalLabel.get();
1244 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1245 LabelRouteInfo lri = getLabelRouteInfo(label);
1246 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1247 PrefixesBuilder prefixBuilder = new PrefixesBuilder();
1248 prefixBuilder.setDpnId(lri.getDpnId());
1249 prefixBuilder.setVpnInterfaceName(lri.getVpnInterfaceName());
1250 prefixBuilder.setIpAddress(lri.getPrefix());
1251 prefixInfo = prefixBuilder.build();
1252 LOG.debug("Fetched labelRouteInfo for label {} interface {} and got dpn {}",
1253 label, prefixInfo.getVpnInterfaceName(), lri.getDpnId());
1254 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, extraRoute);
1259 checkCleanUpOpDataForFib(prefixInfo, vpnId, primaryRd, vrfEntry, null /*Routes*/);
1263 private void checkCleanUpOpDataForFib(final Prefixes prefixInfo, final Long vpnId, final String rd,
1264 final VrfEntry vrfEntry, final Routes extraRoute) {
1266 if (prefixInfo == null) {
1267 LOG.error("Cleanup VPN Data Failed as unable to find prefix Info for prefix {} VpnId {} rd {}",
1268 vrfEntry.getDestPrefix(), vpnId, rd);
1269 return; //Don't have any info for this prefix (shouldn't happen); need to return
1272 if (Prefixes.PrefixCue.Nat.equals(prefixInfo.getPrefixCue())) {
1273 LOG.debug("NAT Prefix {} with vpnId {} rd {}. Skip FIB processing",
1274 vrfEntry.getDestPrefix(), vpnId, rd);
1278 String ifName = prefixInfo.getVpnInterfaceName();
1279 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName,
1280 new CleanupVpnInterfaceWorker(prefixInfo, vpnId, rd, vrfEntry, extraRoute));
1283 private class CleanupVpnInterfaceWorker implements Callable<List<ListenableFuture<Void>>> {
1284 Prefixes prefixInfo;
1290 CleanupVpnInterfaceWorker(final Prefixes prefixInfo, final Long vpnId, final String rd,
1291 final VrfEntry vrfEntry, final Routes extraRoute) {
1292 this.prefixInfo = prefixInfo;
1295 this.vrfEntry = vrfEntry;
1296 this.extraRoute = extraRoute;
1300 public List<ListenableFuture<Void>> call() {
1301 // If another renderer(for eg : CSS) needs to be supported, check can be performed here
1302 // to call the respective helpers.
1303 return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
1304 //First Cleanup LabelRouteInfo
1305 //TODO(KIRAN) : Move the below block when addressing iRT/eRT for L3VPN Over VxLan
1306 LOG.debug("cleanupVpnInterfaceWorker: rd {} prefix {}", rd, prefixInfo.getIpAddress());
1307 if (VrfEntry.EncapType.Mplsgre.equals(vrfEntry.getEncapType())) {
1308 FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
1309 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1310 synchronized (label.toString().intern()) {
1311 LabelRouteInfo lri = getLabelRouteInfo(label);
1312 if (lri != null && lri.getPrefix().equals(vrfEntry.getDestPrefix())
1313 && nextHopAddressList.contains(lri.getNextHopIpList().get(0))) {
1314 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1315 fibUtil.getVpnInstanceOpData(rd);
1316 String vpnInstanceName = "";
1317 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1318 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1320 boolean lriRemoved = deleteLabelRouteInfo(lri, vpnInstanceName, tx);
1322 String parentRd = lri.getParentVpnRd();
1323 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1324 parentRd, vrfEntry.getDestPrefix()));
1327 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1328 rd, vrfEntry.getDestPrefix()));
1333 String ifName = prefixInfo.getVpnInterfaceName();
1334 Optional<String> optVpnName = fibUtil.getVpnNameFromRd(rd);
1335 String vpnName = null;
1337 if (Prefixes.PrefixCue.PhysNetFunc.equals(prefixInfo.getPrefixCue())) {
1338 /*Get vpnId for rd = networkId since op vpnInterface will be pointing to rd = networkId
1340 Optional<String> vpnNameOpt = fibUtil.getVpnNameFromRd(vrfEntry.getParentVpnRd());
1341 if (vpnNameOpt.isPresent()) {
1342 vpnId = fibUtil.getVpnId(vpnNameOpt.get());
1345 if (optVpnName.isPresent()) {
1346 vpnName = optVpnName.get();
1347 Optional<VpnInterfaceOpDataEntry> opVpnInterface = MDSALUtil
1348 .read(dataBroker, LogicalDatastoreType.OPERATIONAL,
1349 fibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1350 if (opVpnInterface.isPresent()) {
1351 long associatedVpnId = fibUtil.getVpnId(vpnName);
1352 if (vpnId != associatedVpnId) {
1353 LOG.warn("Prefixes {} are associated with different vpn instance with id {} rather than {}",
1354 vrfEntry.getDestPrefix(), associatedVpnId, vpnId);
1355 LOG.warn("Not proceeding with Cleanup op data for prefix {}", vrfEntry.getDestPrefix());
1358 LOG.debug("Processing cleanup of prefix {} associated with vpn {}",
1359 vrfEntry.getDestPrefix(), associatedVpnId);
1363 if (extraRoute != null) {
1364 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
1365 //Only one used Rd present in case of removal event
1366 String usedRd = usedRds.get(0);
1367 if (optVpnName.isPresent()) {
1368 tx.delete(LogicalDatastoreType.OPERATIONAL,
1369 baseVrfEntryHandler.getVpnToExtrarouteIdentifier(vpnName, usedRd,
1370 vrfEntry.getDestPrefix()));
1371 tx.delete(LogicalDatastoreType.CONFIGURATION,
1372 VpnExtraRouteHelper.getUsedRdsIdentifier(vpnId, vrfEntry.getDestPrefix()));
1375 handleAdjacencyAndVpnOpInterfaceDeletion(vrfEntry, ifName, vpnName, tx);
1381 * Check all the adjacency in VpnInterfaceOpData and decide whether to delete the entire interface or only adj.
1382 * Remove Adjacency from VPNInterfaceOpData.
1383 * if Adjacency != primary.
1384 * if Adjacency == primary , then mark it for deletion.
1385 * Remove entire VPNinterfaceOpData Entry.
1386 * if sie of Adjacency <= 2 and all are marked for deletion , delete the entire VPNinterface Op entry.
1387 * @param vrfEntry - VrfEntry removed
1388 * @param ifName - Interface name from VRFentry
1389 * @param vpnName - VPN name of corresponding VRF
1390 * @param tx - ReadWrite Tx
1391 * @throws ReadFailedException - Exception thrown in case of read failed
1393 private void handleAdjacencyAndVpnOpInterfaceDeletion(VrfEntry vrfEntry, String ifName, String vpnName,
1394 ReadWriteTransaction tx) throws ReadFailedException {
1395 InstanceIdentifier<Adjacency> adjacencyIid =
1396 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix());
1397 Optional<Adjacency> adjacencyOptional = tx.read(LogicalDatastoreType.OPERATIONAL, adjacencyIid).checkedGet();
1398 if (adjacencyOptional.isPresent()) {
1399 if (adjacencyOptional.get().getAdjacencyType() != Adjacency.AdjacencyType.PrimaryAdjacency) {
1400 tx.delete(LogicalDatastoreType.OPERATIONAL,
1401 FibUtil.getAdjacencyIdentifierOp(ifName, vpnName, vrfEntry.getDestPrefix()));
1403 tx.merge(LogicalDatastoreType.OPERATIONAL, adjacencyIid,
1404 new AdjacencyBuilder(adjacencyOptional.get()).setMarkedForDeletion(true).build());
1408 Optional<AdjacenciesOp> optAdjacencies =
1409 tx.read(LogicalDatastoreType.OPERATIONAL,
1410 FibUtil.getAdjListPathOp(ifName, vpnName)).checkedGet();
1412 if (!optAdjacencies.isPresent() || optAdjacencies.get().getAdjacency() == null) {
1416 if (optAdjacencies.get().getAdjacency().stream().count() <= 2
1417 && optAdjacencies.get().getAdjacency().stream().allMatch(adjacency ->
1418 adjacency.getAdjacencyType() == Adjacency.AdjacencyType.PrimaryAdjacency
1419 && adjacency.isMarkedForDeletion() != null
1420 && adjacency.isMarkedForDeletion()
1422 LOG.info("Clean up vpn interface {} to vpn {} list.", ifName, vpnName);
1423 tx.delete(LogicalDatastoreType.OPERATIONAL,
1424 FibUtil.getVpnInterfaceOpDataEntryIdentifier(ifName, vpnName));
1428 private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier, final VrfEntry vrfEntry) {
1429 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
1430 final String rd = vrfTableKey.getRouteDistinguisher();
1431 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(vrfTableKey.getRouteDistinguisher());
1432 if (vpnInstance == null) {
1433 LOG.error("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
1436 final Collection<VpnToDpnList> vpnToDpnList;
1437 if (vrfEntry.getParentVpnRd() != null
1438 && FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
1439 // This block MUST BE HIT only for PNF (Physical Network Function) FIB Entries.
1440 VpnInstanceOpDataEntry parentVpnInstance = fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
1441 vpnToDpnList = parentVpnInstance != null ? parentVpnInstance.getVpnToDpnList() :
1442 vpnInstance.getVpnToDpnList();
1443 LOG.info("deleteFibEntries: Processing deletion of PNF FIB entry with rd {} prefix {}",
1444 vrfEntry.getParentVpnRd(), vrfEntry.getDestPrefix());
1446 vpnToDpnList = vpnInstance.getVpnToDpnList();
1449 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1450 final java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1451 List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1452 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1453 if (subnetRoute != null) {
1454 long elanTag = subnetRoute.getElantag();
1455 LOG.trace("SUBNETROUTE: deleteFibEntries: SubnetRoute augmented vrfentry found for rd {} prefix {}"
1456 + " with elantag {}", rd, vrfEntry.getDestPrefix(), elanTag);
1457 if (vpnToDpnList != null) {
1458 jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
1459 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1460 for (final VpnToDpnList curDpn : vpnToDpnList) {
1462 baseVrfEntryHandler.makeConnectedRoute(curDpn.getDpnId(), vpnInstance.getVpnId(), vrfEntry,
1463 vrfTableKey.getRouteDistinguisher(), null, NwConstants.DEL_FLOW, tx, null);
1464 if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.SELF_IMPORTED) {
1465 optionalLabel.ifPresent(label -> makeLFibTableEntry(curDpn.getDpnId(), label, null,
1466 DEFAULT_FIB_FLOW_PRIORITY, NwConstants.DEL_FLOW, tx));
1469 installSubnetBroadcastAddrDropRule(curDpn.getDpnId(), rd, vpnInstance.getVpnId(),
1470 vrfEntry, NwConstants.DEL_FLOW, tx);
1474 optionalLabel.ifPresent(label -> {
1475 synchronized (label.toString().intern()) {
1476 LabelRouteInfo lri = getLabelRouteInfo(label);
1477 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
1478 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
1479 fibUtil.getVpnInstanceOpData(rd);
1480 String vpnInstanceName = "";
1481 if (vpnInstanceOpDataEntryOptional.isPresent()) {
1482 vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
1484 boolean lriRemoved = this.deleteLabelRouteInfo(lri, vpnInstanceName, null);
1486 String parentRd = lri.getParentVpnRd();
1487 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1488 parentRd, vrfEntry.getDestPrefix()));
1489 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}"
1490 + " as labelRouteInfo cleared", label, rd, vrfEntry.getDestPrefix());
1493 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
1494 rd, vrfEntry.getDestPrefix()));
1495 LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}",
1496 label, rd, vrfEntry.getDestPrefix());
1503 final List<BigInteger> localDpnIdList = deleteLocalFibEntry(vpnInstance.getVpnId(),
1504 vrfTableKey.getRouteDistinguisher(), vrfEntry);
1505 if (vpnToDpnList != null) {
1506 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1507 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1509 Optional<Routes> extraRouteOptional;
1510 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
1511 if (usedRds != null && !usedRds.isEmpty()) {
1512 jobKey = FibUtil.getJobKeyForRdPrefix(usedRds.get(0), vrfEntry.getDestPrefix());
1513 if (usedRds.size() > 1) {
1514 LOG.error("The extra route prefix is still present in some DPNs");
1517 // The first rd is retrieved from usedrds as Only 1 rd would be present as extra route prefix
1518 //is not present in any other DPN
1519 extraRouteOptional = VpnExtraRouteHelper
1520 .getVpnExtraroutes(dataBroker, vpnName, usedRds.get(0), vrfEntry.getDestPrefix());
1523 jobKey = FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix());
1524 extraRouteOptional = Optional.absent();
1527 jobCoordinator.enqueueJob(jobKey,
1528 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1529 if (localDpnIdList.size() <= 0) {
1530 for (VpnToDpnList curDpn : vpnToDpnList) {
1531 baseVrfEntryHandler.deleteRemoteRoute(BigInteger.ZERO, curDpn.getDpnId(),
1532 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional, tx);
1535 for (BigInteger localDpnId : localDpnIdList) {
1536 for (VpnToDpnList curDpn : vpnToDpnList) {
1537 if (!curDpn.getDpnId().equals(localDpnId)) {
1538 baseVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
1539 vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional, tx);
1547 //The flow/group entry has been deleted from config DS; need to clean up associated operational
1548 //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
1549 cleanUpOpDataForFib(vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(), vrfEntry);
1551 // Remove all fib entries configured due to interVpnLink, when nexthop is the opposite endPoint
1552 // of the interVpnLink.
1553 Optional<String> optVpnUuid = fibUtil.getVpnNameFromRd(rd);
1554 if (optVpnUuid.isPresent()) {
1555 String vpnUuid = optVpnUuid.get();
1556 FibUtil.getFirstNextHopAddress(vrfEntry).ifPresent(routeNexthop -> {
1557 Optional<InterVpnLinkDataComposite> optInterVpnLink = interVpnLinkCache.getInterVpnLinkByVpnId(vpnUuid);
1558 if (optInterVpnLink.isPresent()) {
1559 InterVpnLinkDataComposite interVpnLink = optInterVpnLink.get();
1560 if (interVpnLink.isIpAddrTheOtherVpnEndpoint(routeNexthop, vpnUuid)) {
1561 // This is route that points to the other endpoint of an InterVpnLink
1562 // In that case, we should look for the FIB table pointing to
1563 // LPortDispatcher table and remove it.
1564 removeInterVPNLinkRouteFlows(interVpnLink, vpnUuid, vrfEntry);
1572 private void makeLFibTableEntry(BigInteger dpId, long label, List<InstructionInfo> instructions, int priority,
1573 int addOrRemove, WriteTransaction tx) {
1575 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
1576 newTx -> makeLFibTableEntry(dpId, label, instructions, priority, addOrRemove, newTx)), LOG,
1577 "Error making LFIB table entry");
1581 List<MatchInfo> matches = new ArrayList<>();
1582 matches.add(MatchEthernetType.MPLS_UNICAST);
1583 matches.add(new MatchMplsLabel(label));
1585 // Install the flow entry in L3_LFIB_TABLE
1586 String flowRef = FibUtil.getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, label, priority);
1588 FlowEntity flowEntity;
1589 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef, priority, flowRef, 0, 0,
1590 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
1591 Flow flow = flowEntity.getFlowBuilder().build();
1592 String flowId = flowEntity.getFlowId();
1593 FlowKey flowKey = new FlowKey(new FlowId(flowId));
1594 Node nodeDpn = FibUtil.buildDpnNode(dpId);
1595 InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
1596 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
1597 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
1599 if (addOrRemove == NwConstants.ADD_FLOW) {
1600 tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId, flow, WriteTransaction.CREATE_MISSING_PARENTS);
1602 tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
1605 LOG.debug("LFIB Entry for dpID {} : label : {} instructions {} : key {} {} successfully",
1606 dpId, label, instructions, flowKey, NwConstants.ADD_FLOW == addOrRemove ? "ADDED" : "REMOVED");
1609 public void populateFibOnNewDpn(final BigInteger dpnId, final long vpnId, final String rd,
1610 final FutureCallback<List<Void>> callback) {
1611 LOG.trace("New dpn {} for vpn {} : populateFibOnNewDpn", dpnId, rd);
1612 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1614 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1615 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1616 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1617 LogicalDatastoreType.CONFIGURATION, id);
1618 List<ListenableFuture<Void>> futures = new ArrayList<>();
1619 if (!vrfTable.isPresent()) {
1620 LOG.info("populateFibOnNewDpn: dpn: {}: VRF Table not yet available for RD {}", dpnId, rd);
1621 if (callback != null) {
1622 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1623 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1627 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1628 futures.add(retryingTxRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
1629 for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
1630 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1631 if (subnetRoute != null) {
1632 long elanTag = subnetRoute.getElantag();
1633 installSubnetRouteInFib(dpnId, elanTag, rd, vpnId, vrfEntry, tx);
1634 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry, NwConstants.ADD_FLOW,
1638 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1639 if (routerInt != null) {
1640 LOG.trace("Router augmented vrfentry found rd:{}, uuid:{}, ip:{}, mac:{}",
1641 rd, routerInt.getUuid(), routerInt.getIpAddress(), routerInt.getMacAddress());
1642 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1643 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1644 NwConstants.ADD_FLOW);
1647 //Handle local flow creation for imports
1648 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1649 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1650 if (optionalLabel.isPresent()) {
1651 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1652 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1653 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList, lri)) {
1654 if (lri.getDpnId().equals(dpnId)) {
1656 int etherType = NWUtil.getEtherTypeFromIpPrefix(
1657 vrfEntry.getDestPrefix());
1658 createLocalFibEntry(vpnId, rd, vrfEntry, etherType);
1659 } catch (IllegalArgumentException ex) {
1660 LOG.warn("Unable to get etherType for IP Prefix {}",
1661 vrfEntry.getDestPrefix());
1668 boolean shouldCreateRemoteFibEntry = shouldCreateFibEntryForVrfAndVpnIdOnDpn(vpnId,
1670 if (shouldCreateRemoteFibEntry) {
1671 LOG.trace("Will create remote FIB entry for vrfEntry {} on DPN {}", vrfEntry, dpnId);
1672 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1673 List<SubTransaction> txnObjects = new ArrayList<>();
1674 bgpRouteVrfEntryHandler.createRemoteFibEntry(dpnId, vpnId,
1675 vrfTable.get().getRouteDistinguisher(), vrfEntry, tx, txnObjects);
1677 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
1683 if (callback != null) {
1684 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1685 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1692 public void populateExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1693 final String localNextHopIp, final String remoteNextHopIp) {
1694 LOG.trace("populateExternalRoutesOnDpn : dpn {}, vpn {}, rd {}, localNexthopIp {} , remoteNextHopIp {} ",
1695 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1696 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1697 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1698 List<SubTransaction> txnObjects = new ArrayList<>();
1699 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1700 if (vrfTable.isPresent()) {
1701 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1702 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1703 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1704 vrfTable.get().getVrfEntry().stream()
1705 .filter(vrfEntry -> RouteOrigin.BGP == RouteOrigin.value(vrfEntry.getOrigin()))
1706 .forEach(bgpRouteVrfEntryHandler.getConsumerForCreatingRemoteFib(dpnId, vpnId,
1707 rd, remoteNextHopIp, vrfTable, tx, txnObjects));
1713 public void manageRemoteRouteOnDPN(final boolean action,
1714 final BigInteger localDpnId,
1717 final String destPrefix,
1718 final String destTepIp,
1720 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1722 if (vpnInstance == null) {
1723 LOG.error("VpnInstance for rd {} not present for prefix {}", rd, destPrefix);
1727 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, localDpnId),
1728 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1729 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1730 VrfTablesKey vrfTablesKey = new VrfTablesKey(rd);
1731 VrfEntry vrfEntry = getVrfEntry(dataBroker, rd, destPrefix);
1732 if (vrfEntry == null) {
1735 LOG.trace("manageRemoteRouteOnDPN :: action {}, DpnId {}, vpnId {}, rd {}, destPfx {}",
1736 action, localDpnId, vpnId, rd, destPrefix);
1737 List<RoutePaths> routePathList = vrfEntry.getRoutePaths();
1738 VrfEntry modVrfEntry;
1739 if (routePathList == null || routePathList.isEmpty()) {
1740 modVrfEntry = FibHelper.getVrfEntryBuilder(vrfEntry, label,
1741 Collections.singletonList(destTepIp),
1742 RouteOrigin.value(vrfEntry.getOrigin()), null /* parentVpnRd */).build();
1744 modVrfEntry = vrfEntry;
1748 LOG.trace("manageRemoteRouteOnDPN updated(add) vrfEntry :: {}", modVrfEntry);
1749 createRemoteFibEntry(localDpnId, vpnId, vrfTablesKey.getRouteDistinguisher(),
1752 LOG.trace("manageRemoteRouteOnDPN updated(remove) vrfEntry :: {}", modVrfEntry);
1753 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnInstance.getVpnId(),
1754 vrfEntry.getDestPrefix());
1755 if (usedRds.size() > 1) {
1756 LOG.debug("The extra route prefix is still present in some DPNs");
1759 //Is this fib route an extra route? If yes, get the nexthop which would be
1760 //an adjacency in the vpn
1761 Optional<Routes> extraRouteOptional = Optional.absent();
1762 if (usedRds.size() != 0) {
1763 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
1764 fibUtil.getVpnNameFromId(vpnInstance.getVpnId()),
1765 usedRds.get(0), vrfEntry.getDestPrefix());
1767 baseVrfEntryHandler.deleteRemoteRoute(null, localDpnId, vpnId, vrfTablesKey, modVrfEntry,
1768 extraRouteOptional, tx);
1774 public void cleanUpDpnForVpn(final BigInteger dpnId, final long vpnId, final String rd,
1775 final FutureCallback<List<Void>> callback) {
1776 LOG.trace("cleanUpDpnForVpn: Remove dpn {} for vpn {} : cleanUpDpnForVpn", dpnId, rd);
1777 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1779 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1780 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1781 List<SubTransaction> txnObjects = new ArrayList<>();
1782 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker,
1783 LogicalDatastoreType.CONFIGURATION, id);
1784 List<ListenableFuture<Void>> futures = new ArrayList<>();
1785 if (vrfTable.isPresent()) {
1786 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1787 futures.add(retryingTxRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1788 String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
1789 for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
1790 /* Handle subnet routes here */
1791 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
1792 if (subnetRoute != null && !fibUtil
1793 .isInterfacePresentInDpn(vrfEntry.getParentVpnRd(), dpnId)) {
1794 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Cleaning subnetroute {} on dpn {}"
1795 + " for vpn {}", vrfEntry.getDestPrefix(), dpnId, rd);
1796 baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
1797 NwConstants.DEL_FLOW, tx, null);
1798 List<RoutePaths> routePaths = vrfEntry.getRoutePaths();
1799 if (routePaths != null) {
1800 for (RoutePaths routePath : routePaths) {
1801 makeLFibTableEntry(dpnId, routePath.getLabel(), null,
1802 DEFAULT_FIB_FLOW_PRIORITY,
1803 NwConstants.DEL_FLOW, tx);
1804 LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Released subnetroute label {}"
1805 + " for rd {} prefix {}", routePath.getLabel(), rd,
1806 vrfEntry.getDestPrefix());
1809 installSubnetBroadcastAddrDropRule(dpnId, rd, vpnId, vrfEntry,
1810 NwConstants.DEL_FLOW, tx);
1813 // ping responder for router interfaces
1814 RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
1815 if (routerInt != null) {
1816 LOG.trace("Router augmented vrfentry found for rd:{}, uuid:{}, ip:{}, mac:{}",
1817 rd, routerInt.getUuid(), routerInt.getIpAddress(),
1818 routerInt.getMacAddress());
1819 routerInterfaceVrfEntryHandler.installRouterFibEntry(vrfEntry, dpnId, vpnId,
1820 routerInt.getIpAddress(), new MacAddress(routerInt.getMacAddress()),
1821 NwConstants.DEL_FLOW);
1825 //Handle local flow deletion for imports
1826 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
1827 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1828 if (optionalLabel.isPresent()) {
1829 List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
1830 LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
1831 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList,
1832 lri) && lri.getDpnId().equals(dpnId)) {
1833 deleteLocalFibEntry(vpnId, rd, vrfEntry);
1838 // Passing null as we don't know the dpn
1839 // to which prefix is attached at this point
1840 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
1841 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
1842 Optional<Routes> extraRouteOptional;
1843 //Is this fib route an extra route? If yes, get the nexthop which would be
1844 //an adjacency in the vpn
1845 if (usedRds != null && !usedRds.isEmpty()) {
1846 if (usedRds.size() > 1) {
1847 LOG.error("The extra route prefix is still present in some DPNs");
1850 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
1851 usedRds.get(0), vrfEntry.getDestPrefix());
1855 extraRouteOptional = Optional.absent();
1857 if (RouteOrigin.BGP.getValue().equals(vrfEntry.getOrigin())) {
1858 bgpRouteVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1859 vrfTable.get().key(), vrfEntry, extraRouteOptional, tx, txnObjects);
1861 if (subnetRoute == null || !fibUtil
1862 .isInterfacePresentInDpn(vrfEntry.getParentVpnRd(), dpnId)) {
1863 baseVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
1864 vrfTable.get().key(), vrfEntry, extraRouteOptional, tx);
1870 if (callback != null) {
1871 ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
1872 Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
1875 LOG.error("cleanUpDpnForVpn: No vrf table found for rd {} vpnId {} dpn {}", rd, vpnId, dpnId);
1882 public void cleanUpExternalRoutesOnDpn(final BigInteger dpnId, final long vpnId, final String rd,
1883 final String localNextHopIp, final String remoteNextHopIp) {
1884 LOG.trace("cleanUpExternalRoutesOnDpn : cleanup remote routes on dpn {} for vpn {}, rd {}, "
1885 + " localNexthopIp {} , remoteNexhtHopIp {}",
1886 dpnId, vpnId, rd, localNextHopIp, remoteNextHopIp);
1887 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
1888 final VpnInstanceOpDataEntry vpnInstance = fibUtil.getVpnInstance(rd);
1889 List<SubTransaction> txnObjects = new ArrayList<>();
1890 final Optional<VrfTables> vrfTable = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
1891 if (vrfTable.isPresent()) {
1892 jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
1894 synchronized (vpnInstance.getVpnInstanceName().intern()) {
1895 return Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
1896 tx -> vrfTable.get().getVrfEntry().stream()
1897 .filter(vrfEntry -> RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP)
1898 .forEach(bgpRouteVrfEntryHandler.getConsumerForDeletingRemoteFib(dpnId, vpnId,
1899 remoteNextHopIp, vrfTable, tx, txnObjects))));
1905 public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
1906 InstanceIdentifierBuilder<VrfTables> idBuilder =
1907 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
1908 return idBuilder.build();
1911 private String getInterVpnFibFlowRef(String interVpnLinkName, String prefix, String nextHop) {
1912 return FLOWID_PREFIX + interVpnLinkName + NwConstants.FLOWID_SEPARATOR + prefix + NwConstants
1913 .FLOWID_SEPARATOR + nextHop;
1916 private String getTableMissFlowRef(BigInteger dpnId, short tableId, int tableMiss) {
1917 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR
1918 + tableMiss + FLOWID_PREFIX;
1921 private VrfEntry getVrfEntry(DataBroker broker, String rd, String ipPrefix) {
1922 InstanceIdentifier<VrfEntry> vrfEntryId = InstanceIdentifier.builder(FibEntries.class)
1923 .child(VrfTables.class, new VrfTablesKey(rd))
1924 .child(VrfEntry.class, new VrfEntryKey(ipPrefix)).build();
1925 Optional<VrfEntry> vrfEntry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
1926 if (vrfEntry.isPresent()) {
1927 return vrfEntry.get();
1932 public void removeInterVPNLinkRouteFlows(final InterVpnLinkDataComposite interVpnLink,
1933 final String vpnName,
1934 final VrfEntry vrfEntry) {
1935 Preconditions.checkArgument(vrfEntry.getRoutePaths() != null && vrfEntry.getRoutePaths().size() == 1);
1937 String interVpnLinkName = interVpnLink.getInterVpnLinkName();
1938 List<BigInteger> targetDpns = interVpnLink.getEndpointDpnsByVpnName(vpnName);
1940 if (targetDpns.isEmpty()) {
1941 LOG.warn("Could not find DPNs for VPN {} in InterVpnLink {}", vpnName, interVpnLinkName);
1945 java.util.Optional<String> optNextHop = FibUtil.getFirstNextHopAddress(vrfEntry);
1946 java.util.Optional<Long> optLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
1950 optNextHop.ifPresent(nextHop -> {
1951 String flowRef = getInterVpnFibFlowRef(interVpnLinkName, vrfEntry.getDestPrefix(), nextHop);
1952 FlowKey flowKey = new FlowKey(new FlowId(flowRef));
1953 Flow flow = new FlowBuilder().withKey(flowKey).setId(new FlowId(flowRef))
1954 .setTableId(NwConstants.L3_FIB_TABLE).setFlowName(flowRef).build();
1956 LOG.trace("Removing flow in FIB table for interVpnLink {} key {}", interVpnLinkName, flowRef);
1957 for (BigInteger dpId : targetDpns) {
1958 LOG.debug("Removing flow: VrfEntry=[prefix={} nexthop={}] dpn {} for InterVpnLink {} in FIB",
1959 vrfEntry.getDestPrefix(), nextHop, dpId, interVpnLinkName);
1961 mdsalManager.removeFlow(dpId, flow);
1967 optLabel.ifPresent(label -> {
1968 LOG.trace("Removing flow in FIB table for interVpnLink {}", interVpnLinkName);
1970 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
1971 for (BigInteger dpId : targetDpns) {
1972 LOG.debug("Removing flow: VrfEntry=[prefix={} label={}] dpn {} for InterVpnLink {} in LFIB",
1973 vrfEntry.getDestPrefix(), label, dpId, interVpnLinkName);
1974 makeLFibTableEntry(dpId, label, /*instructions*/null, LFIB_INTERVPN_PRIORITY, NwConstants.DEL_FLOW,
1977 }), LOG, "Error removing flows");
1981 private boolean isPrefixAndNextHopPresentInLri(String prefix,
1982 List<String> nextHopAddressList, LabelRouteInfo lri) {
1983 return lri != null && lri.getPrefix().equals(prefix)
1984 && nextHopAddressList.contains(lri.getNextHopIpList().get(0));
1987 private boolean shouldCreateFibEntryForVrfAndVpnIdOnDpn(Long vpnId, VrfEntry vrfEntry, BigInteger dpnId) {
1988 if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
1992 Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
1993 if (prefix != null) {
1994 BigInteger prefixDpnId = prefix.getDpnId();
1995 if (dpnId.equals(prefixDpnId)) {
1996 LOG.trace("Should not create remote FIB entry for vrfEntry {} on DPN {}",