1: /* $Id: ipfwrdr.c,v 1.1.1.2 2012/05/29 12:55:57 misho Exp $ */
2: /*
3: * MiniUPnP project
4: * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5: * (c) 2009 Jardel Weyrich
6: * (c) 2011 Thomas Bernard
7: * This software is subject to the conditions detailed
8: * in the LICENCE file provided within the distribution
9: */
10:
11: #include "../config.h"
12:
13: #include <sys/param.h>
14: #include <sys/types.h>
15: #include <sys/file.h>
16:
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: //
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"
68: #include "ipfwrdr.h"
69:
70: #include "../upnpglobalvars.h"
71:
72: /* init and shutdown functions */
73:
74: int init_redirect(void) {
75: return ipfw_exec(IP_FW_INIT, NULL, 0);
76: }
77:
78: void shutdown_redirect(void) {
79: ipfw_exec(IP_FW_TERM, NULL, 0);
80: }
81:
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: /* --- */
156: int add_redirect_rule2(
157: const char * ifname,
158: const char * rhost,
159: unsigned short eport,
160: const char * iaddr,
161: unsigned short iport,
162: int proto,
163: const char * desc,
164: unsigned int timestamp)
165: {
166: struct ip_fw rule;
167: int r;
168:
169: if (ipfw_validate_protocol(proto) < 0)
170: return -1;
171: if (ipfw_validate_ifname(ifname) < 0)
172: return -1;
173:
174: memset(&rule, 0, sizeof(struct ip_fw));
175: rule.version = IP_FW_CURRENT_API_VERSION;
176: //rule.fw_number = 1000; // rule number
177: //rule.context = (void *)desc; // The description is kept in a separate list
178: rule.fw_prot = proto; // protocol
179: rule.fw_flg |= IP_FW_F_IIFACE; // interfaces to check
180: rule.fw_flg |= IP_FW_F_IIFNAME; // interfaces to check by name
181: rule.fw_flg |= (IP_FW_F_IN | IP_FW_F_OUT); // packet direction
182: rule.fw_flg |= IP_FW_F_FWD; // forward action
183: #ifdef USE_IFNAME_IN_RULES
184: if (ifname != NULL) {
185: strlcpy(rule.fw_in_if.fu_via_if.name, ifname, IFNAMSIZ); // src interface
186: rule.fw_in_if.fu_via_if.unit = -1;
187: }
188: #endif
189: if (inet_aton(iaddr, &rule.fw_out_if.fu_via_ip) == 0) {
190: syslog(LOG_ERR, "inet_aton(): %m");
191: return -1;
192: }
193: memcpy(&rule.fw_dst, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
194: memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
195: rule.fw_dmsk.s_addr = INADDR_BROADCAST; //TODO check this
196: IP_FW_SETNDSTP(&rule, 1); // number of external ports
197: rule.fw_uar.fw_pts[0] = eport; // external port
198: rule.fw_fwd_ip.sin_port = iport; // internal port
199: if (rhost && rhost[0] != '\0') {
200: inet_aton(rhost, &rule.fw_src);
201: rule.fw_smsk.s_addr = htonl(INADDR_NONE);
202: }
203:
204: r = ipfw_exec(IP_FW_ADD, &rule, sizeof(rule));
205: if(r >= 0)
206: add_desc_time(eport, proto, desc, timestamp);
207: return r;
208: }
209:
210: /* get_redirect_rule()
211: * return value : 0 success (found)
212: * -1 = error or rule not found */
213: int get_redirect_rule(
214: const char * ifname,
215: unsigned short eport,
216: int proto,
217: char * iaddr,
218: int iaddrlen,
219: unsigned short * iport,
220: char * desc,
221: int desclen,
222: char * rhost,
223: int rhostlen,
224: unsigned int * timestamp,
225: u_int64_t * packets,
226: u_int64_t * bytes)
227: {
228: int i, count_rules, total_rules = 0;
229: struct ip_fw * rules = NULL;
230:
231: if (ipfw_validate_protocol(proto) < 0)
232: return -1;
233: if (ipfw_validate_ifname(ifname) < 0)
234: return -1;
235: if (timestamp)
236: *timestamp = 0;
237:
238: do {
239: count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
240: if (count_rules < 0)
241: goto error;
242: } while (count_rules == 10);
243:
244: for (i=0; i<total_rules-1; i++) {
245: const struct ip_fw const * ptr = &rules[i];
246: if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
247: if (packets != NULL)
248: *packets = ptr->fw_pcnt;
249: if (bytes != NULL)
250: *bytes = ptr->fw_bcnt;
251: if (iport != NULL)
252: *iport = ptr->fw_fwd_ip.sin_port;
253: if (iaddr != NULL && iaddrlen > 0) {
254: /* looks like fw_out_if.fu_via_ip is zero */
255: //if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {
256: if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) {
257: syslog(LOG_ERR, "inet_ntop(): %m");
258: goto error;
259: }
260: }
261: if (rhost != NULL && rhostlen > 0) {
262: if (ptr->fw_src.s_addr == 0)
263: rhost[0] = '\0';
264: else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) {
265: syslog(LOG_ERR, "inet_ntop(): %m");
266: goto error;
267: }
268: }
269: // And what if we found more than 1 matching rule?
270: ipfw_free_ruleset(&rules);
271: get_desc_time(eport, proto, desc, desclen, timestamp);
272: return 0;
273: }
274: }
275:
276: error:
277: if (rules != NULL)
278: ipfw_free_ruleset(&rules);
279: return -1;
280: }
281:
282: int delete_redirect_rule(
283: const char * ifname,
284: unsigned short eport,
285: int proto)
286: {
287: int i, count_rules, total_rules = 0;
288: struct ip_fw * rules = NULL;
289:
290: if (ipfw_validate_protocol(proto) < 0)
291: return -1;
292: if (ipfw_validate_ifname(ifname) < 0)
293: return -1;
294:
295: do {
296: count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
297: if (count_rules < 0)
298: goto error;
299: } while (count_rules == 10);
300:
301: for (i=0; i<total_rules-1; i++) {
302: const struct ip_fw const * ptr = &rules[i];
303: if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
304: if (ipfw_exec(IP_FW_DEL, (struct ip_fw *)ptr, sizeof(*ptr)) < 0)
305: goto error;
306: // And what if we found more than 1 matching rule?
307: ipfw_free_ruleset(&rules);
308: del_desc_time(eport, proto);
309: return 0;
310: }
311: }
312:
313: error:
314: if (rules != NULL)
315: ipfw_free_ruleset(&rules);
316: return -1;
317: }
318:
319: int add_filter_rule2(
320: const char * ifname,
321: const char * rhost,
322: const char * iaddr,
323: unsigned short eport,
324: unsigned short iport,
325: int proto,
326: const char * desc)
327: {
328: //return -1;
329: return 0; /* nothing to do, always success */
330: }
331:
332: int delete_filter_rule(
333: const char * ifname,
334: unsigned short eport,
335: int proto)
336: {
337: //return -1;
338: return 0; /* nothing to do, always success */
339: }
340:
341: int get_redirect_rule_by_index(
342: int index,
343: char * ifname,
344: unsigned short * eport,
345: char * iaddr,
346: int iaddrlen,
347: unsigned short * iport,
348: int * proto,
349: char * desc,
350: int desclen,
351: char * rhost,
352: int rhostlen,
353: unsigned int * timestamp,
354: u_int64_t * packets,
355: u_int64_t * bytes)
356: {
357: int total_rules = 0;
358: struct ip_fw * rules = NULL;
359:
360: if (index < 0) // TODO shouldn't we also validate the maximum?
361: return -1;
362:
363: if(timestamp)
364: *timestamp = 0;
365:
366: ipfw_fetch_ruleset(&rules, &total_rules, index + 1);
367:
368: if (total_rules > index) {
369: const struct ip_fw const * ptr = &rules[index];
370: if (ptr->fw_prot == 0) // invalid rule
371: goto error;
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) {
387: /* looks like fw_out_if.fu_via_ip is zero */
388: //if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {
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;
392: }
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) {
398: syslog(LOG_ERR, "inet_ntop(): %m");
399: goto error;
400: }
401: }
402: ipfw_free_ruleset(&rules);
403: get_desc_time(*eport, *proto, desc, desclen, timestamp);
404: return 0;
405: }
406:
407: error:
408: if (rules != NULL)
409: ipfw_free_ruleset(&rules);
410: return -1;
411: }
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;
426:
427: if (ipfw_validate_protocol(proto) < 0)
428: return NULL;
429:
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);
435:
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)
464: ipfw_free_ruleset(&rules);
465: return array;
466: }
467:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>