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.netvirt.natservice.internal.NaptPacketInHandler.NatPacketProcessingState;
72 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
73 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
74 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
75 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInput;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
97 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
98 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
99 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
100 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
101 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
102 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
103 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
104 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
105 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
106 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
107 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
108 import org.opendaylight.yangtools.yang.common.RpcResult;
109 import org.slf4j.Logger;
110 import org.slf4j.LoggerFactory;
113 public class NaptEventHandler {
114 private static final Logger LOG = LoggerFactory.getLogger(NaptEventHandler.class);
115 private final DataBroker dataBroker;
116 private final IMdsalApiManager mdsalManager;
117 private final PacketProcessingService pktService;
118 private final OdlInterfaceRpcService interfaceManagerRpc;
119 private final NaptManager naptManager;
120 private final IElanService elanManager;
121 private final IdManagerService idManager;
122 private final IInterfaceManager interfaceManager;
123 private final SalFlowService salFlowServiceRpc;
124 private final NatOverVxlanUtil natOverVxlanUtil;
127 public NaptEventHandler(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
128 final NaptManager naptManager,
129 final PacketProcessingService pktService,
130 final OdlInterfaceRpcService interfaceManagerRpc,
131 final IInterfaceManager interfaceManager,
132 final IElanService elanManager,
133 final IdManagerService idManager,
134 final SalFlowService salFlowServiceRpc,
135 final NatOverVxlanUtil natOverVxlanUtil) {
136 this.dataBroker = dataBroker;
137 this.mdsalManager = mdsalManager;
138 this.naptManager = naptManager;
139 this.pktService = pktService;
140 this.interfaceManagerRpc = interfaceManagerRpc;
141 this.interfaceManager = interfaceManager;
142 this.elanManager = elanManager;
143 this.idManager = idManager;
144 this.salFlowServiceRpc = salFlowServiceRpc;
145 this.natOverVxlanUtil = natOverVxlanUtil;
148 // TODO Clean up the exception handling
149 @SuppressWarnings("checkstyle:IllegalCatch")
150 public void handleEvent(final NAPTEntryEvent naptEntryEvent) {
152 Flow programming logic of the OUTBOUND NAPT TABLE :
153 1) Get the internal IP address, port number, router ID from the event.
154 2) Use the NAPT service getExternalAddressMapping() to get the External IP and the port.
155 3) Build the flow for replacing the Internal IP and port with the External IP and port.
156 a) Write the matching criteria.
157 b) Match the router ID in the metadata.
158 d) Write the VPN ID to the metadata.
159 e) Write the other data.
160 f) Set the apply actions instruction with the action setfield.
161 4) Write the flow to the OUTBOUND NAPT Table and forward to FIB table for routing the traffic.
163 Flow programming logic of the INBOUND NAPT TABLE :
164 Same as Outbound table logic except that :
165 1) Build the flow for replacing the External IP and port with the Internal IP and port.
166 2) Match the VPN ID in the metadata.
167 3) Write the router ID to the metadata.
168 5) Write the flow to the INBOUND NAPT Table and forward to FIB table for routing the traffic.
171 Long routerId = naptEntryEvent.getRouterId();
172 String internalIpAddress = naptEntryEvent.getIpAddress();
173 int internalPort = naptEntryEvent.getPortNumber();
174 String sourceIPPortKey = routerId + NatConstants.COLON_SEPARATOR
175 + internalIpAddress + NatConstants.COLON_SEPARATOR + internalPort;
176 LOG.trace("handleEvent : Time Elapsed before procesing snat ({}:{}) packet is {} ms,routerId: {},"
177 + "isPktProcessed:{}",
178 internalIpAddress, internalPort,
179 (System.currentTimeMillis() - naptEntryEvent.getObjectCreationTime()), routerId,
180 naptEntryEvent.isPktProcessed());
182 BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
183 long 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 Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
244 SessionAddress internalAddress = new SessionAddress(internalIpAddress, internalPort);
245 NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
247 //Get the external IP address for the corresponding internal IP address
248 SessionAddress externalAddress =
249 naptManager.getExternalAddressMapping(routerId, internalAddress,
250 naptEntryEvent.getProtocol());
251 if (externalAddress == null) {
252 NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
253 LOG.error("handleEvent: externalAddress is null. Droping packet for SNAT ({}) session",
258 Long vpnIdFromExternalSubnet = getVpnIdFromExternalSubnet(routerId,
259 externalAddress.getIpAddress());
260 if (vpnIdFromExternalSubnet != NatConstants.INVALID_ID) {
261 vpnId = vpnIdFromExternalSubnet;
264 // Added External Gateway MAC Address
265 Future<RpcResult<AddFlowOutput>> addFlowResult =
266 buildAndInstallNatFlowsOptionalRpc(dpnId, NwConstants.INBOUND_NAPT_TABLE, vpnId, routerId,
267 bgpVpnId, externalAddress, internalAddress, protocol, extGwMacAddress, true);
268 final BigInteger finalDpnId = dpnId;
269 final Long finalVpnId = vpnId;
270 final Long finalRouterId = routerId;
271 final long finalBgpVpnId = bgpVpnId;
272 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
273 new FutureCallback<RpcResult<AddFlowOutput>>() {
276 public void onSuccess(@Nullable RpcResult<AddFlowOutput> result) {
277 LOG.debug("handleEvent : Configured inbound rule for {} to {}",
278 internalAddress, externalAddress);
279 Future<RpcResult<AddFlowOutput>> addFlowResult =
280 buildAndInstallNatFlowsOptionalRpc(finalDpnId,
281 NwConstants.OUTBOUND_NAPT_TABLE, finalVpnId, finalRouterId,
282 finalBgpVpnId, internalAddress, externalAddress, protocol,
283 extGwMacAddress, true);
284 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
285 new FutureCallback<RpcResult<AddFlowOutput>>() {
288 public void onSuccess(@Nullable RpcResult<AddFlowOutput> result) {
289 LOG.debug("handleEvent : Configured outbound rule, sending packet out"
290 + "from {} to {}", internalAddress, externalAddress);
291 prepareAndSendPacketOut(naptEntryEvent, finalRouterId);
295 public void onFailure(@NonNull Throwable throwable) {
296 LOG.error("handleEvent : Error configuring outbound "
297 + "SNAT flows using RPC for SNAT connection from {} to {}",
298 internalAddress, externalAddress);
300 }, MoreExecutors.directExecutor());
304 public void onFailure(@NonNull Throwable throwable) {
305 LOG.error("handleEvent : Error configuring inbound SNAT flows "
306 + "using RPC for SNAT connection from {} to {}",
307 internalAddress, externalAddress);
309 }, MoreExecutors.directExecutor());
311 NatPacketProcessingState state = naptEntryEvent.getState();
313 state.setFlowInstalledTime(System.currentTimeMillis());
316 prepareAndSendPacketOut(naptEntryEvent, routerId);
318 LOG.trace("handleEvent : Time elapsed after Processsing snat ({}:{}) packet: {}ms,isPktProcessed:{} ",
319 naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(),
320 System.currentTimeMillis() - naptEntryEvent.getObjectCreationTime(),
321 naptEntryEvent.isPktProcessed());
323 LOG.debug("handleEvent : Inside delete Operation of NaptEventHandler");
324 handleFlowRemoved(naptEntryEvent, routerId, sourceIPPortKey, dpnId);
325 LOG.info("handleEvent : exited for removeEvent for IP {}, port {}, routerID : {}",
326 naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
328 } catch (Exception e) {
329 LOG.error("handleEvent :Exception in NaptEventHandler.handleEvent() payload {}", naptEntryEvent, e);
333 private void prepareAndSendPacketOut(NAPTEntryEvent naptEntryEvent, Long routerId) {
334 //Send Packetout - tcp or udp packets which got punted to controller.
335 BigInteger metadata = naptEntryEvent.getPacketReceived().getMatch().getMetadata().getMetadata();
336 byte[] inPayload = naptEntryEvent.getPacketReceived().getPayload();
337 Ethernet ethPkt = new Ethernet();
338 if (inPayload != null) {
340 ethPkt.deserialize(inPayload, 0, inPayload.length * Byte.SIZE);
341 } catch (PacketException e) {
342 LOG.error("prepareAndSendPacketOut : Failed to decode Packet", e);
347 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
348 LOG.debug("prepareAndSendPacketOut : portTag from incoming packet is {}", portTag);
349 List<ActionInfo> actionInfos = new ArrayList<>();
350 String interfaceName = getInterfaceNameFromTag(portTag);
351 BigInteger dpnID = null;
353 if (interfaceName != null) {
354 LOG.debug("prepareAndSendPacketOut : interfaceName fetched from portTag is {}", interfaceName);
355 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
356 .interfaces.Interface iface = null;
358 iface = interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName);
360 LOG.error("prepareAndSendPacketOut : Unable to read interface {} from config DataStore", interfaceName);
364 IfL2vlan ifL2vlan = iface.augmentation(IfL2vlan.class);
365 if (ifL2vlan != null && ifL2vlan.getVlanId() != null) {
366 vlanId = ifL2vlan.getVlanId().getValue() == null ? 0 : ifL2vlan.getVlanId().getValue();
368 InterfaceInfo infInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
369 if (infInfo == null) {
370 LOG.error("prepareAndSendPacketOut : error in getting interfaceInfo from Operation DS");
373 dpnID = infInfo.getDpId();
374 portNum = infInfo.getPortNo();
375 if (ethPkt.getEtherType() != (short) NwConstants.ETHTYPE_802_1Q) {
377 LOG.debug("prepareAndSendPacketOut : vlanId is {}", vlanId);
380 actionInfos.add(new ActionPushVlan(0));
381 actionInfos.add(new ActionSetFieldVlanVid(1, vlanId));
383 LOG.debug("prepareAndSendPacketOut : No vlanId {}, may be untagged", vlanId);
387 LOG.debug("prepareAndSendPacketOut : This is VLAN Trunk port case - need not do VLAN tagging again");
390 // This case will be hit for packets send from non-napt switch.
391 LOG.info("prepareAndSendPacketOut : interfaceName is not available.Retrieve from packet received");
392 NodeConnectorId nodeId = naptEntryEvent.getPacketReceived().getMatch().getInPort();
393 portNum = Integer.parseInt(nodeId.getValue());
394 LOG.debug("prepareAndSendPacketOut : in_port portNum : {}", portNum);
395 //List<PathArgument> dpnNodes = naptEntryEvent.getPacketReceived().getIngress().getValue().getPath();
396 Iterable<PathArgument> outArgs = naptEntryEvent.getPacketReceived().getIngress().getValue()
398 PathArgument pathArgument = Iterables.get(outArgs, 2);
399 LOG.debug("prepareAndSendPacketOut : pathArgument : {}", pathArgument);
400 InstanceIdentifier.IdentifiableItem<?, ?> item = Arguments.checkInstanceOf(pathArgument,
401 InstanceIdentifier.IdentifiableItem.class);
402 NodeConnectorKey key = Arguments.checkInstanceOf(item.getKey(), NodeConnectorKey.class);
403 LOG.info("prepareAndSendPacketOut : NodeConnectorKey key : {}", key.getId().getValue());
404 String dpnKey = key.getId().getValue();
405 if (dpnKey.contains(NatConstants.COLON_SEPARATOR)) {
406 dpnID = new BigInteger(dpnKey.split(NatConstants.COLON_SEPARATOR)[1]);
409 byte[] pktOut = buildNaptPacketOut(ethPkt);
411 if (pktOut != null) {
412 String routerName = NatUtil.getRouterName(dataBroker, routerId);
413 long tunId = NatUtil.getTunnelIdForNonNaptToNaptFlow(dataBroker, natOverVxlanUtil, elanManager,
414 idManager, routerId, routerName);
415 LOG.info("sendNaptPacketOut for ({}:{}) on dpnId {} portNum {} tunId {}",
416 naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), dpnID, portNum, tunId);
417 sendNaptPacketOut(pktOut, dpnID, portNum, actionInfos, tunId);
419 LOG.warn("prepareAndSendPacketOut : Unable to send Packet Out");
423 public void buildAndInstallNatFlows(BigInteger dpnId, short tableId, long vpnId, long routerId,
424 long bgpVpnId, SessionAddress actualSourceAddress,
425 SessionAddress translatedSourceAddress,
426 NAPTEntryEvent.Protocol protocol, String extGwMacAddress) {
427 buildAndInstallNatFlowsOptionalRpc(dpnId, tableId, vpnId, routerId, bgpVpnId, actualSourceAddress,
428 translatedSourceAddress, protocol, extGwMacAddress, false);
431 private Future<RpcResult<AddFlowOutput>> buildAndInstallNatFlowsOptionalRpc(
432 BigInteger dpnId, short tableId, long vpnId, long routerId, long bgpVpnId,
433 SessionAddress actualSourceAddress, SessionAddress translatedSourceAddress,
434 NAPTEntryEvent.Protocol protocol, String extGwMacAddress,
436 LOG.debug("buildAndInstallNatFlowsOptionalRpc : Build and install table={} flow on dpnId {} and routerId {}",
437 tableId, dpnId, routerId);
438 //Build the flow for replacing the actual IP and port with the translated IP and port.
440 if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
441 idleTimeout = NatConstants.DEFAULT_NAPT_IDLE_TIMEOUT;
444 if (bgpVpnId != NatConstants.INVALID_ID) {
445 intranetVpnId = bgpVpnId;
447 intranetVpnId = routerId;
449 LOG.debug("buildAndInstallNatFlowsOptionalRpc : Intranet VPN ID {} Router ID {}", intranetVpnId, routerId);
450 String translatedIp = translatedSourceAddress.getIpAddress();
451 int translatedPort = translatedSourceAddress.getPortNumber();
452 String actualIp = actualSourceAddress.getIpAddress();
453 int actualPort = actualSourceAddress.getPortNumber();
454 String switchFlowRef =
455 NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), actualIp, actualPort);
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) {
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);
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("removeNatFlows : 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("removeNatFlows : Unable to build NaptPacketOut since its neither TCP nor UDP");
673 LOG.error("removeNatFlows : 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 IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId, internalIpv4HostAddress,
730 internalPortNumber.toString(), protocol);
731 if (ipPortExternal == null) {
732 LOG.error("handleFlowRemoved : IpPortExternal is null while queried from the model for routerId {}",
736 String externalIpAddress = ipPortExternal.getIpAddress();
737 int externalPortNumber = ipPortExternal.getPortNum();
739 removeNatFlows(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId, externalIpAddress, externalPortNumber);
741 removeNatFlows(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId, internalIpv4HostAddress, internalPortNumber);
743 //Remove the SourceIP:Port key from the Napt packet handler map.
744 NaptPacketInHandler.removeIncomingPacketMap(sourceIPPortKey);
746 //Remove the mapping of internal fixed ip/port to external ip/port from the datastore.
747 SessionAddress internalSessionAddress = new SessionAddress(internalIpv4HostAddress, internalPortNumber);
748 naptManager.releaseIpExtPortMapping(routerId, internalSessionAddress, protocol);