2 * Copyright © 2016, 2018 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
9 package org.opendaylight.netvirt.natservice.internal;
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;
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;
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;
139 // TODO Clean up the exception handling
140 @SuppressWarnings("checkstyle:IllegalCatch")
141 public void handleEvent(final NAPTEntryEvent naptEntryEvent) {
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.
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.
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());
173 BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
174 long bgpVpnId = NatConstants.INVALID_ID;
176 LOG.warn("handleEvent : dpnId is null. Assuming the router ID {} as the BGP VPN ID and "
177 + "proceeding....", 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);
188 routerId = NatUtil.getVpnId(dataBroker, routerName);
189 LOG.debug("handleEvent : Router ID {}", routerId);
190 dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
192 NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
193 LOG.error("handleEvent: Unable to find router for VpnName {}. Droping packet for SNAT ({})"
194 + "session", vpnName, sourceIPPortKey);
198 if (naptEntryEvent.getOperation() == NAPTEntryEvent.Operation.ADD) {
199 LOG.debug("handleEvent : Inside Add operation of NaptEventHandler");
201 // Build and install the NAPT translation flows in the Outbound and Inbound NAPT tables
202 if (!naptEntryEvent.isPktProcessed()) {
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);
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);
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",
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",
233 Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
235 SessionAddress internalAddress = new SessionAddress(internalIpAddress, internalPort);
236 NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
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",
249 Long vpnIdFromExternalSubnet = getVpnIdFromExternalSubnet(routerId,
250 externalAddress.getIpAddress());
251 if (vpnIdFromExternalSubnet != NatConstants.INVALID_ID) {
252 vpnId = vpnIdFromExternalSubnet;
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>>() {
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>>() {
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);
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);
291 }, MoreExecutors.directExecutor());
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);
300 }, MoreExecutors.directExecutor());
302 NatPacketProcessingState state = naptEntryEvent.getState();
304 state.setFlowInstalledTime(System.currentTimeMillis());
307 prepareAndSendPacketOut(naptEntryEvent, routerId);
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());
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);
320 } catch (Exception e) {
321 LOG.error("handleEvent :Exception in NaptEventHandler.handleEvent() payload {}", naptEntryEvent, e);
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) {
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);
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;
346 iface = interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName);
348 LOG.error("prepareAndSendPacketOut : Unable to read interface {} from config DataStore", interfaceName);
351 List<ActionInfo> actionInfos = new ArrayList<>();
352 IfL2vlan ifL2vlan = iface.getAugmentation(IfL2vlan.class);
353 if (ifL2vlan != null && ifL2vlan.getVlanId() != null) {
354 vlanId = ifL2vlan.getVlanId().getValue() == null ? 0 : ifL2vlan.getVlanId().getValue();
356 InterfaceInfo infInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
357 if (infInfo == null) {
358 LOG.error("prepareAndSendPacketOut : error in getting interfaceInfo from Operation DS");
362 byte[] pktOut = buildNaptPacketOut(ethPkt);
363 if (ethPkt.getEtherType() != (short) NwConstants.ETHTYPE_802_1Q) {
365 LOG.debug("prepareAndSendPacketOut : vlanId is {}", vlanId);
368 actionInfos.add(new ActionPushVlan(0));
369 actionInfos.add(new ActionSetFieldVlanVid(1, vlanId));
371 LOG.debug("prepareAndSendPacketOut : No vlanId {}, may be untagged", vlanId);
375 LOG.debug("prepareAndSendPacketOut : This is VLAN Trunk port case - need not do VLAN tagging again");
377 if (pktOut != null) {
378 String routerName = NatUtil.getRouterName(dataBroker, routerId);
379 long tunId = NatUtil.getTunnelIdForNonNaptToNaptFlow(dataBroker, elanManager, idManager, routerId,
381 sendNaptPacketOut(pktOut, infInfo, actionInfos, tunId);
383 LOG.warn("prepareAndSendPacketOut : Unable to send Packet Out");
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);
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,
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.
404 if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
405 idleTimeout = NatConstants.DEFAULT_NAPT_IDLE_TIMEOUT;
408 if (bgpVpnId != NatConstants.INVALID_ID) {
409 intranetVpnId = bgpVpnId;
411 intranetVpnId = routerId;
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);
421 FlowEntity snatFlowEntity = new FlowEntityBuilder()
424 .setFlowId(switchFlowRef)
425 .setPriority(NatConstants.DEFAULT_NAPT_FLOW_PRIORITY)
426 .setFlowName(NatConstants.NAPT_FLOW_NAME)
427 .setIdleTimeOut(idleTimeout)
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)
436 // Install flows using RPC to prevent race with future packet-out that depends on this flow
437 Future<RpcResult<AddFlowOutput>> addFlowResult = null;
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);
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);
462 LOG.trace("buildAndInstallNatFlowsOptionalRpc : Exited");
464 return addFlowResult;
467 private static Node buildInventoryDpnNode(BigInteger dpnId) {
468 NodeId nodeId = new NodeId("openflow:" + dpnId);
469 Node nodeDpn = new NodeBuilder().setId(nodeId).setKey(new NodeKey(nodeId)).build();
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());
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.getKey()).augmentation(FlowCapableNode.class)
485 .child(Table.class, new TableKey(flow.getTableId()))
486 .child(Flow.class, flowKey)
488 return new FlowRef(flowInstanceId);
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;
499 ipAddress = InetAddress.getByName(ip);
500 ipAddressAsString = ipAddress.getHostAddress();
502 } catch (UnknownHostException e) {
503 LOG.error("buildAndGetMatchInfo : UnknowHostException in buildAndGetMatchInfo."
504 + "Failed to build NAPT Flow for ip {}", ip, e);
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);
519 new MatchMetadata(MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID);
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);
529 //metaDataMatchInfo = new MatchMetadata(BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID);
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);
542 private static List<InstructionInfo> buildAndGetSetActionInstructionInfo(String ipAddress, int port,
543 long segmentId, long vpnId,
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<>();
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);
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)));
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);
574 instructionInfo.add(new InstructionWriteMetadata(
575 MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID));
579 LOG.error("buildAndGetSetActionInstructionInfo : Neither OUTBOUND_NAPT_TABLE nor "
580 + "INBOUND_NAPT_TABLE matches with input table id {}", tableId);
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);
590 instructionInfo.add(new InstructionApplyActions(listActionInfo));
591 instructionInfo.add(new InstructionGotoTable(NwConstants.NAPT_PFIB_TABLE));
593 return instructionInfo;
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);
601 LOG.debug("removeNatFlows : Remove NAPT flows for dpnId {}, segmentId {}, ip {} and port {} ",
602 dpnId, segmentId, ip, port);
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);
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) {
620 IPv4 ipPkt = (IPv4) etherPkt.getPayload();
621 if (ipPkt.getPayload() instanceof TCP || ipPkt.getPayload() instanceof UDP) {
623 rawPkt = etherPkt.serialize();
625 } catch (PacketException e2) {
626 LOG.error("failed to build NAPT Packet out ", e2);
630 LOG.error("removeNatFlows : Unable to build NaptPacketOut since its neither TCP nor UDP");
634 LOG.error("removeNatFlows : Unable to build NaptPacketOut since its not IPv4 packet");
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);
648 JdkFutures.addErrorLogging(pktService.transmitPacket(output), LOG, "Transmit packet");
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);
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");
664 LOG.trace("getInterfaceNameFromTag : Returning interfaceName {} for tag {} form getInterfaceNameFromTag",
665 interfaceName, portTag);
666 return interfaceName;
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);
678 return NatConstants.INVALID_ID;