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