2 * Copyright (c) 2017 Red Hat, Inc. and others. All rights reserved.
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
8 package org.opendaylight.netvirt.natservice.internal;
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11 import static org.opendaylight.netvirt.natservice.internal.NatUtil.requireNonNullElse;
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;
85 public abstract class AbstractSnatService implements SnatServiceListener {
86 private static final Logger LOG = LoggerFactory.getLogger(AbstractSnatService.class);
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());
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;
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;
126 protected DataBroker getDataBroker() {
131 LOG.info("{} init", getClass().getSimpleName());
134 public void close() {
135 LOG.debug("AbstractSnatService Closed");
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);
145 * Primary switch handled separately since the pseudo port created may
146 * not be present in the switch list on delete.
148 addSnat(confTx, routers, primarySwitchId, primarySwitchId);
149 for (BigInteger dpnId : switches) {
150 if (!Objects.equals(primarySwitchId, dpnId)) {
151 addSnat(confTx, routers, primarySwitchId, dpnId);
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);
164 * Primary switch handled separately since the pseudo port created may
165 * not be present in the switch list on delete.
167 removeSnat(confTx, routers, primarySwitchId, primarySwitchId);
168 for (BigInteger dpnId : switches) {
169 if (!Objects.equals(primarySwitchId, dpnId)) {
170 removeSnat(confTx, routers, primarySwitchId, dpnId);
177 public boolean addSnat(TypedReadWriteTransaction<Configuration> confTx, Routers routers, BigInteger primarySwitchId,
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);
186 LOG.info("addSnat : Handle NAPT switch {} for router {}", dpnId, routers.getRouterName());
187 addSnatCommonEntriesForNaptSwitch(confTx, routers, dpnId);
188 addSnatSpecificEntriesForNaptSwitch(confTx, routers, dpnId);
194 public boolean removeSnat(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
195 BigInteger primarySwitchId, BigInteger dpnId) throws ExecutionException, InterruptedException {
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);
203 LOG.info("removeSnat : Handle NAPT switch {} for router {}", dpnId, routers.getRouterName());
204 removeSnatCommonEntriesForNaptSwitch(confTx, routers, dpnId);
205 removeSnatSpecificEntriesForNaptSwitch(confTx, routers, dpnId);
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);
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);
245 if (isLastRouterDelete) {
246 removeLearntIpPorts(routers);
247 removeMipAdjacencies(routers);
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);
260 LOG.info("addCentralizedRouter : Handle NAPT switch {} for router {}", dpnId, routers.getRouterName());
261 addCommonEntriesForNaptSwitch(confTx, routers, dpnId);
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);
274 LOG.info("removeCentralizedRouter : Handle NAPT switch {} for router {}", dpnId, routers.getRouterName());
275 removeCommonEntriesForNaptSwitch(confTx, routers, dpnId);
281 public boolean handleRouterUpdate(TypedReadWriteTransaction<Configuration> confTx,
282 Routers origRouter, Routers updatedRouter) throws ExecutionException, InterruptedException {
286 protected void addCommonEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
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.
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);
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.
318 removeInboundTerminatingServiceTblEntry(confTx, dpnId, routerId);
319 removeTerminatingServiceTblEntry(confTx, dpnId, routerId);
324 protected void addSnatCommonEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
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.
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);
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.
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());
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);
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);
377 protected void addSnatCommonEntriesForNonNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
378 Routers routers, BigInteger primarySwitchId, BigInteger dpnId) {
379 /* Nothing to do here*/
382 protected void removeSnatCommonEntriesForNonNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
383 Routers routers, BigInteger dpnId) throws ExecutionException, InterruptedException {
384 /* Nothing to do here*/
387 protected abstract void addSnatSpecificEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
388 Routers routers, BigInteger dpnId);
390 protected abstract void removeSnatSpecificEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
391 Routers routers, BigInteger dpnId) throws ExecutionException, InterruptedException;
393 protected abstract void addSnatSpecificEntriesForNonNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
394 Routers routers, BigInteger dpnId);
396 protected abstract void removeSnatSpecificEntriesForNonNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
397 Routers routers, BigInteger dpnId);
399 protected void addInboundFibEntry(TypedWriteTransaction<Configuration> confTx, BigInteger dpnId, String externalIp,
400 Long routerId, long extSubnetId, String externalNetId, String subNetId, String routerMac) {
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.");
408 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(extSubnetId),
409 MetaDataUtil.METADATA_MASK_VRFID));
410 matches.add(new MatchIpv4Destination(externalIp, "32"));
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));
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,
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);
428 fibManager.addOrUpdateFibEntry(rd, routerMac, ipPrefix,
429 Collections.singletonList(nextHopIp), VrfEntry.EncapType.Mplsgre, extSubnetId,
430 0, null, externalNetId, RouteOrigin.STATIC, null);
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);
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)));
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);
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);
470 String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
471 NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
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);
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));
492 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll,
494 LOG.debug("installing the PSNAT to NAPTSwitch GroupEntity:{} with GroupId: {}", groupEntity, groupId);
495 mdsalManager.addGroup(confTx, groupEntity);
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));
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));
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,
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));
537 LOG.debug("removing the PSNAT to NAPTSwitch on DPN {} with GroupId: {}", dpnId, groupId);
538 mdsalManager.removeGroup(confTx, dpnId, groupId);
540 // Install miss entry pointing to group
541 LOG.debug("installSnatMissEntry : buildSnatFlowEntity is called for dpId {}, routerName {} and groupId {}",
542 dpnId, routerName, groupId);
544 String flowRef = getFlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
545 NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef);
548 protected void addInboundTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
549 BigInteger dpnId, Long routerId, long extSubnetId) {
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.");
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);
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);
582 protected void addDefaultFibRouteForSNAT(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
585 List<MatchInfo> matches = new ArrayList<>();
586 matches.add(MatchEthernetType.IPV4);
587 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(extNetId),
588 MetaDataUtil.METADATA_MASK_VRFID));
590 List<InstructionInfo> instructions = new ArrayList<>();
591 instructions.add(new InstructionGotoTable(NwConstants.PSNAT_TABLE));
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,
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);
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;
610 protected long createGroupId(String groupIdKey) {
611 AllocateIdInput getIdInput = new AllocateIdInputBuilder()
612 .setPoolName(NatConstants.SNAT_IDPOOL_NAME).setIdKey(groupIdKey)
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);
624 protected String getGroupIdKey(String routerName) {
625 return "snatmiss." + routerName;
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.
637 externalSubNetId = externalIp.getSubnetId().getValue();
640 if (externalSubNetId == null) {
641 LOG.info("removeMipAdjacencies no external Ipv4 address present on router {}",
642 routers.getRouterName());
645 InstanceIdentifier<VpnInterfaces> vpnInterfacesId =
646 InstanceIdentifier.builder(VpnInterfaces.class).build();
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);
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());
669 VpnInterfacesBuilder vpnInterfacesBuilder = new VpnInterfacesBuilder();
670 vpnInterfacesBuilder.setVpnInterface(updatedVpnInterface);
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());
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");
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);
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.
704 externalSubNetId = externalIp.getSubnetId().getValue();
707 if (externalSubNetId == null) {
708 LOG.info("removeLearntIpPorts no external Ipv4 address present on router {}",
709 routers.getRouterName());
712 String prefix = learntVpnVipToPort.getPortFixedip() + "/32";
713 NatUtil.deletePrefixToInterface(dataBroker, NatUtil.getVpnId(dataBroker,
714 externalSubNetId), prefix);
719 learntVpnVipToPortDataBuilder.setLearntVpnVipToPort(learntVpnVipToPortList);
720 InstanceIdentifier<LearntVpnVipToPortData> learntVpnVipToPortDataId = NatUtil
721 .getLearntVpnVipToPortDataId();
722 SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
723 learntVpnVipToPortDataId, learntVpnVipToPortDataBuilder.build());
725 } catch (TransactionCommitFailedException e) {
726 LOG.warn("Failed to remove removeLearntIpPorts with error {}", e.getMessage());
730 static int mostSignificantBit(int value) {
731 return 31 - Integer.numberOfLeadingZeros(value);