2 * Copyright (c) 2019 Ericsson India Global Services Pvt Ltd. 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 com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Optional;
19 import java.util.concurrent.ExecutionException;
20 import javax.annotation.PreDestroy;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
24 import org.opendaylight.genius.infra.Datastore.Configuration;
25 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
26 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
27 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
28 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
29 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
30 import org.opendaylight.infrautils.utils.concurrent.Executors;
31 import org.opendaylight.mdsal.binding.api.DataBroker;
32 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
33 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
34 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
35 import org.opendaylight.netvirt.natservice.api.SnatServiceManager;
36 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.DpnEndpoints;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfo;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryOutput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpn.routers.DpnRoutersList;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpn.routers.dpn.routers.list.RoutersList;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig.NatMode;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
56 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
57 import org.opendaylight.yangtools.yang.common.RpcResult;
58 import org.opendaylight.yangtools.yang.common.Uint32;
59 import org.opendaylight.yangtools.yang.common.Uint64;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
64 public class NatTepChangeListener extends AbstractAsyncDataTreeChangeListener<TunnelEndPoints> {
66 private static final Logger LOG = LoggerFactory
67 .getLogger(NatTepChangeListener.class);
68 private final DataBroker dataBroker;
69 private SNATDefaultRouteProgrammer defaultRouteProgrammer;
70 private OdlInterfaceRpcService interfaceService;
71 private IdManagerService idManager;
72 private IFibManager fibManager;
73 private IBgpManager bgpManager;
74 private IMdsalApiManager mdsalManager;
75 private FloatingIPListener floatingIPListener;
76 private FibRpcService fibRpcService;
77 private NaptSwitchHA naptSwitchHA;
78 private final NatMode natMode;
79 private final SnatServiceManager natServiceManager;
80 private final JobCoordinator coordinator;
81 private final ManagedNewTransactionRunner txRunner;
82 private final NatOverVxlanUtil natOverVxlanUtil;
85 public NatTepChangeListener(final DataBroker dataBroker,
86 final SNATDefaultRouteProgrammer defaultRouteProgrammer,
87 final OdlInterfaceRpcService interfaceService,
88 final IdManagerService idManager, final IFibManager fibManager,
89 final IBgpManager bgpManager,
90 final FloatingIPListener floatingIPListener,
91 final FibRpcService fibRpcService,
92 final IMdsalApiManager mdsalManager,
93 final NaptSwitchHA naptSwitchHA,
94 final NatserviceConfig config,
95 final SnatServiceManager natServiceManager,
96 final JobCoordinator coordinator,
97 final NatOverVxlanUtil natOverVxlanUtil) {
98 super(dataBroker, LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.builder(DpnEndpoints.class)
99 .child(DPNTEPsInfo.class).child(TunnelEndPoints.class).build(),
100 Executors.newListeningSingleThreadExecutor("NatTepChangeListener", LOG));
101 this.dataBroker = dataBroker;
102 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
103 this.defaultRouteProgrammer = defaultRouteProgrammer;
104 this.interfaceService = interfaceService;
105 this.idManager = idManager;
106 this.fibManager = fibManager;
107 this.bgpManager = bgpManager;
108 this.floatingIPListener = floatingIPListener;
109 this.fibRpcService = fibRpcService;
110 this.naptSwitchHA = naptSwitchHA;
111 this.mdsalManager = mdsalManager;
112 this.coordinator = coordinator;
113 this.natServiceManager = natServiceManager;
114 this.natOverVxlanUtil = natOverVxlanUtil;
115 if (config != null) {
116 this.natMode = config.getNatMode();
118 this.natMode = NatMode.Controller;
123 LOG.info("{} init", getClass().getSimpleName());
128 public void close() {
130 Executors.shutdownAndAwaitTermination(getExecutorService());
134 public void remove(InstanceIdentifier<TunnelEndPoints> key,
135 TunnelEndPoints tep) {
137 * Whenever the TEP on a given DPNID is removed, this API take care
138 * of withdrawing the FIB entries for those Floating-IP existing on this
139 * DPN and perform re-election of NAPT Switch for a VRF to which the current
140 * DPN is elected as NAPT Switch.
142 Uint64 srcDpnId = key.firstIdentifierOf(DPNTEPsInfo.class)
143 .firstKeyOf(DPNTEPsInfo.class).getDPNID();
144 final String srcTepIp = tep.getIpAddress().stringValue();
145 String tunnelType = tep.getTunnelType().getName();
147 "NAT Service : Remove Event triggered for Tep on DPN:{} having IP:{} and tunnelType:{}",
148 srcDpnId, srcTepIp, tunnelType);
149 handleTepDelForAllRtrs(srcDpnId, srcTepIp);
153 public void update(InstanceIdentifier<TunnelEndPoints> key,
154 TunnelEndPoints origTep, TunnelEndPoints updatedTep) {
155 // Will be handled in NatTunnelInterfaceStateListener.add()
156 LOG.debug("NO ACTION duing update event : {}", updatedTep.key());
160 public void add(InstanceIdentifier<TunnelEndPoints> key,
161 TunnelEndPoints tep) {
162 LOG.debug("NO ACTION duing add event : {}", tep.key());
165 @SuppressWarnings("checkstyle:IllegalCatch")
166 private void handleTepDelForAllRtrs(Uint64 srcDpnId, String srcTepIp) {
167 LOG.trace("handleTepDelForAllRtrs : TEP DEL ----- on DPN-ID {} having SRC IP : {}",
171 List<RoutersList> routersList = null;
172 InstanceIdentifier<DpnRoutersList> dpnRoutersListId = NatUtil.getDpnRoutersId(srcDpnId);
173 Optional<DpnRoutersList> optionalRouterDpnList =
174 SingleTransactionDataBroker
175 .syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
176 LogicalDatastoreType.OPERATIONAL, dpnRoutersListId);
177 if (optionalRouterDpnList.isPresent()) {
178 routersList = optionalRouterDpnList.get().getRoutersList();
181 "NAT Service : RouterDpnList is empty for DPN {}. Hence ignoring TEP DEL event",
186 if (routersList == null) {
187 LOG.error("handleTepDelForAllRtrs : DPN {} does not have the Routers presence",
192 for (RoutersList router : routersList) {
193 String routerName = router.getRouter();
195 "handleTepDelForAllRtrs : TEP DEL : DNAT -> Withdrawing routes for router {} ",
197 Uint32 routerId = NatUtil.getVpnId(dataBroker, routerName);
198 if (routerId == NatConstants.INVALID_ID) {
199 LOG.error("handleTepDelForAllRtrs :Invalid ROUTER-ID {} returned for routerName {}",
200 routerId, routerName);
203 Uuid externalNetworkId = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
204 ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker,
205 routerName, externalNetworkId);
206 if (extNwProvType == null) {
209 boolean isFipExists = hndlTepDelForDnatInEachRtr(router, routerId, srcDpnId,
212 "handleTepDelForAllRtrs : TEP DEL : SNAT -> Withdrawing and Advertising routes for router {} ",
214 coordinator.enqueueJob((NatConstants.NAT_DJC_PREFIX + router.getRouter()), () -> {
215 List<ListenableFuture<Void>> futures = new ArrayList<>();
216 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx -> {
217 hndlTepDelForSnatInEachRtr(router, routerId, srcDpnId, srcTepIp, isFipExists,
218 extNwProvType, configTx);
222 }, NatConstants.NAT_DJC_MAX_RETRIES);
227 private boolean hndlTepDelForDnatInEachRtr(RoutersList router, Uint32 routerId,
228 Uint64 tepDeletedDpnId,
229 ProviderTypes extNwProvType) {
230 //DNAT : Withdraw the routes from the BGP
231 String routerName = router.getRouter();
232 Boolean isFipExists = Boolean.FALSE;
234 LOG.debug("hndlTepDelForDnatInEachRtr : DNAT -> Trying to clear routes to the Floating IP "
235 + "associated to the router {}", routerName);
237 InstanceIdentifier<RouterPorts> routerPortsId = NatUtil.getRouterPortsId(routerName);
238 Optional<RouterPorts> optRouterPorts;
240 optRouterPorts = SingleTransactionDataBroker.syncReadOptional(dataBroker,
241 LogicalDatastoreType.CONFIGURATION, routerPortsId);
242 } catch (ExecutionException | InterruptedException e) {
243 LOG.error("hndlTepDelForDnatInEachRtr: Exception while reading RouterPorts DS for the router {}",
247 if (!optRouterPorts.isPresent()) {
249 "hndlTepDelForDnatInEachRtr : DNAT -> Could not read Router Ports data object with id: {} "
250 + "from DNAT FloatingIpInfo", routerName);
253 RouterPorts routerPorts = optRouterPorts.get();
254 Uuid extNwId = routerPorts.getExternalNetworkId();
255 final String vpnName = NatUtil.getAssociatedVPN(dataBroker, extNwId);
256 if (vpnName == null) {
258 "hndlTepDelForDnatInEachRtr : DNAT -> No External VPN associated with Ext N/W {} for Router {}",
259 extNwId, routerName);
262 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
263 if (extNwProvType == null) {
266 Uint32 l3Vni = Uint32.ZERO;
267 if (extNwProvType == ProviderTypes.VXLAN) {
268 //get l3Vni value for external VPN
269 l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
270 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
272 "hndlTepDelForDnatInEachRtr : L3VNI value is not configured in Internet VPN {} and RD {} "
273 + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue to installing "
274 + "NAT flows", vpnName, rd);
275 l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId);
278 List<Ports> interfaces = routerPorts.getPorts();
279 for (Ports port : interfaces) {
280 //Get the DPN on which this interface resides
281 String interfaceName = port.getPortName();
282 Uint64 fipCfgdDpnId = NatUtil.getDpnForInterface(interfaceService, interfaceName);
283 if (fipCfgdDpnId.equals(Uint64.ZERO)) {
285 "hndlTepDelForDnatInEachRtr : DNAT -> Abort processing Floating ip configuration. "
286 + "No DPN for port : {}", interfaceName);
289 if (!fipCfgdDpnId.equals(tepDeletedDpnId)) {
291 "hndlTepDelForDnatInEachRtr : DNAT -> TEP deleted DPN {} is not the DPN {} which has the "
292 + "floating IP configured for the port: {}",
293 tepDeletedDpnId, fipCfgdDpnId, interfaceName);
296 isFipExists = Boolean.TRUE;
297 List<InternalToExternalPortMap> intExtPortMapList = port.getInternalToExternalPortMap();
298 for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
299 String internalIp = intExtPortMap.getInternalIp();
300 String externalIp = intExtPortMap.getExternalIp();
301 externalIp = NatUtil.validateAndAddNetworkMask(externalIp);
303 "hndlTepDelForDnatInEachRtr : DNAT -> Withdrawing the FIB route to the floating IP {} "
304 + "configured for the port: {}",
305 externalIp, interfaceName);
306 NatUtil.removePrefixFromBGP(bgpManager, fibManager, rd, externalIp, vpnName);
307 Uint32 serviceId = null;
308 if (extNwProvType == ProviderTypes.VXLAN) {
311 serviceId = floatingIPListener
312 .getOperationalIpMapping(routerName, interfaceName, internalIp);
313 if (serviceId == null || serviceId == NatConstants.INVALID_ID) {
315 "hndlTepDelForDnatInEachRtr : DNAT -> Unable to remove the table 21 entry pushing the"
316 + " MPLS label to the tunnel since label is invalid");
321 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName)
322 .setSourceDpid(fipCfgdDpnId).setIpAddress(externalIp).setServiceId(serviceId)
323 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.FloatingIP).build();
324 ListenableFuture<RpcResult<RemoveFibEntryOutput>> future = fibRpcService
325 .removeFibEntry(input);
327 Futures.addCallback(future, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
330 public void onFailure(Throwable error) {
332 "hndlTepDelForDnatInEachRtr : DNAT -> Error in removing the table 21 entry pushing "
333 + "the MPLS label to the tunnel since label is invalid ", error);
337 public void onSuccess(RpcResult<RemoveFibEntryOutput> result) {
338 if (result.isSuccessful()) {
340 "hndlTepDelForDnatInEachRtr : DNAT -> Successfully removed the entry pushing the "
341 + "MPLS label to the tunnel");
344 "hndlTepDelForDnatInEachRtr : DNAT -> Error in fib rpc call to remove the table "
345 + "21 entry pushing the MPLS label to the tunnnel due to {}",
349 }, MoreExecutors.directExecutor());
355 private void hndlTepDelForSnatInEachRtr(RoutersList router, Uint32 routerId, Uint64 dpnId,
356 String srcTepIp, Boolean isFipExists, ProviderTypes extNwProvType,
357 TypedReadWriteTransaction<Configuration> confTx)
358 throws ExecutionException, InterruptedException {
361 1) Elect a new switch as the primary NAPT
362 2) Advertise the new routes to BGP for the newly elected TEP IP as the DPN IP
363 3) This will make sure old routes are withdrawn and new routes are advertised.
366 String routerName = router.getRouter();
368 "hndlTepDelForSnatInEachRtr : SNAT -> Trying to clear routes to the External fixed IP associated "
369 + "to the router {}", routerName);
371 // Check if this is externalRouter else ignore
372 InstanceIdentifier<Routers> extRoutersId = NatUtil.buildRouterIdentifier(routerName);
373 Optional<Routers> routerData = Optional.empty();
375 routerData = confTx.read(extRoutersId).get();
376 } catch (InterruptedException | ExecutionException e) {
377 LOG.error("Error retrieving routers {}", extRoutersId, e);
379 if (!routerData.isPresent()) {
381 "hndlTepDelForSnatInEachRtr : SNAT->Ignoring TEP del for router {} since its not External Router",
386 //Check if the DPN having the router is the NAPT switch
387 Uint64 naptId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
388 if (naptId == null || naptId.equals(Uint64.ZERO) || !naptId.equals(dpnId)) {
390 "hndlTepDelForSnatInEachRtr : SNAT -> Ignoring TEP delete for the DPN {} since"
391 + "srcTepIp : {} is NOT a NAPT switch", dpnId, srcTepIp);
394 if (natMode == NatMode.Conntrack) {
395 Routers extRouter = routerData.get();
396 natServiceManager.notify(confTx, extRouter, null, naptId, dpnId,
397 SnatServiceManager.Action.CNT_ROUTER_DISBL);
398 if (extRouter.isEnableSnat()) {
399 natServiceManager.notify(confTx, extRouter, null, naptId, dpnId,
400 SnatServiceManager.Action.SNAT_ROUTER_DISBL);
404 Uuid networkId = routerData.get().getNetworkId();
405 if (networkId == null) {
407 "hndlTepDelForSnatInEachRtr : SNAT -> Ignoring TEP delete for the DPN {} for router {}"
408 + "as external network configuraton is missing", dpnId, routerName);
412 LOG.debug("hndlTepDelForSnatInEachRtr : SNAT->Router {} is associated with ext nw {}",
413 routerId, networkId);
414 Uuid bgpVpnUuid = NatUtil.getVpnForRouter(dataBroker, routerName);
416 if (bgpVpnUuid == null) {
418 "hndlTepDelForSnatInEachRtr : SNAT->Internal VPN-ID {} associated to router {}",
419 routerId, routerName);
422 bgpVpnId = NatUtil.getVpnId(dataBroker, bgpVpnUuid.getValue());
423 if (bgpVpnId == NatConstants.INVALID_ID) {
425 "hndlTepDelForSnatInEachRtr :SNAT->Invalid Private BGP VPN ID returned for routerName {}",
431 // Remove default entry in FIB to SNAT table
433 "NAT Service : Installing default route in FIB on DPN {} for router {} with"
435 dpnId, routerName, bgpVpnId);
436 defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, bgpVpnId, routerId, confTx);
439 if (routerData.get().isEnableSnat()) {
440 LOG.info("hndlTepDelForSnatInEachRtr : SNAT enabled for router {}", routerId);
442 Uint32 routerVpnId = routerId;
443 if (bgpVpnId != NatConstants.INVALID_ID) {
445 "hndlTepDelForSnatInEachRtr : SNAT -> Private BGP VPN ID (Internal BGP VPN ID) {} "
446 + "associated to the router {}", bgpVpnId, routerName);
447 routerVpnId = bgpVpnId;
450 "hndlTepDelForSnatInEachRtr : SNAT -> Internal L3 VPN ID (Router ID) {} "
451 + "associated to the router {}", routerVpnId, routerName);
453 //Re-elect the other available switch as the NAPT switch and program the NAT flows.
454 String externalVpnName = NatUtil.getAssociatedVPN(dataBroker,
455 routerData.get().getNetworkId());
456 NatUtil.removeSNATFromDPN(dataBroker, mdsalManager, idManager, naptSwitchHA, dpnId,
457 routerData.get(), routerId, routerVpnId, externalVpnName, extNwProvType, confTx);
460 "hndlTepDelForSnatInEachRtr : SNAT is not enabled for router {} to handle addDPN event {}",