a392b712be4276b4494a5d06197f72a504858c55
[netvirt.git] / fibmanager / impl / src / main / java / org / opendaylight / netvirt / fibmanager / BaseVrfEntryHandler.java
1 /*
2  * Copyright © 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netvirt.fibmanager;
9
10 import static java.util.stream.Collectors.toList;
11 import static org.opendaylight.genius.mdsalutil.NWUtil.isIpv4Address;
12
13 import com.google.common.base.Optional;
14 import java.math.BigInteger;
15 import java.net.Inet4Address;
16 import java.net.InetAddress;
17 import java.net.UnknownHostException;
18 import java.time.Duration;
19 import java.time.temporal.ChronoUnit;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.List;
23 import javax.inject.Inject;
24 import javax.inject.Singleton;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
31 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
32 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
33 import org.opendaylight.genius.mdsalutil.ActionInfo;
34 import org.opendaylight.genius.mdsalutil.FlowEntity;
35 import org.opendaylight.genius.mdsalutil.InstructionInfo;
36 import org.opendaylight.genius.mdsalutil.MDSALUtil;
37 import org.opendaylight.genius.mdsalutil.MatchInfo;
38 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
39 import org.opendaylight.genius.mdsalutil.NwConstants;
40 import org.opendaylight.genius.mdsalutil.actions.ActionMoveSourceDestinationEth;
41 import org.opendaylight.genius.mdsalutil.actions.ActionMoveSourceDestinationIp;
42 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
43 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
44 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
45 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
46 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
47 import org.opendaylight.genius.mdsalutil.actions.ActionSetIcmpType;
48 import org.opendaylight.genius.mdsalutil.actions.ActionSetSourceIp;
49 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
50 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
51 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
52 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetDestination;
53 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
54 import org.opendaylight.genius.mdsalutil.matches.MatchIcmpv4;
55 import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
56 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
57 import org.opendaylight.genius.mdsalutil.matches.MatchIpv6Destination;
58 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
59 import org.opendaylight.genius.utils.batching.SubTransaction;
60 import org.opendaylight.genius.utils.batching.SubTransactionImpl;
61 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
62 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
63 import org.opendaylight.netvirt.fibmanager.NexthopManager.AdjacencyResult;
64 import org.opendaylight.netvirt.fibmanager.api.FibHelper;
65 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
66 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
67 import org.opendaylight.serviceutils.upgrade.UpgradeState;
68 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
69 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnToExtraroutes;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.Vpn;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.VpnKey;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.ExtraRoutes;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.ExtraRoutesKey;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.RoutesKey;
94 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
95 import org.slf4j.Logger;
96 import org.slf4j.LoggerFactory;
97
98 @Singleton
99 public class BaseVrfEntryHandler implements AutoCloseable {
100
101     private static final Logger LOG = LoggerFactory.getLogger(BaseVrfEntryHandler.class);
102     private static final BigInteger COOKIE_VM_FIB_TABLE =  new BigInteger("8000003", 16);
103     private static final int DEFAULT_FIB_FLOW_PRIORITY = 10;
104
105     private final DataBroker dataBroker;
106     private final ManagedNewTransactionRunner txRunner;
107     private final NexthopManager nextHopManager;
108     private final IMdsalApiManager mdsalManager;
109     private final FibUtil fibUtil;
110     private final UpgradeState upgradeState;
111     private final DataTreeEventCallbackRegistrar eventCallbacks;
112
113     @Inject
114     public BaseVrfEntryHandler(final DataBroker dataBroker,
115                                final NexthopManager nexthopManager,
116                                final IMdsalApiManager mdsalManager,
117                                final FibUtil fibUtil,
118                                final UpgradeState upgradeState,
119                                final DataTreeEventCallbackRegistrar eventCallbacks) {
120         this.dataBroker = dataBroker;
121         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
122         this.nextHopManager = nexthopManager;
123         this.mdsalManager = mdsalManager;
124         this.fibUtil = fibUtil;
125         this.upgradeState = upgradeState;
126         this.eventCallbacks = eventCallbacks;
127     }
128
129     @Override
130     public void close() {
131         LOG.info("{} closed", getClass().getSimpleName());
132     }
133
134     protected FibUtil getFibUtil() {
135         return fibUtil;
136     }
137
138     protected NexthopManager getNextHopManager() {
139         return nextHopManager;
140     }
141
142     private void addAdjacencyResultToList(List<AdjacencyResult> adjacencyList, AdjacencyResult adjacencyResult) {
143         if (adjacencyResult != null && !adjacencyList.contains(adjacencyResult)) {
144             adjacencyList.add(adjacencyResult);
145         }
146     }
147
148     protected void deleteLocalAdjacency(final BigInteger dpId, final long vpnId, final String ipAddress,
149                               final String ipPrefixAddress) {
150         LOG.trace("deleteLocalAdjacency called with dpid {}, vpnId{}, primaryIpAddress {} currIpPrefix {}",
151                 dpId, vpnId, ipAddress, ipPrefixAddress);
152         try {
153             nextHopManager.removeLocalNextHop(dpId, vpnId, ipAddress, ipPrefixAddress);
154         } catch (NullPointerException e) {
155             // FIXME: NPEs should not be caught but rather their root cause should be eliminated
156             LOG.trace("Failed to remove nexthop", e);
157         }
158     }
159
160     @NonNull
161     protected List<AdjacencyResult> resolveAdjacency(final BigInteger remoteDpnId, final long vpnId,
162                                                      final VrfEntry vrfEntry, String rd) {
163         List<RoutePaths> routePaths = new ArrayList<>(vrfEntry.nonnullRoutePaths());
164         FibHelper.sortIpAddress(routePaths);
165         List<AdjacencyResult> adjacencyList = new ArrayList<>();
166         List<String> prefixIpList;
167         LOG.trace("resolveAdjacency called with remotedDpnId {}, vpnId{}, VrfEntry {}",
168                 remoteDpnId, vpnId, vrfEntry);
169         final Class<? extends TunnelTypeBase> tunnelType;
170         try {
171             if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
172                 tunnelType = TunnelTypeVxlan.class;
173                 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
174                 List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
175                         fibUtil.getVpnNameFromId(vpnId), usedRds, vrfEntry.getDestPrefix());
176                 if (vpnExtraRoutes.isEmpty()) {
177                     Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
178                     /* We don't want to provide an adjacencyList for
179                      * (1) an extra-route-prefix or,
180                      * (2) for a local route without prefix-to-interface.
181                      * Allow only self-imported routes in such cases */
182                     if (prefixInfo == null && FibHelper
183                             .isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
184                         LOG.debug("The prefix {} in rd {} for vpn {} does not have a valid extra-route or"
185                                 + " prefix-to-interface entry in the data-store", vrfEntry.getDestPrefix(), rd, vpnId);
186                         return adjacencyList;
187                     }
188                     prefixIpList = Collections.singletonList(vrfEntry.getDestPrefix());
189                 } else {
190                     List<String> prefixIpListLocal = new ArrayList<>();
191                     vpnExtraRoutes.stream().filter(route -> route.getNexthopIpList() != null).forEach(
192                         route -> route.getNexthopIpList().forEach(extraRouteIp -> {
193                             String ipPrefix;
194                             if (isIpv4Address(extraRouteIp)) {
195                                 ipPrefix = extraRouteIp + NwConstants.IPV4PREFIX;
196                             } else {
197                                 ipPrefix = extraRouteIp + NwConstants.IPV6PREFIX;
198                             }
199                             prefixIpListLocal.add(ipPrefix);
200                         }));
201                     prefixIpList = prefixIpListLocal;
202                 }
203             } else {
204                 prefixIpList = Collections.singletonList(vrfEntry.getDestPrefix());
205                 if (vrfEntry.getEncapType() == VrfEntry.EncapType.Mplsgre) {
206                     tunnelType = TunnelTypeMplsOverGre.class;
207                 } else {
208                     tunnelType = TunnelTypeVxlan.class;
209                 }
210             }
211
212             for (String prefixIp : prefixIpList) {
213                 if (routePaths == null || routePaths.isEmpty()) {
214                     LOG.trace("Processing Destination IP {} without NextHop IP", prefixIp);
215                     AdjacencyResult adjacencyResult = nextHopManager.getRemoteNextHopPointer(remoteDpnId, vpnId,
216                             prefixIp, null, tunnelType);
217                     addAdjacencyResultToList(adjacencyList, adjacencyResult);
218                     continue;
219                 }
220                 adjacencyList.addAll(routePaths.stream()
221                         .map(routePath -> {
222                             LOG.debug("NextHop IP for destination {} is {}", prefixIp,
223                                     routePath.getNexthopAddress());
224                             return nextHopManager.getRemoteNextHopPointer(remoteDpnId, vpnId,
225                                     prefixIp, routePath.getNexthopAddress(), tunnelType);
226                         })
227                         .filter(adjacencyResult -> adjacencyResult != null && !adjacencyList.contains(adjacencyResult))
228                         .distinct()
229                         .collect(toList()));
230             }
231         } catch (NullPointerException e) {
232             // FIXME: NPEs should not be caught but rather their root cause should be eliminated
233             LOG.trace("Failed to remove adjacency", e);
234         }
235         return adjacencyList;
236     }
237
238     protected void makeConnectedRoute(BigInteger dpId, long vpnId, VrfEntry vrfEntry, String rd,
239                                       @Nullable List<InstructionInfo> instructions, int addOrRemove,
240                                       WriteTransaction tx, @Nullable List<SubTransaction> subTxns) {
241         if (tx == null) {
242             ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
243                 newTx -> makeConnectedRoute(dpId, vpnId, vrfEntry, rd, instructions, addOrRemove, newTx, subTxns)),
244                 LOG, "Error making connected route");
245             return;
246         }
247
248         LOG.trace("makeConnectedRoute: vrfEntry {}", vrfEntry);
249         String[] values = vrfEntry.getDestPrefix().split("/");
250         String ipAddress = values[0];
251         int prefixLength = values.length == 1 ? 0 : Integer.parseInt(values[1]);
252         if (addOrRemove == NwConstants.ADD_FLOW) {
253             LOG.debug("Adding route to DPN {} for rd {} prefix {} ", dpId, rd, vrfEntry.getDestPrefix());
254         } else {
255             LOG.debug("Removing route from DPN {} for rd {} prefix {}", dpId, rd, vrfEntry.getDestPrefix());
256         }
257         InetAddress destPrefix;
258         try {
259             destPrefix = InetAddress.getByName(ipAddress);
260         } catch (UnknownHostException e) {
261             LOG.error("Failed to get destPrefix for prefix {} rd {} VpnId {} DPN {}",
262                     vrfEntry.getDestPrefix(), rd, vpnId, dpId, e);
263             return;
264         }
265
266         List<MatchInfo> matches = new ArrayList<>();
267
268         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
269
270         if (destPrefix instanceof Inet4Address) {
271             matches.add(MatchEthernetType.IPV4);
272             if (prefixLength != 0) {
273                 matches.add(new MatchIpv4Destination(destPrefix.getHostAddress(), Integer.toString(prefixLength)));
274             }
275         } else {
276             matches.add(MatchEthernetType.IPV6);
277             if (prefixLength != 0) {
278                 matches.add(new MatchIpv6Destination(destPrefix.getHostAddress() + "/" + prefixLength));
279             }
280         }
281
282         int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
283         String flowRef = FibUtil.getFlowRef(dpId, NwConstants.L3_FIB_TABLE, rd, priority, destPrefix);
284         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_FIB_TABLE, flowRef, priority,
285                 flowRef, 0, 0,
286                 COOKIE_VM_FIB_TABLE, matches, instructions);
287         Flow flow = flowEntity.getFlowBuilder().build();
288         String flowId = flowEntity.getFlowId();
289         FlowKey flowKey = new FlowKey(new FlowId(flowId));
290         Node nodeDpn = FibUtil.buildDpnNode(dpId);
291
292         InstanceIdentifier<Flow> flowInstanceId = InstanceIdentifier.builder(Nodes.class)
293                 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
294                 .child(Table.class, new TableKey(flow.getTableId())).child(Flow.class, flowKey).build();
295
296         if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP) {
297             SubTransaction subTransaction = new SubTransactionImpl();
298             if (addOrRemove == NwConstants.ADD_FLOW) {
299                 subTransaction.setInstanceIdentifier(flowInstanceId);
300                 subTransaction.setInstance(flow);
301                 subTransaction.setAction(SubTransaction.CREATE);
302             } else {
303                 subTransaction.setInstanceIdentifier(flowInstanceId);
304                 subTransaction.setAction(SubTransaction.DELETE);
305             }
306             subTxns.add(subTransaction);
307         }
308
309         if (addOrRemove == NwConstants.ADD_FLOW) {
310             tx.put(LogicalDatastoreType.CONFIGURATION, flowInstanceId, flow, true);
311         } else {
312             tx.delete(LogicalDatastoreType.CONFIGURATION, flowInstanceId);
313         }
314     }
315
316     protected void addRewriteDstMacAction(long vpnId, VrfEntry vrfEntry, @Nullable Prefixes prefixInfo,
317                                           List<ActionInfo> actionInfos) {
318         if (vrfEntry.getMac() != null) {
319             actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
320                     new MacAddress(vrfEntry.getMac())));
321             return;
322         }
323         if (prefixInfo == null) {
324             prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
325             //Checking PrefixtoInterface again as it is populated later in some cases
326             if (prefixInfo == null) {
327                 LOG.debug("No prefix info found for prefix {}", vrfEntry.getDestPrefix());
328                 return;
329             }
330         }
331         String ipPrefix = prefixInfo.getIpAddress();
332         String ifName = prefixInfo.getVpnInterfaceName();
333         if (ifName == null) {
334             LOG.debug("Failed to get VPN interface for prefix {}", ipPrefix);
335             return;
336         }
337         String vpnName = fibUtil.getVpnNameFromId(vpnId);
338         if (vpnName == null) {
339             LOG.debug("Failed to get VPN name for vpnId {}", vpnId);
340             return;
341         }
342         String macAddress = null;
343         if (vrfEntry.getParentVpnRd() != null) {
344             // Handling iRT/eRT use-case for missing destination mac address in Remote FIB flow
345             Optional<VpnInstanceOpDataEntry> vpnInstance = fibUtil.getVpnInstanceOpData(vrfEntry.getParentVpnRd());
346             if (vpnInstance.isPresent()) {
347                 macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnInstance.get().getVpnInstanceName(), ipPrefix);
348             } else {
349                 LOG.warn("VpnInstance missing for Parent Rd {} value for prefix {}", vrfEntry.getParentVpnRd(),
350                         vrfEntry.getDestPrefix());
351             }
352         } else {
353             macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, ipPrefix);
354         }
355         if (macAddress == null) {
356             LOG.warn("No MAC address found for VPN interface {} prefix {}", ifName, ipPrefix);
357             return;
358         }
359         actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(), new MacAddress(macAddress)));
360     }
361
362     protected void addTunnelInterfaceActions(AdjacencyResult adjacencyResult, long vpnId, VrfEntry vrfEntry,
363                                            List<ActionInfo> actionInfos, String rd) {
364         Class<? extends TunnelTypeBase> tunnelType =
365                 VpnExtraRouteHelper.getTunnelType(nextHopManager.getItmManager(), adjacencyResult.getInterfaceName());
366         if (tunnelType == null) {
367             LOG.debug("Tunnel type not found for vrfEntry {}", vrfEntry);
368             return;
369         }
370         // TODO - For now have added routePath into adjacencyResult so that we know for which
371         // routePath this result is built for. If this is not possible construct a map which does
372         // the same.
373         String nextHopIp = adjacencyResult.getNextHopIp();
374         java.util.Optional<Long> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
375         if (!optionalLabel.isPresent()) {
376             LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
377             return;
378         }
379         long label = optionalLabel.get();
380         BigInteger tunnelId = null;
381         Prefixes prefixInfo = null;
382         // FIXME vxlan vni bit set is not working properly with OVS.need to
383         // revisit
384         if (tunnelType.equals(TunnelTypeVxlan.class)) {
385             if (FibHelper.isControllerManagedNonSelfImportedRoute(RouteOrigin.value(vrfEntry.getOrigin()))) {
386                 prefixInfo = fibUtil.getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
387                 //For extra route, the prefixInfo is fetched from the primary adjacency
388                 if (prefixInfo == null) {
389                     prefixInfo = fibUtil.getPrefixToInterface(vpnId, adjacencyResult.getPrefix());
390                 }
391             } else {
392                 //Imported Route. Get Prefix Info from parent RD
393                 VpnInstanceOpDataEntry parentVpn =  fibUtil.getVpnInstance(vrfEntry.getParentVpnRd());
394                 prefixInfo = fibUtil.getPrefixToInterface(parentVpn.getVpnId(), adjacencyResult.getPrefix());
395             }
396             // Internet VPN VNI will be used as tun_id for NAT use-cases
397             if (Prefixes.PrefixCue.Nat.equals(prefixInfo.getPrefixCue())) {
398                 if (vrfEntry.getL3vni() != null && vrfEntry.getL3vni() != 0) {
399                     tunnelId = BigInteger.valueOf(vrfEntry.getL3vni());
400                 }
401             } else {
402                 if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
403                     tunnelId = BigInteger.valueOf(prefixInfo.getSegmentationId());
404                 } else {
405                     LOG.warn("Network is not of type VXLAN for prefix {}."
406                             + "Going with default Lport Tag.", prefixInfo.toString());
407                     tunnelId = BigInteger.valueOf(label);
408                 }
409             }
410         } else {
411             tunnelId = BigInteger.valueOf(label);
412         }
413         LOG.debug("adding set tunnel id action for label {}", label);
414         actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
415         addRewriteDstMacAction(vpnId, vrfEntry, prefixInfo, actionInfos);
416     }
417
418     @Nullable
419     private InstanceIdentifier<Interface> getFirstAbsentInterfaceStateIid(List<AdjacencyResult> adjacencyResults) {
420         InstanceIdentifier<Interface> res = null;
421         for (AdjacencyResult adjacencyResult : adjacencyResults) {
422             String interfaceName = adjacencyResult.getInterfaceName();
423             if (null == fibUtil.getInterfaceStateFromOperDS(interfaceName)) {
424                 res = FibUtil.buildStateInterfaceId(interfaceName);
425                 break;
426             }
427         }
428
429         return res;
430     }
431
432     public void programRemoteFib(final BigInteger remoteDpnId, final long vpnId,
433                                  final VrfEntry vrfEntry, WriteTransaction tx, String rd,
434                                  List<AdjacencyResult> adjacencyResults,
435                                  @Nullable List<SubTransaction> subTxns) {
436         if (upgradeState.isUpgradeInProgress()) {
437             InstanceIdentifier<Interface> absentInterfaceStateIid = getFirstAbsentInterfaceStateIid(adjacencyResults);
438             if (absentInterfaceStateIid != null) {
439                 LOG.info("programRemoteFib: interface state for {} not yet present, waiting...",
440                          absentInterfaceStateIid);
441                 eventCallbacks.onAddOrUpdate(LogicalDatastoreType.OPERATIONAL,
442                     absentInterfaceStateIid,
443                     (before, after) -> {
444                         LOG.info("programRemoteFib: waited for and got interface state {}", absentInterfaceStateIid);
445                         LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
446                             (wtx) -> programRemoteFib(remoteDpnId, vpnId, vrfEntry, wtx, rd, adjacencyResults, null)),
447                             LOG, "Failed to program remote FIB {}", absentInterfaceStateIid);
448                         return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
449                     },
450                     Duration.of(15, ChronoUnit.MINUTES),
451                     (iid) -> {
452                         LOG.error("programRemoteFib: timed out waiting for {}", absentInterfaceStateIid);
453                         LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
454                             (wtx) -> programRemoteFib(remoteDpnId, vpnId, vrfEntry, wtx, rd, adjacencyResults, null)),
455                             LOG, "Failed to program timed-out remote FIB {}", absentInterfaceStateIid);
456                     });
457                 return;
458             }
459         }
460
461         List<InstructionInfo> instructions = new ArrayList<>();
462         for (AdjacencyResult adjacencyResult : adjacencyResults) {
463             List<ActionInfo> actionInfos = new ArrayList<>();
464             String egressInterface = adjacencyResult.getInterfaceName();
465             if (FibUtil.isTunnelInterface(adjacencyResult)) {
466                 addTunnelInterfaceActions(adjacencyResult, vpnId, vrfEntry, actionInfos, rd);
467             } else {
468                 addRewriteDstMacAction(vpnId, vrfEntry, null, actionInfos);
469             }
470             List<ActionInfo> egressActions = nextHopManager.getEgressActionsForInterface(egressInterface,
471                     actionInfos.size(), true, vpnId, vrfEntry.getDestPrefix());
472             if (egressActions.isEmpty()) {
473                 LOG.error(
474                         "Failed to retrieve egress action for prefix {} route-paths {} interface {}. "
475                                 + "Aborting remote FIB entry creation.",
476                         vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(), egressInterface);
477                 return;
478             }
479             actionInfos.addAll(egressActions);
480             instructions.add(new InstructionApplyActions(actionInfos));
481         }
482         makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx, subTxns);
483     }
484
485     public boolean checkDpnDeleteFibEntry(VpnNexthop localNextHopInfo, BigInteger remoteDpnId, long vpnId,
486                                           VrfEntry vrfEntry, String rd,
487                                           WriteTransaction tx, @Nullable List<SubTransaction> subTxns) {
488         boolean isRemoteRoute = true;
489         if (localNextHopInfo != null) {
490             isRemoteRoute = !remoteDpnId.equals(localNextHopInfo.getDpnId());
491         }
492         if (isRemoteRoute) {
493             makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, null, NwConstants.DEL_FLOW, tx, subTxns);
494             LOG.debug("Successfully delete FIB entry: vrfEntry={}, vpnId={}", vrfEntry.getDestPrefix(), vpnId);
495             return true;
496         } else {
497             LOG.debug("Did not delete FIB entry: rd={}, vrfEntry={}, as it is local to dpnId={}",
498                     rd, vrfEntry.getDestPrefix(), remoteDpnId);
499             return false;
500         }
501     }
502
503     public void deleteRemoteRoute(@Nullable final BigInteger localDpnId, final BigInteger remoteDpnId,
504                                   final long vpnId, final VrfTablesKey vrfTableKey,
505                                   final VrfEntry vrfEntry, Optional<Routes> extraRouteOptional,
506                                   @Nullable WriteTransaction tx) {
507         if (tx == null) {
508             ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
509                 newTx -> deleteRemoteRoute(localDpnId, remoteDpnId, vpnId, vrfTableKey, vrfEntry,
510                         extraRouteOptional, newTx)), LOG, "Error deleting remote route");
511             return;
512         }
513
514         LOG.debug("deleting remote route: prefix={}, vpnId={} localDpnId {} remoteDpnId {}",
515                 vrfEntry.getDestPrefix(), vpnId, localDpnId, remoteDpnId);
516         String rd = vrfTableKey.getRouteDistinguisher();
517
518         if (localDpnId != null && !BigInteger.ZERO.equals(localDpnId)) {
519             // localDpnId is not known when clean up happens for last vm for a vpn on a dpn
520             if (extraRouteOptional.isPresent()) {
521                 nextHopManager.deleteLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
522             }
523             makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, null, NwConstants.DEL_FLOW, tx, null);
524             LOG.debug("Successfully delete FIB entry: vrfEntry={}, vpnId={}", vrfEntry.getDestPrefix(), vpnId);
525             return;
526         }
527
528         // below two reads are kept as is, until best way is found to identify dpnID
529         VpnNexthop localNextHopInfo = nextHopManager.getVpnNexthop(vpnId, vrfEntry.getDestPrefix());
530         if (extraRouteOptional.isPresent()) {
531             nextHopManager.deleteLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
532         } else {
533             checkDpnDeleteFibEntry(localNextHopInfo, remoteDpnId, vpnId, vrfEntry, rd, tx, null);
534         }
535     }
536
537     public static InstanceIdentifier<Routes> getVpnToExtrarouteIdentifier(String vpnName, String vrfId,
538                                                                     String ipPrefix) {
539         return InstanceIdentifier.builder(VpnToExtraroutes.class)
540                 .child(Vpn.class, new VpnKey(vpnName)).child(ExtraRoutes.class,
541                         new ExtraRoutesKey(vrfId)).child(Routes.class, new RoutesKey(ipPrefix)).build();
542     }
543
544     @Nullable
545     public Routes getVpnToExtraroute(Long vpnId, String vpnRd, String destPrefix) {
546         String optVpnName = fibUtil.getVpnNameFromId(vpnId);
547         if (optVpnName != null) {
548             InstanceIdentifier<Routes> vpnExtraRoutesId = getVpnToExtrarouteIdentifier(
549                     optVpnName, vpnRd, destPrefix);
550             return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, vpnExtraRoutesId).orNull();
551         }
552         return null;
553     }
554
555     public FlowEntity buildL3vpnGatewayFlow(BigInteger dpId, String gwMacAddress, long vpnId) {
556         List<MatchInfo> mkMatches = new ArrayList<>();
557         mkMatches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
558         mkMatches.add(new MatchEthernetDestination(new MacAddress(gwMacAddress)));
559         List<InstructionInfo> mkInstructions = new ArrayList<>();
560         mkInstructions.add(new InstructionGotoTable(NwConstants.L3_FIB_TABLE));
561         String flowId = FibUtil.getL3VpnGatewayFlowRef(NwConstants.L3_GW_MAC_TABLE, dpId, vpnId, gwMacAddress);
562         return MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_GW_MAC_TABLE,
563                 flowId, 20, flowId, 0, 0, NwConstants.COOKIE_L3_GW_MAC_TABLE, mkMatches, mkInstructions);
564     }
565
566     public void installPingResponderFlowEntry(BigInteger dpnId, long vpnId, String routerInternalIp,
567                                               MacAddress routerMac, long label, int addOrRemove) {
568
569         List<MatchInfo> matches = new ArrayList<>();
570         matches.add(MatchIpProtocol.ICMP);
571         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
572         matches.add(new MatchIcmpv4((short) 8, (short) 0));
573         matches.add(MatchEthernetType.IPV4);
574         matches.add(new MatchIpv4Destination(routerInternalIp, "32"));
575
576         List<ActionInfo> actionsInfos = new ArrayList<>();
577
578         // Set Eth Src and Eth Dst
579         actionsInfos.add(new ActionMoveSourceDestinationEth());
580         actionsInfos.add(new ActionSetFieldEthernetSource(routerMac));
581
582         // Move Ip Src to Ip Dst
583         actionsInfos.add(new ActionMoveSourceDestinationIp());
584         actionsInfos.add(new ActionSetSourceIp(routerInternalIp, "32"));
585
586         // Set the ICMP type to 0 (echo reply)
587         actionsInfos.add(new ActionSetIcmpType((short) 0));
588
589         actionsInfos.add(new ActionNxLoadInPort(BigInteger.ZERO));
590
591         actionsInfos.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
592
593         List<InstructionInfo> instructions = new ArrayList<>();
594
595         instructions.add(new InstructionApplyActions(actionsInfos));
596
597         int priority = FibConstants.DEFAULT_FIB_FLOW_PRIORITY + FibConstants.DEFAULT_PREFIX_LENGTH;
598         String flowRef = FibUtil.getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, label, priority);
599
600         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_FIB_TABLE, flowRef, priority, flowRef,
601                 0, 0, NwConstants.COOKIE_VM_FIB_TABLE, matches, instructions);
602
603         if (addOrRemove == NwConstants.ADD_FLOW) {
604             mdsalManager.syncInstallFlow(flowEntity);
605         } else {
606             mdsalManager.syncRemoveFlow(flowEntity);
607         }
608     }
609 }