1 /* $Id: ipfrdr.c,v 1.17 2016/01/13 15:51:07 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
;
134 unsigned int timestamp
;
138 /* pointer to the chained list where descriptions are stored */
139 static struct rdr_desc
* rdr_desc_list
;
142 add_redirect_desc(unsigned short eport
, int proto
,
143 unsigned int timestamp
, const char * desc
)
149 l
= strlen(desc
) + 1;
150 p
= malloc(sizeof(struct rdr_desc
) + l
);
152 p
->next
= rdr_desc_list
;
155 p
->timestamp
= timestamp
;
156 memcpy(p
->str
, desc
, l
);
163 del_redirect_desc(unsigned short eport
, int proto
)
165 struct rdr_desc
* p
, * last
;
168 for (p
= rdr_desc_list
; p
; p
= p
->next
) {
169 if(p
->eport
== eport
&& p
->proto
== proto
) {
171 rdr_desc_list
= p
->next
;
173 last
->next
= p
->next
;
181 get_redirect_desc(unsigned short eport
, int proto
, char * desc
, int desclen
, unsigned int * timestamp
)
185 if (desc
== NULL
|| desclen
== 0)
187 for (p
= rdr_desc_list
; p
; p
= p
->next
) {
188 if (p
->eport
== eport
&& p
->proto
== proto
)
190 strncpy(desc
, p
->str
, desclen
);
191 *timestamp
= p
->timestamp
;
198 int init_redirect(void)
201 dev
= open(IPNAT_NAME
, O_RDWR
);
203 syslog(LOG_ERR
, "open(\"%s\"): %m", IPNAT_NAME
);
206 dev_ipl
= open(IPL_NAME
, O_RDWR
);
208 syslog(LOG_ERR
, "open(\"%s\"): %m", IPL_NAME
);
214 void shutdown_redirect(void)
229 add_redirect_rule2(const char * ifname
, const char * rhost
,
230 unsigned short eport
, const char * iaddr
, unsigned short iport
,
231 int proto
, const char * desc
, unsigned int timestamp
)
238 syslog(LOG_ERR
, "%s not open", IPNAT_NAME
);
242 memset(&obj
, 0, sizeof(obj
));
243 memset(&ipnat
, 0, sizeof(ipnat
));
245 ipnat
.in_redir
= NAT_REDIRECT
;
246 #if IPFILTER_VERSION >= 5000000
247 ipnat
.in_pr
[0] = proto
;
248 ipnat
.in_pr
[1] = proto
;
252 if (proto
== IPPROTO_TCP
)
253 ipnat
.in_flags
= IPN_TCP
;
254 if (proto
== IPPROTO_UDP
)
255 ipnat
.in_flags
= IPN_UDP
;
256 ipnat
.in_dcmp
= FR_EQUAL
;
257 #if IPFILTER_VERSION >= 5000000
258 ipnat
.in_dpmin
= htons(eport
);
259 ipnat
.in_dpmax
= htons(eport
);
260 ipnat
.in_dpnext
= htons(iport
);
264 ipnat
.in_pmin
= htons(eport
);
265 ipnat
.in_pmax
= htons(eport
);
266 ipnat
.in_pnext
= htons(iport
);
269 strlcpy(ipnat
.in_tag
.ipt_tag
, group_name
, IPFTAG_LEN
);
271 #ifdef USE_IFNAME_IN_RULES
273 #if IPFILTER_VERSION >= 5000000
274 /* XXX check for stack overflow ! */
275 ipnat
.in_ifnames
[0] = 0;
276 ipnat
.in_ifnames
[1] = 0;
277 strlcpy(ipnat
.in_names
, ifname
, IFNAMSIZ
);
278 ipnat
.in_namelen
= strlen(ipnat
.in_names
) + 1;
280 strlcpy(ipnat
.in_ifnames
[0], ifname
, IFNAMSIZ
);
281 strlcpy(ipnat
.in_ifnames
[1], ifname
, IFNAMSIZ
);
286 if(rhost
&& rhost
[0] != '\0' && rhost
[0] != '*')
288 #if IPFILTER_VERSION >= 5000000
289 inet_pton(AF_INET
, rhost
, &ipnat
.in_nsrc
.na_addr
[0].in4
); /* in_nsrcip */
290 ipnat
.in_nsrc
.na_addr
[1].in4
.s_addr
= 0xffffffff; /* in_nsrcmsk */
292 inet_pton(AF_INET
, rhost
, &ipnat
.in_src
[0].in4
);
293 ipnat
.in_src
[1].in4
.s_addr
= 0xffffffff;
297 #if IPFILTER_VERSION >= 5000000
298 inet_pton(AF_INET
, iaddr
, &ipnat
.in_ndst
.na_addr
[0].in4
); /* in_ndstip */
299 ipnat
.in_ndst
.na_addr
[1].in4
.s_addr
= 0xffffffff; /* in_ndstmsk */
301 inet_pton(AF_INET
, iaddr
, &ipnat
.in_in
[0].in4
);
302 ipnat
.in_in
[1].in4
.s_addr
= 0xffffffff;
305 obj
.ipfo_rev
= IPFILTER_VERSION
;
306 obj
.ipfo_size
= sizeof(ipnat
);
307 obj
.ipfo_ptr
= &ipnat
;
308 obj
.ipfo_type
= IPFOBJ_IPNAT
;
310 r
= ioctl(dev
, SIOCADNAT
, &obj
);
312 syslog(LOG_ERR
, "ioctl(SIOCADNAT): %m");
314 add_redirect_desc(eport
, proto
, timestamp
, desc
);
318 /* get_redirect_rule()
319 * return value : 0 success (found)
320 * -1 = error or rule not found */
322 get_redirect_rule(const char * ifname
, unsigned short eport
, int proto
,
323 char * iaddr
, int iaddrlen
, unsigned short * iport
,
324 char * desc
, int desclen
,
325 char * rhost
, int rhostlen
,
326 unsigned int * timestamp
,
327 u_int64_t
* packets
, u_int64_t
* bytes
)
334 memset(&obj
, 0, sizeof(obj
));
335 obj
.ipfo_rev
= IPFILTER_VERSION
;
336 obj
.ipfo_type
= IPFOBJ_GENITER
;
337 obj
.ipfo_size
= sizeof(iter
);
338 obj
.ipfo_ptr
= &iter
;
340 iter
.igi_type
= IPFGENITER_IPNAT
;
341 #if IPFILTER_VERSION > 4011300
344 iter
.igi_data
= &ipn
;
347 syslog(LOG_ERR
, "%s not open", IPNAT_NAME
);
353 if (ioctl(dev
, SIOCGENITER
, &obj
) == -1) {
354 syslog(LOG_ERR
, "ioctl(dev, SIOCGENITER): %m");
357 #if IPFILTER_VERSION >= 5000000
358 if (eport
== ntohs(ipn
.in_dpmin
) &&
359 eport
== ntohs(ipn
.in_dpmax
) &&
360 strcmp(ipn
.in_tag
.ipt_tag
, group_name
) == 0 &&
361 ipn
.in_pr
[0] == proto
)
363 if (eport
== ntohs(ipn
.in_pmin
) &&
364 eport
== ntohs(ipn
.in_pmax
) &&
365 strcmp(ipn
.in_tag
.ipt_tag
, group_name
) == 0 &&
369 strlcpy(desc
, "", desclen
);
375 #if IPFILTER_VERSION >= 5000000
376 *iport
= ntohs(ipn
.in_dpnext
);
378 *iport
= ntohs(ipn
.in_pnext
);
380 if ((desc
!= NULL
) && (timestamp
!= NULL
))
381 get_redirect_desc(eport
, proto
, desc
, desclen
, timestamp
);
382 if ((rhost
!= NULL
) && (rhostlen
> 0))
383 #if IPFILTER_VERSION >= 5000000
384 inet_ntop(AF_INET
, &ipn
.in_nsrc
.na_addr
[0].in4
, rhost
, rhostlen
); /* in_nsrcip */
386 inet_ntop(AF_INET
, &ipn
.in_src
[0].in4
, rhost
, rhostlen
);
388 #if IPFILTER_VERSION >= 5000000
389 inet_ntop(AF_INET
, &ipn
.in_ndst
.na_addr
[0].in4
, iaddr
, iaddrlen
); /* in_ndstip */
391 inet_ntop(AF_INET
, &ipn
.in_in
[0].in4
, iaddr
, iaddrlen
);
395 } while (ipn
.in_next
!= NULL
);
401 get_redirect_rule_by_index(int index
,
402 char * ifname
, unsigned short * eport
,
403 char * iaddr
, int iaddrlen
, unsigned short * iport
,
404 int * proto
, char * desc
, int desclen
,
405 char * rhost
, int rhostlen
,
406 unsigned int * timestamp
,
407 u_int64_t
* packets
, u_int64_t
* bytes
)
418 syslog(LOG_ERR
, "%s not open", IPNAT_NAME
);
422 memset(&obj
, 0, sizeof(obj
));
423 obj
.ipfo_rev
= IPFILTER_VERSION
;
424 obj
.ipfo_ptr
= &iter
;
425 obj
.ipfo_size
= sizeof(iter
);
426 obj
.ipfo_type
= IPFOBJ_GENITER
;
428 iter
.igi_type
= IPFGENITER_IPNAT
;
429 #if IPFILTER_VERSION > 4011300
432 iter
.igi_data
= &ipn
;
437 if (ioctl(dev
, SIOCGENITER
, &obj
) == -1) {
438 syslog(LOG_ERR
, "%s:ioctl(SIOCGENITER): %m",
439 "get_redirect_rule_by_index");
443 if (strcmp(ipn
.in_tag
.ipt_tag
, group_name
) != 0)
447 #if IPFILTER_VERSION >= 5000000
448 *proto
= ipn
.in_pr
[0];
449 *eport
= ntohs(ipn
.in_dpmax
);
450 *iport
= ntohs(ipn
.in_dpnext
);
453 *eport
= ntohs(ipn
.in_pmax
);
454 *iport
= ntohs(ipn
.in_pnext
);
458 #if IPFILTER_VERSION >= 5000000
459 strlcpy(ifname
, ipn
.in_names
+ ipn
.in_ifnames
[0], IFNAMSIZ
);
461 strlcpy(ifname
, ipn
.in_ifnames
[0], IFNAMSIZ
);
467 if ((desc
!= NULL
) && (timestamp
!= NULL
))
468 get_redirect_desc(*eport
, *proto
, desc
, desclen
, timestamp
);
469 if ((rhost
!= NULL
) && (rhostlen
> 0))
470 #if IPFILTER_VERSION >= 5000000
471 inet_ntop(AF_INET
, &ipn
.in_nsrc
.na_addr
[0].in4
, rhost
, rhostlen
); /* in_nsrcip */
473 inet_ntop(AF_INET
, &ipn
.in_src
[0].in4
, rhost
, rhostlen
);
475 #if IPFILTER_VERSION >= 5000000
476 inet_ntop(AF_INET
, &ipn
.in_ndst
.na_addr
[0].in4
, iaddr
, iaddrlen
); /* in_ndstip */
478 inet_ntop(AF_INET
, &ipn
.in_in
[0].in4
, iaddr
, iaddrlen
);
482 } while (ipn
.in_next
!= NULL
);
487 real_delete_redirect_rule(const char * ifname
, unsigned short eport
, int proto
)
494 memset(&obj
, 0, sizeof(obj
));
495 obj
.ipfo_rev
= IPFILTER_VERSION
;
496 obj
.ipfo_type
= IPFOBJ_GENITER
;
497 obj
.ipfo_size
= sizeof(iter
);
498 obj
.ipfo_ptr
= &iter
;
500 iter
.igi_type
= IPFGENITER_IPNAT
;
501 #if IPFILTER_VERSION > 4011300
504 iter
.igi_data
= &ipn
;
507 syslog(LOG_ERR
, "%s not open", IPNAT_NAME
);
513 if (ioctl(dev
, SIOCGENITER
, &obj
) == -1) {
514 syslog(LOG_ERR
, "%s:ioctl(SIOCGENITER): %m",
515 "delete_redirect_rule");
518 #if IPFILTER_VERSION >= 5000000
519 if (eport
== ntohs(ipn
.in_dpmin
) &&
520 eport
== ntohs(ipn
.in_dpmax
) &&
521 strcmp(ipn
.in_tag
.ipt_tag
, group_name
) == 0 &&
522 ipn
.in_pr
[0] == proto
)
524 if (eport
== ntohs(ipn
.in_pmin
) &&
525 eport
== ntohs(ipn
.in_pmax
) &&
526 strcmp(ipn
.in_tag
.ipt_tag
, group_name
) == 0 &&
530 obj
.ipfo_rev
= IPFILTER_VERSION
;
531 obj
.ipfo_size
= sizeof(ipn
);
533 obj
.ipfo_type
= IPFOBJ_IPNAT
;
534 r
= ioctl(dev
, SIOCRMNAT
, &obj
);
536 syslog(LOG_ERR
, "%s:ioctl(SIOCRMNAT): %m",
537 "delete_redirect_rule");
538 /* Delete the desc even if the above failed */
539 del_redirect_desc(eport
, proto
);
542 } while (ipn
.in_next
!= NULL
);
546 /* FIXME: For some reason, the iter isn't reset every other delete,
547 * so we attempt 2 deletes. */
549 delete_redirect_rule(const char * ifname
, unsigned short eport
, int proto
)
553 r
= real_delete_redirect_rule(ifname
, eport
, proto
);
555 r
= real_delete_redirect_rule(ifname
, eport
, proto
);
559 /* thanks to Seth Mos for this function */
561 add_filter_rule2(const char * ifname
, const char * rhost
,
562 const char * iaddr
, unsigned short eport
, unsigned short iport
,
563 int proto
, const char * desc
)
571 syslog(LOG_ERR
, "%s not open", IPL_NAME
);
575 memset(&obj
, 0, sizeof(obj
));
576 memset(&fr
, 0, sizeof(fr
));
577 memset(&ipffr
, 0, sizeof(ipffr
));
579 fr
.fr_flags
= FR_PASS
|FR_KEEPSTATE
|FR_QUICK
|FR_INQUE
;
580 if (GETFLAG(LOGPACKETSMASK
))
581 fr
.fr_flags
|= FR_LOG
|FR_LOGFIRST
;
582 #if IPFILTER_VERSION >= 5000000
583 fr
.fr_family
= PF_INET
;
588 fr
.fr_type
= FR_T_IPF
;
589 fr
.fr_dun
.fru_ipf
= &ipffr
;
590 fr
.fr_dsize
= sizeof(ipffr
);
591 fr
.fr_isc
= (void *)-1;
595 fr
.fr_dcmp
= FR_EQUAL
;
597 #ifdef USE_IFNAME_IN_RULES
599 #if IPFILTER_VERSION >= 5000000
600 /* XXX check for stack overflow ! */
601 fr
.fr_ifnames
[0] = fr
.fr_namelen
;
602 strlcpy(fr
.fr_names
+ fr
.fr_ifnames
[0], ifname
, IFNAMSIZ
);
603 fr
.fr_namelen
+= strlen(ifname
) + 1;
605 strlcpy(fr
.fr_ifnames
[0], ifname
, IFNAMSIZ
);
609 #if IPFILTER_VERSION >= 5000000
610 /* XXX check for stack overflow ! */
611 fr
.fr_group
= fr
.fr_namelen
;
612 strlcpy(fr
.fr_names
+ fr
.fr_group
, group_name
, FR_GROUPLEN
);
613 fr
.fr_namelen
+= strlen(group_name
) + 1;
615 strlcpy(fr
.fr_group
, group_name
, sizeof(fr
.fr_group
));
618 if (proto
== IPPROTO_TCP
) {
620 fr
.fr_tcpfm
= TH_SYN
|TH_ACK
|TH_RST
|TH_FIN
|TH_URG
|TH_PUSH
;
623 if(rhost
&& rhost
[0] != '\0' && rhost
[0] != '*')
625 inet_pton(AF_INET
, rhost
, &fr
.fr_saddr
);
626 fr
.fr_smask
= 0xffffffff;
629 inet_pton(AF_INET
, iaddr
, &fr
.fr_daddr
);
630 fr
.fr_dmask
= 0xffffffff;
632 obj
.ipfo_rev
= IPFILTER_VERSION
;
634 obj
.ipfo_size
= sizeof(fr
);
636 r
= ioctl(dev_ipl
, SIOCINAFR
, &obj
);
640 "SIOCINAFR(missing 'head %s' rule?):%m",
643 syslog(LOG_ERR
, "SIOCINAFR:%m");
649 delete_filter_rule(const char * ifname
, unsigned short eport
, int proto
)
660 syslog(LOG_ERR
, "%s not open", IPL_NAME
);
664 wobj
.ipfo_rev
= IPFILTER_VERSION
;
665 wobj
.ipfo_type
= IPFOBJ_IPFSTAT
;
666 wobj
.ipfo_size
= sizeof(fio
);
667 wobj
.ipfo_ptr
= &fio
;
669 if (ioctl(dev_ipl
, SIOCGETFS
, &wobj
) == -1) {
670 syslog(LOG_ERR
, "ioctl(SIOCGETFS): %m");
674 wobj
.ipfo_rev
= IPFILTER_VERSION
;
675 wobj
.ipfo_ptr
= &rule
;
676 wobj
.ipfo_size
= sizeof(rule
);
677 wobj
.ipfo_type
= IPFOBJ_IPFITER
;
679 fp
= (frentry_t
*)array
;
680 fp
->fr_dun
.fru_data
= darray
;
681 fp
->fr_dsize
= sizeof(darray
);
684 rule
.iri_active
= fio
.f_active
;
685 #if IPFILTER_VERSION > 4011300
690 strlcpy(rule
.iri_group
, group_name
, sizeof(rule
.iri_group
));
692 dobj
.ipfo_rev
= IPFILTER_VERSION
;
693 dobj
.ipfo_size
= sizeof(*fp
);
694 dobj
.ipfo_type
= IPFOBJ_FRENTRY
;
698 memset(array
, 0xff, sizeof(array
));
700 if (ioctl(dev_ipl
, SIOCIPFITER
, &wobj
) == -1) {
701 syslog(LOG_ERR
, "ioctl(SIOCIPFITER): %m");
705 if (fp
->fr_data
!= NULL
)
706 fp
->fr_data
= (char *)fp
+ sizeof(*fp
);
707 if ((fp
->fr_type
& ~FR_T_BUILTIN
) == FR_T_IPF
&&
708 fp
->fr_dport
== eport
&&
709 fp
->fr_proto
== proto
)
713 r
= ioctl(dev_ipl
, SIOCRMAFR
, &dobj
);
715 syslog(LOG_ERR
, "ioctl(SIOCRMAFR): %m");
718 } while (fp
->fr_next
!= NULL
);
723 get_portmappings_in_range(unsigned short startport
, unsigned short endport
,
724 int proto
, unsigned int * number
)
726 unsigned short *array
, *array2
;
727 unsigned int capacity
;
728 unsigned short eport
;
735 syslog(LOG_ERR
, "%s not open", IPNAT_NAME
);
739 array
= calloc(capacity
, sizeof(unsigned short));
742 syslog(LOG_ERR
, "get_portmappings_in_range() : calloc error");
746 memset(&obj
, 0, sizeof(obj
));
747 obj
.ipfo_rev
= IPFILTER_VERSION
;
748 obj
.ipfo_ptr
= &iter
;
749 obj
.ipfo_size
= sizeof(iter
);
750 obj
.ipfo_type
= IPFOBJ_GENITER
;
752 iter
.igi_type
= IPFGENITER_IPNAT
;
753 #if IPFILTER_VERSION > 4011300
756 iter
.igi_data
= &ipn
;
759 if (ioctl(dev
, SIOCGENITER
, &obj
) == -1) {
760 syslog(LOG_ERR
, "%s:ioctl(SIOCGENITER): %m",
761 "get_portmappings_in_range");
765 if (strcmp(ipn
.in_tag
.ipt_tag
, group_name
) != 0)
768 #if IPFILTER_VERSION >= 5000000
769 eport
= ntohs(ipn
.in_dpmin
);
770 if( (eport
== ntohs(ipn
.in_dpmax
))
771 && (ipn
.in_pr
[0] == proto
)
772 && (startport
<= eport
) && (eport
<= endport
) )
774 eport
= ntohs(ipn
.in_pmin
);
775 if( (eport
== ntohs(ipn
.in_pmax
))
776 && (ipn
.in_p
== proto
)
777 && (startport
<= eport
) && (eport
<= endport
) )
780 if(*number
>= capacity
)
782 /* need to increase the capacity of the array */
784 array2
= realloc(array
, sizeof(unsigned short)*capacity
);
787 syslog(LOG_ERR
, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity
);
794 array
[*number
] = eport
;
797 } while (ipn
.in_next
!= NULL
);