6c68bce518fc79511953aa67d1928d8ad801503b
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / NaptManager.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
9 /*
10  * Created eyugsar 2016/12/1
11  */
12 package org.opendaylight.netvirt.natservice.internal;
13
14 import static org.opendaylight.netvirt.natservice.internal.NatUtil.requireNonNullElse;
15
16 import com.google.common.base.Optional;
17 import com.google.common.util.concurrent.UncheckedExecutionException;
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.Objects;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.Future;
25 import java.util.concurrent.locks.ReentrantLock;
26 import javax.annotation.Nonnull;
27 import javax.annotation.Nullable;
28 import javax.inject.Inject;
29 import javax.inject.Singleton;
30 import org.apache.commons.net.util.SubnetUtils;
31 import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
32 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
33 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
34 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
35 import org.opendaylight.genius.mdsalutil.MDSALUtil;
36 import org.opendaylight.genius.utils.JvmGlobalLocks;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.DeleteIdPoolInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.DeleteIdPoolInputBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.DeleteIdPoolOutput;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdOutput;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalIpsCounter;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.IntextIpMap;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.IntextIpPortMap;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProtocolTypes;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.SnatintIpPortMap;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.ExternalCounters;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.ExternalCountersKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.external.counters.ExternalIpCounter;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.external.counters.ExternalIpCounterBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.ips.counter.external.counters.ExternalIpCounterKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.IpMapping;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.IpMappingKey;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.ip.mapping.IpMap;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.ip.mapping.IpMapBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.ip.mapping.IpMapKey;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.IpPortMapping;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.IpPortMappingKey;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolTypeKey;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMapBuilder;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMapKey;
74 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;
75 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.IpPortExternalBuilder;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.IntipPortMap;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.IntipPortMapKey;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPort;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPortKey;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoType;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoTypeBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoTypeKey;
83 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
84 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
85 import org.opendaylight.yangtools.yang.common.RpcResult;
86 import org.slf4j.Logger;
87 import org.slf4j.LoggerFactory;
88
89 @Singleton
90 public class NaptManager {
91     private static final Logger LOG = LoggerFactory.getLogger(NaptManager.class);
92
93     private static final long LOW_PORT = 49152L;
94     private static final long HIGH_PORT = 65535L;
95
96     private final DataBroker dataBroker;
97     private final IdManagerService idManager;
98
99     @Inject
100     public NaptManager(final DataBroker dataBroker, final IdManagerService idManager) {
101         this.dataBroker = dataBroker;
102         this.idManager = idManager;
103     }
104
105     protected void createNaptPortPool(String poolName) {
106         LOG.debug("createNaptPortPool : requested for : {}", poolName);
107         CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
108             .setPoolName(poolName)
109             .setLow(LOW_PORT)
110             .setHigh(HIGH_PORT)
111             .build();
112         try {
113             Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
114             if (result != null && result.get().isSuccessful()) {
115                 LOG.debug("createNaptPortPool : Created PortPool :{}", poolName);
116             } else {
117                 LOG.error("createNaptPortPool : Unable to create PortPool : {}", poolName);
118             }
119         } catch (InterruptedException | ExecutionException e) {
120             LOG.error("createNaptPortPool : Failed to create PortPool for NAPT Service", e);
121         }
122     }
123
124     void removeNaptPortPool(String poolName) {
125         DeleteIdPoolInput deleteIdPoolInput = new DeleteIdPoolInputBuilder().setPoolName(poolName).build();
126         LOG.debug("removeNaptPortPool : Remove Napt port pool requested for : {}", poolName);
127         try {
128             Future<RpcResult<DeleteIdPoolOutput>> result = idManager.deleteIdPool(deleteIdPoolInput);
129             if (result != null && result.get().isSuccessful()) {
130                 LOG.debug("removeNaptPortPool : Deleted PortPool {}", poolName);
131             } else {
132                 LOG.error("removeNaptPortPool : Unable to delete PortPool {}", poolName);
133             }
134         } catch (InterruptedException | ExecutionException e) {
135             LOG.error("removeNaptPortPool : Failed to delete PortPool {} for NAPT Service", poolName, e);
136         }
137     }
138
139     // 1. napt service functions
140
141     /**
142      * This method is used to inform this service of what external IP address to be used
143      * as mapping when requested one for the internal IP address given in the input.
144      *
145      * @param segmentId – segmentation in which the mapping to be used. Eg; routerid
146      * @param internal  subnet prefix or ip address
147      * @param external  subnet prefix or ip address
148      */
149
150     public void registerMapping(long segmentId, IPAddress internal, IPAddress external) {
151         LOG.debug("registerMapping : called with segmentid {}, internalIp {}, prefix {}, externalIp {} "
152             + "and prefix {} ", segmentId, internal.getIpAddress(),
153             internal.getPrefixLength(), external.getIpAddress(), external.getPrefixLength());
154         // Create Pool per ExternalIp and not for all IPs in the subnet.
155         // Create new Pools during getExternalAddressMapping if exhausted.
156         String externalIpPool;
157         // subnet case
158         if (external.getPrefixLength() != 0 && external.getPrefixLength() != NatConstants.DEFAULT_PREFIX) {
159             String externalSubnet = external.getIpAddress() + "/" + external.getPrefixLength();
160             LOG.debug("registerMapping : externalSubnet is : {}", externalSubnet);
161             SubnetUtils subnetUtils = new SubnetUtils(externalSubnet);
162             SubnetInfo subnetInfo = subnetUtils.getInfo();
163             externalIpPool = subnetInfo.getLowAddress();
164         } else {  // ip case
165             externalIpPool = external.getIpAddress();
166         }
167         createNaptPortPool(externalIpPool);
168
169         // Store the ip to ip map in Operational DS
170         String internalIp = internal.getIpAddress();
171         if (internal.getPrefixLength() != 0) {
172             internalIp = internal.getIpAddress() + "/" + internal.getPrefixLength();
173         }
174         String externalIp = external.getIpAddress();
175         if (external.getPrefixLength() != 0) {
176             externalIp = external.getIpAddress() + "/" + external.getPrefixLength();
177         }
178         updateCounter(segmentId, externalIp, true);
179         //update the actual ip-map
180         IpMap ipm = new IpMapBuilder().withKey(new IpMapKey(internalIp)).setInternalIp(internalIp)
181             .setExternalIp(externalIp).build();
182         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
183             getIpMapIdentifier(segmentId, internalIp), ipm);
184         LOG.debug("registerMapping : registerMapping exit after updating DS with internalIP {}, externalIP {}",
185             internalIp, externalIp);
186     }
187
188     public void updateCounter(long segmentId, String externalIp, boolean isAdd) {
189         short counter = 0;
190         InstanceIdentifier<ExternalIpCounter> id = InstanceIdentifier.builder(ExternalIpsCounter.class)
191             .child(ExternalCounters.class, new ExternalCountersKey(segmentId))
192             .child(ExternalIpCounter.class, new ExternalIpCounterKey(externalIp)).build();
193         Optional<ExternalIpCounter> externalIpCounter =
194             MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
195         if (externalIpCounter.isPresent()) {
196             counter = externalIpCounter.get().getCounter();
197             if (isAdd) {
198                 counter++;
199                 LOG.debug("updateCounter : externalIp and counter after increment are {} and {}", externalIp, counter);
200             } else {
201                 if (counter > 0) {
202                     counter--;
203                 }
204                 LOG.debug("updateCounter : externalIp and counter after decrement are {} and {}", externalIp, counter);
205             }
206
207         } else if (isAdd) {
208             counter = 1;
209         }
210
211         //update the new counter value for this externalIp
212         ExternalIpCounter externalIpCounterData = new ExternalIpCounterBuilder()
213             .withKey(new ExternalIpCounterKey(externalIp)).setExternalIp(externalIp).setCounter(counter).build();
214         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
215             getExternalIpsIdentifier(segmentId, externalIp), externalIpCounterData);
216     }
217
218     /**
219      * method to get external ip/port mapping when provided with internal ip/port pair
220      * If already a mapping exist for the given input, then the existing mapping is returned
221      * instead of overwriting with new ip/port pair.
222      *
223      * @param segmentId     - Router ID
224      * @param sourceAddress - internal ip address/port pair
225      * @param protocol      - TCP/UDP
226      * @return external ip address/port
227      */
228     // TODO Clean up the exception handling
229     @SuppressWarnings("checkstyle:IllegalCatch")
230     @Nullable
231     public SessionAddress getExternalAddressMapping(long segmentId, SessionAddress sourceAddress,
232                                                     NAPTEntryEvent.Protocol protocol) {
233         LOG.debug("getExternalAddressMapping : called with segmentId {}, internalIp {} and port {}",
234             segmentId, sourceAddress.getIpAddress(), sourceAddress.getPortNumber());
235         /*
236          1. Get Internal IP, Port in IP:Port format
237          2. Inside DB with routerId get the list of entries and check if it matches with existing IP:Port
238          3. If True return SessionAddress of ExternalIp and Port
239          4. Else check ip Map and Form the ExternalIp and Port and update DB and then return ExternalIp and Port
240          */
241
242         //SessionAddress externalIpPort = new SessionAddress();
243         String internalIpPort = sourceAddress.getIpAddress() + ":" + sourceAddress.getPortNumber();
244
245         // First check existing Port Map.
246         SessionAddress existingIpPort = checkIpPortMap(segmentId, internalIpPort, protocol);
247         if (existingIpPort != null) {
248             // populate externalIpPort from IpPortMap and return
249             LOG.debug("getExternalAddressMapping : successfully returning existingIpPort as {} and {}",
250                 existingIpPort.getIpAddress(), existingIpPort.getPortNumber());
251             return existingIpPort;
252         }
253
254         // Now check in ip-map
255         String externalIp = checkIpMap(segmentId, sourceAddress.getIpAddress());
256         if (externalIp == null) {
257             LOG.error("getExternalAddressMapping : Unexpected error, internal to external "
258                     + "ip map does not exist");
259             return null;
260         }
261
262         /* Logic assuming internalIp is always ip and not subnet
263          * case 1: externalIp is ip
264          *        a) goto externalIp pool and getPort and return
265          *        b) else return error
266          * case 2: externalIp is subnet
267          *        a) Take first externalIp and goto that Pool and getPort
268          *             if port -> return
269          *             else Take second externalIp and create that Pool and getPort
270          *             if port ->return
271          *             else
272          *             Continue same with third externalIp till we exhaust subnet
273          *        b) Nothing worked return error
274          */
275         SubnetUtils externalIpSubnet;
276         List<String> allIps = new ArrayList<>();
277         String subnetPrefix = "/" + String.valueOf(NatConstants.DEFAULT_PREFIX);
278         boolean extSubnetFlag = false;
279         if (!externalIp.contains(subnetPrefix)) {
280             extSubnetFlag = true;
281             externalIpSubnet = new SubnetUtils(externalIp);
282             allIps = Arrays.asList(externalIpSubnet.getInfo().getAllAddresses());
283             LOG.debug("getExternalAddressMapping : total count of externalIps available {}",
284                 externalIpSubnet.getInfo().getAddressCount());
285         } else {
286             LOG.debug("getExternalAddressMapping : getExternalAddress single ip case");
287             if (externalIp.contains(subnetPrefix)) {
288                 //remove /32 what we got from checkIpMap
289                 externalIp = externalIp.substring(0, externalIp.indexOf(subnetPrefix));
290             }
291             allIps.add(externalIp);
292         }
293
294         boolean nextExtIpFlag = false;
295         for (String extIp : allIps) {
296             LOG.info("getExternalAddressMapping : Looping externalIPs with externalIP now as {}", extIp);
297             if (nextExtIpFlag) {
298                 createNaptPortPool(extIp);
299                 LOG.debug("getExternalAddressMapping : Created Pool for next Ext IP {}", extIp);
300             }
301             AllocateIdInput getIdInput = new AllocateIdInputBuilder()
302                     .setPoolName(extIp).setIdKey(internalIpPort)
303                     .build();
304             try {
305                 Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
306                 RpcResult<AllocateIdOutput> rpcResult;
307                 if (result != null && result.get().isSuccessful()) {
308                     LOG.debug("getExternalAddressMapping : Got id from idManager");
309                     rpcResult = result.get();
310                 } else {
311                     LOG.error("getExternalAddressMapping : getExternalAddressMapping, idManager could not "
312                             + "allocate id retry if subnet");
313                     if (!extSubnetFlag) {
314                         LOG.error("getExternalAddressMapping : getExternalAddressMapping returning null "
315                                 + "for single IP case, may be ports exhausted");
316                         return null;
317                     }
318                     LOG.debug("getExternalAddressMapping : Could be ports exhausted case, "
319                             + "try with another externalIP if possible");
320                     nextExtIpFlag = true;
321                     continue;
322                 }
323                 int extPort = rpcResult.getResult().getIdValue().intValue();
324                 // Write to ip-port-map before returning
325                 IpPortExternalBuilder ipExt = new IpPortExternalBuilder();
326                 IpPortExternal ipPortExt = ipExt.setIpAddress(extIp).setPortNum(extPort).build();
327                 IpPortMap ipm = new IpPortMapBuilder().withKey(new IpPortMapKey(internalIpPort))
328                         .setIpPortInternal(internalIpPort).setIpPortExternal(ipPortExt).build();
329                 LOG.debug("getExternalAddressMapping : writing into ip-port-map with "
330                         + "externalIP {} and port {}",
331                         ipPortExt.getIpAddress(), ipPortExt.getPortNum());
332                 try {
333                     MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
334                         getIpPortMapIdentifier(segmentId, internalIpPort, protocol), ipm);
335                 } catch (UncheckedExecutionException uee) {
336                     LOG.error("getExternalAddressMapping : Failed to write into ip-port-map with exception",
337                         uee);
338                 }
339
340                 // Write to snat-internal-ip-port-info
341                 String internalIpAddress = sourceAddress.getIpAddress();
342                 int ipPort = sourceAddress.getPortNumber();
343                 ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
344                 final ReentrantLock lock = lockFor(segmentId, internalIpAddress, protocolType);
345                 lock.lock();
346                 try {
347                     List<Integer> portList = new ArrayList<>(
348                             NatUtil.getInternalIpPortListInfo(dataBroker, segmentId, internalIpAddress,
349                                 protocolType));
350                     portList.add(ipPort);
351
352                     IntIpProtoTypeBuilder builder = new IntIpProtoTypeBuilder();
353                     IntIpProtoType intIpProtocolType =
354                             builder.withKey(new IntIpProtoTypeKey(protocolType)).setPorts(portList).build();
355                     try {
356                         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
357                             NatUtil.buildSnatIntIpPortIdentifier(segmentId, internalIpAddress, protocolType),
358                             intIpProtocolType);
359                     } catch (Exception ex) {
360                         LOG.error("getExternalAddressMapping : Failed to write into snat-internal-ip-port-info "
361                                 + "with exception", ex);
362                     }
363                 } finally {
364                     lock.unlock();
365                 }
366                 SessionAddress externalIpPort = new SessionAddress(extIp, extPort);
367                 LOG.debug("getExternalAddressMapping : successfully returning externalIP {} "
368                         + "and port {}", externalIpPort.getIpAddress(), externalIpPort.getPortNumber());
369                 return externalIpPort;
370             } catch (InterruptedException | ExecutionException e) {
371                 LOG.error("getExternalAddressMapping : Exception caught", e);
372                 return null;
373             }
374         } // end of for loop
375         LOG.error("getExternalAddressMapping : Unable to handle external IP address and port mapping with segmentId {},"
376                 + "internalIp {} and internalPort {}", segmentId, sourceAddress.getIpAddress(),
377                 sourceAddress.getPortNumber());
378         return null;
379     }
380
381     /**
382      * Release the existing mapping of internal ip/port to external ip/port pair
383      * if no mapping exist for given internal ip/port, it returns false.
384      *
385      * @param segmentId - Router ID
386      * @param address   - Session Address
387      * @param protocol  - TCP/UDP
388      * @return true if mapping exist and the mapping is removed successfully
389      */
390     // TODO Clean up the exception handling
391     @SuppressWarnings("checkstyle:IllegalCatch")
392     public boolean releaseAddressMapping(long segmentId, SessionAddress address, NAPTEntryEvent.Protocol protocol) {
393         LOG.debug("releaseAddressMapping : called with segmentId {}, internalIP {}, port {}",
394             segmentId, address.getIpAddress(), address.getPortNumber());
395         // delete entry from IpPort Map and IP Map if exists
396         String internalIpPort = address.getIpAddress() + ":" + address.getPortNumber();
397         SessionAddress existingIpPort = checkIpPortMap(segmentId, internalIpPort, protocol);
398         if (existingIpPort != null) {
399             // delete the entry from IpPortMap DS
400             try {
401                 removeFromIpPortMapDS(segmentId, internalIpPort, protocol);
402             } catch (Exception e) {
403                 LOG.error("releaseAddressMapping : failed, Removal of ipportmap {} for "
404                     + "router {} failed", internalIpPort, segmentId, e);
405                 return false;
406             }
407         } else {
408             LOG.error("releaseAddressMapping : failed, segmentId {} and internalIpPort {} "
409                 + "not found in IpPortMap DS", segmentId, internalIpPort);
410             return false;
411         }
412         String existingIp = checkIpMap(segmentId, address.getIpAddress());
413         if (existingIp != null) {
414             // delete the entry from IpMap DS
415             try {
416                 removeFromIpMapDS(segmentId, address.getIpAddress());
417             } catch (Exception e) {
418                 LOG.error("releaseAddressMapping : Removal of  ipmap {} for router {} failed",
419                     address.getIpAddress(), segmentId, e);
420                 return false;
421             }
422             //delete the entry from snatIntIpportinfo
423             try {
424                 removeFromSnatIpPortDS(segmentId, address.getIpAddress());
425             } catch (Exception e) {
426                 LOG.error("releaseAddressMapping : failed, Removal of snatipportmap {} for "
427                     + "router {} failed", address.getIpAddress(), segmentId, e);
428                 return false;
429             }
430         } else {
431             LOG.error("releaseAddressMapping : failed, segmentId {} and internalIpPort {} "
432                 + "not found in IpMap DS", segmentId, internalIpPort);
433             return false;
434         }
435         // Finally release port from idmanager
436         removePortFromPool(internalIpPort, existingIpPort.getIpAddress());
437
438         LOG.debug("releaseAddressMapping : Exited successfully for segmentId {} and internalIpPort {}",
439                 segmentId, internalIpPort);
440         return true;
441     }
442
443     // TODO Clean up the exception handling
444     @SuppressWarnings("checkstyle:IllegalCatch")
445     protected void releaseIpExtPortMapping(long segmentId, SessionAddress address, NAPTEntryEvent.Protocol protocol) {
446         String internalIpPort = address.getIpAddress() + ":" + address.getPortNumber();
447         SessionAddress existingIpPort = checkIpPortMap(segmentId, internalIpPort, protocol);
448         if (existingIpPort != null) {
449             // delete the entry from IpPortMap DS
450             try {
451                 removeFromIpPortMapDS(segmentId, internalIpPort, protocol);
452                 // Finally release port from idmanager
453                 removePortFromPool(internalIpPort, existingIpPort.getIpAddress());
454             } catch (Exception e) {
455                 LOG.error("releaseIpExtPortMapping : failed, Removal of ipportmap {} for "
456                     + "router {} failed", internalIpPort, segmentId, e);
457             }
458         } else {
459             LOG.error("releaseIpExtPortMapping : failed, segmentId {} and "
460                 + "internalIpPort {} not found in IpPortMap DS", segmentId, internalIpPort);
461         }
462
463         //delete the entry of port for InternalIp from snatIntIpportMappingDS
464         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
465         final ReentrantLock lock = lockFor(segmentId, address.getIpAddress(), protocolType);
466         lock.lock();
467         try {
468             removeSnatIntIpPortDS(segmentId, address, protocolType);
469         } catch (Exception e) {
470             LOG.error("releaseSnatIpPortMapping : failed, Removal of snatipportmap {} for router {} failed",
471                 address.getIpAddress(), segmentId, e);
472         } finally {
473             lock.unlock();
474         }
475     }
476
477     /**
478      * Removes the internal ip to external ip mapping if present.
479      *
480      * @param segmentId - Router ID
481      * @return true if successfully removed
482      */
483     // TODO Clean up the exception handling
484     @SuppressWarnings("checkstyle:IllegalCatch")
485     public boolean removeMapping(long segmentId) {
486         try {
487             removeIpMappingForRouterID(segmentId);
488             removeIpPortMappingForRouterID(segmentId);
489             removeIntIpPortMappingForRouterID(segmentId);
490         } catch (Exception e) {
491             LOG.error("removeMapping : Removal of  IPMapping for router {} failed", segmentId, e);
492             return false;
493         }
494
495         //TODO :  This is when router is deleted then cleanup the entries in tables, ports etc - Delete scenarios
496         return false;
497     }
498
499     protected InstanceIdentifier<IpMap> getIpMapIdentifier(long segid, String internal) {
500         return InstanceIdentifier.builder(IntextIpMap.class)
501             .child(IpMapping.class, new IpMappingKey(segid))
502             .child(IpMap.class, new IpMapKey(internal)).build();
503     }
504
505     protected InstanceIdentifier<ExternalIpCounter> getExternalIpsIdentifier(long segmentId, String external) {
506         return InstanceIdentifier.builder(ExternalIpsCounter.class)
507             .child(ExternalCounters.class, new ExternalCountersKey(segmentId))
508             .child(ExternalIpCounter.class, new ExternalIpCounterKey(external)).build();
509     }
510
511     @Nonnull
512     public static List<IpMap> getIpMapList(DataBroker broker, Long routerId) {
513         InstanceIdentifier<IpMapping> id = getIpMapList(routerId);
514         return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
515                 LogicalDatastoreType.OPERATIONAL, id).toJavaUtil().map(IpMapping::getIpMap).orElse(
516                 Collections.emptyList());
517     }
518
519     protected static InstanceIdentifier<IpMapping> getIpMapList(long routerId) {
520         return InstanceIdentifier.builder(
521             IntextIpMap.class).child(IpMapping.class, new IpMappingKey(routerId)).build();
522     }
523
524     protected InstanceIdentifier<IpPortMap> getIpPortMapIdentifier(long segid, String internal,
525                                                                    NAPTEntryEvent.Protocol protocol) {
526         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
527         return InstanceIdentifier.builder(IntextIpPortMap.class)
528             .child(IpPortMapping.class, new IpPortMappingKey(segid))
529             .child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType))
530             .child(IpPortMap.class, new IpPortMapKey(internal)).build();
531     }
532
533     @Nullable
534     private SessionAddress checkIpPortMap(long segmentId, String internalIpPort,
535             NAPTEntryEvent.Protocol protocol) {
536         LOG.debug("checkIpPortMap : called with segmentId {} and internalIpPort {}",
537                 segmentId, internalIpPort);
538         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
539         // check if ip-port-map node is there
540         InstanceIdentifierBuilder<IpPortMap> idBuilder =
541                 InstanceIdentifier.builder(IntextIpPortMap.class)
542                 .child(IpPortMapping.class, new IpPortMappingKey(segmentId))
543                 .child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType))
544                 .child(IpPortMap.class, new IpPortMapKey(internalIpPort));
545         InstanceIdentifier<IpPortMap> id = idBuilder.build();
546         Optional<IpPortMap> ipPortMapType =
547                 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
548         if (ipPortMapType.isPresent()) {
549             LOG.debug("checkIpPortMap : {}", ipPortMapType.get());
550             SessionAddress externalIpPort = new SessionAddress(ipPortMapType.get().getIpPortExternal().getIpAddress(),
551                     ipPortMapType.get().getIpPortExternal().getPortNum());
552             LOG.debug("checkIpPortMap : returning successfully externalIP {} and port {}",
553                     externalIpPort.getIpAddress(), externalIpPort.getPortNumber());
554             return externalIpPort;
555         }
556         // return null if not found
557         LOG.warn("checkIpPortMap : no-entry in checkIpPortMap, returning NULL [should be OK] for "
558                 + "segmentId {} and internalIPPort {}", segmentId, internalIpPort);
559         return null;
560     }
561
562     @Nullable
563     protected String checkIpMap(long segmentId, String internalIp) {
564         LOG.debug("checkIpMap : called with segmentId {} and internalIp {}", segmentId, internalIp);
565         String externalIp;
566         // check if ip-map node is there
567         InstanceIdentifierBuilder<IpMapping> idBuilder =
568             InstanceIdentifier.builder(IntextIpMap.class).child(IpMapping.class, new IpMappingKey(segmentId));
569         InstanceIdentifier<IpMapping> id = idBuilder.build();
570         Optional<IpMapping> ipMapping = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
571         if (ipMapping.isPresent()) {
572             for (IpMap ipMap : requireNonNullElse(ipMapping.get().getIpMap(), Collections.<IpMap>emptyList())) {
573                 if (Objects.equals(ipMap.getInternalIp(), internalIp)) {
574                     LOG.debug("checkIpMap : IpMap : {}", ipMap);
575                     externalIp = ipMap.getExternalIp();
576                     LOG.debug("checkIpMap : successfully returning externalIp {}", externalIp);
577                     return externalIp;
578                 } else if (ipMap.getInternalIp().contains("/")) { // subnet case
579                     SubnetUtils subnetUtils = new SubnetUtils(ipMap.getInternalIp());
580                     SubnetInfo subnetInfo = subnetUtils.getInfo();
581                     if (subnetInfo.isInRange(internalIp)) {
582                         LOG.debug("checkIpMap : internalIp {} found to be IpMap of internalIpSubnet {}",
583                             internalIp, ipMap.getInternalIp());
584                         externalIp = ipMap.getExternalIp();
585                         LOG.debug("checkIpMap : checkIpMap successfully returning externalIp {}", externalIp);
586                         return externalIp;
587                     }
588                 }
589             }
590         }
591         // return null if not found
592         LOG.error("checkIpMap : failed, returning NULL for segmentId {} and internalIp {}",
593             segmentId, internalIp);
594         return null;
595     }
596
597     // TODO Clean up the exception handling
598     @SuppressWarnings("checkstyle:IllegalCatch")
599     protected void removeSnatIntIpPortDS(long segmentId, SessionAddress address, ProtocolTypes protocolType) {
600         LOG.trace("removeSnatIntIpPortDS : method called for IntIpport {} of router {} ",
601             address, segmentId);
602         List<Integer> portList =
603             NatUtil.getInternalIpPortListInfo(dataBroker, segmentId, address.getIpAddress(), protocolType);
604         if (portList.isEmpty() || !portList.contains(address.getPortNumber())) {
605             LOG.error("removeSnatIntIpPortDS : Internal IP {} for port {} entry not found in SnatIntIpPort DS",
606                 address.getIpAddress(), address.getPortNumber());
607             return;
608         }
609         LOG.trace("removeSnatIntIpPortDS : PortList {} retrieved for InternalIp {} of router {}",
610             portList, address.getIpAddress(), segmentId);
611         Integer port = address.getPortNumber();
612         portList.remove(port);
613
614         IntIpProtoTypeBuilder builder = new IntIpProtoTypeBuilder();
615         IntIpProtoType intIpProtocolType =
616             builder.withKey(new IntIpProtoTypeKey(protocolType)).setPorts(portList).build();
617         try {
618             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
619                 NatUtil.buildSnatIntIpPortIdentifier(segmentId, address.getIpAddress(), protocolType),
620                 intIpProtocolType);
621         } catch (Exception ex) {
622             LOG.error("removeSnatIntIpPortDS : Failed to write into snat-internal-ip-port-info with exception", ex);
623         }
624         LOG.debug("removeSnatIntIpPortDS : Removing SnatIp {} Port {} of router {} from SNATIntIpport datastore",
625             address.getIpAddress(), address.getPortNumber(), segmentId);
626     }
627
628     protected void removeFromSnatIpPortDS(long segmentId, String internalIp) {
629         InstanceIdentifier<IpPort> intIp = InstanceIdentifier.builder(SnatintIpPortMap.class)
630             .child(IntipPortMap.class, new IntipPortMapKey(segmentId))
631             .child(IpPort.class, new IpPortKey(internalIp)).build();
632         // remove from SnatIpPortDS
633         LOG.debug("removeFromSnatIpPortDS : Removing SnatIpPort from datastore : {}", intIp);
634         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, intIp);
635     }
636
637     protected void removeFromIpPortMapDS(long segmentId, String internalIpPort, NAPTEntryEvent.Protocol protocol) {
638         ProtocolTypes protocolType = NatUtil.getProtocolType(protocol);
639         removeFromIpPortMapDS(segmentId, internalIpPort, protocolType);
640     }
641
642     protected void removeFromIpPortMapDS(long segmentId, String internalIpPort, ProtocolTypes protocolType) {
643         InstanceIdentifierBuilder<IpPortMap> idBuilder = InstanceIdentifier.builder(IntextIpPortMap.class)
644             .child(IpPortMapping.class, new IpPortMappingKey(segmentId))
645             .child(IntextIpProtocolType.class, new IntextIpProtocolTypeKey(protocolType))
646             .child(IpPortMap.class, new IpPortMapKey(internalIpPort));
647         InstanceIdentifier<IpPortMap> id = idBuilder.build();
648         // remove from ipportmap DS
649         LOG.debug("removeFromIpPortMapDS : Removing ipportmap from datastore : {}", id);
650         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
651     }
652
653     protected void removeFromIpMapDS(long segmentId, String internalIp) {
654         InstanceIdentifierBuilder<IpMap> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
655             .child(IpMapping.class, new IpMappingKey(segmentId))
656             .child(IpMap.class, new IpMapKey(internalIp));
657         InstanceIdentifier<IpMap> id = idBuilder.build();
658         // Get externalIp and decrement the counter
659         String externalIp = null;
660         Optional<IpMap> ipMap = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
661         if (ipMap.isPresent()) {
662             externalIp = ipMap.get().getExternalIp();
663             LOG.debug("removeFromIpMapDS : externalIP is {}", externalIp);
664         } else {
665             LOG.warn("removeFromIpMapDS : ipMap not present for the internal IP {}", internalIp);
666         }
667
668         if (externalIp != null) {
669             updateCounter(segmentId, externalIp, false);
670             // remove from ipmap DS
671             LOG.debug("removeFromIpMapDS : Removing ipmap from datastore");
672             MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
673         } else {
674             LOG.warn("removeFromIpMapDS : externalIp not present for the internal IP {}", internalIp);
675         }
676     }
677
678     protected void removeIntExtIpMapDS(long segmentId, String internalIp) {
679         InstanceIdentifierBuilder<IpMap> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
680             .child(IpMapping.class, new IpMappingKey(segmentId))
681             .child(IpMap.class, new IpMapKey(internalIp));
682         InstanceIdentifier<IpMap> id = idBuilder.build();
683
684         LOG.debug("removeIntExtIpMapDS : Removing ipmap from datastore");
685         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
686     }
687
688     @Nullable
689     protected String getExternalIpAllocatedForSubnet(long segmentId, String internalIp) {
690         InstanceIdentifierBuilder<IpMap> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
691             .child(IpMapping.class, new IpMappingKey(segmentId))
692             .child(IpMap.class, new IpMapKey(internalIp));
693         InstanceIdentifier<IpMap> id = idBuilder.build();
694
695         Optional<IpMap> ipMap = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
696         if (ipMap.isPresent()) {
697             return ipMap.get().getExternalIp();
698         }
699         return null;
700     }
701
702     private void removeIpMappingForRouterID(long segmentId) {
703         InstanceIdentifierBuilder<IpMapping> idBuilder = InstanceIdentifier.builder(IntextIpMap.class)
704             .child(IpMapping.class, new IpMappingKey(segmentId));
705         InstanceIdentifier<IpMapping> id = idBuilder.build();
706         // Get all externalIps and decrement their counters before deleting the ipmap
707         Optional<IpMapping> ipMapping = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
708         if (ipMapping.isPresent()) {
709             for (IpMap ipMap : requireNonNullElse(ipMapping.get().getIpMap(), Collections.<IpMap>emptyList())) {
710                 String externalIp = ipMap.getExternalIp();
711                 LOG.debug("removeIpMappingForRouterID : externalIP is {}", externalIp);
712                 if (externalIp != null) {
713                     updateCounter(segmentId, externalIp, false);
714                 }
715             }
716             // remove from ipmap DS
717             LOG.debug("removeIpMappingForRouterID : Removing Ipmap for router {} from datastore", segmentId);
718             MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
719         }
720     }
721
722     void removeIpPortMappingForRouterID(long segmentId) {
723         InstanceIdentifier<IpPortMapping> idBuilder = InstanceIdentifier.builder(IntextIpPortMap.class)
724             .child(IpPortMapping.class, new IpPortMappingKey(segmentId)).build();
725         Optional<IpPortMapping> ipPortMapping = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
726                 idBuilder);
727         if (ipPortMapping.isPresent()) {
728             // remove from IntExtIpPortmap DS
729             LOG.debug("removeIpPortMappingForRouterID : Removing IntExtIpPort map for router {} from datastore",
730                     segmentId);
731             MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, idBuilder);
732         }
733     }
734
735     void removeIntIpPortMappingForRouterID(long segmentId) {
736         InstanceIdentifier<IntipPortMap> intIp = InstanceIdentifier.builder(SnatintIpPortMap.class)
737             .child(IntipPortMap.class, new IntipPortMapKey(segmentId)).build();
738         Optional<IntipPortMap> intIpPortMap = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, intIp);
739         if (intIpPortMap.isPresent()) {
740             // remove from SnatIntIpPortmap DS
741             LOG.debug("removeIntIpPortMappingForRouterID : Removing SnatIntIpPort from datastore : {}", intIp);
742             MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, intIp);
743         }
744     }
745
746     void removePortFromPool(String internalIpPort, String externalIp) {
747         LOG.debug("removePortFromPool : method called");
748         ReleaseIdInput idInput = new ReleaseIdInputBuilder()
749             .setPoolName(externalIp)
750             .setIdKey(internalIpPort).build();
751         try {
752             RpcResult<ReleaseIdOutput> rpcResult = idManager.releaseId(idInput).get();
753             if (!rpcResult.isSuccessful()) {
754                 LOG.error("removePortFromPool : idmanager failed to remove port from pool {}", rpcResult.getErrors());
755             }
756             LOG.debug("removePortFromPool : Removed port from pool for InternalIpPort {} with externalIp {}",
757                 internalIpPort, externalIp);
758         } catch (InterruptedException | ExecutionException e) {
759             LOG.error("removePortFromPool : idmanager failed when removing entry in pool with key {} with Exception",
760                     internalIpPort, e);
761         }
762     }
763
764     protected void initialiseExternalCounter(Routers routers, long routerId) {
765         LOG.debug("initialiseExternalCounter : Initialise External IPs counter");
766
767         //update the new counter value for this externalIp
768         for (ExternalIps externalIp : requireNonNullElse(routers.getExternalIps(),
769                 Collections.<ExternalIps>emptyList())) {
770             String[] ipSplit = externalIp.getIpAddress().split("/");
771             String extIp = ipSplit[0];
772             String extPrefix = Short.toString(NatConstants.DEFAULT_PREFIX);
773             if (ipSplit.length == 2) {
774                 extPrefix = ipSplit[1];
775             }
776             extIp = extIp + "/" + extPrefix;
777             initialiseNewExternalIpCounter(routerId, extIp);
778         }
779     }
780
781     protected void initialiseNewExternalIpCounter(long routerId, String externalIp) {
782         ExternalIpCounter externalIpCounterData = new ExternalIpCounterBuilder()
783             .withKey(new ExternalIpCounterKey(externalIp)).setExternalIp(externalIp).setCounter((short) 0).build();
784         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
785             getExternalIpsIdentifier(routerId, externalIp), externalIpCounterData);
786     }
787
788     protected void removeExternalCounter(long routerId) {
789         // Remove from external-counters model
790         InstanceIdentifier<ExternalCounters> id = InstanceIdentifier.builder(ExternalIpsCounter.class)
791             .child(ExternalCounters.class, new ExternalCountersKey(routerId)).build();
792         LOG.debug("removeExternalCounter : Removing ExternalCounterd from datastore");
793         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
794     }
795
796     protected void removeExternalIpCounter(long routerId, String externalIp) {
797         // Remove from external-counters model
798         InstanceIdentifier<ExternalIpCounter> id = InstanceIdentifier.builder(ExternalIpsCounter.class)
799             .child(ExternalCounters.class, new ExternalCountersKey(routerId))
800             .child(ExternalIpCounter.class, new ExternalIpCounterKey(externalIp)).build();
801         LOG.debug("removeExternalIpCounter : Removing ExternalIpsCounter from datastore");
802         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
803     }
804
805     private static ReentrantLock lockFor(final long segmentId, String ipAddress, final ProtocolTypes protocolType) {
806         // FIXME: use an Identifier class instead?
807         String lockName = new StringBuilder()
808             .append(segmentId)
809             .append(NatConstants.COLON_SEPARATOR)
810             .append(ipAddress)
811             .append(NatConstants.COLON_SEPARATOR)
812             .append(protocolType.getName()).toString();
813
814         return JvmGlobalLocks.getLockForString(lockName);
815     }
816 }