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