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