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;
12 import java.math.BigInteger;
13 import java.time.Duration;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.Objects;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.Future;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
23 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
24 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
25 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
26 import org.opendaylight.genius.infra.Datastore.Configuration;
27 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
28 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
29 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
30 import org.opendaylight.genius.infra.TypedWriteTransaction;
31 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
32 import org.opendaylight.genius.mdsalutil.ActionInfo;
33 import org.opendaylight.genius.mdsalutil.BucketInfo;
34 import org.opendaylight.genius.mdsalutil.GroupEntity;
35 import org.opendaylight.genius.mdsalutil.InstructionInfo;
36 import org.opendaylight.genius.mdsalutil.MDSALUtil;
37 import org.opendaylight.genius.mdsalutil.MatchInfo;
38 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
39 import org.opendaylight.genius.mdsalutil.NWUtil;
40 import org.opendaylight.genius.mdsalutil.NwConstants;
41 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
42 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadMetadata;
43 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
44 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
45 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
46 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
47 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
48 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
49 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
50 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
51 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
52 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
53 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
54 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
55 import org.opendaylight.netvirt.natservice.api.SnatServiceListener;
56 import org.opendaylight.netvirt.natservice.ha.NatDataUtil;
57 import org.opendaylight.netvirt.vpnmanager.api.IVpnFootprintService;
58 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
59 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfacesBuilder;
60 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
61 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.LearntVpnVipToPortData;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.LearntVpnVipToPortDataBuilder;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPort;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
79 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
80 import org.opendaylight.yangtools.yang.common.RpcResult;
81 import org.slf4j.Logger;
82 import org.slf4j.LoggerFactory;
84 public abstract class AbstractSnatService implements SnatServiceListener {
85 private static final Logger LOG = LoggerFactory.getLogger(AbstractSnatService.class);
87 static final int LOAD_START = mostSignificantBit(MetaDataUtil.METADATA_MASK_SH_FLAG.intValue());
88 static final int LOAD_END = mostSignificantBit(MetaDataUtil.METADATA_MASK_VRFID.intValue() | MetaDataUtil
89 .METADATA_MASK_SH_FLAG.intValue());
91 protected final DataBroker dataBroker;
92 protected final ManagedNewTransactionRunner txRunner;
93 protected final IMdsalApiManager mdsalManager;
94 protected final IdManagerService idManager;
95 private final NAPTSwitchSelector naptSwitchSelector;
96 final ItmRpcService itmManager;
97 protected final OdlInterfaceRpcService odlInterfaceRpcService;
98 protected final IInterfaceManager interfaceManager;
99 final IVpnFootprintService vpnFootprintService;
100 protected final IFibManager fibManager;
101 private final NatDataUtil natDataUtil;
102 private final DataTreeEventCallbackRegistrar eventCallbacks;
104 AbstractSnatService(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
105 final ItmRpcService itmManager, final OdlInterfaceRpcService odlInterfaceRpcService,
106 final IdManagerService idManager, final NAPTSwitchSelector naptSwitchSelector,
107 final IInterfaceManager interfaceManager,
108 final IVpnFootprintService vpnFootprintService,
109 final IFibManager fibManager, final NatDataUtil natDataUtil,
110 final DataTreeEventCallbackRegistrar eventCallbacks) {
111 this.dataBroker = dataBroker;
112 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
113 this.mdsalManager = mdsalManager;
114 this.itmManager = itmManager;
115 this.interfaceManager = interfaceManager;
116 this.idManager = idManager;
117 this.naptSwitchSelector = naptSwitchSelector;
118 this.odlInterfaceRpcService = odlInterfaceRpcService;
119 this.vpnFootprintService = vpnFootprintService;
120 this.fibManager = fibManager;
121 this.natDataUtil = natDataUtil;
122 this.eventCallbacks = eventCallbacks;
125 protected DataBroker getDataBroker() {
130 public boolean addSnatAllSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
131 BigInteger primarySwitchId) {
132 LOG.info("addSnatAllSwitch : Handle Snat in all switches for router {}", routers.getRouterName());
133 String routerName = routers.getRouterName();
134 List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
136 * Primary switch handled separately since the pseudo port created may
137 * not be present in the switch list on delete.
139 addSnat(confTx, routers, primarySwitchId, primarySwitchId);
140 for (BigInteger dpnId : switches) {
141 if (!Objects.equals(primarySwitchId, dpnId)) {
142 addSnat(confTx, routers, primarySwitchId, dpnId);
149 public boolean removeSnatAllSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
150 BigInteger primarySwitchId) throws ExecutionException, InterruptedException {
151 LOG.info("removeSnatAllSwitch : Handle Snat in all switches for router {}", routers.getRouterName());
152 String routerName = routers.getRouterName();
153 List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
155 * Primary switch handled separately since the pseudo port created may
156 * not be present in the switch list on delete.
158 removeSnat(confTx, routers, primarySwitchId, primarySwitchId);
159 for (BigInteger dpnId : switches) {
160 if (!Objects.equals(primarySwitchId, dpnId)) {
161 removeSnat(confTx, routers, primarySwitchId, dpnId);
168 public boolean addSnat(TypedReadWriteTransaction<Configuration> confTx, Routers routers, BigInteger primarySwitchId,
171 // Handle non NAPT switches and NAPT switches separately
172 if (!dpnId.equals(primarySwitchId)) {
173 LOG.info("addSnat : Handle non NAPT switch {} for router {}", dpnId, routers.getRouterName());
174 addSnatCommonEntriesForNonNaptSwitch();
175 addSnatSpecificEntriesForNonNaptSwitch();
177 LOG.info("addSnat : Handle NAPT switch {} for router {}", dpnId, routers.getRouterName());
178 addSnatCommonEntriesForNaptSwitch(confTx, routers, dpnId);
179 addSnatSpecificEntriesForNaptSwitch(confTx, routers, dpnId);
185 public boolean removeSnat(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
186 BigInteger primarySwitchId, BigInteger dpnId) throws ExecutionException, InterruptedException {
188 // Handle non NAPT switches and NAPT switches separately
189 if (!dpnId.equals(primarySwitchId)) {
190 LOG.info("removeSnat : Handle non NAPT switch {} for router {}", dpnId, routers.getRouterName());
191 removeSnatCommonEntriesForNonNaptSwitch();
192 removeSnatSpecificEntriesForNonNaptSwitch();
194 LOG.info("removeSnat : Handle NAPT switch {} for router {}", dpnId, routers.getRouterName());
195 removeSnatCommonEntriesForNaptSwitch(confTx, routers, dpnId);
196 removeSnatSpecificEntriesForNaptSwitch(confTx, routers, dpnId);
203 public boolean addCentralizedRouterAllSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
204 BigInteger primarySwitchId) {
205 LOG.info("addCentralizedRouterAllSwitch : Handle Snat in all switches for router {}",
206 routers.getRouterName());
207 String routerName = routers.getRouterName();
208 List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
209 addCentralizedRouter(confTx, routers, primarySwitchId, primarySwitchId);
210 for (BigInteger dpnId : switches) {
211 if (!Objects.equals(primarySwitchId, dpnId)) {
212 addCentralizedRouter(confTx, routers, primarySwitchId, dpnId);
219 public boolean removeCentralizedRouterAllSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
220 BigInteger primarySwitchId) throws ExecutionException, InterruptedException {
221 LOG.info("removeCentralizedRouterAllSwitch : Handle Snat in all switches for router {}",
222 routers.getRouterName());
223 boolean isLastRouterDelete = false;
224 isLastRouterDelete = NatUtil.isLastExternalRouter(routers.getNetworkId()
225 .getValue(), routers.getRouterName(), natDataUtil);
226 LOG.info("removeCentralizedRouterAllSwitch : action is delete for router {} and isLastRouterDelete is {}",
227 routers.getRouterName(), isLastRouterDelete);
228 removeCentralizedRouter(confTx, routers, primarySwitchId, primarySwitchId);
229 String routerName = routers.getRouterName();
230 List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
231 for (BigInteger dpnId : switches) {
232 if (!Objects.equals(primarySwitchId, dpnId)) {
233 removeCentralizedRouter(confTx, routers, primarySwitchId, dpnId);
236 if (isLastRouterDelete) {
237 removeLearntIpPorts(routers);
238 removeMipAdjacencies(routers);
244 public boolean addCentralizedRouter(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
245 BigInteger primarySwitchId, BigInteger dpnId) {
246 if (!dpnId.equals(primarySwitchId)) {
247 LOG.info("addCentralizedRouter : Handle non NAPT switch {} for router {}",
248 dpnId, routers.getRouterName());
249 addCommonEntriesForNonNaptSwitch(confTx, routers, primarySwitchId, dpnId);
251 LOG.info("addCentralizedRouter : Handle NAPT switch {} for router {}", dpnId, routers.getRouterName());
252 addCommonEntriesForNaptSwitch(confTx, routers, dpnId);
258 public boolean removeCentralizedRouter(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
259 BigInteger primarySwitchId, BigInteger dpnId) throws ExecutionException, InterruptedException {
260 if (!dpnId.equals(primarySwitchId)) {
261 LOG.info("removeCentralizedRouter : Handle non NAPT switch {} for router {}",
262 dpnId, routers.getRouterName());
263 removeCommonEntriesForNonNaptSwitch(confTx, routers, dpnId);
265 LOG.info("removeCentralizedRouter : Handle NAPT switch {} for router {}", dpnId, routers.getRouterName());
266 removeCommonEntriesForNaptSwitch(confTx, routers, dpnId);
272 public boolean handleRouterUpdate(TypedReadWriteTransaction<Configuration> confTx,
273 Routers origRouter, Routers updatedRouter) throws ExecutionException, InterruptedException {
277 private void addCommonEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
279 String routerName = routers.getRouterName();
280 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
281 addDefaultFibRouteForSNAT(confTx, dpnId, routerId);
282 for (ExternalIps externalIp : routers.nonnullExternalIps()) {
283 if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
284 // In this class we handle only IPv4 use-cases.
287 //The logic now handle only one external IP per router, others if present will be ignored.
288 long extSubnetId = NatUtil.getExternalSubnetVpnId(dataBroker, externalIp.getSubnetId());
289 addInboundTerminatingServiceTblEntry(confTx, dpnId, routerId, extSubnetId);
290 addTerminatingServiceTblEntry(confTx, dpnId, routerId);
295 private void removeCommonEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
296 BigInteger dpnId) throws ExecutionException, InterruptedException {
297 String routerName = routers.getRouterName();
298 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
299 removeDefaultFibRouteForSNAT(confTx, dpnId, routerId);
300 for (ExternalIps externalIp : routers.nonnullExternalIps()) {
301 if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
302 // In this class we handle only IPv4 use-cases.
305 removeInboundTerminatingServiceTblEntry(confTx, dpnId, routerId);
306 removeTerminatingServiceTblEntry(confTx, dpnId, routerId);
311 private void addSnatCommonEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
313 String routerName = routers.getRouterName();
314 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
315 String externalGwMac = routers.getExtGwMacAddress();
316 for (ExternalIps externalIp : routers.nonnullExternalIps()) {
317 if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
318 // In this class we handle only IPv4 use-cases.
321 //The logic now handle only one external IP per router, others if present will be ignored.
322 long extSubnetId = NatUtil.getExternalSubnetVpnId(dataBroker, externalIp.getSubnetId());
323 addInboundFibEntry(confTx, dpnId, externalIp.getIpAddress(), routerId, extSubnetId,
324 routers.getNetworkId().getValue(), externalIp.getSubnetId().getValue(), externalGwMac);
329 private void removeSnatCommonEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
330 Routers routers, BigInteger dpnId) throws ExecutionException, InterruptedException {
331 String routerName = routers.getRouterName();
332 Long routerId = NatUtil.getVpnId(confTx, routerName);
333 for (ExternalIps externalIp : routers.nonnullExternalIps()) {
334 if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
335 // In this class we handle only IPv4 use-cases.
338 //The logic now handle only one external IP per router, others if present will be ignored.
339 removeInboundFibEntry(confTx, dpnId, externalIp.getIpAddress(), routerId,
340 externalIp.getSubnetId().getValue());
346 private void addCommonEntriesForNonNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
347 BigInteger primarySwitchId, BigInteger dpnId) {
348 String routerName = routers.getRouterName();
349 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
350 addSnatMissEntry(confTx, dpnId, routerId, routerName, primarySwitchId);
351 addDefaultFibRouteForSNAT(confTx, dpnId, routerId);
354 private void removeCommonEntriesForNonNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
355 BigInteger dpnId) throws ExecutionException, InterruptedException {
356 String routerName = routers.getRouterName();
357 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
358 removeSnatMissEntry(confTx, dpnId, routerId, routerName);
359 removeDefaultFibRouteForSNAT(confTx, dpnId, routerId);
362 private void addSnatCommonEntriesForNonNaptSwitch() {
363 /* Nothing to do here*/
366 private void removeSnatCommonEntriesForNonNaptSwitch() {
367 /* Nothing to do here*/
370 protected abstract void addSnatSpecificEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
371 Routers routers, BigInteger dpnId);
373 protected abstract void removeSnatSpecificEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
374 Routers routers, BigInteger dpnId) throws ExecutionException, InterruptedException;
376 protected abstract void addSnatSpecificEntriesForNonNaptSwitch();
378 protected abstract void removeSnatSpecificEntriesForNonNaptSwitch();
380 private void addInboundFibEntry(TypedWriteTransaction<Configuration> confTx, BigInteger dpnId, String externalIp,
381 Long routerId, long extSubnetId, String externalNetId, String subNetId, String routerMac) {
383 List<MatchInfo> matches = new ArrayList<>();
384 matches.add(MatchEthernetType.IPV4);
385 if (extSubnetId == NatConstants.INVALID_ID) {
386 LOG.error("ConntrackBasedSnatService : installInboundFibEntry : external subnet id is invalid.");
389 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(extSubnetId),
390 MetaDataUtil.METADATA_MASK_VRFID));
391 matches.add(new MatchIpv4Destination(externalIp, "32"));
393 ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
394 ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
395 listActionInfo.add(new ActionNxResubmit(NwConstants.INBOUND_NAPT_TABLE));
396 instructionInfo.add(new InstructionApplyActions(listActionInfo));
398 String flowRef = getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
399 flowRef = flowRef + "inbound" + externalIp;
400 NatUtil.addFlow(confTx, mdsalManager,dpnId, NwConstants.L3_FIB_TABLE, flowRef,
401 NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches,
403 String rd = NatUtil.getVpnRd(dataBroker, subNetId);
404 String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
405 String ipPrefix = externalIp + "/32";
406 NatUtil.addPrefixToInterface(dataBroker, NatUtil.getVpnId(dataBroker, subNetId),
407 null, ipPrefix, externalNetId, dpnId, Prefixes.PrefixCue.Nat);
409 fibManager.addOrUpdateFibEntry(rd, routerMac, ipPrefix,
410 Collections.singletonList(nextHopIp), VrfEntry.EncapType.Mplsgre, extSubnetId,
411 0, null, externalNetId, RouteOrigin.STATIC, null);
414 private void removeInboundFibEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
415 String externalIp, Long routerId, String subNetId) throws ExecutionException, InterruptedException {
416 String flowRef = getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
417 flowRef = flowRef + "inbound" + externalIp;
418 NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef);
419 String rd = NatUtil.getVpnRd(dataBroker, subNetId);
420 String ipPrefix = externalIp + "/32";
421 fibManager.removeFibEntry(rd, ipPrefix, confTx);
422 NatUtil.deletePrefixToInterface(dataBroker, NatUtil.getVpnId(dataBroker, subNetId), ipPrefix);
426 private void addTerminatingServiceTblEntry(TypedWriteTransaction<Configuration> confTx, BigInteger dpnId,
428 LOG.info("addTerminatingServiceTblEntry : creating entry for Terminating Service Table "
429 + "for switch {}, routerId {}", dpnId, routerId);
430 List<MatchInfo> matches = new ArrayList<>();
431 matches.add(MatchEthernetType.IPV4);
432 matches.add(new MatchTunnelId(BigInteger.valueOf(routerId)));
434 List<ActionInfo> actionsInfos = new ArrayList<>();
435 ActionNxLoadMetadata actionLoadMeta = new ActionNxLoadMetadata(MetaDataUtil
436 .getVpnIdMetadata(routerId), LOAD_START, LOAD_END);
437 actionsInfos.add(actionLoadMeta);
438 actionsInfos.add(new ActionNxResubmit(NwConstants.PSNAT_TABLE));
439 List<InstructionInfo> instructions = new ArrayList<>();
440 instructions.add(new InstructionApplyActions(actionsInfos));
441 String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
442 NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
443 NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
446 private void removeTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
447 Long routerId) throws ExecutionException, InterruptedException {
448 LOG.info("removeTerminatingServiceTblEntry : creating entry for Terminating Service Table "
449 + "for switch {}, routerId {}", dpnId, routerId);
451 String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
452 NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
455 protected void addSnatMissEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
456 Long routerId, String routerName, BigInteger primarySwitchId) {
457 LOG.debug("installSnatMissEntry : Installing SNAT miss entry in switch {}", dpnId);
458 List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
459 String ifNamePrimary = NatUtil.getTunnelInterfaceName(dpnId, primarySwitchId, itmManager);
460 List<BucketInfo> listBucketInfo = new ArrayList<>();
461 if (ifNamePrimary != null) {
462 LOG.debug("installSnatMissEntry : On Non- Napt switch , Primary Tunnel interface is {}", ifNamePrimary);
463 listActionInfoPrimary = NatUtil.getEgressActionsForInterface(odlInterfaceRpcService, itmManager,
464 interfaceManager, ifNamePrimary, routerId, true);
466 BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
467 listBucketInfo.add(0, bucketPrimary);
468 LOG.debug("installSnatMissEntry : installSnatMissEntry called for dpnId {} with primaryBucket {} ", dpnId,
469 listBucketInfo.get(0));
470 // Install the select group
471 long groupId = createGroupId(getGroupIdKey(routerName));
473 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll,
475 LOG.debug("installing the PSNAT to NAPTSwitch GroupEntity:{} with GroupId: {}", groupEntity, groupId);
476 mdsalManager.addGroup(confTx, groupEntity);
478 // Add the flow to send the packet to the group only after group is available in Config datastore
479 eventCallbacks.onAddOrUpdate(LogicalDatastoreType.CONFIGURATION,
480 NatUtil.getGroupInstanceId(dpnId, groupId), (unused, newGroupId) -> {
481 LOG.info("group {} is created in the config", groupId);
482 ListenableFutures.addErrorLogging(
483 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
484 innerConfTx -> addSnatMissFlowForGroup(innerConfTx, dpnId, routerId, groupId)),
485 LOG, "Error adding flow for the group {}",groupId);
486 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
487 }, Duration.ofSeconds(5), iid -> LOG.error("groupId {} not found in config datastore", groupId));
490 private void addSnatMissFlowForGroup(TypedReadWriteTransaction<Configuration> confTx,
491 BigInteger dpnId, Long routerId, long groupId) {
492 // Install miss entry pointing to group
493 LOG.debug("installSnatMissEntry : buildSnatFlowEntity is called for dpId {}, routerId {} and groupId {}",
494 dpnId, routerId, groupId);
495 List<MatchInfo> matches = new ArrayList<>();
496 matches.add(new MatchEthernetType(0x0800L));
497 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(routerId), MetaDataUtil.METADATA_MASK_VRFID));
500 List<ActionInfo> actionsInfo = new ArrayList<>();
501 actionsInfo.add(new ActionSetFieldTunnelId(BigInteger.valueOf(routerId)));
502 LOG.debug("installSnatMissEntry : Setting the tunnel to the list of action infos {}", actionsInfo);
503 actionsInfo.add(new ActionGroup(groupId));
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,
512 protected void removeSnatMissEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
513 Long routerId, String routerName) throws ExecutionException, InterruptedException {
514 LOG.debug("installSnatMissEntry : Removing SNAT miss entry from switch {}", dpnId);
515 // Install the select group
516 long groupId = createGroupId(getGroupIdKey(routerName));
518 LOG.debug("removing the PSNAT to NAPTSwitch on DPN {} with GroupId: {}", dpnId, groupId);
519 mdsalManager.removeGroup(confTx, dpnId, groupId);
521 // Install miss entry pointing to group
522 LOG.debug("installSnatMissEntry : buildSnatFlowEntity is called for dpId {}, routerName {} and groupId {}",
523 dpnId, routerName, groupId);
525 String flowRef = getFlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
526 NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef);
529 private void addInboundTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
530 BigInteger dpnId, Long routerId, long extSubnetId) {
532 //Install the tunnel table entry in NAPT switch for inbound traffic to SNAT IP from a non a NAPT switch.
533 LOG.info("installInboundTerminatingServiceTblEntry : creating entry for Terminating Service Table "
534 + "for switch {}, routerId {}", dpnId, routerId);
535 List<MatchInfo> matches = new ArrayList<>();
536 matches.add(MatchEthernetType.IPV4);
537 List<ActionInfo> actionsInfos = new ArrayList<>();
538 if (extSubnetId == NatConstants.INVALID_ID) {
539 LOG.error("installInboundTerminatingServiceTblEntry : external subnet id is invalid.");
542 matches.add(new MatchTunnelId(BigInteger.valueOf(extSubnetId)));
543 ActionNxLoadMetadata actionLoadMeta = new ActionNxLoadMetadata(MetaDataUtil
544 .getVpnIdMetadata(extSubnetId), LOAD_START, LOAD_END);
545 actionsInfos.add(actionLoadMeta);
546 actionsInfos.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
547 List<InstructionInfo> instructions = new ArrayList<>();
548 instructions.add(new InstructionApplyActions(actionsInfos));
549 String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId.longValue()) + "INBOUND";
550 NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
551 NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
554 private void removeInboundTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
555 BigInteger dpnId, Long routerId) throws ExecutionException, InterruptedException {
556 //Install the tunnel table entry in NAPT switch for inbound traffic to SNAT IP from a non a NAPT switch.
557 LOG.info("installInboundTerminatingServiceTblEntry : creating entry for Terminating Service Table "
558 + "for switch {}, routerId {}", dpnId, routerId);
559 String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId.longValue()) + "INBOUND";
560 NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
563 private void addDefaultFibRouteForSNAT(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
566 List<MatchInfo> matches = new ArrayList<>();
567 matches.add(MatchEthernetType.IPV4);
568 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(extNetId),
569 MetaDataUtil.METADATA_MASK_VRFID));
571 List<InstructionInfo> instructions = new ArrayList<>();
572 instructions.add(new InstructionGotoTable(NwConstants.PSNAT_TABLE));
574 String flowRef = "DefaultFibRouteForSNAT" + getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, extNetId);
575 NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef,
576 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches,
580 private void removeDefaultFibRouteForSNAT(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
581 Long extNetId) throws ExecutionException, InterruptedException {
582 String flowRef = "DefaultFibRouteForSNAT" + getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, extNetId);
583 NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef);
586 protected String getFlowRef(BigInteger dpnId, short tableId, long routerID) {
587 return NatConstants.NAPT_FLOWID_PREFIX + dpnId + NatConstants.FLOWID_SEPARATOR
588 + tableId + NatConstants.FLOWID_SEPARATOR + routerID;
591 protected long createGroupId(String groupIdKey) {
592 AllocateIdInput getIdInput = new AllocateIdInputBuilder()
593 .setPoolName(NatConstants.SNAT_IDPOOL_NAME).setIdKey(groupIdKey)
596 Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
597 RpcResult<AllocateIdOutput> rpcResult = result.get();
598 return rpcResult.getResult().getIdValue();
599 } catch (NullPointerException | InterruptedException | ExecutionException e) {
600 LOG.error("createGroupId: Exception while creating group with key : {}",groupIdKey, e);
605 protected String getGroupIdKey(String routerName) {
606 return "snatmiss." + routerName;
609 private void removeMipAdjacencies(Routers routers) {
610 LOG.info("removeMipAdjacencies for router {}", routers.getRouterName());
611 String externalSubNetId = null;
612 for (ExternalIps externalIp : routers.nonnullExternalIps()) {
613 if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
614 // In this class we handle only IPv4 use-cases.
617 externalSubNetId = externalIp.getSubnetId().getValue();
620 if (externalSubNetId == null) {
621 LOG.info("removeMipAdjacencies no external Ipv4 address present on router {}",
622 routers.getRouterName());
625 InstanceIdentifier<VpnInterfaces> vpnInterfacesId =
626 InstanceIdentifier.builder(VpnInterfaces.class).build();
628 VpnInterfaces vpnInterfaces = SingleTransactionDataBroker.syncRead(dataBroker,
629 LogicalDatastoreType.CONFIGURATION, vpnInterfacesId);
630 List<VpnInterface> updatedVpnInterface = new ArrayList<>();
631 for (VpnInterface vpnInterface : vpnInterfaces.nonnullVpnInterface()) {
632 List<Adjacency> updatedAdjacencies = new ArrayList<>();
633 Adjacencies adjacencies = vpnInterface.augmentation(Adjacencies.class);
634 if (null != adjacencies) {
635 for (Adjacency adjacency : adjacencies.nonnullAdjacency()) {
636 if (!adjacency.getSubnetId().getValue().equals(externalSubNetId)) {
637 updatedAdjacencies.add(adjacency);
641 AdjacenciesBuilder adjacenciesBuilder = new AdjacenciesBuilder();
642 adjacenciesBuilder.setAdjacency(updatedAdjacencies);
643 VpnInterfaceBuilder vpnInterfaceBuilder = new VpnInterfaceBuilder(vpnInterface);
644 vpnInterfaceBuilder.addAugmentation(Adjacencies.class, adjacenciesBuilder.build());
645 updatedVpnInterface.add(vpnInterfaceBuilder.build());
647 VpnInterfacesBuilder vpnInterfacesBuilder = new VpnInterfacesBuilder();
648 vpnInterfacesBuilder.setVpnInterface(updatedVpnInterface);
650 SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
651 vpnInterfacesId, vpnInterfacesBuilder.build());
652 } catch (ReadFailedException e) {
653 LOG.warn("Failed to read removeMipAdjacencies with error {}", e.getMessage());
654 } catch (TransactionCommitFailedException e) {
655 LOG.warn("Failed to remove removeMipAdjacencies with error {}", e.getMessage());
659 private void removeLearntIpPorts(Routers routers) {
660 LOG.info("removeLearntIpPorts for router {} and network {}", routers.getRouterName(), routers.getNetworkId());
661 String networkId = routers.getNetworkId().getValue();
662 LearntVpnVipToPortData learntVpnVipToPortData = NatUtil.getLearntVpnVipToPortData(dataBroker);
663 if (learntVpnVipToPortData == null) {
664 LOG.info("removeLearntIpPorts, no learned ports present");
667 LearntVpnVipToPortDataBuilder learntVpnVipToPortDataBuilder = new LearntVpnVipToPortDataBuilder();
668 List<LearntVpnVipToPort> learntVpnVipToPortList = new ArrayList<>();
669 for (LearntVpnVipToPort learntVpnVipToPort : learntVpnVipToPortData.nonnullLearntVpnVipToPort()) {
670 if (!networkId.equals(learntVpnVipToPort.getVpnName())) {
671 LOG.info("The learned port belongs to Vpn {} hence not removing", learntVpnVipToPort.getVpnName());
672 learntVpnVipToPortList.add(learntVpnVipToPort);
674 String externalSubNetId = null;
675 for (ExternalIps externalIp : routers.nonnullExternalIps()) {
676 if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
677 // In this class we handle only IPv4 use-cases.
680 externalSubNetId = externalIp.getSubnetId().getValue();
683 if (externalSubNetId == null) {
684 LOG.info("removeLearntIpPorts no external Ipv4 address present on router {}",
685 routers.getRouterName());
688 String prefix = learntVpnVipToPort.getPortFixedip() + "/32";
689 NatUtil.deletePrefixToInterface(dataBroker, NatUtil.getVpnId(dataBroker,
690 externalSubNetId), prefix);
695 learntVpnVipToPortDataBuilder.setLearntVpnVipToPort(learntVpnVipToPortList);
696 InstanceIdentifier<LearntVpnVipToPortData> learntVpnVipToPortDataId = NatUtil
697 .getLearntVpnVipToPortDataId();
698 SingleTransactionDataBroker.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
699 learntVpnVipToPortDataId, learntVpnVipToPortDataBuilder.build());
701 } catch (TransactionCommitFailedException e) {
702 LOG.warn("Failed to remove removeLearntIpPorts with error {}", e.getMessage());
706 static int mostSignificantBit(int value) {
707 return 31 - Integer.numberOfLeadingZeros(value);