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.math.BigInteger;
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.concurrent.ExecutionException;
22 import javax.annotation.PostConstruct;
23 import javax.inject.Inject;
24 import javax.inject.Singleton;
26 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
29 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
30 import org.opendaylight.genius.infra.Datastore.Configuration;
31 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
32 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
33 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
34 import org.opendaylight.genius.mdsalutil.MDSALUtil;
35 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
36 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
37 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
38 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
39 import org.opendaylight.netvirt.natservice.api.SnatServiceManager;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.DpnEndpoints;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.DPNTEPsInfo;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryOutput;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpn.routers.DpnRoutersList;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpn.routers.dpn.routers.list.RoutersList;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.config.rev170206.NatserviceConfig.NatMode;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.opendaylight.yangtools.yang.common.RpcResult;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
65 public class NatTepChangeListener extends
66 AsyncDataTreeChangeListenerBase<TunnelEndPoints, NatTepChangeListener> {
68 private static final Logger LOG = LoggerFactory
69 .getLogger(NatTepChangeListener.class);
70 private final DataBroker dataBroker;
71 private SNATDefaultRouteProgrammer defaultRouteProgrammer;
72 private OdlInterfaceRpcService interfaceService;
73 private IdManagerService idManager;
74 private IFibManager fibManager;
75 private IBgpManager bgpManager;
76 private IMdsalApiManager mdsalManager;
77 private FloatingIPListener floatingIPListener;
78 private FibRpcService fibRpcService;
79 private NaptSwitchHA naptSwitchHA;
80 private final NatMode natMode;
81 private final SnatServiceManager natServiceManager;
82 private final JobCoordinator coordinator;
83 private final ManagedNewTransactionRunner txRunner;
84 private final NatOverVxlanUtil natOverVxlanUtil;
87 public NatTepChangeListener(final DataBroker dataBroker,
88 final SNATDefaultRouteProgrammer defaultRouteProgrammer,
89 final OdlInterfaceRpcService interfaceService,
90 final IdManagerService idManager, final IFibManager fibManager,
91 final IBgpManager bgpManager,
92 final FloatingIPListener floatingIPListener,
93 final FibRpcService fibRpcService,
94 final IMdsalApiManager mdsalManager,
95 final NaptSwitchHA naptSwitchHA,
96 final NatserviceConfig config,
97 final SnatServiceManager natServiceManager,
98 final JobCoordinator coordinator,
99 final NatOverVxlanUtil natOverVxlanUtil) {
100 super(TunnelEndPoints.class, NatTepChangeListener.class);
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;
125 LOG.info("{} init", getClass().getSimpleName());
126 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
130 protected InstanceIdentifier<TunnelEndPoints> getWildCardPath() {
131 return InstanceIdentifier.builder(DpnEndpoints.class)
132 .child(DPNTEPsInfo.class).child(TunnelEndPoints.class).build();
136 protected void remove(InstanceIdentifier<TunnelEndPoints> key,
137 TunnelEndPoints tep) {
139 * Whenever the TEP on a given DPNID is removed, this API take care
140 * of withdrawing the FIB entries for those Floating-IP existing on this
141 * DPN and perform re-election of NAPT Switch for a VRF to which the current
142 * DPN is elected as NAPT Switch.
144 BigInteger srcDpnId = key.firstIdentifierOf(DPNTEPsInfo.class)
145 .firstKeyOf(DPNTEPsInfo.class).getDPNID();
146 final String srcTepIp = tep.getIpAddress().stringValue();
147 String tunnelType = tep.getTunnelType().getName();
149 "NAT Service : Remove Event triggered for Tep on DPN:{} having IP:{} and tunnelType:{}",
150 srcDpnId, srcTepIp, tunnelType);
151 handleTepDelForAllRtrs(srcDpnId, srcTepIp);
155 protected void update(InstanceIdentifier<TunnelEndPoints> key,
156 TunnelEndPoints origTep, TunnelEndPoints updatedTep) {
157 // Will be handled in NatTunnelInterfaceStateListener.add()
158 LOG.debug("NO ACTION duing update event : {}", updatedTep.key());
162 protected void add(InstanceIdentifier<TunnelEndPoints> key,
163 TunnelEndPoints tep) {
164 LOG.debug("NO ACTION duing add event : {}", tep.key());
167 @SuppressWarnings("checkstyle:IllegalCatch")
168 private void handleTepDelForAllRtrs(BigInteger srcDpnId, String srcTepIp) {
169 LOG.trace("handleTepDelForAllRtrs : TEP DEL ----- on DPN-ID {} having SRC IP : {}",
173 List<RoutersList> routersList = null;
174 InstanceIdentifier<DpnRoutersList> dpnRoutersListId = NatUtil.getDpnRoutersId(srcDpnId);
175 Optional<DpnRoutersList> optionalRouterDpnList =
176 SingleTransactionDataBroker
177 .syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
178 LogicalDatastoreType.OPERATIONAL, dpnRoutersListId);
179 if (optionalRouterDpnList.isPresent()) {
180 routersList = optionalRouterDpnList.get().getRoutersList();
183 "NAT Service : RouterDpnList is empty for DPN {}. Hence ignoring TEP DEL event",
188 if (routersList == null) {
189 LOG.error("handleTepDelForAllRtrs : DPN {} does not have the Routers presence",
194 for (RoutersList router : routersList) {
195 String routerName = router.getRouter();
197 "handleTepDelForAllRtrs : TEP DEL : DNAT -> Withdrawing routes for router {} ",
199 long routerId = NatUtil.getVpnId(dataBroker, routerName);
200 if (routerId == NatConstants.INVALID_ID) {
201 LOG.error("handleTepDelForAllRtrs :Invalid ROUTER-ID {} returned for routerName {}",
202 routerId, routerName);
205 Uuid externalNetworkId = NatUtil.getNetworkIdFromRouterName(dataBroker, routerName);
206 ProviderTypes extNwProvType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker,
207 routerName, externalNetworkId);
208 if (extNwProvType == null) {
211 boolean isFipExists = hndlTepDelForDnatInEachRtr(router, routerId, srcDpnId,
214 "handleTepDelForAllRtrs : TEP DEL : SNAT -> Withdrawing and Advertising routes for router {} ",
216 coordinator.enqueueJob((NatConstants.NAT_DJC_PREFIX + router.getRouter()), () -> {
217 List<ListenableFuture<Void>> futures = new ArrayList<>();
218 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx -> {
219 hndlTepDelForSnatInEachRtr(router, routerId, srcDpnId, srcTepIp, isFipExists,
220 extNwProvType, configTx);
224 }, NatConstants.NAT_DJC_MAX_RETRIES);
229 private boolean hndlTepDelForDnatInEachRtr(RoutersList router, long routerId,
230 BigInteger tepDeletedDpnId,
231 ProviderTypes extNwProvType) {
232 //DNAT : Withdraw the routes from the BGP
233 String routerName = router.getRouter();
234 Boolean isFipExists = Boolean.FALSE;
236 LOG.debug("hndlTepDelForDnatInEachRtr : DNAT -> Trying to clear routes to the Floating IP "
237 + "associated to the router {}", routerName);
239 InstanceIdentifier<RouterPorts> routerPortsId = NatUtil.getRouterPortsId(routerName);
240 Optional<RouterPorts> optRouterPorts = MDSALUtil.read(dataBroker, LogicalDatastoreType
241 .CONFIGURATION, routerPortsId);
242 if (!optRouterPorts.isPresent()) {
244 "hndlTepDelForDnatInEachRtr : DNAT -> Could not read Router Ports data object with id: {} "
245 + "from DNAT FloatingIpInfo", routerName);
248 RouterPorts routerPorts = optRouterPorts.get();
249 Uuid extNwId = routerPorts.getExternalNetworkId();
250 final String vpnName = NatUtil.getAssociatedVPN(dataBroker, extNwId);
251 if (vpnName == null) {
253 "hndlTepDelForDnatInEachRtr : DNAT -> No External VPN associated with Ext N/W {} for Router {}",
254 extNwId, routerName);
257 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
258 if (extNwProvType == null) {
262 if (extNwProvType == ProviderTypes.VXLAN) {
263 //get l3Vni value for external VPN
264 l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
265 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
267 "hndlTepDelForDnatInEachRtr : L3VNI value is not configured in Internet VPN {} and RD {} "
268 + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue to installing "
269 + "NAT flows", vpnName, rd);
270 l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId).longValue();
273 List<Ports> interfaces = routerPorts.getPorts();
274 for (Ports port : interfaces) {
275 //Get the DPN on which this interface resides
276 String interfaceName = port.getPortName();
277 BigInteger fipCfgdDpnId = NatUtil.getDpnForInterface(interfaceService, interfaceName);
278 if (fipCfgdDpnId.equals(BigInteger.ZERO)) {
280 "hndlTepDelForDnatInEachRtr : DNAT -> Abort processing Floating ip configuration. "
281 + "No DPN for port : {}", interfaceName);
284 if (!fipCfgdDpnId.equals(tepDeletedDpnId)) {
286 "hndlTepDelForDnatInEachRtr : DNAT -> TEP deleted DPN {} is not the DPN {} which has the "
287 + "floating IP configured for the port: {}",
288 tepDeletedDpnId, fipCfgdDpnId, interfaceName);
291 isFipExists = Boolean.TRUE;
292 List<InternalToExternalPortMap> intExtPortMapList = port.getInternalToExternalPortMap();
293 for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
294 String internalIp = intExtPortMap.getInternalIp();
295 String externalIp = intExtPortMap.getExternalIp();
296 externalIp = NatUtil.validateAndAddNetworkMask(externalIp);
298 "hndlTepDelForDnatInEachRtr : DNAT -> Withdrawing the FIB route to the floating IP {} "
299 + "configured for the port: {}",
300 externalIp, interfaceName);
301 NatUtil.removePrefixFromBGP(bgpManager, fibManager, rd, externalIp, vpnName);
303 if (extNwProvType == ProviderTypes.VXLAN) {
306 long label = floatingIPListener
307 .getOperationalIpMapping(routerName, interfaceName, internalIp);
308 if (label == NatConstants.INVALID_ID) {
310 "hndlTepDelForDnatInEachRtr : DNAT -> Unable to remove the table 21 entry pushing the"
311 + " 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, long routerId, BigInteger 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 BigInteger naptId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
384 if (naptId == null || naptId.equals(BigInteger.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 long 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.
451 .removeSNATFromDPN(dataBroker, mdsalManager, idManager, naptSwitchHA, dpnId,
452 routerName, routerId, routerVpnId, networkId, extNwProvType, confTx);
455 "hndlTepDelForSnatInEachRtr : SNAT is not enabled for router {} to handle addDPN event {}",
462 protected NatTepChangeListener getDataTreeChangeListener() {