2 * Copyright (c) 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 com.google.common.base.Optional;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.concurrent.Callable;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.Future;
18 import javax.annotation.PostConstruct;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
25 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
26 import org.opendaylight.genius.mdsalutil.FlowEntity;
27 import org.opendaylight.genius.mdsalutil.NwConstants;
28 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
29 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
30 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.NaptSwitches;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProtocolTypes;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.RouterPorts;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.Ports;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.IpPort;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.snatint.ip.port.map.intip.port.map.ip.port.IntIpProtoType;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortInputBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.GetFixedIPsForNeutronPortOutput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.opendaylight.yangtools.yang.common.RpcResult;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 public class InterfaceStateEventListener
55 extends AsyncDataTreeChangeListenerBase<Interface, InterfaceStateEventListener>
56 implements AutoCloseable {
58 private static final Logger LOG = LoggerFactory.getLogger(InterfaceStateEventListener.class);
59 private final DataBroker dataBroker;
60 private final IMdsalApiManager mdsalManager;
61 private final FloatingIPListener floatingIPListener;
62 private final NaptManager naptManager;
63 private final NeutronvpnService neutronVpnService;
64 private final JobCoordinator coordinator;
65 private static final String NAT_FLOW = "NATFLOW";
68 public InterfaceStateEventListener(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
69 final FloatingIPListener floatingIPListener,
70 final NaptManager naptManager,
71 final NeutronvpnService neutronvpnService,
72 final JobCoordinator coordinator) {
73 super(Interface.class, InterfaceStateEventListener.class);
74 this.dataBroker = dataBroker;
75 this.mdsalManager = mdsalManager;
76 this.floatingIPListener = floatingIPListener;
77 this.naptManager = naptManager;
78 this.neutronVpnService = neutronvpnService;
79 this.coordinator = coordinator;
85 LOG.info("{} init", getClass().getSimpleName());
86 registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
90 protected InstanceIdentifier<Interface> getWildCardPath() {
91 return InstanceIdentifier.create(InterfacesState.class).child(Interface.class);
95 protected InterfaceStateEventListener getDataTreeChangeListener() {
96 return InterfaceStateEventListener.this;
100 // TODO Clean up the exception handling
101 @SuppressWarnings("checkstyle:IllegalCatch")
102 protected void remove(InstanceIdentifier<Interface> identifier, Interface delintrf) {
103 LOG.trace("remove : Interface {} removed event received", delintrf);
104 if (!L2vlan.class.equals(delintrf.getType())) {
105 LOG.debug("remove : Interface {} is a not type Vlan.Ignoring", delintrf.getName());
109 NatFlowRemoveWorker natFlowRemoveWorker =
110 new NatFlowRemoveWorker(delintrf);
111 coordinator.enqueueJob(NAT_FLOW + "-" + delintrf.getName(), natFlowRemoveWorker,
112 NatConstants.NAT_DJC_MAX_RETRIES);
116 // TODO Clean up the exception handling
117 @SuppressWarnings("checkstyle:IllegalCatch")
118 protected void update(InstanceIdentifier<Interface> identifier, Interface original, Interface update) {
119 LOG.trace("update : Operation Interface update event - Old: {}, New: {}", original, update);
120 if (!L2vlan.class.equals(update.getType())) {
121 LOG.debug("update : Interface {} is not type Vlan.Ignoring", update.getName());
125 NatFlowUpdateWorker natFlowUpdateWorker =
126 new NatFlowUpdateWorker(original, update);
127 coordinator.enqueueJob(NAT_FLOW + "-" + update.getName(), natFlowUpdateWorker,
128 NatConstants.NAT_DJC_MAX_RETRIES);
133 // TODO Clean up the exception handling
134 @SuppressWarnings("checkstyle:IllegalCatch")
135 protected void add(InstanceIdentifier<Interface> identifier, Interface intrf) {
136 LOG.trace("add : Interface {} up event received", intrf);
137 if (!L2vlan.class.equals(intrf.getType())) {
138 LOG.debug("add : Interface {} is not type vlan.Ignoring", intrf.getName());
142 NatFlowAddWorker natFlowAddWorker =
143 new NatFlowAddWorker(intrf);
144 coordinator.enqueueJob(NAT_FLOW + "-" + intrf.getName(), natFlowAddWorker,
145 NatConstants.NAT_DJC_MAX_RETRIES);
148 // TODO Clean up the exception handling
149 @SuppressWarnings("checkstyle:IllegalCatch")
150 private void removeSnatEntriesForPort(String interfaceName, String routerName) {
151 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
152 if (routerId == NatConstants.INVALID_ID) {
153 LOG.error("removeSnatEntriesForPort : routerId not found for routername {}", routerName);
156 BigInteger naptSwitch = getNaptSwitchforRouter(dataBroker, routerName);
157 if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
158 LOG.error("removeSnatEntriesForPort : NaptSwitch is not elected for router {} with Id {}",
159 routerName, routerId);
162 //getInternalIp for port
163 List<String> fixedIps = getFixedIpsForPort(interfaceName);
164 if (fixedIps == null) {
165 LOG.warn("removeSnatEntriesForPort : Internal Ips not found for InterfaceName {} in router {} with id {}",
166 interfaceName, routerName, routerId);
169 List<ProtocolTypes> protocolTypesList = getPortocolList();
170 for (String internalIp : fixedIps) {
171 LOG.debug("removeSnatEntriesForPort : Internal Ip retrieved for interface {} is {} in router with Id {}",
172 interfaceName, internalIp, routerId);
173 IpPort ipPort = NatUtil.getInternalIpPortInfo(dataBroker, routerId, internalIp);
174 if (ipPort == null) {
175 LOG.debug("removeSnatEntriesForPort : no snatint-ip-port-map found for ip:{}", internalIp);
179 for (IntIpProtoType protoType: ipPort.getIntIpProtoType()) {
180 ProtocolTypes protocol = protoType.getProtocol();
181 for (Integer portnum : protoType.getPorts()) {
182 //build and remove the flow in outbound table
184 removeNatFlow(naptSwitch, NwConstants.OUTBOUND_NAPT_TABLE, routerId, internalIp, portnum);
185 } catch (Exception ex) {
186 LOG.error("removeSnatEntriesForPort : Failed to remove snat flow for internalIP {} with "
187 + "Port {} protocol {} for routerId {} in OUTBOUNDTABLE of NaptSwitch {}: {}",
188 internalIp, portnum, protocol, routerId, naptSwitch, ex);
190 //Get the external IP address and the port from the model
191 NAPTEntryEvent.Protocol proto = protocol.toString().equals(ProtocolTypes.TCP.toString())
192 ? NAPTEntryEvent.Protocol.TCP : NAPTEntryEvent.Protocol.UDP;
193 IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId,
194 internalIp, String.valueOf(portnum), proto);
195 if (ipPortExternal == null) {
196 LOG.error("removeSnatEntriesForPort : Mapping for internalIp {} with port {} is not found in "
197 + "router with Id {}", internalIp, portnum, routerId);
200 String externalIpAddress = ipPortExternal.getIpAddress();
201 Integer portNumber = ipPortExternal.getPortNum();
203 //build and remove the flow in inboundtable
205 removeNatFlow(naptSwitch, NwConstants.INBOUND_NAPT_TABLE, routerId,
206 externalIpAddress, portNumber);
207 } catch (Exception ex) {
208 LOG.error("removeSnatEntriesForPort : Failed to remove snat flow internalIP {} with "
209 + "Port {} protocol {} for routerId {} in INBOUNDTABLE of naptSwitch {}",
210 externalIpAddress, portNumber, protocol, routerId, naptSwitch, ex);
213 String internalIpPort = internalIp + ":" + portnum;
214 // delete the entry from IntExtIpPortMap DS
216 naptManager.removeFromIpPortMapDS(routerId, internalIpPort, proto);
217 naptManager.removePortFromPool(internalIpPort, externalIpAddress);
218 } catch (Exception ex) {
219 LOG.error("removeSnatEntriesForPort : releaseIpExtPortMapping failed, Removal of "
220 + "ipportmap {} for router {} failed {}", internalIpPort, routerId, ex);
224 // delete the entry from SnatIntIpPortMap DS
225 LOG.debug("removeSnatEntriesForPort : Removing InternalIp:{} on router {}", internalIp, routerId);
226 naptManager.removeFromSnatIpPortDS(routerId, internalIp);
230 // TODO Clean up the exception handling
231 @SuppressWarnings("checkstyle:IllegalCatch")
232 private String getRouterIdForPort(DataBroker dataBroker, String interfaceName) {
233 String vpnName = null;
234 String routerName = null;
235 VpnInterface vpnInterface = null;
237 vpnInterface = NatUtil.getConfiguredVpnInterface(dataBroker, interfaceName);
238 } catch (Exception ex) {
239 LOG.error("getRouterIdForPort : Unable to process for interface {} as it is not configured",
242 if (vpnInterface != null) {
245 vpnName = vpnInterface.getVpnInstanceName();
246 LOG.debug("getRouterIdForPort: Retrieved VpnName {}", vpnName);
247 } catch (Exception e) {
248 LOG.error("getRouterIdForPort : Unable to get vpnname for vpninterface {}", vpnInterface, e);
250 if (vpnName != null) {
252 routerName = NatUtil.getRouterIdfromVpnInstance(dataBroker, vpnName);
253 } catch (Exception e) {
254 LOG.error("getRouterIdForPort : Unable to get routerId for vpnName {}", vpnName, e);
256 if (routerName != null) {
257 //check router is associated to external network
258 if (NatUtil.isSnatEnabledForRouterId(dataBroker, routerName)) {
259 LOG.debug("getRouterIdForPort : Retreived Router Id {} for vpnname {} "
260 + "associated to interface {}", routerName, vpnName, interfaceName);
263 LOG.warn("getRouterIdForPort : Interface {} associated to routerId {} is not "
264 + "associated to external network", interfaceName, routerName);
267 LOG.debug("getRouterIdForPort : Router is not associated to vpnname {} for interface {}",
268 vpnName, interfaceName);
271 LOG.debug("getRouterIdForPort : vpnName not found for vpnInterface {} of port {}",
272 vpnInterface, interfaceName);
275 LOG.debug("getRouterIdForPort : Interface {} is not a vpninterface", interfaceName);
280 private List<ProtocolTypes> getPortocolList() {
281 List<ProtocolTypes> protocollist = new ArrayList<>();
282 protocollist.add(ProtocolTypes.TCP);
283 protocollist.add(ProtocolTypes.UDP);
287 private BigInteger getNaptSwitchforRouter(DataBroker broker, String routerName) {
288 InstanceIdentifier<RouterToNaptSwitch> rtrNaptSw = InstanceIdentifier.builder(NaptSwitches.class)
289 .child(RouterToNaptSwitch.class, new RouterToNaptSwitchKey(routerName)).build();
290 Optional<RouterToNaptSwitch> routerToNaptSwitchData =
291 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(broker,
292 LogicalDatastoreType.CONFIGURATION, rtrNaptSw);
293 if (routerToNaptSwitchData.isPresent()) {
294 RouterToNaptSwitch routerToNaptSwitchInstance = routerToNaptSwitchData.get();
295 return routerToNaptSwitchInstance.getPrimarySwitchId();
300 private void removeNatFlow(BigInteger dpnId, short tableId, Long routerId, String ipAddress, int ipPort) {
302 String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), ipAddress, ipPort);
303 FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef);
305 mdsalManager.removeFlow(snatFlowEntity);
306 LOG.debug("removeNatFlow : Removed the flow in table {} for the switch with the DPN ID {} for "
307 + "router {} ip {} port {}", tableId, dpnId, routerId, ipAddress, ipPort);
310 private void processInterfaceAdded(String portName, String routerId, List<ListenableFuture<Void>> futures) {
311 LOG.trace("processInterfaceAdded : Processing Interface Add Event for interface {}", portName);
312 List<InternalToExternalPortMap> intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
313 if (intExtPortMapList == null || intExtPortMapList.isEmpty()) {
314 LOG.debug("processInterfaceAdded : Ip Mapping list is empty/null for portname {}", portName);
317 InstanceIdentifier<RouterPorts> portIid = NatUtil.buildRouterPortsIdentifier(routerId);
318 WriteTransaction installFlowInvTx = dataBroker.newWriteOnlyTransaction();
319 for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
320 floatingIPListener.createNATFlowEntries(portName, intExtPortMap, portIid, routerId, installFlowInvTx);
322 //final submit call for installFlowInvTx
323 futures.add(NatUtil.waitForTransactionToComplete(installFlowInvTx));
326 private void processInterfaceRemoved(String portName, BigInteger dpnId, String routerId,
327 List<ListenableFuture<Void>> futures) {
328 LOG.trace("processInterfaceRemoved : Processing Interface Removed Event for interface {} on DPN ID {}",
330 List<InternalToExternalPortMap> intExtPortMapList = getIntExtPortMapListForPortName(portName, routerId);
331 if (intExtPortMapList == null || intExtPortMapList.isEmpty()) {
332 LOG.debug("processInterfaceRemoved : Ip Mapping list is empty/null for portName {}", portName);
335 InstanceIdentifier<RouterPorts> portIid = NatUtil.buildRouterPortsIdentifier(routerId);
336 WriteTransaction removeFlowInvTx = dataBroker.newWriteOnlyTransaction();
337 for (InternalToExternalPortMap intExtPortMap : intExtPortMapList) {
338 LOG.trace("processInterfaceRemoved : Removing DNAT Flow entries for dpnId {} ", dpnId);
339 floatingIPListener.removeNATFlowEntries(portName, intExtPortMap, portIid, routerId, dpnId, removeFlowInvTx);
341 //final submit call for removeFlowInvTx
342 futures.add(NatUtil.waitForTransactionToComplete(removeFlowInvTx));
345 private List<InternalToExternalPortMap> getIntExtPortMapListForPortName(String portName, String routerId) {
346 InstanceIdentifier<Ports> portToIpMapIdentifier = NatUtil.buildPortToIpMapIdentifier(routerId, portName);
347 Optional<Ports> port =
348 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
349 LogicalDatastoreType.CONFIGURATION, portToIpMapIdentifier);
350 if (!port.isPresent()) {
351 LOG.info("getIntExtPortMapListForPortName : Unable to read router port entry for router ID {} "
352 + "and port name {}", routerId, portName);
355 return port.get().getInternalToExternalPortMap();
358 private List<String> getFixedIpsForPort(String interfname) {
359 LOG.debug("getFixedIpsForPort : getFixedIpsForPort method is called for interface {}", interfname);
361 Future<RpcResult<GetFixedIPsForNeutronPortOutput>> result =
362 neutronVpnService.getFixedIPsForNeutronPort(new GetFixedIPsForNeutronPortInputBuilder()
363 .setPortId(new Uuid(interfname)).build());
365 RpcResult<GetFixedIPsForNeutronPortOutput> rpcResult = result.get();
366 if (!rpcResult.isSuccessful()) {
367 LOG.error("getFixedIpsForPort : RPC Call to GetFixedIPsForNeutronPortOutput returned with Errors {}",
368 rpcResult.getErrors());
370 return rpcResult.getResult().getFixedIPs();
372 } catch (InterruptedException | ExecutionException | NullPointerException ex) {
373 LOG.error("getFixedIpsForPort : Exception while receiving fixedIps for port {}", interfname, ex);
378 private class NatFlowAddWorker implements Callable<List<ListenableFuture<Void>>> {
381 NatFlowAddWorker(Interface iface) {
386 @SuppressWarnings("checkstyle:IllegalCatch")
387 public List<ListenableFuture<Void>> call() {
388 final List<ListenableFuture<Void>> futures = new ArrayList<>();
389 LOG.trace("call : Interface {} up event received", iface);
390 String interfaceName = iface.getName();
392 LOG.trace("call : Port added event received for interface {} ", interfaceName);
393 String routerId = getRouterIdForPort(dataBroker, interfaceName);
394 if (routerId != null) {
395 processInterfaceAdded(interfaceName, routerId, futures);
397 } catch (Exception ex) {
398 LOG.error("call : Exception caught in Interface {} Operational State Up event",
405 private class NatFlowRemoveWorker implements Callable<List<ListenableFuture<Void>>> {
408 NatFlowRemoveWorker(Interface delintrf) {
409 this.delintrf = delintrf;
413 @SuppressWarnings("checkstyle:IllegalCatch")
414 public List<ListenableFuture<Void>> call() {
415 final List<ListenableFuture<Void>> futures = new ArrayList<>();
416 final String interfaceName = delintrf.getName();
417 LOG.trace("call : Interface {} removed event received", delintrf);
419 String routerName = getRouterIdForPort(dataBroker, interfaceName);
420 if (routerName != null) {
421 LOG.trace("call : Port removed event received for interface {} ", interfaceName);
425 dpId = NatUtil.getDpIdFromInterface(delintrf);
426 } catch (Exception e) {
428 "call: Unable to retrieve DPNID from Interface operational data store for"
429 + " Interface {}. Fetching from VPN Interface op data store. ",
431 InstanceIdentifier<VpnInterface> id = NatUtil.getVpnInterfaceIdentifier(interfaceName);
432 Optional<VpnInterface> optVpnInterface =
433 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(
434 dataBroker, LogicalDatastoreType.OPERATIONAL, id);
435 if (!optVpnInterface.isPresent()) {
436 LOG.debug("call : Interface {} is not a VPN Interface, ignoring.", interfaceName);
439 final VpnInterface vpnInterface = optVpnInterface.get();
440 dpId = vpnInterface.getDpnId();
442 if (dpId == null || dpId.equals(BigInteger.ZERO)) {
443 LOG.error("call : Unable to get DPN ID for the Interface {}", interfaceName);
446 processInterfaceRemoved(interfaceName, dpId, routerName, futures);
447 removeSnatEntriesForPort(interfaceName, routerName);
449 LOG.debug("call : PORT_REMOVE: Router Id is null either Interface {} is not associated "
450 + "to router or failed to retrieve routerId due to exception", interfaceName);
452 } catch (Exception e) {
453 LOG.error("call : Exception caught in Interface {} OperationalStateRemove", interfaceName, e);
459 private class NatFlowUpdateWorker implements Callable<List<ListenableFuture<Void>>> {
463 NatFlowUpdateWorker(Interface original, Interface update) {
464 this.original = original;
465 this.update = update;
469 @SuppressWarnings("checkstyle:IllegalCatch")
470 public List<ListenableFuture<Void>> call() {
471 final List<ListenableFuture<Void>> futures = new ArrayList<>();
472 String interfaceName = update.getName();
473 if (update.getOperStatus().equals(Interface.OperStatus.Up)) {
474 LOG.debug("call : Port UP event received for interface {} ", interfaceName);
475 } else if (update.getOperStatus().equals(Interface.OperStatus.Down)) {
476 LOG.debug("call : Port DOWN event received for interface {} ", interfaceName);
478 String routerName = getRouterIdForPort(dataBroker, interfaceName);
479 if (routerName != null) {
480 removeSnatEntriesForPort(interfaceName, routerName);
483 "call : PORT_DOWN: Router Id is null, either Interface {} is not associated "
484 + "to router {} or failed to retrieve routerId due to exception",
485 interfaceName, routerName);
487 } catch (Exception ex) {
488 LOG.error("call : Exception caught in Interface {} OperationalStateDown", interfaceName, ex);