Miniupnpd: update from 1.8 (20140422) to 1.9 (20141209)
[tomato.git] / release / src / router / miniupnpd / pf / pfpinhole.c
blobb05b75d2fb148e84fad68210a0324a95eefcc3b8
1 /* $Id: pfpinhole.c,v 1.24 2014/12/05 09:54:55 nanard Exp $ */
2 /* MiniUPnP project
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 */
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <sys/param.h>
11 #include <net/if.h>
12 #include <netinet/in.h>
13 #include <netinet/tcp.h>
14 #include <arpa/inet.h>
15 #ifdef __DragonFly__
16 #include <net/pf/pfvar.h>
17 #else
18 #ifdef __APPLE__
19 #define PRIVATE 1
20 #endif
21 #include <net/pfvar.h>
22 #endif
23 #include <fcntl.h>
24 #include <sys/ioctl.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <syslog.h>
28 #include <stdio.h>
29 #include <stdlib.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 */
49 extern int dev;
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)
61 int uid;
62 struct pfioc_rule pcr;
63 #ifndef PF_NEWSTYLE
64 struct pfioc_pooladdr pp;
65 #endif
67 if(dev<0) {
68 syslog(LOG_ERR, "pf device is not open");
69 return -1;
71 memset(&pcr, 0, sizeof(pcr));
72 strlcpy(pcr.anchor, anchor_name, MAXPATHLEN);
74 #ifndef PF_NEWSTYLE
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");
79 return -1;
80 } else {
81 pcr.pool_ticket = pp.ticket;
82 #else
84 #endif
85 pcr.rule.direction = PF_IN;
86 pcr.rule.action = PF_PASS;
87 pcr.rule.af = AF_INET6;
88 #ifdef PF_NEWSTYLE
89 pcr.rule.nat.addr.type = PF_ADDR_NONE;
90 pcr.rule.rdr.addr.type = PF_ADDR_NONE;
91 #endif
92 #ifdef USE_IFNAME_IN_RULES
93 if(ifname)
94 strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ);
95 #endif
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 */
106 #endif
107 #ifdef PFRULE_HAS_ONRDOMAIN
108 pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */
109 #endif
110 pcr.rule.keep_state = 1;
111 uid = next_uid;
112 snprintf(pcr.rule.label, PF_RULE_LABEL_SIZE,
113 PINEHOLE_LABEL_FORMAT, uid, timestamp, desc);
114 if(queue)
115 strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE);
116 if(tag)
117 strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE);
119 if(rem_port) {
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);
139 if(ifname)
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");
145 return -1;
146 } else {
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");
150 return -1;
155 if(++next_uid >= 65535) {
156 next_uid = 1;
158 return uid;
161 int delete_pinhole(unsigned short uid)
163 int i, n;
164 struct pfioc_rule pr;
165 char label_start[PF_RULE_LABEL_SIZE];
166 char tmp_label[PF_RULE_LABEL_SIZE];
168 if(dev<0) {
169 syslog(LOG_ERR, "pf device is not open");
170 return -1;
172 snprintf(label_start, sizeof(label_start),
173 "pinhole-%hu", uid);
174 memset(&pr, 0, sizeof(pr));
175 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
176 #ifndef PF_NEWSTYLE
177 pr.rule.action = PF_PASS;
178 #endif
179 if(ioctl(dev, DIOCGETRULES, &pr) < 0) {
180 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
181 return -1;
183 n = pr.nr;
184 for(i=0; i<n; i++) {
185 pr.nr = i;
186 if(ioctl(dev, DIOCGETRULE, &pr) < 0) {
187 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
188 return -1;
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");
196 return -1;
198 pr.action = PF_CHANGE_REMOVE;
199 pr.nr = i;
200 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) {
201 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
202 return -1;
204 return 0;
207 /* not found */
208 return -2;
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)
219 int i, n;
220 struct pfioc_rule pr;
221 char label_start[PF_RULE_LABEL_SIZE];
222 char tmp_label[PF_RULE_LABEL_SIZE];
223 char * p;
225 if(dev<0) {
226 syslog(LOG_ERR, "pf device is not open");
227 return -1;
229 snprintf(label_start, sizeof(label_start),
230 "pinhole-%hu", uid);
231 memset(&pr, 0, sizeof(pr));
232 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
233 #ifndef PF_NEWSTYLE
234 pr.rule.action = PF_PASS;
235 #endif
236 if(ioctl(dev, DIOCGETRULES, &pr) < 0) {
237 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
238 return -1;
240 n = pr.nr;
241 for(i=0; i<n; i++) {
242 pr.nr = i;
243 if(ioctl(dev, DIOCGETRULE, &pr) < 0) {
244 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
245 return -1;
247 strlcpy(tmp_label, pr.rule.label, sizeof(tmp_label));
248 p = tmp_label;
249 strsep(&p, " ");
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)) {
252 return -1;
254 if(rem_port)
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)) {
257 return -1;
259 if(int_port)
260 *int_port = ntohs(pr.rule.dst.port[0]);
261 if(proto)
262 *proto = pr.rule.proto;
263 if(timestamp)
264 sscanf(p, "ts-%u", timestamp);
265 if(desc) {
266 strsep(&p, " ");
267 if(p) {
268 strlcpy(desc, p, desclen);
269 } else {
270 desc[0] = '\0';
273 #ifdef PFRULE_INOUT_COUNTS
274 if(packets)
275 *packets = pr.rule.packets[0] + pr.rule.packets[1];
276 if(bytes)
277 *bytes = pr.rule.bytes[0] + pr.rule.bytes[1];
278 #else
279 if(packets)
280 *packets = pr.rule.packets;
281 if(bytes)
282 *bytes = pr.rule.bytes;
283 #endif
284 return 0;
287 /* not found */
288 return -2;
291 int update_pinhole(unsigned short uid, unsigned int timestamp)
293 /* TODO :
294 * As it is not possible to change rule label, we should :
295 * 1 - delete
296 * 2 - Add new
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)
306 int i;
307 struct pfioc_rule pr;
308 time_t current_time;
309 unsigned int ts;
310 int uid;
311 unsigned int min_ts = UINT_MAX;
312 int min_uid = INT_MAX, max_uid = -1;
313 int n = 0;
315 if(dev<0) {
316 syslog(LOG_ERR, "pf device is not open");
317 return -1;
319 current_time = time(NULL);
320 memset(&pr, 0, sizeof(pr));
321 strlcpy(pr.anchor, anchor_name, MAXPATHLEN);
322 #ifndef PF_NEWSTYLE
323 pr.rule.action = PF_PASS;
324 #endif
325 if(ioctl(dev, DIOCGETRULES, &pr) < 0) {
326 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
327 return -1;
329 for(i = pr.nr - 1; i >= 0; i--) {
330 pr.nr = i;
331 if(ioctl(dev, DIOCGETRULE, &pr) < 0) {
332 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m");
333 return -1;
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);
337 continue;
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");
344 return -1;
346 pr.action = PF_CHANGE_REMOVE;
347 pr.nr = i;
348 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) {
349 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m");
350 return -1;
352 n++;
353 #ifndef PF_NEWSTYLE
354 pr.rule.action = PF_PASS;
355 #endif
356 if(ioctl(dev, DIOCGETRULES, &pr) < 0) {
357 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m");
358 return -1;
360 } else {
361 if(uid > max_uid)
362 max_uid = uid;
363 else if(uid < min_uid)
364 min_uid = uid;
365 if(ts < min_ts)
366 min_ts = ts;
369 if(next_timestamp && (min_ts != UINT_MAX))
370 *next_timestamp = min_ts;
371 if(max_uid > 0) {
372 if(((min_uid - 32000) <= next_uid) && (next_uid <= max_uid)) {
373 next_uid = max_uid + 1;
375 if(next_uid >= 65535) {
376 next_uid = 1;
379 return n; /* number of rules removed */
382 #endif /* ENABLE_UPNPPINHOLE */