blob: 62840c996c1edf407baacbc97fd5648ba7ab7aee [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// Copyright (C) 2004-2010 TintaDigital - STI, LDA.
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation; either version 2 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program; if not, write to the Free Software
15// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16//
17// As a special exception, you may use this file as part of a free software
18// library without restriction. Specifically, if other files instantiate
19// templates or use macros or inline functions from this file, or you compile
20// this file and link it with other files to produce an executable, this
21// file does not by itself cause the resulting executable to be covered by
22// the GNU General Public License. This exception does not however
23// invalidate any other reasons why the executable file might be covered by
24// the GNU General Public License.
25//
26// This exception applies only to the code released under the name GNU
27// Common C++. If you copy code from other releases into a copy of GNU
28// Common C++, as the General Public License permits, the exception does
29// not apply to the code that you add in this way. To avoid misleading
30// anyone as to the status of such modified files, you must delete
31// this exception notice from them.
32//
33// If you write modifications of your own for GNU Common C++, it is your choice
34// whether to permit this exception to apply to your modifications.
35// If you do not wish that, delete this exception notice.
36//
37
38/**
39 * @file nat.c
40 * @short Network Address Translation interface implementation.
41 * @author Ricardo Gameiro <rgameiro at tintadigital dot com>
42 **/
43
44#include <cc++/config.h>
45#include "nat.h"
46
47#ifdef CCXX_NAT
48# ifdef HAVE_SYS_TYPES_H
49# include <sys/types.h>
50# endif
51# ifdef HAVE_SYS_SOCKET_H
52# include <sys/socket.h>
53# endif
54# ifdef HAVE_NAT_NETFILTER // Linux
55# ifdef HAVE_LIMITS_H
56# include <limits.h>
57# endif
58# ifdef HAVE_LINUX_NETFILTER_IPV4_H
59# include <linux/netfilter_ipv4.h>
60# endif
61# ifdef HAVE_LINUX_NETFILTER_IPV6_H
62# include <linux/netfilter_ipv6.h>
63# endif
64# else
65# ifdef HAVE_NET_IP6_H
66# include <netinet/ip6.h>
67# endif
68# ifdef HAVE_NETINET_IN_H
69# include <netinet/in.h>
70# endif
71# ifdef HAVE_NET_IF_H
72# include <net/if.h>
73# endif
74# ifdef HAVE_SYS_IOCTL_H
75# include <sys/ioctl.h>
76# endif
77# ifdef HAVE_IOCTL_H
78# include <ioctl.h>
79# endif
80# ifdef HAVE_UNISTD_H
81# include <unistd.h>
82# endif
83# ifdef HAVE_ERRNO_H
84# include <errno.h>
85# endif
86# ifdef HAVE_NAT_IPF // Solaris, *BSD (except OpenBSD), HP-UX
87# ifdef HAVE_NETINET_IP_COMPAT_H
88# include <netinet/ip_compat.h>
89# endif
90# ifdef HAVE_IP_COMPAT_H
91# include <ip_compat.h>
92# endif
93# ifdef HAVE_NETINET_IP_FIL_COMPAT_H
94# include <netinet/ip_fil_compat.h>
95# endif
96# ifdef HAVE_IP_FIL_COMPAT_H
97# include <ip_fil_compat.h>
98# endif
99# ifdef HAVE_NETINET_IP_FIL_H
100# include <netinet/ip_fil.h>
101# endif
102# ifdef HAVE_IP_FIL_H
103# include <ip_fil.h>
104# endif
105# ifdef HAVE_NETINET_IP_NAT_H
106# include <netinet/ip_nat.h>
107# endif
108# ifdef HAVE_IP_NAT_H
109# include <ip_nat.h>
110# endif
111# endif
112# ifdef HAVE_NAT_PF // OpenBSD
113# ifdef HAVE_NET_PFVAR_H
114# include <net/pfvar.h>
115# endif
116# endif
117# endif
118#endif
119
120#ifdef CCXX_NAMESPACES
121namespace ost {
122#endif
123
124#ifdef HAVE_NAT_NETFILTER
125# define NAT_SYSCALL "getsockopt"
126# define NAT_DEVICE ""
127#else
128# define NAT_SYSCALL "ioctl"
129# if defined(HAVE_NAT_IPF) && defined(IPL_NAT)
130# define NAT_DEVICE IPL_NAT
131# else
132# ifdef HAVE_NAT_PF
133# define NAT_DEVICE "/dev/pf"
134# endif
135# endif
136#endif
137
138#ifndef NAT_DEVICE
139#define NAT_DEVICE ""
140#endif
141
142#ifdef CCXX_NAT
143
144const char * natmsg[] = {
145 "nat lookup successful",
146 "nat address not in table",
147 "nat not supported/implemented",
148 "unable to open device "NAT_DEVICE,
149 "unable to get socket name",
150 "unable to get peer name",
151 "unable to get socket type",
152 "unable to lookup, nat "NAT_SYSCALL" failed",
153 "unkown nat error code"
154};
155
156#ifdef HAVE_NAT_NETFILTER // Linux
157natResult natv4Lookup( SOCKET sfd, struct sockaddr_in * nat )
158{
159 struct sockaddr_in local;
160 socklen_t nlen = sizeof( *nat ), llen = sizeof( local );
161
162 if( getsockname( sfd, ( struct sockaddr * ) &local, &llen ) )
163 return natSocknameErr;
164
165 memset( &nat->sin_addr.s_addr, 0, sizeof( nat->sin_addr.s_addr ) );
166 if( getsockopt( sfd, SOL_IP, SO_ORIGINAL_DST, ( struct sockaddr * ) nat, &nlen) )
167 return natIFaceErr;
168
169 if( local.sin_addr.s_addr == nat->sin_addr.s_addr )
170 return natSearchErr;
171
172 nat->sin_family = local.sin_family;
173
174 return natOK;
175}
176
177#ifdef CCXX_IPV6
178natResult natv6Lookup( SOCKET sfd, struct sockaddr_in6 * nat )
179{
180 struct sockaddr_in6 local;
181 socklen_t llen = sizeof( local ), nlen = sizeof( *nat );
182
183 if( getsockname( sfd, ( struct sockaddr * ) &local, &llen ) )
184 return natSocknameErr;
185
186 memset( &nat->sin6_addr.s6_addr, 0, sizeof( nat->sin6_addr.s6_addr ) );
187 if( getsockopt( sfd, SOL_IP, SO_ORIGINAL_DST, ( struct sockaddr * ) nat, &nlen) )
188 return natIFaceErr;
189
190 if( local.sin6_addr.s6_addr == nat->sin6_addr.s6_addr )
191 return natSearchErr;
192
193 nat->sin6_family = local.sin6_family;
194
195 return natOK;
196}
197#endif
198#endif
199
200#ifdef HAVE_NAT_IPF // Solaris, *BSD (except OpenBSD), HP-UX, etc.
201natResult natv4Lookup( int sfd, struct sockaddr_in * nat )
202{
203 static int natfd = -1;
204 struct sockaddr_in local, peer;
205 socklen_t nlen = sizeof( *nat ), llen = sizeof( local ), plen = sizeof( peer );
206 int socktype;
207 socklen_t stlen = sizeof( socktype );
208 struct natlookup nlu;
209 int nres;
210
211 if( natfd < 0 )
212 if( ( natfd = open( NAT_DEVICE, O_RDONLY, 0 ) ) < 0 )
213 return natDevUnavail;
214
215 if( getsockname( sfd, ( struct sockaddr * ) &local, &llen ) )
216 return natSocknameErr;
217 if( getpeername( sfd, ( struct sockaddr * ) &peer, &plen ) )
218 return natPeernameErr;
219 if( getsockopt( sfd, SOL_SOCKET, SO_TYPE, ( int * ) &socktype, &stlen ) )
220 return natSockTypeErr;
221
222 memset( &nlu.nl_realip.s_addr, 0, sizeof( nlu.nl_realip.s_addr ) );
223 nlu.nl_inip = local.sin_addr;
224 nlu.nl_inport = local.sin_port;
225 nlu.nl_outip = peer.sin_addr;
226 nlu.nl_outport = peer.sin_port;
227 nlu.nl_flags = ( socktype == SOCK_STREAM ) ? IPN_TCP : IPN_UDP;
228
229 if( 63 == ( SIOCGNATL & 0xff ) ) {
230 struct natlookup * nlup = &nlu;
231 nres = ioctl( natfd, SIOCGNATL, &nlup );
232 } else
233 nres = ioctl( natfd, SIOCGNATL, &nlu );
234
235 if( nres ) {
236 if( errno != ESRCH ) {
237 close( natfd );
238 natfd = -1;
239 return natIFaceErr;
240 } else
241 return natSearchErr;
242 }
243
244 if( local.sin_addr.s_addr == nlu.nl_realip.s_addr )
245 return natSearchErr;
246
247 nat->sin_family = local.sin_family;
248 nat->sin_port = nlu.nl_realport;
249 nat->sin_addr = nlu.nl_realip;
250
251 return natOK;
252}
253
254#ifdef CCXX_IPV6 // IPV6 is not yet supported by IPFilter
255natResult natv6Lookup( SOCKET sfd, struct sockaddr_in6 * nat ) {
256 return natNotSupported;
257}
258#endif
259#endif
260
261#ifdef HAVE_NAT_PF // OpenBSD
262natResult natv4Lookup( SOCKET sfd, struct sockaddr_in * nat ) {
263
264 static int natfd = -1;
265 struct sockaddr_in local, peer;
266 socklen_t nlen = sizeof( *nat ), llen = sizeof( local ), plen = sizeof( peer );
267 int socktype;
268 socklen_t stlen = sizeof( socktype );
269 struct pfioc_natlook nlu;
270 int nres;
271
272 if( natfd < 0 )
273 if( ( natfd = open( NAT_DEVICE, O_RDWR ) ) < 0 )
274 return natDevUnavail;
275
276 if( getsockname( sfd, ( struct sockaddr * ) &local, &llen ) )
277 return natSocknameErr;
278 if( getpeername( sfd, ( struct sockaddr * ) &peer, &plen ) )
279 return natPeernameErr;
280 if( getsockopt( sfd, SOL_SOCKET, SO_TYPE, ( int * ) &socktype, &stlen ) )
281 return natSockTypeErr;
282
283 memset( &nlu, 0, sizeof( nlu ) );
284 nlu.daddr.v4.s_addr = local.sin_addr.s_addr;
285 nlu.dport = local.sin_port;
286 nlu.saddr.v4.s_addr = peer.sin_addr.s_addr;
287 nlu.sport = peer.sin_port;
288 nlu.af = AF_INET;
289 nlu.proto = ( socktype == SOCK_STREAM ) ? IPPROTO_TCP : IPROTO_UDP;
290 nlu.direction = PF_OUT;
291
292 if( ioctl( natfd, DIOCNATLOOK, &nlu ) ) {
293 if( errno != ESRCH ) {
294 close( natfd );
295 natfd = -1;
296 return natIFaceErr;
297 } else
298 return natSearchErr;
299 }
300
301 if( local.sin_addr.s_addr == nlu.raddr.v4.s_addr )
302 return natSearchErr;
303
304 nat->sin_family = local.sin_family;
305 nat->sin_port = nlu.rdport;
306 nat->sin_addr = nlu.rdaddr.v4;
307
308 return natOK;
309}
310
311#ifdef CCXX_IPV6
312natResult natv6Lookup( SOCKET sfd, struct sockaddr_in6 * nat )
313{
314 static int natfd = -1;
315 struct sockaddr_in6 local, peer;
316 socklen_t nlen = sizeof( *nat ), llen = sizeof( local ), plen = sizeof( peer );
317 int socktype;
318 socklen_t stlen = sizeof( socktype );
319 struct pfioc_natlook nlu;
320 int nres;
321
322 if( natfd < 0 )
323 if( ( natfd = open( NAT_DEVICE, O_RDWR ) ) < 0 )
324 return natDevUnavail;
325
326 if( getsockname( sfd, ( struct sockaddr * ) &local, &llen ) )
327 return natSocknameErr;
328 if( getpeername( sfd, ( struct sockaddr * ) &peer, &plen ) )
329 return natPeernameErr;
330 if( getsockopt( sfd, SOL_SOCKET, SO_TYPE, ( int * ) &socktype, &stlen ) )
331 return natSockTypeErr;
332
333 memset( &nlu, 0, sizeof( nlu ) );
334 nlu.daddr.v6.s6_addr = local.sin6_addr.s6_addr;
335 nlu.dport = local.sin6_port;
336 nlu.saddr.v6.s6_addr = peer.sin6_addr.s6_addr;
337 nlu.sport = peer.sin6_port;
338 nlu.af = AF_INET6;
339 nlu.proto = ( socktype == SOCK_STREAM ) ? IPPROTO_TCP : IPROTO_UDP;
340 nlu.direction = PF_OUT;
341
342 if( ioctl( natfd, DIOCNATLOOK, &nlu ) ) {
343 if( errno != ESRCH ) {
344 close( natfd );
345 natfd = -1;
346 return natIFaceErr;
347 } else
348 return natSearchErr;
349 }
350
351 if( local.sin6_addr.s6_addr == nlu.raddr.v6.s6_addr )
352 return natSearchErr;
353
354 nat->sin6_family = local.sin6_family;
355 nat->sin6_flowinfo = local.sin6_flowinfo;
356 nat->sin6_port = nlu.rdport;
357 nat->sin6_addr = nlu.rdaddr.v6;
358
359 return natOK;
360}
361#endif
362#endif
363
364const char * natErrorString( natResult res )
365{
366 return (char *)natmsg[ ( res >= natOK && res <= natIFaceErr ) ? res : natUnkownErr ];
367}
368
369#else
370natResult natv4Lookup( SOCKET sfd, struct sockaddr_in * nat )
371{
372 return natNotSupported;
373}
374
375#ifdef CCXX_IPV6
376natResult natv6Lookup( SOCKET sfd, struct sockaddr_in6 * nat )
377{
378 return natNotSupported;
379}
380#endif
381
382const char * natErrorString( natResult res )
383{
384 return "nat support not included";
385}
386
387#endif
388
389#ifdef CCXX_NAMESPACES
390}
391#endif
392