1 /* $Id: obsdrdr.c,v 1.87 2016/02/16 12:14:28 nanard Exp $ */
2 /* vim: tabstop=4 shiftwidth=4 noexpandtab
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 */
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
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
30 * - PF_ENABLE_FILTER_RULES
31 * If set, two rules are created : rdr + pass. Else a rdr/pass rule
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.
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>
47 #include <netinet/in.h>
48 #include <netinet/tcp.h>
49 #include <arpa/inet.h>
51 #include <net/pf/pfvar.h>
56 #include <net/pfvar.h>
59 #include <sys/ioctl.h>
66 #include "../macros.h"
67 #include "../config.h"
69 #include "../upnpglobalvars.h"
72 #error "USE_PF macro is undefined, check consistency between config.h and Makefile"
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
;
83 static struct timestamp_entry
* timestamp_list
= NULL
;
86 get_timestamp(unsigned short eport
, int proto
)
88 struct timestamp_entry
* e
;
91 if(e
->eport
== eport
&& e
->protocol
== (short)proto
)
99 remove_timestamp_entry(unsigned short eport
, int proto
)
101 struct timestamp_entry
* e
;
102 struct timestamp_entry
* * p
;
106 if(e
->eport
== eport
&& e
->protocol
== (short)proto
) {
107 /* remove the entry */
118 add_timestamp_entry(unsigned short eport
, int proto
, unsigned timestamp
)
120 struct timestamp_entry
* tmp
;
121 tmp
= malloc(sizeof(struct timestamp_entry
));
124 tmp
->next
= timestamp_list
;
125 tmp
->timestamp
= timestamp
;
127 tmp
->protocol
= (short)proto
;
128 timestamp_list
= tmp
;
132 syslog(LOG_ERR
, "add_timestamp_entry() malloc(%lu) error",
133 sizeof(struct timestamp_entry
));
137 /* /dev/pf when opened */
140 /* shutdown_redirect() :
141 * close the /dev/pf device */
143 shutdown_redirect(void)
146 syslog(LOG_ERR
, "close(\"/dev/pf\"): %m");
150 /* open the device */
154 struct pf_status status
;
157 dev
= open("/dev/pf", O_RDWR
);
159 syslog(LOG_ERR
, "open(\"/dev/pf\"): %m");
162 if(ioctl(dev
, DIOCGETSTATUS
, &status
)<0) {
163 syslog(LOG_ERR
, "DIOCGETSTATUS: %m");
166 if(!status
.running
) {
167 syslog(LOG_ERR
, "pf is disabled");
176 clear_redirect_rules(void)
178 struct pfioc_trans io
;
179 struct pfioc_trans_e ioe
;
181 syslog(LOG_ERR
, "pf device is not open");
184 memset(&ioe
, 0, sizeof(ioe
));
186 io
.esize
= sizeof(ioe
);
189 ioe
.rs_num
= PF_RULESET_RDR
;
191 ioe
.type
= PF_TRANS_RULESET
;
193 strlcpy(ioe
.anchor
, anchor_name
, MAXPATHLEN
);
194 if(ioctl(dev
, DIOCXBEGIN
, &io
) < 0)
196 syslog(LOG_ERR
, "ioctl(dev, DIOCXBEGIN, ...): %m");
199 if(ioctl(dev
, DIOCXCOMMIT
, &io
) < 0)
201 syslog(LOG_ERR
, "ioctl(dev, DIOCXCOMMIT, ...): %m");
210 clear_filter_rules(void)
212 #ifndef PF_ENABLE_FILTER_RULES
215 struct pfioc_trans io
;
216 struct pfioc_trans_e ioe
;
218 syslog(LOG_ERR
, "pf device is not open");
221 memset(&ioe
, 0, sizeof(ioe
));
223 io
.esize
= sizeof(ioe
);
226 ioe
.rs_num
= PF_RULESET_FILTER
;
229 ioe
.type
= PF_TRANS_RULESET
;
231 strlcpy(ioe
.anchor
, anchor_name
, MAXPATHLEN
);
232 if(ioctl(dev
, DIOCXBEGIN
, &io
) < 0)
234 syslog(LOG_ERR
, "ioctl(dev, DIOCXBEGIN, ...): %m");
237 if(ioctl(dev
, DIOCXCOMMIT
, &io
) < 0)
239 syslog(LOG_ERR
, "ioctl(dev, DIOCXCOMMIT, ...): %m");
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
)
258 struct pfioc_rule pcr
;
260 struct pfioc_pooladdr pp
;
261 struct pf_pooladdr
*a
;
264 syslog(LOG_ERR
, "pf device is not open");
268 memset(&pcr
, 0, sizeof(pcr
));
269 strlcpy(pcr
.anchor
, anchor_name
, MAXPATHLEN
);
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");
281 pcr
.pool_ticket
= pp
.ticket
;
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
;
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
);
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
);
303 pcr
.rule
.action
= PF_RDR
;
304 #ifndef PF_ENABLE_FILTER_RULES
305 pcr
.rule
.natpass
= 1;
307 pcr
.rule
.natpass
= 0;
310 #ifndef PF_ENABLE_FILTER_RULES
311 pcr
.rule
.action
= PF_PASS
;
313 pcr
.rule
.action
= PF_MATCH
;
316 pcr
.rule
.af
= AF_INET
;
317 #ifdef USE_IFNAME_IN_RULES
319 strlcpy(pcr
.rule
.ifname
, ifname
, IFNAMSIZ
);
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 */
326 #ifdef PFRULE_HAS_ONRDOMAIN
327 pcr
.rule
.onrdomain
= -1; /* first appeared in OpenBSD 5.0 */
330 pcr
.rule
.keep_state
= PF_STATE_NORMAL
;
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
);
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");
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
);
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");
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");
384 if(r
== 0 && timestamp
> 0)
385 add_timestamp_entry(eport
, proto
, timestamp
);
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
398 UNUSED(rhost
); UNUSED(iaddr
);
399 UNUSED(eport
); UNUSED(iport
);
400 UNUSED(proto
); UNUSED(desc
);
404 struct pfioc_rule pcr
;
406 struct pfioc_pooladdr pp
;
408 #ifndef USE_IFNAME_IN_RULES
413 syslog(LOG_ERR
, "pf device is not open");
417 memset(&pcr
, 0, sizeof(pcr
));
418 strlcpy(pcr
.anchor
, anchor_name
, MAXPATHLEN
);
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");
430 pcr
.pool_ticket
= pp
.ticket
;
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
442 strlcpy(pcr
.rule
.ifname
, ifname
, IFNAMSIZ
);
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 */
454 #ifdef PFRULE_HAS_ONRDOMAIN
455 pcr
.rule
.onrdomain
= -1; /* first appeared in OpenBSD 5.0 */
457 pcr
.rule
.keep_state
= 1;
458 strlcpy(pcr
.rule
.label
, desc
, PF_RULE_LABEL_SIZE
);
460 strlcpy(pcr
.rule
.qname
, queue
, PF_QNAME_SIZE
);
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
);
473 pcr
.rule
.rpool
.proxy_port
[0] = iport
;
474 pcr
.rule
.rpool
.proxy_port
[1] = iport
;
475 TAILQ_INIT(&pcr
.rule
.rpool
.list
);
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");
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");
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
)
512 struct pfioc_rule pr
;
514 struct pfioc_pooladdr pp
;
519 syslog(LOG_ERR
, "pf device is not open");
522 memset(&pr
, 0, sizeof(pr
));
523 strlcpy(pr
.anchor
, anchor_name
, MAXPATHLEN
);
525 pr
.rule
.action
= PF_RDR
;
527 if(ioctl(dev
, DIOCGETRULES
, &pr
) < 0)
529 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULES, ...): %m");
536 if(ioctl(dev
, DIOCGETRULE
, &pr
) < 0)
538 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULE): %m");
542 if( (eport
== ntohs(pr
.rule
.dst
.xport
.range
.port
[0]))
543 && (eport
== ntohs(pr
.rule
.dst
.xport
.range
.port
[1]))
545 if( (eport
== ntohs(pr
.rule
.dst
.port
[0]))
546 && (eport
== ntohs(pr
.rule
.dst
.port
[1]))
548 && (pr
.rule
.proto
== proto
) )
551 *iport
= pr
.rule
.rpool
.proxy_port
[0];
553 *iport
= pr
.rule
.rdr
.proxy_port
[0];
556 strlcpy(desc
, pr
.rule
.label
, desclen
);
557 #ifdef PFRULE_INOUT_COUNTS
559 *packets
= pr
.rule
.packets
[0] + pr
.rule
.packets
[1];
561 *bytes
= pr
.rule
.bytes
[0] + pr
.rule
.bytes
[1];
564 *packets
= pr
.rule
.packets
;
566 *bytes
= pr
.rule
.bytes
;
569 memset(&pp
, 0, sizeof(pp
));
570 strlcpy(pp
.anchor
, anchor_name
, MAXPATHLEN
);
571 pp
.r_action
= PF_RDR
;
573 pp
.ticket
= pr
.ticket
;
574 if(ioctl(dev
, DIOCGETADDRS
, &pp
) < 0)
576 syslog(LOG_ERR
, "ioctl(dev, DIOCGETADDRS, ...): %m");
581 syslog(LOG_NOTICE
, "No address associated with pf rule");
584 pp
.nr
= 0; /* first */
585 if(ioctl(dev
, DIOCGETADDR
, &pp
) < 0)
587 syslog(LOG_ERR
, "ioctl(dev, DIOCGETADDR, ...): %m");
590 inet_ntop(AF_INET
, &pp
.addr
.addr
.v
.a
.addr
.v4
.s_addr
,
593 inet_ntop(AF_INET
, &pr
.rule
.rdr
.addr
.v
.a
.addr
.v4
.s_addr
,
596 if(rhost
&& rhostlen
> 0)
598 if (pr
.rule
.src
.addr
.v
.a
.addr
.v4
.s_addr
== 0)
600 rhost
[0] = '\0'; /* empty string */
604 inet_ntop(AF_INET
, &pr
.rule
.src
.addr
.v
.a
.addr
.v4
.s_addr
,
609 *timestamp
= get_timestamp(eport
, proto
);
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
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
)
633 struct pfioc_rule pr
;
637 syslog(LOG_ERR
, "pf device is not open");
640 memset(&pr
, 0, sizeof(pr
));
641 strlcpy(pr
.anchor
, anchor_name
, MAXPATHLEN
);
643 pr
.rule
.action
= PF_RDR
;
645 if(ioctl(dev
, DIOCGETRULES
, &pr
) < 0)
647 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULES, ...): %m");
654 if(ioctl(dev
, DIOCGETRULE
, &pr
) < 0)
656 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULE): %m");
660 if( (eport
== ntohs(pr
.rule
.dst
.xport
.range
.port
[0]))
661 && (eport
== ntohs(pr
.rule
.dst
.xport
.range
.port
[1]))
663 if( (eport
== ntohs(pr
.rule
.dst
.port
[0]))
664 && (eport
== ntohs(pr
.rule
.dst
.port
[1]))
666 && (pr
.rule
.proto
== proto
) )
668 /* retrieve iport in order to remove filter rule */
670 if(iport
) *iport
= pr
.rule
.rpool
.proxy_port
[0];
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
;
679 pp
.ticket
= pr
.ticket
;
680 if(ioctl(dev
, DIOCGETADDRS
, &pp
) < 0)
682 syslog(LOG_ERR
, "ioctl(dev, DIOCGETADDRS, ...): %m");
687 syslog(LOG_NOTICE
, "No address associated with pf rule");
690 pp
.nr
= 0; /* first */
691 if(ioctl(dev
, DIOCGETADDR
, &pp
) < 0)
693 syslog(LOG_ERR
, "ioctl(dev, DIOCGETADDR, ...): %m");
696 *iaddr
= pp
.addr
.addr
.v
.a
.addr
.v4
.s_addr
;
699 if(iport
) *iport
= pr
.rule
.rdr
.proxy_port
[0];
702 /* retrieve internal address */
703 *iaddr
= pr
.rule
.rdr
.addr
.v
.a
.addr
.v4
.s_addr
;
706 if(rhost
&& rhostlen
> 0)
708 if (pr
.rule
.src
.addr
.v
.a
.addr
.v4
.s_addr
== 0)
709 rhost
[0] = '\0'; /* empty string */
711 inet_ntop(AF_INET
, &pr
.rule
.src
.addr
.v
.a
.addr
.v4
.s_addr
,
715 if((desc
== NULL
&& pr
.rule
.label
[0] == '\0') ||
716 (desc
&& 0 == strcmp(desc
, pr
.rule
.label
))) {
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");
726 pr
.action
= PF_CHANGE_REMOVE
;
728 if(ioctl(dev
, DIOCCHANGERULE
, &pr
) < 0)
730 syslog(LOG_ERR
, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
733 remove_timestamp_entry(eport
, proto
);
742 delete_redirect_rule(const char * ifname
, unsigned short eport
,
745 return priv_delete_redirect_rule(ifname
, eport
, proto
, NULL
, NULL
, NULL
, 0);
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
);
757 struct pfioc_rule pr
;
760 syslog(LOG_ERR
, "pf device is not open");
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");
775 if(ioctl(dev
, DIOCGETRULE
, &pr
) < 0)
777 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULE): %m");
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*/
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");
797 pr
.action
= PF_CHANGE_REMOVE
;
799 if(ioctl(dev
, DIOCCHANGERULE
, &pr
) < 0)
801 syslog(LOG_ERR
, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
813 delete_redirect_and_filter_rules(const char * ifname
, unsigned short eport
,
817 unsigned short iport
;
819 r
= priv_delete_redirect_rule(ifname
, eport
, proto
, &iport
, &iaddr
, NULL
, 0);
822 r
= priv_delete_filter_rule(ifname
, iport
, proto
, iaddr
);
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
)
837 struct pfioc_rule pr
;
839 struct pfioc_pooladdr pp
;
844 syslog(LOG_ERR
, "pf device is not open");
847 memset(&pr
, 0, sizeof(pr
));
848 strlcpy(pr
.anchor
, anchor_name
, MAXPATHLEN
);
850 pr
.rule
.action
= PF_RDR
;
852 if(ioctl(dev
, DIOCGETRULES
, &pr
) < 0)
854 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULES, ...): %m");
861 if(ioctl(dev
, DIOCGETRULE
, &pr
) < 0)
863 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULE): %m");
866 *proto
= pr
.rule
.proto
;
868 *eport
= ntohs(pr
.rule
.dst
.xport
.range
.port
[0]);
870 *eport
= ntohs(pr
.rule
.dst
.port
[0]);
873 *iport
= pr
.rule
.rpool
.proxy_port
[0];
875 *iport
= pr
.rule
.rdr
.proxy_port
[0];
878 strlcpy(ifname
, pr
.rule
.ifname
, IFNAMSIZ
);
880 strlcpy(desc
, pr
.rule
.label
, desclen
);
881 #ifdef PFRULE_INOUT_COUNTS
883 *packets
= pr
.rule
.packets
[0] + pr
.rule
.packets
[1];
885 *bytes
= pr
.rule
.bytes
[0] + pr
.rule
.bytes
[1];
888 *packets
= pr
.rule
.packets
;
890 *bytes
= pr
.rule
.bytes
;
893 memset(&pp
, 0, sizeof(pp
));
894 strlcpy(pp
.anchor
, anchor_name
, MAXPATHLEN
);
895 pp
.r_action
= PF_RDR
;
897 pp
.ticket
= pr
.ticket
;
898 if(ioctl(dev
, DIOCGETADDRS
, &pp
) < 0)
900 syslog(LOG_ERR
, "ioctl(dev, DIOCGETADDRS, ...): %m");
905 syslog(LOG_NOTICE
, "No address associated with pf rule");
908 pp
.nr
= 0; /* first */
909 if(ioctl(dev
, DIOCGETADDR
, &pp
) < 0)
911 syslog(LOG_ERR
, "ioctl(dev, DIOCGETADDR, ...): %m");
914 inet_ntop(AF_INET
, &pp
.addr
.addr
.v
.a
.addr
.v4
.s_addr
,
917 inet_ntop(AF_INET
, &pr
.rule
.rdr
.addr
.v
.a
.addr
.v4
.s_addr
,
920 if(rhost
&& rhostlen
> 0)
922 if (pr
.rule
.src
.addr
.v
.a
.addr
.v4
.s_addr
== 0)
924 rhost
[0] = '\0'; /* empty string */
928 inet_ntop(AF_INET
, &pr
.rule
.src
.addr
.v
.a
.addr
.v4
.s_addr
,
933 *timestamp
= get_timestamp(*eport
, *proto
);
939 /* return an (malloc'ed) array of "external" port for which there is
940 * a port mapping. number is the size of the array */
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
;
948 unsigned short eport
;
949 struct pfioc_rule pr
;
953 syslog(LOG_ERR
, "pf device is not open");
957 array
= calloc(capacity
, sizeof(unsigned short));
960 syslog(LOG_ERR
, "get_portmappings_in_range() : calloc error");
963 memset(&pr
, 0, sizeof(pr
));
964 strlcpy(pr
.anchor
, anchor_name
, MAXPATHLEN
);
966 pr
.rule
.action
= PF_RDR
;
968 if(ioctl(dev
, DIOCGETRULES
, &pr
) < 0)
970 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULES, ...): %m");
978 if(ioctl(dev
, DIOCGETRULE
, &pr
) < 0)
980 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULE): %m");
984 eport
= ntohs(pr
.rule
.dst
.xport
.range
.port
[0]);
985 if( (eport
== ntohs(pr
.rule
.dst
.xport
.range
.port
[1]))
987 eport
= ntohs(pr
.rule
.dst
.port
[0]);
988 if( (eport
== ntohs(pr
.rule
.dst
.port
[1]))
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
;
998 tmp
= realloc(array
, sizeof(unsigned short)*capacity
);
1001 syslog(LOG_ERR
, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity
);
1008 array
[*number
] = eport
;
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
;
1026 if(priv_delete_redirect_rule(ifname
, eport
, proto
, &old_iport
, &iaddr
, rhost
, sizeof(rhost
)) < 0)
1028 if (priv_delete_filter_rule(ifname
, old_iport
, proto
, iaddr
) < 0)
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)
1036 if(add_filter_rule2(ifname
, rhost
, iaddr_str
, eport
, iport
, proto
, desc
) < 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
;
1054 r
= priv_delete_redirect_rule_check_desc(ifname
, eport
, proto
, &iport
, &iaddr
, rhost
, sizeof(rhost
), 1, desc
);
1058 /* only change timestamp */
1059 remove_timestamp_entry(eport
, proto
);
1060 add_timestamp_entry(eport
, proto
, timestamp
);
1063 if (priv_delete_filter_rule(ifname
, iport
, proto
, iaddr
) < 0)
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)
1071 if(add_filter_rule2(ifname
, rhost
, iaddr_str
, eport
, iport
, proto
, desc
) < 0)
1078 /* this function is only for testing */
1085 struct pfioc_rule pr
;
1087 struct pfioc_pooladdr pp
;
1092 perror("pf dev not open");
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
);
1104 printf("-- rule %d --\n", 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",
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]),
1114 (int)pr
.rule
.rpool
.proxy_port
[0],
1115 (int)pr
.rule
.rpool
.proxy_port
[1],
1117 (int)pr
.rule
.rdr
.proxy_port
[0],
1118 (int)pr
.rule
.rdr
.proxy_port
[1],
1121 (int)pr
.rule
.keep_state
,
1122 (int)pr
.rule
.action
);
1123 printf(" description: \"%s\"\n", pr
.rule
.label
);
1125 memset(&pp
, 0, sizeof(pp
));
1126 strlcpy(pp
.anchor
, anchor_name
, MAXPATHLEN
);
1127 pp
.r_action
= PF_RDR
;
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));
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));