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 java.math.BigInteger;
15 import java.net.InetAddress;
16 import java.net.UnknownHostException;
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.Future;
21 import javax.annotation.Nullable;
22 import javax.inject.Inject;
23 import javax.inject.Singleton;
24 import org.opendaylight.controller.liblldp.NetUtils;
25 import org.opendaylight.controller.liblldp.PacketException;
26 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
27 import org.opendaylight.genius.interfacemanager.globals.InterfaceInfo;
28 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
29 import org.opendaylight.genius.mdsalutil.ActionInfo;
30 import org.opendaylight.genius.mdsalutil.FlowEntity;
31 import org.opendaylight.genius.mdsalutil.FlowEntityBuilder;
32 import org.opendaylight.genius.mdsalutil.InstructionInfo;
33 import org.opendaylight.genius.mdsalutil.MDSALUtil;
34 import org.opendaylight.genius.mdsalutil.MatchInfo;
35 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
36 import org.opendaylight.genius.mdsalutil.NwConstants;
37 import org.opendaylight.genius.mdsalutil.actions.ActionOutput;
38 import org.opendaylight.genius.mdsalutil.actions.ActionPushVlan;
39 import org.opendaylight.genius.mdsalutil.actions.ActionSetDestinationIp;
40 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
41 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
42 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
43 import org.opendaylight.genius.mdsalutil.actions.ActionSetSourceIp;
44 import org.opendaylight.genius.mdsalutil.actions.ActionSetTcpDestinationPort;
45 import org.opendaylight.genius.mdsalutil.actions.ActionSetTcpSourcePort;
46 import org.opendaylight.genius.mdsalutil.actions.ActionSetUdpDestinationPort;
47 import org.opendaylight.genius.mdsalutil.actions.ActionSetUdpSourcePort;
48 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
49 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
50 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
51 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
52 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
53 import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
54 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
55 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Source;
56 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
57 import org.opendaylight.genius.mdsalutil.matches.MatchTcpDestinationPort;
58 import org.opendaylight.genius.mdsalutil.matches.MatchTcpSourcePort;
59 import org.opendaylight.genius.mdsalutil.matches.MatchUdpDestinationPort;
60 import org.opendaylight.genius.mdsalutil.matches.MatchUdpSourcePort;
61 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
62 import org.opendaylight.genius.mdsalutil.packet.IPv4;
63 import org.opendaylight.genius.mdsalutil.packet.TCP;
64 import org.opendaylight.genius.mdsalutil.packet.UDP;
65 import org.opendaylight.netvirt.elanmanager.api.IElanService;
66 import org.opendaylight.netvirt.natservice.internal.NaptPacketInHandler.NatPacketProcessingState;
67 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
68 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
69 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfL2vlan;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInput;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
90 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
91 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
92 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
93 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
94 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
95 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
96 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
97 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
98 import org.opendaylight.yangtools.yang.common.RpcResult;
99 import org.slf4j.Logger;
100 import org.slf4j.LoggerFactory;
103 public class NaptEventHandler {
104 private static final Logger LOG = LoggerFactory.getLogger(NaptEventHandler.class);
105 private final DataBroker dataBroker;
106 private static IMdsalApiManager mdsalManager;
107 private final PacketProcessingService pktService;
108 private final OdlInterfaceRpcService interfaceManagerRpc;
109 private final NaptManager naptManager;
110 private final IElanService elanManager;
111 private final IdManagerService idManager;
112 private final IInterfaceManager interfaceManager;
113 private static SalFlowService salFlowServiceRpc;
116 public NaptEventHandler(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
117 final NaptManager naptManager,
118 final PacketProcessingService pktService,
119 final OdlInterfaceRpcService interfaceManagerRpc,
120 final IInterfaceManager interfaceManager,
121 final IElanService elanManager,
122 final IdManagerService idManager,
123 final SalFlowService salFlowServiceRpc) {
124 this.dataBroker = dataBroker;
125 NaptEventHandler.mdsalManager = mdsalManager;
126 this.naptManager = naptManager;
127 this.pktService = pktService;
128 this.interfaceManagerRpc = interfaceManagerRpc;
129 this.interfaceManager = interfaceManager;
130 this.elanManager = elanManager;
131 this.idManager = idManager;
132 this.salFlowServiceRpc = salFlowServiceRpc;
135 // TODO Clean up the exception handling
136 @SuppressWarnings("checkstyle:IllegalCatch")
137 public void handleEvent(NAPTEntryEvent naptEntryEvent) {
139 Flow programming logic of the OUTBOUND NAPT TABLE :
140 1) Get the internal IP address, port number, router ID from the event.
141 2) Use the NAPT service getExternalAddressMapping() to get the External IP and the port.
142 3) Build the flow for replacing the Internal IP and port with the External IP and port.
143 a) Write the matching criteria.
144 b) Match the router ID in the metadata.
145 d) Write the VPN ID to the metadata.
146 e) Write the other data.
147 f) Set the apply actions instruction with the action setfield.
148 4) Write the flow to the OUTBOUND NAPT Table and forward to FIB table for routing the traffic.
150 Flow programming logic of the INBOUND NAPT TABLE :
151 Same as Outbound table logic except that :
152 1) Build the flow for replacing the External IP and port with the Internal IP and port.
153 2) Match the VPN ID in the metadata.
154 3) Write the router ID to the metadata.
155 5) Write the flow to the INBOUND NAPT Table and forward to FIB table for routing the traffic.
158 Long routerId = naptEntryEvent.getRouterId();
159 LOG.info("NAT Service : handleEvent() entry for {}:{}, routerID {},"
160 + "TimeElapsed before procesing {}ms,isPktProcessed:{}",
161 naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId,
162 (System.currentTimeMillis() - naptEntryEvent.getObjectCreationTime()),
163 naptEntryEvent.isPktProcessed());
165 BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
166 long bgpVpnId = NatConstants.INVALID_ID;
168 LOG.warn("NAT Service : dpnId is null. Assuming the router ID {} as the BGP VPN ID and proceeding....",
171 LOG.debug("NAT Service : BGP VPN ID {}", bgpVpnId);
172 String vpnName = NatUtil.getRouterName(dataBroker, bgpVpnId);
173 String routerName = NatUtil.getRouterIdfromVpnInstance(dataBroker, vpnName);
174 if (routerName == null) {
175 LOG.error("NAT Service: Unable to find router for VpnName {}", vpnName);
178 routerId = NatUtil.getVpnId(dataBroker, routerName);
179 LOG.debug("NAT Service : Router ID {}", routerId);
180 dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
182 LOG.error("NAT Service : dpnId is null for the router {}", routerId);
186 if (naptEntryEvent.getOperation() == NAPTEntryEvent.Operation.ADD) {
187 LOG.debug("NAT Service : Inside Add operation of NaptEventHandler");
189 // Build and install the NAPT translation flows in the Outbound and Inbound NAPT tables
190 if (!naptEntryEvent.isPktProcessed()) {
192 // Get the External Gateway MAC Address
193 String extGwMacAddress = NatUtil.getExtGwMacAddFromRouterId(dataBroker, routerId);
194 if (extGwMacAddress != null) {
195 LOG.debug("NAT Service : External Gateway MAC address {} found for External Router ID {}",
196 extGwMacAddress, routerId);
198 LOG.error("NAT Service : No External Gateway MAC address found for External Router ID {}",
202 //Get the external network ID from the ExternalRouter model
203 Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
204 if (networkId == null) {
205 LOG.error("NAT Service : networkId is null");
209 //Get the VPN ID from the ExternalNetworks model
210 Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
211 if (vpnUuid == null) {
212 LOG.error("NAT Service : vpnUuid is null");
215 Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
217 //Get the internal IpAddress, internal port number from the event
218 String internalIpAddress = naptEntryEvent.getIpAddress();
219 int internalPort = naptEntryEvent.getPortNumber();
220 SessionAddress internalAddress = new SessionAddress(internalIpAddress, internalPort);
221 NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
223 //Get the external IP address for the corresponding internal IP address
224 SessionAddress externalAddress =
225 naptManager.getExternalAddressMapping(routerId, internalAddress,
226 naptEntryEvent.getProtocol());
227 if (externalAddress == null) {
228 LOG.error("NAT Service : externalAddress is null");
232 Long vpnIdFromExternalSubnet = getVpnIdFromExternalSubnet(dataBroker, routerId,
233 externalAddress.getIpAddress());
234 if (vpnIdFromExternalSubnet != NatConstants.INVALID_ID) {
235 vpnId = vpnIdFromExternalSubnet;
238 // Added External Gateway MAC Address
239 Future<RpcResult<AddFlowOutput>> addFlowResult =
240 buildAndInstallNatFlowsOptionalRpc(dpnId, NwConstants.INBOUND_NAPT_TABLE, vpnId, routerId,
241 bgpVpnId, externalAddress, internalAddress, protocol, extGwMacAddress, true);
242 final BigInteger finalDpnId = dpnId;
243 final Long finalVpnId = vpnId;
244 final Long finalRouterId = routerId;
245 final long finalBgpVpnId = bgpVpnId;
246 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
247 new FutureCallback<RpcResult<AddFlowOutput>>() {
250 public void onSuccess(@Nullable RpcResult<AddFlowOutput> result) {
251 LOG.debug("Configured inbound rule for {} to {}",
252 internalAddress, externalAddress);
253 Future<RpcResult<AddFlowOutput>> addFlowResult =
254 buildAndInstallNatFlowsOptionalRpc(finalDpnId,
255 NwConstants.OUTBOUND_NAPT_TABLE, finalVpnId, finalRouterId,
256 finalBgpVpnId, internalAddress, externalAddress, protocol,
257 extGwMacAddress, true);
258 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
259 new FutureCallback<RpcResult<AddFlowOutput>>() {
262 public void onSuccess(@Nullable RpcResult<AddFlowOutput> result) {
263 LOG.debug("Configured outbound rule, sending packet out"
264 + "from {} to {}", internalAddress, externalAddress);
265 prepareAndSendPacketOut(naptEntryEvent, finalRouterId);
269 public void onFailure(Throwable throwable) {
270 LOG.error("Error configuring outbound SNAT flows using RPC for "
271 + "SNAT connection from {} to {}",
272 internalAddress, externalAddress);
278 public void onFailure(Throwable throwable) {
279 LOG.error("Error configuring inbound SNAT flows using RPC for SNAT connection"
280 + " from {} to {}", internalAddress, externalAddress);
283 String key = naptEntryEvent.getIpAddress() + ":" + naptEntryEvent.getPortNumber();
284 NatPacketProcessingState state = NaptPacketInHandler.INCOMING_PACKET_MAP.get(key);
285 state.setFlowInstalledTime(System.currentTimeMillis());
287 prepareAndSendPacketOut(naptEntryEvent, routerId);
289 LOG.info("NAT Service : handleNaptEvent() exited for {}:{},routerID:{},"
290 + "ElapsedTime packet:{}ms,isPktProcessed:{} ",
291 naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId,
292 (System.currentTimeMillis() - naptEntryEvent.getObjectCreationTime()),
293 naptEntryEvent.isPktProcessed());
295 LOG.debug("NAT Service : Inside delete Operation of NaptEventHandler");
296 removeNatFlows(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId, naptEntryEvent.getIpAddress(),
297 naptEntryEvent.getPortNumber());
298 LOG.info("NAT Service : handleNaptEvent() exited for removeEvent for IP {}, port {}, routerID : {}",
299 naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
301 } catch (Exception e) {
302 LOG.error("NAT Service :Exception in NaptEventHandler.handleEvent() payload {}", naptEntryEvent, e);
306 private void prepareAndSendPacketOut(NAPTEntryEvent naptEntryEvent, Long routerId) {
307 //Send Packetout - tcp or udp packets which got punted to controller.
308 BigInteger metadata = naptEntryEvent.getPacketReceived().getMatch().getMetadata().getMetadata();
309 byte[] inPayload = naptEntryEvent.getPacketReceived().getPayload();
310 Ethernet ethPkt = new Ethernet();
311 if (inPayload != null) {
313 ethPkt.deserialize(inPayload, 0, inPayload.length * NetUtils.NumBitsInAByte);
314 } catch (PacketException e) {
315 LOG.warn("NAT Service : Failed to decode Packet", e);
320 long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
321 LOG.debug("NAT Service : portTag from incoming packet is {}", portTag);
322 String interfaceName = getInterfaceNameFromTag(portTag);
323 LOG.debug("NAT Service : interfaceName fetched from portTag is {}", interfaceName);
324 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
325 .interfaces.Interface iface = null;
327 iface = interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName);
329 LOG.error("NAT Service : Unable to read interface {} from config DataStore", interfaceName);
332 List<ActionInfo> actionInfos = new ArrayList<>();
333 IfL2vlan ifL2vlan = iface.getAugmentation(IfL2vlan.class);
334 if (ifL2vlan != null && ifL2vlan.getVlanId() != null) {
335 vlanId = ifL2vlan.getVlanId().getValue() == null ? 0 : ifL2vlan.getVlanId().getValue();
337 InterfaceInfo infInfo = interfaceManager.getInterfaceInfoFromOperationalDataStore(interfaceName);
338 if (infInfo == null) {
339 LOG.error("NAT Service : error in getting interfaceInfo");
343 byte[] pktOut = buildNaptPacketOut(ethPkt);
344 if (ethPkt.getEtherType() != (short) NwConstants.ETHTYPE_802_1Q) {
346 LOG.debug("NAT Service : vlanId is {}", vlanId);
349 actionInfos.add(new ActionPushVlan(0));
350 actionInfos.add(new ActionSetFieldVlanVid(1, vlanId));
352 LOG.debug("NAT Service : No vlanId {}, may be untagged", vlanId);
356 LOG.debug("NAT Service : This is VLAN Trunk port case - need not do VLAN tagging again");
358 if (pktOut != null) {
359 String routerName = NatUtil.getRouterName(dataBroker, routerId);
360 long tunId = NatUtil.getTunnelIdForNonNaptToNaptFlow(dataBroker, elanManager, idManager, routerId,
362 sendNaptPacketOut(pktOut, infInfo, actionInfos, tunId);
364 LOG.warn("NAT Service : Unable to send Packet Out");
368 public static void buildAndInstallNatFlows(BigInteger dpnId, short tableId, long vpnId, long routerId,
369 long bgpVpnId, SessionAddress actualSourceAddress,
370 SessionAddress translatedSourceAddress,
371 NAPTEntryEvent.Protocol protocol, String extGwMacAddress) {
372 buildAndInstallNatFlowsOptionalRpc(dpnId, tableId, vpnId, routerId, bgpVpnId, actualSourceAddress,
373 translatedSourceAddress, protocol, extGwMacAddress, false);
376 public static Future<RpcResult<AddFlowOutput>> buildAndInstallNatFlowsOptionalRpc(
377 BigInteger dpnId, short tableId, long vpnId, long routerId, long bgpVpnId,
378 SessionAddress actualSourceAddress, SessionAddress translatedSourceAddress,
379 NAPTEntryEvent.Protocol protocol, String extGwMacAddress,
381 LOG.debug("NAT Service : Build and install NAPT flows for table {} for "
382 + "dpnId {} and routerId {}", tableId, dpnId, routerId);
383 //Build the flow for replacing the actual IP and port with the translated IP and port.
385 if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
386 idleTimeout = NatConstants.DEFAULT_NAPT_IDLE_TIMEOUT;
389 if (bgpVpnId != NatConstants.INVALID_ID) {
390 intranetVpnId = bgpVpnId;
392 intranetVpnId = routerId;
394 LOG.debug("NAT Service : Intranet VPN ID {} Router ID {}", intranetVpnId, routerId);
395 String translatedIp = translatedSourceAddress.getIpAddress();
396 int translatedPort = translatedSourceAddress.getPortNumber();
397 String actualIp = actualSourceAddress.getIpAddress();
398 int actualPort = actualSourceAddress.getPortNumber();
399 String switchFlowRef =
400 NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(routerId), actualIp, actualPort);
402 FlowEntity snatFlowEntity = new FlowEntityBuilder()
405 .setFlowId(switchFlowRef)
406 .setPriority(NatConstants.DEFAULT_NAPT_FLOW_PRIORITY)
407 .setFlowName(NatConstants.NAPT_FLOW_NAME)
408 .setIdleTimeOut(idleTimeout)
410 .setCookie(NatUtil.getCookieNaptFlow(routerId))
411 .setMatchInfoList(buildAndGetMatchInfo(actualIp, actualPort, tableId, protocol, intranetVpnId))
412 .setInstructionInfoList(buildAndGetSetActionInstructionInfo(translatedIp, translatedPort,
413 intranetVpnId, vpnId, tableId, protocol, extGwMacAddress))
414 .setSendFlowRemFlag(true)
417 LOG.debug("NAT Service : Installing the NAPT flow in the table {} for the switch with the DPN ID {} ",
420 // Install flows using RPC to prevent race with future packet-out that depends on this flow
421 Future<RpcResult<AddFlowOutput>> addFlowResult = null;
423 Flow flow = snatFlowEntity.getFlowBuilder().build();
424 NodeRef nodeRef = getNodeRef(dpnId);
425 FlowRef flowRef = getFlowRef(dpnId, flow);
426 AddFlowInput addFlowInput = new AddFlowInputBuilder(flow).setFlowRef(flowRef).setNode(nodeRef).build();
427 long startTime = System.currentTimeMillis();
428 addFlowResult = salFlowServiceRpc.addFlow(addFlowInput);
429 LOG.debug("NAT Service : Time elapsed for salFlowServiceRpc table {}: {}ms ", tableId,
430 (System.currentTimeMillis() - startTime));
431 // Keep flow installation through MDSAL as well to be able to handle switch failures
432 startTime = System.currentTimeMillis();
433 mdsalManager.installFlow(snatFlowEntity);
434 LOG.debug("NAT Service : Time elapsed for installFlow table {}: {}ms ", tableId,
435 (System.currentTimeMillis() - startTime));
437 long startTime = System.currentTimeMillis();
438 mdsalManager.syncInstallFlow(snatFlowEntity, 1);
439 LOG.debug("NAT Service : Time elapsed for syncInstallFlow table {}: {}ms ", tableId,
440 (System.currentTimeMillis() - startTime));
442 LOG.trace("NAT Service : Exited buildAndInstallNatflows");
444 return addFlowResult;
447 private static Node buildInventoryDpnNode(BigInteger dpnId) {
448 NodeId nodeId = new NodeId("openflow:" + dpnId);
449 Node nodeDpn = new NodeBuilder().setId(nodeId).setKey(new NodeKey(nodeId)).build();
453 private static NodeRef getNodeRef(BigInteger dpnId) {
454 NodeId nodeId = new NodeId("openflow:" + dpnId);
455 return new NodeRef(InstanceIdentifier.builder(Nodes.class)
456 .child(Node.class, new NodeKey(nodeId)).toInstance());
459 public static FlowRef getFlowRef(BigInteger dpId, Flow flow) {
460 FlowKey flowKey = new FlowKey(new FlowId(flow.getId()));
461 Node nodeDpn = buildInventoryDpnNode(dpId);
462 InstanceIdentifier<Flow> flowInstanceId =
463 InstanceIdentifier.builder(Nodes.class)
464 .child(Node.class, nodeDpn.getKey()).augmentation(FlowCapableNode.class)
465 .child(Table.class, new TableKey(flow.getTableId()))
466 .child(Flow.class, flowKey)
468 return new FlowRef(flowInstanceId);
471 private static List<MatchInfo> buildAndGetMatchInfo(String ip, int port, short tableId,
472 NAPTEntryEvent.Protocol protocol, long segmentId) {
473 MatchInfo ipMatchInfo = null;
474 MatchInfo portMatchInfo = null;
475 MatchInfo protocolMatchInfo = null;
476 InetAddress ipAddress = null;
477 String ipAddressAsString = null;
479 ipAddress = InetAddress.getByName(ip);
480 ipAddressAsString = ipAddress.getHostAddress();
482 } catch (UnknownHostException e) {
483 LOG.error("NAT Service : UnknowHostException in buildAndGetMatchInfo. Failed to build NAPT Flow for "
484 + "ip {}", ipAddress);
488 MatchInfo metaDataMatchInfo = null;
489 if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
490 ipMatchInfo = new MatchIpv4Source(ipAddressAsString, "32");
491 if (protocol == NAPTEntryEvent.Protocol.TCP) {
492 protocolMatchInfo = MatchIpProtocol.TCP;
493 portMatchInfo = new MatchTcpSourcePort(port);
494 } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
495 protocolMatchInfo = MatchIpProtocol.UDP;
496 portMatchInfo = new MatchUdpSourcePort(port);
499 new MatchMetadata(MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID);
501 ipMatchInfo = new MatchIpv4Destination(ipAddressAsString, "32");
502 if (protocol == NAPTEntryEvent.Protocol.TCP) {
503 protocolMatchInfo = MatchIpProtocol.TCP;
504 portMatchInfo = new MatchTcpDestinationPort(port);
505 } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
506 protocolMatchInfo = MatchIpProtocol.UDP;
507 portMatchInfo = new MatchUdpDestinationPort(port);
509 //metaDataMatchInfo = new MatchMetadata(BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID);
511 ArrayList<MatchInfo> matchInfo = new ArrayList<>();
512 matchInfo.add(MatchEthernetType.IPV4);
513 matchInfo.add(ipMatchInfo);
514 matchInfo.add(protocolMatchInfo);
515 matchInfo.add(portMatchInfo);
516 if (tableId == NwConstants.OUTBOUND_NAPT_TABLE) {
517 matchInfo.add(metaDataMatchInfo);
522 private static List<InstructionInfo> buildAndGetSetActionInstructionInfo(String ipAddress, int port,
523 long segmentId, long vpnId,
525 NAPTEntryEvent.Protocol protocol,
526 String extGwMacAddress) {
527 ActionInfo ipActionInfo = null;
528 ActionInfo macActionInfo = null;
529 ActionInfo portActionInfo = null;
530 ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
531 ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
533 case NwConstants.OUTBOUND_NAPT_TABLE:
534 ipActionInfo = new ActionSetSourceIp(ipAddress);
535 // Added External Gateway MAC Address
536 macActionInfo = new ActionSetFieldEthernetSource(new MacAddress(extGwMacAddress));
537 if (protocol == NAPTEntryEvent.Protocol.TCP) {
538 portActionInfo = new ActionSetTcpSourcePort(port);
539 } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
540 portActionInfo = new ActionSetUdpSourcePort(port);
542 // reset the split-horizon bit to allow traffic from tunnel to be sent back to the provider port
543 instructionInfo.add(new InstructionWriteMetadata(MetaDataUtil.getVpnIdMetadata(vpnId),
544 MetaDataUtil.METADATA_MASK_VRFID.or(MetaDataUtil.METADATA_MASK_SH_FLAG)));
547 case NwConstants.INBOUND_NAPT_TABLE:
548 ipActionInfo = new ActionSetDestinationIp(ipAddress);
549 if (protocol == NAPTEntryEvent.Protocol.TCP) {
550 portActionInfo = new ActionSetTcpDestinationPort(port);
551 } else if (protocol == NAPTEntryEvent.Protocol.UDP) {
552 portActionInfo = new ActionSetUdpDestinationPort(port);
554 instructionInfo.add(new InstructionWriteMetadata(
555 MetaDataUtil.getVpnIdMetadata(segmentId), MetaDataUtil.METADATA_MASK_VRFID));
559 LOG.error("NAT Service : Neither OUTBOUND_NAPT_TABLE nor INBOUND_NAPT_TABLE matches with "
560 + "input table id {}", tableId);
564 listActionInfo.add(ipActionInfo);
565 listActionInfo.add(portActionInfo);
566 if (macActionInfo != null) {
567 listActionInfo.add(macActionInfo);
568 LOG.debug("NAT Service : External GW MAC Address {} is found ", macActionInfo);
570 instructionInfo.add(new InstructionApplyActions(listActionInfo));
571 instructionInfo.add(new InstructionGotoTable(NwConstants.NAPT_PFIB_TABLE));
573 return instructionInfo;
576 void removeNatFlows(BigInteger dpnId, short tableId ,long segmentId, String ip, int port) {
577 if (dpnId == null || dpnId.equals(BigInteger.ZERO)) {
578 LOG.error("NAT Service : DPN ID {} is invalid" , dpnId);
580 LOG.debug("NAT Service : Remove NAPT flows for dpnId {}, segmentId {}, ip {} and port {} ",
581 dpnId, segmentId, ip, port);
583 //Build the flow with the port IP and port as the match info.
584 String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(segmentId), ip, port);
585 FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef);
586 LOG.debug("NAT Service : Remove the flow in the table {} for the switch with the DPN ID {}",
587 NwConstants.INBOUND_NAPT_TABLE, dpnId);
588 long startTime = System.currentTimeMillis();
589 mdsalManager.removeFlow(snatFlowEntity);
590 LOG.debug("NAT Service : Time Taken for removeFlow:{}ms", (System.currentTimeMillis() - startTime));
594 protected byte[] buildNaptPacketOut(Ethernet etherPkt) {
595 LOG.debug("NAT Service : About to build Napt Packet Out");
596 if (etherPkt.getPayload() instanceof IPv4) {
598 IPv4 ipPkt = (IPv4) etherPkt.getPayload();
599 if (ipPkt.getPayload() instanceof TCP || ipPkt.getPayload() instanceof UDP) {
601 rawPkt = etherPkt.serialize();
603 } catch (PacketException e2) {
604 LOG.error("failed to build NAPT Packet out ", e2);
608 LOG.error("NAT Service : Unable to build NaptPacketOut since its neither TCP nor UDP");
612 LOG.error("NAT Service : Unable to build NaptPacketOut since its not IPv4 packet");
616 private void sendNaptPacketOut(byte[] pktOut, InterfaceInfo infInfo, List<ActionInfo> actionInfos, Long tunId) {
617 LOG.trace("NAT Service: Sending packet out DpId {}, interfaceInfo {}", infInfo.getDpId(), infInfo);
618 // set inPort, and action as OFPP_TABLE so that it starts from table 0 (lowest table as per spec)
619 actionInfos.add(new ActionSetFieldTunnelId(2, BigInteger.valueOf(tunId)));
620 actionInfos.add(new ActionOutput(3, new Uri("0xfffffff9")));
621 NodeConnectorRef inPort = MDSALUtil.getNodeConnRef(infInfo.getDpId(), String.valueOf(infInfo.getPortNo()));
622 LOG.debug("NAT Service : inPort for packetout is being set to {}", String.valueOf(infInfo.getPortNo()));
623 TransmitPacketInput output = MDSALUtil.getPacketOut(actionInfos, pktOut, infInfo.getDpId().longValue(), inPort);
624 LOG.debug("NAT Service: Transmitting packet: {}, inPort {}", output, inPort);
625 this.pktService.transmitPacket(output);
628 private String getInterfaceNameFromTag(long portTag) {
629 String interfaceName = null;
630 GetInterfaceFromIfIndexInput input =
631 new GetInterfaceFromIfIndexInputBuilder().setIfIndex((int) portTag).build();
632 Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
633 interfaceManagerRpc.getInterfaceFromIfIndex(input);
635 GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
636 interfaceName = output.getInterfaceName();
637 } catch (InterruptedException | ExecutionException e) {
638 LOG.error("NAT Service : Error while retrieving the interfaceName from tag using "
639 + "getInterfaceFromIfIndex RPC");
641 LOG.trace("NAT Service : Returning interfaceName {} for tag {} form getInterfaceNameFromTag",
642 interfaceName, portTag);
643 return interfaceName;
646 private long getVpnIdFromExternalSubnet(DataBroker dataBroker, Long routerId, String externalIpAddress) {
647 String routerName = NatUtil.getRouterName(dataBroker, routerId);
648 if (routerName != null) {
649 Routers extRouter = NatUtil.getRoutersFromConfigDS(dataBroker, routerName);
650 if (extRouter != null) {
651 return NatUtil.getExternalSubnetVpnIdForRouterExternalIp(dataBroker, externalIpAddress, extRouter);
655 return NatConstants.INVALID_ID;