Gracefully Handle IDManager failure in NAT
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / AbstractSnatService.java
1 /*
2  * Copyright (c) 2017 Red Hat, Inc. 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 static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11
12 import java.math.BigInteger;
13 import java.time.Duration;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.Objects;
18 import java.util.concurrent.ExecutionException;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
22 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
23 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
24 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
25 import org.opendaylight.genius.infra.Datastore.Configuration;
26 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
27 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
28 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
29 import org.opendaylight.genius.infra.TypedWriteTransaction;
30 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
31 import org.opendaylight.genius.mdsalutil.ActionInfo;
32 import org.opendaylight.genius.mdsalutil.BucketInfo;
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.NWUtil;
39 import org.opendaylight.genius.mdsalutil.NwConstants;
40 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
41 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadMetadata;
42 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
43 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
44 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
45 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
46 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
47 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
48 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
49 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
50 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
51 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
52 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
53 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
54 import org.opendaylight.netvirt.natservice.api.SnatServiceListener;
55 import org.opendaylight.netvirt.natservice.ha.NatDataUtil;
56 import org.opendaylight.netvirt.vpnmanager.api.IVpnFootprintService;
57 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
58 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfacesBuilder;
59 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
60 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.LearntVpnVipToPortData;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.LearntVpnVipToPortDataBuilder;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPort;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
75 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
78
79 public abstract class AbstractSnatService implements SnatServiceListener {
80     private static final Logger LOG = LoggerFactory.getLogger(AbstractSnatService.class);
81
82     static final int LOAD_START = mostSignificantBit(MetaDataUtil.METADATA_MASK_SH_FLAG.intValue());
83     static final int LOAD_END = mostSignificantBit(MetaDataUtil.METADATA_MASK_VRFID.intValue() | MetaDataUtil
84             .METADATA_MASK_SH_FLAG.intValue());
85
86     protected final DataBroker dataBroker;
87     protected final ManagedNewTransactionRunner txRunner;
88     protected final IMdsalApiManager mdsalManager;
89     protected final IdManagerService idManager;
90     private final NAPTSwitchSelector naptSwitchSelector;
91     final ItmRpcService itmManager;
92     protected final OdlInterfaceRpcService odlInterfaceRpcService;
93     protected final IInterfaceManager interfaceManager;
94     final IVpnFootprintService vpnFootprintService;
95     protected final IFibManager fibManager;
96     private final NatDataUtil natDataUtil;
97     private final DataTreeEventCallbackRegistrar eventCallbacks;
98
99     AbstractSnatService(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
100         final ItmRpcService itmManager, final OdlInterfaceRpcService odlInterfaceRpcService,
101         final IdManagerService idManager, final NAPTSwitchSelector naptSwitchSelector,
102         final IInterfaceManager interfaceManager,
103         final IVpnFootprintService vpnFootprintService,
104         final IFibManager fibManager, final NatDataUtil natDataUtil,
105         final DataTreeEventCallbackRegistrar eventCallbacks) {
106         this.dataBroker = dataBroker;
107         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
108         this.mdsalManager = mdsalManager;
109         this.itmManager = itmManager;
110         this.interfaceManager = interfaceManager;
111         this.idManager = idManager;
112         this.naptSwitchSelector = naptSwitchSelector;
113         this.odlInterfaceRpcService = odlInterfaceRpcService;
114         this.vpnFootprintService = vpnFootprintService;
115         this.fibManager = fibManager;
116         this.natDataUtil = natDataUtil;
117         this.eventCallbacks = eventCallbacks;
118     }
119
120     protected DataBroker getDataBroker() {
121         return dataBroker;
122     }
123
124     @Override
125     public boolean addSnatAllSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
126         BigInteger primarySwitchId) {
127         LOG.info("addSnatAllSwitch : Handle Snat in all switches for router {}", routers.getRouterName());
128         String routerName = routers.getRouterName();
129         List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
130         /*
131          * Primary switch handled separately since the pseudo port created may
132          * not be present in the switch list on delete.
133          */
134         addSnat(confTx, routers, primarySwitchId, primarySwitchId);
135         for (BigInteger dpnId : switches) {
136             if (!Objects.equals(primarySwitchId, dpnId)) {
137                 addSnat(confTx, routers, primarySwitchId, dpnId);
138             }
139         }
140         return true;
141     }
142
143     @Override
144     public boolean removeSnatAllSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
145             BigInteger primarySwitchId) throws ExecutionException, InterruptedException {
146         LOG.info("removeSnatAllSwitch : Handle Snat in all switches for router {}", routers.getRouterName());
147         String routerName = routers.getRouterName();
148         List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
149         /*
150          * Primary switch handled separately since the pseudo port created may
151          * not be present in the switch list on delete.
152          */
153         removeSnat(confTx, routers, primarySwitchId, primarySwitchId);
154         for (BigInteger dpnId : switches) {
155             if (!Objects.equals(primarySwitchId, dpnId)) {
156                 removeSnat(confTx, routers, primarySwitchId, dpnId);
157             }
158         }
159         return true;
160     }
161
162     @Override
163     public boolean addSnat(TypedReadWriteTransaction<Configuration> confTx, Routers routers, BigInteger primarySwitchId,
164         BigInteger dpnId) {
165
166         // Handle non NAPT switches and NAPT switches separately
167         if (!dpnId.equals(primarySwitchId)) {
168             LOG.info("addSnat : Handle non NAPT switch {} for router {}", dpnId, routers.getRouterName());
169             addSnatCommonEntriesForNonNaptSwitch();
170             addSnatSpecificEntriesForNonNaptSwitch();
171         } else {
172             LOG.info("addSnat : Handle NAPT switch {} for router {}", dpnId, routers.getRouterName());
173             addSnatCommonEntriesForNaptSwitch(confTx, routers, dpnId);
174             addSnatSpecificEntriesForNaptSwitch(confTx, routers, dpnId);
175         }
176         return true;
177     }
178
179     @Override
180     public boolean removeSnat(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
181             BigInteger primarySwitchId, BigInteger dpnId) throws ExecutionException, InterruptedException {
182
183         // Handle non NAPT switches and NAPT switches separately
184         if (!dpnId.equals(primarySwitchId)) {
185             LOG.info("removeSnat : Handle non NAPT switch {} for router {}", dpnId, routers.getRouterName());
186             removeSnatCommonEntriesForNonNaptSwitch();
187             removeSnatSpecificEntriesForNonNaptSwitch();
188         } else {
189             LOG.info("removeSnat : Handle NAPT switch {} for router {}", dpnId, routers.getRouterName());
190             removeSnatCommonEntriesForNaptSwitch(confTx, routers, dpnId);
191             removeSnatSpecificEntriesForNaptSwitch(confTx, routers, dpnId);
192
193         }
194         return true;
195     }
196
197     @Override
198     public boolean addCentralizedRouterAllSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
199             BigInteger primarySwitchId) {
200         LOG.info("addCentralizedRouterAllSwitch : Handle Snat in all switches for router {}",
201                 routers.getRouterName());
202         String routerName = routers.getRouterName();
203         List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
204         addCentralizedRouter(confTx, routers, primarySwitchId, primarySwitchId);
205         for (BigInteger dpnId : switches) {
206             if (!Objects.equals(primarySwitchId, dpnId)) {
207                 addCentralizedRouter(confTx, routers, primarySwitchId, dpnId);
208             }
209         }
210         return true;
211     }
212
213     @Override
214     public boolean removeCentralizedRouterAllSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
215             BigInteger primarySwitchId)  throws ExecutionException, InterruptedException {
216         LOG.info("removeCentralizedRouterAllSwitch : Handle Snat in all switches for router {}",
217                 routers.getRouterName());
218         boolean isLastRouterDelete = false;
219         isLastRouterDelete = NatUtil.isLastExternalRouter(routers.getNetworkId()
220                 .getValue(), routers.getRouterName(), natDataUtil);
221         LOG.info("removeCentralizedRouterAllSwitch : action is delete for router {} and isLastRouterDelete is {}",
222                 routers.getRouterName(), isLastRouterDelete);
223         removeCentralizedRouter(confTx, routers, primarySwitchId, primarySwitchId);
224         String routerName = routers.getRouterName();
225         List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
226         for (BigInteger dpnId : switches) {
227             if (!Objects.equals(primarySwitchId, dpnId)) {
228                 removeCentralizedRouter(confTx, routers, primarySwitchId, dpnId);
229             }
230         }
231         if (isLastRouterDelete) {
232             removeLearntIpPorts(routers);
233             removeMipAdjacencies(routers);
234         }
235         return true;
236     }
237
238     @Override
239     public boolean addCentralizedRouter(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
240             BigInteger primarySwitchId, BigInteger dpnId) {
241         if (!dpnId.equals(primarySwitchId)) {
242             LOG.info("addCentralizedRouter : Handle non NAPT switch {} for router {}",
243                     dpnId, routers.getRouterName());
244             addCommonEntriesForNonNaptSwitch(confTx, routers, primarySwitchId, dpnId);
245         } else {
246             LOG.info("addCentralizedRouter : Handle NAPT switch {} for router {}", dpnId, routers.getRouterName());
247             addCommonEntriesForNaptSwitch(confTx, routers, dpnId);
248         }
249         return true;
250     }
251
252     @Override
253     public boolean removeCentralizedRouter(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
254             BigInteger primarySwitchId, BigInteger dpnId) throws ExecutionException, InterruptedException {
255         if (!dpnId.equals(primarySwitchId)) {
256             LOG.info("removeCentralizedRouter : Handle non NAPT switch {} for router {}",
257                     dpnId, routers.getRouterName());
258             removeCommonEntriesForNonNaptSwitch(confTx, routers, dpnId);
259         } else {
260             LOG.info("removeCentralizedRouter : Handle NAPT switch {} for router {}", dpnId, routers.getRouterName());
261             removeCommonEntriesForNaptSwitch(confTx, routers, dpnId);
262         }
263         return true;
264     }
265
266     @Override
267     public boolean handleRouterUpdate(TypedReadWriteTransaction<Configuration> confTx,
268             Routers origRouter, Routers updatedRouter) throws ExecutionException, InterruptedException {
269         return true;
270     }
271
272     private void addCommonEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
273         BigInteger dpnId) {
274         String routerName = routers.getRouterName();
275         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
276         addDefaultFibRouteForSNAT(confTx, dpnId, routerId);
277         for (ExternalIps externalIp : routers.nonnullExternalIps()) {
278             if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
279                 // In this class we handle only IPv4 use-cases.
280                 continue;
281             }
282             //The logic now handle only one external IP per router, others if present will be ignored.
283             long extSubnetId = NatUtil.getExternalSubnetVpnId(dataBroker, externalIp.getSubnetId());
284             addInboundTerminatingServiceTblEntry(confTx, dpnId, routerId, extSubnetId);
285             addTerminatingServiceTblEntry(confTx, dpnId, routerId);
286             break;
287         }
288     }
289
290     private void removeCommonEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
291         BigInteger dpnId) throws ExecutionException, InterruptedException {
292         String routerName = routers.getRouterName();
293         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
294         removeDefaultFibRouteForSNAT(confTx, dpnId, routerId);
295         for (ExternalIps externalIp : routers.nonnullExternalIps()) {
296             if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
297                 // In this class we handle only IPv4 use-cases.
298                 continue;
299             }
300             removeInboundTerminatingServiceTblEntry(confTx, dpnId, routerId);
301             removeTerminatingServiceTblEntry(confTx, dpnId, routerId);
302             break;
303         }
304     }
305
306     private void addSnatCommonEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
307         BigInteger dpnId) {
308         String routerName = routers.getRouterName();
309         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
310         String externalGwMac = routers.getExtGwMacAddress();
311         for (ExternalIps externalIp : routers.nonnullExternalIps()) {
312             if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
313                 // In this class we handle only IPv4 use-cases.
314                 continue;
315             }
316             //The logic now handle only one external IP per router, others if present will be ignored.
317             long extSubnetId = NatUtil.getExternalSubnetVpnId(dataBroker, externalIp.getSubnetId());
318             addInboundFibEntry(confTx, dpnId, externalIp.getIpAddress(), routerId, extSubnetId,
319                 routers.getNetworkId().getValue(), externalIp.getSubnetId().getValue(), externalGwMac);
320             break;
321         }
322     }
323
324     private void removeSnatCommonEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
325         Routers routers, BigInteger dpnId) throws ExecutionException, InterruptedException {
326         String routerName = routers.getRouterName();
327         Long routerId = NatUtil.getVpnId(confTx, routerName);
328         for (ExternalIps externalIp : routers.nonnullExternalIps()) {
329             if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
330                 // In this class we handle only IPv4 use-cases.
331                 continue;
332             }
333             //The logic now handle only one external IP per router, others if present will be ignored.
334             removeInboundFibEntry(confTx, dpnId, externalIp.getIpAddress(), routerId,
335                 externalIp.getSubnetId().getValue());
336             break;
337         }
338     }
339
340
341     private void addCommonEntriesForNonNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
342         BigInteger primarySwitchId, BigInteger dpnId) {
343         String routerName = routers.getRouterName();
344         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
345         addSnatMissEntry(confTx, dpnId, routerId, routerName, primarySwitchId);
346         addDefaultFibRouteForSNAT(confTx, dpnId, routerId);
347     }
348
349     private void removeCommonEntriesForNonNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
350         BigInteger dpnId) throws ExecutionException, InterruptedException {
351         String routerName = routers.getRouterName();
352         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
353         removeSnatMissEntry(confTx, dpnId, routerId, routerName);
354         removeDefaultFibRouteForSNAT(confTx, dpnId, routerId);
355     }
356
357     private void addSnatCommonEntriesForNonNaptSwitch() {
358         /* Nothing to do here*/
359     }
360
361     private void removeSnatCommonEntriesForNonNaptSwitch() {
362         /* Nothing to do here*/
363     }
364
365     protected abstract void addSnatSpecificEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
366         Routers routers, BigInteger dpnId);
367
368     protected abstract void removeSnatSpecificEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
369         Routers routers, BigInteger dpnId) throws ExecutionException, InterruptedException;
370
371     protected abstract void addSnatSpecificEntriesForNonNaptSwitch();
372
373     protected abstract void removeSnatSpecificEntriesForNonNaptSwitch();
374
375     private void addInboundFibEntry(TypedWriteTransaction<Configuration> confTx, BigInteger dpnId, String externalIp,
376         Long routerId, long extSubnetId, String externalNetId, String subNetId, String routerMac) {
377
378         List<MatchInfo> matches = new ArrayList<>();
379         matches.add(MatchEthernetType.IPV4);
380         if (extSubnetId == NatConstants.INVALID_ID) {
381             LOG.error("ConntrackBasedSnatService : installInboundFibEntry : external subnet id is invalid.");
382             return;
383         }
384         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(extSubnetId),
385                 MetaDataUtil.METADATA_MASK_VRFID));
386         matches.add(new MatchIpv4Destination(externalIp, "32"));
387
388         ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
389         ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
390         listActionInfo.add(new ActionNxResubmit(NwConstants.INBOUND_NAPT_TABLE));
391         instructionInfo.add(new InstructionApplyActions(listActionInfo));
392
393         String flowRef = getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
394         flowRef = flowRef + "inbound" + externalIp;
395         NatUtil.addFlow(confTx, mdsalManager,dpnId, NwConstants.L3_FIB_TABLE, flowRef,
396                 NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches,
397                 instructionInfo);
398         String rd = NatUtil.getVpnRd(dataBroker, subNetId);
399         String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
400         String ipPrefix = externalIp + "/32";
401         NatUtil.addPrefixToInterface(dataBroker, NatUtil.getVpnId(dataBroker, subNetId),
402                 null, ipPrefix, externalNetId, dpnId, Prefixes.PrefixCue.Nat);
403
404         fibManager.addOrUpdateFibEntry(rd, routerMac, ipPrefix,
405                 Collections.singletonList(nextHopIp), VrfEntry.EncapType.Mplsgre, extSubnetId,
406                 0, null, externalNetId, RouteOrigin.STATIC, null);
407     }
408
409     private void removeInboundFibEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
410         String externalIp, Long routerId, String subNetId) throws ExecutionException, InterruptedException {
411         String flowRef = getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
412         flowRef = flowRef + "inbound" + externalIp;
413         NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef);
414         String rd = NatUtil.getVpnRd(dataBroker, subNetId);
415         String ipPrefix = externalIp + "/32";
416         fibManager.removeFibEntry(rd, ipPrefix, confTx);
417         NatUtil.deletePrefixToInterface(dataBroker, NatUtil.getVpnId(dataBroker, subNetId), ipPrefix);
418     }
419
420
421     private void addTerminatingServiceTblEntry(TypedWriteTransaction<Configuration> confTx, BigInteger dpnId,
422         Long routerId) {
423         LOG.info("addTerminatingServiceTblEntry : creating entry for Terminating Service Table "
424                 + "for switch {}, routerId {}", dpnId, routerId);
425         List<MatchInfo> matches = new ArrayList<>();
426         matches.add(MatchEthernetType.IPV4);
427         matches.add(new MatchTunnelId(BigInteger.valueOf(routerId)));
428
429         List<ActionInfo> actionsInfos = new ArrayList<>();
430         ActionNxLoadMetadata actionLoadMeta = new ActionNxLoadMetadata(MetaDataUtil
431                 .getVpnIdMetadata(routerId), LOAD_START, LOAD_END);
432         actionsInfos.add(actionLoadMeta);
433         actionsInfos.add(new ActionNxResubmit(NwConstants.PSNAT_TABLE));
434         List<InstructionInfo> instructions = new ArrayList<>();
435         instructions.add(new InstructionApplyActions(actionsInfos));
436         String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
437         NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
438                 NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
439     }
440
441     private void removeTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
442         Long routerId) throws ExecutionException, InterruptedException {
443         LOG.info("removeTerminatingServiceTblEntry : creating entry for Terminating Service Table "
444             + "for switch {}, routerId {}", dpnId, routerId);
445
446         String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
447         NatUtil.removeFlow(confTx, mdsalManager, dpnId,  NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
448     }
449
450     protected void addSnatMissEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
451         Long routerId, String routerName, BigInteger primarySwitchId)  {
452         LOG.debug("installSnatMissEntry : Installing SNAT miss entry in switch {}", dpnId);
453         List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
454         String ifNamePrimary = NatUtil.getTunnelInterfaceName(dpnId, primarySwitchId, itmManager);
455         List<BucketInfo> listBucketInfo = new ArrayList<>();
456         if (ifNamePrimary != null) {
457             LOG.debug("installSnatMissEntry : On Non- Napt switch , Primary Tunnel interface is {}", ifNamePrimary);
458             listActionInfoPrimary = NatUtil.getEgressActionsForInterface(odlInterfaceRpcService, itmManager,
459                 interfaceManager, ifNamePrimary, routerId, true);
460         }
461         BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
462         listBucketInfo.add(0, bucketPrimary);
463         LOG.debug("installSnatMissEntry : installSnatMissEntry called for dpnId {} with primaryBucket {} ", dpnId,
464             listBucketInfo.get(0));
465         // Install the select group
466         long groupId = NatUtil.getUniqueId(idManager, NatConstants.SNAT_IDPOOL_NAME, getGroupIdKey(routerName));
467         if (groupId != NatConstants.INVALID_ID) {
468             GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll,
469                 listBucketInfo);
470             LOG.debug("installing the PSNAT to NAPTSwitch GroupEntity:{} with GroupId: {}", groupEntity, groupId);
471             mdsalManager.addGroup(confTx, groupEntity);
472
473             // Add the flow to send the packet to the group only after group is available in Config datastore
474             eventCallbacks.onAddOrUpdate(LogicalDatastoreType.CONFIGURATION,
475                     NatUtil.getGroupInstanceId(dpnId, groupId), (unused, newGroupId) -> {
476                     LOG.info("group {} is created in the config", groupId);
477                     ListenableFutures.addErrorLogging(
478                             txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
479                                 innerConfTx -> addSnatMissFlowForGroup(innerConfTx, dpnId, routerId, groupId)),
480                             LOG, "Error adding flow for the group {}",groupId);
481                     return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
482                 }, Duration.ofSeconds(5), iid -> LOG.error("groupId {} not found in config datastore", groupId));
483         } else {
484             LOG.error("installSnatMissEntry: Unable to get groupId for routerName:{}", routerName);
485         }
486     }
487
488     private void addSnatMissFlowForGroup(TypedReadWriteTransaction<Configuration> confTx,
489             BigInteger dpnId, Long routerId, long groupId) {
490         // Install miss entry pointing to group
491         LOG.debug("installSnatMissEntry : buildSnatFlowEntity is called for dpId {}, routerId {} and groupId {}",
492             dpnId, routerId, groupId);
493         List<MatchInfo> matches = new ArrayList<>();
494         matches.add(new MatchEthernetType(0x0800L));
495         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(routerId), MetaDataUtil.METADATA_MASK_VRFID));
496
497
498         List<ActionInfo> actionsInfo = new ArrayList<>();
499         actionsInfo.add(new ActionSetFieldTunnelId(BigInteger.valueOf(routerId)));
500         LOG.debug("installSnatMissEntry : Setting the tunnel to the list of action infos {}", actionsInfo);
501         actionsInfo.add(new ActionGroup(groupId));
502         List<InstructionInfo> instructions = new ArrayList<>();
503         instructions.add(new InstructionApplyActions(actionsInfo));
504         String flowRef = getFlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
505         NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef,
506                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches,
507                 instructions);
508     }
509
510     protected void removeSnatMissEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
511             Long routerId, String routerName) throws ExecutionException, InterruptedException {
512         LOG.debug("removeSnatMissEntry : Removing SNAT miss entry from switch {}", dpnId);
513         // Install the select group
514         long groupId = NatUtil.getUniqueId(idManager, NatConstants.SNAT_IDPOOL_NAME, getGroupIdKey(routerName));
515         if (groupId != NatConstants.INVALID_ID) {
516             LOG.debug("removeSnatMissEntry : removing the PSNAT to NAPTSwitch on DPN {} with GroupId: {}", dpnId,
517                 groupId);
518             mdsalManager.removeGroup(confTx, dpnId, groupId);
519         } else {
520             LOG.error("removeSnatMissEntry: Unable to get groupId for routerName:{}", routerName);
521         }
522         // Install miss entry pointing to group
523         LOG.debug("removeSnatMissEntry : buildSnatFlowEntity is called for dpId {}, routerName {} and groupId {}",
524             dpnId, routerName, groupId);
525
526         String flowRef = getFlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
527         NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef);
528     }
529
530     private void addInboundTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
531         BigInteger dpnId, Long routerId, long extSubnetId) {
532
533         //Install the tunnel table entry in NAPT switch for inbound traffic to SNAT IP from a non a NAPT switch.
534         LOG.info("installInboundTerminatingServiceTblEntry : creating entry for Terminating Service Table "
535                 + "for switch {}, routerId {}", dpnId, routerId);
536         List<MatchInfo> matches = new ArrayList<>();
537         matches.add(MatchEthernetType.IPV4);
538         List<ActionInfo> actionsInfos = new ArrayList<>();
539         if (extSubnetId == NatConstants.INVALID_ID) {
540             LOG.error("installInboundTerminatingServiceTblEntry : external subnet id is invalid.");
541             return;
542         }
543         matches.add(new MatchTunnelId(BigInteger.valueOf(extSubnetId)));
544         ActionNxLoadMetadata actionLoadMeta = new ActionNxLoadMetadata(MetaDataUtil
545                 .getVpnIdMetadata(extSubnetId), LOAD_START, LOAD_END);
546         actionsInfos.add(actionLoadMeta);
547         actionsInfos.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
548         List<InstructionInfo> instructions = new ArrayList<>();
549         instructions.add(new InstructionApplyActions(actionsInfos));
550         String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId.longValue()) + "INBOUND";
551         NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
552                 NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
553     }
554
555     private void removeInboundTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
556         BigInteger dpnId, Long routerId) throws ExecutionException, InterruptedException {
557         //Install the tunnel table entry in NAPT switch for inbound traffic to SNAT IP from a non a NAPT switch.
558         LOG.info("installInboundTerminatingServiceTblEntry : creating entry for Terminating Service Table "
559             + "for switch {}, routerId {}", dpnId, routerId);
560         String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId.longValue()) + "INBOUND";
561         NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
562     }
563
564     private void addDefaultFibRouteForSNAT(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
565         Long extNetId) {
566
567         List<MatchInfo> matches = new ArrayList<>();
568         matches.add(MatchEthernetType.IPV4);
569         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(extNetId),
570             MetaDataUtil.METADATA_MASK_VRFID));
571
572         List<InstructionInfo> instructions = new ArrayList<>();
573         instructions.add(new InstructionGotoTable(NwConstants.PSNAT_TABLE));
574
575         String flowRef = "DefaultFibRouteForSNAT" + getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, extNetId);
576         NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef,
577                 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches,
578                 instructions);
579     }
580
581     private void removeDefaultFibRouteForSNAT(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
582         Long extNetId) throws ExecutionException, InterruptedException {
583         String flowRef = "DefaultFibRouteForSNAT" + getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, extNetId);
584         NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef);
585     }
586
587     protected String getFlowRef(BigInteger dpnId, short tableId, long routerID) {
588         return NatConstants.NAPT_FLOWID_PREFIX + dpnId + NatConstants.FLOWID_SEPARATOR
589             + tableId + NatConstants.FLOWID_SEPARATOR + routerID;
590     }
591
592     protected String getGroupIdKey(String routerName) {
593         return "snatmiss." + routerName;
594     }
595
596     private void removeMipAdjacencies(Routers routers) {
597         LOG.info("removeMipAdjacencies for router {}", routers.getRouterName());
598         String externalSubNetId  = null;
599         for (ExternalIps externalIp : routers.nonnullExternalIps()) {
600             if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
601                 // In this class we handle only IPv4 use-cases.
602                 continue;
603             }
604             externalSubNetId = externalIp.getSubnetId().getValue();
605             break;
606         }
607         if (externalSubNetId == null) {
608             LOG.info("removeMipAdjacencies no external Ipv4 address present on router {}",
609                     routers.getRouterName());
610             return;
611         }
612         InstanceIdentifier<VpnInterfaces> vpnInterfacesId =
613                 InstanceIdentifier.builder(VpnInterfaces.class).build();
614         try {
615             VpnInterfaces vpnInterfaces = SingleTransactionDataBroker.syncRead(dataBroker,
616                     LogicalDatastoreType.CONFIGURATION, vpnInterfacesId);
617             List<VpnInterface> updatedVpnInterface = new ArrayList<>();
618             for (VpnInterface vpnInterface : vpnInterfaces.nonnullVpnInterface()) {
619                 List<Adjacency> updatedAdjacencies = new ArrayList<>();
620                 Adjacencies adjacencies = vpnInterface.augmentation(Adjacencies.class);
621                 if (null != adjacencies) {
622                     for (Adjacency adjacency : adjacencies.nonnullAdjacency()) {
623                         if (!adjacency.getSubnetId().getValue().equals(externalSubNetId)) {
624                             updatedAdjacencies.add(adjacency);
625                         }
626                     }
627                 }
628                 AdjacenciesBuilder adjacenciesBuilder = new AdjacenciesBuilder();
629                 adjacenciesBuilder.setAdjacency(updatedAdjacencies);
630                 VpnInterfaceBuilder vpnInterfaceBuilder = new VpnInterfaceBuilder(vpnInterface);
631                 vpnInterfaceBuilder.addAugmentation(Adjacencies.class, adjacenciesBuilder.build());
632                 updatedVpnInterface.add(vpnInterfaceBuilder.build());
633             }
634             VpnInterfacesBuilder vpnInterfacesBuilder = new VpnInterfacesBuilder();
635             vpnInterfacesBuilder.setVpnInterface(updatedVpnInterface);
636
637             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
638                     vpnInterfacesId, vpnInterfacesBuilder.build());
639         } catch (ReadFailedException e) {
640             LOG.warn("Failed to read removeMipAdjacencies with error {}", e.getMessage());
641         } catch (TransactionCommitFailedException e) {
642             LOG.warn("Failed to remove removeMipAdjacencies with error {}", e.getMessage());
643         }
644     }
645
646     private void removeLearntIpPorts(Routers routers) {
647         LOG.info("removeLearntIpPorts for router {} and network {}", routers.getRouterName(), routers.getNetworkId());
648         String networkId = routers.getNetworkId().getValue();
649         LearntVpnVipToPortData learntVpnVipToPortData = NatUtil.getLearntVpnVipToPortData(dataBroker);
650         if (learntVpnVipToPortData == null) {
651             LOG.info("removeLearntIpPorts, no learned ports present");
652             return;
653         }
654         LearntVpnVipToPortDataBuilder learntVpnVipToPortDataBuilder = new LearntVpnVipToPortDataBuilder();
655         List<LearntVpnVipToPort> learntVpnVipToPortList = new ArrayList<>();
656         for (LearntVpnVipToPort learntVpnVipToPort : learntVpnVipToPortData.nonnullLearntVpnVipToPort()) {
657             if (!networkId.equals(learntVpnVipToPort.getVpnName())) {
658                 LOG.info("The learned port belongs to Vpn {} hence not removing", learntVpnVipToPort.getVpnName());
659                 learntVpnVipToPortList.add(learntVpnVipToPort);
660             } else {
661                 String externalSubNetId = null;
662                 for (ExternalIps externalIp : routers.nonnullExternalIps()) {
663                     if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
664                         // In this class we handle only IPv4 use-cases.
665                         continue;
666                     }
667                     externalSubNetId = externalIp.getSubnetId().getValue();
668                     break;
669                 }
670                 if (externalSubNetId == null) {
671                     LOG.info("removeLearntIpPorts no external Ipv4 address present on router {}",
672                             routers.getRouterName());
673                     return;
674                 }
675                 String prefix = learntVpnVipToPort.getPortFixedip() + "/32";
676                 NatUtil.deletePrefixToInterface(dataBroker, NatUtil.getVpnId(dataBroker,
677                         externalSubNetId), prefix);
678             }
679         }
680
681         try {
682             learntVpnVipToPortDataBuilder.setLearntVpnVipToPort(learntVpnVipToPortList);
683             InstanceIdentifier<LearntVpnVipToPortData> learntVpnVipToPortDataId = NatUtil
684                     .getLearntVpnVipToPortDataId();
685             SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
686                     learntVpnVipToPortDataId, learntVpnVipToPortDataBuilder.build());
687
688         } catch (TransactionCommitFailedException e) {
689             LOG.warn("Failed to remove removeLearntIpPorts with error {}", e.getMessage());
690         }
691     }
692
693     static int mostSignificantBit(int value) {
694         return 31 - Integer.numberOfLeadingZeros(value);
695     }
696 }