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.netvirt.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);
67 BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
68 long bgpVpnId = NatConstants.INVALID_ID;
70 LOG.warn("NAT Service : dpnId is null. Assuming the router ID {} as the BGP VPN ID and proceeding....", routerId);
72 LOG.debug("NAT Service : BGP VPN ID {}", bgpVpnId);
73 String vpnName = NatUtil.getRouterName(dataBroker, bgpVpnId);
74 String routerName = NatUtil.getRouterIdfromVpnId(dataBroker, vpnName);
75 routerId = NatUtil.getVpnId(dataBroker, routerName);
76 LOG.debug("NAT Service : Router ID {}", routerId);
77 dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
79 LOG.error("NAT Service : dpnId is null for the router {}", routerId);
83 if(naptEntryEvent.getOperation() == NAPTEntryEvent.Operation.ADD) {
84 LOG.debug("NAT Service : Inside Add operation of NaptEventHandler");
86 //Get the external network ID from the ExternalRouter model
87 Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
88 if(networkId == null ){
89 LOG.error("NAT Service : networkId is null");
93 //Get the VPN ID from the ExternalNetworks model
94 Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
96 LOG.error("NAT Service : vpnUuid is null");
99 Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
101 //Get the internal IpAddress, internal port number from the event
102 String internalIpAddress = naptEntryEvent.getIpAddress();
103 int internalPort = naptEntryEvent.getPortNumber();
104 SessionAddress internalAddress = new SessionAddress(internalIpAddress, internalPort);
105 NAPTEntryEvent.Protocol protocol = naptEntryEvent.getProtocol();
107 //Get the external IP address for the corresponding internal IP address
108 SessionAddress externalAddress = naptManager.getExternalAddressMapping(routerId, internalAddress, naptEntryEvent.getProtocol());
109 if(externalAddress == null ){
110 if(externalAddress == null){
111 LOG.error("NAT Service : externalAddress is null");
115 //Build and install the NAPT translation flows in the Outbound and Inbound NAPT tables
116 buildAndInstallNatFlows(dpnId, NatConstants.OUTBOUND_NAPT_TABLE, vpnId, routerId, bgpVpnId, internalAddress, externalAddress, protocol);
117 buildAndInstallNatFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, vpnId, routerId, bgpVpnId, externalAddress, internalAddress, protocol);
120 LOG.debug("NAT Service : Inside delete Operation of NaptEventHandler");
121 removeNatFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, routerId, naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber());
124 LOG.info("NAT Service : handleNaptEvent() exited for IP, port, routerID : {}", naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
127 public static void buildAndInstallNatFlows(BigInteger dpnId, short tableId, long vpnId, long routerId, long bgpVpnId, SessionAddress actualSourceAddress,
128 SessionAddress translatedSourceAddress, NAPTEntryEvent.Protocol protocol){
129 LOG.debug("NAT Service : Build and install NAPT flows in InBound and OutBound tables for dpnId {} and routerId {}", dpnId, routerId);
130 //Build the flow for replacing the actual IP and port with the translated IP and port.
131 String actualIp = actualSourceAddress.getIpAddress();
132 int actualPort = actualSourceAddress.getPortNumber();
133 String translatedIp = translatedSourceAddress.getIpAddress();
134 String translatedPort = String.valueOf(translatedSourceAddress.getPortNumber());
136 if(tableId == NatConstants.OUTBOUND_NAPT_TABLE) {
137 idleTimeout = NatConstants.DEFAULT_NAPT_IDLE_TIMEOUT;
139 long metaDataValue = routerId;
140 String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(metaDataValue), actualIp, actualPort);
143 if(bgpVpnId != NatConstants.INVALID_ID){
144 intranetVpnId = bgpVpnId;
146 intranetVpnId = routerId;
148 LOG.debug("NAT Service : Intranet VPN ID {}", intranetVpnId);
149 LOG.debug("NAT Service : Router ID {}", routerId);
150 FlowEntity snatFlowEntity = MDSALUtil.buildFlowEntity(dpnId, tableId, switchFlowRef, NatConstants.DEFAULT_NAPT_FLOW_PRIORITY, NatConstants.NAPT_FLOW_NAME,
151 idleTimeout, 0, NatUtil.getCookieNaptFlow(metaDataValue), buildAndGetMatchInfo(actualIp, actualPort, tableId, protocol, intranetVpnId, vpnId),
152 buildAndGetSetActionInstructionInfo(translatedIp, translatedPort, intranetVpnId, vpnId, tableId, protocol));
154 snatFlowEntity.setSendFlowRemFlag(true);
156 LOG.debug("NAT Service : Installing the NAPT flow in the table {} for the switch with the DPN ID {} ", tableId, dpnId);
157 mdsalManager.installFlow(snatFlowEntity);
160 private static List<MatchInfo> buildAndGetMatchInfo(String ip, int port, short tableId, NAPTEntryEvent.Protocol protocol, long segmentId, long vpnId){
161 MatchInfo ipMatchInfo = null;
162 MatchInfo portMatchInfo = null;
163 MatchInfo protocolMatchInfo = null;
164 InetAddress ipAddress = null;
165 String ipAddressAsString = null;
167 ipAddress = InetAddress.getByName(ip);
168 ipAddressAsString = ipAddress.getHostAddress();
170 } catch (UnknownHostException e) {
171 LOG.error("NAT Service : UnknowHostException in buildAndGetMatchInfo. Failed to build NAPT Flow for ip {}", ipAddress);
175 MatchInfo metaDataMatchInfo = null;
176 if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){
177 ipMatchInfo = new MatchInfo(MatchFieldType.ipv4_source, new String[] {ipAddressAsString, "32" });
178 if(protocol == NAPTEntryEvent.Protocol.TCP) {
179 protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.TCP.intValue()});
180 portMatchInfo = new MatchInfo(MatchFieldType.tcp_src, new long[]{port});
181 } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
182 protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.UDP.intValue()});
183 portMatchInfo = new MatchInfo(MatchFieldType.udp_src, new long[]{port});
185 metaDataMatchInfo = new MatchInfo(MatchFieldType.metadata, new BigInteger[]{BigInteger.valueOf(segmentId), MetaDataUtil.METADATA_MASK_VRFID});
187 ipMatchInfo = new MatchInfo(MatchFieldType.ipv4_destination, new String[] {ipAddressAsString, "32" });
188 if(protocol == NAPTEntryEvent.Protocol.TCP) {
189 protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.TCP.intValue()});
190 portMatchInfo = new MatchInfo(MatchFieldType.tcp_dst, new long[]{port});
191 } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
192 protocolMatchInfo = new MatchInfo(MatchFieldType.ip_proto, new long[] {IPProtocols.UDP.intValue()});
193 portMatchInfo = new MatchInfo(MatchFieldType.udp_dst, new long[]{port});
195 //metaDataMatchInfo = new MatchInfo(MatchFieldType.metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID});
197 ArrayList<MatchInfo> matchInfo = new ArrayList<>();
198 matchInfo.add(new MatchInfo(MatchFieldType.eth_type, new long[] { 0x0800L }));
199 matchInfo.add(ipMatchInfo);
200 matchInfo.add(protocolMatchInfo);
201 matchInfo.add(portMatchInfo);
202 if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){
203 matchInfo.add(metaDataMatchInfo);
208 private static List<InstructionInfo> buildAndGetSetActionInstructionInfo(String ipAddress, String port, long segmentId, long vpnId, short tableId, NAPTEntryEvent.Protocol protocol) {
209 ActionInfo ipActionInfo = null;
210 ActionInfo portActionInfo = null;
211 ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
212 ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
214 if(tableId == NatConstants.OUTBOUND_NAPT_TABLE){
215 ipActionInfo = new ActionInfo(ActionType.set_source_ip, new String[] {ipAddress});
216 if(protocol == NAPTEntryEvent.Protocol.TCP) {
217 portActionInfo = new ActionInfo( ActionType.set_tcp_source_port, new String[] {port});
218 } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
219 portActionInfo = new ActionInfo( ActionType.set_udp_source_port, new String[] {port});
221 instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID}));
223 ipActionInfo = new ActionInfo(ActionType.set_destination_ip, new String[] {ipAddress});
224 if(protocol == NAPTEntryEvent.Protocol.TCP) {
225 portActionInfo = new ActionInfo( ActionType.set_tcp_destination_port, new String[] {port});
226 } else if(protocol == NAPTEntryEvent.Protocol.UDP) {
227 portActionInfo = new ActionInfo( ActionType.set_udp_destination_port, new String[] {port});
229 instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(segmentId), MetaDataUtil.METADATA_MASK_VRFID}));
232 listActionInfo.add(ipActionInfo);
233 listActionInfo.add(portActionInfo);
235 instructionInfo.add(new InstructionInfo(InstructionType.apply_actions, listActionInfo));
236 instructionInfo.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.NAPT_PFIB_TABLE }));
238 return instructionInfo;
241 void removeNatFlows(BigInteger dpnId, short tableId ,long segmentId, String ip, int port){
242 if(dpnId == null || dpnId.equals(BigInteger.ZERO)){
243 LOG.error("NAT Service : DPN ID {} is invalid" , dpnId);
245 LOG.debug("NAT Service : Remove NAPT flows for dpnId {}, segmentId {}, ip {} and port {} ", dpnId, segmentId, ip, port);
247 //Build the flow with the port IP and port as the match info.
248 String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(segmentId), ip, port);
249 FlowEntity snatFlowEntity = NatUtil.buildFlowEntity(dpnId, tableId, switchFlowRef);
250 LOG.debug("NAT Service : Remove the flow in the table {} for the switch with the DPN ID {}", NatConstants.INBOUND_NAPT_TABLE, dpnId);
251 mdsalManager.removeFlow(snatFlowEntity);