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