TOR: fix compilation
[tomato.git] / release / src / router / miniupnpd / pf / obsdrdr.c
blob141f985cd2e6d3da6a4861f4ba7dc68d8d2e8d3c
1 /* $Id: obsdrdr.c,v 1.87 2016/02/16 12:14:28 nanard Exp $ */
2 /* vim: tabstop=4 shiftwidth=4 noexpandtab
3 * MiniUPnP project
4 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5 * (c) 2006-2016 Thomas Bernard
6 * This software is subject to the conditions detailed
7 * in the LICENCE file provided within the distribution */
9 /*
10 * pf rules created (with ext_if = xl1)
11 * - OpenBSD up to version 4.6 :
12 * rdr pass on xl1 inet proto udp from any to any port = 54321 \
13 * keep state label "test label" -> 192.168.0.42 port 12345
14 * or a rdr rule + a pass rule :
15 * rdr quick on xl1 inet proto udp from any to any port = 54321 \
16 * keep state label "test label" -> 192.168.0.42 port 12345
17 * pass in quick on xl1 inet proto udp from any to 192.168.0.42 port = 12345 \
18 * flags S/SA keep state label "test label"
20 * - OpenBSD starting from version 4.7
21 * match in on xl1 inet proto udp from any to any port 54321 \
22 * label "test label" rdr-to 192.168.0.42 port 12345
23 * or
24 * pass in quick on xl1 inet proto udp from any to any port 54321 \
25 * label "test label" rdr-to 192.168.0.42 port 12345
29 * Macros/#defines :
30 * - PF_ENABLE_FILTER_RULES
31 * If set, two rules are created : rdr + pass. Else a rdr/pass rule
32 * is created.
33 * - USE_IFNAME_IN_RULES
34 * If set the interface name is set in the rule.
35 * - PFRULE_INOUT_COUNTS
36 * Must be set with OpenBSD version 3.8 and up.
37 * - PFRULE_HAS_RTABLEID
38 * Must be set with OpenBSD version 4.0 and up.
39 * - PF_NEWSSTYLE
40 * Must be set with OpenBSD version 4.7 and up.
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <sys/param.h>
46 #include <net/if.h>
47 #include <netinet/in.h>
48 #include <netinet/tcp.h>
49 #include <arpa/inet.h>
50 #ifdef __DragonFly__
51 #include <net/pf/pfvar.h>
52 #else
53 #ifdef __APPLE__
54 #define PRIVATE 1
55 #endif
56 #include <net/pfvar.h>
57 #endif
58 #include <fcntl.h>
59 #include <sys/ioctl.h>
60 #include <unistd.h>
61 #include <string.h>
62 #include <syslog.h>
63 #include <stdio.h>
64 #include <stdlib.h>
66 #include "../macros.h"
67 #include "../config.h"
68 #include "obsdrdr.h"
69 #include "../upnpglobalvars.h"
71 #ifndef USE_PF
72 #error "USE_PF macro is undefined, check consistency between config.h and Makefile"
73 #else
75 /* list to keep timestamps for port mappings having a lease duration */
76 struct timestamp_entry {
77 struct timestamp_entry * next;
78 unsigned int timestamp;
79 unsigned short eport;
80 short protocol;
83 static struct timestamp_entry * timestamp_list = NULL;
85 static unsigned int
86 get_timestamp(unsigned short eport, int proto)
88 struct timestamp_entry * e;
89 e = timestamp_list;
90 while(e) {
91 if(e->eport == eport && e->protocol == (short)proto)
92 return e->timestamp;
93 e = e->next;
95 return 0;
98 static void
99 remove_timestamp_entry(unsigned short eport, int proto)
101 struct timestamp_entry * e;
102 struct timestamp_entry * * p;
103 p = &timestamp_list;
104 e = *p;
105 while(e) {
106 if(e->eport == eport && e->protocol == (short)proto) {
107 /* remove the entry */
108 *p = e->next;
109 free(e);
110 return;
112 p = &(e->next);
113 e = *p;
117 static void
118 add_timestamp_entry(unsigned short eport, int proto, unsigned timestamp)
120 struct timestamp_entry * tmp;
121 tmp = malloc(sizeof(struct timestamp_entry));
122 if(tmp)
124 tmp->next = timestamp_list;
125 tmp->timestamp = timestamp;
126 tmp->eport = eport;
127 tmp->protocol = (short)proto;
128 timestamp_list = tmp;
130 else
132 syslog(LOG_ERR, "add_timestamp_entry() malloc(%lu) error",
133 sizeof(struct timestamp_entry));
137 /* /dev/pf when opened */
138 int dev = -1;
140 /* shutdown_redirect() :
141 * close the /dev/pf device */
142 void
143 shutdown_redirect(void)
145 if(close(dev)<0)
146 syslog(LOG_ERR, "close(\"/dev/pf\"): %m");
147 dev = -1;
150 /* open the device */
152 init_redirect(void)
154 struct pf_status status;
155 if(dev>=0)
156 shutdown_redirect();
157 dev = open("/dev/pf", O_RDWR);
158 if(dev<0) {
159 syslog(LOG_ERR, "open(\"/dev/pf\"): %m");
160 return -1;
162 if(ioctl(dev, DIOCGETSTATUS, &status)<0) {
163 syslog(LOG_ERR, "DIOCGETSTATUS: %m");
164 return -1;
166 if(!status.running) {
167 syslog(LOG_ERR, "pf is disabled");
168 return -1;
170 return 0;
173 #if TEST
174 /* for debug */
176 clear_redirect_rules(void)
178 struct pfioc_trans io;
179 struct pfioc_trans_e ioe;
180 if(dev<0) {
181 syslog(LOG_ERR, "pf device is not open");
182 return -1;
184 memset(&ioe, 0, sizeof(ioe));
185 io.size = 1;
186 io.esize = sizeof(ioe);
187 io.array = &ioe;
188 #ifndef PF_NEWSTYLE
189 ioe.rs_num = PF_RULESET_RDR;
190 #else
191 ioe.type = PF_TRANS_RULESET;
192 #endif
193 strlcpy(ioe.anchor, anchor_name, MAXPATHLEN);
194 if(ioctl(dev, DIOCXBEGIN, &io) < 0)
196 syslog(LOG_ERR, "ioctl(dev, DIOCXBEGIN, ...): %m");
197 goto error;
199 if(ioctl(dev, DIOCXCOMMIT, &io) < 0)
201 syslog(LOG_ERR, "ioctl(dev, DIOCXCOMMIT, ...): %m");
202 goto error;
204 return 0;
205 error:
206 return -1;
210 clear_filter_rules(void)
212 #ifndef PF_ENABLE_FILTER_RULES
213 return 0;
214 #else
215 struct pfioc_trans io;
216 struct pfioc_trans_e ioe;
217 if(dev<0) {
218 syslog(LOG_ERR, "pf device is not open");
219 return -1;
221 memset(&ioe, 0, sizeof(ioe));
222 io.size = 1;
223 io.esize = sizeof(ioe);
224 io.array = &ioe;
225 #ifndef PF_NEWSTYLE
226 ioe.rs_num = PF_RULESET_FILTER;
227 #else
228 /* ? */
229 ioe.type = PF_TRANS_RULESET;
230 #endif
231 strlcpy(ioe.anchor, anchor_name, MAXPATHLEN);
232 if(ioctl(dev, DIOCXBEGIN, &io) < 0)
234 syslog(LOG_ERR, "ioctl(dev, DIOCXBEGIN, ...): %m");
235 goto error;
237 if(ioctl(dev, DIOCXCOMMIT, &io) < 0)
239 syslog(LOG_ERR, "ioctl(dev, DIOCXCOMMIT, ...): %m");
240 goto error;
242 return 0;
243 error:
244 return -1;
245 #endif
247 #endif
249 /* add_redirect_rule2() :
250 * create a rdr rule */
252 add_redirect_rule2(const char * ifname,
253 const char * rhost, unsigned short eport,
254 const char * iaddr, unsigned short iport, int proto,
255 const char * desc, unsigned int timestamp)
257 int r;
258 struct pfioc_rule pcr;
259 #ifndef PF_NEWSTYLE
260 struct pfioc_pooladdr pp;
261 struct pf_pooladdr *a;
262 #endif
263 if(dev<0) {
264 syslog(LOG_ERR, "pf device is not open");
265 return -1;
267 r = 0;
268 memset(&pcr, 0, sizeof(pcr));
269 strlcpy(pcr.anchor, anchor_name, MAXPATHLEN);
271 #ifndef PF_NEWSTYLE
272 memset(&pp, 0, sizeof(pp));
273 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
274 if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0)
276 syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m");
277 r = -1;
279 else
281 pcr.pool_ticket = pp.ticket;
282 #else
283 if(1)
285 pcr.rule.direction = PF_IN;
286 /*pcr.rule.src.addr.type = PF_ADDR_NONE;*/
287 pcr.rule.src.addr.type = PF_ADDR_ADDRMASK;
288 pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
289 pcr.rule.nat.addr.type = PF_ADDR_NONE;
290 pcr.rule.rdr.addr.type = PF_ADDR_ADDRMASK;
291 #endif
293 #ifdef __APPLE__
294 pcr.rule.dst.xport.range.op = PF_OP_EQ;
295 pcr.rule.dst.xport.range.port[0] = htons(eport);
296 pcr.rule.dst.xport.range.port[1] = htons(eport);
297 #else
298 pcr.rule.dst.port_op = PF_OP_EQ;
299 pcr.rule.dst.port[0] = htons(eport);
300 pcr.rule.dst.port[1] = htons(eport);
301 #endif
302 #ifndef PF_NEWSTYLE
303 pcr.rule.action = PF_RDR;
304 #ifndef PF_ENABLE_FILTER_RULES
305 pcr.rule.natpass = 1;
306 #else
307 pcr.rule.natpass = 0;
308 #endif
309 #else
310 #ifndef PF_ENABLE_FILTER_RULES
311 pcr.rule.action = PF_PASS;
312 #else
313 pcr.rule.action = PF_MATCH;
314 #endif
315 #endif
316 pcr.rule.af = AF_INET;
317 #ifdef USE_IFNAME_IN_RULES
318 if(ifname)
319 strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ);
320 #endif
321 pcr.rule.proto = proto;
322 pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/
323 #ifdef PFRULE_HAS_RTABLEID
324 pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */
325 #endif
326 #ifdef PFRULE_HAS_ONRDOMAIN
327 pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */
328 #endif
329 pcr.rule.quick = 1;
330 pcr.rule.keep_state = PF_STATE_NORMAL;
331 if(tag)
332 strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE);
333 strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE);
334 if(rhost && rhost[0] != '\0' && rhost[0] != '*')
336 inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr);
337 pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
339 #ifndef PF_NEWSTYLE
340 pcr.rule.rpool.proxy_port[0] = iport;
341 pcr.rule.rpool.proxy_port[1] = iport;
342 TAILQ_INIT(&pcr.rule.rpool.list);
343 a = calloc(1, sizeof(struct pf_pooladdr));
344 inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr);
345 a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
346 TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries);
348 memcpy(&pp.addr, a, sizeof(struct pf_pooladdr));
349 if(ioctl(dev, DIOCADDADDR, &pp) < 0)
351 syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m");
352 r = -1;
354 else
356 #else
357 pcr.rule.rdr.proxy_port[0] = iport;
358 pcr.rule.rdr.proxy_port[1] = iport;
359 inet_pton(AF_INET, iaddr, &pcr.rule.rdr.addr.v.a.addr.v4.s_addr);
360 pcr.rule.rdr.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
361 if(1)
363 #endif
364 pcr.action = PF_CHANGE_GET_TICKET;
365 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
367 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
368 r = -1;
370 else
372 pcr.action = PF_CHANGE_ADD_TAIL;
373 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
375 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m");
376 r = -1;
380 #ifndef PF_NEWSTYLE
381 free(a);
382 #endif
384 if(r == 0 && timestamp > 0)
385 add_timestamp_entry(eport, proto, timestamp);
386 return r;
389 /* thanks to Seth Mos for this function */
391 add_filter_rule2(const char * ifname,
392 const char * rhost, const char * iaddr,
393 unsigned short eport, unsigned short iport,
394 int proto, const char * desc)
396 #ifndef PF_ENABLE_FILTER_RULES
397 UNUSED(ifname);
398 UNUSED(rhost); UNUSED(iaddr);
399 UNUSED(eport); UNUSED(iport);
400 UNUSED(proto); UNUSED(desc);
401 return 0;
402 #else
403 int r;
404 struct pfioc_rule pcr;
405 #ifndef PF_NEWSTYLE
406 struct pfioc_pooladdr pp;
407 #endif
408 #ifndef USE_IFNAME_IN_RULES
409 UNUSED(ifname);
410 #endif
411 UNUSED(eport);
412 if(dev<0) {
413 syslog(LOG_ERR, "pf device is not open");
414 return -1;
416 r = 0;
417 memset(&pcr, 0, sizeof(pcr));
418 strlcpy(pcr.anchor, anchor_name, MAXPATHLEN);
420 #ifndef PF_NEWSTYLE
421 memset(&pp, 0, sizeof(pp));
422 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
423 if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0)
425 syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m");
426 r = -1;
428 else
430 pcr.pool_ticket = pp.ticket;
431 #else
432 if(1)
434 #endif
435 pcr.rule.dst.port_op = PF_OP_EQ;
436 pcr.rule.dst.port[0] = htons(iport);
437 pcr.rule.direction = PF_IN;
438 pcr.rule.action = PF_PASS;
439 pcr.rule.af = AF_INET;
440 #ifdef USE_IFNAME_IN_RULES
441 if(ifname)
442 strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ);
443 #endif
444 pcr.rule.proto = proto;
445 pcr.rule.quick = (GETFLAG(PFNOQUICKRULESMASK))?0:1;
446 pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/
447 /* see the discussion on the forum :
448 * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */
449 pcr.rule.flags = TH_SYN;
450 pcr.rule.flagset = (TH_SYN|TH_ACK);
451 #ifdef PFRULE_HAS_RTABLEID
452 pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */
453 #endif
454 #ifdef PFRULE_HAS_ONRDOMAIN
455 pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */
456 #endif
457 pcr.rule.keep_state = 1;
458 strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE);
459 if(queue)
460 strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE);
461 if(tag)
462 strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE);
464 if(rhost && rhost[0] != '\0' && rhost[0] != '*')
466 inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr);
467 pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
469 /* we want any - iaddr port = # keep state label */
470 inet_pton(AF_INET, iaddr, &pcr.rule.dst.addr.v.a.addr.v4.s_addr);
471 pcr.rule.dst.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE);
472 #ifndef PF_NEWSTYLE
473 pcr.rule.rpool.proxy_port[0] = iport;
474 pcr.rule.rpool.proxy_port[1] = iport;
475 TAILQ_INIT(&pcr.rule.rpool.list);
476 #endif
477 if(1)
479 pcr.action = PF_CHANGE_GET_TICKET;
480 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
482 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
483 r = -1;
485 else
487 pcr.action = PF_CHANGE_ADD_TAIL;
488 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
490 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m");
491 r = -1;
496 return r;
497 #endif
500 /* get_redirect_rule()
501 * return value : 0 success (found)
502 * -1 = error or rule not found */
504 get_redirect_rule(const char * ifname, unsigned short eport, int proto,
505 char * iaddr, int iaddrlen, unsigned short * iport,
506 char * desc, int desclen,
507 char * rhost, int rhostlen,
508 unsigned int * timestamp,
509 u_int64_t * packets, u_int64_t * bytes)
511 int i, n;
512 struct pfioc_rule pr;
513 #ifndef PF_NEWSTYLE
514 struct pfioc_pooladdr pp;
515 #endif
516 UNUSED(ifname);
518 if(dev<0) {
519 syslog(LOG_ERR, "pf device is not open");
520 return -1;
522 memset(&pr, 0, sizeof(pr));
523 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
524 #ifndef PF_NEWSTYLE
525 pr.rule.action = PF_RDR;
526 #endif
527 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
529 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
530 goto error;
532 n = pr.nr;
533 for(i=0; i<n; i++)
535 pr.nr = i;
536 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
538 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
539 goto error;
541 #ifdef __APPLE__
542 if( (eport == ntohs(pr.rule.dst.xport.range.port[0]))
543 && (eport == ntohs(pr.rule.dst.xport.range.port[1]))
544 #else
545 if( (eport == ntohs(pr.rule.dst.port[0]))
546 && (eport == ntohs(pr.rule.dst.port[1]))
547 #endif
548 && (pr.rule.proto == proto) )
550 #ifndef PF_NEWSTYLE
551 *iport = pr.rule.rpool.proxy_port[0];
552 #else
553 *iport = pr.rule.rdr.proxy_port[0];
554 #endif
555 if(desc)
556 strlcpy(desc, pr.rule.label, desclen);
557 #ifdef PFRULE_INOUT_COUNTS
558 if(packets)
559 *packets = pr.rule.packets[0] + pr.rule.packets[1];
560 if(bytes)
561 *bytes = pr.rule.bytes[0] + pr.rule.bytes[1];
562 #else
563 if(packets)
564 *packets = pr.rule.packets;
565 if(bytes)
566 *bytes = pr.rule.bytes;
567 #endif
568 #ifndef PF_NEWSTYLE
569 memset(&pp, 0, sizeof(pp));
570 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
571 pp.r_action = PF_RDR;
572 pp.r_num = i;
573 pp.ticket = pr.ticket;
574 if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
576 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m");
577 goto error;
579 if(pp.nr != 1)
581 syslog(LOG_NOTICE, "No address associated with pf rule");
582 goto error;
584 pp.nr = 0; /* first */
585 if(ioctl(dev, DIOCGETADDR, &pp) < 0)
587 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m");
588 goto error;
590 inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr,
591 iaddr, iaddrlen);
592 #else
593 inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr,
594 iaddr, iaddrlen);
595 #endif
596 if(rhost && rhostlen > 0)
598 if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0)
600 rhost[0] = '\0'; /* empty string */
602 else
604 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr,
605 rhost, rhostlen);
608 if(timestamp)
609 *timestamp = get_timestamp(eport, proto);
610 return 0;
613 error:
614 return -1;
617 #define priv_delete_redirect_rule(ifname, eport, proto, iport, \
618 iaddr, rhost, rhostlen) \
619 priv_delete_redirect_rule_check_desc(ifname, eport, proto, iport, \
620 iaddr, rhost, rhostlen, 0, NULL)
621 /* if check_desc is true, only delete the rule if the description differs.
622 * returns : -1 : error / rule not found
623 * 0 : rule deleted
624 * 1 : rule untouched
626 static int
627 priv_delete_redirect_rule_check_desc(const char * ifname, unsigned short eport,
628 int proto, unsigned short * iport,
629 in_addr_t * iaddr, char * rhost, int rhostlen,
630 int check_desc, const char * desc)
632 int i, n;
633 struct pfioc_rule pr;
634 UNUSED(ifname);
636 if(dev<0) {
637 syslog(LOG_ERR, "pf device is not open");
638 return -1;
640 memset(&pr, 0, sizeof(pr));
641 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
642 #ifndef PF_NEWSTYLE
643 pr.rule.action = PF_RDR;
644 #endif
645 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
647 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
648 goto error;
650 n = pr.nr;
651 for(i=0; i<n; i++)
653 pr.nr = i;
654 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
656 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
657 goto error;
659 #ifdef __APPLE__
660 if( (eport == ntohs(pr.rule.dst.xport.range.port[0]))
661 && (eport == ntohs(pr.rule.dst.xport.range.port[1]))
662 #else
663 if( (eport == ntohs(pr.rule.dst.port[0]))
664 && (eport == ntohs(pr.rule.dst.port[1]))
665 #endif
666 && (pr.rule.proto == proto) )
668 /* retrieve iport in order to remove filter rule */
669 #ifndef PF_NEWSTYLE
670 if(iport) *iport = pr.rule.rpool.proxy_port[0];
671 if(iaddr)
673 /* retrieve internal address */
674 struct pfioc_pooladdr pp;
675 memset(&pp, 0, sizeof(pp));
676 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
677 pp.r_action = PF_RDR;
678 pp.r_num = i;
679 pp.ticket = pr.ticket;
680 if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
682 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m");
683 goto error;
685 if(pp.nr != 1)
687 syslog(LOG_NOTICE, "No address associated with pf rule");
688 goto error;
690 pp.nr = 0; /* first */
691 if(ioctl(dev, DIOCGETADDR, &pp) < 0)
693 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m");
694 goto error;
696 *iaddr = pp.addr.addr.v.a.addr.v4.s_addr;
698 #else
699 if(iport) *iport = pr.rule.rdr.proxy_port[0];
700 if(iaddr)
702 /* retrieve internal address */
703 *iaddr = pr.rule.rdr.addr.v.a.addr.v4.s_addr;
705 #endif
706 if(rhost && rhostlen > 0)
708 if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0)
709 rhost[0] = '\0'; /* empty string */
710 else
711 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr,
712 rhost, rhostlen);
714 if(check_desc) {
715 if((desc == NULL && pr.rule.label[0] == '\0') ||
716 (desc && 0 == strcmp(desc, pr.rule.label))) {
717 return 1;
720 pr.action = PF_CHANGE_GET_TICKET;
721 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
723 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
724 goto error;
726 pr.action = PF_CHANGE_REMOVE;
727 pr.nr = i;
728 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
730 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
731 goto error;
733 remove_timestamp_entry(eport, proto);
734 return 0;
737 error:
738 return -1;
742 delete_redirect_rule(const char * ifname, unsigned short eport,
743 int proto)
745 return priv_delete_redirect_rule(ifname, eport, proto, NULL, NULL, NULL, 0);
748 static int
749 priv_delete_filter_rule(const char * ifname, unsigned short iport,
750 int proto, in_addr_t iaddr)
752 #ifndef PF_ENABLE_FILTER_RULES
753 UNUSED(ifname); UNUSED(iport); UNUSED(proto); UNUSED(iaddr);
754 return 0;
755 #else
756 int i, n;
757 struct pfioc_rule pr;
758 UNUSED(ifname);
759 if(dev<0) {
760 syslog(LOG_ERR, "pf device is not open");
761 return -1;
763 memset(&pr, 0, sizeof(pr));
764 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
765 pr.rule.action = PF_PASS;
766 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
768 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
769 goto error;
771 n = pr.nr;
772 for(i=0; i<n; i++)
774 pr.nr = i;
775 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
777 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
778 goto error;
780 #ifdef TEST
781 syslog(LOG_DEBUG, "%2d port=%hu proto=%d addr=%8x",
782 i, ntohs(pr.rule.dst.port[0]), pr.rule.proto,
783 pr.rule.dst.addr.v.a.addr.v4.s_addr);
784 /*pr.rule.dst.addr.v.a.mask.v4.s_addr*/
785 #endif
786 if( (iport == ntohs(pr.rule.dst.port[0]))
787 && (pr.rule.proto == proto) &&
788 (iaddr == pr.rule.dst.addr.v.a.addr.v4.s_addr)
791 pr.action = PF_CHANGE_GET_TICKET;
792 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
794 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
795 goto error;
797 pr.action = PF_CHANGE_REMOVE;
798 pr.nr = i;
799 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0)
801 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
802 goto error;
804 return 0;
807 error:
808 return -1;
809 #endif
813 delete_redirect_and_filter_rules(const char * ifname, unsigned short eport,
814 int proto)
816 int r;
817 unsigned short iport;
818 in_addr_t iaddr;
819 r = priv_delete_redirect_rule(ifname, eport, proto, &iport, &iaddr, NULL, 0);
820 if(r == 0)
822 r = priv_delete_filter_rule(ifname, iport, proto, iaddr);
824 return r;
828 get_redirect_rule_by_index(int index,
829 char * ifname, unsigned short * eport,
830 char * iaddr, int iaddrlen, unsigned short * iport,
831 int * proto, char * desc, int desclen,
832 char * rhost, int rhostlen,
833 unsigned int * timestamp,
834 u_int64_t * packets, u_int64_t * bytes)
836 int n;
837 struct pfioc_rule pr;
838 #ifndef PF_NEWSTYLE
839 struct pfioc_pooladdr pp;
840 #endif
841 if(index < 0)
842 return -1;
843 if(dev<0) {
844 syslog(LOG_ERR, "pf device is not open");
845 return -1;
847 memset(&pr, 0, sizeof(pr));
848 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
849 #ifndef PF_NEWSTYLE
850 pr.rule.action = PF_RDR;
851 #endif
852 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
854 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
855 goto error;
857 n = pr.nr;
858 if(index >= n)
859 goto error;
860 pr.nr = index;
861 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
863 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
864 goto error;
866 *proto = pr.rule.proto;
867 #ifdef __APPLE__
868 *eport = ntohs(pr.rule.dst.xport.range.port[0]);
869 #else
870 *eport = ntohs(pr.rule.dst.port[0]);
871 #endif
872 #ifndef PF_NEWSTYLE
873 *iport = pr.rule.rpool.proxy_port[0];
874 #else
875 *iport = pr.rule.rdr.proxy_port[0];
876 #endif
877 if(ifname)
878 strlcpy(ifname, pr.rule.ifname, IFNAMSIZ);
879 if(desc)
880 strlcpy(desc, pr.rule.label, desclen);
881 #ifdef PFRULE_INOUT_COUNTS
882 if(packets)
883 *packets = pr.rule.packets[0] + pr.rule.packets[1];
884 if(bytes)
885 *bytes = pr.rule.bytes[0] + pr.rule.bytes[1];
886 #else
887 if(packets)
888 *packets = pr.rule.packets;
889 if(bytes)
890 *bytes = pr.rule.bytes;
891 #endif
892 #ifndef PF_NEWSTYLE
893 memset(&pp, 0, sizeof(pp));
894 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
895 pp.r_action = PF_RDR;
896 pp.r_num = index;
897 pp.ticket = pr.ticket;
898 if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
900 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m");
901 goto error;
903 if(pp.nr != 1)
905 syslog(LOG_NOTICE, "No address associated with pf rule");
906 goto error;
908 pp.nr = 0; /* first */
909 if(ioctl(dev, DIOCGETADDR, &pp) < 0)
911 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m");
912 goto error;
914 inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr,
915 iaddr, iaddrlen);
916 #else
917 inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr,
918 iaddr, iaddrlen);
919 #endif
920 if(rhost && rhostlen > 0)
922 if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0)
924 rhost[0] = '\0'; /* empty string */
926 else
928 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr,
929 rhost, rhostlen);
932 if(timestamp)
933 *timestamp = get_timestamp(*eport, *proto);
934 return 0;
935 error:
936 return -1;
939 /* return an (malloc'ed) array of "external" port for which there is
940 * a port mapping. number is the size of the array */
941 unsigned short *
942 get_portmappings_in_range(unsigned short startport, unsigned short endport,
943 int proto, unsigned int * number)
945 unsigned short * array;
946 unsigned int capacity;
947 int i, n;
948 unsigned short eport;
949 struct pfioc_rule pr;
951 *number = 0;
952 if(dev<0) {
953 syslog(LOG_ERR, "pf device is not open");
954 return NULL;
956 capacity = 128;
957 array = calloc(capacity, sizeof(unsigned short));
958 if(!array)
960 syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
961 return NULL;
963 memset(&pr, 0, sizeof(pr));
964 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
965 #ifndef PF_NEWSTYLE
966 pr.rule.action = PF_RDR;
967 #endif
968 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
970 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
971 free(array);
972 return NULL;
974 n = pr.nr;
975 for(i=0; i<n; i++)
977 pr.nr = i;
978 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
980 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
981 continue;
983 #ifdef __APPLE__
984 eport = ntohs(pr.rule.dst.xport.range.port[0]);
985 if( (eport == ntohs(pr.rule.dst.xport.range.port[1]))
986 #else
987 eport = ntohs(pr.rule.dst.port[0]);
988 if( (eport == ntohs(pr.rule.dst.port[1]))
989 #endif
990 && (pr.rule.proto == proto)
991 && (startport <= eport) && (eport <= endport) )
993 if(*number >= capacity)
995 /* need to increase the capacity of the array */
996 unsigned short * tmp;
997 capacity += 128;
998 tmp = realloc(array, sizeof(unsigned short)*capacity);
999 if(!tmp)
1001 syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity);
1002 *number = 0;
1003 free(array);
1004 return NULL;
1006 array = tmp;
1008 array[*number] = eport;
1009 (*number)++;
1012 return array;
1015 /* update the port mapping internal port, decription and timestamp */
1017 update_portmapping(const char * ifname, unsigned short eport, int proto,
1018 unsigned short iport, const char * desc,
1019 unsigned int timestamp)
1021 unsigned short old_iport;
1022 in_addr_t iaddr;
1023 char iaddr_str[16];
1024 char rhost[32];
1026 if(priv_delete_redirect_rule(ifname, eport, proto, &old_iport, &iaddr, rhost, sizeof(rhost)) < 0)
1027 return -1;
1028 if (priv_delete_filter_rule(ifname, old_iport, proto, iaddr) < 0)
1029 return -1;
1031 inet_ntop(AF_INET, &iaddr, iaddr_str, sizeof(iaddr_str));
1033 if(add_redirect_rule2(ifname, rhost, eport, iaddr_str, iport, proto,
1034 desc, timestamp) < 0)
1035 return -1;
1036 if(add_filter_rule2(ifname, rhost, iaddr_str, eport, iport, proto, desc) < 0)
1037 return -1;
1039 return 0;
1042 /* update the port mapping decription and timestamp */
1044 update_portmapping_desc_timestamp(const char * ifname,
1045 unsigned short eport, int proto,
1046 const char * desc, unsigned int timestamp)
1048 unsigned short iport;
1049 in_addr_t iaddr;
1050 char iaddr_str[16];
1051 char rhost[32];
1052 int r;
1054 r = priv_delete_redirect_rule_check_desc(ifname, eport, proto, &iport, &iaddr, rhost, sizeof(rhost), 1, desc);
1055 if(r < 0)
1056 return -1;
1057 if(r == 1) {
1058 /* only change timestamp */
1059 remove_timestamp_entry(eport, proto);
1060 add_timestamp_entry(eport, proto, timestamp);
1061 return 0;
1063 if (priv_delete_filter_rule(ifname, iport, proto, iaddr) < 0)
1064 return -1;
1066 inet_ntop(AF_INET, &iaddr, iaddr_str, sizeof(iaddr_str));
1068 if(add_redirect_rule2(ifname, rhost, eport, iaddr_str, iport, proto,
1069 desc, timestamp) < 0)
1070 return -1;
1071 if(add_filter_rule2(ifname, rhost, iaddr_str, eport, iport, proto, desc) < 0)
1072 return -1;
1074 return 0;
1078 /* this function is only for testing */
1079 #if TEST
1080 void
1081 list_rules(void)
1083 char buf[32];
1084 int i, n;
1085 struct pfioc_rule pr;
1086 #ifndef PF_NEWSTYLE
1087 struct pfioc_pooladdr pp;
1088 #endif
1090 if(dev<0)
1092 perror("pf dev not open");
1093 return ;
1095 memset(&pr, 0, sizeof(pr));
1096 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
1097 pr.rule.action = PF_RDR;
1098 if(ioctl(dev, DIOCGETRULES, &pr) < 0)
1099 perror("DIOCGETRULES");
1100 printf("ticket = %d, nr = %d\n", pr.ticket, pr.nr);
1101 n = pr.nr;
1102 for(i=0; i<n; i++)
1104 printf("-- rule %d --\n", i);
1105 pr.nr = i;
1106 if(ioctl(dev, DIOCGETRULE, &pr) < 0)
1107 perror("DIOCGETRULE");
1108 printf(" %s %s %d:%d -> %d:%d proto %d keep_state=%d action=%d\n",
1109 pr.rule.ifname,
1110 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, buf, 32),
1111 (int)ntohs(pr.rule.dst.port[0]),
1112 (int)ntohs(pr.rule.dst.port[1]),
1113 #ifndef PF_NEWSTYLE
1114 (int)pr.rule.rpool.proxy_port[0],
1115 (int)pr.rule.rpool.proxy_port[1],
1116 #else
1117 (int)pr.rule.rdr.proxy_port[0],
1118 (int)pr.rule.rdr.proxy_port[1],
1119 #endif
1120 (int)pr.rule.proto,
1121 (int)pr.rule.keep_state,
1122 (int)pr.rule.action);
1123 printf(" description: \"%s\"\n", pr.rule.label);
1124 #ifndef PF_NEWSTYLE
1125 memset(&pp, 0, sizeof(pp));
1126 strlcpy(pp.anchor, anchor_name, MAXPATHLEN);
1127 pp.r_action = PF_RDR;
1128 pp.r_num = i;
1129 pp.ticket = pr.ticket;
1130 if(ioctl(dev, DIOCGETADDRS, &pp) < 0)
1131 perror("DIOCGETADDRS");
1132 printf(" nb pool addr = %d ticket=%d\n", pp.nr, pp.ticket);
1133 /*if(ioctl(dev, DIOCGETRULE, &pr) < 0)
1134 perror("DIOCGETRULE"); */
1135 pp.nr = 0; /* first */
1136 if(ioctl(dev, DIOCGETADDR, &pp) < 0)
1137 perror("DIOCGETADDR");
1138 /* addr.v.a.addr.v4.s_addr */
1139 printf(" %s\n", inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, buf, 32));
1140 #else
1141 printf(" rule_flag=%08x action=%d direction=%d log=%d logif=%d "
1142 "quick=%d ifnot=%d af=%d type=%d code=%d rdr.port_op=%d rdr.opts=%d\n",
1143 pr.rule.rule_flag, pr.rule.action, pr.rule.direction,
1144 pr.rule.log, pr.rule.logif, pr.rule.quick, pr.rule.ifnot,
1145 pr.rule.af, pr.rule.type, pr.rule.code,
1146 pr.rule.rdr.port_op, pr.rule.rdr.opts);
1147 printf(" %s\n", inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, buf, 32));
1148 #endif
1151 #endif /* TEST */
1153 #endif /* USE_PF */