2 * Copyright (c) 2016 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.vpnservice.natservice.internal;
11 import org.opendaylight.vpnservice.mdsalutil.*;
12 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
13 import org.opendaylight.vpnservice.mdsalutil.packet.IPProtocols;
14 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
15 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
16 import java.math.BigInteger;
17 import java.net.InetAddress;
18 import java.net.UnknownHostException;
19 import java.util.List;
20 import java.util.ArrayList;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
24 public class NaptEventHandler {
25 private NaptManager naptManager;
26 private static final Logger LOG = LoggerFactory.getLogger(NaptEventHandler.class);
27 private static IMdsalApiManager mdsalManager;
28 private DataBroker dataBroker;
30 public NaptEventHandler(final DataBroker dataBroker) {
31 this.dataBroker = dataBroker;
34 public void setMdsalManager(IMdsalApiManager mdsalManager) {
35 this.mdsalManager = mdsalManager;
38 public void setNaptManager(NaptManager naptManager) {
39 this.naptManager = naptManager;
43 public void handleEvent(NAPTEntryEvent naptEntryEvent){
45 Flow programming logic of the OUTBOUND NAPT TABLE :
46 1) Get the internal IP address, port number, router ID from the event.
47 2) Use the NAPT service getExternalAddressMapping() to get the External IP and the port.
48 3) Build the flow for replacing the Internal IP and port with the External IP and port.
49 a) Write the matching criteria.
50 b) Match the router ID in the metadata.
51 d) Write the VPN ID to the metadata.
52 e) Write the other data.
53 f) Set the apply actions instruction with the action setfield.
54 4) Write the flow to the OUTBOUND NAPT Table and forward to FIB table for routing the traffic.
56 Flow programming logic of the INBOUND NAPT TABLE :
57 Same as Outbound table logic except that :
58 1) Build the flow for replacing the External IP and port with the Internal IP and port.
59 2) Match the VPN ID in the metadata.
60 3) Write the router ID to the metadata.
61 5) Write the flow to the INBOUND NAPT Table and forward to FIB table for routing the traffic.
63 Long routerId = naptEntryEvent.getRouterId();
64 LOG.info("NAT Service : handleEvent() entry for IP {}, port {}, routerID {}", naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
65 BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
67 LOG.error("NAT Service : dpnId is null");
70 if(naptEntryEvent.getOperation() == NAPTEntryEvent.Operation.ADD) {
71 LOG.debug("NAT Service : Inside Add operation of NaptEventHandler");
73 //Get the external network ID from the ExternalRouter model
74 Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
75 if(networkId == null ){
76 LOG.error("NAT Service : networkId is null");
80 //Get the VPN ID from the ExternalNetworks model
81 Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
83 LOG.error("NAT Service : vpnUuid is null");
86 Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
88 //Get the internal IpAddress, internal port number from the event
89 String internalIpAddress = naptEntryEvent.getIpAddress();
90 int internalPort = naptEntryEvent.getPortNumber();
91 SessionAddress internalAddress = new SessionAddress(internalIpAddress, internalPort);
92 NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
94 //Get the external IP address for the corresponding internal IP address
95 SessionAddress externalAddress = naptManager.getExternalAddressMapping(routerId, internalAddress, naptEntryEvent.getProtocol());
96 if(externalAddress == null ){
97 LOG.error("NAT Service : externalAddress is null");
101 //Build and install the NAPT translation flows in the Outbound and Inbound NAPT tables
102 buildAndInstallNatFlows(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, vpnId, routerId, internalAddress, externalAddress, protocol);
103 buildAndInstallNatFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnId, routerId, externalAddress, internalAddress, protocol);
106 LOG.debug("NAT Service : Inside delete Operation of NaptEventHandler");
107 removeNatFlows(dpnId, routerId, naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber());
110 LOG.info("NAT Service : handleNaptEvent() exited for IP, port, routerID : {}", naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
113 public static void buildAndInstallNatFlows(BigInteger dpnId, short tableId, long vpnId, long routerId, SessionAddress actualSourceAddress,
114 SessionAddress translatedSourceAddress, NAPTEntryEvent.Protocol protocol){
115 LOG.debug("NAT Service : Build and install NAPT flows in InBound and OutBound tables for dpnId {} and routerId {}", dpnId, routerId);
116 //Build the flow for replacing the actual IP and port with the translated IP and port.
117 String actualIp = actualSourceAddress.getIpAddress();
118 int actualPort = actualSourceAddress.getPortNumber();
119 String translatedIp = translatedSourceAddress.getIpAddress();
120 String translatedPort = String.valueOf(translatedSourceAddress.getPortNumber());
122 if(tableId == NatConstants.OUTBOUND_NAPT_TABLE) {
123 idleTimeout = NatConstants.DEFAULT_NAPT_IDLE_TIMEOUT;
125 long metaDataValue = routerId;
126 String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(metaDataValue), actualIp, actualPort);
128 FlowEntity snatFlowEntity = MDSALUtil.buildFlowEntity(dpnId, tableId, switchFlowRef, NatConstants.DEFAULT_NAPT_FLOW_PRIORITY, NatConstants.NAPT_FLOW_NAME,
129 idleTimeout, 0, NatUtil.getCookieNaptFlow(metaDataValue), buildAndGetMatchInfo(actualIp, actualPort, tableId, protocol, routerId, vpnId),
130 buildAndGetSetActionInstructionInfo(translatedIp, translatedPort, routerId, vpnId, tableId, protocol));
132 snatFlowEntity.setSendFlowRemFlag(true);
134 LOG.debug("NAT Service : Installing the NAPT flow in the table {} for the switch with the DPN ID {} ", tableId, dpnId);
135 mdsalManager.installFlow(snatFlowEntity);
138 private static List<MatchInfo> buildAndGetMatchInfo(String ip, int port, short tableId, NAPTEntryEvent.Protocol protocol, long segmentId, long vpnId){
139 MatchInfo ipMatchInfo = null;
140 MatchInfo portMatchInfo = null;
141 MatchInfo protocolMatchInfo = null;
142 InetAddress ipAddress = null;
143 String ipAddressAsString = null;
145 ipAddress = InetAddress.getByName(ip);
146 ipAddressAsString = ipAddress.getHostAddress();
148 } catch (UnknownHostException e) {
149 LOG.error("NAT Service : UnknowHostException in buildAndGetMatchInfo. Failed to build NAPT Flow for ip {}", ipAddress);
153 MatchInfo metaDataMatchInfo;
154 if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){
155 ipMatchInfo = new MatchInfo(MatchFieldType.ipv4_source, new String[] {ipAddressAsString, "32" });
156 if(protocol == NAPTEntryEvent.Protocol.TCP) {
157 protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.TCP.intValue()});
158 portMatchInfo = new MatchInfo(MatchFieldType.tcp_src, new long[]{port});
159 } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
160 protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.UDP.intValue()});
161 portMatchInfo = new MatchInfo(MatchFieldType.udp_src, new long[]{port});
163 metaDataMatchInfo = new MatchInfo(MatchFieldType.metadata, new BigInteger[]{BigInteger.valueOf(segmentId), MetaDataUtil.METADATA_MASK_VRFID});
165 ipMatchInfo = new MatchInfo(MatchFieldType.ipv4_destination, new String[] {ipAddressAsString, "32" });
166 if(protocol == NAPTEntryEvent.Protocol.TCP) {
167 protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.TCP.intValue()});
168 portMatchInfo = new MatchInfo(MatchFieldType.tcp_dst, new long[]{port});
169 } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
170 protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.UDP.intValue()});
171 portMatchInfo = new MatchInfo(MatchFieldType.udp_dst, new long[]{port});
173 metaDataMatchInfo = new MatchInfo(MatchFieldType.metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID});
175 ArrayList<MatchInfo> matchInfo = new ArrayList<>();
176 matchInfo.add(new MatchInfo(MatchFieldType.eth_type, new long[] { 0x0800L }));
177 matchInfo.add(ipMatchInfo);
178 matchInfo.add(protocolMatchInfo);
179 matchInfo.add(portMatchInfo);
180 matchInfo.add(metaDataMatchInfo);
184 private static List<InstructionInfo> buildAndGetSetActionInstructionInfo(String ipAddress, String port, long routerId, long vpnId, short tableId, NAPTEntryEvent.Protocol protocol) {
185 ActionInfo ipActionInfo = null;
186 ActionInfo portActionInfo = null;
187 ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
188 ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
190 if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){
191 ipActionInfo = new ActionInfo(ActionType.set_source_ip, new String[] {ipAddress});
192 if(protocol == NAPTEntryEvent.Protocol.TCP) {
193 portActionInfo = new ActionInfo( ActionType.set_tcp_source_port, new String[] {port});
194 } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
195 portActionInfo = new ActionInfo( ActionType.set_udp_source_port, new String[] {port});
197 instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID}));
199 ipActionInfo = new ActionInfo(ActionType.set_destination_ip, new String[] {ipAddress});
200 if(protocol == NAPTEntryEvent.Protocol.TCP) {
201 portActionInfo = new ActionInfo( ActionType.set_tcp_destination_port, new String[] {port});
202 } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
203 portActionInfo = new ActionInfo( ActionType.set_udp_destination_port, new String[] {port});
205 instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(routerId), MetaDataUtil.METADATA_MASK_VRFID}));
208 listActionInfo.add(ipActionInfo);
209 listActionInfo.add(portActionInfo);
211 instructionInfo.add(new InstructionInfo(InstructionType.apply_actions, listActionInfo));
212 instructionInfo.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.NAPT_PFIB_TABLE }));
214 return instructionInfo;
217 private void removeNatFlows(BigInteger dpnId, long routerId, String externalIp, int externalPort){
218 LOG.debug("NAT Service : Remove NAPT flows for dpnId {} and routerId {}", dpnId, routerId);
220 //Build the flow with the externalPort IP and port as the match info.
221 String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, NatConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId), externalIp, externalPort);
222 FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, NatConstants.INBOUND_NAPT_TABLE, switchFlowRef);
223 LOG.debug("NAT Service : Remove the flow in the table {} for the switch with the DPN ID {}", NatConstants.INBOUND_NAPT_TABLE, dpnId);
224 mdsalManager.removeFlow(snatFlowEntity);