MRI version bumpup for Aluminium
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / NaptSwitchHA.java
1 /*
2  * Copyright © 2016, 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.natservice.internal;
9
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.Collection;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Map.Entry;
17 import java.util.Optional;
18 import java.util.Set;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.Future;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.eclipse.jdt.annotation.NonNull;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
26 import org.opendaylight.genius.infra.Datastore.Configuration;
27 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
28 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
29 import org.opendaylight.genius.mdsalutil.ActionInfo;
30 import org.opendaylight.genius.mdsalutil.BucketInfo;
31 import org.opendaylight.genius.mdsalutil.FlowEntity;
32 import org.opendaylight.genius.mdsalutil.GroupEntity;
33 import org.opendaylight.genius.mdsalutil.InstructionInfo;
34 import org.opendaylight.genius.mdsalutil.MDSALUtil;
35 import org.opendaylight.genius.mdsalutil.MatchInfo;
36 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
37 import org.opendaylight.genius.mdsalutil.NwConstants;
38 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
39 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
40 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
41 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
42 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
43 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
44 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
45 import org.opendaylight.mdsal.binding.api.DataBroker;
46 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
47 import org.opendaylight.netvirt.elanmanager.api.IElanService;
48 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
49 import org.opendaylight.netvirt.natservice.api.SnatServiceManager;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig.NatMode;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProtocolTypes;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksKey;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.IpPortMapping;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolTypeKey;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMapKey;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchBuilder;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
82 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
83 import org.opendaylight.yangtools.yang.common.RpcResult;
84 import org.opendaylight.yangtools.yang.common.Uint32;
85 import org.opendaylight.yangtools.yang.common.Uint64;
86 import org.slf4j.Logger;
87 import org.slf4j.LoggerFactory;
88
89 @Singleton
90 public class NaptSwitchHA {
91     private static final Logger LOG = LoggerFactory.getLogger(NaptSwitchHA.class);
92     private final DataBroker dataBroker;
93     private final IMdsalApiManager mdsalManager;
94     private final ItmRpcService itmManager;
95     private final OdlInterfaceRpcService odlInterfaceRpcService;
96     private final IdManagerService idManager;
97     private final NAPTSwitchSelector naptSwitchSelector;
98     private final ExternalRoutersListener externalRouterListener;
99     private final NaptEventHandler naptEventHandler;
100     private final IFibManager fibManager;
101     private final IElanService elanManager;
102     private final EvpnNaptSwitchHA evpnNaptSwitchHA;
103     private final SnatServiceManager natServiceManager;
104     private final NatMode natMode;
105     private final IInterfaceManager interfaceManager;
106     private final NatOverVxlanUtil natOverVxlanUtil;
107
108     private volatile Collection<String> externalIpsCache;
109
110     @Inject
111     public NaptSwitchHA(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
112                         final ExternalRoutersListener externalRouterListener,
113                         final ItmRpcService itmManager,
114                         final OdlInterfaceRpcService odlInterfaceRpcService,
115                         final IdManagerService idManager,
116                         final NAPTSwitchSelector naptSwitchSelector,
117                         final IFibManager fibManager,
118                         final EvpnNaptSwitchHA evpnNaptSwitchHA,
119                         final IElanService elanManager,
120                         final SnatServiceManager natServiceManager,
121                         final NatserviceConfig config,
122                         final NaptEventHandler naptEventHandler,
123                         final IInterfaceManager interfaceManager,
124                         final NatOverVxlanUtil natOverVxlanUtil) {
125         this.dataBroker = dataBroker;
126         this.mdsalManager = mdsalManager;
127         this.externalRouterListener = externalRouterListener;
128         this.itmManager = itmManager;
129         this.odlInterfaceRpcService = odlInterfaceRpcService;
130         this.idManager = idManager;
131         this.naptSwitchSelector = naptSwitchSelector;
132         this.naptEventHandler = naptEventHandler;
133         this.fibManager = fibManager;
134         this.evpnNaptSwitchHA = evpnNaptSwitchHA;
135         this.elanManager = elanManager;
136         this.natServiceManager = natServiceManager;
137         this.interfaceManager = interfaceManager;
138         if (config != null) {
139             this.natMode = config.getNatMode();
140         } else {
141             this.natMode = NatMode.Controller;
142         }
143         this.natOverVxlanUtil = natOverVxlanUtil;
144     }
145
146     protected void removeSnatFlowsInOldNaptSwitch(Routers extRouter, Uint32 routerId, Uint64 naptSwitch,
147                                                   @Nullable Map<String, Uint32> externalIpmap, String externalVpnName,
148                                                   TypedReadWriteTransaction<Configuration> confTx)
149             throws ExecutionException, InterruptedException {
150
151         //remove SNAT flows in old NAPT SWITCH
152         String routerName = extRouter.getRouterName();
153         Uuid networkId = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
154         String vpnName = getExtNetworkVpnName(routerName, networkId);
155         if (vpnName == null) {
156             LOG.error("removeSnatFlowsInOldNaptSwitch : Vpn is not associated to externalN/w of router {}",
157                 routerName);
158             return;
159         }
160         ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName, networkId);
161         if (extNwProvType == null) {
162             LOG.error("removeSnatFlowsInOldNaptSwitch : Unable to retrieve the External Network Provider Type "
163                 + "for Router {}", routerName);
164             return;
165         }
166         if (extNwProvType == ProviderTypes.VXLAN) {
167             evpnNaptSwitchHA.evpnRemoveSnatFlowsInOldNaptSwitch(routerName, routerId, vpnName, naptSwitch, confTx);
168         } else {
169             //Remove the Terminating Service table entry which forwards the packet to Outbound NAPT Table
170             Uint64 tunnelId = NatUtil.getTunnelIdForNonNaptToNaptFlow(dataBroker, natOverVxlanUtil, elanManager,
171                     idManager, routerId, routerName);
172             String tsFlowRef = externalRouterListener.getFlowRefTs(naptSwitch, NwConstants.INTERNAL_TUNNEL_TABLE,
173                 Uint32.valueOf(tunnelId));
174             FlowEntity tsNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NwConstants.INTERNAL_TUNNEL_TABLE,
175                 tsFlowRef);
176
177             LOG.info("removeSnatFlowsInOldNaptSwitch : Remove the flow in table {} for the old napt switch "
178                 + "with the DPN ID {} and router ID {}", NwConstants.INTERNAL_TUNNEL_TABLE, naptSwitch, routerId);
179             mdsalManager.removeFlow(confTx, tsNatFlowEntity);
180         }
181         if (NatUtil.isOpenStackVniSemanticsEnforcedForGreAndVxlan(elanManager, extNwProvType)) {
182             //Remove the flow table 25->44 If there is no FIP Match on table 25 (PDNAT_TABLE)
183             NatUtil.removePreDnatToSnatTableEntry(confTx, mdsalManager, naptSwitch);
184         }
185         //Remove the Outbound flow entry which forwards the packet to Outbound NAPT Table
186         LOG.info("Remove the flow in table {} for the old napt switch with the DPN ID {} and router ID {}",
187             NwConstants.OUTBOUND_NAPT_TABLE, naptSwitch, routerId);
188
189         String outboundTcpNatFlowRef = externalRouterListener.getFlowRefOutbound(naptSwitch,
190             NwConstants.OUTBOUND_NAPT_TABLE, routerId, NwConstants.IP_PROT_TCP);
191         FlowEntity outboundTcpNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch,
192             NwConstants.OUTBOUND_NAPT_TABLE, outboundTcpNatFlowRef);
193         mdsalManager.removeFlow(confTx, outboundTcpNatFlowEntity);
194
195         String outboundUdpNatFlowRef = externalRouterListener.getFlowRefOutbound(naptSwitch,
196             NwConstants.OUTBOUND_NAPT_TABLE, routerId, NwConstants.IP_PROT_UDP);
197         FlowEntity outboundUdpNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch,
198             NwConstants.OUTBOUND_NAPT_TABLE, outboundUdpNatFlowRef);
199         mdsalManager.removeFlow(confTx, outboundUdpNatFlowEntity);
200
201         String icmpDropFlowRef = externalRouterListener.getFlowRefOutbound(naptSwitch,
202             NwConstants.OUTBOUND_NAPT_TABLE, routerId, NwConstants.IP_PROT_ICMP);
203         FlowEntity icmpDropFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NwConstants.OUTBOUND_NAPT_TABLE,
204             icmpDropFlowRef);
205         mdsalManager.removeFlow(confTx, icmpDropFlowEntity);
206
207         //Remove the NAPT PFIB TABLE (47->21) which forwards the incoming packet to FIB Table matching on the
208         // External Subnet Vpn Id.
209         Collection<Uuid> externalSubnetIdsForRouter = NatUtil.getExternalSubnetIdsForRouter(dataBroker,
210             routerName);
211         for (Uuid externalSubnetId : externalSubnetIdsForRouter) {
212             Uint32 subnetVpnId = NatUtil.getVpnId(dataBroker, externalSubnetId.getValue());
213             if (subnetVpnId != NatConstants.INVALID_ID && !NatUtil.checkForRoutersWithSameExtSubnetAndNaptSwitch(
214                 dataBroker, externalSubnetId, routerName, naptSwitch)) {
215                 String natPfibSubnetFlowRef = externalRouterListener.getFlowRefTs(naptSwitch,
216                     NwConstants.NAPT_PFIB_TABLE, subnetVpnId);
217                 FlowEntity natPfibFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NwConstants.NAPT_PFIB_TABLE,
218                     natPfibSubnetFlowRef);
219                 mdsalManager.removeFlow(confTx, natPfibFlowEntity);
220                 LOG.debug("removeSnatFlowsInOldNaptSwitch : Removed the flow in table {} with external subnet "
221                           + "Vpn Id {} as metadata on Napt Switch {}", NwConstants.NAPT_PFIB_TABLE,
222                     subnetVpnId, naptSwitch);
223             }
224         }
225
226         // Remove the NAPT_PFIB_TABLE(47) flow entry forwards the packet to Fib Table for inbound traffic
227         // matching on the router ID.
228         String naptPFibflowRef =
229             externalRouterListener.getFlowRefTs(naptSwitch, NwConstants.NAPT_PFIB_TABLE, routerId);
230         FlowEntity naptPFibFlowEntity =
231             NatUtil.buildFlowEntity(naptSwitch, NwConstants.NAPT_PFIB_TABLE, naptPFibflowRef);
232         LOG.info("removeSnatFlowsInOldNaptSwitch : Remove the flow in table {} for the old napt switch "
233             + "with the DPN ID {} and router ID {}", NwConstants.NAPT_PFIB_TABLE, naptSwitch, routerId);
234         mdsalManager.removeFlow(confTx, naptPFibFlowEntity);
235
236         // Remove the NAPT_PFIB_TABLE(47) flow entry forwards the packet to Fib Table for outbound traffic
237         // matching on the vpn ID.
238         boolean switchSharedByRouters = false;
239         Uuid extNetworkId = extRouter.getNetworkId();
240         if (extNetworkId != null && !NatUtil.checkForRoutersWithSameExtNetAndNaptSwitch(
241             dataBroker, extNetworkId, routerName, naptSwitch)) {
242             List<String> routerNamesAssociated = getRouterIdsForExtNetwork(extNetworkId);
243             for (String routerNameAssociated : routerNamesAssociated) {
244                 if (!routerNameAssociated.equals(routerName)) {
245                     Uint32 routerIdAssociated = NatUtil.getVpnId(dataBroker, routerNameAssociated);
246                     Uint64 naptDpn = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerNameAssociated);
247                     if (naptDpn != null && naptDpn.equals(naptSwitch)) {
248                         LOG.debug("removeSnatFlowsInOldNaptSwitch : Napt switch {} is also acting as primary "
249                             + "for router {}", naptSwitch, routerIdAssociated);
250                         switchSharedByRouters = true;
251                         break;
252                     }
253                 }
254             }
255             if (!switchSharedByRouters) {
256                 Uint32 vpnId = NatUtil.getVpnId(dataBroker,externalVpnName);
257                 if (vpnId != NatConstants.INVALID_ID) {
258                     String naptFibflowRef =
259                         externalRouterListener.getFlowRefTs(naptSwitch, NwConstants.NAPT_PFIB_TABLE, vpnId);
260                     FlowEntity naptFibFlowEntity =
261                         NatUtil.buildFlowEntity(naptSwitch, NwConstants.NAPT_PFIB_TABLE, naptFibflowRef);
262                     LOG.info("removeSnatFlowsInOldNaptSwitch : Remove the flow in table {} for the old napt switch"
263                         + " with the DPN ID {} and vpnId {}", NwConstants.NAPT_PFIB_TABLE, naptSwitch, vpnId);
264                     mdsalManager.removeFlow(confTx, naptFibFlowEntity);
265                 } else {
266                     LOG.error("removeSnatFlowsInOldNaptSwitch : Invalid vpnId retrieved for routerId {}",
267                         routerId);
268                     return;
269                 }
270             }
271         }
272
273         //Remove Fib entries,tables 20->44 ,36-> 44
274         String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
275         if (externalIpmap != null && !externalIpmap.isEmpty()) {
276             for (Entry<String, Uint32> entry : externalIpmap.entrySet()) {
277                 String externalIp = entry.getKey();
278                 Uint32 label = entry.getValue();
279                 externalRouterListener.delFibTsAndReverseTraffic(naptSwitch, routerName, routerId, externalIp, vpnName,
280                     extNetworkId, label, gwMacAddress, true, confTx);
281                 LOG.debug("removeSnatFlowsInOldNaptSwitch : Successfully removed fib entries in old naptswitch {} "
282                     + "for router {} and externalIps {} label {}", naptSwitch, routerId, externalIp, label);
283             }
284         } else {
285             List<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker, routerName);
286             if (extNetworkId != null) {
287                 externalRouterListener.clearFibTsAndReverseTraffic(naptSwitch, routerId, extNetworkId,
288                     externalIps, null, gwMacAddress, confTx);
289                 LOG.debug(
290                     "removeSnatFlowsInOldNaptSwitch : Successfully removed fib entries in old naptswitch {} for "
291                         + "router {} with networkId {} and externalIps {}", naptSwitch, routerId, extNetworkId,
292                     externalIps);
293             } else {
294                 LOG.debug("removeSnatFlowsInOldNaptSwitch : External network not associated to router {}",
295                     routerId);
296             }
297             externalRouterListener.removeNaptFibExternalOutputFlows(routerId, naptSwitch, extNetworkId,
298                 externalIps, confTx);
299         }
300
301         //For the router ID get the internal IP , internal port and the corresponding external IP and external Port.
302         IpPortMapping ipPortMapping = NatUtil.getIportMapping(dataBroker, routerId);
303         if (ipPortMapping == null || ipPortMapping.getIntextIpProtocolType() == null
304             || ipPortMapping.getIntextIpProtocolType().isEmpty()) {
305             LOG.warn("removeSnatFlowsInOldNaptSwitch : No Internal Ip Port mapping associated to router {}, "
306                 + "no flows need to be removed in oldNaptSwitch {}", routerId, naptSwitch);
307             return;
308         }
309         Uint64 cookieSnatFlow = NatUtil.getCookieNaptFlow(routerId);
310         Map<IntextIpProtocolTypeKey, IntextIpProtocolType> keyIntextIpProtocolTypeMap
311                 = ipPortMapping.getIntextIpProtocolType();
312         for (IntextIpProtocolType intextIpProtocolType : keyIntextIpProtocolTypeMap.values()) {
313             if (intextIpProtocolType.getIpPortMap() == null || intextIpProtocolType.getIpPortMap().isEmpty()) {
314                 LOG.debug("removeSnatFlowsInOldNaptSwitch : No {} session associated to router {},"
315                         + "no flows need to be removed in oldNaptSwitch {}",
316                     intextIpProtocolType.getProtocol(), routerId, naptSwitch);
317                 continue;
318             }
319             String protocol = intextIpProtocolType.getProtocol().name();
320             Map<IpPortMapKey, IpPortMap> keyIpPortMapMap = intextIpProtocolType.getIpPortMap();
321             for (IpPortMap ipPortMap : keyIpPortMapMap.values()) {
322                 String ipPortInternal = ipPortMap.getIpPortInternal();
323                 String[] ipPortParts = ipPortInternal.split(":");
324                 if (ipPortParts.length != 2) {
325                     LOG.error("removeSnatFlowsInOldNaptSwitch : Unable to retrieve the Internal IP and port");
326                     continue;
327                 }
328                 String internalIp = ipPortParts[0];
329                 String internalPort = ipPortParts[1];
330
331                 //Build and remove flow in outbound NAPT table
332                 String switchFlowRef =
333                     NatUtil.getNaptFlowRef(naptSwitch, NwConstants.OUTBOUND_NAPT_TABLE, String.valueOf(routerId),
334                         internalIp, Integer.parseInt(internalPort), protocol);
335                 FlowEntity outboundNaptFlowEntity =
336                     NatUtil.buildFlowEntity(naptSwitch, NwConstants.OUTBOUND_NAPT_TABLE,
337                         cookieSnatFlow, switchFlowRef);
338
339                 LOG.info("removeSnatFlowsInOldNaptSwitch : Remove the flow in table {} for old napt switch "
340                     + "with the DPN ID {} and router ID {}", NwConstants.OUTBOUND_NAPT_TABLE, naptSwitch, routerId);
341                 mdsalManager.removeFlow(confTx, outboundNaptFlowEntity);
342
343                 //Build and remove flow in  inbound NAPT table
344                 switchFlowRef =
345                     NatUtil.getNaptFlowRef(naptSwitch, NwConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId),
346                         internalIp, Integer.parseInt(internalPort), protocol);
347                 FlowEntity inboundNaptFlowEntity =
348                     NatUtil.buildFlowEntity(naptSwitch, NwConstants.INBOUND_NAPT_TABLE,
349                         cookieSnatFlow, switchFlowRef);
350
351                 LOG.info(
352                     "removeSnatFlowsInOldNaptSwitch : Remove the flow in table {} for old napt switch with the "
353                         + "DPN ID {} and router ID {}", NwConstants.INBOUND_NAPT_TABLE, naptSwitch, routerId);
354                 mdsalManager.removeFlow(confTx, inboundNaptFlowEntity);
355             }
356         }
357     }
358
359     @NonNull
360     private List<String> getRouterIdsForExtNetwork(Uuid extNetworkId) {
361         List<String> routerUuidsAsString = new ArrayList<>();
362         InstanceIdentifier<Networks> extNetwork = InstanceIdentifier.builder(ExternalNetworks.class)
363             .child(Networks.class, new NetworksKey(extNetworkId)).build();
364         Optional<Networks> extNetworkData =
365                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
366                         LogicalDatastoreType.CONFIGURATION, extNetwork);
367         if (extNetworkData.isPresent()) {
368             List<Uuid> routerUuids = extNetworkData.get().getRouterIds();
369             if (routerUuids != null) {
370                 for (Uuid routerUuid : routerUuids) {
371                     routerUuidsAsString.add(routerUuid.getValue());
372                 }
373             }
374         }
375         return routerUuidsAsString;
376     }
377
378     public boolean isNaptSwitchDown(Routers extRouter, Uint32 routerId, Uint64 dpnId, Uint64 naptSwitch,
379                                     Uint32 routerVpnId, Collection<String> externalIpCache,
380                                     TypedReadWriteTransaction<Configuration> confTx)
381             throws ExecutionException, InterruptedException {
382         return isNaptSwitchDown(extRouter, routerId, dpnId, naptSwitch, routerVpnId, externalIpCache, true,
383                 confTx);
384     }
385
386     // TODO Clean up the exception handling
387     @SuppressWarnings("checkstyle:IllegalCatch")
388     public boolean isNaptSwitchDown(Routers extRouter, Uint32 routerId, Uint64 dpnId, Uint64 naptSwitch,
389                                     Uint32 routerVpnId, Collection<String> externalIpCache, boolean isClearBgpRts,
390                                     TypedReadWriteTransaction<Configuration> confTx)
391             throws ExecutionException, InterruptedException {
392         externalIpsCache = externalIpCache;
393         String routerName = extRouter.getRouterName();
394         if (!naptSwitch.equals(dpnId)) {
395             LOG.debug("isNaptSwitchDown : DpnId {} is not a naptSwitch {} for Router {}",
396                     dpnId, naptSwitch, routerName);
397             return false;
398         }
399         LOG.debug("NaptSwitch {} is down for Router {}", naptSwitch, routerName);
400         if (routerId == NatConstants.INVALID_ID) {
401             LOG.error("isNaptSwitchDown : Invalid routerId returned for routerName {}", routerName);
402             return true;
403         }
404         Uuid networkId = extRouter.getNetworkId();
405         String vpnName = getExtNetworkVpnName(routerName, networkId);
406         //elect a new NaptSwitch
407         naptSwitch = naptSwitchSelector.selectNewNAPTSwitch(routerName, Arrays.asList(naptSwitch));
408         if (natMode == NatMode.Conntrack) {
409             Routers extRouters = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
410             natServiceManager.notify(confTx, extRouters, null, dpnId, dpnId,
411                     SnatServiceManager.Action.CNT_ROUTER_ALL_SWITCH_DISBL);
412             if (extRouters.isEnableSnat()) {
413                 natServiceManager.notify(confTx, extRouters, null, dpnId, dpnId,
414                         SnatServiceManager.Action.SNAT_ALL_SWITCH_DISBL);
415             }
416             natServiceManager.notify(confTx, extRouters, null, naptSwitch, naptSwitch,
417                     SnatServiceManager.Action.CNT_ROUTER_ALL_SWITCH_ENBL);
418             if (extRouters.isEnableSnat()) {
419                 natServiceManager.notify(confTx, extRouters, null, naptSwitch, naptSwitch,
420                         SnatServiceManager.Action.SNAT_ALL_SWITCH_ENBL);
421             }
422         } else {
423             if (naptSwitch.equals(Uint64.ZERO)) {
424                 LOG.warn("isNaptSwitchDown : No napt switch is elected since all the switches for router {}"
425                         + " are down. SNAT IS NOT SUPPORTED FOR ROUTER {}", routerName, routerName);
426                 boolean naptUpdatedStatus = updateNaptSwitch(routerName, naptSwitch);
427                 if (!naptUpdatedStatus) {
428                     LOG.debug("isNaptSwitchDown : Failed to update naptSwitch {} for router {} in ds",
429                             naptSwitch, routerName);
430                 }
431                 //clearBgpRoutes
432                 if (externalIpsCache != null) {
433                     if (vpnName != null) {
434                         //List<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
435                         //if (externalIps != null) {
436                         if (isClearBgpRts) {
437                             LOG.debug("isNaptSwitchDown : Clearing both FIB entries and the BGP routes");
438                             for (String externalIp : externalIpsCache) {
439                                 externalRouterListener.clearBgpRoutes(externalIp, vpnName);
440                             }
441                         } else {
442                             LOG.debug("isNaptSwitchDown : Clearing the FIB entries but not the BGP routes");
443                             String rd = NatUtil.getVpnRd(dataBroker, vpnName);
444                             for (String externalIp : externalIpsCache) {
445                                 LOG.debug("isNaptSwitchDown : Removing Fib entry rd {} prefix {}", rd, externalIp);
446                                 fibManager.removeFibEntry(rd, externalIp, null, null);
447                             }
448                         }
449                     } else {
450                         LOG.debug("isNaptSwitchDown : vpn is not associated to extn/w for router {}", routerName);
451                     }
452                 } else {
453                     LOG.debug("isNaptSwitchDown : No ExternalIps found for subnets under router {}, "
454                             + "no bgp routes need to be cleared", routerName);
455                 }
456                 return true;
457             }
458             //checking elected switch health status
459             if (!NatUtil.getSwitchStatus(dataBroker, naptSwitch)) {
460                 LOG.error("isNaptSwitchDown : Newly elected Napt switch {} for router {} is down",
461                         naptSwitch, routerName);
462                 return true;
463             }
464             LOG.debug("isNaptSwitchDown : New NaptSwitch {} is up for Router {} and can proceed for flow installation",
465                     naptSwitch, routerName);
466             //update napt model for new napt switch
467             boolean naptUpdated = updateNaptSwitch(routerName, naptSwitch);
468             if (naptUpdated) {
469                 //update group of ordinary switch point to naptSwitch tunnel port
470                 updateNaptSwitchBucketStatus(routerName, routerId, naptSwitch);
471             } else {
472                 LOG.error("isNaptSwitchDown : Failed to update naptSwitch model for newNaptSwitch {} for router {}",
473                         naptSwitch, routerName);
474             }
475
476             //update table26 forward packets to table46(outbound napt table)
477             FlowEntity flowEntity =
478                     buildSnatFlowEntityForNaptSwitch(naptSwitch, routerName, routerVpnId, NatConstants.ADD_FLOW);
479             if (flowEntity == null) {
480                 LOG.error("isNaptSwitchDown : Failed to populate flowentity for router {} in naptSwitch {}",
481                         routerName, naptSwitch);
482             } else {
483                 LOG.debug("isNaptSwitchDown : Successfully installed flow in naptSwitch {} for router {}",
484                         naptSwitch, routerName);
485                 mdsalManager.addFlow(confTx, flowEntity);
486             }
487
488             installSnatFlows(routerName, routerId, naptSwitch, routerVpnId, networkId, vpnName, confTx);
489
490             boolean flowInstalledStatus = handleNatFlowsInNewNaptSwitch(routerName, routerId, dpnId, naptSwitch,
491                     routerVpnId, networkId);
492             if (flowInstalledStatus) {
493                 LOG.debug("isNaptSwitchDown :Installed all active session flows in newNaptSwitch {} for routerName {}",
494                         naptSwitch, routerName);
495             } else {
496                 LOG.error("isNaptSwitchDown : Failed to install flows in newNaptSwitch {} for routerId {}",
497                         naptSwitch, routerId);
498             }
499
500             //remove group in new naptswitch, coz this switch acted previously as ordinary switch
501             Uint32 groupId = NatUtil.getUniqueId(idManager, NatConstants.SNAT_IDPOOL_NAME,
502                 NatUtil.getGroupIdKey(routerName));
503             if (groupId != NatConstants.INVALID_ID) {
504                 try {
505                     LOG.info("isNaptSwitchDown : Removing NAPT Group in new naptSwitch {}",
506                         naptSwitch);
507                     mdsalManager.removeGroup(confTx, naptSwitch, groupId.longValue());
508                 } catch (Exception ex) {
509                     LOG.error("isNaptSwitchDown : Failed to remove group in new naptSwitch {}",
510                         naptSwitch, ex);
511                 }
512             } else {
513                 LOG.error("NAT Service : Unable to obtain groupId for router:{}", routerName);
514             }
515         }
516         return true;
517     }
518
519     @Nullable
520     private String getExtNetworkVpnName(String routerName, Uuid networkId) {
521         if (networkId == null) {
522             LOG.error("getExtNetworkVpnName : networkId is null for the router ID {}", routerName);
523         } else {
524             final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId);
525             if (vpnName != null) {
526                 LOG.debug("getExtNetworkVpnName : retrieved vpn name {} associated with ext nw {} in router {}",
527                     vpnName, networkId, routerName);
528                 return vpnName;
529             } else {
530                 LOG.error("getExtNetworkVpnName : No VPN associated with ext nw {} belonging to routerId {}",
531                     networkId, routerName);
532             }
533         }
534         LOG.error("getExtNetworkVpnName : External Network VPN not found for router : {}", routerName);
535         return null;
536     }
537
538     public void updateNaptSwitchBucketStatus(String routerName, Uint32 routerId, Uint64 naptSwitch) {
539         LOG.debug("updateNaptSwitchBucketStatus : called");
540
541         List<Uint64> dpnList = naptSwitchSelector.getDpnsForVpn(routerName);
542         //List<BigInteger> dpnList = getDpnListForRouter(routerName);
543         if (dpnList.isEmpty()) {
544             LOG.warn("updateNaptSwitchBucketStatus : No switches found for router {}", routerName);
545             return;
546         }
547         for (Uint64 dpn : dpnList) {
548             if (!dpn.equals(naptSwitch)) {
549                 LOG.debug("updateNaptSwitchBucketStatus : Updating SNAT_TABLE missentry for DpnId {} "
550                         + "which is not naptSwitch for router {}", dpn, routerName);
551                 List<BucketInfo> bucketInfoList = handleGroupInNeighborSwitches(dpn, routerName, routerId, naptSwitch);
552                 if (bucketInfoList.isEmpty()) {
553                     LOG.error("Failed to populate bucketInfo for non-napt switch {} whose naptSwitch:{} for router:{}",
554                         dpn,naptSwitch,routerName);
555                     continue;
556                 }
557                 modifySnatGroupEntry(dpn, bucketInfoList, routerName);
558                 externalRouterListener.installSnatMissEntry(dpn, bucketInfoList, routerName, routerId);
559             }
560         }
561     }
562
563     // TODO Clean up the exception handling
564     @SuppressWarnings("checkstyle:IllegalCatch")
565     private boolean handleNatFlowsInNewNaptSwitch(String routerName, Uint32 routerId, Uint64 oldNaptSwitch,
566                                                     Uint64 newNaptSwitch, Uint32 routerVpnId, Uuid networkId) {
567         LOG.debug("handleNatFlowsInNewNaptSwitch : Proceeding to install flows in newNaptSwitch {} for routerId {}",
568                 newNaptSwitch, routerId);
569         IpPortMapping ipPortMapping = NatUtil.getIportMapping(dataBroker, routerId);
570         if (ipPortMapping == null || ipPortMapping.getIntextIpProtocolType() == null
571             || ipPortMapping.getIntextIpProtocolType().isEmpty()) {
572             LOG.debug("handleNatFlowsInNewNaptSwitch : No Internal Ip Port mapping associated to router {},"
573                     + "no flows need to be installed in newNaptSwitch {}", routerId, newNaptSwitch);
574             return true;
575         }
576         //getvpnId
577         Uint32 vpnId = getVpnIdForRouter(routerId, networkId);
578         if (vpnId == NatConstants.INVALID_ID) {
579             LOG.error("handleNatFlowsInNewNaptSwitch : Invalid vpnId for routerId {}", routerId);
580             return false;
581         }
582         Uint32 bgpVpnId;
583         if (routerId.equals(routerVpnId)) {
584             bgpVpnId = NatConstants.INVALID_ID;
585         } else {
586             bgpVpnId = routerVpnId;
587         }
588         LOG.debug("handleNatFlowsInNewNaptSwitch : retrieved bgpVpnId {} for router {}", bgpVpnId, routerId);
589         // Get the External Gateway MAC Address
590         String extGwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
591         if (extGwMacAddress != null) {
592             LOG.debug("handleNatFlowsInNewNaptSwitch :External Gateway MAC address {} found for External Router ID {}",
593                     extGwMacAddress, routerId);
594         } else {
595             LOG.error("handleNatFlowsInNewNaptSwitch : No External Gateway MAC address found for External Router ID {}",
596                     routerId);
597             return false;
598         }
599         for (IntextIpProtocolType protocolType : ipPortMapping.getIntextIpProtocolType().values()) {
600             if (protocolType.getIpPortMap() == null || protocolType.getIpPortMap().isEmpty()) {
601                 LOG.debug("handleNatFlowsInNewNaptSwitch : No {} session associated to router {}",
602                         protocolType.getProtocol(), routerId);
603                 return true;
604             }
605             for (IpPortMap intIpPortMap : protocolType.getIpPortMap().values()) {
606                 String internalIpAddress = intIpPortMap.getIpPortInternal().split(":")[0];
607                 String intportnum = intIpPortMap.getIpPortInternal().split(":")[1];
608                 LOG.debug("handleNatFlowsInNewNaptSwitch : Found Internal IP Address {} and Port Number {}",
609                     internalIpAddress, intportnum);
610                 //Get the external IP address and the port from the model
611                 NAPTEntryEvent.Protocol proto =
612                     protocolType.getProtocol().toString().equals(ProtocolTypes.TCP.toString())
613                     ? NAPTEntryEvent.Protocol.TCP : NAPTEntryEvent.Protocol.UDP;
614                 IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId,
615                     internalIpAddress, intportnum, proto);
616                 if (ipPortExternal == null) {
617                     LOG.debug("handleNatFlowsInNewNaptSwitch : External Ipport mapping is not found for internalIp {} "
618                             + "with port {}", internalIpAddress, intportnum);
619                     continue;
620                 }
621                 String externalIpAddress = ipPortExternal.getIpAddress();
622                 Integer extportNumber = ipPortExternal.getPortNum().toJava();
623                 LOG.debug("handleNatFlowsInNewNaptSwitch : ExternalIPport {}:{} mapping for internal ipport {}:{}",
624                         externalIpAddress, extportNumber, internalIpAddress, intportnum);
625
626                 SessionAddress sourceAddress = new SessionAddress(internalIpAddress, Integer.parseInt(intportnum));
627                 SessionAddress externalAddress = new SessionAddress(externalIpAddress, extportNumber);
628
629                 //checking naptSwitch status before installing flows
630                 if (NatUtil.getSwitchStatus(dataBroker, newNaptSwitch)) {
631                     //Install the flow in newNaptSwitch Inbound NAPT table.
632                     try {
633                         naptEventHandler.buildAndInstallNatFlows(newNaptSwitch, NwConstants.INBOUND_NAPT_TABLE,
634                             vpnId, routerId, bgpVpnId, externalAddress, sourceAddress, proto, extGwMacAddress);
635                     } catch (RuntimeException ex) {
636                         LOG.error("handleNatFlowsInNewNaptSwitch : Failed to add flow in INBOUND_NAPT_TABLE for "
637                                 + "routerid {} dpnId {} extIpport{}:{} proto {} ipport {}:{} BgpVpnId {}",
638                             routerId, newNaptSwitch, externalAddress, extportNumber, proto,
639                             internalIpAddress, intportnum, bgpVpnId);
640                         return false;
641                     }
642                     LOG.debug("handleNatFlowsInNewNaptSwitch : Successfully installed a flow in Primary switch {} "
643                             + "Inbound NAPT table for router {} ipport {}:{} proto {} extIpport {}:{} BgpVpnId {}",
644                         newNaptSwitch, routerId, internalIpAddress,
645                         intportnum, proto, externalAddress, extportNumber, bgpVpnId);
646                     //Install the flow in newNaptSwitch Outbound NAPT table.
647                     try {
648                         naptEventHandler.buildAndInstallNatFlows(newNaptSwitch, NwConstants.OUTBOUND_NAPT_TABLE,
649                             vpnId, routerId, bgpVpnId, sourceAddress, externalAddress, proto, extGwMacAddress);
650                     } catch (RuntimeException ex) {
651                         LOG.error("handleNatFlowsInNewNaptSwitch : Failed to add flow in OUTBOUND_NAPT_TABLE for "
652                                 + "routerid {} dpnId {} ipport {}:{} proto {} extIpport {}:{} BgpVpnId {}",
653                             routerId, newNaptSwitch, internalIpAddress,
654                             intportnum, proto, externalAddress, extportNumber, bgpVpnId, ex);
655                         return false;
656                     }
657                     LOG.debug("handleNatFlowsInNewNaptSwitch : Successfully installed a flow in Primary switch {} "
658                             + "Outbound NAPT table for router {} ipport {}:{} proto {} extIpport {}:{} BgpVpnId {}",
659                         newNaptSwitch, routerId, internalIpAddress,
660                         intportnum, proto, externalAddress, extportNumber, bgpVpnId);
661                 } else {
662                     LOG.error("handleNatFlowsInNewNaptSwitch : NewNaptSwitch {} gone down while installing flows "
663                             + "from oldNaptswitch {}", newNaptSwitch, oldNaptSwitch);
664                     return false;
665                 }
666             }
667         }
668         return true;
669     }
670
671     // TODO Clean up the exception handling
672     @SuppressWarnings("checkstyle:IllegalCatch")
673     private Uint32 getVpnIdForRouter(Uint32 routerId, Uuid networkId) {
674         try {
675             //getvpnId
676             if (networkId == null) {
677                 LOG.debug("getVpnIdForRouter : network is not associated to router {}", routerId);
678             } else {
679                 Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
680                 if (vpnUuid == null) {
681                     LOG.debug("getVpnIdForRouter : vpn is not associated for network {} in router {}",
682                             networkId, routerId);
683                 } else {
684                     Uint32 vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
685                     if (vpnId.longValue() > 0) {
686                         LOG.debug("getVpnIdForRouter : retrieved vpnId {} for router {}", vpnId, routerId);
687                         return vpnId;
688                     } else {
689                         LOG.debug("getVpnIdForRouter : retrieved invalid vpn Id");
690                     }
691                 }
692             }
693         } catch (Exception ex) {
694             LOG.error("getVpnIdForRouter : Exception while retrieving vpnId for router {}", routerId, ex);
695         }
696         return NatConstants.INVALID_ID;
697     }
698
699     @NonNull
700     public List<BucketInfo> handleGroupInNeighborSwitches(Uint64 dpnId, String routerName, Uint32 routerId,
701             Uint64 naptSwitch) {
702         List<BucketInfo> listBucketInfo = new ArrayList<>();
703         String ifNamePrimary;
704         if (routerId == NatConstants.INVALID_ID) {
705             LOG.error("handleGroupInNeighborSwitches : Invalid routerId returned for routerName {}", routerName);
706             return listBucketInfo;
707         }
708         ifNamePrimary = getTunnelInterfaceName(dpnId, naptSwitch);
709         if (ifNamePrimary != null) {
710             LOG.debug("handleGroupInNeighborSwitches : TunnelInterface {} between ordinary switch {} and naptSwitch {}",
711                 ifNamePrimary, dpnId, naptSwitch);
712             List<ActionInfo> listActionInfoPrimary =
713                 NatUtil.getEgressActionsForInterface(odlInterfaceRpcService, itmManager, interfaceManager,
714                         ifNamePrimary, routerId, true);
715             BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
716             listBucketInfo.add(bucketPrimary);
717         } else {
718             LOG.debug("handleGroupInNeighborSwitches : No TunnelInterface between ordinary switch {} and naptSwitch {}",
719                     dpnId, naptSwitch);
720         }
721         return listBucketInfo;
722     }
723
724     // TODO Clean up the exception handling
725     @SuppressWarnings("checkstyle:IllegalCatch")
726     protected void installSnatGroupEntry(Uint64 dpnId, List<BucketInfo> bucketInfo, String routerName) {
727         GroupEntity groupEntity = null;
728         try {
729             Uint32 groupId = NatUtil.getUniqueId(idManager, NatConstants.SNAT_IDPOOL_NAME,
730                 NatUtil.getGroupIdKey(routerName));
731             if (groupId != NatConstants.INVALID_ID) {
732                 LOG.debug(
733                     "installSnatGroupEntry : install SnatMissEntry for groupId {} for dpnId {} for router {}",
734                     groupId, dpnId, routerName);
735                 groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId.longValue(), routerName,
736                     GroupTypes.GroupAll, bucketInfo);
737                 mdsalManager.syncInstallGroup(groupEntity);
738                 LOG.debug("installSnatGroupEntry : installed the SNAT to NAPT GroupEntity:{}",
739                     groupEntity);
740             } else {
741                 LOG.error("installSnatGroupEntry: Unable to obtain groupId for router:{}", routerName);
742             }
743         } catch (Exception ex) {
744             LOG.error("installSnatGroupEntry : Failed to install group for groupEntity {}", groupEntity, ex);
745         }
746     }
747
748     private void modifySnatGroupEntry(Uint64 dpnId, List<BucketInfo> bucketInfo, String routerName) {
749         installSnatGroupEntry(dpnId, bucketInfo, routerName);
750         LOG.debug("modifySnatGroupEntry : modified SnatMissEntry for dpnId {} of router {}", dpnId, routerName);
751     }
752
753     @Nullable
754     protected String getTunnelInterfaceName(Uint64 srcDpId, Uint64 dstDpId) {
755         Class<? extends TunnelTypeBase> tunType = TunnelTypeVxlan.class;
756         RpcResult<GetTunnelInterfaceNameOutput> rpcResult;
757
758         try {
759             Future<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager.getTunnelInterfaceName(
760                 new GetTunnelInterfaceNameInputBuilder().setSourceDpid(srcDpId).setDestinationDpid(dstDpId)
761                     .setTunnelType(tunType).build());
762             rpcResult = result.get();
763             if (!rpcResult.isSuccessful()) {
764                 tunType = TunnelTypeGre.class;
765                 result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
766                     .setSourceDpid(srcDpId)
767                     .setDestinationDpid(dstDpId)
768                     .setTunnelType(tunType)
769                     .build());
770                 rpcResult = result.get();
771                 if (!rpcResult.isSuccessful()) {
772                     LOG.warn("getTunnelInterfaceName : RPC Call to getTunnelInterfaceId returned with Errors {}",
773                             rpcResult.getErrors());
774                 } else {
775                     return rpcResult.getResult().getInterfaceName();
776                 }
777                 LOG.warn("getTunnelInterfaceName : RPC Call to getTunnelInterfaceId returned with Errors {}",
778                         rpcResult.getErrors());
779             } else {
780                 return rpcResult.getResult().getInterfaceName();
781             }
782         } catch (InterruptedException | ExecutionException e) {
783             LOG.error("getTunnelInterfaceName :Exception when getting tunnel interface Id for tunnel between {} and {}",
784                 srcDpId, dstDpId, e);
785         }
786         LOG.error("getTunnelInterfaceName : Tunnel missing between dpn {}:{}", srcDpId, dstDpId);
787         return null;
788     }
789
790     // TODO Clean up the exception handling
791     @SuppressWarnings("checkstyle:IllegalCatch")
792     public boolean updateNaptSwitch(String routerName, Uint64 naptSwitchId) {
793         RouterToNaptSwitch naptSwitch = new RouterToNaptSwitchBuilder().withKey(new RouterToNaptSwitchKey(routerName))
794             .setPrimarySwitchId(naptSwitchId).build();
795         try {
796             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
797                 NatUtil.buildNaptSwitchRouterIdentifier(routerName), naptSwitch);
798         } catch (Exception ex) {
799             LOG.error("updateNaptSwitch : Failed to write naptSwitch {} for router {} in ds",
800                 naptSwitchId, routerName);
801             return false;
802         }
803         LOG.debug("updateNaptSwitch : Successfully updated naptSwitch {} for router {} in ds",
804             naptSwitchId, routerName);
805         return true;
806     }
807
808     public FlowEntity buildSnatFlowEntity(Uint64 dpId, String routerName, long groupId,
809                                           Uint32 routerVpnId, int addordel) {
810         FlowEntity flowEntity;
811         List<MatchInfo> matches = new ArrayList<>();
812         matches.add(MatchEthernetType.IPV4);
813         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(routerVpnId.longValue()),
814                 MetaDataUtil.METADATA_MASK_VRFID));
815
816         String flowRef = getFlowRefSnat(dpId, NwConstants.PSNAT_TABLE, routerName);
817
818         if (addordel == NatConstants.ADD_FLOW) {
819             List<ActionInfo> actionsInfo = new ArrayList<>();
820             Uint64 tunnelId = NatUtil.getTunnelIdForNonNaptToNaptFlow(dataBroker, natOverVxlanUtil, elanManager,
821                     idManager, routerVpnId, routerName);
822             actionsInfo.add(new ActionSetFieldTunnelId(tunnelId));
823             LOG.debug("buildSnatFlowEntity : Setting the tunnel to the list of action infos {}", actionsInfo);
824             actionsInfo.add(new ActionGroup(groupId));
825             List<InstructionInfo> instructions = new ArrayList<>();
826             instructions.add(new InstructionApplyActions(actionsInfo));
827
828             flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
829                     NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
830                     NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
831         } else {
832             flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
833                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
834                 NwConstants.COOKIE_SNAT_TABLE, matches, null);
835         }
836         return flowEntity;
837     }
838
839     public FlowEntity buildSnatFlowEntityForNaptSwitch(Uint64 dpId, String routerName,
840                                                        Uint32 routerVpnId, int addordel) {
841         FlowEntity flowEntity;
842         List<MatchInfo> matches = new ArrayList<>();
843         matches.add(MatchEthernetType.IPV4);
844         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(routerVpnId.longValue()),
845                 MetaDataUtil.METADATA_MASK_VRFID));
846
847         String flowRef = getFlowRefSnat(dpId, NwConstants.PSNAT_TABLE, routerName);
848
849         if (addordel == NatConstants.ADD_FLOW) {
850             List<InstructionInfo> instructions = new ArrayList<>();
851
852             instructions.add(new InstructionGotoTable(NwConstants.OUTBOUND_NAPT_TABLE));
853
854             flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
855                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
856                 NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
857         } else {
858             flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
859                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
860                 NwConstants.COOKIE_SNAT_TABLE, matches, null);
861         }
862         return flowEntity;
863     }
864
865     private String getFlowRefSnat(Uint64 dpnId, short tableId, String routerID) {
866         return NatConstants.SNAT_FLOWID_PREFIX + dpnId + NatConstants.FLOWID_SEPARATOR + tableId + NatConstants
867                 .FLOWID_SEPARATOR + routerID;
868     }
869
870     protected void installSnatFlows(String routerName, Uint32 routerId, Uint64 naptSwitch, Uint32 routerVpnId,
871         Uuid networkId, String vpnName, TypedReadWriteTransaction<Configuration> confTx) {
872
873         if (routerId.equals(routerVpnId)) {
874             LOG.debug("installSnatFlows : Installing flows for router with internalvpnId");
875             //36 -> 46 ..Install flow forwarding packet to table46 from table36
876             LOG.debug("installSnatFlows : installTerminatingServiceTblEntry in naptswitch with dpnId {} for "
877                 + "routerName {} with routerId {}", naptSwitch, routerName, routerId);
878             externalRouterListener.installTerminatingServiceTblEntry(naptSwitch, routerName, routerId, confTx);
879
880             //Install default flows punting to controller in table 46(OutBoundNapt table)
881             LOG.debug("installSnatFlows : installOutboundMissEntry in naptswitch with dpnId {} for "
882                 + "routerName {} with routerId {}", naptSwitch, routerName, routerId);
883             externalRouterListener.createOutboundTblEntry(naptSwitch, routerId, confTx);
884
885             //Table 47 point to table 21 for inbound traffic
886             LOG.debug("installSnatFlows : installNaptPfibEntry in naptswitch with dpnId {} for router {}",
887                 naptSwitch, routerId);
888             externalRouterListener.installNaptPfibEntry(naptSwitch, routerId, confTx);
889
890             //Table 47 point to group
891             LOG.debug("installSnatFlows : installNaptPfibExternalOutputFlow in naptswitch with dpnId {} for router {}",
892                 naptSwitch, routerId);
893             externalRouterListener.installNaptPfibExternalOutputFlow(routerName, routerId, naptSwitch, confTx);
894         } else {
895             Uuid extNetworkUuid = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
896             if (extNetworkUuid == null) {
897                 LOG.error("onRouterAssociatedToVpn : Unable to retrieve external network Uuid for router {}",
898                         routerName);
899                 return;
900             }
901             ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName,
902                     extNetworkUuid);
903             if (extNwProvType == null) {
904                 LOG.error("onRouterAssociatedToVpn : External Network Provider Type missing");
905                 return;
906             }
907             //36 -> 46 ..Install flow forwarding packet to table46 from table36
908             LOG.debug("installSnatFlows : installTerminatingServiceTblEntry in naptswitch with dpnId {} for "
909                 + "routerName {} with BgpVpnId {}", naptSwitch, routerName, routerVpnId);
910             externalRouterListener
911                 .installTerminatingServiceTblEntryWithUpdatedVpnId(naptSwitch, routerName, routerId,
912                         routerVpnId, confTx, extNwProvType);
913
914             //Install default flows punting to controller in table 46(OutBoundNapt table)
915             LOG.debug("installSnatFlows : installOutboundMissEntry in naptswitch with dpnId {} for "
916                 + "routerName {} with BgpVpnId {}", naptSwitch, routerName, routerVpnId);
917             externalRouterListener.createOutboundTblEntryWithBgpVpn(naptSwitch, routerId, routerVpnId, confTx);
918
919             //Table 47 point to table 21 for inbound traffic
920             LOG.debug("installSnatFlows : installNaptPfibEntry in naptswitch with dpnId {} for router {} "
921                     + "with BgpVpnId {}", naptSwitch, routerId, routerVpnId);
922             externalRouterListener.installNaptPfibEntryWithBgpVpn(naptSwitch, routerId, routerVpnId, confTx);
923         }
924
925         if (vpnName != null) {
926             //NAPT PFIB point to FIB table for outbound traffic
927             org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn
928                 .instance.to.vpn.id.VpnInstance vpnInstance = NatUtil.getVpnIdToVpnInstance(dataBroker, vpnName);
929             if (vpnInstance == null) {
930                 LOG.error("NAT Service : installNaptPfibEntry vpnInstance not found for {}", vpnName);
931                 return;
932             }
933             Uint32 vpnId = vpnInstance.getVpnId();
934             if (vpnName.equals(networkId.getValue())) {
935                 // below condition valid only for flat/vlan use-case
936                 boolean shouldInstallNaptPfibWithExtNetworkVpnId = true;
937                 Collection<Uuid> externalSubnetIds = NatUtil
938                     .getExternalSubnetIdsForRouter(dataBroker, routerName);
939                 if (!externalSubnetIds.isEmpty()) {
940                     //NAPT PFIB point to FIB table for outbound traffic - using external subnetID as vpnID.
941                     for (Uuid externalSubnetId : externalSubnetIds) {
942                         Uint32 externalSubnetVpnId = NatUtil
943                             .getExternalSubnetVpnId(dataBroker, externalSubnetId);
944                         if (externalSubnetVpnId != NatConstants.INVALID_ID) {
945                             shouldInstallNaptPfibWithExtNetworkVpnId = false;
946                             LOG.debug(
947                                 "installSnatFlows : installNaptPfibEntry fin naptswitch with dpnId {} for "
948                                     + "BgpVpnId {}", naptSwitch, externalSubnetVpnId);
949                             externalRouterListener
950                                 .installNaptPfibEntry(naptSwitch, externalSubnetVpnId, confTx);
951                         }
952                     }
953                 }
954                 if (vpnId != NatConstants.INVALID_ID && shouldInstallNaptPfibWithExtNetworkVpnId) {
955                     //NAPT PFIB table point to FIB table for outbound traffic - using external networkID as vpnID.
956                     LOG.debug(
957                         "installSnatFlows : installNaptPfibEntry fin naptswitch with dpnId {} for "
958                             + "BgpVpnId {}", naptSwitch, vpnId);
959                     externalRouterListener.installNaptPfibEntry(naptSwitch, vpnId, confTx);
960                 } else if (vpnId != NatConstants.INVALID_ID) {
961                     LOG.debug("installSnatFlows : Associated BgpvpnId not found for router {}",
962                         routerId);
963                 }
964             }
965             //Install Fib entries for ExternalIps & program 36 -> 44
966             Collection<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
967             String rd = vpnInstance.getVrfId();
968             for (String externalIp : externalIps) {
969                 removeFibEntry(rd, externalIp);
970                 LOG.debug("installSnatFlows : advToBgpAndInstallFibAndTsFlows in naptswitch id {} "
971                     + "with vpnName {} and externalIp {}", naptSwitch, vpnName, externalIp);
972                 externalRouterListener.advToBgpAndInstallFibAndTsFlows(naptSwitch, NwConstants.INBOUND_NAPT_TABLE,
973                     vpnName, routerId, routerName, externalIp, networkId, null /* external-router */, confTx);
974                 LOG.debug("installSnatFlows : Successfully added fib entries in naptswitch {} for "
975                     + "router {} with external IP {}", naptSwitch, routerId, externalIp);
976             }
977         } else {
978             LOG.debug("installSnatFlows : Associated vpnName not found for router {}", routerId);
979         }
980     }
981
982     protected void bestEffortDeletion(Uint32 routerId, String routerName, Map<String, Uint32> externalIpLabel,
983                                       TypedReadWriteTransaction<Configuration> confTx)
984             throws ExecutionException, InterruptedException {
985         Collection<String> newExternalIps = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
986         if (externalIpsCache != null) {
987             Set<String> removedExternalIps = new HashSet<>(externalIpsCache);
988             removedExternalIps.removeAll(newExternalIps);
989             if (removedExternalIps.isEmpty()) {
990                 LOG.info("bestEffortDeletion : No external Ip needed to be removed in bestEffortDeletion "
991                         + "method for router {}", routerName);
992                 return;
993             }
994             Uuid networkId = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
995             String vpnName = getExtNetworkVpnName(routerName, networkId);
996             if (vpnName == null) {
997                 LOG.error("bestEffortDeletion : Vpn is not associated to externalN/w of router {}", routerName);
998                 return;
999             }
1000             Uint64 naptSwitch = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
1001             if (naptSwitch == null || naptSwitch.equals(Uint64.ZERO)) {
1002                 LOG.error("bestEffortDeletion : No naptSwitch is selected for router {}", routerName);
1003                 return;
1004             }
1005             ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker,routerName, networkId);
1006             if (extNwProvType == null) {
1007                 return;
1008             }
1009             String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
1010             if (gwMacAddress != null) {
1011                 LOG.debug("bestEffortDeletion : External Gateway MAC address {} found for External Router ID {}",
1012                         gwMacAddress, routerId);
1013             } else {
1014                 LOG.error("bestEffortDeletion : No External Gateway MAC address found for External Router ID {}",
1015                         routerId);
1016                 return;
1017             }
1018             if (extNwProvType == ProviderTypes.VXLAN) {
1019                 for (String externalIp : removedExternalIps) {
1020                     externalRouterListener.clearBgpRoutes(externalIp, vpnName);
1021                     externalRouterListener.delFibTsAndReverseTraffic(naptSwitch, routerName, routerId, externalIp,
1022                         vpnName, networkId, NatConstants.DEFAULT_LABEL_VALUE, gwMacAddress, true, confTx);
1023                     LOG.debug("bestEffortDeletion : Successfully removed fib entry for externalIp {} for routerId {} "
1024                                     + "on NAPT switch {} ", externalIp, routerId, naptSwitch);
1025                 }
1026             } else {
1027                 if (externalIpLabel == null || externalIpLabel.isEmpty()) {
1028                     LOG.error("bestEffortDeletion : ExternalIpLabel map is empty for router {}", routerName);
1029                     return;
1030                 }
1031                 Uint32 label;
1032                 for (String externalIp : removedExternalIps) {
1033                     if (externalIpLabel.containsKey(externalIp)) {
1034                         label = externalIpLabel.get(externalIp);
1035                         LOG.debug("bestEffortDeletion : Label {} for ExternalIp {} for router {}",
1036                                 label, externalIp, routerName);
1037                     } else {
1038                         LOG.debug("bestEffortDeletion : Label for ExternalIp {} is not found for router {}",
1039                                 externalIp, routerName);
1040                         continue;
1041                     }
1042                     externalRouterListener.clearBgpRoutes(externalIp, vpnName);
1043                     externalRouterListener.delFibTsAndReverseTraffic(naptSwitch, routerName, routerId, externalIp,
1044                             vpnName, networkId, label, gwMacAddress, true, confTx);
1045                     LOG.debug("bestEffortDeletion : Successfully removed fib entries in switch {} for router {} "
1046                             + "and externalIps {}", naptSwitch, routerId, externalIp);
1047                 }
1048             }
1049         } else {
1050             LOG.debug("bestEffortDeletion : No external IP found for router {}", routerId);
1051         }
1052     }
1053
1054     private void removeFibEntry(String rd, String prefix) {
1055         InstanceIdentifier.InstanceIdentifierBuilder<VrfEntry> idBuilder =
1056             InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd))
1057                 .child(VrfEntry.class, new VrfEntryKey(prefix));
1058         InstanceIdentifier<VrfEntry> vrfEntryId = idBuilder.build();
1059         Optional<VrfEntry> ent;
1060         try {
1061             ent = SingleTransactionDataBroker.syncReadOptional(dataBroker,
1062                     LogicalDatastoreType.CONFIGURATION, vrfEntryId);
1063         } catch (ExecutionException | InterruptedException e) {
1064             LOG.error("removeFibEntry: Exception while reading the VrfEntry for the prefix {} rd {}", prefix, rd, e);
1065             return;
1066         }
1067         if (ent.isPresent()) {
1068             LOG.debug("removeFibEntry : Removing Fib entry rd {} prefix {}", rd, prefix);
1069             fibManager.removeFibEntry(rd, prefix, null, null);
1070         }
1071     }
1072
1073     protected void subnetRegisterMapping(Routers routerEntry, Uint32 segmentId) {
1074         externalRouterListener.subnetRegisterMapping(routerEntry, segmentId);
1075     }
1076 }