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