Miniupnpd ver 1.7 (20121005)
[tomato.git] / release / src / router / miniupnpd / pf / obsdrdr.c
blobd0287cb4cd060456aacf8e0f20e1c2de3aae79a1
1 /* $Id: obsdrdr.c,v 1.74 2012/05/01 09:20:43 nanard Exp $ */
2 /* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2012 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
8 /*
9 * pf rules created (with ext_if = xl1)
10 * - OpenBSD up to version 4.6 :
11 * rdr pass on xl1 inet proto udp from any to any port = 54321 \
12 * label "test label" -> 192.168.0.141 port 12345
13 * or a rdr rule + a pass rule
15 * - OpenBSD starting from version 4.7
16 * match in on xl1 inet proto udp from any to any port 54321 \
17 * label "test label" rdr-to 192.168.0.141 port 12345
18 * or
19 * pass in quick on xl1 inet proto udp from any to any port 54321 \
20 * label "test label" rdr-to 192.168.0.141 port 12345
24 * Macros/#defines :
25 * - PF_ENABLE_FILTER_RULES
26 * If set, two rules are created : rdr + pass. Else a rdr/pass rule
27 * is created.
28 * - USE_IFNAME_IN_RULES
29 * If set the interface name is set in the rule.
30 * - PFRULE_INOUT_COUNTS
31 * Must be set with OpenBSD version 3.8 and up.
32 * - PFRULE_HAS_RTABLEID
33 * Must be set with OpenBSD version 4.0 and up.
34 * - PF_NEWSSTYLE
35 * Must be set with OpenBSD version 4.7 and up.
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/param.h>
41 #include <net/if.h>
42 #include <netinet/in.h>
43 #include <netinet/tcp.h>
44 #include <arpa/inet.h>
45 #ifdef __DragonFly__
46 #include <net/pf/pfvar.h>
47 #else
48 #include <net/pfvar.h>
49 #endif
50 #include <fcntl.h>
51 #include <sys/ioctl.h>
52 #include <unistd.h>
53 #include <string.h>
54 #include <syslog.h>
55 #include <stdio.h>
56 #include <stdlib.h>
58 #include "../macros.h"
59 #include "../config.h"
60 #include "obsdrdr.h"
61 #include "../upnpglobalvars.h"
63 /* list too keep timestamps for port mappings having a lease duration */
64 struct timestamp_entry {
65 struct timestamp_entry * next;
66 unsigned int timestamp;
67 unsigned short eport;
68 short protocol;
71 static struct timestamp_entry * timestamp_list = NULL;
73 static unsigned int
74 get_timestamp(unsigned short eport, int proto)
76 struct timestamp_entry * e;
77 e = timestamp_list;
78 while(e) {
79 if(e->eport == eport && e->protocol == (short)proto)
80 return e->timestamp;
81 e = e->next;
83 return 0;
86 static void
87 remove_timestamp_entry(unsigned short eport, int proto)
89 struct timestamp_entry * e;
90 struct timestamp_entry * * p;
91 p = &timestamp_list;
92 e = *p;
93 while(e) {
94 if(e->eport == eport && e->protocol == (short)proto) {
95 /* remove the entry */
96 *p = e->next;
97 free(e);
98 return;
100 p = &(e->next);
101 e = *p;
105 /* /dev/pf when opened */
106 int dev = -1;
108 /* shutdown_redirect() :
109 * close the /dev/pf device */
110 void
111 shutdown_redirect(void)
113 if(close(dev)<0)
114 syslog(LOG_ERR, "close(\"/dev/pf\"): %m");
115 dev = -1;
118 /* open the device */
120 init_redirect(void)
122 struct pf_status status;
123 if(dev>=0)
124 shutdown_redirect();
125 dev = open("/dev/pf", O_RDWR);
126 if(dev<0) {
127 syslog(LOG_ERR, "open(\"/dev/pf\"): %m");
128 return -1;
130 if(ioctl(dev, DIOCGETSTATUS, &status)<0) {
131 syslog(LOG_ERR, "DIOCGETSTATUS: %m");
132 return -1;
134 if(!status.running) {
135 syslog(LOG_ERR, "pf is disabled");
136 return -1;
138 return 0;
141 #if TEST
142 /* for debug */
144 clear_redirect_rules(void)
146 struct pfioc_trans io;
147 struct pfioc_trans_e ioe;
148 if(dev<0) {
149 syslog(LOG_ERR, "pf device is not open");
150 return -1;
152 memset(&ioe, 0, sizeof(ioe));
153 io.size = 1;
154 io.esize = sizeof(ioe);
155 io.array = &ioe;
156 #ifndef PF_NEWSTYLE
157 ioe.rs_num = PF_RULESET_RDR;
158 #else
159 ioe.type = PF_TRANS_RULESET;
160 #endif
161 strlcpy(ioe.anchor, anchor_name, MAXPATHLEN);
162 if(ioctl(dev, DIOCXBEGIN, &io) < 0)
164 syslog(LOG_ERR, "ioctl(dev, DIOCXBEGIN, ...): %m");
165 goto error;
167 if(ioctl(dev, DIOCXCOMMIT, &io) < 0)
169 syslog(LOG_ERR, "ioctl(dev, DIOCXCOMMIT, ...): %m");
170 goto error;
172 return 0;
173 error:
174 return -1;
176 #endif
178 /* add_redirect_rule2() :
179 * create a rdr rule */
181 add_redirect_rule2(const char * ifname,
182 const char * rhost, unsigned short eport,
183 const char * iaddr, unsigned short iport, int proto,
184 const char * desc, unsigned int timestamp)
186 int r;
187 struct pfioc_rule pcr;
188 #ifndef PF_NEWSTYLE
189 struct pfioc_pooladdr pp;
190 struct pf_pooladdr *a;
191 #endif
192 if(dev<0) {
193 syslog(LOG_ERR, "pf device is not open");
194 return -1;
196 r = 0;
197 memset(&pcr, 0, sizeof(pcr));
198 strlcpy(pcr.anchor, anchor_name, MAXPATHLEN);
200 #ifndef PF_NEWSTYLE
201 memset(&pp, 0, sizeof(pp));
202 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
203 if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0)
205 syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m");
206 r = -1;
208 else
210 pcr.pool_ticket = pp.ticket;
211 #else
212 if(1)
214 pcr.rule.direction = PF_IN;
215 /*pcr.rule.src.addr.type = PF_ADDR_NONE;*/
216 pcr.rule.src.addr.type = PF_ADDR_ADDRMASK;
217 pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
218 pcr.rule.nat.addr.type = PF_ADDR_NONE;
219 pcr.rule.rdr.addr.type = PF_ADDR_ADDRMASK;
220 #endif
222 pcr.rule.dst.port_op = PF_OP_EQ;
223 pcr.rule.dst.port[0] = htons(eport);
224 pcr.rule.dst.port[1] = htons(eport);
225 #ifndef PF_NEWSTYLE
226 pcr.rule.action = PF_RDR;
227 #ifndef PF_ENABLE_FILTER_RULES
228 pcr.rule.natpass = 1;
229 #else
230 pcr.rule.natpass = 0;
231 #endif
232 #else
233 #ifndef PF_ENABLE_FILTER_RULES
234 pcr.rule.action = PF_PASS;
235 #else
236 pcr.rule.action = PF_MATCH;
237 #endif
238 #endif
239 pcr.rule.af = AF_INET;
240 #ifdef USE_IFNAME_IN_RULES
241 if(ifname)
242 strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ);
243 #endif
244 pcr.rule.proto = proto;
245 pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/
246 #ifdef PFRULE_HAS_RTABLEID
247 pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */
248 #endif
249 #ifdef PFRULE_HAS_ONRDOMAIN
250 pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */
251 #endif
252 pcr.rule.quick = 1;
253 pcr.rule.keep_state = PF_STATE_NORMAL;
254 if(tag)
255 strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE);
256 strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE);
257 if(rhost && rhost[0] != '\0' && rhost[0] != '*')
259 inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr);
260 pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
262 #ifndef PF_NEWSTYLE
263 pcr.rule.rpool.proxy_port[0] = iport;
264 pcr.rule.rpool.proxy_port[1] = iport;
265 TAILQ_INIT(&pcr.rule.rpool.list);
266 a = calloc(1, sizeof(struct pf_pooladdr));
267 inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr);
268 a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
269 TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries);
271 memcpy(&pp.addr, a, sizeof(struct pf_pooladdr));
272 if(ioctl(dev, DIOCADDADDR, &pp) < 0)
274 syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m");
275 r = -1;
277 else
279 #else
280 pcr.rule.rdr.proxy_port[0] = iport;
281 pcr.rule.rdr.proxy_port[1] = iport;
282 inet_pton(AF_INET, iaddr, &pcr.rule.rdr.addr.v.a.addr.v4.s_addr);
283 pcr.rule.rdr.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
284 if(1)
286 #endif
287 pcr.action = PF_CHANGE_GET_TICKET;
288 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
290 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
291 r = -1;
293 else
295 pcr.action = PF_CHANGE_ADD_TAIL;
296 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
298 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m");
299 r = -1;
303 #ifndef PF_NEWSTYLE
304 free(a);
305 #endif
307 if(r == 0 && timestamp > 0)
309 struct timestamp_entry * tmp;
310 tmp = malloc(sizeof(struct timestamp_entry));
311 if(tmp)
313 tmp->next = timestamp_list;
314 tmp->timestamp = timestamp;
315 tmp->eport = eport;
316 tmp->protocol = (short)proto;
317 timestamp_list = tmp;
320 return r;
323 /* thanks to Seth Mos for this function */
325 add_filter_rule2(const char * ifname,
326 const char * rhost, const char * iaddr,
327 unsigned short eport, unsigned short iport,
328 int proto, const char * desc)
330 #ifndef PF_ENABLE_FILTER_RULES
331 UNUSED(ifname);
332 UNUSED(rhost); UNUSED(iaddr);
333 UNUSED(eport); UNUSED(iport);
334 UNUSED(proto); UNUSED(desc);
335 return 0;
336 #else
337 int r;
338 struct pfioc_rule pcr;
339 #ifndef PF_NEWSTYLE
340 struct pfioc_pooladdr pp;
341 struct pf_pooladdr *a;
342 #endif
343 if(dev<0) {
344 syslog(LOG_ERR, "pf device is not open");
345 return -1;
347 r = 0;
348 memset(&pcr, 0, sizeof(pcr));
349 strlcpy(pcr.anchor, anchor_name, MAXPATHLEN);
351 #ifndef PF_NEWSTYLE
352 memset(&pp, 0, sizeof(pp));
353 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
354 if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0)
356 syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m");
357 r = -1;
359 else
361 pcr.pool_ticket = pp.ticket;
362 #else
363 if(1)
365 #endif
367 pcr.rule.dst.port_op = PF_OP_EQ;
368 pcr.rule.dst.port[0] = htons(eport);
369 pcr.rule.direction = PF_IN;
370 pcr.rule.action = PF_PASS;
371 pcr.rule.af = AF_INET;
372 #ifdef USE_IFNAME_IN_RULES
373 if(ifname)
374 strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ);
375 #endif
376 pcr.rule.proto = proto;
377 pcr.rule.quick = (GETFLAG(PFNOQUICKRULESMASK))?0:1;
378 pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/
379 /* see the discussion on the forum :
380 * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */
381 pcr.rule.flags = TH_SYN;
382 pcr.rule.flagset = (TH_SYN|TH_ACK);
383 #ifdef PFRULE_HAS_RTABLEID
384 pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */
385 #endif
386 #ifdef PFRULE_HAS_ONRDOMAIN
387 pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */
388 #endif
389 pcr.rule.keep_state = 1;
390 strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE);
391 if(queue)
392 strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE);
393 if(tag)
394 strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE);
396 if(rhost && rhost[0] != '\0' && rhost[0] != '*')
398 inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr);
399 pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
401 #ifndef PF_NEWSTYLE
402 pcr.rule.rpool.proxy_port[0] = eport;
403 a = calloc(1, sizeof(struct pf_pooladdr));
404 inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr);
405 a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
406 memcpy(&pp.addr, a, sizeof(struct pf_pooladdr));
407 TAILQ_INIT(&pcr.rule.rpool.list);
408 inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr);
409 TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries);
411 /* we have any - any port = # keep state label */
412 /* we want any - iaddr port = # keep state label */
413 /* memcpy(&pcr.rule.dst, a, sizeof(struct pf_pooladdr)); */
415 memcpy(&pp.addr, a, sizeof(struct pf_pooladdr));
416 strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE);
417 if(ioctl(dev, DIOCADDADDR, &pp) < 0)
419 syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m");
420 r = -1;
422 else
424 #else
425 if(1)
427 #endif
428 pcr.action = PF_CHANGE_GET_TICKET;
429 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
431 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
432 r = -1;
434 else
436 pcr.action = PF_CHANGE_ADD_TAIL;
437 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
439 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m");
440 r = -1;
444 #ifndef PF_NEWSTYLE
445 free(a);
446 #endif
448 return r;
449 #endif
452 /* get_redirect_rule()
453 * return value : 0 success (found)
454 * -1 = error or rule not found */
456 get_redirect_rule(const char * ifname, unsigned short eport, int proto,
457 char * iaddr, int iaddrlen, unsigned short * iport,
458 char * desc, int desclen,
459 char * rhost, int rhostlen,
460 unsigned int * timestamp,
461 u_int64_t * packets, u_int64_t * bytes)
463 int i, n;
464 struct pfioc_rule pr;
465 #ifndef PF_NEWSTYLE
466 struct pfioc_pooladdr pp;
467 #endif
468 UNUSED(ifname);
470 if(dev<0) {
471 syslog(LOG_ERR, "pf device is not open");
472 return -1;
474 memset(&pr, 0, sizeof(pr));
475 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
476 #ifndef PF_NEWSTYLE
477 pr.rule.action = PF_RDR;
478 #endif
479 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
481 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
482 goto error;
484 n = pr.nr;
485 for(i=0; i<n; i++)
487 pr.nr = i;
488 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
490 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
491 goto error;
493 if( (eport == ntohs(pr.rule.dst.port[0]))
494 && (eport == ntohs(pr.rule.dst.port[1]))
495 && (pr.rule.proto == proto) )
497 #ifndef PF_NEWSTYLE
498 *iport = pr.rule.rpool.proxy_port[0];
499 #else
500 *iport = pr.rule.rdr.proxy_port[0];
501 #endif
502 if(desc)
503 strlcpy(desc, pr.rule.label, desclen);
504 #ifdef PFRULE_INOUT_COUNTS
505 if(packets)
506 *packets = pr.rule.packets[0] + pr.rule.packets[1];
507 if(bytes)
508 *bytes = pr.rule.bytes[0] + pr.rule.bytes[1];
509 #else
510 if(packets)
511 *packets = pr.rule.packets;
512 if(bytes)
513 *bytes = pr.rule.bytes;
514 #endif
515 #ifndef PF_NEWSTYLE
516 memset(&pp, 0, sizeof(pp));
517 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
518 pp.r_action = PF_RDR;
519 pp.r_num = i;
520 pp.ticket = pr.ticket;
521 if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
523 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m");
524 goto error;
526 if(pp.nr != 1)
528 syslog(LOG_NOTICE, "No address associated with pf rule");
529 goto error;
531 pp.nr = 0; /* first */
532 if(ioctl(dev, DIOCGETADDR, &pp) < 0)
534 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m");
535 goto error;
537 inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr,
538 iaddr, iaddrlen);
539 #else
540 inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr,
541 iaddr, iaddrlen);
542 #endif
543 if(rhost && rhostlen > 0)
545 if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0)
547 rhost[0] = '\0'; /* empty string */
549 else
551 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr,
552 rhost, rhostlen);
555 if(timestamp)
556 *timestamp = get_timestamp(eport, proto);
557 return 0;
560 error:
561 return -1;
565 delete_redirect_rule(const char * ifname, unsigned short eport, int proto)
567 int i, n;
568 struct pfioc_rule pr;
569 UNUSED(ifname);
571 if(dev<0) {
572 syslog(LOG_ERR, "pf device is not open");
573 return -1;
575 memset(&pr, 0, sizeof(pr));
576 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
577 #ifndef PF_NEWSTYLE
578 pr.rule.action = PF_RDR;
579 #endif
580 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
582 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
583 goto error;
585 n = pr.nr;
586 for(i=0; i<n; i++)
588 pr.nr = i;
589 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
591 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
592 goto error;
594 if( (eport == ntohs(pr.rule.dst.port[0]))
595 && (eport == ntohs(pr.rule.dst.port[1]))
596 && (pr.rule.proto == proto) )
598 pr.action = PF_CHANGE_GET_TICKET;
599 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
601 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
602 goto error;
604 pr.action = PF_CHANGE_REMOVE;
605 pr.nr = i;
606 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
608 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
609 goto error;
611 remove_timestamp_entry(eport, proto);
612 return 0;
615 error:
616 return -1;
620 delete_filter_rule(const char * ifname, unsigned short eport, int proto)
622 #ifndef PF_ENABLE_FILTER_RULES
623 UNUSED(ifname); UNUSED(eport); UNUSED(proto);
624 return 0;
625 #else
626 int i, n;
627 struct pfioc_rule pr;
628 if(dev<0) {
629 syslog(LOG_ERR, "pf device is not open");
630 return -1;
632 memset(&pr, 0, sizeof(pr));
633 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
634 pr.rule.action = PF_PASS;
635 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
637 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
638 goto error;
640 n = pr.nr;
641 for(i=0; i<n; i++)
643 pr.nr = i;
644 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
646 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
647 goto error;
649 if( (eport == ntohs(pr.rule.dst.port[0]))
650 && (pr.rule.proto == proto) )
652 pr.action = PF_CHANGE_GET_TICKET;
653 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
655 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
656 goto error;
658 pr.action = PF_CHANGE_REMOVE;
659 pr.nr = i;
660 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
662 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
663 goto error;
665 return 0;
668 error:
669 return -1;
670 #endif
674 get_redirect_rule_by_index(int index,
675 char * ifname, unsigned short * eport,
676 char * iaddr, int iaddrlen, unsigned short * iport,
677 int * proto, char * desc, int desclen,
678 char * rhost, int rhostlen,
679 unsigned int * timestamp,
680 u_int64_t * packets, u_int64_t * bytes)
682 int n;
683 struct pfioc_rule pr;
684 #ifndef PF_NEWSTYLE
685 struct pfioc_pooladdr pp;
686 #endif
687 if(index < 0)
688 return -1;
689 if(dev<0) {
690 syslog(LOG_ERR, "pf device is not open");
691 return -1;
693 memset(&pr, 0, sizeof(pr));
694 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
695 #ifndef PF_NEWSTYLE
696 pr.rule.action = PF_RDR;
697 #endif
698 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
700 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
701 goto error;
703 n = pr.nr;
704 if(index >= n)
705 goto error;
706 pr.nr = index;
707 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
709 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
710 goto error;
712 *proto = pr.rule.proto;
713 *eport = ntohs(pr.rule.dst.port[0]);
714 #ifndef PF_NEWSTYLE
715 *iport = pr.rule.rpool.proxy_port[0];
716 #else
717 *iport = pr.rule.rdr.proxy_port[0];
718 #endif
719 if(ifname)
720 strlcpy(ifname, pr.rule.ifname, IFNAMSIZ);
721 if(desc)
722 strlcpy(desc, pr.rule.label, desclen);
723 #ifdef PFRULE_INOUT_COUNTS
724 if(packets)
725 *packets = pr.rule.packets[0] + pr.rule.packets[1];
726 if(bytes)
727 *bytes = pr.rule.bytes[0] + pr.rule.bytes[1];
728 #else
729 if(packets)
730 *packets = pr.rule.packets;
731 if(bytes)
732 *bytes = pr.rule.bytes;
733 #endif
734 #ifndef PF_NEWSTYLE
735 memset(&pp, 0, sizeof(pp));
736 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
737 pp.r_action = PF_RDR;
738 pp.r_num = index;
739 pp.ticket = pr.ticket;
740 if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
742 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m");
743 goto error;
745 if(pp.nr != 1)
747 syslog(LOG_NOTICE, "No address associated with pf rule");
748 goto error;
750 pp.nr = 0; /* first */
751 if(ioctl(dev, DIOCGETADDR, &pp) < 0)
753 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m");
754 goto error;
756 inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr,
757 iaddr, iaddrlen);
758 #else
759 inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr,
760 iaddr, iaddrlen);
761 #endif
762 if(rhost && rhostlen > 0)
764 if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0)
766 rhost[0] = '\0'; /* empty string */
768 else
770 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr,
771 rhost, rhostlen);
774 if(timestamp)
775 *timestamp = get_timestamp(*eport, *proto);
776 return 0;
777 error:
778 return -1;
781 /* return an (malloc'ed) array of "external" port for which there is
782 * a port mapping. number is the size of the array */
783 unsigned short *
784 get_portmappings_in_range(unsigned short startport, unsigned short endport,
785 int proto, unsigned int * number)
787 unsigned short * array;
788 unsigned int capacity;
789 int i, n;
790 unsigned short eport;
791 struct pfioc_rule pr;
793 *number = 0;
794 if(dev<0) {
795 syslog(LOG_ERR, "pf device is not open");
796 return NULL;
798 capacity = 128;
799 array = calloc(capacity, sizeof(unsigned short));
800 if(!array)
802 syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
803 return NULL;
805 memset(&pr, 0, sizeof(pr));
806 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
807 #ifndef PF_NEWSTYLE
808 pr.rule.action = PF_RDR;
809 #endif
810 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
812 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
813 free(array);
814 return NULL;
816 n = pr.nr;
817 for(i=0; i<n; i++)
819 pr.nr = i;
820 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
822 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
823 continue;
825 eport = ntohs(pr.rule.dst.port[0]);
826 if( (eport == ntohs(pr.rule.dst.port[1]))
827 && (pr.rule.proto == proto)
828 && (startport <= eport) && (eport <= endport) )
830 if(*number >= capacity)
832 /* need to increase the capacity of the array */
833 capacity += 128;
834 array = realloc(array, sizeof(unsigned short)*capacity);
835 if(!array)
837 syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity);
838 *number = 0;
839 return NULL;
842 array[*number] = eport;
843 (*number)++;
846 return array;
849 /* this function is only for testing */
850 #if TEST
851 void
852 list_rules(void)
854 char buf[32];
855 int i, n;
856 struct pfioc_rule pr;
857 #ifndef PF_NEWSTYLE
858 struct pfioc_pooladdr pp;
859 #endif
861 if(dev<0)
863 perror("pf dev not open");
864 return ;
866 memset(&pr, 0, sizeof(pr));
867 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
868 pr.rule.action = PF_RDR;
869 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
870 perror("DIOCGETRULES");
871 printf("ticket = %d, nr = %d\n", pr.ticket, pr.nr);
872 n = pr.nr;
873 for(i=0; i<n; i++)
875 printf("-- rule %d --\n", i);
876 pr.nr = i;
877 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
878 perror("DIOCGETRULE");
879 printf(" %s %s %d:%d -> %d:%d proto %d keep_state=%d action=%d\n",
880 pr.rule.ifname,
881 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, buf, 32),
882 (int)ntohs(pr.rule.dst.port[0]),
883 (int)ntohs(pr.rule.dst.port[1]),
884 #ifndef PF_NEWSTYLE
885 (int)pr.rule.rpool.proxy_port[0],
886 (int)pr.rule.rpool.proxy_port[1],
887 #else
888 (int)pr.rule.rdr.proxy_port[0],
889 (int)pr.rule.rdr.proxy_port[1],
890 #endif
891 (int)pr.rule.proto,
892 (int)pr.rule.keep_state,
893 (int)pr.rule.action);
894 printf(" description: \"%s\"\n", pr.rule.label);
895 #ifndef PF_NEWSTYLE
896 memset(&pp, 0, sizeof(pp));
897 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
898 pp.r_action = PF_RDR;
899 pp.r_num = i;
900 pp.ticket = pr.ticket;
901 if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
902 perror("DIOCGETADDRS");
903 printf(" nb pool addr = %d ticket=%d\n", pp.nr, pp.ticket);
904 /*if(ioctl(dev, DIOCGETRULE, &pr) < 0)
905 perror("DIOCGETRULE"); */
906 pp.nr = 0; /* first */
907 if(ioctl(dev, DIOCGETADDR, &pp) < 0)
908 perror("DIOCGETADDR");
909 /* addr.v.a.addr.v4.s_addr */
910 printf(" %s\n", inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, buf, 32));
911 #else
912 printf(" rule_flag=%08x action=%d direction=%d log=%d logif=%d "
913 "quick=%d ifnot=%d af=%d type=%d code=%d rdr.port_op=%d rdr.opts=%d\n",
914 pr.rule.rule_flag, pr.rule.action, pr.rule.direction,
915 pr.rule.log, pr.rule.logif, pr.rule.quick, pr.rule.ifnot,
916 pr.rule.af, pr.rule.type, pr.rule.code,
917 pr.rule.rdr.port_op, pr.rule.rdr.opts);
918 printf(" %s\n", inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, buf, 32));
919 #endif
922 #endif