Add PPTP runtime and GUI
[tomato.git] / release / src / router / miniupnpd / ipf / ipfrdr.c
blob43a17493cc301932ae88a91b664dfeb9faebb617
1 /* $Id: ipfrdr.c,v 1.11 2009/10/10 18:34:39 nanard Exp $ */
2 /* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2007 Darren Reed
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
8 #include <sys/param.h>
9 #include <sys/types.h>
10 #include <sys/file.h>
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
14 * like <sys/socket.h>
16 #ifndef _KERNEL
17 # define ADD_KERNEL
18 # define _KERNEL
19 # define KERNEL
20 #endif
21 #ifdef __OpenBSD__
22 struct file;
23 #endif
24 #include <sys/uio.h>
25 #ifdef ADD_KERNEL
26 # undef _KERNEL
27 # undef KERNEL
28 #endif
29 #include <sys/time.h>
30 #include <sys/socket.h>
31 #include <sys/syslog.h>
32 #include <sys/ioctl.h>
33 #include <net/if.h>
34 #if __FreeBSD_version >= 300000
35 # include <net/if_var.h>
36 #endif
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>
43 #endif
44 #include <netinet/udp.h>
46 #include <arpa/inet.h>
48 #include <errno.h>
49 #include <limits.h>
50 #include <netdb.h>
51 #include <stdlib.h>
52 #include <fcntl.h>
53 #include <syslog.h>
54 #include <stddef.h>
55 #include <stdio.h>
56 #if !defined(__SVR4) && !defined(__svr4__) && defined(sun)
57 # include <strings.h>
58 #endif
59 #include <string.h>
60 #include <unistd.h>
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"
70 #ifndef __P
71 # ifdef __STDC__
72 # define __P(x) x
73 # else
74 # define __P(x) ()
75 # endif
76 #endif
77 #ifndef __STDC__
78 # undef const
79 # define const
80 #endif
82 #ifndef U_32_T
83 # define U_32_T 1
84 # if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \
85 defined(__sgi)
86 typedef u_int32_t u_32_t;
87 # else
88 # if defined(__alpha__) || defined(__alpha) || defined(_LP64)
89 typedef unsigned int u_32_t;
90 # else
91 # if SOLARIS2 >= 6
92 typedef uint32_t u_32_t;
93 # else
94 typedef unsigned int u_32_t;
95 # endif
96 # endif
97 # endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ || __sgi */
98 #endif /* U_32_T */
101 #if defined(__NetBSD__) || defined(__OpenBSD__) || \
102 (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000) || \
103 SOLARIS || defined(__sgi) || defined(__osf__) || defined(linux)
104 # include <stdarg.h>
105 typedef int (* ioctlfunc_t) __P((int, ioctlcmd_t, ...));
106 #else
107 typedef int (* ioctlfunc_t) __P((dev_t, ioctlcmd_t, void *));
108 #endif
109 typedef void (* addfunc_t) __P((int, ioctlfunc_t, void *));
110 typedef int (* copyfunc_t) __P((void *, void *, size_t));
114 * SunOS4
116 #if defined(sun) && !defined(__SVR4) && !defined(__svr4__)
117 extern int ioctl __P((int, int, void *));
118 #endif
120 #include "../upnpglobalvars.h"
122 /* group name */
123 static const char group_name[] = "miniupnpd";
125 static int dev = -1;
126 static int dev_ipl = -1;
128 /* IPFilter cannot store redirection descriptions, so we use our
129 * own structure to store them */
130 struct rdr_desc {
131 struct rdr_desc * next;
132 unsigned short eport;
133 int proto;
134 char str[];
137 /* pointer to the chained list where descriptions are stored */
138 static struct rdr_desc * rdr_desc_list;
140 static void
141 add_redirect_desc(unsigned short eport, int proto, const char * desc)
143 struct rdr_desc * p;
144 size_t l;
146 if (desc != NULL) {
147 l = strlen(desc) + 1;
148 p = malloc(sizeof(struct rdr_desc) + l);
149 if (p) {
150 p->next = rdr_desc_list;
151 p->eport = eport;
152 p->proto = proto;
153 memcpy(p->str, desc, l);
154 rdr_desc_list = p;
159 static void
160 del_redirect_desc(unsigned short eport, int proto)
162 struct rdr_desc * p, * last;
164 last = NULL;
165 for (p = rdr_desc_list; p; p = p->next) {
166 if(p->eport == eport && p->proto == proto) {
167 if (last == NULL)
168 rdr_desc_list = p->next;
169 else
170 last->next = p->next;
171 free(p);
172 return;
177 static void
178 get_redirect_desc(unsigned short eport, int proto, char * desc, int desclen)
180 struct rdr_desc * p;
182 if (desc == NULL || desclen == 0)
183 return;
184 for (p = rdr_desc_list; p; p = p->next) {
185 if (p->eport == eport && p->proto == proto)
187 strncpy(desc, p->str, desclen);
188 return;
193 int init_redirect(void)
196 dev = open(IPNAT_NAME, O_RDWR);
197 if (dev < 0) {
198 syslog(LOG_ERR, "open(\"%s\"): %m", IPNAT_NAME);
199 return -1;
201 dev_ipl = open(IPL_NAME, O_RDWR);
202 if (dev_ipl < 0) {
203 syslog(LOG_ERR, "open(\"%s\"): %m", IPL_NAME);
204 return -1;
206 return 0;
209 void shutdown_redirect(void)
212 if (dev >= 0) {
213 close(dev);
214 dev = -1;
216 if (dev_ipl >= 0) {
217 close(dev_ipl);
218 dev = -1;
220 return;
224 add_redirect_rule2(const char * ifname, unsigned short eport,
225 const char * iaddr, unsigned short iport, int proto,
226 const char * desc)
228 struct ipnat ipnat;
229 struct ipfobj obj;
230 int r;
232 if (dev < 0) {
233 syslog(LOG_ERR, "%s not open", IPNAT_NAME);
234 return -1;
237 memset(&obj, 0, sizeof(obj));
238 memset(&ipnat, 0, sizeof(ipnat));
240 ipnat.in_redir = NAT_REDIRECT;
241 ipnat.in_p = proto;
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);
250 ipnat.in_v = 4;
251 strlcpy(ipnat.in_tag.ipt_tag, group_name, IPFTAG_LEN);
253 #ifdef USE_IFNAME_IN_RULES
254 if (ifname) {
255 strlcpy(ipnat.in_ifnames[0], ifname, IFNAMSIZ);
256 strlcpy(ipnat.in_ifnames[1], ifname, IFNAMSIZ);
258 #endif
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);
269 if (r == -1)
270 syslog(LOG_ERR, "ioctl(SIOCADNAT): %m");
271 else
272 add_redirect_desc(eport, proto, desc);
273 return r;
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)
285 ipfgeniter_t iter;
286 ipfobj_t obj;
287 ipnat_t ipn;
288 int r;
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
298 iter.igi_nitems = 1;
299 #endif
300 iter.igi_data = &ipn;
302 if (dev < 0) {
303 syslog(LOG_ERR, "%s not open", IPNAT_NAME);
304 return -1;
307 r = -1;
308 do {
309 if (ioctl(dev, SIOCGENITER, &obj) == -1) {
310 syslog(LOG_ERR, "ioctl(dev, SIOCGENITER): %m");
311 break;
313 if (eport == ntohs(ipn.in_pmin) &&
314 eport == ntohs(ipn.in_pmax) &&
315 strcmp(ipn.in_tag.ipt_tag, group_name) == 0 &&
316 ipn.in_p == proto)
318 strlcpy(desc, "", desclen);
319 if (packets != NULL)
320 *packets = 0;
321 if (bytes != NULL)
322 *bytes = 0;
323 if (iport != NULL)
324 *iport = ntohs(ipn.in_pnext);
325 if (desc != NULL)
326 get_redirect_desc(eport, proto, desc, desclen);
327 inet_ntop(AF_INET, &ipn.in_in[0].in4, iaddr, iaddrlen);
328 r = 0;
330 } while (ipn.in_next != NULL);
331 return r;
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)
342 ipfgeniter_t iter;
343 ipfobj_t obj;
344 ipnat_t ipn;
345 int n, r;
347 if (index < 0)
348 return -1;
350 if (dev < 0) {
351 syslog(LOG_ERR, "%s not open", IPNAT_NAME);
352 return -1;
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
363 iter.igi_nitems = 1;
364 #endif
365 iter.igi_data = &ipn;
367 n = 0;
368 r = -1;
369 do {
370 if (ioctl(dev, SIOCGENITER, &obj) == -1) {
371 syslog(LOG_ERR, "%s:ioctl(SIOCGENITER): %m",
372 "get_redirect_rule_by_index");
373 break;
376 if (strcmp(ipn.in_tag.ipt_tag, group_name) != 0)
377 continue;
379 if (index == n++) {
380 *proto = ipn.in_p;
381 *eport = ntohs(ipn.in_pmax);
382 *iport = ntohs(ipn.in_pnext);
384 if (ifname)
385 strlcpy(ifname, ipn.in_ifnames[0], IFNAMSIZ);
386 if (packets != NULL)
387 *packets = 0;
388 if (bytes != NULL)
389 *bytes = 0;
390 if (desc != NULL)
391 get_redirect_desc(*eport, *proto, desc, desclen);
392 inet_ntop(AF_INET, &ipn.in_in[0].in4, iaddr, iaddrlen);
393 r = 0;
395 } while (ipn.in_next != NULL);
396 return r;
399 static int
400 real_delete_redirect_rule(const char * ifname, unsigned short eport, int proto)
402 ipfgeniter_t iter;
403 ipfobj_t obj;
404 ipnat_t ipn;
405 int r;
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
415 iter.igi_nitems = 1;
416 #endif
417 iter.igi_data = &ipn;
419 if (dev < 0) {
420 syslog(LOG_ERR, "%s not open", IPNAT_NAME);
421 return -1;
424 r = -1;
425 do {
426 if (ioctl(dev, SIOCGENITER, &obj) == -1) {
427 syslog(LOG_ERR, "%s:ioctl(SIOCGENITER): %m",
428 "delete_redirect_rule");
429 break;
431 if (eport == ntohs(ipn.in_pmin) &&
432 eport == ntohs(ipn.in_pmax) &&
433 strcmp(ipn.in_tag.ipt_tag, group_name) == 0 &&
434 ipn.in_p == proto)
436 obj.ipfo_rev = IPFILTER_VERSION;
437 obj.ipfo_size = sizeof(ipn);
438 obj.ipfo_ptr = &ipn;
439 obj.ipfo_type = IPFOBJ_IPNAT;
440 r = ioctl(dev, SIOCRMNAT, &obj);
441 if (r == -1)
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);
446 break;
448 } while (ipn.in_next != NULL);
449 return r;
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)
457 int r;
459 r = real_delete_redirect_rule(ifname, eport, proto);
460 if (r == -1)
461 r = real_delete_redirect_rule(ifname, eport, proto);
462 return r;
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)
471 ipfobj_t obj;
472 frentry_t fr;
473 fripf_t ipffr;
474 int r;
476 if (dev_ipl < 0) {
477 syslog(LOG_ERR, "%s not open", IPL_NAME);
478 return -1;
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;
488 fr.fr_v = 4;
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;
495 fr.fr_proto = proto;
496 fr.fr_mproto = 0xff;
497 fr.fr_dcmp = FR_EQUAL;
498 fr.fr_dport = eport;
499 #ifdef USE_IFNAME_IN_RULES
500 if (ifname)
501 strlcpy(fr.fr_ifnames[0], ifname, IFNAMSIZ);
502 #endif
503 strlcpy(fr.fr_group, group_name, sizeof(fr.fr_group));
505 if (proto == IPPROTO_TCP) {
506 fr.fr_tcpf = TH_SYN;
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;
514 obj.ipfo_ptr = &fr;
515 obj.ipfo_size = sizeof(fr);
517 r = ioctl(dev_ipl, SIOCINAFR, &obj);
518 if (r == -1) {
519 if (errno == ESRCH)
520 syslog(LOG_ERR,
521 "SIOCINAFR(missing 'head %s' rule?):%m",
522 group_name);
523 else
524 syslog(LOG_ERR, "SIOCINAFR:%m");
526 return r;
530 delete_filter_rule(const char * ifname, unsigned short eport, int proto)
532 ipfobj_t wobj, dobj;
533 ipfruleiter_t rule;
534 u_long darray[1000];
535 u_long array[1000];
536 friostat_t fio;
537 frentry_t *fp;
538 int r;
540 if (dev_ipl < 0) {
541 syslog(LOG_ERR, "%s not open", IPL_NAME);
542 return -1;
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");
552 return -1;
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);
564 rule.iri_inout = 0;
565 rule.iri_active = fio.f_active;
566 #if IPFILTER_VERSION > 4011300
567 rule.iri_nrules = 1;
568 rule.iri_v = 4;
569 #endif
570 rule.iri_rule = fp;
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;
577 r = -1;
578 do {
579 memset(array, 0xff, sizeof(array));
581 if (ioctl(dev_ipl, SIOCIPFITER, &wobj) == -1) {
582 syslog(LOG_ERR, "ioctl(SIOCIPFITER): %m");
583 break;
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)
592 dobj.ipfo_ptr = fp;
594 r = ioctl(dev_ipl, SIOCRMAFR, &dobj);
595 if (r == -1)
596 syslog(LOG_ERR, "ioctl(SIOCRMAFR): %m");
597 break;
599 } while (fp->fr_next != NULL);
600 return r;