2 * Copyright © 2016, 2017 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 java.math.BigInteger;
14 import java.net.InetAddress;
15 import java.net.UnknownHostException;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.concurrent.ExecutionException;
20 import javax.annotation.PostConstruct;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
27 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
28 import org.opendaylight.genius.infra.Datastore.Configuration;
29 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
30 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
31 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
32 import org.opendaylight.genius.mdsalutil.ActionInfo;
33 import org.opendaylight.genius.mdsalutil.FlowEntity;
34 import org.opendaylight.genius.mdsalutil.InstructionInfo;
35 import org.opendaylight.genius.mdsalutil.MDSALUtil;
36 import org.opendaylight.genius.mdsalutil.MatchInfo;
37 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
38 import org.opendaylight.genius.mdsalutil.NwConstants;
39 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
40 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
41 import org.opendaylight.genius.mdsalutil.actions.ActionSetDestinationIp;
42 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
43 import org.opendaylight.genius.mdsalutil.actions.ActionSetSourceIp;
44 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
45 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
46 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
47 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
48 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetDestination;
49 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
50 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
51 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Source;
52 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
53 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
54 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
55 import org.opendaylight.netvirt.natservice.api.CentralizedSwitchScheduler;
56 import org.opendaylight.netvirt.natservice.api.NatSwitchCache;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.FloatingIpInfo;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.PortsBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.PortsKey;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMapBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMapKey;
72 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
73 import org.opendaylight.yangtools.yang.common.Uint32;
74 import org.opendaylight.yangtools.yang.common.Uint64;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
79 public class FloatingIPListener extends AsyncDataTreeChangeListenerBase<InternalToExternalPortMap, FloatingIPListener> {
80 private static final Logger LOG = LoggerFactory.getLogger(FloatingIPListener.class);
81 private final DataBroker dataBroker;
82 private final ManagedNewTransactionRunner txRunner;
83 private final IMdsalApiManager mdsalManager;
84 private final OdlInterfaceRpcService interfaceManager;
85 private final FloatingIPHandler floatingIPHandler;
86 private final SNATDefaultRouteProgrammer defaultRouteProgrammer;
87 private final JobCoordinator coordinator;
88 private final CentralizedSwitchScheduler centralizedSwitchScheduler;
89 private final NatSwitchCache natSwitchCache;
92 public FloatingIPListener(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
93 final OdlInterfaceRpcService interfaceManager,
94 final FloatingIPHandler floatingIPHandler,
95 final SNATDefaultRouteProgrammer snatDefaultRouteProgrammer,
96 final JobCoordinator coordinator,
97 final CentralizedSwitchScheduler centralizedSwitchScheduler,
98 final NatSwitchCache natSwitchCache) {
99 super(InternalToExternalPortMap.class, FloatingIPListener.class);
100 this.dataBroker = dataBroker;
101 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
102 this.mdsalManager = mdsalManager;
103 this.interfaceManager = interfaceManager;
104 this.floatingIPHandler = floatingIPHandler;
105 this.defaultRouteProgrammer = snatDefaultRouteProgrammer;
106 this.coordinator = coordinator;
107 this.centralizedSwitchScheduler = centralizedSwitchScheduler;
108 this.natSwitchCache = natSwitchCache;
114 LOG.info("{} init", getClass().getSimpleName());
115 registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
119 protected InstanceIdentifier<InternalToExternalPortMap> getWildCardPath() {
120 return InstanceIdentifier.create(FloatingIpInfo.class).child(RouterPorts.class).child(Ports.class)
121 .child(InternalToExternalPortMap.class);
125 protected FloatingIPListener getDataTreeChangeListener() {
126 return FloatingIPListener.this;
130 protected void add(final InstanceIdentifier<InternalToExternalPortMap> identifier,
131 final InternalToExternalPortMap mapping) {
132 LOG.trace("FloatingIPListener add ip mapping method - key: {} value: {}",mapping.key(), mapping);
133 processFloatingIPAdd(identifier, mapping);
137 protected void remove(InstanceIdentifier<InternalToExternalPortMap> identifier, InternalToExternalPortMap mapping) {
138 LOG.trace("FloatingIPListener remove ip mapping method - kkey: {} value: {}",mapping.key(), mapping);
139 processFloatingIPDel(identifier, mapping);
143 protected void update(InstanceIdentifier<InternalToExternalPortMap> identifier, InternalToExternalPortMap
144 original, InternalToExternalPortMap update) {
145 LOG.trace("FloatingIPListener update ip mapping method - key: {}, original: {}, update: {}",
146 update.key(), original, update);
150 private FlowEntity buildPreDNATFlowEntity(Uint64 dpId, InternalToExternalPortMap mapping, Uint32 routerId,
151 Uint32 associatedVpn) {
152 String externalIp = mapping.getExternalIp();
153 Uuid floatingIpId = mapping.getExternalId();
154 //Get the FIP MAC address for DNAT
155 String floatingIpPortMacAddress = NatUtil.getFloatingIpPortMacFromFloatingIpId(dataBroker, floatingIpId);
156 if (floatingIpPortMacAddress == null) {
157 LOG.error("buildPreDNATFlowEntity : Unable to retrieve floatingIpPortMacAddress from floating IP UUID {} "
158 + "for floating IP {}", floatingIpId, externalIp);
161 LOG.debug("buildPreDNATFlowEntity : Bulding DNAT Flow entity for ip {} ", externalIp);
162 Uint32 segmentId = associatedVpn == NatConstants.INVALID_ID ? routerId : associatedVpn;
163 LOG.debug("buildPreDNATFlowEntity : Segment id {} in build preDNAT Flow", segmentId);
165 List<MatchInfo> matches = new ArrayList<>();
166 matches.add(MatchEthernetType.IPV4);
168 matches.add(new MatchIpv4Destination(externalIp, "32"));
169 //Match Destination Floating IP MAC Address on table = 25 (PDNAT_TABLE)
170 matches.add(new MatchEthernetDestination(new MacAddress(floatingIpPortMacAddress)));
172 // matches.add(new MatchMetadata(
173 // BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID));
174 List<ActionInfo> actionsInfos = new ArrayList<>();
175 String internalIp = mapping.getInternalIp();
176 actionsInfos.add(new ActionSetDestinationIp(internalIp, "32"));
178 List<InstructionInfo> instructions = new ArrayList<>();
179 instructions.add(new InstructionWriteMetadata(MetaDataUtil.getVpnIdMetadata(segmentId.longValue()),
180 MetaDataUtil.METADATA_MASK_VRFID));
181 instructions.add(new InstructionApplyActions(actionsInfos));
182 instructions.add(new InstructionGotoTable(NwConstants.DNAT_TABLE));
184 String flowRef = NatUtil.getFlowRef(dpId, NwConstants.PDNAT_TABLE, routerId, externalIp);
186 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PDNAT_TABLE, flowRef,
187 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
188 NwConstants.COOKIE_DNAT_TABLE, matches, instructions);
193 private FlowEntity buildDNATFlowEntity(Uint64 dpId, InternalToExternalPortMap mapping, Uint32 routerId, Uint32
195 String externalIp = mapping.getExternalIp();
196 LOG.info("buildDNATFlowEntity : Bulding DNAT Flow entity for ip {} ", externalIp);
198 Uint32 segmentId = associatedVpn == NatConstants.INVALID_ID ? routerId : associatedVpn;
199 LOG.debug("buildDNATFlowEntity : Segment id {} in build DNAT", segmentId);
201 List<MatchInfo> matches = new ArrayList<>();
202 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(segmentId.longValue()),
203 MetaDataUtil.METADATA_MASK_VRFID));
205 matches.add(MatchEthernetType.IPV4);
206 String internalIp = mapping.getInternalIp();
207 matches.add(new MatchIpv4Destination(internalIp, "32"));
209 List<ActionInfo> actionsInfos = new ArrayList<>();
210 // actionsInfos.add(new ActionSetDestinationIp(internalIp, "32"));
212 List<InstructionInfo> instructions = new ArrayList<>();
213 // instructions.add(new InstructionWriteMetadata(Uint64.valueOf
214 // (routerId), MetaDataUtil.METADATA_MASK_VRFID));
215 actionsInfos.add(new ActionNxLoadInPort(Uint64.valueOf(BigInteger.ZERO)));
216 actionsInfos.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
217 instructions.add(new InstructionApplyActions(actionsInfos));
218 //instructions.add(new InstructionGotoTable(NatConstants.L3_FIB_TABLE));
220 String flowRef = NatUtil.getFlowRef(dpId, NwConstants.DNAT_TABLE, routerId, internalIp);
222 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.DNAT_TABLE, flowRef,
223 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
224 NwConstants.COOKIE_DNAT_TABLE, matches, instructions);
230 private FlowEntity buildPreSNATFlowEntity(Uint64 dpId, String internalIp, String externalIp, Uint32 vpnId, Uint32
231 routerId, Uint32 associatedVpn) {
233 LOG.debug("buildPreSNATFlowEntity : Building PSNAT Flow entity for ip {} ", internalIp);
235 Uint32 segmentId = associatedVpn == NatConstants.INVALID_ID ? routerId : associatedVpn;
237 LOG.debug("buildPreSNATFlowEntity : Segment id {} in build preSNAT flow", segmentId);
239 List<MatchInfo> matches = new ArrayList<>();
240 matches.add(MatchEthernetType.IPV4);
242 matches.add(new MatchIpv4Source(internalIp, "32"));
244 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(segmentId.longValue()),
245 MetaDataUtil.METADATA_MASK_VRFID));
247 List<ActionInfo> actionsInfos = new ArrayList<>();
248 actionsInfos.add(new ActionSetSourceIp(externalIp, "32"));
250 List<InstructionInfo> instructions = new ArrayList<>();
252 new InstructionWriteMetadata(MetaDataUtil.getVpnIdMetadata(vpnId.longValue()),
253 MetaDataUtil.METADATA_MASK_VRFID));
254 instructions.add(new InstructionApplyActions(actionsInfos));
255 instructions.add(new InstructionGotoTable(NwConstants.SNAT_TABLE));
257 String flowRef = NatUtil.getFlowRef(dpId, NwConstants.PSNAT_TABLE, routerId, internalIp);
259 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
260 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
261 NwConstants.COOKIE_DNAT_TABLE, matches, instructions);
267 private FlowEntity buildSNATFlowEntity(Uint64 dpId, InternalToExternalPortMap mapping, Uint32 vpnId, Uuid
269 String internalIp = mapping.getInternalIp();
270 LOG.debug("buildSNATFlowEntity : Building SNAT Flow entity for ip {} ", internalIp);
272 ProviderTypes provType = NatUtil.getProviderTypefromNetworkId(dataBroker, externalNetworkId);
273 if (provType == null) {
274 LOG.error("buildSNATFlowEntity : Unable to get Network Provider Type for network {}", externalNetworkId);
278 List<MatchInfo> matches = new ArrayList<>();
279 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(vpnId.longValue()),
280 MetaDataUtil.METADATA_MASK_VRFID));
281 matches.add(MatchEthernetType.IPV4);
282 String externalIp = mapping.getExternalIp();
283 matches.add(new MatchIpv4Source(externalIp, "32"));
285 List<ActionInfo> actionsInfo = new ArrayList<>();
286 actionsInfo.add(new ActionNxLoadInPort(Uint64.valueOf(BigInteger.ZERO)));
287 Uuid floatingIpId = mapping.getExternalId();
288 String macAddress = NatUtil.getFloatingIpPortMacFromFloatingIpId(dataBroker, floatingIpId);
289 if (macAddress != null) {
290 actionsInfo.add(new ActionSetFieldEthernetSource(new MacAddress(macAddress)));
292 LOG.warn("buildSNATFlowEntity : No MAC address found for floating IP {}", externalIp);
295 LOG.trace("buildSNATFlowEntity : External Network Provider Type is {}, resubmit to FIB", provType.toString());
296 actionsInfo.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
297 List<InstructionInfo> instructions = new ArrayList<>();
298 instructions.add(new InstructionApplyActions(actionsInfo));
299 String flowRef = NatUtil.getFlowRef(dpId, NwConstants.SNAT_TABLE, vpnId, externalIp);
301 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.SNAT_TABLE, flowRef,
302 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
303 NwConstants.COOKIE_DNAT_TABLE, matches, instructions);
309 private void createDNATTblEntry(Uint64 dpnId, InternalToExternalPortMap mapping, Uint32 routerId,
310 Uint32 associatedVpnId, TypedReadWriteTransaction<Configuration> confTx) {
311 FlowEntity preFlowEntity = buildPreDNATFlowEntity(dpnId, mapping, routerId, associatedVpnId);
312 if (preFlowEntity == null) {
313 LOG.error("createDNATTblEntry : Flow entity received as NULL. "
314 + "Cannot proceed with installation of Pre-DNAT flow table {} --> table {} on DpnId {}",
315 NwConstants.PDNAT_TABLE, NwConstants.DNAT_TABLE, dpnId);
317 mdsalManager.addFlow(confTx, preFlowEntity);
318 FlowEntity flowEntity = buildDNATFlowEntity(dpnId, mapping, routerId, associatedVpnId);
319 if (flowEntity != null) {
320 mdsalManager.addFlow(confTx, flowEntity);
325 private void removeDNATTblEntry(Uint64 dpnId, String internalIp, String externalIp, Uint32 routerId,
326 TypedReadWriteTransaction<Configuration> confTx) throws ExecutionException, InterruptedException {
327 FlowEntity preFlowEntity = buildPreDNATDeleteFlowEntity(dpnId, externalIp, routerId);
328 mdsalManager.removeFlow(confTx, preFlowEntity);
330 FlowEntity flowEntity = buildDNATDeleteFlowEntity(dpnId, internalIp, routerId);
331 if (flowEntity != null) {
332 mdsalManager.removeFlow(confTx, flowEntity);
336 private void createSNATTblEntry(Uint64 dpnId, InternalToExternalPortMap mapping, Uint32 vpnId, Uint32 routerId,
337 Uint32 associatedVpnId, Uuid externalNetworkId,
338 TypedReadWriteTransaction<Configuration> confTx) {
339 FlowEntity preFlowEntity = buildPreSNATFlowEntity(dpnId, mapping.getInternalIp(), mapping.getExternalIp(),
340 vpnId, routerId, associatedVpnId);
341 mdsalManager.addFlow(confTx, preFlowEntity);
343 FlowEntity flowEntity = buildSNATFlowEntity(dpnId, mapping, vpnId, externalNetworkId);
344 if (flowEntity != null) {
345 mdsalManager.addFlow(confTx, flowEntity);
349 private void removeSNATTblEntry(Uint64 dpnId, String internalIp, String externalIp, Uint32 routerId, Uint32 vpnId,
350 TypedReadWriteTransaction<Configuration> removeFlowInvTx)
351 throws ExecutionException, InterruptedException {
352 FlowEntity preFlowEntity = buildPreSNATDeleteFlowEntity(dpnId, internalIp, routerId);
353 mdsalManager.removeFlow(removeFlowInvTx, preFlowEntity);
355 FlowEntity flowEntity = buildSNATDeleteFlowEntity(dpnId, externalIp, vpnId);
356 if (flowEntity != null) {
357 mdsalManager.removeFlow(removeFlowInvTx, flowEntity);
362 private Uuid getExtNetworkId(final InstanceIdentifier<RouterPorts> portIid,
363 LogicalDatastoreType dataStoreType) {
364 Optional<RouterPorts> rtrPort =
365 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
366 dataStoreType, portIid);
367 if (!rtrPort.isPresent()) {
368 LOG.error("getExtNetworkId : Unable to read router port entry for {}", portIid);
372 return rtrPort.get().getExternalNetworkId();
375 private Uint32 getVpnId(Uuid extNwId, Uuid floatingIpExternalId) {
376 Uuid subnetId = NatUtil.getFloatingIpPortSubnetIdFromFloatingIpId(dataBroker, floatingIpExternalId);
377 if (subnetId != null) {
378 Uint32 vpnId = NatUtil.getVpnId(dataBroker, subnetId.getValue());
379 if (vpnId != NatConstants.INVALID_ID) {
380 LOG.debug("getVpnId : Got vpnId {} for floatingIpExternalId {}", vpnId, floatingIpExternalId);
385 InstanceIdentifier<Networks> nwId = InstanceIdentifier.builder(ExternalNetworks.class).child(Networks.class,
386 new NetworksKey(extNwId)).build();
387 Optional<Networks> nw =
388 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
389 LogicalDatastoreType.CONFIGURATION, nwId);
390 if (!nw.isPresent()) {
391 LOG.error("getVpnId : Unable to read external network for {}", extNwId);
392 return NatConstants.INVALID_ID;
395 Uuid vpnUuid = nw.get().getVpnid();
396 if (vpnUuid == null) {
397 LOG.error("getVpnId : Unable to read vpn from External network: {}", extNwId);
398 return NatConstants.INVALID_ID;
401 //Get the id using the VPN UUID (also vpn instance name)
402 return NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
405 private void processFloatingIPAdd(final InstanceIdentifier<InternalToExternalPortMap> identifier,
406 final InternalToExternalPortMap mapping) {
407 LOG.trace("processFloatingIPAdd key: {}, value: {}", mapping.key(), mapping);
409 final String routerId = identifier.firstKeyOf(RouterPorts.class).getRouterId();
410 final PortsKey pKey = identifier.firstKeyOf(Ports.class);
411 String interfaceName = pKey.getPortName();
413 InstanceIdentifier<RouterPorts> portIid = identifier.firstIdentifierOf(RouterPorts.class);
414 coordinator.enqueueJob(NatConstants.NAT_DJC_PREFIX + mapping.key(), () -> Collections.singletonList(
415 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
416 tx -> createNATFlowEntries(interfaceName, mapping, portIid, routerId, null, tx))),
417 NatConstants.NAT_DJC_MAX_RETRIES);
420 private void processFloatingIPDel(final InstanceIdentifier<InternalToExternalPortMap> identifier,
421 final InternalToExternalPortMap mapping) {
422 LOG.trace("processFloatingIPDel : key: {}, value: {}", mapping.key(), mapping);
424 final String routerId = identifier.firstKeyOf(RouterPorts.class).getRouterId();
425 final PortsKey pKey = identifier.firstKeyOf(Ports.class);
426 String interfaceName = pKey.getPortName();
428 InstanceIdentifier<RouterPorts> portIid = identifier.firstIdentifierOf(RouterPorts.class);
429 coordinator.enqueueJob(NatConstants.NAT_DJC_PREFIX + mapping.key(), () -> Collections.singletonList(
430 txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
431 tx -> removeNATFlowEntries(interfaceName, mapping, portIid, routerId, null, tx))),
432 NatConstants.NAT_DJC_MAX_RETRIES);
435 private InetAddress getInetAddress(String ipAddr) {
436 InetAddress ipAddress = null;
438 ipAddress = InetAddress.getByName(ipAddr);
439 } catch (UnknownHostException e) {
440 LOG.error("getInetAddress : UnknowHostException for ip {}", ipAddr, e);
445 private boolean validateIpMapping(InternalToExternalPortMap mapping) {
446 return getInetAddress(mapping.getInternalIp()) != null && getInetAddress(mapping.getExternalIp()) != null;
449 private Uint64 getAssociatedDpnWithExternalInterface(final String routerName, Uuid extNwId, Uint64 dpnId,
450 String interfaceName) {
451 //Get the DPN on which this interface resides
453 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
454 .state.Interface interfaceState = NatUtil.getInterfaceStateFromOperDS(dataBroker, interfaceName);
455 if (interfaceState != null) {
456 dpnId = NatUtil.getDpIdFromInterface(interfaceState);
459 Uint64 updatedDpnId = dpnId;
460 if (updatedDpnId != null && updatedDpnId.equals(Uint64.ZERO)) {
461 LOG.debug("getAssociatedDpnWithExternalInterface : The interface {} is not associated with any dpn",
465 ProviderTypes providerType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName, extNwId);
466 if (providerType == null) {
467 LOG.warn("getAssociatedDpnWithExternalInterface : Provider Network Type for router {} and"
468 + " externalNetwork {} is missing.", routerName, extNwId);
472 // For FLAT and VLAN provider networks, we have to ensure that dpn hosting the VM has connectivity
473 // to External Network via provider_mappings. In case the dpn does not have the provider mappings,
474 // traffic from the VM has to be forwarded to the NAPT Switch (which is scheduled based on the provider
475 // mappings) and then sent out on the external Network.
476 if (providerType == ProviderTypes.FLAT || providerType == ProviderTypes.VLAN) {
477 String providerNet = NatUtil.getElanInstancePhysicalNetwok(extNwId.getValue(), dataBroker);
478 boolean isDpnConnected = natSwitchCache.isSwitchConnectedToExternal(updatedDpnId, providerNet);
479 if (!isDpnConnected) {
480 updatedDpnId = centralizedSwitchScheduler.getCentralizedSwitch(routerName);
486 void createNATFlowEntries(String interfaceName, final InternalToExternalPortMap mapping,
487 final InstanceIdentifier<RouterPorts> portIid, final String routerName, @Nullable Uint64 dpnId,
488 TypedReadWriteTransaction<Configuration> confTx) throws ExecutionException, InterruptedException {
489 if (!validateIpMapping(mapping)) {
490 LOG.error("createNATFlowEntries : Not a valid ip addresses in the mapping {}", mapping);
494 Uuid extNwId = getExtNetworkId(portIid, LogicalDatastoreType.CONFIGURATION);
495 if (extNwId == null) {
496 LOG.error("createNATFlowEntries : External network associated with interface {} could not be retrieved",
501 // For Overlay Networks, get the DPN on which this interface resides.
502 // For FLAT/VLAN Networks, get the DPN with provider_mappings for external network.
503 dpnId = getAssociatedDpnWithExternalInterface(routerName, extNwId, dpnId, interfaceName);
504 if (dpnId == null || dpnId.equals(Uint64.ZERO)) {
505 LOG.warn("createNATFlowEntries : No DPN for interface {}. NAT flow entries for ip mapping {} will "
506 + "not be installed", interfaceName, mapping);
510 Uint32 routerId = NatUtil.getVpnId(dataBroker, routerName);
511 if (routerId == NatConstants.INVALID_ID) {
512 LOG.error("createNATFlowEntries : Could not retrieve router id for {} to create NAT Flow entries",
516 //Check if the router to vpn association is present
517 //long associatedVpnId = NatUtil.getAssociatedVpn(dataBroker, routerName);
518 Uuid associatedVpn = NatUtil.getVpnForRouter(dataBroker, routerName);
519 Uint32 associatedVpnId = NatConstants.INVALID_ID;
520 if (associatedVpn == null) {
521 LOG.debug("createNATFlowEntries : Router {} is not assicated with any BGP VPN instance", routerName);
523 LOG.debug("createNATFlowEntries : Router {} is associated with VPN Instance with Id {}",
524 routerName, associatedVpn);
525 associatedVpnId = NatUtil.getVpnId(dataBroker, associatedVpn.getValue());
526 LOG.debug("createNATFlowEntries : vpninstance Id is {} for VPN {}", associatedVpnId, associatedVpn);
527 //routerId = associatedVpnId;
530 Uint32 vpnId = getVpnId(extNwId, mapping.getExternalId());
531 if (vpnId.longValue() < 0) {
532 LOG.error("createNATFlowEntries : No VPN associated with Ext nw {}. Unable to create SNAT table entry "
533 + "for fixed ip {}", extNwId, mapping.getInternalIp());
536 //Install the DNAT default FIB flow L3_FIB_TABLE (21) -> PSNAT_TABLE (26) if SNAT is disabled
537 boolean isSnatEnabled = NatUtil.isSnatEnabledForRouterId(dataBroker, routerName);
538 if (!isSnatEnabled) {
539 addOrDelDefaultFibRouteForDnat(dpnId, routerName, routerId, confTx, true);
541 //Create the DNAT and SNAT table entries
542 createDNATTblEntry(dpnId, mapping, routerId, associatedVpnId, confTx);
543 createSNATTblEntry(dpnId, mapping, vpnId, routerId, associatedVpnId, extNwId, confTx);
544 floatingIPHandler.onAddFloatingIp(dpnId, routerName, routerId, extNwId, interfaceName, mapping, confTx);
547 void createNATFlowEntries(Uint64 dpnId, String interfaceName, String routerName, Uuid externalNetworkId,
548 InternalToExternalPortMap mapping, TypedReadWriteTransaction<Configuration> confTx)
549 throws ExecutionException, InterruptedException {
550 String internalIp = mapping.getInternalIp();
551 Uint32 routerId = NatUtil.getVpnId(dataBroker, routerName);
552 if (routerId == NatConstants.INVALID_ID) {
553 LOG.error("createNATFlowEntries : Could not retrieve router id for {} to create NAT Flow entries",
557 //Check if the router to vpn association is present
558 Uint32 associatedVpnId = NatUtil.getAssociatedVpn(dataBroker, routerName);
559 if (associatedVpnId == NatConstants.INVALID_ID) {
560 LOG.debug("createNATFlowEntries : Router {} is not assicated with any BGP VPN instance", routerName);
562 LOG.debug("createNATFlowEntries : Router {} is associated with VPN Instance with Id {}",
563 routerName, associatedVpnId);
564 //routerId = associatedVpnId;
567 Uint32 vpnId = getVpnId(externalNetworkId, mapping.getExternalId());
568 if (vpnId.longValue() < 0) {
569 LOG.error("createNATFlowEntries : Unable to create SNAT table entry for fixed ip {}", internalIp);
572 //Install the DNAT default FIB flow L3_FIB_TABLE (21) -> PSNAT_TABLE (26) if SNAT is disabled
573 boolean isSnatEnabled = NatUtil.isSnatEnabledForRouterId(dataBroker, routerName);
574 if (!isSnatEnabled) {
575 addOrDelDefaultFibRouteForDnat(dpnId, routerName, routerId, confTx, true);
577 //Create the DNAT and SNAT table entries
578 createDNATTblEntry(dpnId, mapping, routerId, associatedVpnId, confTx);
579 createSNATTblEntry(dpnId, mapping, vpnId, routerId, associatedVpnId, externalNetworkId, confTx);
580 floatingIPHandler.onAddFloatingIp(dpnId, routerName, routerId, externalNetworkId, interfaceName, mapping,
584 void createNATOnlyFlowEntries(Uint64 dpnId, String routerName, @Nullable String associatedVPN,
585 Uuid externalNetworkId, InternalToExternalPortMap mapping)
586 throws ExecutionException, InterruptedException {
587 String internalIp = mapping.getInternalIp();
588 //String segmentId = associatedVPN == null ? routerName : associatedVPN;
589 LOG.debug("createNATOnlyFlowEntries : Retrieving vpn id for VPN {} to proceed with create NAT Flows",
591 Uint32 routerId = NatUtil.getVpnId(dataBroker, routerName);
592 if (routerId == NatConstants.INVALID_ID) {
593 LOG.error("createNATOnlyFlowEntries : Could not retrieve vpn id for {} to create NAT Flow entries",
597 Uint32 associatedVpnId = NatUtil.getVpnId(dataBroker, associatedVPN);
598 LOG.debug("createNATOnlyFlowEntries : Associated VPN Id {} for router {}", associatedVpnId, routerName);
599 Uint32 vpnId = getVpnId(externalNetworkId, mapping.getExternalId());
600 if (vpnId.longValue() < 0) {
601 LOG.error("createNATOnlyFlowEntries : Unable to create SNAT table entry for fixed ip {}", internalIp);
604 //Install the DNAT default FIB flow L3_FIB_TABLE (21) -> PSNAT_TABLE (26) if SNAT is disabled
605 boolean isSnatEnabled = NatUtil.isSnatEnabledForRouterId(dataBroker, routerName);
606 if (!isSnatEnabled) {
607 addOrDelDefaultFibRouteForDnat(dpnId, routerName, routerId, null, true);
609 //Create the DNAT and SNAT table entries
610 FlowEntity preFlowEntity = buildPreDNATFlowEntity(dpnId, mapping, routerId, associatedVpnId);
611 mdsalManager.installFlow(preFlowEntity);
613 FlowEntity flowEntity = buildDNATFlowEntity(dpnId, mapping, routerId, associatedVpnId);
614 mdsalManager.installFlow(flowEntity);
616 String externalIp = mapping.getExternalIp();
617 preFlowEntity = buildPreSNATFlowEntity(dpnId, internalIp, externalIp, vpnId , routerId, associatedVpnId);
618 mdsalManager.installFlow(preFlowEntity);
620 flowEntity = buildSNATFlowEntity(dpnId, mapping, vpnId, externalNetworkId);
621 if (flowEntity != null) {
622 mdsalManager.installFlow(flowEntity);
627 void removeNATFlowEntries(String interfaceName, final InternalToExternalPortMap mapping,
628 InstanceIdentifier<RouterPorts> portIid, final String routerName, @Nullable Uint64 dpnId,
629 TypedReadWriteTransaction<Configuration> removeFlowInvTx) throws ExecutionException, InterruptedException {
630 Uuid extNwId = getExtNetworkId(portIid, LogicalDatastoreType.OPERATIONAL);
631 if (extNwId == null) {
632 LOG.error("removeNATFlowEntries : External network associated with interface {} could not be retrieved",
637 // For Overlay Networks, get the DPN on which this interface resides.
638 // For FLAT/VLAN Networks, get the DPN with provider_mappings for external network.
640 dpnId = getAssociatedDpnWithExternalInterface(routerName, extNwId,
641 NatUtil.getDpnForInterface(interfaceManager, interfaceName), interfaceName);
642 if (dpnId == null || dpnId.equals(Uint64.ZERO)) {
643 LOG.warn("removeNATFlowEntries: Abort processing Floating ip configuration. No DPN for port: {}",
649 Uint32 routerId = NatUtil.getVpnId(dataBroker, routerName);
650 if (routerId == NatConstants.INVALID_ID) {
651 LOG.error("removeNATFlowEntries : Could not retrieve router id for {} to remove NAT Flow entries",
656 String internalIp = mapping.getInternalIp();
657 String externalIp = mapping.getExternalIp();
659 //Delete the DNAT and SNAT table entries
660 removeDNATTblEntry(dpnId, internalIp, externalIp, routerId, removeFlowInvTx);
662 Uint32 vpnId = getVpnId(extNwId, mapping.getExternalId());
663 if (vpnId.longValue() < 0) {
664 LOG.error("removeNATFlowEntries : No VPN associated with ext nw {}. Unable to delete SNAT table "
665 + "entry for fixed ip {}", extNwId, internalIp);
668 removeSNATTblEntry(dpnId, internalIp, externalIp, routerId, vpnId, removeFlowInvTx);
669 //Remove the DNAT default FIB flow L3_FIB_TABLE (21) -> PSNAT_TABLE (26) if SNAT is disabled
670 boolean isSnatEnabled = NatUtil.isSnatEnabledForRouterId(dataBroker, routerName);
671 if (!isSnatEnabled) {
672 addOrDelDefaultFibRouteForDnat(dpnId, routerName, routerId, removeFlowInvTx, false);
674 ProviderTypes provType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName, extNwId);
675 if (provType == null) {
676 LOG.error("removeNATFlowEntries : External Network Provider Type missing");
679 if (provType == ProviderTypes.VXLAN) {
680 floatingIPHandler.onRemoveFloatingIp(dpnId, routerName, routerId, extNwId, mapping,
681 NatConstants.DEFAULT_L3VNI_VALUE, removeFlowInvTx);
682 removeOperationalDS(routerName, interfaceName, internalIp);
685 Uint32 label = getOperationalIpMapping(routerName, interfaceName, internalIp);
686 if (label.longValue() < 0) {
687 LOG.error("removeNATFlowEntries : Could not retrieve label for prefix {} in router {}",
688 internalIp, routerId);
691 floatingIPHandler.onRemoveFloatingIp(dpnId, routerName, routerId, extNwId, mapping, label,
693 removeOperationalDS(routerName, interfaceName, internalIp);
696 void removeNATFlowEntries(Uint64 dpnId, String interfaceName, String vpnName, String routerName,
697 InternalToExternalPortMap mapping, TypedReadWriteTransaction<Configuration> confTx)
698 throws ExecutionException, InterruptedException {
699 String internalIp = mapping.getInternalIp();
700 String externalIp = mapping.getExternalIp();
701 Uint32 routerId = NatUtil.getVpnId(dataBroker, routerName);
702 if (routerId == NatConstants.INVALID_ID) {
703 LOG.error("removeNATFlowEntries : Could not retrieve router id for {} to remove NAT Flow entries",
708 Uint32 vpnId = NatUtil.getVpnId(dataBroker, vpnName);
709 if (vpnId == NatConstants.INVALID_ID) {
710 LOG.warn("removeNATFlowEntries : VPN Id not found for {} to remove NAT flow entries {}",
711 vpnName, internalIp);
714 //Delete the DNAT and SNAT table entries
715 removeDNATTblEntry(dpnId, internalIp, externalIp, routerId, confTx);
716 removeSNATTblEntry(dpnId, internalIp, externalIp, routerId, vpnId, confTx);
717 //Remove the DNAT default FIB flow L3_FIB_TABLE (21) -> PSNAT_TABLE (26) if SNAT is disabled
718 boolean isSnatEnabled = NatUtil.isSnatEnabledForRouterId(dataBroker, routerName);
719 if (!isSnatEnabled) {
720 addOrDelDefaultFibRouteForDnat(dpnId, routerName, routerId, confTx, false);
722 Uuid externalNetworkId = NatUtil.getNetworkIdFromRouterName(dataBroker,routerName);
723 ProviderTypes provType = NatEvpnUtil.getExtNwProvTypeFromRouterName(dataBroker, routerName, externalNetworkId);
724 if (provType == null) {
725 LOG.error("removeNATFlowEntries : External Network Provider Type Missing");
728 if (provType == ProviderTypes.VXLAN) {
729 floatingIPHandler.cleanupFibEntries(dpnId, vpnName, externalIp, NatConstants.DEFAULT_L3VNI_VALUE,
731 removeOperationalDS(routerName, interfaceName, internalIp);
734 Uint32 label = getOperationalIpMapping(routerName, interfaceName, internalIp);
735 if (label.longValue() < 0) {
736 LOG.error("removeNATFlowEntries : Could not retrieve label for prefix {} in router {}",
737 internalIp, routerId);
740 floatingIPHandler.cleanupFibEntries(dpnId, vpnName, externalIp, label, confTx, provType);
741 removeOperationalDS(routerName, interfaceName, internalIp);
744 protected Uint32 getOperationalIpMapping(String routerId, String interfaceName, String internalIp) {
745 InstanceIdentifier<InternalToExternalPortMap> intExtPortMapIdentifier =
746 NatUtil.getIntExtPortMapIdentifier(routerId, interfaceName, internalIp);
747 return SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
748 LogicalDatastoreType.OPERATIONAL, intExtPortMapIdentifier).toJavaUtil().map(
749 InternalToExternalPortMap::getLabel).orElse(NatConstants.INVALID_ID);
752 static void updateOperationalDS(DataBroker dataBroker, String routerId, String interfaceName, Uint32 label,
753 String internalIp, String externalIp) {
755 LOG.info("updateOperationalDS : Updating operational DS for floating ip config : {} with label {}",
757 InstanceIdentifier<Ports> portsId = NatUtil.getPortsIdentifier(routerId, interfaceName);
758 Optional<Ports> optPorts =
759 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
760 LogicalDatastoreType.OPERATIONAL, portsId);
761 InternalToExternalPortMap intExtPortMap = new InternalToExternalPortMapBuilder().withKey(new
762 InternalToExternalPortMapKey(internalIp)).setInternalIp(internalIp).setExternalIp(externalIp)
763 .setLabel(label).build();
764 if (optPorts.isPresent()) {
765 LOG.debug("updateOperationalDS : Ports {} entry already present. Updating intExtPortMap for internal ip {}",
766 interfaceName, internalIp);
767 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, portsId.child(InternalToExternalPortMap
768 .class, new InternalToExternalPortMapKey(internalIp)), intExtPortMap);
770 LOG.debug("updateOperationalDS : Adding Ports entry {} along with intExtPortMap {}",
771 interfaceName, internalIp);
772 List<InternalToExternalPortMap> intExtPortMapList = new ArrayList<>();
773 intExtPortMapList.add(intExtPortMap);
774 Ports ports = new PortsBuilder().withKey(new PortsKey(interfaceName)).setPortName(interfaceName)
775 .setInternalToExternalPortMap(intExtPortMapList).build();
776 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, portsId, ports);
780 void removeOperationalDS(String routerId, String interfaceName, String internalIp) {
781 LOG.info("removeOperationalDS : Remove operational DS for floating ip config: {}", internalIp);
782 InstanceIdentifier<InternalToExternalPortMap> intExtPortMapId = NatUtil.getIntExtPortMapIdentifier(routerId,
783 interfaceName, internalIp);
784 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, intExtPortMapId);
787 private FlowEntity buildPreDNATDeleteFlowEntity(Uint64 dpId, String externalIp, Uint32 routerId) {
789 LOG.info("buildPreDNATDeleteFlowEntity : Bulding Delete DNAT Flow entity for ip {} ", externalIp);
791 String flowRef = NatUtil.getFlowRef(dpId, NwConstants.PDNAT_TABLE, routerId, externalIp);
793 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PDNAT_TABLE, flowRef,
794 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
795 NwConstants.COOKIE_DNAT_TABLE, null, null);
802 private FlowEntity buildDNATDeleteFlowEntity(Uint64 dpId, String internalIp, Uint32 routerId) {
804 LOG.info("buildDNATDeleteFlowEntity : Bulding Delete DNAT Flow entity for ip {} ", internalIp);
806 String flowRef = NatUtil.getFlowRef(dpId, NwConstants.DNAT_TABLE, routerId, internalIp);
808 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.DNAT_TABLE, flowRef,
809 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
810 NwConstants.COOKIE_DNAT_TABLE, null, null);
816 private FlowEntity buildPreSNATDeleteFlowEntity(Uint64 dpId, String internalIp, Uint32 routerId) {
818 LOG.info("buildPreSNATDeleteFlowEntity : Building Delete PSNAT Flow entity for ip {} ", internalIp);
820 String flowRef = NatUtil.getFlowRef(dpId, NwConstants.PSNAT_TABLE, routerId, internalIp);
822 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.PSNAT_TABLE, flowRef,
823 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
824 NwConstants.COOKIE_DNAT_TABLE, null, null);
828 private FlowEntity buildSNATDeleteFlowEntity(Uint64 dpId, String externalIp, Uint32 routerId) {
830 LOG.info("buildSNATDeleteFlowEntity : Building Delete SNAT Flow entity for ip {} ", externalIp);
832 String flowRef = NatUtil.getFlowRef(dpId, NwConstants.SNAT_TABLE, routerId, externalIp);
834 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.SNAT_TABLE, flowRef,
835 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, 0, 0,
836 NwConstants.COOKIE_DNAT_TABLE, null, null);
841 private void addOrDelDefaultFibRouteForDnat(Uint64 dpnId, String routerName, Uint32 routerId,
842 @Nullable TypedReadWriteTransaction<Configuration> confTx, boolean create)
843 throws ExecutionException, InterruptedException {
844 if (confTx == null) {
845 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
846 newTx -> addOrDelDefaultFibRouteForDnat(dpnId, routerName, routerId, newTx, create)), LOG,
847 "Error handling default FIB route for DNAT");
850 //Check if the router to bgp-vpn association is present
851 Uint32 associatedVpnId = NatConstants.INVALID_ID;
852 Uuid associatedVpn = NatUtil.getVpnForRouter(dataBroker, routerName);
853 if (associatedVpn != null) {
854 associatedVpnId = NatUtil.getVpnId(dataBroker, associatedVpn.getValue());
857 if (associatedVpnId != NatConstants.INVALID_ID) {
858 LOG.debug("addOrDelDefaultFibRouteForDnat: Install NAT default route on DPN {} for the router {} with "
859 + "vpn-id {}", dpnId, routerName, associatedVpnId);
860 defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, associatedVpnId, routerId, confTx);
862 LOG.debug("addOrDelDefaultFibRouteForDnat: Install NAT default route on DPN {} for the router {} with "
863 + "vpn-id {}", dpnId, routerName, routerId);
864 defaultRouteProgrammer.installDefNATRouteInDPN(dpnId, routerId, confTx);
867 if (associatedVpnId != NatConstants.INVALID_ID) {
868 LOG.debug("addOrDelDefaultFibRouteForDnat: Remove NAT default route on DPN {} for the router {} "
869 + "with vpn-id {}", dpnId, routerName, associatedVpnId);
870 defaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, associatedVpnId, routerId, confTx);
872 LOG.debug("addOrDelDefaultFibRouteForDnat: Remove NAT default route on DPN {} for the router {} "
873 + "with vpn-id {}", dpnId, routerName, routerId);
874 defaultRouteProgrammer.removeDefNATRouteInDPN(dpnId, routerId, confTx);