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