Annotation of embedaddon/miniupnpd/ipfw/ipfwrdr.c, revision 1.1.1.3
1.1.1.3 ! misho 1: /* $Id: ipfwrdr.c,v 1.13 2012/03/05 20:36:19 nanard Exp $ */
1.1 misho 2: /*
3: * MiniUPnP project
4: * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5: * (c) 2009 Jardel Weyrich
1.1.1.3 ! misho 6: * (c) 2011-2012 Thomas Bernard
1.1 misho 7: * This software is subject to the conditions detailed
8: * in the LICENCE file provided within the distribution
9: */
10:
1.1.1.2 misho 11: #include "../config.h"
12:
1.1 misho 13: #include <sys/param.h>
14: #include <sys/types.h>
15: #include <sys/file.h>
16:
1.1.1.3 ! misho 17: /*
! 18: This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD.
! 19: Needed here because on some systems <sys/uio.h> gets included by things
! 20: like <sys/socket.h>
! 21: */
1.1 misho 22: #ifndef _KERNEL
23: # define ADD_KERNEL
24: # define _KERNEL
25: # define KERNEL
26: #endif
27: #ifdef __OpenBSD__
28: struct file;
29: #endif
30: #include <sys/uio.h>
31: #ifdef ADD_KERNEL
32: # undef _KERNEL
33: # undef KERNEL
34: #endif
35:
36: #include <sys/time.h>
37: #include <sys/socket.h>
38: #include <sys/syslog.h>
39: #include <sys/ioctl.h>
40: #include <net/if.h>
41: #if __FreeBSD_version >= 300000
42: # include <net/if_var.h>
43: #endif
44: #include <netinet/in.h>
45: #include <netinet/in_systm.h>
46: #include <netinet/ip.h>
47: #include <netinet/ip_icmp.h>
48: #include <netinet/tcp.h>
49: #include <netinet/udp.h>
50: #include <arpa/inet.h>
51:
52: #include <sys/types.h>
53: #include <sys/queue.h>
54: #include <sys/socket.h>
55: #include <errno.h>
56: #include <limits.h>
57: #include <netdb.h>
58: #include <stdlib.h>
59: #include <fcntl.h>
60: #include <syslog.h>
61: #include <stddef.h>
62: #include <stdio.h>
63: #include <strings.h>
64: #include <string.h>
65: #include <unistd.h>
66: #include <netinet/ip_fw.h>
67: #include "ipfwaux.h"
1.1.1.2 misho 68: #include "ipfwrdr.h"
1.1 misho 69:
70: #include "../upnpglobalvars.h"
71:
1.1.1.2 misho 72: /* init and shutdown functions */
1.1 misho 73:
74: int init_redirect(void) {
1.1.1.2 misho 75: return ipfw_exec(IP_FW_INIT, NULL, 0);
1.1 misho 76: }
77:
78: void shutdown_redirect(void) {
79: ipfw_exec(IP_FW_TERM, NULL, 0);
80: }
81:
1.1.1.2 misho 82: /* ipfw cannot store descriptions and timestamp for port mappings so we keep
83: * our own list in memory */
84: struct mapping_desc_time {
85: struct mapping_desc_time * next;
86: unsigned int timestamp;
87: unsigned short eport;
88: short proto;
89: char desc[];
90: };
91:
92: static struct mapping_desc_time * mappings_list = NULL;
93:
94: /* add an element to the port mappings descriptions & timestamp list */
95: static void
96: add_desc_time(unsigned short eport, int proto,
97: const char * desc, unsigned int timestamp)
98: {
99: struct mapping_desc_time * tmp;
100: size_t l;
101: if(!desc)
102: desc = "miniupnpd";
103: l = strlen(desc) + 1;
104: tmp = malloc(sizeof(struct mapping_desc_time) + l);
105: if(tmp) {
106: /* fill the element and insert it as head of the list */
107: tmp->next = mappings_list;
108: tmp->timestamp = timestamp;
109: tmp->eport = eport;
110: tmp->proto = (short)proto;
111: memcpy(tmp->desc, desc, l);
112: mappings_list = tmp;
113: }
114: }
115:
116: /* remove an element to the port mappings descriptions & timestamp list */
117: static void
118: del_desc_time(unsigned short eport, int proto)
119: {
120: struct mapping_desc_time * e;
121: struct mapping_desc_time * * p;
122: p = &mappings_list;
123: e = *p;
124: while(e) {
125: if(e->eport == eport && e->proto == (short)proto) {
126: *p = e->next;
127: free(e);
128: return;
129: } else {
130: p = &e->next;
131: e = *p;
132: }
133: }
134: }
135:
136: /* go through the list and find the description and timestamp */
137: static void
138: get_desc_time(unsigned short eport, int proto,
139: char * desc, int desclen,
140: unsigned int * timestamp)
141: {
142: struct mapping_desc_time * e;
143:
144: for(e = mappings_list; e; e = e->next) {
145: if(e->eport == eport && e->proto == (short)proto) {
146: if(desc)
147: strlcpy(desc, e->desc, desclen);
148: if(timestamp)
149: *timestamp = e->timestamp;
150: return;
151: }
152: }
153: }
154:
155: /* --- */
1.1 misho 156: int add_redirect_rule2(
157: const char * ifname,
1.1.1.2 misho 158: const char * rhost,
1.1 misho 159: unsigned short eport,
160: const char * iaddr,
161: unsigned short iport,
162: int proto,
1.1.1.2 misho 163: const char * desc,
164: unsigned int timestamp)
1.1 misho 165: {
166: struct ip_fw rule;
1.1.1.2 misho 167: int r;
1.1 misho 168:
169: if (ipfw_validate_protocol(proto) < 0)
170: return -1;
171: if (ipfw_validate_ifname(ifname) < 0)
172: return -1;
1.1.1.3 ! misho 173:
1.1 misho 174: memset(&rule, 0, sizeof(struct ip_fw));
175: rule.version = IP_FW_CURRENT_API_VERSION;
1.1.1.3 ! misho 176: #if 0
! 177: rule.fw_number = 1000; /* rule number */
! 178: rule.context = (void *)desc; /* The description is kept in a separate list */
! 179: #endif
! 180: rule.fw_prot = proto; /* protocol */
! 181: rule.fw_flg |= IP_FW_F_IIFACE; /* interfaces to check */
! 182: rule.fw_flg |= IP_FW_F_IIFNAME; /* interfaces to check by name */
! 183: rule.fw_flg |= (IP_FW_F_IN | IP_FW_F_OUT); /* packet direction */
! 184: rule.fw_flg |= IP_FW_F_FWD; /* forward action */
1.1 misho 185: #ifdef USE_IFNAME_IN_RULES
186: if (ifname != NULL) {
1.1.1.3 ! misho 187: strlcpy(rule.fw_in_if.fu_via_if.name, ifname, IFNAMSIZ); /* src interface */
1.1 misho 188: rule.fw_in_if.fu_via_if.unit = -1;
189: }
190: #endif
191: if (inet_aton(iaddr, &rule.fw_out_if.fu_via_ip) == 0) {
192: syslog(LOG_ERR, "inet_aton(): %m");
193: return -1;
1.1.1.3 ! misho 194: }
1.1 misho 195: memcpy(&rule.fw_dst, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
196: memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
1.1.1.3 ! misho 197: rule.fw_dmsk.s_addr = INADDR_BROADCAST; /* TODO check this */
! 198: IP_FW_SETNDSTP(&rule, 1); /* number of external ports */
! 199: rule.fw_uar.fw_pts[0] = eport; /* external port */
! 200: rule.fw_fwd_ip.sin_port = iport; /* internal port */
1.1.1.2 misho 201: if (rhost && rhost[0] != '\0') {
202: inet_aton(rhost, &rule.fw_src);
203: rule.fw_smsk.s_addr = htonl(INADDR_NONE);
204: }
1.1 misho 205:
1.1.1.2 misho 206: r = ipfw_exec(IP_FW_ADD, &rule, sizeof(rule));
207: if(r >= 0)
208: add_desc_time(eport, proto, desc, timestamp);
209: return r;
1.1 misho 210: }
211:
212: /* get_redirect_rule()
213: * return value : 0 success (found)
214: * -1 = error or rule not found */
215: int get_redirect_rule(
216: const char * ifname,
217: unsigned short eport,
218: int proto,
1.1.1.3 ! misho 219: char * iaddr,
! 220: int iaddrlen,
1.1 misho 221: unsigned short * iport,
1.1.1.3 ! misho 222: char * desc,
1.1 misho 223: int desclen,
1.1.1.2 misho 224: char * rhost,
225: int rhostlen,
226: unsigned int * timestamp,
1.1 misho 227: u_int64_t * packets,
228: u_int64_t * bytes)
229: {
230: int i, count_rules, total_rules = 0;
231: struct ip_fw * rules = NULL;
1.1.1.3 ! misho 232:
1.1 misho 233: if (ipfw_validate_protocol(proto) < 0)
234: return -1;
235: if (ipfw_validate_ifname(ifname) < 0)
236: return -1;
1.1.1.2 misho 237: if (timestamp)
238: *timestamp = 0;
239:
1.1 misho 240: do {
241: count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
242: if (count_rules < 0)
243: goto error;
244: } while (count_rules == 10);
1.1.1.3 ! misho 245:
1.1 misho 246: for (i=0; i<total_rules-1; i++) {
247: const struct ip_fw const * ptr = &rules[i];
248: if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
249: if (packets != NULL)
250: *packets = ptr->fw_pcnt;
251: if (bytes != NULL)
252: *bytes = ptr->fw_bcnt;
253: if (iport != NULL)
254: *iport = ptr->fw_fwd_ip.sin_port;
255: if (iaddr != NULL && iaddrlen > 0) {
1.1.1.2 misho 256: /* looks like fw_out_if.fu_via_ip is zero */
1.1.1.3 ! misho 257: /*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/
1.1.1.2 misho 258: if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) {
259: syslog(LOG_ERR, "inet_ntop(): %m");
260: goto error;
1.1.1.3 ! misho 261: }
1.1.1.2 misho 262: }
263: if (rhost != NULL && rhostlen > 0) {
264: if (ptr->fw_src.s_addr == 0)
265: rhost[0] = '\0';
266: else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) {
1.1 misho 267: syslog(LOG_ERR, "inet_ntop(): %m");
268: goto error;
1.1.1.3 ! misho 269: }
1.1 misho 270: }
1.1.1.3 ! misho 271: /* And what if we found more than 1 matching rule? */
1.1 misho 272: ipfw_free_ruleset(&rules);
1.1.1.2 misho 273: get_desc_time(eport, proto, desc, desclen, timestamp);
1.1 misho 274: return 0;
275: }
276: }
277:
278: error:
279: if (rules != NULL)
1.1.1.3 ! misho 280: ipfw_free_ruleset(&rules);
1.1 misho 281: return -1;
282: }
283:
284: int delete_redirect_rule(
285: const char * ifname,
286: unsigned short eport,
1.1.1.3 ! misho 287: int proto)
1.1 misho 288: {
289: int i, count_rules, total_rules = 0;
290: struct ip_fw * rules = NULL;
1.1.1.3 ! misho 291:
1.1 misho 292: if (ipfw_validate_protocol(proto) < 0)
293: return -1;
294: if (ipfw_validate_ifname(ifname) < 0)
295: return -1;
1.1.1.3 ! misho 296:
1.1 misho 297: do {
298: count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
299: if (count_rules < 0)
300: goto error;
301: } while (count_rules == 10);
1.1.1.3 ! misho 302:
1.1 misho 303: for (i=0; i<total_rules-1; i++) {
304: const struct ip_fw const * ptr = &rules[i];
305: if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
306: if (ipfw_exec(IP_FW_DEL, (struct ip_fw *)ptr, sizeof(*ptr)) < 0)
307: goto error;
1.1.1.3 ! misho 308: /* And what if we found more than 1 matching rule? */
1.1 misho 309: ipfw_free_ruleset(&rules);
1.1.1.2 misho 310: del_desc_time(eport, proto);
1.1 misho 311: return 0;
312: }
313: }
1.1.1.3 ! misho 314:
1.1 misho 315: error:
316: if (rules != NULL)
1.1.1.3 ! misho 317: ipfw_free_ruleset(&rules);
1.1 misho 318: return -1;
319: }
320:
321: int add_filter_rule2(
1.1.1.3 ! misho 322: const char * ifname,
1.1.1.2 misho 323: const char * rhost,
1.1 misho 324: const char * iaddr,
1.1.1.3 ! misho 325: unsigned short eport,
1.1 misho 326: unsigned short iport,
1.1.1.3 ! misho 327: int proto,
1.1 misho 328: const char * desc)
329: {
1.1.1.2 misho 330: return 0; /* nothing to do, always success */
1.1 misho 331: }
332:
333: int delete_filter_rule(
1.1.1.3 ! misho 334: const char * ifname,
! 335: unsigned short eport,
! 336: int proto)
1.1 misho 337: {
1.1.1.2 misho 338: return 0; /* nothing to do, always success */
1.1 misho 339: }
340:
341: int get_redirect_rule_by_index(
342: int index,
1.1.1.3 ! misho 343: char * ifname,
1.1 misho 344: unsigned short * eport,
1.1.1.3 ! misho 345: char * iaddr,
! 346: int iaddrlen,
1.1 misho 347: unsigned short * iport,
1.1.1.3 ! misho 348: int * proto,
! 349: char * desc,
1.1 misho 350: int desclen,
1.1.1.2 misho 351: char * rhost,
352: int rhostlen,
353: unsigned int * timestamp,
1.1.1.3 ! misho 354: u_int64_t * packets,
1.1 misho 355: u_int64_t * bytes)
356: {
357: int total_rules = 0;
358: struct ip_fw * rules = NULL;
359:
1.1.1.3 ! misho 360: if (index < 0) /* TODO shouldn't we also validate the maximum? */
1.1 misho 361: return -1;
362:
1.1.1.2 misho 363: if(timestamp)
364: *timestamp = 0;
365:
1.1 misho 366: ipfw_fetch_ruleset(&rules, &total_rules, index + 1);
367:
1.1.1.2 misho 368: if (total_rules > index) {
1.1 misho 369: const struct ip_fw const * ptr = &rules[index];
1.1.1.3 ! misho 370: if (ptr->fw_prot == 0) /* invalid rule */
1.1.1.2 misho 371: goto error;
1.1 misho 372: if (proto != NULL)
373: *proto = ptr->fw_prot;
374: if (eport != NULL)
375: *eport = ptr->fw_uar.fw_pts[0];
376: if (iport != NULL)
377: *iport = ptr->fw_fwd_ip.sin_port;
378: if (ifname != NULL)
379: strlcpy(ifname, ptr->fw_in_if.fu_via_if.name, IFNAMSIZ);
380: if (packets != NULL)
381: *packets = ptr->fw_pcnt;
382: if (bytes != NULL)
383: *bytes = ptr->fw_bcnt;
384: if (iport != NULL)
385: *iport = ptr->fw_fwd_ip.sin_port;
386: if (iaddr != NULL && iaddrlen > 0) {
1.1.1.2 misho 387: /* looks like fw_out_if.fu_via_ip is zero */
1.1.1.3 ! misho 388: /*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/
1.1.1.2 misho 389: if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) {
390: syslog(LOG_ERR, "inet_ntop(): %m");
391: goto error;
1.1.1.3 ! misho 392: }
1.1.1.2 misho 393: }
394: if (rhost != NULL && rhostlen > 0) {
395: if (ptr->fw_src.s_addr == 0)
396: rhost[0] = '\0';
397: else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) {
1.1 misho 398: syslog(LOG_ERR, "inet_ntop(): %m");
399: goto error;
1.1.1.3 ! misho 400: }
1.1 misho 401: }
402: ipfw_free_ruleset(&rules);
1.1.1.2 misho 403: get_desc_time(*eport, *proto, desc, desclen, timestamp);
1.1 misho 404: return 0;
405: }
406:
407: error:
408: if (rules != NULL)
1.1.1.3 ! misho 409: ipfw_free_ruleset(&rules);
! 410: return -1;
1.1 misho 411: }
1.1.1.2 misho 412:
413: /* upnp_get_portmappings_in_range()
414: * return a list of all "external" ports for which a port
415: * mapping exists */
416: unsigned short *
417: get_portmappings_in_range(unsigned short startport,
418: unsigned short endport,
419: int proto,
420: unsigned int * number)
421: {
422: unsigned short * array = NULL;
423: unsigned int capacity = 128;
424: int i, count_rules, total_rules = 0;
425: struct ip_fw * rules = NULL;
1.1.1.3 ! misho 426:
1.1.1.2 misho 427: if (ipfw_validate_protocol(proto) < 0)
428: return NULL;
1.1.1.3 ! misho 429:
1.1.1.2 misho 430: do {
431: count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
432: if (count_rules < 0)
433: goto error;
434: } while (count_rules == 10);
1.1.1.3 ! misho 435:
1.1.1.2 misho 436: array = calloc(capacity, sizeof(unsigned short));
437: if(!array) {
438: syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
439: goto error;
440: }
441: *number = 0;
442:
443: for (i=0; i<total_rules-1; i++) {
444: const struct ip_fw const * ptr = &rules[i];
445: unsigned short eport = ptr->fw_uar.fw_pts[0];
446: if (proto == ptr->fw_prot
447: && startport <= eport
448: && eport <= endport) {
449: if(*number >= capacity) {
450: capacity += 128;
451: array = realloc(array, sizeof(unsigned short)*capacity);
452: if(!array) {
453: syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity);
454: *number = 0;
455: goto error;
456: }
457: }
458: array[*number] = eport;
459: (*number)++;
460: }
461: }
462: error:
463: if (rules != NULL)
1.1.1.3 ! misho 464: ipfw_free_ruleset(&rules);
1.1.1.2 misho 465: return array;
466: }
467:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>