Bump versions by 0.1.0 for next dev cycle
[vpnservice.git] / natservice / natservice-impl / src / main / java / org / opendaylight / vpnservice / natservice / internal / NaptEventHandler.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.vpnservice.natservice.internal;
10
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;
23
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;
29
30     public NaptEventHandler(final DataBroker dataBroker) {
31         this.dataBroker = dataBroker;
32     }
33
34     public void setMdsalManager(IMdsalApiManager mdsalManager) {
35         this.mdsalManager = mdsalManager;
36     }
37
38     public void setNaptManager(NaptManager naptManager) {
39         this.naptManager = naptManager;
40     }
41
42
43     public void handleEvent(NAPTEntryEvent naptEntryEvent){
44     /*
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.
55
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.
62     */
63         Long routerId = naptEntryEvent.getRouterId();
64         LOG.info("NAT Service : handleEvent() entry for IP {}, port {}, routerID {}", naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
65
66         //Get the DPN ID
67         BigInteger dpnId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
68         long bgpVpnId = NatConstants.INVALID_ID;
69         if(dpnId == null ){
70             LOG.warn("NAT Service : dpnId is null. Assuming the router ID {} as the BGP VPN ID and proceeding....", routerId);
71             bgpVpnId = 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);
78             if(dpnId == null){
79                 LOG.error("NAT Service : dpnId is null for the router {}", routerId);
80                 return;
81             }
82         }
83         if(naptEntryEvent.getOperation() == NAPTEntryEvent.Operation.ADD) {
84             LOG.debug("NAT Service : Inside Add operation of NaptEventHandler");
85
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");
90                 return;
91             }
92
93             //Get the VPN ID from the ExternalNetworks model
94             Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
95             if(vpnUuid == null ){
96                 LOG.error("NAT Service : vpnUuid is null");
97                 return;
98             }
99             Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
100
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();
106
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");
112                     return;
113                 }
114             }
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);
118
119         }else{
120             LOG.debug("NAT Service : Inside delete Operation of NaptEventHandler");
121             removeNatFlows(dpnId, NatConstants.INBOUND_NAPT_TABLE, routerId, naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber());
122         }
123
124         LOG.info("NAT Service : handleNaptEvent() exited for IP, port, routerID : {}", naptEntryEvent.getIpAddress(), naptEntryEvent.getPortNumber(), routerId);
125     }
126
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());
135         int idleTimeout=0;
136         if(tableId == NatConstants.OUTBOUND_NAPT_TABLE) {
137             idleTimeout = NatConstants.DEFAULT_NAPT_IDLE_TIMEOUT;
138         }
139         long metaDataValue = routerId;
140         String switchFlowRef = NatUtil.getNaptFlowRef(dpnId, tableId, String.valueOf(metaDataValue), actualIp, actualPort);
141
142         long intranetVpnId;
143         if(bgpVpnId != NatConstants.INVALID_ID){
144             intranetVpnId = bgpVpnId;
145         }else{
146             intranetVpnId = routerId;
147         }
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));
153
154         snatFlowEntity.setSendFlowRemFlag(true);
155
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);
158     }
159
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;
166         try {
167             ipAddress = InetAddress.getByName(ip);
168             ipAddressAsString = ipAddress.getHostAddress();
169
170         } catch (UnknownHostException e) {
171             LOG.error("NAT Service : UnknowHostException in buildAndGetMatchInfo. Failed  to build NAPT Flow for  ip {}", ipAddress);
172             return null;
173         }
174
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});
184             }
185             metaDataMatchInfo = new MatchInfo(MatchFieldType.metadata, new BigInteger[]{BigInteger.valueOf(segmentId), MetaDataUtil.METADATA_MASK_VRFID});
186         }else{
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});
194             }
195             //metaDataMatchInfo = new MatchInfo(MatchFieldType.metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID});
196         }
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);
204         }
205         return matchInfo;
206     }
207
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<>();
213
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});
220             }
221             instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID}));
222         }else{
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});
228             }
229             instructionInfo.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[]{BigInteger.valueOf(segmentId), MetaDataUtil.METADATA_MASK_VRFID}));
230         }
231
232         listActionInfo.add(ipActionInfo);
233         listActionInfo.add(portActionInfo);
234
235         instructionInfo.add(new InstructionInfo(InstructionType.apply_actions, listActionInfo));
236         instructionInfo.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.NAPT_PFIB_TABLE }));
237
238         return instructionInfo;
239     }
240
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);
244         }
245         LOG.debug("NAT Service : Remove NAPT flows for dpnId {}, segmentId {}, ip {} and port {} ", dpnId, segmentId, ip, port);
246
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);
252
253     }
254
255 }