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