@Immutable FlowEntity with FlowEntityBuilder
[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 java.math.BigInteger;
12 import java.net.InetAddress;
13 import java.net.UnknownHostException;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.Future;
18 import javax.inject.Inject;
19 import javax.inject.Singleton;
20 import org.opendaylight.controller.liblldp.NetUtils;
21 import org.opendaylight.controller.liblldp.PacketException;
22 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
24 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
25 import org.opendaylight.genius.mdsalutil.ActionInfo;
26 import org.opendaylight.genius.mdsalutil.FlowEntity;
27 import org.opendaylight.genius.mdsalutil.FlowEntityBuilder;
28 import org.opendaylight.genius.mdsalutil.InstructionInfo;
29 import org.opendaylight.genius.mdsalutil.MDSALUtil;
30 import org.opendaylight.genius.mdsalutil.MatchInfo;
31 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
32 import org.opendaylight.genius.mdsalutil.NwConstants;
33 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
34 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
35 import org.opendaylight.genius.mdsalutil.actions.ActionSetDestinationIp;
36 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
37 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
38 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
39 import org.opendaylight.genius.mdsalutil.actions.ActionSetSourceIp;
40 import org.opendaylight.genius.mdsalutil.actions.ActionSetTcpDestinationPort;
41 import org.opendaylight.genius.mdsalutil.actions.ActionSetTcpSourcePort;
42 import org.opendaylight.genius.mdsalutil.actions.ActionSetUdpDestinationPort;
43 import org.opendaylight.genius.mdsalutil.actions.ActionSetUdpSourcePort;
44 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
45 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
46 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
47 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
48 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
49 import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
50 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
51 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Source;
52 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
53 import org.opendaylight.genius.mdsalutil.matches.MatchTcpDestinationPort;
54 import org.opendaylight.genius.mdsalutil.matches.MatchTcpSourcePort;
55 import org.opendaylight.genius.mdsalutil.matches.MatchUdpDestinationPort;
56 import org.opendaylight.genius.mdsalutil.matches.MatchUdpSourcePort;
57 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
58 import org.opendaylight.genius.mdsalutil.packet.IPv4;
59 import org.opendaylight.genius.mdsalutil.packet.TCP;
60 import org.opendaylight.genius.mdsalutil.packet.UDP;
61 import org.opendaylight.netvirt.elanmanager.api.IElanService;
62 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
63 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
64 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInput;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
75 import org.opendaylight.yangtools.yang.common.RpcResult;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
78
79 @Singleton
80 public class NaptEventHandler {
81     private static final Logger LOG = LoggerFactory.getLogger(NaptEventHandler.class);
82     private final DataBroker dataBroker;
83     private static IMdsalApiManager mdsalManager;
84     private final PacketProcessingService pktService;
85     private final OdlInterfaceRpcService interfaceManagerRpc;
86     private final NaptManager naptManager;
87     private final IElanService elanManager;
88     private final IdManagerService idManager;
89     private final IInterfaceManager interfaceManager;
90
91     @Inject
92     public NaptEventHandler(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
93                             final NaptManager naptManager,
94                             final PacketProcessingService pktService,
95                             final OdlInterfaceRpcService interfaceManagerRpc,
96                             final IInterfaceManager interfaceManager,
97                             final IElanService elanManager,
98                             final IdManagerService idManager) {
99         this.dataBroker = dataBroker;
100         NaptEventHandler.mdsalManager = mdsalManager;
101         this.naptManager = naptManager;
102         this.pktService = pktService;
103         this.interfaceManagerRpc = interfaceManagerRpc;
104         this.interfaceManager = interfaceManager;
105         this.elanManager = elanManager;
106         this.idManager = idManager;
107     }
108
109     // TODO Clean up the exception handling
110     @SuppressWarnings("checkstyle:IllegalCatch")
111     public void handleEvent(NAPTEntryEvent naptEntryEvent) {
112     /*
113             Flow programming logic of the OUTBOUND NAPT TABLE :
114             1) Get the internal IP address, port number, router ID from the event.
115             2) Use the NAPT service getExternalAddressMapping() to get the External IP and the port.
116             3) Build the flow for replacing the Internal IP and port with the External IP and port.
117               a) Write the matching criteria.
118               b) Match the router ID in the metadata.
119               d) Write the VPN ID to the metadata.
120               e) Write the other data.
121               f) Set the apply actions instruction with the action setfield.
122             4) Write the flow to the OUTBOUND NAPT Table and forward to FIB table for routing the traffic.
123
124             Flow programming logic of the INBOUND NAPT TABLE :
125             Same as Outbound table logic except that :
126             1) Build the flow for replacing the External IP and port with the Internal IP and port.
127             2) Match the VPN ID in the metadata.
128             3) Write the router ID to the metadata.
129             5) Write the flow to the INBOUND NAPT Table and forward to FIB table for routing the traffic.
130     */
131         try {
132             Long routerId = naptEntryEvent.getRouterId();
133             LOG.info("NAT Service : handleEvent() entry for IP {}, port {}, routerID {}",
134                 naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
135             //Get the DPN ID
136             BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
137             long bgpVpnId = NatConstants.INVALID_ID;
138             if (dpnId == null) {
139                 LOG.warn("NAT Service : dpnId is null. Assuming the router ID {} as the BGP VPN ID and proceeding....",
140                     routerId);
141                 bgpVpnId = routerId;
142                 LOG.debug("NAT Service : BGP VPN ID {}", bgpVpnId);
143                 String vpnName = NatUtil.getRouterName(dataBroker, bgpVpnId);
144                 String routerName = NatUtil.getRouterIdfromVpnInstance(dataBroker, vpnName);
145                 if (routerName == null) {
146                     LOG.error("NAT Service: Unable to find router for VpnName {}", vpnName);
147                     return;
148                 }
149                 routerId = NatUtil.getVpnId(dataBroker, routerName);
150                 LOG.debug("NAT Service : Router ID {}", routerId);
151                 dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
152                 if (dpnId == null) {
153                     LOG.error("NAT Service : dpnId is null for the router {}", routerId);
154                     return;
155                 }
156             }
157             if (naptEntryEvent.getOperation() == NAPTEntryEvent.Operation.ADD) {
158                 LOG.debug("NAT Service : Inside Add operation of NaptEventHandler");
159                 // Get the External Gateway MAC Address
160                 String extGwMacAddress = NatUtil.getExtGwMacAddFromRouterId(dataBroker, routerId);
161                 if (extGwMacAddress != null) {
162                     LOG.debug("NAT Service : External Gateway MAC address {} found for External Router ID {}",
163                             extGwMacAddress, routerId);
164                 } else {
165                     LOG.error("NAT Service : No External Gateway MAC address found for External Router ID {}",
166                             routerId);
167                     return;
168                 }
169                 //Get the external network ID from the ExternalRouter model
170                 Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
171                 if (networkId == null) {
172                     LOG.error("NAT Service : networkId is null");
173                     return;
174                 }
175
176                 //Get the VPN ID from the ExternalNetworks model
177                 Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
178                 if (vpnUuid == null) {
179                     LOG.error("NAT Service : vpnUuid is null");
180                     return;
181                 }
182                 Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
183
184                 //Get the internal IpAddress, internal port number from the event
185                 String internalIpAddress = naptEntryEvent.getIpAddress();
186                 int internalPort = naptEntryEvent.getPortNumber();
187                 SessionAddress internalAddress = new SessionAddress(internalIpAddress, internalPort);
188                 NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
189
190                 //Get the external IP address for the corresponding internal IP address
191                 SessionAddress externalAddress =
192                     naptManager.getExternalAddressMapping(routerId, internalAddress, naptEntryEvent.getProtocol());
193                 if (externalAddress == null) {
194                     LOG.error("NAT Service : externalAddress is null");
195                     return;
196                 }
197                 // Build and install the NAPT translation flows in the Outbound and Inbound NAPT tables
198                 if (!naptEntryEvent.isPktProcessed()) {
199                     Long vpnIdFromExternalSubnet = getVpnIdFromExternalSubnet(dataBroker, routerId,
200                             externalAddress.getIpAddress());
201                     if (vpnIdFromExternalSubnet != NatConstants.INVALID_ID) {
202                         vpnId = vpnIdFromExternalSubnet;
203                     }
204
205                     // Added External Gateway MAC Address
206                     buildAndInstallNatFlows(dpnId, NwConstants.INBOUND_NAPT_TABLE, vpnId, routerId, bgpVpnId,
207                         externalAddress, internalAddress, protocol, extGwMacAddress);
208                     buildAndInstallNatFlows(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, vpnId, routerId, bgpVpnId,
209                             internalAddress, externalAddress, protocol, extGwMacAddress);
210                 }
211
212                 //Send Packetout - tcp or udp packets which got punted to controller.
213                 BigInteger metadata = naptEntryEvent.getPacketReceived().getMatch().getMetadata().getMetadata();
214                 byte[] inPayload = naptEntryEvent.getPacketReceived().getPayload();
215                 Ethernet ethPkt = new Ethernet();
216                 if (inPayload != null) {
217                     try {
218                         ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NumBitsInAByte);
219                     } catch (Exception e) {
220                         LOG.warn("NAT Service : Failed to decode Packet", e);
221                         return;
222                     }
223                 }
224
225
226                 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
227                 LOG.debug("NAT Service : portTag from incoming packet is {}", portTag);
228                 String interfaceName = getInterfaceNameFromTag(portTag);
229                 LOG.debug("NAT Service : interfaceName fetched from portTag is {}", interfaceName);
230                 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
231                     .interfaces.Interface iface = null;
232                 int vlanId = 0;
233                 iface = interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName);
234                 if (iface == null) {
235                     LOG.error("NAT Service : Unable to read interface {} from config DataStore", interfaceName);
236                     return;
237                 }
238                 IfL2vlan ifL2vlan = iface.getAugmentation(IfL2vlan.class);
239                 if (ifL2vlan != null && ifL2vlan.getVlanId() != null) {
240                     vlanId = ifL2vlan.getVlanId().getValue() == null ? 0 : ifL2vlan.getVlanId().getValue();
241                 }
242                 InterfaceInfo infInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
243                 if (infInfo != null) {
244                     LOG.debug("NAT Service : portName fetched from interfaceManager is {}", infInfo.getPortName());
245                 }
246
247                 byte[] pktOut = buildNaptPacketOut(ethPkt);
248
249                 List<ActionInfo> actionInfos = new ArrayList<>();
250                 if (ethPkt.getPayload() instanceof IPv4) {
251                     IPv4 ipPkt = (IPv4) ethPkt.getPayload();
252                     if (ipPkt.getPayload() instanceof TCP || ipPkt.getPayload() instanceof UDP) {
253                         if (ethPkt.getEtherType() != (short) NwConstants.ETHTYPE_802_1Q) {
254                             // VLAN Access port
255                             if (infInfo != null) {
256                                 LOG.debug("NAT Service : vlanId is {}", vlanId);
257                                 if (vlanId != 0) {
258                                     // Push vlan
259                                     actionInfos.add(new ActionPushVlan(0));
260                                     actionInfos.add(new ActionSetFieldVlanVid(1, vlanId));
261                                 } else {
262                                     LOG.debug("NAT Service : No vlanId {}, may be untagged", vlanId);
263                                 }
264                             } else {
265                                 LOG.error("NAT Service : error in getting interfaceInfo");
266                                 return;
267                             }
268                         } else {
269                             // VLAN Trunk Port
270                             LOG.debug("NAT Service : This is VLAN Trunk port case - need not do VLAN tagging again");
271                         }
272                     }
273                 }
274                 if (pktOut != null) {
275                     String routerName = NatUtil.getRouterName(dataBroker, routerId);
276                     long tunId = NatUtil.getTunnelIdForNonNaptToNaptFlow(dataBroker, elanManager, idManager, routerId,
277                             routerName);
278                     sendNaptPacketOut(pktOut, infInfo, actionInfos, tunId);
279                 } else {
280                     LOG.warn("NAT Service : Unable to send Packet Out");
281                 }
282
283             } else {
284                 LOG.debug("NAT Service : Inside delete Operation of NaptEventHandler");
285                 removeNatFlows(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId, naptEntryEvent.getIpAddress(),
286                     naptEntryEvent.getPortNumber());
287             }
288
289             LOG.info("NAT Service : handleNaptEvent() exited for IP {}, port {}, routerID : {}",
290                 naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
291         } catch (Exception e) {
292             LOG.error("NAT Service :Exception in NaptEventHandler.handleEvent() payload {}", naptEntryEvent, e);
293         }
294     }
295
296     public static void buildAndInstallNatFlows(BigInteger dpnId, short tableId, long vpnId, long routerId,
297                                                long bgpVpnId, SessionAddress actualSourceAddress,
298                                                SessionAddress translatedSourceAddress,
299                                                NAPTEntryEvent.Protocol protocol, String extGwMacAddress) {
300         LOG.debug("NAT Service : Build and install NAPT flows in InBound and OutBound tables for "
301             + "dpnId {} and routerId {}", dpnId, routerId);
302         //Build the flow for replacing the actual IP and port with the translated IP and port.
303         int idleTimeout = 0;
304         if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
305             idleTimeout = NatConstants.DEFAULT_NAPT_IDLE_TIMEOUT;
306         }
307         long intranetVpnId;
308         if (bgpVpnId != NatConstants.INVALID_ID) {
309             intranetVpnId = bgpVpnId;
310         } else {
311             intranetVpnId = routerId;
312         }
313         LOG.debug("NAT Service : Intranet VPN ID {}", intranetVpnId);
314         LOG.debug("NAT Service : Router ID {}", routerId);
315         String translatedIp = translatedSourceAddress.getIpAddress();
316         int translatedPort = translatedSourceAddress.getPortNumber();
317         String actualIp = actualSourceAddress.getIpAddress();
318         int actualPort = actualSourceAddress.getPortNumber();
319         String switchFlowRef =
320             NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), actualIp, actualPort);
321
322         FlowEntity snatFlowEntity = new FlowEntityBuilder()
323             .setDpnId(dpnId)
324             .setTableId(tableId)
325             .setFlowId(switchFlowRef)
326             .setPriority(NatConstants.DEFAULT_NAPT_FLOW_PRIORITY)
327             .setFlowName(NatConstants.NAPT_FLOW_NAME)
328             .setIdleTimeOut(idleTimeout)
329             .setHardTimeOut(0)
330             .setCookie(NatUtil.getCookieNaptFlow(routerId))
331             .setMatchInfoList(buildAndGetMatchInfo(actualIp, actualPort, tableId, protocol, intranetVpnId))
332             .setInstructionInfoList(buildAndGetSetActionInstructionInfo(translatedIp, translatedPort,
333                                             intranetVpnId, vpnId, tableId, protocol, extGwMacAddress))
334             .setSendFlowRemFlag(true)
335             .build();
336
337         LOG.debug("NAT Service : Installing the NAPT flow in the table {} for the switch with the DPN ID {} ",
338             tableId, dpnId);
339         mdsalManager.syncInstallFlow(snatFlowEntity, 1);
340         LOG.trace("NAT Service : Exited buildAndInstallNatflows");
341     }
342
343     private static List<MatchInfo> buildAndGetMatchInfo(String ip, int port, short tableId,
344                                                         NAPTEntryEvent.Protocol protocol, long segmentId) {
345         MatchInfo ipMatchInfo = null;
346         MatchInfo portMatchInfo = null;
347         MatchInfo protocolMatchInfo = null;
348         InetAddress ipAddress = null;
349         String ipAddressAsString = null;
350         try {
351             ipAddress = InetAddress.getByName(ip);
352             ipAddressAsString = ipAddress.getHostAddress();
353
354         } catch (UnknownHostException e) {
355             LOG.error("NAT Service : UnknowHostException in buildAndGetMatchInfo. Failed  to build NAPT Flow for "
356                 + "ip {}", ipAddress);
357             return null;
358         }
359
360         MatchInfo metaDataMatchInfo = null;
361         if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
362             ipMatchInfo = new MatchIpv4Source(ipAddressAsString, "32");
363             if (protocol == NAPTEntryEvent.Protocol.TCP) {
364                 protocolMatchInfo = MatchIpProtocol.TCP;
365                 portMatchInfo = new MatchTcpSourcePort(port);
366             } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
367                 protocolMatchInfo = MatchIpProtocol.UDP;
368                 portMatchInfo = new MatchUdpSourcePort(port);
369             }
370             metaDataMatchInfo =
371                     new MatchMetadata(MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID);
372         } else {
373             ipMatchInfo = new MatchIpv4Destination(ipAddressAsString, "32");
374             if (protocol == NAPTEntryEvent.Protocol.TCP) {
375                 protocolMatchInfo = MatchIpProtocol.TCP;
376                 portMatchInfo = new MatchTcpDestinationPort(port);
377             } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
378                 protocolMatchInfo = MatchIpProtocol.UDP;
379                 portMatchInfo = new MatchUdpDestinationPort(port);
380             }
381             //metaDataMatchInfo = new MatchMetadata(BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID);
382         }
383         ArrayList<MatchInfo> matchInfo = new ArrayList<>();
384         matchInfo.add(MatchEthernetType.IPV4);
385         matchInfo.add(ipMatchInfo);
386         matchInfo.add(protocolMatchInfo);
387         matchInfo.add(portMatchInfo);
388         if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
389             matchInfo.add(metaDataMatchInfo);
390         }
391         return matchInfo;
392     }
393
394     private static List<InstructionInfo> buildAndGetSetActionInstructionInfo(String ipAddress, int port,
395                                                                              long segmentId, long vpnId,
396                                                                              short tableId,
397                                                                              NAPTEntryEvent.Protocol protocol,
398                                                                              String extGwMacAddress) {
399         ActionInfo ipActionInfo = null;
400         ActionInfo macActionInfo = null;
401         ActionInfo portActionInfo = null;
402         ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
403         ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
404         switch (tableId) {
405             case NwConstants.OUTBOUND_NAPT_TABLE:
406                 ipActionInfo = new ActionSetSourceIp(ipAddress);
407                 // Added External Gateway MAC Address
408                 macActionInfo = new ActionSetFieldEthernetSource(new MacAddress(extGwMacAddress));
409                 if (protocol == NAPTEntryEvent.Protocol.TCP) {
410                     portActionInfo = new ActionSetTcpSourcePort(port);
411                 } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
412                     portActionInfo = new ActionSetUdpSourcePort(port);
413                 }
414                 // reset the split-horizon bit to allow traffic from tunnel to be sent back to the provider port
415                 instructionInfo.add(new InstructionWriteMetadata(MetaDataUtil.getVpnIdMetadata(vpnId),
416                     MetaDataUtil.METADATA_MASK_VRFID.or(MetaDataUtil.METADATA_MASK_SH_FLAG)));
417                 break;
418
419             case NwConstants.INBOUND_NAPT_TABLE:
420                 ipActionInfo = new ActionSetDestinationIp(ipAddress);
421                 if (protocol == NAPTEntryEvent.Protocol.TCP) {
422                     portActionInfo = new ActionSetTcpDestinationPort(port);
423                 } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
424                     portActionInfo = new ActionSetUdpDestinationPort(port);
425                 }
426                 instructionInfo.add(new InstructionWriteMetadata(
427                         MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID));
428                 break;
429
430             default:
431                 LOG.error("NAT Service : Neither OUTBOUND_NAPT_TABLE nor INBOUND_NAPT_TABLE matches with "
432                     + "input table id {}", tableId);
433                 return null;
434         }
435
436         listActionInfo.add(ipActionInfo);
437         listActionInfo.add(portActionInfo);
438         if (macActionInfo != null) {
439             listActionInfo.add(macActionInfo);
440             LOG.debug("NAT Service : External GW MAC Address {} is found  ", macActionInfo);
441         }
442         instructionInfo.add(new InstructionApplyActions(listActionInfo));
443         instructionInfo.add(new InstructionGotoTable(NwConstants.NAPT_PFIB_TABLE));
444
445         return instructionInfo;
446     }
447
448     void removeNatFlows(BigInteger dpnId, short tableId ,long segmentId, String ip, int port) {
449         if (dpnId == null || dpnId.equals(BigInteger.ZERO)) {
450             LOG.error("NAT Service : DPN ID {} is invalid" , dpnId);
451         }
452         LOG.debug("NAT Service : Remove NAPT flows for dpnId {}, segmentId {}, ip {} and port {} ",
453             dpnId, segmentId, ip, port);
454
455         //Build the flow with the port IP and port as the match info.
456         String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(segmentId), ip, port);
457         FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef);
458         LOG.debug("NAT Service : Remove the flow in the table {} for the switch with the DPN ID {}",
459             NwConstants.INBOUND_NAPT_TABLE, dpnId);
460         mdsalManager.removeFlow(snatFlowEntity);
461
462     }
463
464     protected byte[] buildNaptPacketOut(Ethernet etherPkt) {
465         LOG.debug("NAT Service : About to build Napt Packet Out");
466         if (etherPkt.getPayload() instanceof IPv4) {
467             byte[] rawPkt;
468             IPv4 ipPkt = (IPv4) etherPkt.getPayload();
469             if (ipPkt.getPayload() instanceof TCP || ipPkt.getPayload() instanceof UDP) {
470                 try {
471                     rawPkt = etherPkt.serialize();
472                     return rawPkt;
473                 } catch (PacketException e2) {
474                     LOG.error("failed to build NAPT Packet out ", e2);
475                     return null;
476                 }
477             } else {
478                 LOG.error("NAT Service : Unable to build NaptPacketOut since its neither TCP nor UDP");
479                 return null;
480             }
481         }
482         LOG.error("NAT Service : Unable to build NaptPacketOut since its not IPv4 packet");
483         return null;
484     }
485
486     private void sendNaptPacketOut(byte[] pktOut, InterfaceInfo infInfo, List<ActionInfo> actionInfos, Long tunId) {
487         LOG.trace("NAT Service: Sending packet out DpId {}, interfaceInfo {}", infInfo.getDpId(), infInfo);
488         // set inPort, and action as OFPP_TABLE so that it starts from table 0 (lowest table as per spec)
489         actionInfos.add(new ActionSetFieldTunnelId(2, BigInteger.valueOf(tunId)));
490         actionInfos.add(new ActionOutput(3, new Uri("0xfffffff9")));
491         NodeConnectorRef inPort = MDSALUtil.getNodeConnRef(infInfo.getDpId(), String.valueOf(infInfo.getPortNo()));
492         LOG.debug("NAT Service : inPort for packetout is being set to {}", String.valueOf(infInfo.getPortNo()));
493         TransmitPacketInput output = MDSALUtil.getPacketOut(actionInfos, pktOut, infInfo.getDpId().longValue(), inPort);
494         LOG.trace("NAT Service: Transmitting packet: {}",output);
495         this.pktService.transmitPacket(output);
496     }
497
498     private String getInterfaceNameFromTag(long portTag) {
499         String interfaceName = null;
500         GetInterfaceFromIfIndexInput input =
501             new GetInterfaceFromIfIndexInputBuilder().setIfIndex((int) portTag).build();
502         Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
503             interfaceManagerRpc.getInterfaceFromIfIndex(input);
504         try {
505             GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
506             interfaceName = output.getInterfaceName();
507         } catch (InterruptedException | ExecutionException e) {
508             LOG.error("NAT Service : Error while retrieving the interfaceName from tag using "
509                 + "getInterfaceFromIfIndex RPC");
510         }
511         LOG.trace("NAT Service : Returning interfaceName {} for tag {} form getInterfaceNameFromTag",
512             interfaceName, portTag);
513         return interfaceName;
514     }
515
516     private long getVpnIdFromExternalSubnet(DataBroker dataBroker, Long routerId, String externalIpAddress) {
517         String routerName = NatUtil.getRouterName(dataBroker, routerId);
518         if (routerName != null) {
519             Routers extRouter = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
520             if (extRouter != null) {
521                 return NatUtil.getExternalSubnetVpnIdForRouterExternalIp(dataBroker, externalIpAddress, extRouter);
522             }
523         }
524
525         return NatConstants.INVALID_ID;
526     }
527 }