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