Miniupnpd: update to 2.0
[tomato.git] / release / src / router / miniupnpd / ipfw / ipfwrdr.c
blobce37af39357774dc1a9261a8d72e4de1dfd873d7
1 /* $Id: ipfwrdr.c,v 1.16 2016/02/12 13:44:01 nanard Exp $ */
2 /*
3 * MiniUPnP project
4 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5 * (c) 2009 Jardel Weyrich
6 * (c) 2011-2016 Thomas Bernard
7 * This software is subject to the conditions detailed
8 * in the LICENCE file provided within the distribution
9 */
11 #include "../config.h"
12 #include "../macros.h"
14 #include <sys/param.h>
15 #include <sys/types.h>
16 #include <sys/file.h>
19 This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD.
20 Needed here because on some systems <sys/uio.h> gets included by things
21 like <sys/socket.h>
23 #ifndef _KERNEL
24 # define ADD_KERNEL
25 # define _KERNEL
26 # define KERNEL
27 #endif
28 #ifdef __OpenBSD__
29 struct file;
30 #endif
31 #include <sys/uio.h>
32 #ifdef ADD_KERNEL
33 # undef _KERNEL
34 # undef KERNEL
35 #endif
37 #include <sys/time.h>
38 #include <sys/socket.h>
39 #include <sys/syslog.h>
40 #include <sys/ioctl.h>
41 #include <net/if.h>
42 #if __FreeBSD_version >= 300000
43 # include <net/if_var.h>
44 #endif
45 #include <netinet/in.h>
46 #include <netinet/in_systm.h>
47 #include <netinet/ip.h>
48 #include <netinet/ip_icmp.h>
49 #include <netinet/tcp.h>
50 #include <netinet/udp.h>
51 #include <arpa/inet.h>
53 #include <sys/types.h>
54 #include <sys/queue.h>
55 #include <sys/socket.h>
56 #include <errno.h>
57 #include <limits.h>
58 #include <netdb.h>
59 #include <stdlib.h>
60 #include <fcntl.h>
61 #include <syslog.h>
62 #include <stddef.h>
63 #include <stdio.h>
64 #include <strings.h>
65 #include <string.h>
66 #include <unistd.h>
67 #include <netinet/ip_fw.h>
68 #include "ipfwaux.h"
69 #include "ipfwrdr.h"
71 #include "../upnpglobalvars.h"
73 /* init and shutdown functions */
75 int init_redirect(void) {
76 return ipfw_exec(IP_FW_INIT, NULL, 0);
79 void shutdown_redirect(void) {
80 ipfw_exec(IP_FW_TERM, NULL, 0);
83 /* ipfw cannot store descriptions and timestamp for port mappings so we keep
84 * our own list in memory */
85 struct mapping_desc_time {
86 struct mapping_desc_time * next;
87 unsigned int timestamp;
88 unsigned short eport;
89 short proto;
90 char desc[];
93 static struct mapping_desc_time * mappings_list = NULL;
95 /* add an element to the port mappings descriptions & timestamp list */
96 static void
97 add_desc_time(unsigned short eport, int proto,
98 const char * desc, unsigned int timestamp)
100 struct mapping_desc_time * tmp;
101 size_t l;
102 if(!desc)
103 desc = "miniupnpd";
104 l = strlen(desc) + 1;
105 tmp = malloc(sizeof(struct mapping_desc_time) + l);
106 if(tmp) {
107 /* fill the element and insert it as head of the list */
108 tmp->next = mappings_list;
109 tmp->timestamp = timestamp;
110 tmp->eport = eport;
111 tmp->proto = (short)proto;
112 memcpy(tmp->desc, desc, l);
113 mappings_list = tmp;
117 /* remove an element to the port mappings descriptions & timestamp list */
118 static void
119 del_desc_time(unsigned short eport, int proto)
121 struct mapping_desc_time * e;
122 struct mapping_desc_time * * p;
123 p = &mappings_list;
124 e = *p;
125 while(e) {
126 if(e->eport == eport && e->proto == (short)proto) {
127 *p = e->next;
128 free(e);
129 return;
130 } else {
131 p = &e->next;
132 e = *p;
137 /* go through the list and find the description and timestamp */
138 static void
139 get_desc_time(unsigned short eport, int proto,
140 char * desc, int desclen,
141 unsigned int * timestamp)
143 struct mapping_desc_time * e;
145 for(e = mappings_list; e; e = e->next) {
146 if(e->eport == eport && e->proto == (short)proto) {
147 if(desc)
148 strlcpy(desc, e->desc, desclen);
149 if(timestamp)
150 *timestamp = e->timestamp;
151 return;
156 /* --- */
157 int add_redirect_rule2(
158 const char * ifname,
159 const char * rhost,
160 unsigned short eport,
161 const char * iaddr,
162 unsigned short iport,
163 int proto,
164 const char * desc,
165 unsigned int timestamp)
167 struct ip_fw rule;
168 int r;
170 if (ipfw_validate_protocol(proto) < 0)
171 return -1;
172 if (ipfw_validate_ifname(ifname) < 0)
173 return -1;
175 memset(&rule, 0, sizeof(struct ip_fw));
176 rule.version = IP_FW_CURRENT_API_VERSION;
177 #if 0
178 rule.fw_number = 1000; /* rule number */
179 rule.context = (void *)desc; /* The description is kept in a separate list */
180 #endif
181 rule.fw_prot = proto; /* protocol */
182 rule.fw_flg |= IP_FW_F_IIFACE; /* interfaces to check */
183 rule.fw_flg |= IP_FW_F_IIFNAME; /* interfaces to check by name */
184 rule.fw_flg |= (IP_FW_F_IN | IP_FW_F_OUT); /* packet direction */
185 rule.fw_flg |= IP_FW_F_FWD; /* forward action */
186 #ifdef USE_IFNAME_IN_RULES
187 if (ifname != NULL) {
188 strlcpy(rule.fw_in_if.fu_via_if.name, ifname, IFNAMSIZ); /* src interface */
189 rule.fw_in_if.fu_via_if.unit = -1;
191 #endif
192 if (inet_aton(iaddr, &rule.fw_out_if.fu_via_ip) == 0) {
193 syslog(LOG_ERR, "inet_aton(): %m");
194 return -1;
196 memcpy(&rule.fw_dst, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
197 memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
198 rule.fw_dmsk.s_addr = INADDR_BROADCAST; /* TODO check this */
199 IP_FW_SETNDSTP(&rule, 1); /* number of external ports */
200 rule.fw_uar.fw_pts[0] = eport; /* external port */
201 rule.fw_fwd_ip.sin_port = iport; /* internal port */
202 if (rhost && rhost[0] != '\0') {
203 inet_aton(rhost, &rule.fw_src);
204 rule.fw_smsk.s_addr = htonl(INADDR_NONE);
207 r = ipfw_exec(IP_FW_ADD, &rule, sizeof(rule));
208 if(r >= 0)
209 add_desc_time(eport, proto, desc, timestamp);
210 return r;
213 /* get_redirect_rule()
214 * return value : 0 success (found)
215 * -1 = error or rule not found */
216 int get_redirect_rule(
217 const char * ifname,
218 unsigned short eport,
219 int proto,
220 char * iaddr,
221 int iaddrlen,
222 unsigned short * iport,
223 char * desc,
224 int desclen,
225 char * rhost,
226 int rhostlen,
227 unsigned int * timestamp,
228 u_int64_t * packets,
229 u_int64_t * bytes)
231 int i, count_rules, total_rules = 0;
232 struct ip_fw * rules = NULL;
234 if (ipfw_validate_protocol(proto) < 0)
235 return -1;
236 if (ipfw_validate_ifname(ifname) < 0)
237 return -1;
238 if (timestamp)
239 *timestamp = 0;
241 do {
242 count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
243 if (count_rules < 0)
244 goto error;
245 } while (count_rules == 10);
247 for (i=0; i<total_rules-1; i++) {
248 const struct ip_fw const * ptr = &rules[i];
249 if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
250 if (packets != NULL)
251 *packets = ptr->fw_pcnt;
252 if (bytes != NULL)
253 *bytes = ptr->fw_bcnt;
254 if (iport != NULL)
255 *iport = ptr->fw_fwd_ip.sin_port;
256 if (iaddr != NULL && iaddrlen > 0) {
257 /* looks like fw_out_if.fu_via_ip is zero */
258 /*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/
259 if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) {
260 syslog(LOG_ERR, "inet_ntop(): %m");
261 goto error;
264 if (rhost != NULL && rhostlen > 0) {
265 if (ptr->fw_src.s_addr == 0)
266 rhost[0] = '\0';
267 else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) {
268 syslog(LOG_ERR, "inet_ntop(): %m");
269 goto error;
272 /* And what if we found more than 1 matching rule? */
273 ipfw_free_ruleset(&rules);
274 get_desc_time(eport, proto, desc, desclen, timestamp);
275 return 0;
279 error:
280 if (rules != NULL)
281 ipfw_free_ruleset(&rules);
282 return -1;
285 int delete_redirect_rule(
286 const char * ifname,
287 unsigned short eport,
288 int proto)
290 int i, count_rules, total_rules = 0;
291 struct ip_fw * rules = NULL;
293 if (ipfw_validate_protocol(proto) < 0)
294 return -1;
295 if (ipfw_validate_ifname(ifname) < 0)
296 return -1;
298 do {
299 count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
300 if (count_rules < 0)
301 goto error;
302 } while (count_rules == 10);
304 for (i=0; i<total_rules-1; i++) {
305 const struct ip_fw const * ptr = &rules[i];
306 if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
307 if (ipfw_exec(IP_FW_DEL, (struct ip_fw *)ptr, sizeof(*ptr)) < 0)
308 goto error;
309 /* And what if we found more than 1 matching rule? */
310 ipfw_free_ruleset(&rules);
311 del_desc_time(eport, proto);
312 return 0;
316 error:
317 if (rules != NULL)
318 ipfw_free_ruleset(&rules);
319 return -1;
322 int add_filter_rule2(
323 const char * ifname,
324 const char * rhost,
325 const char * iaddr,
326 unsigned short eport,
327 unsigned short iport,
328 int proto,
329 const char * desc)
331 UNUSED(ifname);
332 UNUSED(rhost);
333 UNUSED(iaddr);
334 UNUSED(eport);
335 UNUSED(iport);
336 UNUSED(proto);
337 UNUSED(desc);
338 return 0; /* nothing to do, always success */
341 int delete_filter_rule(
342 const char * ifname,
343 unsigned short eport,
344 int proto)
346 UNUSED(ifname);
347 UNUSED(eport);
348 UNUSED(proto);
349 return 0; /* nothing to do, always success */
352 int get_redirect_rule_by_index(
353 int index,
354 char * ifname,
355 unsigned short * eport,
356 char * iaddr,
357 int iaddrlen,
358 unsigned short * iport,
359 int * proto,
360 char * desc,
361 int desclen,
362 char * rhost,
363 int rhostlen,
364 unsigned int * timestamp,
365 u_int64_t * packets,
366 u_int64_t * bytes)
368 int total_rules = 0;
369 struct ip_fw * rules = NULL;
371 if (index < 0) /* TODO shouldn't we also validate the maximum? */
372 return -1;
374 if(timestamp)
375 *timestamp = 0;
377 ipfw_fetch_ruleset(&rules, &total_rules, index + 1);
379 if (total_rules > index) {
380 const struct ip_fw const * ptr = &rules[index];
381 if (ptr->fw_prot == 0) /* invalid rule */
382 goto error;
383 if (proto != NULL)
384 *proto = ptr->fw_prot;
385 if (eport != NULL)
386 *eport = ptr->fw_uar.fw_pts[0];
387 if (iport != NULL)
388 *iport = ptr->fw_fwd_ip.sin_port;
389 if (ifname != NULL)
390 strlcpy(ifname, ptr->fw_in_if.fu_via_if.name, IFNAMSIZ);
391 if (packets != NULL)
392 *packets = ptr->fw_pcnt;
393 if (bytes != NULL)
394 *bytes = ptr->fw_bcnt;
395 if (iport != NULL)
396 *iport = ptr->fw_fwd_ip.sin_port;
397 if (iaddr != NULL && iaddrlen > 0) {
398 /* looks like fw_out_if.fu_via_ip is zero */
399 /*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/
400 if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) {
401 syslog(LOG_ERR, "inet_ntop(): %m");
402 goto error;
405 if (rhost != NULL && rhostlen > 0) {
406 if (ptr->fw_src.s_addr == 0)
407 rhost[0] = '\0';
408 else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) {
409 syslog(LOG_ERR, "inet_ntop(): %m");
410 goto error;
413 ipfw_free_ruleset(&rules);
414 get_desc_time(*eport, *proto, desc, desclen, timestamp);
415 return 0;
418 error:
419 if (rules != NULL)
420 ipfw_free_ruleset(&rules);
421 return -1;
424 /* upnp_get_portmappings_in_range()
425 * return a list of all "external" ports for which a port
426 * mapping exists */
427 unsigned short *
428 get_portmappings_in_range(unsigned short startport,
429 unsigned short endport,
430 int proto,
431 unsigned int * number)
433 unsigned short *array = NULL, *array2 = NULL;
434 unsigned int capacity = 128;
435 int i, count_rules, total_rules = 0;
436 struct ip_fw * rules = NULL;
438 if (ipfw_validate_protocol(proto) < 0)
439 return NULL;
441 do {
442 count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
443 if (count_rules < 0)
444 goto error;
445 } while (count_rules == 10);
447 array = calloc(capacity, sizeof(unsigned short));
448 if(!array) {
449 syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
450 goto error;
452 *number = 0;
454 for (i=0; i<total_rules-1; i++) {
455 const struct ip_fw const * ptr = &rules[i];
456 unsigned short eport = ptr->fw_uar.fw_pts[0];
457 if (proto == ptr->fw_prot
458 && startport <= eport
459 && eport <= endport) {
460 if(*number >= capacity) {
461 capacity += 128;
462 array2 = realloc(array, sizeof(unsigned short)*capacity);
463 if(!array2) {
464 syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity);
465 *number = 0;
466 free(array);
467 goto error;
469 array = array2;
471 array[*number] = eport;
472 (*number)++;
475 error:
476 if (rules != NULL)
477 ipfw_free_ruleset(&rules);
478 return array;
482 update_portmapping_desc_timestamp(const char * ifname,
483 unsigned short eport, int proto,
484 const char * desc, unsigned int timestamp)
486 UNUSED(ifname);
487 del_desc_time(eport, proto);
488 add_desc_time(eport, proto, desc, timestamp);
489 return 0;
493 update_portmapping(const char * ifname, unsigned short eport, int proto,
494 unsigned short iport, const char * desc,
495 unsigned int timestamp)
497 int i, count_rules, total_rules = 0;
498 struct ip_fw * rules = NULL;
499 int r = -1;
500 char iaddr[16];
501 char rhost[16];
502 int found;
504 if (ipfw_validate_protocol(proto) < 0)
505 return -1;
506 if (ipfw_validate_ifname(ifname) < 0)
507 return -1;
509 do {
510 count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
511 if (count_rules < 0)
512 goto error;
513 } while (count_rules == 10);
515 found = 0;
516 iaddr[0] = '\0';
517 rhost[0] = '\0';
519 for (i=0; i<total_rules-1; i++) {
520 const struct ip_fw const * ptr = &rules[i];
521 if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
522 if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, sizeof(iaddr)) == NULL) {
523 syslog(LOG_ERR, "inet_ntop(): %m");
524 goto error;
526 if ((ptr->fw_src.s_addr != 0) &&
527 (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, sizeof(rhost)) == NULL)) {
528 syslog(LOG_ERR, "inet_ntop(): %m");
529 goto error;
531 found = 1;
532 if (ipfw_exec(IP_FW_DEL, (struct ip_fw *)ptr, sizeof(*ptr)) < 0)
533 goto error;
534 del_desc_time(eport, proto);
535 break;
538 ipfw_free_ruleset(&rules);
539 rules = NULL;
541 if(found)
542 r = add_redirect_rule2(ifname, rhost, eport, iaddr, iport, proto, desc, timestamp);
544 error:
545 if (rules != NULL)
546 ipfw_free_ruleset(&rules);
547 return r;