Miniupnpd: update from 1.8 (20140422) to 1.9 (20141209)
[tomato.git] / release / src / router / miniupnpd / pf / obsdrdr.c
blob3a38d4a369b4d7317ede2ddaada57a50542c3a49
1 /* $Id: obsdrdr.c,v 1.83 2014/06/27 13:08:49 nanard Exp $ */
2 /* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2014 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 * keep state label "test label" -> 192.168.0.42 port 12345
13 * or a rdr rule + a pass rule :
14 * rdr quick on xl1 inet proto udp from any to any port = 54321 \
15 * keep state label "test label" -> 192.168.0.42 port 12345
16 * pass in quick on xl1 inet proto udp from any to 192.168.0.42 port = 12345 \
17 * flags S/SA keep state label "test label"
19 * - OpenBSD starting from version 4.7
20 * match in on xl1 inet proto udp from any to any port 54321 \
21 * label "test label" rdr-to 192.168.0.42 port 12345
22 * or
23 * pass in quick on xl1 inet proto udp from any to any port 54321 \
24 * label "test label" rdr-to 192.168.0.42 port 12345
28 * Macros/#defines :
29 * - PF_ENABLE_FILTER_RULES
30 * If set, two rules are created : rdr + pass. Else a rdr/pass rule
31 * is created.
32 * - USE_IFNAME_IN_RULES
33 * If set the interface name is set in the rule.
34 * - PFRULE_INOUT_COUNTS
35 * Must be set with OpenBSD version 3.8 and up.
36 * - PFRULE_HAS_RTABLEID
37 * Must be set with OpenBSD version 4.0 and up.
38 * - PF_NEWSSTYLE
39 * Must be set with OpenBSD version 4.7 and up.
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <sys/param.h>
45 #include <net/if.h>
46 #include <netinet/in.h>
47 #include <netinet/tcp.h>
48 #include <arpa/inet.h>
49 #ifdef __DragonFly__
50 #include <net/pf/pfvar.h>
51 #else
52 #ifdef __APPLE__
53 #define PRIVATE 1
54 #endif
55 #include <net/pfvar.h>
56 #endif
57 #include <fcntl.h>
58 #include <sys/ioctl.h>
59 #include <unistd.h>
60 #include <string.h>
61 #include <syslog.h>
62 #include <stdio.h>
63 #include <stdlib.h>
65 #include "../macros.h"
66 #include "../config.h"
67 #include "obsdrdr.h"
68 #include "../upnpglobalvars.h"
70 #ifndef USE_PF
71 #error "USE_PF macro is undefined, check consistency between config.h and Makefile"
72 #else
74 /* list too keep timestamps for port mappings having a lease duration */
75 struct timestamp_entry {
76 struct timestamp_entry * next;
77 unsigned int timestamp;
78 unsigned short eport;
79 short protocol;
82 static struct timestamp_entry * timestamp_list = NULL;
84 static unsigned int
85 get_timestamp(unsigned short eport, int proto)
87 struct timestamp_entry * e;
88 e = timestamp_list;
89 while(e) {
90 if(e->eport == eport && e->protocol == (short)proto)
91 return e->timestamp;
92 e = e->next;
94 return 0;
97 static void
98 remove_timestamp_entry(unsigned short eport, int proto)
100 struct timestamp_entry * e;
101 struct timestamp_entry * * p;
102 p = &timestamp_list;
103 e = *p;
104 while(e) {
105 if(e->eport == eport && e->protocol == (short)proto) {
106 /* remove the entry */
107 *p = e->next;
108 free(e);
109 return;
111 p = &(e->next);
112 e = *p;
116 /* /dev/pf when opened */
117 int dev = -1;
119 /* shutdown_redirect() :
120 * close the /dev/pf device */
121 void
122 shutdown_redirect(void)
124 if(close(dev)<0)
125 syslog(LOG_ERR, "close(\"/dev/pf\"): %m");
126 dev = -1;
129 /* open the device */
131 init_redirect(void)
133 struct pf_status status;
134 if(dev>=0)
135 shutdown_redirect();
136 dev = open("/dev/pf", O_RDWR);
137 if(dev<0) {
138 syslog(LOG_ERR, "open(\"/dev/pf\"): %m");
139 return -1;
141 if(ioctl(dev, DIOCGETSTATUS, &status)<0) {
142 syslog(LOG_ERR, "DIOCGETSTATUS: %m");
143 return -1;
145 if(!status.running) {
146 syslog(LOG_ERR, "pf is disabled");
147 return -1;
149 return 0;
152 #if TEST
153 /* for debug */
155 clear_redirect_rules(void)
157 struct pfioc_trans io;
158 struct pfioc_trans_e ioe;
159 if(dev<0) {
160 syslog(LOG_ERR, "pf device is not open");
161 return -1;
163 memset(&ioe, 0, sizeof(ioe));
164 io.size = 1;
165 io.esize = sizeof(ioe);
166 io.array = &ioe;
167 #ifndef PF_NEWSTYLE
168 ioe.rs_num = PF_RULESET_RDR;
169 #else
170 ioe.type = PF_TRANS_RULESET;
171 #endif
172 strlcpy(ioe.anchor, anchor_name, MAXPATHLEN);
173 if(ioctl(dev, DIOCXBEGIN, &io) < 0)
175 syslog(LOG_ERR, "ioctl(dev, DIOCXBEGIN, ...): %m");
176 goto error;
178 if(ioctl(dev, DIOCXCOMMIT, &io) < 0)
180 syslog(LOG_ERR, "ioctl(dev, DIOCXCOMMIT, ...): %m");
181 goto error;
183 return 0;
184 error:
185 return -1;
189 clear_filter_rules(void)
191 #ifndef PF_ENABLE_FILTER_RULES
192 return 0;
193 #else
194 struct pfioc_trans io;
195 struct pfioc_trans_e ioe;
196 if(dev<0) {
197 syslog(LOG_ERR, "pf device is not open");
198 return -1;
200 memset(&ioe, 0, sizeof(ioe));
201 io.size = 1;
202 io.esize = sizeof(ioe);
203 io.array = &ioe;
204 #ifndef PF_NEWSTYLE
205 ioe.rs_num = PF_RULESET_FILTER;
206 #else
207 /* ? */
208 ioe.type = PF_TRANS_RULESET;
209 #endif
210 strlcpy(ioe.anchor, anchor_name, MAXPATHLEN);
211 if(ioctl(dev, DIOCXBEGIN, &io) < 0)
213 syslog(LOG_ERR, "ioctl(dev, DIOCXBEGIN, ...): %m");
214 goto error;
216 if(ioctl(dev, DIOCXCOMMIT, &io) < 0)
218 syslog(LOG_ERR, "ioctl(dev, DIOCXCOMMIT, ...): %m");
219 goto error;
221 return 0;
222 error:
223 return -1;
224 #endif
226 #endif
228 /* add_redirect_rule2() :
229 * create a rdr rule */
231 add_redirect_rule2(const char * ifname,
232 const char * rhost, unsigned short eport,
233 const char * iaddr, unsigned short iport, int proto,
234 const char * desc, unsigned int timestamp)
236 int r;
237 struct pfioc_rule pcr;
238 #ifndef PF_NEWSTYLE
239 struct pfioc_pooladdr pp;
240 struct pf_pooladdr *a;
241 #endif
242 if(dev<0) {
243 syslog(LOG_ERR, "pf device is not open");
244 return -1;
246 r = 0;
247 memset(&pcr, 0, sizeof(pcr));
248 strlcpy(pcr.anchor, anchor_name, MAXPATHLEN);
250 #ifndef PF_NEWSTYLE
251 memset(&pp, 0, sizeof(pp));
252 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
253 if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0)
255 syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m");
256 r = -1;
258 else
260 pcr.pool_ticket = pp.ticket;
261 #else
262 if(1)
264 pcr.rule.direction = PF_IN;
265 /*pcr.rule.src.addr.type = PF_ADDR_NONE;*/
266 pcr.rule.src.addr.type = PF_ADDR_ADDRMASK;
267 pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
268 pcr.rule.nat.addr.type = PF_ADDR_NONE;
269 pcr.rule.rdr.addr.type = PF_ADDR_ADDRMASK;
270 #endif
272 #ifdef __APPLE__
273 pcr.rule.dst.xport.range.op = PF_OP_EQ;
274 pcr.rule.dst.xport.range.port[0] = htons(eport);
275 pcr.rule.dst.xport.range.port[1] = htons(eport);
276 #else
277 pcr.rule.dst.port_op = PF_OP_EQ;
278 pcr.rule.dst.port[0] = htons(eport);
279 pcr.rule.dst.port[1] = htons(eport);
280 #endif
281 #ifndef PF_NEWSTYLE
282 pcr.rule.action = PF_RDR;
283 #ifndef PF_ENABLE_FILTER_RULES
284 pcr.rule.natpass = 1;
285 #else
286 pcr.rule.natpass = 0;
287 #endif
288 #else
289 #ifndef PF_ENABLE_FILTER_RULES
290 pcr.rule.action = PF_PASS;
291 #else
292 pcr.rule.action = PF_MATCH;
293 #endif
294 #endif
295 pcr.rule.af = AF_INET;
296 #ifdef USE_IFNAME_IN_RULES
297 if(ifname)
298 strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ);
299 #endif
300 pcr.rule.proto = proto;
301 pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/
302 #ifdef PFRULE_HAS_RTABLEID
303 pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */
304 #endif
305 #ifdef PFRULE_HAS_ONRDOMAIN
306 pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */
307 #endif
308 pcr.rule.quick = 1;
309 pcr.rule.keep_state = PF_STATE_NORMAL;
310 if(tag)
311 strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE);
312 strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE);
313 if(rhost && rhost[0] != '\0' && rhost[0] != '*')
315 inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr);
316 pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
318 #ifndef PF_NEWSTYLE
319 pcr.rule.rpool.proxy_port[0] = iport;
320 pcr.rule.rpool.proxy_port[1] = iport;
321 TAILQ_INIT(&pcr.rule.rpool.list);
322 a = calloc(1, sizeof(struct pf_pooladdr));
323 inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr);
324 a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
325 TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries);
327 memcpy(&pp.addr, a, sizeof(struct pf_pooladdr));
328 if(ioctl(dev, DIOCADDADDR, &pp) < 0)
330 syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m");
331 r = -1;
333 else
335 #else
336 pcr.rule.rdr.proxy_port[0] = iport;
337 pcr.rule.rdr.proxy_port[1] = iport;
338 inet_pton(AF_INET, iaddr, &pcr.rule.rdr.addr.v.a.addr.v4.s_addr);
339 pcr.rule.rdr.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
340 if(1)
342 #endif
343 pcr.action = PF_CHANGE_GET_TICKET;
344 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
346 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
347 r = -1;
349 else
351 pcr.action = PF_CHANGE_ADD_TAIL;
352 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
354 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m");
355 r = -1;
359 #ifndef PF_NEWSTYLE
360 free(a);
361 #endif
363 if(r == 0 && timestamp > 0)
365 struct timestamp_entry * tmp;
366 tmp = malloc(sizeof(struct timestamp_entry));
367 if(tmp)
369 tmp->next = timestamp_list;
370 tmp->timestamp = timestamp;
371 tmp->eport = eport;
372 tmp->protocol = (short)proto;
373 timestamp_list = tmp;
376 return r;
379 /* thanks to Seth Mos for this function */
381 add_filter_rule2(const char * ifname,
382 const char * rhost, const char * iaddr,
383 unsigned short eport, unsigned short iport,
384 int proto, const char * desc)
386 #ifndef PF_ENABLE_FILTER_RULES
387 UNUSED(ifname);
388 UNUSED(rhost); UNUSED(iaddr);
389 UNUSED(eport); UNUSED(iport);
390 UNUSED(proto); UNUSED(desc);
391 return 0;
392 #else
393 int r;
394 struct pfioc_rule pcr;
395 #ifndef PF_NEWSTYLE
396 struct pfioc_pooladdr pp;
397 #endif
398 #ifndef USE_IFNAME_IN_RULES
399 UNUSED(ifname);
400 #endif
401 UNUSED(eport);
402 if(dev<0) {
403 syslog(LOG_ERR, "pf device is not open");
404 return -1;
406 r = 0;
407 memset(&pcr, 0, sizeof(pcr));
408 strlcpy(pcr.anchor, anchor_name, MAXPATHLEN);
410 #ifndef PF_NEWSTYLE
411 memset(&pp, 0, sizeof(pp));
412 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
413 if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0)
415 syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m");
416 r = -1;
418 else
420 pcr.pool_ticket = pp.ticket;
421 #else
422 if(1)
424 #endif
425 pcr.rule.dst.port_op = PF_OP_EQ;
426 pcr.rule.dst.port[0] = htons(iport);
427 pcr.rule.direction = PF_IN;
428 pcr.rule.action = PF_PASS;
429 pcr.rule.af = AF_INET;
430 #ifdef USE_IFNAME_IN_RULES
431 if(ifname)
432 strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ);
433 #endif
434 pcr.rule.proto = proto;
435 pcr.rule.quick = (GETFLAG(PFNOQUICKRULESMASK))?0:1;
436 pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/
437 /* see the discussion on the forum :
438 * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */
439 pcr.rule.flags = TH_SYN;
440 pcr.rule.flagset = (TH_SYN|TH_ACK);
441 #ifdef PFRULE_HAS_RTABLEID
442 pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */
443 #endif
444 #ifdef PFRULE_HAS_ONRDOMAIN
445 pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */
446 #endif
447 pcr.rule.keep_state = 1;
448 strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE);
449 if(queue)
450 strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE);
451 if(tag)
452 strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE);
454 if(rhost && rhost[0] != '\0' && rhost[0] != '*')
456 inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr);
457 pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
459 /* we want any - iaddr port = # keep state label */
460 inet_pton(AF_INET, iaddr, &pcr.rule.dst.addr.v.a.addr.v4.s_addr);
461 pcr.rule.dst.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
462 #ifndef PF_NEWSTYLE
463 pcr.rule.rpool.proxy_port[0] = iport;
464 pcr.rule.rpool.proxy_port[1] = iport;
465 TAILQ_INIT(&pcr.rule.rpool.list);
466 #endif
467 if(1)
469 pcr.action = PF_CHANGE_GET_TICKET;
470 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
472 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
473 r = -1;
475 else
477 pcr.action = PF_CHANGE_ADD_TAIL;
478 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
480 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m");
481 r = -1;
486 return r;
487 #endif
490 /* get_redirect_rule()
491 * return value : 0 success (found)
492 * -1 = error or rule not found */
494 get_redirect_rule(const char * ifname, unsigned short eport, int proto,
495 char * iaddr, int iaddrlen, unsigned short * iport,
496 char * desc, int desclen,
497 char * rhost, int rhostlen,
498 unsigned int * timestamp,
499 u_int64_t * packets, u_int64_t * bytes)
501 int i, n;
502 struct pfioc_rule pr;
503 #ifndef PF_NEWSTYLE
504 struct pfioc_pooladdr pp;
505 #endif
506 UNUSED(ifname);
508 if(dev<0) {
509 syslog(LOG_ERR, "pf device is not open");
510 return -1;
512 memset(&pr, 0, sizeof(pr));
513 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
514 #ifndef PF_NEWSTYLE
515 pr.rule.action = PF_RDR;
516 #endif
517 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
519 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
520 goto error;
522 n = pr.nr;
523 for(i=0; i<n; i++)
525 pr.nr = i;
526 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
528 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
529 goto error;
531 #ifdef __APPLE__
532 if( (eport == ntohs(pr.rule.dst.xport.range.port[0]))
533 && (eport == ntohs(pr.rule.dst.xport.range.port[1]))
534 #else
535 if( (eport == ntohs(pr.rule.dst.port[0]))
536 && (eport == ntohs(pr.rule.dst.port[1]))
537 #endif
538 && (pr.rule.proto == proto) )
540 #ifndef PF_NEWSTYLE
541 *iport = pr.rule.rpool.proxy_port[0];
542 #else
543 *iport = pr.rule.rdr.proxy_port[0];
544 #endif
545 if(desc)
546 strlcpy(desc, pr.rule.label, desclen);
547 #ifdef PFRULE_INOUT_COUNTS
548 if(packets)
549 *packets = pr.rule.packets[0] + pr.rule.packets[1];
550 if(bytes)
551 *bytes = pr.rule.bytes[0] + pr.rule.bytes[1];
552 #else
553 if(packets)
554 *packets = pr.rule.packets;
555 if(bytes)
556 *bytes = pr.rule.bytes;
557 #endif
558 #ifndef PF_NEWSTYLE
559 memset(&pp, 0, sizeof(pp));
560 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
561 pp.r_action = PF_RDR;
562 pp.r_num = i;
563 pp.ticket = pr.ticket;
564 if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
566 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m");
567 goto error;
569 if(pp.nr != 1)
571 syslog(LOG_NOTICE, "No address associated with pf rule");
572 goto error;
574 pp.nr = 0; /* first */
575 if(ioctl(dev, DIOCGETADDR, &pp) < 0)
577 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m");
578 goto error;
580 inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr,
581 iaddr, iaddrlen);
582 #else
583 inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr,
584 iaddr, iaddrlen);
585 #endif
586 if(rhost && rhostlen > 0)
588 if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0)
590 rhost[0] = '\0'; /* empty string */
592 else
594 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr,
595 rhost, rhostlen);
598 if(timestamp)
599 *timestamp = get_timestamp(eport, proto);
600 return 0;
603 error:
604 return -1;
607 static int
608 priv_delete_redirect_rule(const char * ifname, unsigned short eport,
609 int proto, unsigned short * iport,
610 in_addr_t * iaddr)
612 int i, n;
613 struct pfioc_rule pr;
614 UNUSED(ifname);
616 if(dev<0) {
617 syslog(LOG_ERR, "pf device is not open");
618 return -1;
620 memset(&pr, 0, sizeof(pr));
621 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
622 #ifndef PF_NEWSTYLE
623 pr.rule.action = PF_RDR;
624 #endif
625 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
627 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
628 goto error;
630 n = pr.nr;
631 for(i=0; i<n; i++)
633 pr.nr = i;
634 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
636 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
637 goto error;
639 #ifdef __APPLE__
640 if( (eport == ntohs(pr.rule.dst.xport.range.port[0]))
641 && (eport == ntohs(pr.rule.dst.xport.range.port[1]))
642 #else
643 if( (eport == ntohs(pr.rule.dst.port[0]))
644 && (eport == ntohs(pr.rule.dst.port[1]))
645 #endif
646 && (pr.rule.proto == proto) )
648 /* retrieve iport in order to remove filter rule */
649 #ifndef PF_NEWSTYLE
650 if(iport) *iport = pr.rule.rpool.proxy_port[0];
651 if(iaddr)
653 /* retrieve internal address */
654 struct pfioc_pooladdr pp;
655 memset(&pp, 0, sizeof(pp));
656 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
657 pp.r_action = PF_RDR;
658 pp.r_num = i;
659 pp.ticket = pr.ticket;
660 if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
662 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m");
663 goto error;
665 if(pp.nr != 1)
667 syslog(LOG_NOTICE, "No address associated with pf rule");
668 goto error;
670 pp.nr = 0; /* first */
671 if(ioctl(dev, DIOCGETADDR, &pp) < 0)
673 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m");
674 goto error;
676 *iaddr = pp.addr.addr.v.a.addr.v4.s_addr;
678 #else
679 if(iport) *iport = pr.rule.rdr.proxy_port[0];
680 if(iaddr)
682 /* retrieve internal address */
683 *iaddr = pr.rule.rdr.addr.v.a.addr.v4.s_addr;
685 #endif
686 pr.action = PF_CHANGE_GET_TICKET;
687 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
689 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
690 goto error;
692 pr.action = PF_CHANGE_REMOVE;
693 pr.nr = i;
694 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
696 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
697 goto error;
699 remove_timestamp_entry(eport, proto);
700 return 0;
703 error:
704 return -1;
708 delete_redirect_rule(const char * ifname, unsigned short eport,
709 int proto)
711 return priv_delete_redirect_rule(ifname, eport, proto, NULL, NULL);
714 static int
715 priv_delete_filter_rule(const char * ifname, unsigned short iport,
716 int proto, in_addr_t iaddr)
718 #ifndef PF_ENABLE_FILTER_RULES
719 UNUSED(ifname); UNUSED(iport); UNUSED(proto); UNUSED(iaddr);
720 return 0;
721 #else
722 int i, n;
723 struct pfioc_rule pr;
724 UNUSED(ifname);
725 if(dev<0) {
726 syslog(LOG_ERR, "pf device is not open");
727 return -1;
729 memset(&pr, 0, sizeof(pr));
730 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
731 pr.rule.action = PF_PASS;
732 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
734 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
735 goto error;
737 n = pr.nr;
738 for(i=0; i<n; i++)
740 pr.nr = i;
741 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
743 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
744 goto error;
746 #ifdef TEST
747 syslog(LOG_DEBUG, "%2d port=%hu proto=%d addr=%8x",
748 i, ntohs(pr.rule.dst.port[0]), pr.rule.proto,
749 pr.rule.dst.addr.v.a.addr.v4.s_addr);
750 /*pr.rule.dst.addr.v.a.mask.v4.s_addr*/
751 #endif
752 if( (iport == ntohs(pr.rule.dst.port[0]))
753 && (pr.rule.proto == proto) &&
754 (iaddr == pr.rule.dst.addr.v.a.addr.v4.s_addr)
757 pr.action = PF_CHANGE_GET_TICKET;
758 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
760 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
761 goto error;
763 pr.action = PF_CHANGE_REMOVE;
764 pr.nr = i;
765 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
767 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
768 goto error;
770 return 0;
773 error:
774 return -1;
775 #endif
779 delete_redirect_and_filter_rules(const char * ifname, unsigned short eport,
780 int proto)
782 int r;
783 unsigned short iport;
784 in_addr_t iaddr;
785 r = priv_delete_redirect_rule(ifname, eport, proto, &iport, &iaddr);
786 if(r == 0)
788 r = priv_delete_filter_rule(ifname, iport, proto, iaddr);
790 return r;
794 get_redirect_rule_by_index(int index,
795 char * ifname, unsigned short * eport,
796 char * iaddr, int iaddrlen, unsigned short * iport,
797 int * proto, char * desc, int desclen,
798 char * rhost, int rhostlen,
799 unsigned int * timestamp,
800 u_int64_t * packets, u_int64_t * bytes)
802 int n;
803 struct pfioc_rule pr;
804 #ifndef PF_NEWSTYLE
805 struct pfioc_pooladdr pp;
806 #endif
807 if(index < 0)
808 return -1;
809 if(dev<0) {
810 syslog(LOG_ERR, "pf device is not open");
811 return -1;
813 memset(&pr, 0, sizeof(pr));
814 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
815 #ifndef PF_NEWSTYLE
816 pr.rule.action = PF_RDR;
817 #endif
818 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
820 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
821 goto error;
823 n = pr.nr;
824 if(index >= n)
825 goto error;
826 pr.nr = index;
827 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
829 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
830 goto error;
832 *proto = pr.rule.proto;
833 #ifdef __APPLE__
834 *eport = ntohs(pr.rule.dst.xport.range.port[0]);
835 #else
836 *eport = ntohs(pr.rule.dst.port[0]);
837 #endif
838 #ifndef PF_NEWSTYLE
839 *iport = pr.rule.rpool.proxy_port[0];
840 #else
841 *iport = pr.rule.rdr.proxy_port[0];
842 #endif
843 if(ifname)
844 strlcpy(ifname, pr.rule.ifname, IFNAMSIZ);
845 if(desc)
846 strlcpy(desc, pr.rule.label, desclen);
847 #ifdef PFRULE_INOUT_COUNTS
848 if(packets)
849 *packets = pr.rule.packets[0] + pr.rule.packets[1];
850 if(bytes)
851 *bytes = pr.rule.bytes[0] + pr.rule.bytes[1];
852 #else
853 if(packets)
854 *packets = pr.rule.packets;
855 if(bytes)
856 *bytes = pr.rule.bytes;
857 #endif
858 #ifndef PF_NEWSTYLE
859 memset(&pp, 0, sizeof(pp));
860 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
861 pp.r_action = PF_RDR;
862 pp.r_num = index;
863 pp.ticket = pr.ticket;
864 if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
866 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m");
867 goto error;
869 if(pp.nr != 1)
871 syslog(LOG_NOTICE, "No address associated with pf rule");
872 goto error;
874 pp.nr = 0; /* first */
875 if(ioctl(dev, DIOCGETADDR, &pp) < 0)
877 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m");
878 goto error;
880 inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr,
881 iaddr, iaddrlen);
882 #else
883 inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr,
884 iaddr, iaddrlen);
885 #endif
886 if(rhost && rhostlen > 0)
888 if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0)
890 rhost[0] = '\0'; /* empty string */
892 else
894 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr,
895 rhost, rhostlen);
898 if(timestamp)
899 *timestamp = get_timestamp(*eport, *proto);
900 return 0;
901 error:
902 return -1;
905 /* return an (malloc'ed) array of "external" port for which there is
906 * a port mapping. number is the size of the array */
907 unsigned short *
908 get_portmappings_in_range(unsigned short startport, unsigned short endport,
909 int proto, unsigned int * number)
911 unsigned short * array;
912 unsigned int capacity;
913 int i, n;
914 unsigned short eport;
915 struct pfioc_rule pr;
917 *number = 0;
918 if(dev<0) {
919 syslog(LOG_ERR, "pf device is not open");
920 return NULL;
922 capacity = 128;
923 array = calloc(capacity, sizeof(unsigned short));
924 if(!array)
926 syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
927 return NULL;
929 memset(&pr, 0, sizeof(pr));
930 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
931 #ifndef PF_NEWSTYLE
932 pr.rule.action = PF_RDR;
933 #endif
934 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
936 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
937 free(array);
938 return NULL;
940 n = pr.nr;
941 for(i=0; i<n; i++)
943 pr.nr = i;
944 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
946 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
947 continue;
949 #ifdef __APPLE__
950 eport = ntohs(pr.rule.dst.xport.range.port[0]);
951 if( (eport == ntohs(pr.rule.dst.xport.range.port[1]))
952 #else
953 eport = ntohs(pr.rule.dst.port[0]);
954 if( (eport == ntohs(pr.rule.dst.port[1]))
955 #endif
956 && (pr.rule.proto == proto)
957 && (startport <= eport) && (eport <= endport) )
959 if(*number >= capacity)
961 /* need to increase the capacity of the array */
962 capacity += 128;
963 array = realloc(array, sizeof(unsigned short)*capacity);
964 if(!array)
966 syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity);
967 *number = 0;
968 return NULL;
971 array[*number] = eport;
972 (*number)++;
975 return array;
978 /* this function is only for testing */
979 #if TEST
980 void
981 list_rules(void)
983 char buf[32];
984 int i, n;
985 struct pfioc_rule pr;
986 #ifndef PF_NEWSTYLE
987 struct pfioc_pooladdr pp;
988 #endif
990 if(dev<0)
992 perror("pf dev not open");
993 return ;
995 memset(&pr, 0, sizeof(pr));
996 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
997 pr.rule.action = PF_RDR;
998 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
999 perror("DIOCGETRULES");
1000 printf("ticket = %d, nr = %d\n", pr.ticket, pr.nr);
1001 n = pr.nr;
1002 for(i=0; i<n; i++)
1004 printf("-- rule %d --\n", i);
1005 pr.nr = i;
1006 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
1007 perror("DIOCGETRULE");
1008 printf(" %s %s %d:%d -> %d:%d proto %d keep_state=%d action=%d\n",
1009 pr.rule.ifname,
1010 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, buf, 32),
1011 (int)ntohs(pr.rule.dst.port[0]),
1012 (int)ntohs(pr.rule.dst.port[1]),
1013 #ifndef PF_NEWSTYLE
1014 (int)pr.rule.rpool.proxy_port[0],
1015 (int)pr.rule.rpool.proxy_port[1],
1016 #else
1017 (int)pr.rule.rdr.proxy_port[0],
1018 (int)pr.rule.rdr.proxy_port[1],
1019 #endif
1020 (int)pr.rule.proto,
1021 (int)pr.rule.keep_state,
1022 (int)pr.rule.action);
1023 printf(" description: \"%s\"\n", pr.rule.label);
1024 #ifndef PF_NEWSTYLE
1025 memset(&pp, 0, sizeof(pp));
1026 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
1027 pp.r_action = PF_RDR;
1028 pp.r_num = i;
1029 pp.ticket = pr.ticket;
1030 if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
1031 perror("DIOCGETADDRS");
1032 printf(" nb pool addr = %d ticket=%d\n", pp.nr, pp.ticket);
1033 /*if(ioctl(dev, DIOCGETRULE, &pr) < 0)
1034 perror("DIOCGETRULE"); */
1035 pp.nr = 0; /* first */
1036 if(ioctl(dev, DIOCGETADDR, &pp) < 0)
1037 perror("DIOCGETADDR");
1038 /* addr.v.a.addr.v4.s_addr */
1039 printf(" %s\n", inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, buf, 32));
1040 #else
1041 printf(" rule_flag=%08x action=%d direction=%d log=%d logif=%d "
1042 "quick=%d ifnot=%d af=%d type=%d code=%d rdr.port_op=%d rdr.opts=%d\n",
1043 pr.rule.rule_flag, pr.rule.action, pr.rule.direction,
1044 pr.rule.log, pr.rule.logif, pr.rule.quick, pr.rule.ifnot,
1045 pr.rule.af, pr.rule.type, pr.rule.code,
1046 pr.rule.rdr.port_op, pr.rule.rdr.opts);
1047 printf(" %s\n", inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, buf, 32));
1048 #endif
1051 #endif /* TEST */
1053 #endif /* USE_PF */