Stale table=44 flows remains on VM deletion
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / NaptEventHandler.java
1 /*
2  * Copyright © 2016, 2018 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.netvirt.natservice.internal;
10
11 import com.google.common.collect.Iterables;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.JdkFutureAdapters;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
17 import java.math.BigInteger;
18 import java.net.InetAddress;
19 import java.net.UnknownHostException;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.Future;
25 import javax.inject.Inject;
26 import javax.inject.Singleton;
27 import org.eclipse.jdt.annotation.NonNull;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
30 import org.opendaylight.controller.sal.common.util.Arguments;
31 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
32 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
33 import org.opendaylight.genius.mdsalutil.ActionInfo;
34 import org.opendaylight.genius.mdsalutil.FlowEntity;
35 import org.opendaylight.genius.mdsalutil.FlowEntityBuilder;
36 import org.opendaylight.genius.mdsalutil.InstructionInfo;
37 import org.opendaylight.genius.mdsalutil.MDSALUtil;
38 import org.opendaylight.genius.mdsalutil.MatchInfo;
39 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
40 import org.opendaylight.genius.mdsalutil.NwConstants;
41 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
42 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
43 import org.opendaylight.genius.mdsalutil.actions.ActionSetDestinationIp;
44 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
45 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
46 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
47 import org.opendaylight.genius.mdsalutil.actions.ActionSetSourceIp;
48 import org.opendaylight.genius.mdsalutil.actions.ActionSetTcpDestinationPort;
49 import org.opendaylight.genius.mdsalutil.actions.ActionSetTcpSourcePort;
50 import org.opendaylight.genius.mdsalutil.actions.ActionSetUdpDestinationPort;
51 import org.opendaylight.genius.mdsalutil.actions.ActionSetUdpSourcePort;
52 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
53 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
54 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
55 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
56 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
57 import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
58 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
59 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Source;
60 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
61 import org.opendaylight.genius.mdsalutil.matches.MatchTcpDestinationPort;
62 import org.opendaylight.genius.mdsalutil.matches.MatchTcpSourcePort;
63 import org.opendaylight.genius.mdsalutil.matches.MatchUdpDestinationPort;
64 import org.opendaylight.genius.mdsalutil.matches.MatchUdpSourcePort;
65 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
66 import org.opendaylight.genius.mdsalutil.packet.IPv4;
67 import org.opendaylight.genius.mdsalutil.packet.TCP;
68 import org.opendaylight.genius.mdsalutil.packet.UDP;
69 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
70 import org.opendaylight.netvirt.elanmanager.api.IElanService;
71 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
72 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
73 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
74 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInput;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
104 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
105 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
106 import org.opendaylight.yangtools.yang.common.RpcResult;
107 import org.slf4j.Logger;
108 import org.slf4j.LoggerFactory;
109
110 @Singleton
111 public class NaptEventHandler {
112     private static final Logger LOG = LoggerFactory.getLogger(NaptEventHandler.class);
113     private final DataBroker dataBroker;
114     private final IMdsalApiManager mdsalManager;
115     private final PacketProcessingService pktService;
116     private final OdlInterfaceRpcService interfaceManagerRpc;
117     private final NaptManager naptManager;
118     private final IElanService elanManager;
119     private final IdManagerService idManager;
120     private final IInterfaceManager interfaceManager;
121     private final SalFlowService salFlowServiceRpc;
122     private final NatOverVxlanUtil natOverVxlanUtil;
123
124     @Inject
125     public NaptEventHandler(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
126                             final NaptManager naptManager,
127                             final PacketProcessingService pktService,
128                             final OdlInterfaceRpcService interfaceManagerRpc,
129                             final IInterfaceManager interfaceManager,
130                             final IElanService elanManager,
131                             final IdManagerService idManager,
132                             final SalFlowService salFlowServiceRpc,
133                             final NatOverVxlanUtil natOverVxlanUtil) {
134         this.dataBroker = dataBroker;
135         this.mdsalManager = mdsalManager;
136         this.naptManager = naptManager;
137         this.pktService = pktService;
138         this.interfaceManagerRpc = interfaceManagerRpc;
139         this.interfaceManager = interfaceManager;
140         this.elanManager = elanManager;
141         this.idManager = idManager;
142         this.salFlowServiceRpc = salFlowServiceRpc;
143         this.natOverVxlanUtil = natOverVxlanUtil;
144     }
145
146     // TODO Clean up the exception handling
147     @SuppressWarnings("checkstyle:IllegalCatch")
148     public void handleEvent(final NAPTEntryEvent naptEntryEvent) {
149     /*
150             Flow programming logic of the OUTBOUND NAPT TABLE :
151             1) Get the internal IP address, port number, router ID from the event.
152             2) Use the NAPT service getExternalAddressMapping() to get the External IP and the port.
153             3) Build the flow for replacing the Internal IP and port with the External IP and port.
154               a) Write the matching criteria.
155               b) Match the router ID in the metadata.
156               d) Write the VPN ID to the metadata.
157               e) Write the other data.
158               f) Set the apply actions instruction with the action setfield.
159             4) Write the flow to the OUTBOUND NAPT Table and forward to FIB table for routing the traffic.
160
161             Flow programming logic of the INBOUND NAPT TABLE :
162             Same as Outbound table logic except that :
163             1) Build the flow for replacing the External IP and port with the Internal IP and port.
164             2) Match the VPN ID in the metadata.
165             3) Write the router ID to the metadata.
166             5) Write the flow to the INBOUND NAPT Table and forward to FIB table for routing the traffic.
167     */
168         try {
169             Long routerId = naptEntryEvent.getRouterId();
170             String internalIpAddress = naptEntryEvent.getIpAddress();
171             int internalPort = naptEntryEvent.getPortNumber();
172             NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
173             String sourceIPPortKey = routerId + NatConstants.COLON_SEPARATOR
174                 + internalIpAddress + NatConstants.COLON_SEPARATOR + internalPort;
175             LOG.trace("handleEvent : Time Elapsed before procesing snat ({}:{}) packet is {} ms,routerId: {},"
176                     + "isPktProcessed:{}",
177                               internalIpAddress, internalPort,
178                               (System.currentTimeMillis() - naptEntryEvent.getObjectCreationTime()), routerId,
179                               naptEntryEvent.isPktProcessed());
180             //Get the DPN ID
181             BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
182             long bgpVpnId = NatConstants.INVALID_ID;
183             if (dpnId == null) {
184                 LOG.warn("handleEvent : dpnId is null. Assuming the router ID {} as the BGP VPN ID and "
185                     + "proceeding....", routerId);
186                 bgpVpnId = routerId;
187                 LOG.debug("handleEvent : BGP VPN ID {}", bgpVpnId);
188                 String vpnName = NatUtil.getRouterName(dataBroker, bgpVpnId);
189                 String routerName = NatUtil.getRouterIdfromVpnInstance(dataBroker, vpnName, internalIpAddress);
190                 if (routerName == null) {
191                     NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
192                     LOG.error("handleEvent: Unable to find router for VpnName {}. Droping packet for SNAT ({})"
193                         + "session", vpnName, sourceIPPortKey);
194                     return;
195                 }
196                 routerId = NatUtil.getVpnId(dataBroker, routerName);
197                 LOG.debug("handleEvent : Router ID {}", routerId);
198                 dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
199                 if (dpnId == null) {
200                     NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
201                     LOG.error("handleEvent: Unable to find router for VpnName {}. Droping packet for SNAT ({})"
202                         + "session", vpnName, sourceIPPortKey);
203                     return;
204                 }
205             }
206             if (naptEntryEvent.getOperation() == NAPTEntryEvent.Operation.ADD) {
207                 LOG.debug("handleEvent : Inside Add operation of NaptEventHandler");
208
209                 // Build and install the NAPT translation flows in the Outbound and Inbound NAPT tables
210                 if (!naptEntryEvent.isPktProcessed()) {
211
212                     // Get the External Gateway MAC Address
213                     String extGwMacAddress = NatUtil.getExtGwMacAddFromRouterId(dataBroker, routerId);
214                     if (extGwMacAddress != null) {
215                         LOG.debug("handleEvent : External Gateway MAC address {} found for External Router ID {}",
216                                   extGwMacAddress, routerId);
217                     } else {
218                         NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
219                         LOG.error("handleEvent: No External Gateway MAC address found for External Router ID {}."
220                             + "Droping packet for SNAT ({}) session", routerId, sourceIPPortKey);
221                         return;
222                     }
223
224                     //Get the external network ID from the ExternalRouter model
225                     Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
226                     if (networkId == null) {
227                         NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
228                         LOG.error("handleEvent: networkId is null. Droping packet for SNAT ({}) session",
229                                  sourceIPPortKey);
230                         return;
231                     }
232
233                     //Get the VPN ID from the ExternalNetworks model
234                     Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
235                     if (vpnUuid == null) {
236                         NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
237                         LOG.error("handleEvent: vpnUuid is null. Droping packet for SNAT ({}) session",
238                                  sourceIPPortKey);
239                         return;
240                     }
241                     Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
242
243                     SessionAddress internalAddress = new SessionAddress(internalIpAddress, internalPort);
244
245                     //Get the external IP address for the corresponding internal IP address
246                     SessionAddress externalAddress =
247                             naptManager.getExternalAddressMapping(routerId, internalAddress,
248                                     naptEntryEvent.getProtocol());
249                     if (externalAddress == null) {
250                         NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
251                         LOG.error("handleEvent: externalAddress is null. Droping packet for SNAT ({}) session",
252                                   sourceIPPortKey);
253                         return;
254                     }
255
256                     Long vpnIdFromExternalSubnet = getVpnIdFromExternalSubnet(routerId,
257                             externalAddress.getIpAddress());
258                     if (vpnIdFromExternalSubnet != NatConstants.INVALID_ID) {
259                         vpnId = vpnIdFromExternalSubnet;
260                     }
261
262                     // Added External Gateway MAC Address
263                     Future<RpcResult<AddFlowOutput>> addFlowResult =
264                             buildAndInstallNatFlowsOptionalRpc(dpnId, NwConstants.INBOUND_NAPT_TABLE, vpnId, routerId,
265                                     bgpVpnId, externalAddress, internalAddress, protocol, extGwMacAddress, true);
266                     final BigInteger finalDpnId = dpnId;
267                     final Long finalVpnId = vpnId;
268                     final Long finalRouterId = routerId;
269                     final long finalBgpVpnId = bgpVpnId;
270                     Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
271                                         new FutureCallback<RpcResult<AddFlowOutput>>() {
272
273                                 @Override
274                                 public void onSuccess(@Nullable RpcResult<AddFlowOutput> result) {
275                                     LOG.debug("handleEvent : Configured inbound rule for {} to {}",
276                                              internalAddress, externalAddress);
277                                     Future<RpcResult<AddFlowOutput>> addFlowResult =
278                                             buildAndInstallNatFlowsOptionalRpc(finalDpnId,
279                                                     NwConstants.OUTBOUND_NAPT_TABLE, finalVpnId, finalRouterId,
280                                                     finalBgpVpnId, internalAddress, externalAddress, protocol,
281                                                     extGwMacAddress, true);
282                                     Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
283                                             new FutureCallback<RpcResult<AddFlowOutput>>() {
284
285                                             @Override
286                                             public void onSuccess(@Nullable RpcResult<AddFlowOutput> result) {
287                                                 LOG.debug("handleEvent : Configured outbound rule, sending packet out"
288                                                         + "from {} to {}", internalAddress, externalAddress);
289                                                 prepareAndSendPacketOut(naptEntryEvent, finalRouterId, sourceIPPortKey);
290                                             }
291
292                                             @Override
293                                             public void onFailure(@NonNull Throwable throwable) {
294                                                 LOG.error("handleEvent : Error configuring outbound "
295                                                         + "SNAT flows using RPC for SNAT connection from {} to {}",
296                                                                   internalAddress, externalAddress);
297                                             }
298                                         }, MoreExecutors.directExecutor());
299                                 }
300
301                                 @Override
302                                 public void onFailure(@NonNull Throwable throwable) {
303                                     LOG.error("handleEvent : Error configuring inbound SNAT flows "
304                                             + "using RPC for SNAT connection from {} to {}",
305                                             internalAddress, externalAddress);
306                                 }
307                             }, MoreExecutors.directExecutor());
308                 } else {
309                     prepareAndSendPacketOut(naptEntryEvent, routerId, sourceIPPortKey);
310                 }
311                 LOG.trace("handleEvent : Time elapsed after Processsing snat ({}:{}) packet: {}ms,isPktProcessed:{} ",
312                         naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(),
313                         System.currentTimeMillis() - naptEntryEvent.getObjectCreationTime(),
314                         naptEntryEvent.isPktProcessed());
315             } else {
316                 LOG.debug("handleEvent : Inside delete Operation of NaptEventHandler");
317                 handleFlowRemoved(naptEntryEvent, routerId, sourceIPPortKey, dpnId);
318                 LOG.info("handleEvent : exited for removeEvent for IP {}, port {}, routerID : {}",
319                         naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
320             }
321         } catch (Exception e) {
322             LOG.error("handleEvent :Exception in NaptEventHandler.handleEvent() payload {}", naptEntryEvent, e);
323         }
324     }
325
326     private void prepareAndSendPacketOut(NAPTEntryEvent naptEntryEvent, Long routerId, String sourceIPPortKey) {
327         //Send Packetout - tcp or udp packets which got punted to controller.
328         BigInteger metadata = naptEntryEvent.getPacketReceived().getMatch().getMetadata().getMetadata();
329         byte[] inPayload = naptEntryEvent.getPacketReceived().getPayload();
330         Ethernet ethPkt = new Ethernet();
331         if (inPayload != null) {
332             try {
333                 ethPkt.deserialize(inPayload, 0, inPayload.length * Byte.SIZE);
334             } catch (PacketException e) {
335                 NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
336                 LOG.error("prepareAndSendPacketOut : Failed to decode Packet", e);
337                 return;
338             }
339         }
340
341         long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
342         LOG.debug("prepareAndSendPacketOut : portTag from incoming packet is {}", portTag);
343         List<ActionInfo> actionInfos = new ArrayList<>();
344         String interfaceName = getInterfaceNameFromTag(portTag);
345         BigInteger dpnID = null;
346         int portNum = -1;
347         if (interfaceName != null) {
348             LOG.debug("prepareAndSendPacketOut : interfaceName fetched from portTag is {}", interfaceName);
349             org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
350                     .interfaces.Interface iface = null;
351             int vlanId = 0;
352             iface = interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName);
353             if (iface == null) {
354                 NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
355                 LOG.error("prepareAndSendPacketOut : Unable to read interface {} from config DataStore", interfaceName);
356                 return;
357             }
358
359             IfL2vlan ifL2vlan = iface.augmentation(IfL2vlan.class);
360             if (ifL2vlan != null && ifL2vlan.getVlanId() != null) {
361                 vlanId = ifL2vlan.getVlanId().getValue() == null ? 0 : ifL2vlan.getVlanId().getValue();
362             }
363             InterfaceInfo infInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
364             if (infInfo == null) {
365                 LOG.error("prepareAndSendPacketOut : error in getting interfaceInfo from Operation DS");
366                 return;
367             }
368             dpnID = infInfo.getDpId();
369             portNum = infInfo.getPortNo();
370             if (ethPkt.getEtherType() != (short) NwConstants.ETHTYPE_802_1Q) {
371                 // VLAN Access port
372                 LOG.debug("prepareAndSendPacketOut : vlanId is {}", vlanId);
373                 if (vlanId != 0) {
374                     // Push vlan
375                     actionInfos.add(new ActionPushVlan(0));
376                     actionInfos.add(new ActionSetFieldVlanVid(1, vlanId));
377                 } else {
378                     LOG.debug("prepareAndSendPacketOut : No vlanId {}, may be untagged", vlanId);
379                 }
380             } else {
381                 // VLAN Trunk Port
382                 LOG.debug("prepareAndSendPacketOut : This is VLAN Trunk port case - need not do VLAN tagging again");
383             }
384         } else {
385             // This case will be hit for packets send from non-napt switch.
386             LOG.info("prepareAndSendPacketOut : interfaceName is not available.Retrieve from packet received");
387             NodeConnectorId nodeId = naptEntryEvent.getPacketReceived().getMatch().getInPort();
388             portNum = Integer.parseInt(nodeId.getValue());
389             LOG.debug("prepareAndSendPacketOut : in_port portNum : {}", portNum);
390             //List<PathArgument> dpnNodes = naptEntryEvent.getPacketReceived().getIngress().getValue().getPath();
391             Iterable<PathArgument> outArgs = naptEntryEvent.getPacketReceived().getIngress().getValue()
392                     .getPathArguments();
393             PathArgument pathArgument = Iterables.get(outArgs, 2);
394             LOG.debug("prepareAndSendPacketOut : pathArgument : {}", pathArgument);
395             InstanceIdentifier.IdentifiableItem<?, ?> item = Arguments.checkInstanceOf(pathArgument,
396                     InstanceIdentifier.IdentifiableItem.class);
397             NodeConnectorKey key = Arguments.checkInstanceOf(item.getKey(), NodeConnectorKey.class);
398             LOG.info("prepareAndSendPacketOut : NodeConnectorKey key : {}", key.getId().getValue());
399             String dpnKey = key.getId().getValue();
400             if (dpnKey.contains(NatConstants.COLON_SEPARATOR)) {
401                 dpnID = new BigInteger(dpnKey.split(NatConstants.COLON_SEPARATOR)[1]);
402             }
403         }
404         byte[] pktOut = buildNaptPacketOut(ethPkt);
405
406         if (pktOut != null) {
407             String routerName = NatUtil.getRouterName(dataBroker, routerId);
408             long tunId = NatUtil.getTunnelIdForNonNaptToNaptFlow(dataBroker, natOverVxlanUtil, elanManager,
409                     idManager, routerId, routerName);
410             LOG.info("sendNaptPacketOut for ({}:{}) on dpnId {} portNum {} tunId {}",
411                 naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), dpnID, portNum, tunId);
412             sendNaptPacketOut(pktOut, dpnID, portNum, actionInfos, tunId);
413         } else {
414             LOG.warn("prepareAndSendPacketOut : Unable to send Packet Out");
415         }
416     }
417
418     public void buildAndInstallNatFlows(BigInteger dpnId, short tableId, long vpnId, long routerId,
419                                                long bgpVpnId, SessionAddress actualSourceAddress,
420                                                SessionAddress translatedSourceAddress,
421                                                NAPTEntryEvent.Protocol protocol, String extGwMacAddress) {
422         buildAndInstallNatFlowsOptionalRpc(dpnId, tableId, vpnId, routerId, bgpVpnId, actualSourceAddress,
423                 translatedSourceAddress, protocol, extGwMacAddress, false);
424     }
425
426     private Future<RpcResult<AddFlowOutput>> buildAndInstallNatFlowsOptionalRpc(
427             BigInteger dpnId, short tableId, long vpnId, long routerId, long bgpVpnId,
428             SessionAddress actualSourceAddress, SessionAddress translatedSourceAddress,
429             NAPTEntryEvent.Protocol protocol, String extGwMacAddress,
430             boolean sendRpc) {
431         LOG.debug("buildAndInstallNatFlowsOptionalRpc : Build and install table={} flow on dpnId {} and routerId {}",
432                 tableId, dpnId, routerId);
433         //Build the flow for replacing the actual IP and port with the translated IP and port.
434         int idleTimeout = 0;
435         if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
436             idleTimeout = NatConstants.DEFAULT_NAPT_IDLE_TIMEOUT;
437         }
438         long intranetVpnId;
439         if (bgpVpnId != NatConstants.INVALID_ID) {
440             intranetVpnId = bgpVpnId;
441         } else {
442             intranetVpnId = routerId;
443         }
444         LOG.debug("buildAndInstallNatFlowsOptionalRpc : Intranet VPN ID {} Router ID {}", intranetVpnId, routerId);
445         String translatedIp = translatedSourceAddress.getIpAddress();
446         int translatedPort = translatedSourceAddress.getPortNumber();
447         String actualIp = actualSourceAddress.getIpAddress();
448         int actualPort = actualSourceAddress.getPortNumber();
449         String switchFlowRef = null;
450         if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
451             switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), actualIp, actualPort,
452                 protocol.name());
453         } else {
454             switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), translatedIp,
455                 translatedPort, protocol.name());
456         }
457         FlowEntity snatFlowEntity = new FlowEntityBuilder()
458             .setDpnId(dpnId)
459             .setTableId(tableId)
460             .setFlowId(switchFlowRef)
461             .setPriority(NatConstants.DEFAULT_NAPT_FLOW_PRIORITY)
462             .setFlowName(NatConstants.NAPT_FLOW_NAME)
463             .setIdleTimeOut(idleTimeout)
464             .setHardTimeOut(0)
465             .setCookie(NatUtil.getCookieNaptFlow(routerId))
466             .setMatchInfoList(buildAndGetMatchInfo(actualIp, actualPort, tableId, protocol, intranetVpnId))
467             .setInstructionInfoList(buildAndGetSetActionInstructionInfo(translatedIp, translatedPort,
468                                             intranetVpnId, vpnId, tableId, protocol, extGwMacAddress))
469             .setSendFlowRemFlag(true)
470             .build();
471
472         // Install flows using RPC to prevent race with future packet-out that depends on this flow
473         Future<RpcResult<AddFlowOutput>> addFlowResult = null;
474         if (sendRpc) {
475             Flow flow = snatFlowEntity.getFlowBuilder().build();
476             NodeRef nodeRef = getNodeRef(dpnId);
477             FlowRef flowRef = getFlowRef(dpnId, flow);
478             AddFlowInput addFlowInput = new AddFlowInputBuilder(flow).setFlowRef(flowRef).setNode(nodeRef).build();
479             long startTime = System.currentTimeMillis();
480             addFlowResult = salFlowServiceRpc.addFlow(addFlowInput);
481             LOG.debug("buildAndInstallNatFlowsOptionalRpc : Time elapsed for salFlowServiceRpc table {}: {}ms ",
482                     tableId, System.currentTimeMillis() - startTime);
483          // Keep flow installation through MDSAL as well to be able to handle switch failures
484             startTime = System.currentTimeMillis();
485             mdsalManager.installFlow(snatFlowEntity);
486             LOG.trace("buildAndInstallNatFlowsOptionalRpc : Time Elapsed while installing table-{} "
487                     + "flow on DPN:{} for snat packet({},{}): {}ms", tableId, dpnId,
488                     actualSourceAddress.getIpAddress(),actualSourceAddress.getPortNumber(),
489                     System.currentTimeMillis() - startTime);
490         } else {
491             long startTime = System.currentTimeMillis();
492             mdsalManager.syncInstallFlow(snatFlowEntity);
493             LOG.trace("buildAndInstallNatFlowsOptionalRpc : Time Elapsed while installing table-{} "
494                     + "flow on DPN:{} for snat packet({},{}): {}ms", tableId, dpnId,
495                     actualSourceAddress.getIpAddress(),actualSourceAddress.getPortNumber(),
496                     System.currentTimeMillis() - startTime);
497         }
498         LOG.trace("buildAndInstallNatFlowsOptionalRpc : Exited");
499
500         return addFlowResult;
501     }
502
503     private static Node buildInventoryDpnNode(BigInteger dpnId) {
504         NodeId nodeId = new NodeId("openflow:" + dpnId);
505         Node nodeDpn = new NodeBuilder().setId(nodeId).withKey(new NodeKey(nodeId)).build();
506         return nodeDpn;
507     }
508
509     private static NodeRef getNodeRef(BigInteger dpnId) {
510         NodeId nodeId = new NodeId("openflow:" + dpnId);
511         return new NodeRef(InstanceIdentifier.builder(Nodes.class)
512                 .child(Node.class, new NodeKey(nodeId)).build());
513     }
514
515     public static FlowRef getFlowRef(BigInteger dpId, Flow flow) {
516         FlowKey flowKey = new FlowKey(new FlowId(flow.getId()));
517         Node nodeDpn = buildInventoryDpnNode(dpId);
518         InstanceIdentifier<Flow> flowInstanceId =
519                 InstanceIdentifier.builder(Nodes.class)
520                 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
521                 .child(Table.class, new TableKey(flow.getTableId()))
522                 .child(Flow.class, flowKey)
523                 .build();
524         return new FlowRef(flowInstanceId);
525     }
526
527     @Nullable
528     private static List<MatchInfo> buildAndGetMatchInfo(String ip, int port, short tableId,
529                                                         NAPTEntryEvent.Protocol protocol, long segmentId) {
530         MatchInfo ipMatchInfo = null;
531         MatchInfo portMatchInfo = null;
532         MatchInfo protocolMatchInfo = null;
533         InetAddress ipAddress = null;
534         String ipAddressAsString = null;
535         try {
536             ipAddress = InetAddress.getByName(ip);
537             ipAddressAsString = ipAddress.getHostAddress();
538
539         } catch (UnknownHostException e) {
540             LOG.error("buildAndGetMatchInfo : UnknowHostException in buildAndGetMatchInfo."
541                     + "Failed  to build NAPT Flow for ip {}", ip, e);
542             return null;
543         }
544
545         MatchInfo metaDataMatchInfo = null;
546         if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
547             ipMatchInfo = new MatchIpv4Source(ipAddressAsString, "32");
548             if (protocol == NAPTEntryEvent.Protocol.TCP) {
549                 protocolMatchInfo = MatchIpProtocol.TCP;
550                 portMatchInfo = new MatchTcpSourcePort(port);
551             } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
552                 protocolMatchInfo = MatchIpProtocol.UDP;
553                 portMatchInfo = new MatchUdpSourcePort(port);
554             }
555             metaDataMatchInfo =
556                     new MatchMetadata(MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID);
557         } else {
558             ipMatchInfo = new MatchIpv4Destination(ipAddressAsString, "32");
559             if (protocol == NAPTEntryEvent.Protocol.TCP) {
560                 protocolMatchInfo = MatchIpProtocol.TCP;
561                 portMatchInfo = new MatchTcpDestinationPort(port);
562             } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
563                 protocolMatchInfo = MatchIpProtocol.UDP;
564                 portMatchInfo = new MatchUdpDestinationPort(port);
565             }
566             //metaDataMatchInfo = new MatchMetadata(BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID);
567         }
568         ArrayList<MatchInfo> matchInfo = new ArrayList<>();
569         matchInfo.add(MatchEthernetType.IPV4);
570         matchInfo.add(ipMatchInfo);
571         matchInfo.add(protocolMatchInfo);
572         matchInfo.add(portMatchInfo);
573         if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
574             matchInfo.add(metaDataMatchInfo);
575         }
576         return matchInfo;
577     }
578
579     @NonNull
580     private static List<InstructionInfo> buildAndGetSetActionInstructionInfo(String ipAddress, int port,
581                                                                              long segmentId, long vpnId,
582                                                                              short tableId,
583                                                                              NAPTEntryEvent.Protocol protocol,
584                                                                              String extGwMacAddress) {
585         ActionInfo ipActionInfo = null;
586         ActionInfo macActionInfo = null;
587         ActionInfo portActionInfo = null;
588         ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
589         ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
590         switch (tableId) {
591             case NwConstants.OUTBOUND_NAPT_TABLE:
592                 ipActionInfo = new ActionSetSourceIp(ipAddress);
593                 // Added External Gateway MAC Address
594                 macActionInfo = new ActionSetFieldEthernetSource(new MacAddress(extGwMacAddress));
595                 if (protocol == NAPTEntryEvent.Protocol.TCP) {
596                     portActionInfo = new ActionSetTcpSourcePort(port);
597                 } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
598                     portActionInfo = new ActionSetUdpSourcePort(port);
599                 }
600                 // reset the split-horizon bit to allow traffic from tunnel to be sent back to the provider port
601                 instructionInfo.add(new InstructionWriteMetadata(MetaDataUtil.getVpnIdMetadata(vpnId),
602                     MetaDataUtil.METADATA_MASK_VRFID.or(MetaDataUtil.METADATA_MASK_SH_FLAG)));
603                 break;
604
605             case NwConstants.INBOUND_NAPT_TABLE:
606                 ipActionInfo = new ActionSetDestinationIp(ipAddress);
607                 if (protocol == NAPTEntryEvent.Protocol.TCP) {
608                     portActionInfo = new ActionSetTcpDestinationPort(port);
609                 } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
610                     portActionInfo = new ActionSetUdpDestinationPort(port);
611                 }
612                 instructionInfo.add(new InstructionWriteMetadata(
613                         MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID));
614                 break;
615
616             default:
617                 LOG.error("buildAndGetSetActionInstructionInfo : Neither OUTBOUND_NAPT_TABLE nor "
618                         + "INBOUND_NAPT_TABLE matches with input table id {}", tableId);
619                 return Collections.emptyList();
620         }
621
622         listActionInfo.add(ipActionInfo);
623         listActionInfo.add(portActionInfo);
624         if (macActionInfo != null) {
625             listActionInfo.add(macActionInfo);
626             LOG.debug("buildAndGetSetActionInstructionInfo : External GW MAC Address {} is found  ", macActionInfo);
627         }
628         instructionInfo.add(new InstructionApplyActions(listActionInfo));
629         instructionInfo.add(new InstructionGotoTable(NwConstants.NAPT_PFIB_TABLE));
630
631         return instructionInfo;
632     }
633
634     void removeNatFlows(BigInteger dpnId, short tableId ,long segmentId, String ip, int port, String protocol) {
635         if (dpnId == null || dpnId.equals(BigInteger.ZERO)) {
636             LOG.error("removeNatFlows : DPN ID {} is invalid" , dpnId);
637             return;
638         }
639         LOG.debug("removeNatFlows : Remove NAPT flows for dpnId {}, segmentId {}, ip {} and port {} ",
640             dpnId, segmentId, ip, port);
641
642         //Build the flow with the port IP and port as the match info.
643         String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(segmentId), ip, port, protocol);
644         FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef);
645         LOG.debug("removeNatFlows : Remove the flow in the table {} for the switch with the DPN ID {}",
646                 tableId, dpnId);
647         long startTime = System.currentTimeMillis();
648         mdsalManager.removeFlow(snatFlowEntity);
649         LOG.trace("removeNatFlows : Time Elapsed for removing table-{} flow from switch with DPN ID:{} "
650                 + "for SNAT ({}:{}) session:{}ms", tableId, dpnId, ip, port, System.currentTimeMillis() - startTime);
651     }
652
653     @Nullable
654     @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
655     protected byte[] buildNaptPacketOut(Ethernet etherPkt) {
656         LOG.debug("buildNaptPacketOut : About to build Napt Packet Out");
657         if (etherPkt.getPayload() instanceof IPv4) {
658             byte[] rawPkt;
659             IPv4 ipPkt = (IPv4) etherPkt.getPayload();
660             if (ipPkt.getPayload() instanceof TCP || ipPkt.getPayload() instanceof UDP) {
661                 try {
662                     rawPkt = etherPkt.serialize();
663                     return rawPkt;
664                 } catch (PacketException e2) {
665                     LOG.error("failed to build NAPT Packet out ", e2);
666                     return null;
667                 }
668             } else {
669                 LOG.error("buildNaptPacketOut : Unable to build NaptPacketOut since its neither TCP nor UDP");
670                 return null;
671             }
672         }
673         LOG.error("buildNaptPacketOut : Unable to build NaptPacketOut since its not IPv4 packet");
674         return null;
675     }
676
677     private void sendNaptPacketOut(byte[] pktOut, BigInteger dpnID, int portNum,
678             List<ActionInfo> actionInfos, Long tunId) {
679         LOG.trace("sendNaptPacketOut: Sending packet out DpId {}, interface {}", dpnID, portNum);
680         // set inPort, and action as OFPP_TABLE so that it starts from table 0 (lowest table as per spec)
681         actionInfos.add(new ActionSetFieldTunnelId(2, BigInteger.valueOf(tunId)));
682         actionInfos.add(new ActionOutput(3, new Uri("0xfffffff9")));
683         NodeConnectorRef inPort = MDSALUtil.getNodeConnRef(dpnID, String.valueOf(portNum));
684         LOG.debug("sendNaptPacketOut : inPort for packetout is being set to {}", portNum);
685         TransmitPacketInput output = MDSALUtil.getPacketOut(actionInfos, pktOut, dpnID.longValue(), inPort);
686         LOG.debug("sendNaptPacketOut : Transmitting packet: {}, inPort {}", output, inPort);
687
688         JdkFutures.addErrorLogging(pktService.transmitPacket(output), LOG, "Transmit packet");
689     }
690
691     private String getInterfaceNameFromTag(long portTag) {
692         String interfaceName = null;
693         GetInterfaceFromIfIndexInput input =
694             new GetInterfaceFromIfIndexInputBuilder().setIfIndex((int) portTag).build();
695         Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
696             interfaceManagerRpc.getInterfaceFromIfIndex(input);
697         try {
698             GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
699             if (output != null) {
700                 interfaceName = output.getInterfaceName();
701             }
702         } catch (InterruptedException | ExecutionException e) {
703             LOG.error("getInterfaceNameFromTag : Error while retrieving the interfaceName from tag using "
704                 + "getInterfaceFromIfIndex RPC");
705         }
706         LOG.trace("getInterfaceNameFromTag : Returning interfaceName {} for tag {} form getInterfaceNameFromTag",
707             interfaceName, portTag);
708         return interfaceName;
709     }
710
711     private long getVpnIdFromExternalSubnet(Long routerId, String externalIpAddress) {
712         String routerName = NatUtil.getRouterName(dataBroker, routerId);
713         if (routerName != null) {
714             Routers extRouter = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
715             if (extRouter != null) {
716                 return NatUtil.getExternalSubnetVpnIdForRouterExternalIp(dataBroker, externalIpAddress, extRouter);
717             }
718         }
719
720         return NatConstants.INVALID_ID;
721     }
722
723     public void handleFlowRemoved(NAPTEntryEvent naptEntryEvent, Long routerId, String sourceIPPortKey,
724                                   BigInteger dpnId) {
725         String internalIpv4HostAddress = naptEntryEvent.getIpAddress();
726         Integer internalPortNumber = naptEntryEvent.getPortNumber();
727         NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
728         //Get the external IP address and the port from the model
729         LOG.trace("handleFlowRemoved: Failed to remove snat flow internalIP {} with "
730                 + "Port {} protocol {} for routerId {} in OUTBOUNDTABLE of naptSwitch {}",
731             internalIpv4HostAddress, internalPortNumber, protocol, routerId, dpnId);
732         removeNatFlows(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId, internalIpv4HostAddress,
733             internalPortNumber, protocol.name());
734
735         LOG.trace("handleFlowRemoved: Failed to remove snat flow internalIP {} with "
736                 + "Port {} protocol {} for routerId {} in INBOUNDTABLE of naptSwitch {}",
737             internalIpv4HostAddress, internalPortNumber, protocol, routerId, dpnId);
738         removeNatFlows(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId, internalIpv4HostAddress,
739             internalPortNumber, protocol.name());
740
741         //Remove the SourceIP:Port key from the Napt packet handler map.
742         NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
743
744         //Remove the mapping of internal fixed ip/port to external ip/port from the datastore.
745         SessionAddress internalSessionAddress = new SessionAddress(internalIpv4HostAddress, internalPortNumber);
746         naptManager.releaseIpExtPortMapping(routerId, internalSessionAddress, protocol);
747     }
748 }