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