1 /* $Id: ipfrdr.c,v 1.11 2009/10/10 18:34:39 nanard Exp $ */
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
12 * This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD.
13 * Needed here because on some systems <sys/uio.h> gets included by things
30 #include <sys/socket.h>
31 #include <sys/syslog.h>
32 #include <sys/ioctl.h>
34 #if __FreeBSD_version >= 300000
35 # include <net/if_var.h>
37 #include <netinet/in.h>
38 #include <netinet/in_systm.h>
39 #include <netinet/ip.h>
40 #include <netinet/ip_icmp.h>
41 #ifndef TCP_PAWS_IDLE /* IRIX */
42 # include <netinet/tcp.h>
44 #include <netinet/udp.h>
46 #include <arpa/inet.h>
56 #if !defined(__SVR4) && !defined(__svr4__) && defined(sun)
62 #include "../config.h"
63 #include "netinet/ipl.h"
64 #include "netinet/ip_compat.h"
65 #include "netinet/ip_fil.h"
66 #include "netinet/ip_nat.h"
67 #include "netinet/ip_state.h"
84 # if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \
86 typedef u_int32_t u_32_t
;
88 # if defined(__alpha__) || defined(__alpha) || defined(_LP64)
89 typedef unsigned int u_32_t
;
92 typedef uint32_t u_32_t
;
94 typedef unsigned int u_32_t
;
97 # endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ || __sgi */
101 #if defined(__NetBSD__) || defined(__OpenBSD__) || \
102 (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000) || \
103 SOLARIS || defined(__sgi) || defined(__osf__) || defined(linux)
105 typedef int (* ioctlfunc_t
) __P((int, ioctlcmd_t
, ...));
107 typedef int (* ioctlfunc_t
) __P((dev_t
, ioctlcmd_t
, void *));
109 typedef void (* addfunc_t
) __P((int, ioctlfunc_t
, void *));
110 typedef int (* copyfunc_t
) __P((void *, void *, size_t));
116 #if defined(sun) && !defined(__SVR4) && !defined(__svr4__)
117 extern int ioctl
__P((int, int, void *));
120 #include "../upnpglobalvars.h"
123 static const char group_name
[] = "miniupnpd";
126 static int dev_ipl
= -1;
128 /* IPFilter cannot store redirection descriptions, so we use our
129 * own structure to store them */
131 struct rdr_desc
* next
;
132 unsigned short eport
;
137 /* pointer to the chained list where descriptions are stored */
138 static struct rdr_desc
* rdr_desc_list
;
141 add_redirect_desc(unsigned short eport
, int proto
, const char * desc
)
147 l
= strlen(desc
) + 1;
148 p
= malloc(sizeof(struct rdr_desc
) + l
);
150 p
->next
= rdr_desc_list
;
153 memcpy(p
->str
, desc
, l
);
160 del_redirect_desc(unsigned short eport
, int proto
)
162 struct rdr_desc
* p
, * last
;
165 for (p
= rdr_desc_list
; p
; p
= p
->next
) {
166 if(p
->eport
== eport
&& p
->proto
== proto
) {
168 rdr_desc_list
= p
->next
;
170 last
->next
= p
->next
;
178 get_redirect_desc(unsigned short eport
, int proto
, char * desc
, int desclen
)
182 if (desc
== NULL
|| desclen
== 0)
184 for (p
= rdr_desc_list
; p
; p
= p
->next
) {
185 if (p
->eport
== eport
&& p
->proto
== proto
)
187 strncpy(desc
, p
->str
, desclen
);
193 int init_redirect(void)
196 dev
= open(IPNAT_NAME
, O_RDWR
);
198 syslog(LOG_ERR
, "open(\"%s\"): %m", IPNAT_NAME
);
201 dev_ipl
= open(IPL_NAME
, O_RDWR
);
203 syslog(LOG_ERR
, "open(\"%s\"): %m", IPL_NAME
);
209 void shutdown_redirect(void)
224 add_redirect_rule2(const char * ifname
, unsigned short eport
,
225 const char * iaddr
, unsigned short iport
, int proto
,
233 syslog(LOG_ERR
, "%s not open", IPNAT_NAME
);
237 memset(&obj
, 0, sizeof(obj
));
238 memset(&ipnat
, 0, sizeof(ipnat
));
240 ipnat
.in_redir
= NAT_REDIRECT
;
242 if (proto
== IPPROTO_TCP
)
243 ipnat
.in_flags
= IPN_TCP
;
244 if (proto
== IPPROTO_UDP
)
245 ipnat
.in_flags
= IPN_UDP
;
246 ipnat
.in_dcmp
= FR_EQUAL
;
247 ipnat
.in_pmin
= htons(eport
);
248 ipnat
.in_pmax
= htons(eport
);
249 ipnat
.in_pnext
= htons(iport
);
251 strlcpy(ipnat
.in_tag
.ipt_tag
, group_name
, IPFTAG_LEN
);
253 #ifdef USE_IFNAME_IN_RULES
255 strlcpy(ipnat
.in_ifnames
[0], ifname
, IFNAMSIZ
);
256 strlcpy(ipnat
.in_ifnames
[1], ifname
, IFNAMSIZ
);
260 inet_pton(AF_INET
, iaddr
, &ipnat
.in_in
[0].in4
);
261 ipnat
.in_in
[1].in4
.s_addr
= 0xffffffff;
263 obj
.ipfo_rev
= IPFILTER_VERSION
;
264 obj
.ipfo_size
= sizeof(ipnat
);
265 obj
.ipfo_ptr
= &ipnat
;
266 obj
.ipfo_type
= IPFOBJ_IPNAT
;
268 r
= ioctl(dev
, SIOCADNAT
, &obj
);
270 syslog(LOG_ERR
, "ioctl(SIOCADNAT): %m");
272 add_redirect_desc(eport
, proto
, desc
);
276 /* get_redirect_rule()
277 * return value : 0 success (found)
278 * -1 = error or rule not found */
280 get_redirect_rule(const char * ifname
, unsigned short eport
, int proto
,
281 char * iaddr
, int iaddrlen
, unsigned short * iport
,
282 char * desc
, int desclen
,
283 u_int64_t
* packets
, u_int64_t
* bytes
)
290 memset(&obj
, 0, sizeof(obj
));
291 obj
.ipfo_rev
= IPFILTER_VERSION
;
292 obj
.ipfo_type
= IPFOBJ_GENITER
;
293 obj
.ipfo_size
= sizeof(iter
);
294 obj
.ipfo_ptr
= &iter
;
296 iter
.igi_type
= IPFGENITER_IPNAT
;
297 #if IPFILTER_VERSION > 4011300
300 iter
.igi_data
= &ipn
;
303 syslog(LOG_ERR
, "%s not open", IPNAT_NAME
);
309 if (ioctl(dev
, SIOCGENITER
, &obj
) == -1) {
310 syslog(LOG_ERR
, "ioctl(dev, SIOCGENITER): %m");
313 if (eport
== ntohs(ipn
.in_pmin
) &&
314 eport
== ntohs(ipn
.in_pmax
) &&
315 strcmp(ipn
.in_tag
.ipt_tag
, group_name
) == 0 &&
318 strlcpy(desc
, "", desclen
);
324 *iport
= ntohs(ipn
.in_pnext
);
326 get_redirect_desc(eport
, proto
, desc
, desclen
);
327 inet_ntop(AF_INET
, &ipn
.in_in
[0].in4
, iaddr
, iaddrlen
);
330 } while (ipn
.in_next
!= NULL
);
336 get_redirect_rule_by_index(int index
,
337 char * ifname
, unsigned short * eport
,
338 char * iaddr
, int iaddrlen
, unsigned short * iport
,
339 int * proto
, char * desc
, int desclen
,
340 u_int64_t
* packets
, u_int64_t
* bytes
)
351 syslog(LOG_ERR
, "%s not open", IPNAT_NAME
);
355 memset(&obj
, 0, sizeof(obj
));
356 obj
.ipfo_rev
= IPFILTER_VERSION
;
357 obj
.ipfo_ptr
= &iter
;
358 obj
.ipfo_size
= sizeof(iter
);
359 obj
.ipfo_type
= IPFOBJ_GENITER
;
361 iter
.igi_type
= IPFGENITER_IPNAT
;
362 #if IPFILTER_VERSION > 4011300
365 iter
.igi_data
= &ipn
;
370 if (ioctl(dev
, SIOCGENITER
, &obj
) == -1) {
371 syslog(LOG_ERR
, "%s:ioctl(SIOCGENITER): %m",
372 "get_redirect_rule_by_index");
376 if (strcmp(ipn
.in_tag
.ipt_tag
, group_name
) != 0)
381 *eport
= ntohs(ipn
.in_pmax
);
382 *iport
= ntohs(ipn
.in_pnext
);
385 strlcpy(ifname
, ipn
.in_ifnames
[0], IFNAMSIZ
);
391 get_redirect_desc(*eport
, *proto
, desc
, desclen
);
392 inet_ntop(AF_INET
, &ipn
.in_in
[0].in4
, iaddr
, iaddrlen
);
395 } while (ipn
.in_next
!= NULL
);
400 real_delete_redirect_rule(const char * ifname
, unsigned short eport
, int proto
)
407 memset(&obj
, 0, sizeof(obj
));
408 obj
.ipfo_rev
= IPFILTER_VERSION
;
409 obj
.ipfo_type
= IPFOBJ_GENITER
;
410 obj
.ipfo_size
= sizeof(iter
);
411 obj
.ipfo_ptr
= &iter
;
413 iter
.igi_type
= IPFGENITER_IPNAT
;
414 #if IPFILTER_VERSION > 4011300
417 iter
.igi_data
= &ipn
;
420 syslog(LOG_ERR
, "%s not open", IPNAT_NAME
);
426 if (ioctl(dev
, SIOCGENITER
, &obj
) == -1) {
427 syslog(LOG_ERR
, "%s:ioctl(SIOCGENITER): %m",
428 "delete_redirect_rule");
431 if (eport
== ntohs(ipn
.in_pmin
) &&
432 eport
== ntohs(ipn
.in_pmax
) &&
433 strcmp(ipn
.in_tag
.ipt_tag
, group_name
) == 0 &&
436 obj
.ipfo_rev
= IPFILTER_VERSION
;
437 obj
.ipfo_size
= sizeof(ipn
);
439 obj
.ipfo_type
= IPFOBJ_IPNAT
;
440 r
= ioctl(dev
, SIOCRMNAT
, &obj
);
442 syslog(LOG_ERR
, "%s:ioctl(SIOCRMNAT): %m",
443 "delete_redirect_rule");
444 /* Delete the desc even if the above failed */
445 del_redirect_desc(eport
, proto
);
448 } while (ipn
.in_next
!= NULL
);
452 /* FIXME: For some reason, the iter isn't reset every other delete,
453 * so we attempt 2 deletes. */
455 delete_redirect_rule(const char * ifname
, unsigned short eport
, int proto
)
459 r
= real_delete_redirect_rule(ifname
, eport
, proto
);
461 r
= real_delete_redirect_rule(ifname
, eport
, proto
);
465 /* thanks to Seth Mos for this function */
467 add_filter_rule2(const char * ifname
, const char * iaddr
,
468 unsigned short eport
, unsigned short iport
,
469 int proto
, const char * desc
)
477 syslog(LOG_ERR
, "%s not open", IPL_NAME
);
481 memset(&obj
, 0, sizeof(obj
));
482 memset(&fr
, 0, sizeof(fr
));
483 memset(&ipffr
, 0, sizeof(ipffr
));
485 fr
.fr_flags
= FR_PASS
|FR_KEEPSTATE
|FR_QUICK
|FR_INQUE
;
486 if (GETFLAG(LOGPACKETSMASK
))
487 fr
.fr_flags
|= FR_LOG
|FR_LOGFIRST
;
490 fr
.fr_type
= FR_T_IPF
;
491 fr
.fr_dun
.fru_ipf
= &ipffr
;
492 fr
.fr_dsize
= sizeof(ipffr
);
493 fr
.fr_isc
= (void *)-1;
497 fr
.fr_dcmp
= FR_EQUAL
;
499 #ifdef USE_IFNAME_IN_RULES
501 strlcpy(fr
.fr_ifnames
[0], ifname
, IFNAMSIZ
);
503 strlcpy(fr
.fr_group
, group_name
, sizeof(fr
.fr_group
));
505 if (proto
== IPPROTO_TCP
) {
507 fr
.fr_tcpfm
= TH_SYN
|TH_ACK
|TH_RST
|TH_FIN
|TH_URG
|TH_PUSH
;
510 inet_pton(AF_INET
, iaddr
, &fr
.fr_daddr
);
511 fr
.fr_dmask
= 0xffffffff;
513 obj
.ipfo_rev
= IPFILTER_VERSION
;
515 obj
.ipfo_size
= sizeof(fr
);
517 r
= ioctl(dev_ipl
, SIOCINAFR
, &obj
);
521 "SIOCINAFR(missing 'head %s' rule?):%m",
524 syslog(LOG_ERR
, "SIOCINAFR:%m");
530 delete_filter_rule(const char * ifname
, unsigned short eport
, int proto
)
541 syslog(LOG_ERR
, "%s not open", IPL_NAME
);
545 wobj
.ipfo_rev
= IPFILTER_VERSION
;
546 wobj
.ipfo_type
= IPFOBJ_IPFSTAT
;
547 wobj
.ipfo_size
= sizeof(fio
);
548 wobj
.ipfo_ptr
= &fio
;
550 if (ioctl(dev_ipl
, SIOCGETFS
, &wobj
) == -1) {
551 syslog(LOG_ERR
, "ioctl(SIOCGETFS): %m");
555 wobj
.ipfo_rev
= IPFILTER_VERSION
;
556 wobj
.ipfo_ptr
= &rule
;
557 wobj
.ipfo_size
= sizeof(rule
);
558 wobj
.ipfo_type
= IPFOBJ_IPFITER
;
560 fp
= (frentry_t
*)array
;
561 fp
->fr_dun
.fru_data
= darray
;
562 fp
->fr_dsize
= sizeof(darray
);
565 rule
.iri_active
= fio
.f_active
;
566 #if IPFILTER_VERSION > 4011300
571 strlcpy(rule
.iri_group
, group_name
, sizeof(rule
.iri_group
));
573 dobj
.ipfo_rev
= IPFILTER_VERSION
;
574 dobj
.ipfo_size
= sizeof(*fp
);
575 dobj
.ipfo_type
= IPFOBJ_FRENTRY
;
579 memset(array
, 0xff, sizeof(array
));
581 if (ioctl(dev_ipl
, SIOCIPFITER
, &wobj
) == -1) {
582 syslog(LOG_ERR
, "ioctl(SIOCIPFITER): %m");
586 if (fp
->fr_data
!= NULL
)
587 fp
->fr_data
= (char *)fp
+ sizeof(*fp
);
588 if ((fp
->fr_type
& ~FR_T_BUILTIN
) == FR_T_IPF
&&
589 fp
->fr_dport
== eport
&&
590 fp
->fr_proto
== proto
)
594 r
= ioctl(dev_ipl
, SIOCRMAFR
, &dobj
);
596 syslog(LOG_ERR
, "ioctl(SIOCRMAFR): %m");
599 } while (fp
->fr_next
!= NULL
);