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;
19 import java.util.Optional;
20 import java.util.concurrent.ExecutionException;
21 import javax.annotation.PreDestroy;
22 import javax.inject.Inject;
23 import javax.inject.Singleton;
24 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
25 import org.opendaylight.genius.infra.Datastore.Configuration;
26 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
27 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
28 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
29 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
30 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
31 import org.opendaylight.infrautils.utils.concurrent.Executors;
32 import org.opendaylight.mdsal.binding.api.DataBroker;
33 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
34 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
35 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
36 import org.opendaylight.netvirt.natservice.api.SnatServiceManager;
37 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.DpnEndpoints;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfo;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryOutput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpn.routers.DpnRoutersList;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpn.routers.dpn.routers.list.RoutersList;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig.NatMode;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.PortsKey;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMapKey;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.opendaylight.yangtools.yang.common.RpcResult;
61 import org.opendaylight.yangtools.yang.common.Uint32;
62 import org.opendaylight.yangtools.yang.common.Uint64;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
67 public class NatTepChangeListener extends AbstractAsyncDataTreeChangeListener<TunnelEndPoints> {
69 private static final Logger LOG = LoggerFactory
70 .getLogger(NatTepChangeListener.class);
71 private final DataBroker dataBroker;
72 private SNATDefaultRouteProgrammer defaultRouteProgrammer;
73 private OdlInterfaceRpcService interfaceService;
74 private IdManagerService idManager;
75 private IFibManager fibManager;
76 private IBgpManager bgpManager;
77 private IMdsalApiManager mdsalManager;
78 private FloatingIPListener floatingIPListener;
79 private FibRpcService fibRpcService;
80 private NaptSwitchHA naptSwitchHA;
81 private final NatMode natMode;
82 private final SnatServiceManager natServiceManager;
83 private final JobCoordinator coordinator;
84 private final ManagedNewTransactionRunner txRunner;
85 private final NatOverVxlanUtil natOverVxlanUtil;
88 public NatTepChangeListener(final DataBroker dataBroker,
89 final SNATDefaultRouteProgrammer defaultRouteProgrammer,
90 final OdlInterfaceRpcService interfaceService,
91 final IdManagerService idManager, final IFibManager fibManager,
92 final IBgpManager bgpManager,
93 final FloatingIPListener floatingIPListener,
94 final FibRpcService fibRpcService,
95 final IMdsalApiManager mdsalManager,
96 final NaptSwitchHA naptSwitchHA,
97 final NatserviceConfig config,
98 final SnatServiceManager natServiceManager,
99 final JobCoordinator coordinator,
100 final NatOverVxlanUtil natOverVxlanUtil) {
101 super(dataBroker, LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.builder(DpnEndpoints.class)
102 .child(DPNTEPsInfo.class).child(TunnelEndPoints.class).build(),
103 Executors.newListeningSingleThreadExecutor("NatTepChangeListener", LOG));
104 this.dataBroker = dataBroker;
105 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
106 this.defaultRouteProgrammer = defaultRouteProgrammer;
107 this.interfaceService = interfaceService;
108 this.idManager = idManager;
109 this.fibManager = fibManager;
110 this.bgpManager = bgpManager;
111 this.floatingIPListener = floatingIPListener;
112 this.fibRpcService = fibRpcService;
113 this.naptSwitchHA = naptSwitchHA;
114 this.mdsalManager = mdsalManager;
115 this.coordinator = coordinator;
116 this.natServiceManager = natServiceManager;
117 this.natOverVxlanUtil = natOverVxlanUtil;
118 if (config != null) {
119 this.natMode = config.getNatMode();
121 this.natMode = NatMode.Controller;
126 LOG.info("{} init", getClass().getSimpleName());
131 public void close() {
133 Executors.shutdownAndAwaitTermination(getExecutorService());
137 public void remove(InstanceIdentifier<TunnelEndPoints> key,
138 TunnelEndPoints tep) {
140 * Whenever the TEP on a given DPNID is removed, this API take care
141 * of withdrawing the FIB entries for those Floating-IP existing on this
142 * DPN and perform re-election of NAPT Switch for a VRF to which the current
143 * DPN is elected as NAPT Switch.
145 Uint64 srcDpnId = key.firstIdentifierOf(DPNTEPsInfo.class)
146 .firstKeyOf(DPNTEPsInfo.class).getDPNID();
147 final String srcTepIp = tep.getIpAddress().stringValue();
148 String tunnelType = tep.getTunnelType().getName();
150 "NAT Service : Remove Event triggered for Tep on DPN:{} having IP:{} and tunnelType:{}",
151 srcDpnId, srcTepIp, tunnelType);
152 handleTepDelForAllRtrs(srcDpnId, srcTepIp);
156 public void update(InstanceIdentifier<TunnelEndPoints> key,
157 TunnelEndPoints origTep, TunnelEndPoints updatedTep) {
158 // Will be handled in NatTunnelInterfaceStateListener.add()
159 LOG.debug("NO ACTION duing update event : {}", updatedTep.key());
163 public void add(InstanceIdentifier<TunnelEndPoints> key,
164 TunnelEndPoints tep) {
165 LOG.debug("NO ACTION duing add event : {}", tep.key());
168 @SuppressWarnings("checkstyle:IllegalCatch")
169 private void handleTepDelForAllRtrs(Uint64 srcDpnId, String srcTepIp) {
170 LOG.trace("handleTepDelForAllRtrs : TEP DEL ----- on DPN-ID {} having SRC IP : {}",
174 List<RoutersList> routersList = null;
175 InstanceIdentifier<DpnRoutersList> dpnRoutersListId = NatUtil.getDpnRoutersId(srcDpnId);
176 Optional<DpnRoutersList> optionalRouterDpnList =
177 SingleTransactionDataBroker
178 .syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
179 LogicalDatastoreType.OPERATIONAL, dpnRoutersListId);
180 if (optionalRouterDpnList.isPresent()) {
181 routersList = new ArrayList<RoutersList>(optionalRouterDpnList.get().getRoutersList().values());
184 "NAT Service : RouterDpnList is empty for DPN {}. Hence ignoring TEP DEL event",
189 if (routersList == null) {
190 LOG.error("handleTepDelForAllRtrs : DPN {} does not have the Routers presence",
195 for (RoutersList router : routersList) {
196 String routerName = router.getRouter();
198 "handleTepDelForAllRtrs : TEP DEL : DNAT -> Withdrawing routes for router {} ",
200 Uint32 routerId = NatUtil.getVpnId(dataBroker, routerName);
201 if (routerId == NatConstants.INVALID_ID) {
202 LOG.error("handleTepDelForAllRtrs :Invalid ROUTER-ID {} returned for routerName {}",
203 routerId, routerName);
206 Uuid externalNetworkId = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
207 ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker,
208 routerName, externalNetworkId);
209 if (extNwProvType == null) {
212 boolean isFipExists = hndlTepDelForDnatInEachRtr(router, routerId, srcDpnId,
215 "handleTepDelForAllRtrs : TEP DEL : SNAT -> Withdrawing and Advertising routes for router {} ",
217 coordinator.enqueueJob((NatConstants.NAT_DJC_PREFIX + router.getRouter()), () -> {
218 List<ListenableFuture<Void>> futures = new ArrayList<>();
219 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx -> {
220 hndlTepDelForSnatInEachRtr(router, routerId, srcDpnId, srcTepIp, isFipExists,
221 extNwProvType, configTx);
225 }, NatConstants.NAT_DJC_MAX_RETRIES);
230 private boolean hndlTepDelForDnatInEachRtr(RoutersList router, Uint32 routerId,
231 Uint64 tepDeletedDpnId,
232 ProviderTypes extNwProvType) {
233 //DNAT : Withdraw the routes from the BGP
234 String routerName = router.getRouter();
235 Boolean isFipExists = Boolean.FALSE;
237 LOG.debug("hndlTepDelForDnatInEachRtr : DNAT -> Trying to clear routes to the Floating IP "
238 + "associated to the router {}", routerName);
240 InstanceIdentifier<RouterPorts> routerPortsId = NatUtil.getRouterPortsId(routerName);
241 Optional<RouterPorts> optRouterPorts;
243 optRouterPorts = SingleTransactionDataBroker.syncReadOptional(dataBroker,
244 LogicalDatastoreType.CONFIGURATION, routerPortsId);
245 } catch (ExecutionException | InterruptedException e) {
246 LOG.error("hndlTepDelForDnatInEachRtr: Exception while reading RouterPorts DS for the router {}",
250 if (!optRouterPorts.isPresent()) {
252 "hndlTepDelForDnatInEachRtr : DNAT -> Could not read Router Ports data object with id: {} "
253 + "from DNAT FloatingIpInfo", routerName);
256 RouterPorts routerPorts = optRouterPorts.get();
257 Uuid extNwId = routerPorts.getExternalNetworkId();
258 final String vpnName = NatUtil.getAssociatedVPN(dataBroker, extNwId);
259 if (vpnName == null) {
261 "hndlTepDelForDnatInEachRtr : DNAT -> No External VPN associated with Ext N/W {} for Router {}",
262 extNwId, routerName);
265 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
266 if (extNwProvType == null) {
269 Uint32 l3Vni = Uint32.ZERO;
270 if (extNwProvType == ProviderTypes.VXLAN) {
271 //get l3Vni value for external VPN
272 l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
273 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
275 "hndlTepDelForDnatInEachRtr : L3VNI value is not configured in Internet VPN {} and RD {} "
276 + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue to installing "
277 + "NAT flows", vpnName, rd);
278 l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId);
281 Map<PortsKey, Ports> interfacesMap = routerPorts.getPorts();
282 for (Ports port : interfacesMap.values()) {
283 //Get the DPN on which this interface resides
284 String interfaceName = port.getPortName();
285 Uint64 fipCfgdDpnId = NatUtil.getDpnForInterface(interfaceService, interfaceName);
286 if (fipCfgdDpnId.equals(Uint64.ZERO)) {
288 "hndlTepDelForDnatInEachRtr : DNAT -> Abort processing Floating ip configuration. "
289 + "No DPN for port : {}", interfaceName);
292 if (!fipCfgdDpnId.equals(tepDeletedDpnId)) {
294 "hndlTepDelForDnatInEachRtr : DNAT -> TEP deleted DPN {} is not the DPN {} which has the "
295 + "floating IP configured for the port: {}",
296 tepDeletedDpnId, fipCfgdDpnId, interfaceName);
299 isFipExists = Boolean.TRUE;
300 Map<InternalToExternalPortMapKey, InternalToExternalPortMap> keyInternalToExternalPortMapMap
301 = port.getInternalToExternalPortMap();
302 for (InternalToExternalPortMap intExtPortMap : keyInternalToExternalPortMapMap.values()) {
303 String internalIp = intExtPortMap.getInternalIp();
304 String externalIp = intExtPortMap.getExternalIp();
305 externalIp = NatUtil.validateAndAddNetworkMask(externalIp);
307 "hndlTepDelForDnatInEachRtr : DNAT -> Withdrawing the FIB route to the floating IP {} "
308 + "configured for the port: {}",
309 externalIp, interfaceName);
310 NatUtil.removePrefixFromBGP(bgpManager, fibManager, rd, externalIp, vpnName);
311 Uint32 serviceId = null;
312 if (extNwProvType == ProviderTypes.VXLAN) {
315 serviceId = floatingIPListener
316 .getOperationalIpMapping(routerName, interfaceName, internalIp);
317 if (serviceId == null || serviceId == NatConstants.INVALID_ID) {
319 "hndlTepDelForDnatInEachRtr : DNAT -> Unable to remove the table 21 entry pushing the"
320 + " MPLS label to the tunnel since label is invalid");
325 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName)
326 .setSourceDpid(fipCfgdDpnId).setIpAddress(externalIp).setServiceId(serviceId)
327 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.FloatingIP).build();
328 ListenableFuture<RpcResult<RemoveFibEntryOutput>> future = fibRpcService
329 .removeFibEntry(input);
331 Futures.addCallback(future, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
334 public void onFailure(Throwable error) {
336 "hndlTepDelForDnatInEachRtr : DNAT -> Error in removing the table 21 entry pushing "
337 + "the MPLS label to the tunnel since label is invalid ", error);
341 public void onSuccess(RpcResult<RemoveFibEntryOutput> result) {
342 if (result.isSuccessful()) {
344 "hndlTepDelForDnatInEachRtr : DNAT -> Successfully removed the entry pushing the "
345 + "MPLS label to the tunnel");
348 "hndlTepDelForDnatInEachRtr : DNAT -> Error in fib rpc call to remove the table "
349 + "21 entry pushing the MPLS label to the tunnnel due to {}",
353 }, MoreExecutors.directExecutor());
359 private void hndlTepDelForSnatInEachRtr(RoutersList router, Uint32 routerId, Uint64 dpnId,
360 String srcTepIp, Boolean isFipExists, ProviderTypes extNwProvType,
361 TypedReadWriteTransaction<Configuration> confTx)
362 throws ExecutionException, InterruptedException {
365 1) Elect a new switch as the primary NAPT
366 2) Advertise the new routes to BGP for the newly elected TEP IP as the DPN IP
367 3) This will make sure old routes are withdrawn and new routes are advertised.
370 String routerName = router.getRouter();
372 "hndlTepDelForSnatInEachRtr : SNAT -> Trying to clear routes to the External fixed IP associated "
373 + "to the router {}", routerName);
375 // Check if this is externalRouter else ignore
376 InstanceIdentifier<Routers> extRoutersId = NatUtil.buildRouterIdentifier(routerName);
377 Optional<Routers> routerData = Optional.empty();
379 routerData = confTx.read(extRoutersId).get();
380 } catch (InterruptedException | ExecutionException e) {
381 LOG.error("Error retrieving routers {}", extRoutersId, e);
383 if (!routerData.isPresent()) {
385 "hndlTepDelForSnatInEachRtr : SNAT->Ignoring TEP del for router {} since its not External Router",
390 //Check if the DPN having the router is the NAPT switch
391 Uint64 naptId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
392 if (naptId == null || naptId.equals(Uint64.ZERO) || !naptId.equals(dpnId)) {
394 "hndlTepDelForSnatInEachRtr : SNAT -> Ignoring TEP delete for the DPN {} since"
395 + "srcTepIp : {} is NOT a NAPT switch", dpnId, srcTepIp);
398 if (natMode == NatMode.Conntrack) {
399 Routers extRouter = routerData.get();
400 natServiceManager.notify(confTx, extRouter, null, naptId, dpnId,
401 SnatServiceManager.Action.CNT_ROUTER_DISBL);
402 if (extRouter.isEnableSnat()) {
403 natServiceManager.notify(confTx, extRouter, null, naptId, dpnId,
404 SnatServiceManager.Action.SNAT_ROUTER_DISBL);
408 Uuid networkId = routerData.get().getNetworkId();
409 if (networkId == null) {
411 "hndlTepDelForSnatInEachRtr : SNAT -> Ignoring TEP delete for the DPN {} for router {}"
412 + "as external network configuraton is missing", dpnId, routerName);
416 LOG.debug("hndlTepDelForSnatInEachRtr : SNAT->Router {} is associated with ext nw {}",
417 routerId, networkId);
418 Uuid bgpVpnUuid = NatUtil.getVpnForRouter(dataBroker, routerName);
420 if (bgpVpnUuid == null) {
422 "hndlTepDelForSnatInEachRtr : SNAT->Internal VPN-ID {} associated to router {}",
423 routerId, routerName);
426 bgpVpnId = NatUtil.getVpnId(dataBroker, bgpVpnUuid.getValue());
427 if (bgpVpnId == NatConstants.INVALID_ID) {
429 "hndlTepDelForSnatInEachRtr :SNAT->Invalid Private BGP VPN ID returned for routerName {}",
435 // Remove default entry in FIB to SNAT table
437 "NAT Service : Installing default route in FIB on DPN {} for router {} with"
439 dpnId, routerName, bgpVpnId);
440 defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, bgpVpnId, routerId, confTx);
443 if (routerData.get().isEnableSnat()) {
444 LOG.info("hndlTepDelForSnatInEachRtr : SNAT enabled for router {}", routerId);
446 Uint32 routerVpnId = routerId;
447 if (bgpVpnId != NatConstants.INVALID_ID) {
449 "hndlTepDelForSnatInEachRtr : SNAT -> Private BGP VPN ID (Internal BGP VPN ID) {} "
450 + "associated to the router {}", bgpVpnId, routerName);
451 routerVpnId = bgpVpnId;
454 "hndlTepDelForSnatInEachRtr : SNAT -> Internal L3 VPN ID (Router ID) {} "
455 + "associated to the router {}", routerVpnId, routerName);
457 //Re-elect the other available switch as the NAPT switch and program the NAT flows.
458 String externalVpnName = NatUtil.getAssociatedVPN(dataBroker,
459 routerData.get().getNetworkId());
460 NatUtil.removeSNATFromDPN(dataBroker, mdsalManager, idManager, naptSwitchHA, dpnId,
461 routerData.get(), routerId, routerVpnId, externalVpnName, extNwProvType, confTx);
464 "hndlTepDelForSnatInEachRtr : SNAT is not enabled for router {} to handle addDPN event {}",