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