1 /* $Id: pfpinhole.c,v 1.24 2014/12/05 09:54:55 nanard Exp $ */
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2012 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
9 #include <sys/socket.h>
10 #include <sys/param.h>
12 #include <netinet/in.h>
13 #include <netinet/tcp.h>
14 #include <arpa/inet.h>
16 #include <net/pf/pfvar.h>
21 #include <net/pfvar.h>
24 #include <sys/ioctl.h>
31 #include "../config.h"
32 #include "pfpinhole.h"
33 #include "../upnpglobalvars.h"
34 #include "../macros.h"
36 /* the pass rules created by add_pinhole() are as follow :
38 * pass in quick on ep0 inet6 proto udp
39 * from any to dead:beef::42:42 port = 8080
40 * flags S/SA keep state
41 * label "pinhole-2 ts-4321000"
43 * with the label "pinhole-$uid ts-$timestamp"
46 #ifdef ENABLE_UPNPPINHOLE
48 /* /dev/pf when opened */
51 static int next_uid
= 1;
53 #define PINEHOLE_LABEL_FORMAT "pinhole-%d ts-%u: %s"
54 #define PINEHOLE_LABEL_FORMAT_SKIPDESC "pinhole-%d ts-%u: %*s"
56 int add_pinhole(const char * ifname
,
57 const char * rem_host
, unsigned short rem_port
,
58 const char * int_client
, unsigned short int_port
,
59 int proto
, const char * desc
, unsigned int timestamp
)
62 struct pfioc_rule pcr
;
64 struct pfioc_pooladdr pp
;
68 syslog(LOG_ERR
, "pf device is not open");
71 memset(&pcr
, 0, sizeof(pcr
));
72 strlcpy(pcr
.anchor
, anchor_name
, MAXPATHLEN
);
75 memset(&pp
, 0, sizeof(pp
));
76 strlcpy(pp
.anchor
, anchor_name
, MAXPATHLEN
);
77 if(ioctl(dev
, DIOCBEGINADDRS
, &pp
) < 0) {
78 syslog(LOG_ERR
, "ioctl(dev, DIOCBEGINADDRS, ...): %m");
81 pcr
.pool_ticket
= pp
.ticket
;
85 pcr
.rule
.direction
= PF_IN
;
86 pcr
.rule
.action
= PF_PASS
;
87 pcr
.rule
.af
= AF_INET6
;
89 pcr
.rule
.nat
.addr
.type
= PF_ADDR_NONE
;
90 pcr
.rule
.rdr
.addr
.type
= PF_ADDR_NONE
;
92 #ifdef USE_IFNAME_IN_RULES
94 strlcpy(pcr
.rule
.ifname
, ifname
, IFNAMSIZ
);
96 pcr
.rule
.proto
= proto
;
98 pcr
.rule
.quick
= 1;/*(GETFLAG(PFNOQUICKRULESMASK))?0:1;*/
99 pcr
.rule
.log
= (GETFLAG(LOGPACKETSMASK
))?1:0; /*logpackets;*/
100 /* see the discussion on the forum :
101 * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */
102 pcr
.rule
.flags
= TH_SYN
;
103 pcr
.rule
.flagset
= (TH_SYN
|TH_ACK
);
104 #ifdef PFRULE_HAS_RTABLEID
105 pcr
.rule
.rtableid
= -1; /* first appeared in OpenBSD 4.0 */
107 #ifdef PFRULE_HAS_ONRDOMAIN
108 pcr
.rule
.onrdomain
= -1; /* first appeared in OpenBSD 5.0 */
110 pcr
.rule
.keep_state
= 1;
112 snprintf(pcr
.rule
.label
, PF_RULE_LABEL_SIZE
,
113 PINEHOLE_LABEL_FORMAT
, uid
, timestamp
, desc
);
115 strlcpy(pcr
.rule
.qname
, queue
, PF_QNAME_SIZE
);
117 strlcpy(pcr
.rule
.tagname
, tag
, PF_TAG_NAME_SIZE
);
120 pcr
.rule
.src
.port_op
= PF_OP_EQ
;
121 pcr
.rule
.src
.port
[0] = htons(rem_port
);
123 if(rem_host
&& rem_host
[0] != '\0' && rem_host
[0] != '*') {
124 pcr
.rule
.src
.addr
.type
= PF_ADDR_ADDRMASK
;
125 if(inet_pton(AF_INET6
, rem_host
, &pcr
.rule
.src
.addr
.v
.a
.addr
.v6
) != 1) {
126 syslog(LOG_ERR
, "inet_pton(%s) failed", rem_host
);
128 memset(&pcr
.rule
.src
.addr
.v
.a
.mask
.addr8
, 255, 16);
131 pcr
.rule
.dst
.port_op
= PF_OP_EQ
;
132 pcr
.rule
.dst
.port
[0] = htons(int_port
);
133 pcr
.rule
.dst
.addr
.type
= PF_ADDR_ADDRMASK
;
134 if(inet_pton(AF_INET6
, int_client
, &pcr
.rule
.dst
.addr
.v
.a
.addr
.v6
) != 1) {
135 syslog(LOG_ERR
, "inet_pton(%s) failed", int_client
);
137 memset(&pcr
.rule
.dst
.addr
.v
.a
.mask
.addr8
, 255, 16);
140 strlcpy(pcr
.rule
.ifname
, ifname
, IFNAMSIZ
);
142 pcr
.action
= PF_CHANGE_GET_TICKET
;
143 if(ioctl(dev
, DIOCCHANGERULE
, &pcr
) < 0) {
144 syslog(LOG_ERR
, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
147 pcr
.action
= PF_CHANGE_ADD_TAIL
;
148 if(ioctl(dev
, DIOCCHANGERULE
, &pcr
) < 0) {
149 syslog(LOG_ERR
, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m");
155 if(++next_uid
>= 65535) {
161 int delete_pinhole(unsigned short uid
)
164 struct pfioc_rule pr
;
165 char label_start
[PF_RULE_LABEL_SIZE
];
166 char tmp_label
[PF_RULE_LABEL_SIZE
];
169 syslog(LOG_ERR
, "pf device is not open");
172 snprintf(label_start
, sizeof(label_start
),
174 memset(&pr
, 0, sizeof(pr
));
175 strlcpy(pr
.anchor
, anchor_name
, MAXPATHLEN
);
177 pr
.rule
.action
= PF_PASS
;
179 if(ioctl(dev
, DIOCGETRULES
, &pr
) < 0) {
180 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULES, ...): %m");
186 if(ioctl(dev
, DIOCGETRULE
, &pr
) < 0) {
187 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULE): %m");
190 strlcpy(tmp_label
, pr
.rule
.label
, sizeof(tmp_label
));
191 strtok(tmp_label
, " ");
192 if(0 == strcmp(tmp_label
, label_start
)) {
193 pr
.action
= PF_CHANGE_GET_TICKET
;
194 if(ioctl(dev
, DIOCCHANGERULE
, &pr
) < 0) {
195 syslog(LOG_ERR
, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
198 pr
.action
= PF_CHANGE_REMOVE
;
200 if(ioctl(dev
, DIOCCHANGERULE
, &pr
) < 0) {
201 syslog(LOG_ERR
, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
212 get_pinhole_info(unsigned short uid
,
213 char * rem_host
, int rem_hostlen
, unsigned short * rem_port
,
214 char * int_client
, int int_clientlen
, unsigned short * int_port
,
215 int * proto
, char * desc
, int desclen
,
216 unsigned int * timestamp
,
217 u_int64_t
* packets
, u_int64_t
* bytes
)
220 struct pfioc_rule pr
;
221 char label_start
[PF_RULE_LABEL_SIZE
];
222 char tmp_label
[PF_RULE_LABEL_SIZE
];
226 syslog(LOG_ERR
, "pf device is not open");
229 snprintf(label_start
, sizeof(label_start
),
231 memset(&pr
, 0, sizeof(pr
));
232 strlcpy(pr
.anchor
, anchor_name
, MAXPATHLEN
);
234 pr
.rule
.action
= PF_PASS
;
236 if(ioctl(dev
, DIOCGETRULES
, &pr
) < 0) {
237 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULES, ...): %m");
243 if(ioctl(dev
, DIOCGETRULE
, &pr
) < 0) {
244 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULE): %m");
247 strlcpy(tmp_label
, pr
.rule
.label
, sizeof(tmp_label
));
250 if(0 == strcmp(tmp_label
, label_start
)) {
251 if(rem_host
&& (inet_ntop(AF_INET6
, &pr
.rule
.src
.addr
.v
.a
.addr
.v6
, rem_host
, rem_hostlen
) == NULL
)) {
255 *rem_port
= ntohs(pr
.rule
.src
.port
[0]);
256 if(int_client
&& (inet_ntop(AF_INET6
, &pr
.rule
.dst
.addr
.v
.a
.addr
.v6
, int_client
, int_clientlen
) == NULL
)) {
260 *int_port
= ntohs(pr
.rule
.dst
.port
[0]);
262 *proto
= pr
.rule
.proto
;
264 sscanf(p
, "ts-%u", timestamp
);
268 strlcpy(desc
, p
, desclen
);
273 #ifdef PFRULE_INOUT_COUNTS
275 *packets
= pr
.rule
.packets
[0] + pr
.rule
.packets
[1];
277 *bytes
= pr
.rule
.bytes
[0] + pr
.rule
.bytes
[1];
280 *packets
= pr
.rule
.packets
;
282 *bytes
= pr
.rule
.bytes
;
291 int update_pinhole(unsigned short uid
, unsigned int timestamp
)
294 * As it is not possible to change rule label, we should :
297 * the stats of the rule will then be reset :( */
298 UNUSED(uid
); UNUSED(timestamp
);
299 return -42; /* not implemented */
302 /* return the number of rules removed
303 * or a negative integer in case of error */
304 int clean_pinhole_list(unsigned int * next_timestamp
)
307 struct pfioc_rule pr
;
311 unsigned int min_ts
= UINT_MAX
;
312 int min_uid
= INT_MAX
, max_uid
= -1;
316 syslog(LOG_ERR
, "pf device is not open");
319 current_time
= time(NULL
);
320 memset(&pr
, 0, sizeof(pr
));
321 strlcpy(pr
.anchor
, anchor_name
, MAXPATHLEN
);
323 pr
.rule
.action
= PF_PASS
;
325 if(ioctl(dev
, DIOCGETRULES
, &pr
) < 0) {
326 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULES, ...): %m");
329 for(i
= pr
.nr
- 1; i
>= 0; i
--) {
331 if(ioctl(dev
, DIOCGETRULE
, &pr
) < 0) {
332 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULE): %m");
335 if(sscanf(pr
.rule
.label
, PINEHOLE_LABEL_FORMAT_SKIPDESC
, &uid
, &ts
) != 2) {
336 syslog(LOG_DEBUG
, "rule with label '%s' is not a IGD pinhole", pr
.rule
.label
);
339 if(ts
<= (unsigned int)current_time
) {
340 syslog(LOG_INFO
, "removing expired pinhole '%s'", pr
.rule
.label
);
341 pr
.action
= PF_CHANGE_GET_TICKET
;
342 if(ioctl(dev
, DIOCCHANGERULE
, &pr
) < 0) {
343 syslog(LOG_ERR
, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m");
346 pr
.action
= PF_CHANGE_REMOVE
;
348 if(ioctl(dev
, DIOCCHANGERULE
, &pr
) < 0) {
349 syslog(LOG_ERR
, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
354 pr
.rule
.action
= PF_PASS
;
356 if(ioctl(dev
, DIOCGETRULES
, &pr
) < 0) {
357 syslog(LOG_ERR
, "ioctl(dev, DIOCGETRULES, ...): %m");
363 else if(uid
< min_uid
)
369 if(next_timestamp
&& (min_ts
!= UINT_MAX
))
370 *next_timestamp
= min_ts
;
372 if(((min_uid
- 32000) <= next_uid
) && (next_uid
<= max_uid
)) {
373 next_uid
= max_uid
+ 1;
375 if(next_uid
>= 65535) {
379 return n
; /* number of rules removed */
382 #endif /* ENABLE_UPNPPINHOLE */