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.base.Optional;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.MoreExecutors;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import javax.annotation.PostConstruct;
22 import javax.inject.Inject;
23 import javax.inject.Singleton;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
28 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
29 import org.opendaylight.genius.infra.Datastore.Configuration;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
31 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
32 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
33 import org.opendaylight.genius.mdsalutil.MDSALUtil;
34 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
35 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
36 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
37 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
38 import org.opendaylight.netvirt.natservice.api.SnatServiceManager;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.DpnEndpoints;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfo;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryOutput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpn.routers.DpnRoutersList;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpn.routers.dpn.routers.list.RoutersList;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig.NatMode;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
58 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
59 import org.opendaylight.yangtools.yang.common.RpcResult;
60 import org.opendaylight.yangtools.yang.common.Uint32;
61 import org.opendaylight.yangtools.yang.common.Uint64;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
66 public class NatTepChangeListener extends
67 AsyncDataTreeChangeListenerBase<TunnelEndPoints, NatTepChangeListener> {
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(TunnelEndPoints.class, NatTepChangeListener.class);
102 this.dataBroker = dataBroker;
103 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
104 this.defaultRouteProgrammer = defaultRouteProgrammer;
105 this.interfaceService = interfaceService;
106 this.idManager = idManager;
107 this.fibManager = fibManager;
108 this.bgpManager = bgpManager;
109 this.floatingIPListener = floatingIPListener;
110 this.fibRpcService = fibRpcService;
111 this.naptSwitchHA = naptSwitchHA;
112 this.mdsalManager = mdsalManager;
113 this.coordinator = coordinator;
114 this.natServiceManager = natServiceManager;
115 this.natOverVxlanUtil = natOverVxlanUtil;
116 if (config != null) {
117 this.natMode = config.getNatMode();
119 this.natMode = NatMode.Controller;
126 LOG.info("{} init", getClass().getSimpleName());
127 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
131 protected InstanceIdentifier<TunnelEndPoints> getWildCardPath() {
132 return InstanceIdentifier.builder(DpnEndpoints.class)
133 .child(DPNTEPsInfo.class).child(TunnelEndPoints.class).build();
137 protected 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 protected 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 protected 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 = optionalRouterDpnList.get().getRoutersList();
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 = MDSALUtil.read(dataBroker, LogicalDatastoreType
242 .CONFIGURATION, routerPortsId);
243 if (!optRouterPorts.isPresent()) {
245 "hndlTepDelForDnatInEachRtr : DNAT -> Could not read Router Ports data object with id: {} "
246 + "from DNAT FloatingIpInfo", routerName);
249 RouterPorts routerPorts = optRouterPorts.get();
250 Uuid extNwId = routerPorts.getExternalNetworkId();
251 final String vpnName = NatUtil.getAssociatedVPN(dataBroker, extNwId);
252 if (vpnName == null) {
254 "hndlTepDelForDnatInEachRtr : DNAT -> No External VPN associated with Ext N/W {} for Router {}",
255 extNwId, routerName);
258 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
259 if (extNwProvType == null) {
262 Uint32 l3Vni = Uint32.ZERO;
263 if (extNwProvType == ProviderTypes.VXLAN) {
264 //get l3Vni value for external VPN
265 l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
266 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
268 "hndlTepDelForDnatInEachRtr : L3VNI value is not configured in Internet VPN {} and RD {} "
269 + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue to installing "
270 + "NAT flows", vpnName, rd);
271 l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId);
274 List<Ports> interfaces = routerPorts.getPorts();
275 for (Ports port : interfaces) {
276 //Get the DPN on which this interface resides
277 String interfaceName = port.getPortName();
278 Uint64 fipCfgdDpnId = NatUtil.getDpnForInterface(interfaceService, interfaceName);
279 if (fipCfgdDpnId.equals(Uint64.ZERO)) {
281 "hndlTepDelForDnatInEachRtr : DNAT -> Abort processing Floating ip configuration. "
282 + "No DPN for port : {}", interfaceName);
285 if (!fipCfgdDpnId.equals(tepDeletedDpnId)) {
287 "hndlTepDelForDnatInEachRtr : DNAT -> TEP deleted DPN {} is not the DPN {} which has the "
288 + "floating IP configured for the port: {}",
289 tepDeletedDpnId, fipCfgdDpnId, interfaceName);
292 isFipExists = Boolean.TRUE;
293 List<InternalToExternalPortMap> intExtPortMapList = port.getInternalToExternalPortMap();
294 for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
295 String internalIp = intExtPortMap.getInternalIp();
296 String externalIp = intExtPortMap.getExternalIp();
297 externalIp = NatUtil.validateAndAddNetworkMask(externalIp);
299 "hndlTepDelForDnatInEachRtr : DNAT -> Withdrawing the FIB route to the floating IP {} "
300 + "configured for the port: {}",
301 externalIp, interfaceName);
302 NatUtil.removePrefixFromBGP(bgpManager, fibManager, rd, externalIp, vpnName);
303 Uint32 serviceId = null;
304 if (extNwProvType == ProviderTypes.VXLAN) {
307 serviceId = floatingIPListener
308 .getOperationalIpMapping(routerName, interfaceName, internalIp);
309 if (serviceId == null || serviceId == NatConstants.INVALID_ID) {
311 "hndlTepDelForDnatInEachRtr : DNAT -> Unable to remove the table 21 entry pushing the"
312 + " MPLS label to the tunnel since label is invalid");
317 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName)
318 .setSourceDpid(fipCfgdDpnId).setIpAddress(externalIp).setServiceId(serviceId)
319 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.FloatingIP).build();
320 ListenableFuture<RpcResult<RemoveFibEntryOutput>> future = fibRpcService
321 .removeFibEntry(input);
323 Futures.addCallback(future, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
326 public void onFailure(Throwable error) {
328 "hndlTepDelForDnatInEachRtr : DNAT -> Error in removing the table 21 entry pushing "
329 + "the MPLS label to the tunnel since label is invalid ", error);
333 public void onSuccess(RpcResult<RemoveFibEntryOutput> result) {
334 if (result.isSuccessful()) {
336 "hndlTepDelForDnatInEachRtr : DNAT -> Successfully removed the entry pushing the "
337 + "MPLS label to the tunnel");
340 "hndlTepDelForDnatInEachRtr : DNAT -> Error in fib rpc call to remove the table "
341 + "21 entry pushing the MPLS label to the tunnnel due to {}",
345 }, MoreExecutors.directExecutor());
351 private void hndlTepDelForSnatInEachRtr(RoutersList router, Uint32 routerId, Uint64 dpnId,
352 String srcTepIp, Boolean isFipExists, ProviderTypes extNwProvType,
353 TypedReadWriteTransaction<Configuration> confTx)
354 throws ExecutionException, InterruptedException {
357 1) Elect a new switch as the primary NAPT
358 2) Advertise the new routes to BGP for the newly elected TEP IP as the DPN IP
359 3) This will make sure old routes are withdrawn and new routes are advertised.
362 String routerName = router.getRouter();
364 "hndlTepDelForSnatInEachRtr : SNAT -> Trying to clear routes to the External fixed IP associated "
365 + "to the router {}", routerName);
367 // Check if this is externalRouter else ignore
368 InstanceIdentifier<Routers> extRoutersId = NatUtil.buildRouterIdentifier(routerName);
369 Optional<Routers> routerData = Optional.absent();
371 routerData = confTx.read(extRoutersId).get();
372 } catch (InterruptedException | ExecutionException e) {
373 LOG.error("Error retrieving routers {}", extRoutersId, e);
375 if (!routerData.isPresent()) {
377 "hndlTepDelForSnatInEachRtr : SNAT->Ignoring TEP del for router {} since its not External Router",
382 //Check if the DPN having the router is the NAPT switch
383 Uint64 naptId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
384 if (naptId == null || naptId.equals(Uint64.ZERO) || !naptId.equals(dpnId)) {
386 "hndlTepDelForSnatInEachRtr : SNAT -> Ignoring TEP delete for the DPN {} since"
387 + "srcTepIp : {} is NOT a NAPT switch", dpnId, srcTepIp);
390 if (natMode == NatMode.Conntrack) {
391 Routers extRouter = routerData.get();
392 natServiceManager.notify(confTx, extRouter, null, naptId, dpnId,
393 SnatServiceManager.Action.CNT_ROUTER_DISBL);
394 if (extRouter.isEnableSnat()) {
395 natServiceManager.notify(confTx, extRouter, null, naptId, dpnId,
396 SnatServiceManager.Action.SNAT_ROUTER_DISBL);
400 Uuid networkId = routerData.get().getNetworkId();
401 if (networkId == null) {
403 "hndlTepDelForSnatInEachRtr : SNAT -> Ignoring TEP delete for the DPN {} for router {}"
404 + "as external network configuraton is missing", dpnId, routerName);
408 LOG.debug("hndlTepDelForSnatInEachRtr : SNAT->Router {} is associated with ext nw {}",
409 routerId, networkId);
410 Uuid bgpVpnUuid = NatUtil.getVpnForRouter(dataBroker, routerName);
412 if (bgpVpnUuid == null) {
414 "hndlTepDelForSnatInEachRtr : SNAT->Internal VPN-ID {} associated to router {}",
415 routerId, routerName);
418 bgpVpnId = NatUtil.getVpnId(dataBroker, bgpVpnUuid.getValue());
419 if (bgpVpnId == NatConstants.INVALID_ID) {
421 "hndlTepDelForSnatInEachRtr :SNAT->Invalid Private BGP VPN ID returned for routerName {}",
427 // Remove default entry in FIB to SNAT table
429 "NAT Service : Installing default route in FIB on DPN {} for router {} with"
431 dpnId, routerName, bgpVpnId);
432 defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, bgpVpnId, routerId, confTx);
435 if (routerData.get().isEnableSnat()) {
436 LOG.info("hndlTepDelForSnatInEachRtr : SNAT enabled for router {}", routerId);
438 Uint32 routerVpnId = routerId;
439 if (bgpVpnId != NatConstants.INVALID_ID) {
441 "hndlTepDelForSnatInEachRtr : SNAT -> Private BGP VPN ID (Internal BGP VPN ID) {} "
442 + "associated to the router {}", bgpVpnId, routerName);
443 routerVpnId = bgpVpnId;
446 "hndlTepDelForSnatInEachRtr : SNAT -> Internal L3 VPN ID (Router ID) {} "
447 + "associated to the router {}", routerVpnId, routerName);
449 //Re-elect the other available switch as the NAPT switch and program the NAT flows.
450 String externalVpnName = NatUtil.getAssociatedVPN(dataBroker,
451 routerData.get().getNetworkId());
452 NatUtil.removeSNATFromDPN(dataBroker, mdsalManager, idManager, naptSwitchHA, dpnId,
453 routerData.get(), routerId, routerVpnId, externalVpnName, extNwProvType, confTx);
456 "hndlTepDelForSnatInEachRtr : SNAT is not enabled for router {} to handle addDPN event {}",
463 protected NatTepChangeListener getDataTreeChangeListener() {