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.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.net.InetAddress;
18 import java.net.UnknownHostException;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.Future;
24 import javax.inject.Inject;
25 import javax.inject.Singleton;
26 import org.eclipse.jdt.annotation.NonNull;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.opendaylight.controller.sal.common.util.Arguments;
29 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
30 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
31 import org.opendaylight.genius.mdsalutil.ActionInfo;
32 import org.opendaylight.genius.mdsalutil.FlowEntity;
33 import org.opendaylight.genius.mdsalutil.FlowEntityBuilder;
34 import org.opendaylight.genius.mdsalutil.InstructionInfo;
35 import org.opendaylight.genius.mdsalutil.MDSALUtil;
36 import org.opendaylight.genius.mdsalutil.MatchInfo;
37 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
38 import org.opendaylight.genius.mdsalutil.NwConstants;
39 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
40 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
41 import org.opendaylight.genius.mdsalutil.actions.ActionSetDestinationIp;
42 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
43 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
44 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
45 import org.opendaylight.genius.mdsalutil.actions.ActionSetSourceIp;
46 import org.opendaylight.genius.mdsalutil.actions.ActionSetTcpDestinationPort;
47 import org.opendaylight.genius.mdsalutil.actions.ActionSetTcpSourcePort;
48 import org.opendaylight.genius.mdsalutil.actions.ActionSetUdpDestinationPort;
49 import org.opendaylight.genius.mdsalutil.actions.ActionSetUdpSourcePort;
50 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
51 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
52 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
53 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
54 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
55 import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
56 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
57 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Source;
58 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
59 import org.opendaylight.genius.mdsalutil.matches.MatchTcpDestinationPort;
60 import org.opendaylight.genius.mdsalutil.matches.MatchTcpSourcePort;
61 import org.opendaylight.genius.mdsalutil.matches.MatchUdpDestinationPort;
62 import org.opendaylight.genius.mdsalutil.matches.MatchUdpSourcePort;
63 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
64 import org.opendaylight.genius.mdsalutil.packet.IPv4;
65 import org.opendaylight.genius.mdsalutil.packet.TCP;
66 import org.opendaylight.genius.mdsalutil.packet.UDP;
67 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
68 import org.opendaylight.mdsal.binding.api.DataBroker;
69 import org.opendaylight.netvirt.elanmanager.api.IElanService;
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.NodeConnectorId;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
103 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
104 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
105 import org.opendaylight.yangtools.yang.common.RpcResult;
106 import org.opendaylight.yangtools.yang.common.Uint32;
107 import org.opendaylight.yangtools.yang.common.Uint64;
108 import org.slf4j.Logger;
109 import org.slf4j.LoggerFactory;
112 public class NaptEventHandler {
113 private static final Logger LOG = LoggerFactory.getLogger(NaptEventHandler.class);
114 private final DataBroker dataBroker;
115 private final IMdsalApiManager mdsalManager;
116 private final PacketProcessingService pktService;
117 private final OdlInterfaceRpcService interfaceManagerRpc;
118 private final NaptManager naptManager;
119 private final IElanService elanManager;
120 private final IdManagerService idManager;
121 private final IInterfaceManager interfaceManager;
122 private final SalFlowService salFlowServiceRpc;
123 private final NatOverVxlanUtil natOverVxlanUtil;
126 public NaptEventHandler(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
127 final NaptManager naptManager,
128 final PacketProcessingService pktService,
129 final OdlInterfaceRpcService interfaceManagerRpc,
130 final IInterfaceManager interfaceManager,
131 final IElanService elanManager,
132 final IdManagerService idManager,
133 final SalFlowService salFlowServiceRpc,
134 final NatOverVxlanUtil natOverVxlanUtil) {
135 this.dataBroker = dataBroker;
136 this.mdsalManager = mdsalManager;
137 this.naptManager = naptManager;
138 this.pktService = pktService;
139 this.interfaceManagerRpc = interfaceManagerRpc;
140 this.interfaceManager = interfaceManager;
141 this.elanManager = elanManager;
142 this.idManager = idManager;
143 this.salFlowServiceRpc = salFlowServiceRpc;
144 this.natOverVxlanUtil = natOverVxlanUtil;
147 // TODO Clean up the exception handling
148 @SuppressWarnings("checkstyle:IllegalCatch")
149 public void handleEvent(final NAPTEntryEvent naptEntryEvent) {
151 Flow programming logic of the OUTBOUND NAPT TABLE :
152 1) Get the internal IP address, port number, router ID from the event.
153 2) Use the NAPT service getExternalAddressMapping() to get the External IP and the port.
154 3) Build the flow for replacing the Internal IP and port with the External IP and port.
155 a) Write the matching criteria.
156 b) Match the router ID in the metadata.
157 d) Write the VPN ID to the metadata.
158 e) Write the other data.
159 f) Set the apply actions instruction with the action setfield.
160 4) Write the flow to the OUTBOUND NAPT Table and forward to FIB table for routing the traffic.
162 Flow programming logic of the INBOUND NAPT TABLE :
163 Same as Outbound table logic except that :
164 1) Build the flow for replacing the External IP and port with the Internal IP and port.
165 2) Match the VPN ID in the metadata.
166 3) Write the router ID to the metadata.
167 5) Write the flow to the INBOUND NAPT Table and forward to FIB table for routing the traffic.
170 Uint32 routerId = naptEntryEvent.getRouterId();
171 String internalIpAddress = naptEntryEvent.getIpAddress();
172 int internalPort = naptEntryEvent.getPortNumber();
173 NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
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());
182 Uint64 dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
183 Uint32 bgpVpnId = NatConstants.INVALID_ID;
185 LOG.warn("handleEvent : dpnId is null. Assuming the router ID {} as the BGP VPN ID and "
186 + "proceeding....", 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);
197 routerId = NatUtil.getVpnId(dataBroker, routerName);
198 LOG.debug("handleEvent : Router ID {}", routerId);
199 dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
201 NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
202 LOG.error("handleEvent: Unable to find router for VpnName {}. Droping packet for SNAT ({})"
203 + "session", vpnName, sourceIPPortKey);
207 if (naptEntryEvent.getOperation() == NAPTEntryEvent.Operation.ADD) {
208 LOG.debug("handleEvent : Inside Add operation of NaptEventHandler");
210 // Build and install the NAPT translation flows in the Outbound and Inbound NAPT tables
211 if (!naptEntryEvent.isPktProcessed()) {
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);
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);
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",
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",
242 Uint32 vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
244 SessionAddress internalAddress = new SessionAddress(internalIpAddress, internalPort);
246 //Get the external IP address for the corresponding internal IP address
247 SessionAddress externalAddress =
248 naptManager.getExternalAddressMapping(routerId, internalAddress,
249 naptEntryEvent.getProtocol());
250 if (externalAddress == null) {
251 NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
252 LOG.error("handleEvent: externalAddress is null. Droping packet for SNAT ({}) session",
257 Uint32 vpnIdFromExternalSubnet = getVpnIdFromExternalSubnet(routerId,
258 externalAddress.getIpAddress());
259 if (vpnIdFromExternalSubnet != NatConstants.INVALID_ID) {
260 vpnId = vpnIdFromExternalSubnet;
263 // Added External Gateway MAC Address
264 Future<RpcResult<AddFlowOutput>> addFlowResult =
265 buildAndInstallNatFlowsOptionalRpc(dpnId, NwConstants.INBOUND_NAPT_TABLE, vpnId, routerId,
266 bgpVpnId, externalAddress, internalAddress, protocol, extGwMacAddress, true);
267 final Uint64 finalDpnId = dpnId;
268 final Uint32 finalVpnId = vpnId;
269 final Uint32 finalRouterId = routerId;
270 final Uint32 finalBgpVpnId = bgpVpnId;
271 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
272 new FutureCallback<RpcResult<AddFlowOutput>>() {
275 public void onSuccess(@Nullable RpcResult<AddFlowOutput> result) {
276 LOG.debug("handleEvent : Configured inbound rule for {} to {}",
277 internalAddress, externalAddress);
278 Future<RpcResult<AddFlowOutput>> addFlowResult =
279 buildAndInstallNatFlowsOptionalRpc(finalDpnId,
280 NwConstants.OUTBOUND_NAPT_TABLE, finalVpnId, finalRouterId,
281 finalBgpVpnId, internalAddress, externalAddress, protocol,
282 extGwMacAddress, true);
283 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
284 new FutureCallback<RpcResult<AddFlowOutput>>() {
287 public void onSuccess(@Nullable RpcResult<AddFlowOutput> result) {
288 LOG.debug("handleEvent : Configured outbound rule, sending packet out"
289 + "from {} to {}", internalAddress, externalAddress);
290 prepareAndSendPacketOut(naptEntryEvent, finalRouterId, sourceIPPortKey);
294 public void onFailure(@NonNull Throwable throwable) {
295 LOG.error("handleEvent : Error configuring outbound "
296 + "SNAT flows using RPC for SNAT connection from {} to {}",
297 internalAddress, externalAddress);
299 }, MoreExecutors.directExecutor());
303 public void onFailure(@NonNull Throwable throwable) {
304 LOG.error("handleEvent : Error configuring inbound SNAT flows "
305 + "using RPC for SNAT connection from {} to {}",
306 internalAddress, externalAddress);
308 }, MoreExecutors.directExecutor());
310 prepareAndSendPacketOut(naptEntryEvent, routerId, sourceIPPortKey);
312 LOG.trace("handleEvent : Time elapsed after Processsing snat ({}:{}) packet: {}ms,isPktProcessed:{} ",
313 naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(),
314 System.currentTimeMillis() - naptEntryEvent.getObjectCreationTime(),
315 naptEntryEvent.isPktProcessed());
317 LOG.debug("handleEvent : Inside delete Operation of NaptEventHandler");
318 handleFlowRemoved(naptEntryEvent, routerId, sourceIPPortKey, dpnId);
319 LOG.info("handleEvent : exited for removeEvent for IP {}, port {}, routerID : {}",
320 naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
322 } catch (Exception e) {
323 LOG.error("handleEvent :Exception in NaptEventHandler.handleEvent() payload {}", naptEntryEvent, e);
327 private void prepareAndSendPacketOut(NAPTEntryEvent naptEntryEvent, Uint32 routerId, String sourceIPPortKey) {
328 //Send Packetout - tcp or udp packets which got punted to controller.
329 Uint64 metadata = naptEntryEvent.getPacketReceived().getMatch().getMetadata().getMetadata();
330 byte[] inPayload = naptEntryEvent.getPacketReceived().getPayload();
331 Ethernet ethPkt = new Ethernet();
332 if (inPayload != null) {
334 ethPkt.deserialize(inPayload, 0, inPayload.length * Byte.SIZE);
335 } catch (PacketException e) {
336 NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
337 LOG.error("prepareAndSendPacketOut : Failed to decode Packet", e);
342 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
343 LOG.debug("prepareAndSendPacketOut : portTag from incoming packet is {}", portTag);
344 List<ActionInfo> actionInfos = new ArrayList<>();
345 String interfaceName = getInterfaceNameFromTag(portTag);
348 if (interfaceName != null) {
349 LOG.debug("prepareAndSendPacketOut : interfaceName fetched from portTag is {}", interfaceName);
350 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
351 .interfaces.Interface iface = null;
353 iface = interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName);
355 NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
356 LOG.error("prepareAndSendPacketOut : Unable to read interface {} from config DataStore", interfaceName);
360 IfL2vlan ifL2vlan = iface.augmentation(IfL2vlan.class);
361 if (ifL2vlan != null && ifL2vlan.getVlanId() != null) {
362 vlanId = ifL2vlan.getVlanId().getValue() == null ? 0 : ifL2vlan.getVlanId().getValue().toJava();
364 InterfaceInfo infInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
365 if (infInfo == null) {
366 LOG.error("prepareAndSendPacketOut : error in getting interfaceInfo from Operation DS");
369 dpnID = infInfo.getDpId();
370 portNum = infInfo.getPortNo();
371 if (ethPkt.getEtherType() != (short) NwConstants.ETHTYPE_802_1Q) {
373 LOG.debug("prepareAndSendPacketOut : vlanId is {}", vlanId);
376 actionInfos.add(new ActionPushVlan(0));
377 actionInfos.add(new ActionSetFieldVlanVid(1, vlanId));
379 LOG.debug("prepareAndSendPacketOut : No vlanId {}, may be untagged", vlanId);
383 LOG.debug("prepareAndSendPacketOut : This is VLAN Trunk port case - need not do VLAN tagging again");
386 // This case will be hit for packets send from non-napt switch.
387 LOG.info("prepareAndSendPacketOut : interfaceName is not available.Retrieve from packet received");
388 NodeConnectorId nodeId = naptEntryEvent.getPacketReceived().getMatch().getInPort();
389 portNum = Integer.parseInt(nodeId.getValue());
390 LOG.debug("prepareAndSendPacketOut : in_port portNum : {}", portNum);
391 //List<PathArgument> dpnNodes = naptEntryEvent.getPacketReceived().getIngress().getValue().getPath();
392 Iterable<PathArgument> outArgs = naptEntryEvent.getPacketReceived().getIngress().getValue()
394 PathArgument pathArgument = Iterables.get(outArgs, 2);
395 LOG.debug("prepareAndSendPacketOut : pathArgument : {}", pathArgument);
396 InstanceIdentifier.IdentifiableItem<?, ?> item = Arguments.checkInstanceOf(pathArgument,
397 InstanceIdentifier.IdentifiableItem.class);
398 NodeConnectorKey key = Arguments.checkInstanceOf(item.getKey(), NodeConnectorKey.class);
399 LOG.info("prepareAndSendPacketOut : NodeConnectorKey key : {}", key.getId().getValue());
400 String dpnKey = key.getId().getValue();
401 if (dpnKey.contains(NatConstants.COLON_SEPARATOR)) {
402 dpnID = Uint64.valueOf(dpnKey.split(NatConstants.COLON_SEPARATOR)[1]).intern();
405 byte[] pktOut = buildNaptPacketOut(ethPkt);
407 if (pktOut != null) {
408 String routerName = NatUtil.getRouterName(dataBroker, routerId);
409 Uint64 tunId = NatUtil.getTunnelIdForNonNaptToNaptFlow(dataBroker, natOverVxlanUtil, elanManager,
410 idManager, routerId, routerName);
411 LOG.info("sendNaptPacketOut for ({}:{}) on dpnId {} portNum {} tunId {}",
412 naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), dpnID, portNum, tunId);
413 sendNaptPacketOut(pktOut, dpnID, portNum, actionInfos, tunId);
415 LOG.warn("prepareAndSendPacketOut : Unable to send Packet Out");
419 public void buildAndInstallNatFlows(Uint64 dpnId, short tableId, Uint32 vpnId, Uint32 routerId,
420 Uint32 bgpVpnId, SessionAddress actualSourceAddress,
421 SessionAddress translatedSourceAddress,
422 NAPTEntryEvent.Protocol protocol, String extGwMacAddress) {
423 buildAndInstallNatFlowsOptionalRpc(dpnId, tableId, vpnId, routerId, bgpVpnId, actualSourceAddress,
424 translatedSourceAddress, protocol, extGwMacAddress, false);
427 private Future<RpcResult<AddFlowOutput>> buildAndInstallNatFlowsOptionalRpc(
428 Uint64 dpnId, short tableId, Uint32 vpnId, Uint32 routerId, Uint32 bgpVpnId,
429 SessionAddress actualSourceAddress, SessionAddress translatedSourceAddress,
430 NAPTEntryEvent.Protocol protocol, String extGwMacAddress,
432 LOG.debug("buildAndInstallNatFlowsOptionalRpc : Build and install table={} flow on dpnId {} and routerId {}",
433 tableId, dpnId, routerId);
434 //Build the flow for replacing the actual IP and port with the translated IP and port.
436 if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
437 idleTimeout = NatConstants.DEFAULT_NAPT_IDLE_TIMEOUT;
439 Uint32 intranetVpnId;
440 if (bgpVpnId != NatConstants.INVALID_ID) {
441 intranetVpnId = bgpVpnId;
443 intranetVpnId = routerId;
445 LOG.debug("buildAndInstallNatFlowsOptionalRpc : Intranet VPN ID {} Router ID {}", intranetVpnId, routerId);
446 String translatedIp = translatedSourceAddress.getIpAddress();
447 int translatedPort = translatedSourceAddress.getPortNumber();
448 String actualIp = actualSourceAddress.getIpAddress();
449 int actualPort = actualSourceAddress.getPortNumber();
450 String switchFlowRef = null;
451 if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
452 switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), actualIp, actualPort,
455 switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), translatedIp,
456 translatedPort, protocol.name());
458 FlowEntity snatFlowEntity = new FlowEntityBuilder()
461 .setFlowId(switchFlowRef)
462 .setPriority(NatConstants.DEFAULT_NAPT_FLOW_PRIORITY)
463 .setFlowName(NatConstants.NAPT_FLOW_NAME)
464 .setIdleTimeOut(idleTimeout)
466 .setCookie(NatUtil.getCookieNaptFlow(routerId))
467 .setMatchInfoList(buildAndGetMatchInfo(actualIp, actualPort, tableId, protocol, intranetVpnId))
468 .setInstructionInfoList(buildAndGetSetActionInstructionInfo(translatedIp, translatedPort,
469 intranetVpnId, vpnId, tableId, protocol, extGwMacAddress))
470 .setSendFlowRemFlag(true)
473 // Install flows using RPC to prevent race with future packet-out that depends on this flow
474 Future<RpcResult<AddFlowOutput>> addFlowResult = null;
476 Flow flow = snatFlowEntity.getFlowBuilder().build();
477 NodeRef nodeRef = getNodeRef(dpnId);
478 FlowRef flowRef = getFlowRef(dpnId, flow);
479 AddFlowInput addFlowInput = new AddFlowInputBuilder(flow).setFlowRef(flowRef).setNode(nodeRef).build();
480 long startTime = System.currentTimeMillis();
481 addFlowResult = salFlowServiceRpc.addFlow(addFlowInput);
482 LOG.debug("buildAndInstallNatFlowsOptionalRpc : Time elapsed for salFlowServiceRpc table {}: {}ms ",
483 tableId, System.currentTimeMillis() - startTime);
484 // Keep flow installation through MDSAL as well to be able to handle switch failures
485 startTime = System.currentTimeMillis();
486 mdsalManager.installFlow(snatFlowEntity);
487 LOG.trace("buildAndInstallNatFlowsOptionalRpc : Time Elapsed while installing table-{} "
488 + "flow on DPN:{} for snat packet({},{}): {}ms", tableId, dpnId,
489 actualSourceAddress.getIpAddress(),actualSourceAddress.getPortNumber(),
490 System.currentTimeMillis() - startTime);
492 long startTime = System.currentTimeMillis();
493 mdsalManager.syncInstallFlow(snatFlowEntity);
494 LOG.trace("buildAndInstallNatFlowsOptionalRpc : Time Elapsed while installing table-{} "
495 + "flow on DPN:{} for snat packet({},{}): {}ms", tableId, dpnId,
496 actualSourceAddress.getIpAddress(),actualSourceAddress.getPortNumber(),
497 System.currentTimeMillis() - startTime);
499 LOG.trace("buildAndInstallNatFlowsOptionalRpc : Exited");
501 return addFlowResult;
504 private static Node buildInventoryDpnNode(Uint64 dpnId) {
505 NodeId nodeId = new NodeId("openflow:" + dpnId);
506 Node nodeDpn = new NodeBuilder().setId(nodeId).withKey(new NodeKey(nodeId)).build();
510 private static NodeRef getNodeRef(Uint64 dpnId) {
511 NodeId nodeId = new NodeId("openflow:" + dpnId);
512 return new NodeRef(InstanceIdentifier.builder(Nodes.class)
513 .child(Node.class, new NodeKey(nodeId)).build());
516 public static FlowRef getFlowRef(Uint64 dpId, Flow flow) {
517 FlowKey flowKey = new FlowKey(new FlowId(flow.getId()));
518 Node nodeDpn = buildInventoryDpnNode(dpId);
519 InstanceIdentifier<Flow> flowInstanceId =
520 InstanceIdentifier.builder(Nodes.class)
521 .child(Node.class, nodeDpn.key()).augmentation(FlowCapableNode.class)
522 .child(Table.class, new TableKey(flow.getTableId()))
523 .child(Flow.class, flowKey)
525 return new FlowRef(flowInstanceId);
529 private static List<MatchInfo> buildAndGetMatchInfo(String ip, int port, short tableId,
530 NAPTEntryEvent.Protocol protocol, Uint32 segmentId) {
531 MatchInfo ipMatchInfo = null;
532 MatchInfo portMatchInfo = null;
533 MatchInfo protocolMatchInfo = null;
534 InetAddress ipAddress = null;
535 String ipAddressAsString = null;
537 ipAddress = InetAddress.getByName(ip);
538 ipAddressAsString = ipAddress.getHostAddress();
540 } catch (UnknownHostException e) {
541 LOG.error("buildAndGetMatchInfo : UnknowHostException in buildAndGetMatchInfo."
542 + "Failed to build NAPT Flow for ip {}", ip, e);
546 MatchInfo metaDataMatchInfo = null;
547 if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
548 ipMatchInfo = new MatchIpv4Source(ipAddressAsString, "32");
549 if (protocol == NAPTEntryEvent.Protocol.TCP) {
550 protocolMatchInfo = MatchIpProtocol.TCP;
551 portMatchInfo = new MatchTcpSourcePort(port);
552 } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
553 protocolMatchInfo = MatchIpProtocol.UDP;
554 portMatchInfo = new MatchUdpSourcePort(port);
557 new MatchMetadata(MetaDataUtil.getVpnIdMetadata(segmentId.longValue()),
558 MetaDataUtil.METADATA_MASK_VRFID);
560 ipMatchInfo = new MatchIpv4Destination(ipAddressAsString, "32");
561 if (protocol == NAPTEntryEvent.Protocol.TCP) {
562 protocolMatchInfo = MatchIpProtocol.TCP;
563 portMatchInfo = new MatchTcpDestinationPort(port);
564 } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
565 protocolMatchInfo = MatchIpProtocol.UDP;
566 portMatchInfo = new MatchUdpDestinationPort(port);
568 //metaDataMatchInfo = new MatchMetadata(BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID);
570 ArrayList<MatchInfo> matchInfo = new ArrayList<>();
571 matchInfo.add(MatchEthernetType.IPV4);
572 matchInfo.add(ipMatchInfo);
573 matchInfo.add(protocolMatchInfo);
574 matchInfo.add(portMatchInfo);
575 if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
576 matchInfo.add(metaDataMatchInfo);
582 private static List<InstructionInfo> buildAndGetSetActionInstructionInfo(String ipAddress, int port,
583 Uint32 segmentId, Uint32 vpnId,
585 NAPTEntryEvent.Protocol protocol,
586 String extGwMacAddress) {
587 ActionInfo ipActionInfo = null;
588 ActionInfo macActionInfo = null;
589 ActionInfo portActionInfo = null;
590 ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
591 ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
593 case NwConstants.OUTBOUND_NAPT_TABLE:
594 ipActionInfo = new ActionSetSourceIp(ipAddress);
595 // Added External Gateway MAC Address
596 macActionInfo = new ActionSetFieldEthernetSource(new MacAddress(extGwMacAddress));
597 if (protocol == NAPTEntryEvent.Protocol.TCP) {
598 portActionInfo = new ActionSetTcpSourcePort(port);
599 } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
600 portActionInfo = new ActionSetUdpSourcePort(port);
602 // reset the split-horizon bit to allow traffic from tunnel to be sent back to the provider port
603 instructionInfo.add(new InstructionWriteMetadata(MetaDataUtil.getVpnIdMetadata(vpnId.longValue()),
604 Uint64.fromLongBits(MetaDataUtil.METADATA_MASK_VRFID.longValue()
605 | MetaDataUtil.METADATA_MASK_SH_FLAG.longValue())));
608 case NwConstants.INBOUND_NAPT_TABLE:
609 ipActionInfo = new ActionSetDestinationIp(ipAddress);
610 if (protocol == NAPTEntryEvent.Protocol.TCP) {
611 portActionInfo = new ActionSetTcpDestinationPort(port);
612 } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
613 portActionInfo = new ActionSetUdpDestinationPort(port);
615 instructionInfo.add(new InstructionWriteMetadata(
616 MetaDataUtil.getVpnIdMetadata(segmentId.longValue()), MetaDataUtil.METADATA_MASK_VRFID));
620 LOG.error("buildAndGetSetActionInstructionInfo : Neither OUTBOUND_NAPT_TABLE nor "
621 + "INBOUND_NAPT_TABLE matches with input table id {}", tableId);
622 return Collections.emptyList();
625 listActionInfo.add(ipActionInfo);
626 listActionInfo.add(portActionInfo);
627 if (macActionInfo != null) {
628 listActionInfo.add(macActionInfo);
629 LOG.debug("buildAndGetSetActionInstructionInfo : External GW MAC Address {} is found ", macActionInfo);
631 instructionInfo.add(new InstructionApplyActions(listActionInfo));
632 instructionInfo.add(new InstructionGotoTable(NwConstants.NAPT_PFIB_TABLE));
634 return instructionInfo;
637 void removeNatFlows(Uint64 dpnId, short tableId ,Uint32 segmentId, String ip, int port, String protocol) {
638 if (dpnId == null || dpnId.equals(Uint64.ZERO)) {
639 LOG.error("removeNatFlows : DPN ID {} is invalid" , dpnId);
642 LOG.debug("removeNatFlows : Remove NAPT flows for dpnId {}, segmentId {}, ip {} and port {} ",
643 dpnId, segmentId, ip, port);
645 //Build the flow with the port IP and port as the match info.
646 String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(segmentId), ip, port, protocol);
647 FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef);
648 LOG.debug("removeNatFlows : Remove the flow in the table {} for the switch with the DPN ID {}",
650 long startTime = System.currentTimeMillis();
651 mdsalManager.removeFlow(snatFlowEntity);
652 LOG.trace("removeNatFlows : Time Elapsed for removing table-{} flow from switch with DPN ID:{} "
653 + "for SNAT ({}:{}) session:{}ms", tableId, dpnId, ip, port, System.currentTimeMillis() - startTime);
657 @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
658 protected byte[] buildNaptPacketOut(Ethernet etherPkt) {
659 LOG.debug("buildNaptPacketOut : About to build Napt Packet Out");
660 if (etherPkt.getPayload() instanceof IPv4) {
662 IPv4 ipPkt = (IPv4) etherPkt.getPayload();
663 if (ipPkt.getPayload() instanceof TCP || ipPkt.getPayload() instanceof UDP) {
665 rawPkt = etherPkt.serialize();
667 } catch (PacketException e2) {
668 LOG.error("failed to build NAPT Packet out ", e2);
672 LOG.error("buildNaptPacketOut : Unable to build NaptPacketOut since its neither TCP nor UDP");
676 LOG.error("buildNaptPacketOut : Unable to build NaptPacketOut since its not IPv4 packet");
680 private void sendNaptPacketOut(byte[] pktOut, Uint64 dpnID, int portNum,
681 List<ActionInfo> actionInfos, Uint64 tunId) {
682 LOG.trace("sendNaptPacketOut: Sending packet out DpId {}, interface {}", dpnID, portNum);
683 // set inPort, and action as OFPP_TABLE so that it starts from table 0 (lowest table as per spec)
684 actionInfos.add(new ActionSetFieldTunnelId(2, tunId));
685 actionInfos.add(new ActionOutput(3, new Uri("0xfffffff9")));
686 NodeConnectorRef inPort = MDSALUtil.getNodeConnRef(dpnID, String.valueOf(portNum));
687 LOG.debug("sendNaptPacketOut : inPort for packetout is being set to {}", portNum);
688 TransmitPacketInput output = MDSALUtil.getPacketOut(actionInfos, pktOut, dpnID.longValue(), inPort);
689 LOG.debug("sendNaptPacketOut : Transmitting packet: {}, inPort {}", output, inPort);
691 LoggingFutures.addErrorLogging(pktService.transmitPacket(output), LOG, "Transmit packet");
694 private String getInterfaceNameFromTag(long portTag) {
695 String interfaceName = null;
696 GetInterfaceFromIfIndexInput input =
697 new GetInterfaceFromIfIndexInputBuilder().setIfIndex((int) portTag).build();
698 Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
699 interfaceManagerRpc.getInterfaceFromIfIndex(input);
701 GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
702 if (output != null) {
703 interfaceName = output.getInterfaceName();
705 } catch (InterruptedException | ExecutionException e) {
706 LOG.error("getInterfaceNameFromTag : Error while retrieving the interfaceName from tag using "
707 + "getInterfaceFromIfIndex RPC");
709 LOG.trace("getInterfaceNameFromTag : Returning interfaceName {} for tag {} form getInterfaceNameFromTag",
710 interfaceName, portTag);
711 return interfaceName;
714 private Uint32 getVpnIdFromExternalSubnet(Uint32 routerId, String externalIpAddress) {
715 String routerName = NatUtil.getRouterName(dataBroker, routerId);
716 if (routerName != null) {
717 Routers extRouter = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
718 if (extRouter != null) {
719 return NatUtil.getExternalSubnetVpnIdForRouterExternalIp(dataBroker, externalIpAddress, extRouter);
723 return NatConstants.INVALID_ID;
726 public void handleFlowRemoved(NAPTEntryEvent naptEntryEvent, Uint32 routerId, String sourceIPPortKey,
728 String internalIpv4HostAddress = naptEntryEvent.getIpAddress();
729 Integer internalPortNumber = naptEntryEvent.getPortNumber();
730 NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
731 //Get the external IP address and the port from the model
732 LOG.trace("handleFlowRemoved: Failed to remove snat flow internalIP {} with "
733 + "Port {} protocol {} for routerId {} in OUTBOUNDTABLE of naptSwitch {}",
734 internalIpv4HostAddress, internalPortNumber, protocol, routerId, dpnId);
735 removeNatFlows(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId, internalIpv4HostAddress,
736 internalPortNumber, protocol.name());
738 LOG.trace("handleFlowRemoved: Failed to remove snat flow internalIP {} with "
739 + "Port {} protocol {} for routerId {} in INBOUNDTABLE of naptSwitch {}",
740 internalIpv4HostAddress, internalPortNumber, protocol, routerId, dpnId);
741 removeNatFlows(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId, internalIpv4HostAddress,
742 internalPortNumber, protocol.name());
744 //Remove the SourceIP:Port key from the Napt packet handler map.
745 NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
747 //Remove the mapping of internal fixed ip/port to external ip/port from the datastore.
748 SessionAddress internalSessionAddress = new SessionAddress(internalIpv4HostAddress, internalPortNumber);
749 naptManager.releaseIpExtPortMapping(routerId, internalSessionAddress, protocol);