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