Annotation of embedaddon/miniupnpd/netfilter/iptcrdr.c, revision 1.1.1.3
1.1.1.3 ! misho 1: /* $Id: iptcrdr.c,v 1.50 2012/10/03 14:49:08 nanard Exp $ */
1.1 misho 2: /* MiniUPnP project
3: * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
1.1.1.2 misho 4: * (c) 2006-2011 Thomas Bernard
1.1 misho 5: * This software is subject to the conditions detailed
6: * in the LICENCE file provided within the distribution */
7: #include <stdio.h>
8: #include <stdlib.h>
9: #include <string.h>
10: #include <syslog.h>
11: #include <sys/errno.h>
12: #include <sys/socket.h>
13: #include <netinet/in.h>
14: #include <arpa/inet.h>
15: #include <dlfcn.h>
1.1.1.3 ! misho 16: #include <xtables.h>
1.1 misho 17: #include <libiptc/libiptc.h>
18:
19: #include <linux/version.h>
20:
21: #if IPTABLES_143
22: /* IPTABLES API version >= 1.4.3 */
1.1.1.2 misho 23:
24: /* added in order to compile on gentoo :
25: * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=2183 */
26: #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
27: #define __must_be_array(a) \
28: BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(typeof(a), typeof(&a[0])))
29: #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
30: #define LIST_POISON2 ((void *) 0x00200200 )
31:
1.1.1.3 ! misho 32: #if 0
! 33: #include <linux/netfilter/nf_nat.h>
! 34: #else
! 35: #include "tiny_nf_nat.h"
! 36: #endif
1.1 misho 37: #define ip_nat_multi_range nf_nat_multi_range
38: #define ip_nat_range nf_nat_range
39: #define IPTC_HANDLE struct iptc_handle *
40: #else
41: /* IPTABLES API version < 1.4.3 */
42: #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
43: #include <linux/netfilter_ipv4/ip_nat.h>
44: #else
1.1.1.3 ! misho 45: #if 0
1.1 misho 46: #include <linux/netfilter/nf_nat.h>
1.1.1.3 ! misho 47: #else
! 48: #include "tiny_nf_nat.h"
! 49: #endif
1.1 misho 50: #endif
51: #define IPTC_HANDLE iptc_handle_t
52: #endif
53:
1.1.1.2 misho 54: /* IPT_ALIGN was renamed XT_ALIGN in iptables-1.4.11 */
55: #ifndef IPT_ALIGN
56: #define IPT_ALIGN XT_ALIGN
57: #endif
58:
1.1.1.3 ! misho 59: #include "../macros.h"
! 60: #include "../config.h"
1.1 misho 61: #include "iptcrdr.h"
62: #include "../upnpglobalvars.h"
63:
1.1.1.2 misho 64: /* local functions declarations */
65: static int
66: addnatrule(int proto, unsigned short eport,
67: const char * iaddr, unsigned short iport,
68: const char * rhost);
69:
70: static int
71: add_filter_rule(int proto, const char * rhost,
72: const char * iaddr, unsigned short iport);
73:
1.1 misho 74: /* dummy init and shutdown functions */
75: int init_redirect(void)
76: {
77: return 0;
78: }
79:
80: void shutdown_redirect(void)
81: {
82: return;
83: }
84:
85: /* convert an ip address to string */
86: static int snprintip(char * dst, size_t size, uint32_t ip)
87: {
88: return snprintf(dst, size,
89: "%u.%u.%u.%u", ip >> 24, (ip >> 16) & 0xff,
90: (ip >> 8) & 0xff, ip & 0xff);
91: }
92:
93: /* netfilter cannot store redirection descriptions, so we use our
94: * own structure to store them */
95: struct rdr_desc {
96: struct rdr_desc * next;
1.1.1.2 misho 97: unsigned int timestamp;
1.1 misho 98: unsigned short eport;
99: short proto;
100: char str[];
101: };
102:
103: /* pointer to the chained list where descriptions are stored */
104: static struct rdr_desc * rdr_desc_list = 0;
105:
1.1.1.2 misho 106: /* add a description to the list of redirection descriptions */
1.1 misho 107: static void
1.1.1.2 misho 108: add_redirect_desc(unsigned short eport, int proto,
109: const char * desc, unsigned int timestamp)
1.1 misho 110: {
111: struct rdr_desc * p;
112: size_t l;
1.1.1.2 misho 113: /* set a default description if none given */
114: if(!desc)
115: desc = "miniupnpd";
116: l = strlen(desc) + 1;
117: p = malloc(sizeof(struct rdr_desc) + l);
118: if(p)
119: {
120: p->next = rdr_desc_list;
121: p->timestamp = timestamp;
122: p->eport = eport;
123: p->proto = (short)proto;
124: memcpy(p->str, desc, l);
125: rdr_desc_list = p;
126: }
1.1 misho 127: }
128:
1.1.1.2 misho 129: /* delete a description from the list */
1.1 misho 130: static void
131: del_redirect_desc(unsigned short eport, int proto)
132: {
133: struct rdr_desc * p, * last;
134: p = rdr_desc_list;
135: last = 0;
136: while(p)
137: {
138: if(p->eport == eport && p->proto == proto)
139: {
140: if(!last)
141: rdr_desc_list = p->next;
142: else
143: last->next = p->next;
144: free(p);
145: return;
146: }
147: last = p;
148: p = p->next;
149: }
150: }
151:
1.1.1.2 misho 152: /* go through the list to find the description */
1.1 misho 153: static void
154: get_redirect_desc(unsigned short eport, int proto,
1.1.1.2 misho 155: char * desc, int desclen,
156: unsigned int * timestamp)
1.1 misho 157: {
158: struct rdr_desc * p;
159: for(p = rdr_desc_list; p; p = p->next)
160: {
161: if(p->eport == eport && p->proto == (short)proto)
162: {
1.1.1.2 misho 163: if(desc)
164: strncpy(desc, p->str, desclen);
165: if(timestamp)
166: *timestamp = p->timestamp;
1.1 misho 167: return;
168: }
169: }
170: /* if no description was found, return miniupnpd as default */
1.1.1.2 misho 171: if(desc)
172: strncpy(desc, "miniupnpd", desclen);
173: if(timestamp)
174: *timestamp = 0;
1.1 misho 175: }
176:
1.1.1.2 misho 177: #if USE_INDEX_FROM_DESC_LIST
178: static int
1.1 misho 179: get_redirect_desc_by_index(int index, unsigned short * eport, int * proto,
1.1.1.2 misho 180: char * desc, int desclen, unsigned int * timestamp)
1.1 misho 181: {
182: int i = 0;
183: struct rdr_desc * p;
184: if(!desc || (desclen == 0))
185: return -1;
186: for(p = rdr_desc_list; p; p = p->next, i++)
187: {
188: if(i == index)
189: {
190: *eport = p->eport;
191: *proto = (int)p->proto;
192: strncpy(desc, p->str, desclen);
1.1.1.2 misho 193: if(timestamp)
194: *timestamp = p->timestamp;
1.1 misho 195: return 0;
196: }
197: }
198: return -1;
199: }
1.1.1.2 misho 200: #endif
1.1 misho 201:
202: /* add_redirect_rule2() */
203: int
1.1.1.2 misho 204: add_redirect_rule2(const char * ifname,
205: const char * rhost, unsigned short eport,
1.1 misho 206: const char * iaddr, unsigned short iport, int proto,
1.1.1.2 misho 207: const char * desc, unsigned int timestamp)
1.1 misho 208: {
1.1.1.3 ! misho 209: int r;
! 210: UNUSED(ifname);
! 211:
! 212: r = addnatrule(proto, eport, iaddr, iport, rhost);
1.1 misho 213: if(r >= 0)
1.1.1.2 misho 214: add_redirect_desc(eport, proto, desc, timestamp);
1.1 misho 215: return r;
216: }
217:
218: int
1.1.1.2 misho 219: add_filter_rule2(const char * ifname,
220: const char * rhost, const char * iaddr,
1.1 misho 221: unsigned short eport, unsigned short iport,
222: int proto, const char * desc)
223: {
1.1.1.3 ! misho 224: UNUSED(ifname);
! 225: UNUSED(eport);
! 226: UNUSED(desc);
! 227:
1.1.1.2 misho 228: return add_filter_rule(proto, rhost, iaddr, iport);
1.1 misho 229: }
230:
1.1.1.3 ! misho 231: /* get_redirect_rule()
1.1 misho 232: * returns -1 if the rule is not found */
233: int
234: get_redirect_rule(const char * ifname, unsigned short eport, int proto,
235: char * iaddr, int iaddrlen, unsigned short * iport,
236: char * desc, int desclen,
1.1.1.2 misho 237: char * rhost, int rhostlen,
238: unsigned int * timestamp,
1.1 misho 239: u_int64_t * packets, u_int64_t * bytes)
240: {
241: int r = -1;
242: IPTC_HANDLE h;
243: const struct ipt_entry * e;
244: const struct ipt_entry_target * target;
245: const struct ip_nat_multi_range * mr;
246: const struct ipt_entry_match *match;
1.1.1.3 ! misho 247: UNUSED(ifname);
1.1 misho 248:
249: h = iptc_init("nat");
250: if(!h)
251: {
252: syslog(LOG_ERR, "get_redirect_rule() : "
253: "iptc_init() failed : %s",
254: iptc_strerror(errno));
255: return -1;
256: }
257: if(!iptc_is_chain(miniupnpd_nat_chain, h))
258: {
259: syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain);
260: }
261: else
262: {
263: #ifdef IPTABLES_143
264: for(e = iptc_first_rule(miniupnpd_nat_chain, h);
265: e;
266: e = iptc_next_rule(e, h))
267: #else
268: for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
269: e;
270: e = iptc_next_rule(e, &h))
271: #endif
272: {
273: if(proto==e->ip.proto)
274: {
275: match = (const struct ipt_entry_match *)&e->elems;
276: if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
277: {
278: const struct ipt_tcp * info;
279: info = (const struct ipt_tcp *)match->data;
280: if(eport != info->dpts[0])
281: continue;
282: }
283: else
284: {
285: const struct ipt_udp * info;
286: info = (const struct ipt_udp *)match->data;
287: if(eport != info->dpts[0])
288: continue;
289: }
290: target = (void *)e + e->target_offset;
1.1.1.3 ! misho 291: /* target = ipt_get_target(e); */
1.1 misho 292: mr = (const struct ip_nat_multi_range *)&target->data[0];
293: snprintip(iaddr, iaddrlen, ntohl(mr->range[0].min_ip));
294: *iport = ntohs(mr->range[0].min.all);
1.1.1.2 misho 295: get_redirect_desc(eport, proto, desc, desclen, timestamp);
1.1 misho 296: if(packets)
297: *packets = e->counters.pcnt;
298: if(bytes)
299: *bytes = e->counters.bcnt;
1.1.1.2 misho 300: /* rhost */
301: if(e->ip.src.s_addr && rhost) {
302: snprintip(rhost, rhostlen, ntohl(e->ip.src.s_addr));
303: }
1.1 misho 304: r = 0;
305: break;
306: }
307: }
308: }
309: if(h)
310: #ifdef IPTABLES_143
311: iptc_free(h);
312: #else
313: iptc_free(&h);
314: #endif
315: return r;
316: }
317:
1.1.1.3 ! misho 318: /* get_redirect_rule_by_index()
1.1 misho 319: * return -1 when the rule was not found */
320: int
321: get_redirect_rule_by_index(int index,
322: char * ifname, unsigned short * eport,
323: char * iaddr, int iaddrlen, unsigned short * iport,
324: int * proto, char * desc, int desclen,
1.1.1.2 misho 325: char * rhost, int rhostlen,
326: unsigned int * timestamp,
1.1 misho 327: u_int64_t * packets, u_int64_t * bytes)
328: {
329: int r = -1;
1.1.1.2 misho 330: #if USE_INDEX_FROM_DESC_LIST
331: r = get_redirect_desc_by_index(index, eport, proto,
332: desc, desclen, timestamp);
1.1 misho 333: if (r==0)
1.1.1.2 misho 334: {
1.1 misho 335: r = get_redirect_rule(ifname, *eport, *proto, iaddr, iaddrlen, iport,
336: 0, 0, packets, bytes);
1.1.1.2 misho 337: }
338: #else
1.1 misho 339: int i = 0;
340: IPTC_HANDLE h;
341: const struct ipt_entry * e;
342: const struct ipt_entry_target * target;
343: const struct ip_nat_multi_range * mr;
344: const struct ipt_entry_match *match;
1.1.1.3 ! misho 345: UNUSED(ifname);
1.1 misho 346:
347: h = iptc_init("nat");
348: if(!h)
349: {
350: syslog(LOG_ERR, "get_redirect_rule_by_index() : "
351: "iptc_init() failed : %s",
352: iptc_strerror(errno));
353: return -1;
354: }
355: if(!iptc_is_chain(miniupnpd_nat_chain, h))
356: {
357: syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain);
358: }
359: else
360: {
361: #ifdef IPTABLES_143
362: for(e = iptc_first_rule(miniupnpd_nat_chain, h);
363: e;
364: e = iptc_next_rule(e, h))
365: #else
366: for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
367: e;
368: e = iptc_next_rule(e, &h))
369: #endif
370: {
371: if(i==index)
372: {
373: *proto = e->ip.proto;
374: match = (const struct ipt_entry_match *)&e->elems;
375: if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
376: {
377: const struct ipt_tcp * info;
378: info = (const struct ipt_tcp *)match->data;
379: *eport = info->dpts[0];
380: }
381: else
382: {
383: const struct ipt_udp * info;
384: info = (const struct ipt_udp *)match->data;
385: *eport = info->dpts[0];
386: }
387: target = (void *)e + e->target_offset;
388: mr = (const struct ip_nat_multi_range *)&target->data[0];
389: snprintip(iaddr, iaddrlen, ntohl(mr->range[0].min_ip));
390: *iport = ntohs(mr->range[0].min.all);
1.1.1.2 misho 391: get_redirect_desc(*eport, *proto, desc, desclen, timestamp);
1.1 misho 392: if(packets)
393: *packets = e->counters.pcnt;
394: if(bytes)
395: *bytes = e->counters.bcnt;
1.1.1.2 misho 396: /* rhost */
397: if(rhost && rhostlen > 0) {
398: if(e->ip.src.s_addr) {
399: snprintip(rhost, rhostlen, ntohl(e->ip.src.s_addr));
400: } else {
401: rhost[0] = '\0';
402: }
403: }
1.1 misho 404: r = 0;
405: break;
406: }
407: i++;
408: }
409: }
410: if(h)
411: #ifdef IPTABLES_143
412: iptc_free(h);
413: #else
414: iptc_free(&h);
415: #endif
1.1.1.2 misho 416: #endif
1.1 misho 417: return r;
418: }
419:
420: /* delete_rule_and_commit() :
421: * subfunction used in delete_redirect_and_filter_rules() */
1.1.1.2 misho 422: static int
423: delete_rule_and_commit(unsigned int index, IPTC_HANDLE h,
424: const char * miniupnpd_chain,
425: const char * logcaller)
426: {
427: int r = 0;
428: #ifdef IPTABLES_143
429: if(!iptc_delete_num_entry(miniupnpd_chain, index, h))
430: #else
431: if(!iptc_delete_num_entry(miniupnpd_chain, index, &h))
432: #endif
433: {
434: syslog(LOG_ERR, "%s() : iptc_delete_num_entry(): %s\n",
435: logcaller, iptc_strerror(errno));
436: r = -1;
437: }
438: #ifdef IPTABLES_143
439: else if(!iptc_commit(h))
440: #else
441: else if(!iptc_commit(&h))
442: #endif
443: {
444: syslog(LOG_ERR, "%s() : iptc_commit(): %s\n",
445: logcaller, iptc_strerror(errno));
446: r = -1;
447: }
448: if(h)
449: #ifdef IPTABLES_143
450: iptc_free(h);
451: #else
452: iptc_free(&h);
453: #endif
454: return r;
455: }
456:
457: /* delete_redirect_and_filter_rules()
458: */
1.1 misho 459: int
1.1.1.2 misho 460: delete_redirect_and_filter_rules(unsigned short eport, int proto)
1.1 misho 461: {
462: int r = -1;
463: unsigned index = 0;
464: unsigned i = 0;
465: IPTC_HANDLE h;
466: const struct ipt_entry * e;
1.1.1.2 misho 467: const struct ipt_entry_target * target;
468: const struct ip_nat_multi_range * mr;
1.1 misho 469: const struct ipt_entry_match *match;
1.1.1.2 misho 470: unsigned short iport = 0;
471: uint32_t iaddr = 0;
1.1 misho 472:
1.1.1.2 misho 473: h = iptc_init("nat");
1.1 misho 474: if(!h)
475: {
1.1.1.2 misho 476: syslog(LOG_ERR, "delete_redirect_and_filter_rules() : "
477: "iptc_init() failed : %s",
1.1 misho 478: iptc_strerror(errno));
479: return -1;
480: }
1.1.1.2 misho 481: /* First step : find the right nat rule */
482: if(!iptc_is_chain(miniupnpd_nat_chain, h))
1.1 misho 483: {
1.1.1.2 misho 484: syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain);
1.1 misho 485: }
486: else
487: {
488: #ifdef IPTABLES_143
1.1.1.2 misho 489: for(e = iptc_first_rule(miniupnpd_nat_chain, h);
1.1 misho 490: e;
491: e = iptc_next_rule(e, h), i++)
492: #else
1.1.1.2 misho 493: for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
1.1 misho 494: e;
495: e = iptc_next_rule(e, &h), i++)
496: #endif
497: {
498: if(proto==e->ip.proto)
499: {
500: match = (const struct ipt_entry_match *)&e->elems;
501: if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
502: {
503: const struct ipt_tcp * info;
504: info = (const struct ipt_tcp *)match->data;
505: if(eport != info->dpts[0])
506: continue;
507: }
508: else
509: {
510: const struct ipt_udp * info;
511: info = (const struct ipt_udp *)match->data;
512: if(eport != info->dpts[0])
513: continue;
514: }
1.1.1.2 misho 515: /* get the index, the internal address and the internal port
516: * of the rule */
1.1 misho 517: index = i;
1.1.1.2 misho 518: target = (void *)e + e->target_offset;
519: mr = (const struct ip_nat_multi_range *)&target->data[0];
520: iaddr = mr->range[0].min_ip;
521: iport = ntohs(mr->range[0].min.all);
522: r = 0;
1.1 misho 523: break;
524: }
525: }
526: }
527: if(h)
528: #ifdef IPTABLES_143
529: iptc_free(h);
530: #else
531: iptc_free(&h);
532: #endif
1.1.1.2 misho 533: if(r == 0)
1.1 misho 534: {
1.1.1.2 misho 535: syslog(LOG_INFO, "Trying to delete nat rule at index %u", index);
536: /* Now delete both rules */
537: /* first delete the nat rule */
538: h = iptc_init("nat");
539: if(h)
540: {
541: r = delete_rule_and_commit(index, h, miniupnpd_nat_chain, "delete_redirect_rule");
542: }
543: if((r == 0) && (h = iptc_init("filter")))
544: {
545: i = 0;
546: /* we must find the right index for the filter rule */
1.1 misho 547: #ifdef IPTABLES_143
1.1.1.2 misho 548: for(e = iptc_first_rule(miniupnpd_forward_chain, h);
549: e;
550: e = iptc_next_rule(e, h), i++)
1.1 misho 551: #else
1.1.1.2 misho 552: for(e = iptc_first_rule(miniupnpd_forward_chain, &h);
553: e;
554: e = iptc_next_rule(e, &h), i++)
1.1 misho 555: #endif
1.1.1.2 misho 556: {
557: if(proto==e->ip.proto)
558: {
559: match = (const struct ipt_entry_match *)&e->elems;
560: /*syslog(LOG_DEBUG, "filter rule #%u: %s %s",
561: i, match->u.user.name, inet_ntoa(e->ip.dst));*/
562: if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
563: {
564: const struct ipt_tcp * info;
565: info = (const struct ipt_tcp *)match->data;
566: if(iport != info->dpts[0])
567: continue;
568: }
569: else
570: {
571: const struct ipt_udp * info;
572: info = (const struct ipt_udp *)match->data;
573: if(iport != info->dpts[0])
574: continue;
575: }
576: if(iaddr != e->ip.dst.s_addr)
577: continue;
578: index = i;
579: break;
580: }
581: }
582: syslog(LOG_INFO, "Trying to delete filter rule at index %u", index);
583: r = delete_rule_and_commit(index, h, miniupnpd_forward_chain, "delete_filter_rule");
584: }
1.1 misho 585: }
1.1.1.2 misho 586: del_redirect_desc(eport, proto);
1.1 misho 587: return r;
588: }
589:
590: /* ==================================== */
1.1.1.3 ! misho 591: /* TODO : add the -m state --state NEW,ESTABLISHED,RELATED
1.1 misho 592: * only for the filter rule */
593: static struct ipt_entry_match *
594: get_tcp_match(unsigned short dport)
595: {
596: struct ipt_entry_match *match;
597: struct ipt_tcp * tcpinfo;
598: size_t size;
599: size = IPT_ALIGN(sizeof(struct ipt_entry_match))
600: + IPT_ALIGN(sizeof(struct ipt_tcp));
601: match = calloc(1, size);
602: match->u.match_size = size;
1.1.1.2 misho 603: strncpy(match->u.user.name, "tcp", sizeof(match->u.user.name));
1.1 misho 604: tcpinfo = (struct ipt_tcp *)match->data;
605: tcpinfo->spts[0] = 0; /* all source ports */
606: tcpinfo->spts[1] = 0xFFFF;
607: tcpinfo->dpts[0] = dport; /* specified destination port */
608: tcpinfo->dpts[1] = dport;
609: return match;
610: }
611:
612: static struct ipt_entry_match *
613: get_udp_match(unsigned short dport)
614: {
615: struct ipt_entry_match *match;
616: struct ipt_udp * udpinfo;
617: size_t size;
618: size = IPT_ALIGN(sizeof(struct ipt_entry_match))
619: + IPT_ALIGN(sizeof(struct ipt_udp));
620: match = calloc(1, size);
621: match->u.match_size = size;
1.1.1.2 misho 622: strncpy(match->u.user.name, "udp", sizeof(match->u.user.name));
1.1 misho 623: udpinfo = (struct ipt_udp *)match->data;
624: udpinfo->spts[0] = 0; /* all source ports */
625: udpinfo->spts[1] = 0xFFFF;
626: udpinfo->dpts[0] = dport; /* specified destination port */
627: udpinfo->dpts[1] = dport;
628: return match;
629: }
630:
631: static struct ipt_entry_target *
632: get_dnat_target(const char * daddr, unsigned short dport)
633: {
634: struct ipt_entry_target * target;
635: struct ip_nat_multi_range * mr;
636: struct ip_nat_range * range;
637: size_t size;
638:
639: size = IPT_ALIGN(sizeof(struct ipt_entry_target))
640: + IPT_ALIGN(sizeof(struct ip_nat_multi_range));
641: target = calloc(1, size);
642: target->u.target_size = size;
1.1.1.2 misho 643: strncpy(target->u.user.name, "DNAT", sizeof(target->u.user.name));
1.1 misho 644: /* one ip_nat_range already included in ip_nat_multi_range */
645: mr = (struct ip_nat_multi_range *)&target->data[0];
646: mr->rangesize = 1;
647: range = &mr->range[0];
648: range->min_ip = range->max_ip = inet_addr(daddr);
649: range->flags |= IP_NAT_RANGE_MAP_IPS;
650: range->min.all = range->max.all = htons(dport);
651: range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
652: return target;
653: }
654:
655: /* iptc_init_verify_and_append()
656: * return 0 on success, -1 on failure */
657: static int
1.1.1.2 misho 658: iptc_init_verify_and_append(const char * table,
659: const char * miniupnpd_chain,
660: struct ipt_entry * e,
1.1 misho 661: const char * logcaller)
662: {
663: IPTC_HANDLE h;
664: h = iptc_init(table);
665: if(!h)
666: {
667: syslog(LOG_ERR, "%s : iptc_init() error : %s\n",
668: logcaller, iptc_strerror(errno));
669: return -1;
670: }
671: if(!iptc_is_chain(miniupnpd_chain, h))
672: {
1.1.1.2 misho 673: syslog(LOG_ERR, "%s : chain %s not found",
674: logcaller, miniupnpd_chain);
1.1 misho 675: if(h)
676: #ifdef IPTABLES_143
677: iptc_free(h);
678: #else
679: iptc_free(&h);
680: #endif
681: return -1;
682: }
683: /* iptc_insert_entry(miniupnpd_chain, e, n, h/&h) could also be used */
684: #ifdef IPTABLES_143
685: if(!iptc_append_entry(miniupnpd_chain, e, h))
686: #else
687: if(!iptc_append_entry(miniupnpd_chain, e, &h))
688: #endif
689: {
690: syslog(LOG_ERR, "%s : iptc_append_entry() error : %s\n",
691: logcaller, iptc_strerror(errno));
692: if(h)
693: #ifdef IPTABLES_143
694: iptc_free(h);
695: #else
696: iptc_free(&h);
697: #endif
698: return -1;
699: }
700: #ifdef IPTABLES_143
701: if(!iptc_commit(h))
702: #else
703: if(!iptc_commit(&h))
704: #endif
705: {
706: syslog(LOG_ERR, "%s : iptc_commit() error : %s\n",
707: logcaller, iptc_strerror(errno));
708: if(h)
709: #ifdef IPTABLES_143
710: iptc_free(h);
711: #else
712: iptc_free(&h);
713: #endif
714: return -1;
715: }
716: if(h)
717: #ifdef IPTABLES_143
718: iptc_free(h);
719: #else
720: iptc_free(&h);
721: #endif
722: return 0;
723: }
724:
1.1.1.3 ! misho 725: /* add nat rule
1.1 misho 726: * iptables -t nat -A MINIUPNPD -p proto --dport eport -j DNAT --to iaddr:iport
727: * */
1.1.1.2 misho 728: static int
1.1 misho 729: addnatrule(int proto, unsigned short eport,
1.1.1.2 misho 730: const char * iaddr, unsigned short iport,
731: const char * rhost)
1.1 misho 732: {
733: int r = 0;
734: struct ipt_entry * e;
735: struct ipt_entry_match *match = NULL;
736: struct ipt_entry_target *target = NULL;
737:
738: e = calloc(1, sizeof(struct ipt_entry));
739: e->ip.proto = proto;
740: if(proto == IPPROTO_TCP)
741: {
742: match = get_tcp_match(eport);
743: }
744: else
745: {
746: match = get_udp_match(eport);
747: }
748: e->nfcache = NFC_IP_DST_PT;
749: target = get_dnat_target(iaddr, iport);
750: e->nfcache |= NFC_UNKNOWN;
751: e = realloc(e, sizeof(struct ipt_entry)
752: + match->u.match_size
753: + target->u.target_size);
754: memcpy(e->elems, match, match->u.match_size);
755: memcpy(e->elems + match->u.match_size, target, target->u.target_size);
756: e->target_offset = sizeof(struct ipt_entry)
757: + match->u.match_size;
758: e->next_offset = sizeof(struct ipt_entry)
759: + match->u.match_size
760: + target->u.target_size;
1.1.1.2 misho 761: /* remote host */
762: if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*")))
763: {
764: e->ip.src.s_addr = inet_addr(rhost);
765: e->ip.smsk.s_addr = INADDR_NONE;
766: }
1.1.1.3 ! misho 767:
1.1 misho 768: r = iptc_init_verify_and_append("nat", miniupnpd_nat_chain, e, "addnatrule()");
769: free(target);
770: free(match);
771: free(e);
772: return r;
773: }
774: /* ================================= */
775: static struct ipt_entry_target *
776: get_accept_target(void)
777: {
778: struct ipt_entry_target * target = NULL;
779: size_t size;
780: size = IPT_ALIGN(sizeof(struct ipt_entry_target))
781: + IPT_ALIGN(sizeof(int));
782: target = calloc(1, size);
783: target->u.user.target_size = size;
1.1.1.2 misho 784: strncpy(target->u.user.name, "ACCEPT", sizeof(target->u.user.name));
1.1 misho 785: return target;
786: }
787:
788: /* add_filter_rule()
789: * */
1.1.1.2 misho 790: static int
791: add_filter_rule(int proto, const char * rhost,
792: const char * iaddr, unsigned short iport)
1.1 misho 793: {
794: int r = 0;
795: struct ipt_entry * e;
796: struct ipt_entry_match *match = NULL;
797: struct ipt_entry_target *target = NULL;
798:
799: e = calloc(1, sizeof(struct ipt_entry));
800: e->ip.proto = proto;
801: if(proto == IPPROTO_TCP)
802: {
803: match = get_tcp_match(iport);
804: }
805: else
806: {
807: match = get_udp_match(iport);
808: }
809: e->nfcache = NFC_IP_DST_PT;
810: e->ip.dst.s_addr = inet_addr(iaddr);
811: e->ip.dmsk.s_addr = INADDR_NONE;
812: target = get_accept_target();
813: e->nfcache |= NFC_UNKNOWN;
814: e = realloc(e, sizeof(struct ipt_entry)
815: + match->u.match_size
816: + target->u.target_size);
817: memcpy(e->elems, match, match->u.match_size);
818: memcpy(e->elems + match->u.match_size, target, target->u.target_size);
819: e->target_offset = sizeof(struct ipt_entry)
820: + match->u.match_size;
821: e->next_offset = sizeof(struct ipt_entry)
822: + match->u.match_size
823: + target->u.target_size;
1.1.1.2 misho 824: /* remote host */
825: if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*")))
826: {
827: e->ip.src.s_addr = inet_addr(rhost);
828: e->ip.smsk.s_addr = INADDR_NONE;
829: }
1.1.1.3 ! misho 830:
1.1 misho 831: r = iptc_init_verify_and_append("filter", miniupnpd_forward_chain, e, "add_filter_rule()");
832: free(target);
833: free(match);
834: free(e);
835: return r;
836: }
837:
1.1.1.2 misho 838: /* return an (malloc'ed) array of "external" port for which there is
839: * a port mapping. number is the size of the array */
840: unsigned short *
841: get_portmappings_in_range(unsigned short startport, unsigned short endport,
842: int proto, unsigned int * number)
843: {
844: unsigned short * array;
845: unsigned int capacity;
846: unsigned short eport;
847: IPTC_HANDLE h;
848: const struct ipt_entry * e;
849: const struct ipt_entry_match *match;
850:
851: *number = 0;
852: capacity = 128;
853: array = calloc(capacity, sizeof(unsigned short));
854: if(!array)
855: {
856: syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
857: return NULL;
858: }
859:
860: h = iptc_init("nat");
861: if(!h)
862: {
863: syslog(LOG_ERR, "get_redirect_rule_by_index() : "
864: "iptc_init() failed : %s",
865: iptc_strerror(errno));
866: free(array);
867: return NULL;
868: }
869: if(!iptc_is_chain(miniupnpd_nat_chain, h))
870: {
871: syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain);
872: free(array);
873: array = NULL;
874: }
875: else
876: {
877: #ifdef IPTABLES_143
878: for(e = iptc_first_rule(miniupnpd_nat_chain, h);
879: e;
880: e = iptc_next_rule(e, h))
881: #else
882: for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
883: e;
884: e = iptc_next_rule(e, &h))
885: #endif
886: {
887: if(proto == e->ip.proto)
888: {
889: match = (const struct ipt_entry_match *)&e->elems;
890: if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
891: {
892: const struct ipt_tcp * info;
893: info = (const struct ipt_tcp *)match->data;
894: eport = info->dpts[0];
895: }
896: else
897: {
898: const struct ipt_udp * info;
899: info = (const struct ipt_udp *)match->data;
900: eport = info->dpts[0];
901: }
902: if(startport <= eport && eport <= endport)
903: {
904: if(*number >= capacity)
905: {
906: /* need to increase the capacity of the array */
907: array = realloc(array, sizeof(unsigned short)*capacity);
908: if(!array)
909: {
910: syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%u) error",
911: (unsigned)sizeof(unsigned short)*capacity);
912: *number = 0;
913: break;
914: }
915: array[*number] = eport;
916: (*number)++;
917: }
918: }
919: }
920: }
921: }
922: if(h)
923: #ifdef IPTABLES_143
924: iptc_free(h);
925: #else
926: iptc_free(&h);
927: #endif
928: return array;
929: }
930:
1.1 misho 931: /* ================================ */
1.1.1.2 misho 932: #ifdef DEBUG
1.1 misho 933: static int
934: print_match(const struct ipt_entry_match *match)
935: {
936: printf("match %s\n", match->u.user.name);
937: if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
938: {
939: struct ipt_tcp * tcpinfo;
940: tcpinfo = (struct ipt_tcp *)match->data;
941: printf("srcport = %hu:%hu dstport = %hu:%hu\n",
942: tcpinfo->spts[0], tcpinfo->spts[1],
943: tcpinfo->dpts[0], tcpinfo->dpts[1]);
944: }
945: else if(0 == strncmp(match->u.user.name, "udp", IPT_FUNCTION_MAXNAMELEN))
946: {
947: struct ipt_udp * udpinfo;
948: udpinfo = (struct ipt_udp *)match->data;
949: printf("srcport = %hu:%hu dstport = %hu:%hu\n",
950: udpinfo->spts[0], udpinfo->spts[1],
951: udpinfo->dpts[0], udpinfo->dpts[1]);
952: }
953: return 0;
954: }
955:
956: static void
957: print_iface(const char * iface, const unsigned char * mask, int invert)
958: {
959: unsigned i;
960: if(mask[0] == 0)
961: return;
962: if(invert)
963: printf("! ");
964: for(i=0; i<IFNAMSIZ; i++)
965: {
966: if(mask[i])
967: {
968: if(iface[i])
969: putchar(iface[i]);
970: }
971: else
972: {
973: if(iface[i-1])
974: putchar('+');
975: break;
976: }
977: }
978: }
979:
980: static void
981: printip(uint32_t ip)
982: {
983: printf("%u.%u.%u.%u", ip >> 24, (ip >> 16) & 0xff,
984: (ip >> 8) & 0xff, ip & 0xff);
985: }
986:
987: /* for debug */
988: /* read the "filter" and "nat" tables */
989: int
990: list_redirect_rule(const char * ifname)
991: {
992: IPTC_HANDLE h;
993: const struct ipt_entry * e;
994: const struct ipt_entry_target * target;
995: const struct ip_nat_multi_range * mr;
996: const char * target_str;
1.1.1.2 misho 997: char addr[16], mask[16];
1.1.1.3 ! misho 998: (void)ifname;
1.1 misho 999:
1000: h = iptc_init("nat");
1001: if(!h)
1002: {
1003: printf("iptc_init() error : %s\n", iptc_strerror(errno));
1004: return -1;
1005: }
1006: if(!iptc_is_chain(miniupnpd_nat_chain, h))
1007: {
1008: printf("chain %s not found\n", miniupnpd_nat_chain);
1009: #ifdef IPTABLES_143
1010: iptc_free(h);
1011: #else
1012: iptc_free(&h);
1013: #endif
1014: return -1;
1015: }
1016: #ifdef IPTABLES_143
1017: for(e = iptc_first_rule(miniupnpd_nat_chain, h);
1018: e;
1019: e = iptc_next_rule(e, h))
1020: {
1021: target_str = iptc_get_target(e, h);
1022: #else
1023: for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
1024: e;
1025: e = iptc_next_rule(e, &h))
1026: {
1027: target_str = iptc_get_target(e, &h);
1028: #endif
1029: printf("===\n");
1.1.1.2 misho 1030: inet_ntop(AF_INET, &e->ip.src, addr, sizeof(addr));
1031: inet_ntop(AF_INET, &e->ip.smsk, mask, sizeof(mask));
1.1 misho 1032: printf("src = %s%s/%s\n", (e->ip.invflags & IPT_INV_SRCIP)?"! ":"",
1.1.1.2 misho 1033: /*inet_ntoa(e->ip.src), inet_ntoa(e->ip.smsk)*/
1034: addr, mask);
1035: inet_ntop(AF_INET, &e->ip.dst, addr, sizeof(addr));
1036: inet_ntop(AF_INET, &e->ip.dmsk, mask, sizeof(mask));
1.1 misho 1037: printf("dst = %s%s/%s\n", (e->ip.invflags & IPT_INV_DSTIP)?"! ":"",
1.1.1.2 misho 1038: /*inet_ntoa(e->ip.dst), inet_ntoa(e->ip.dmsk)*/
1039: addr, mask);
1.1 misho 1040: /*printf("in_if = %s out_if = %s\n", e->ip.iniface, e->ip.outiface);*/
1041: printf("in_if = ");
1042: print_iface(e->ip.iniface, e->ip.iniface_mask,
1043: e->ip.invflags & IPT_INV_VIA_IN);
1044: printf(" out_if = ");
1045: print_iface(e->ip.outiface, e->ip.outiface_mask,
1046: e->ip.invflags & IPT_INV_VIA_OUT);
1047: printf("\n");
1048: printf("ip.proto = %s%d\n", (e->ip.invflags & IPT_INV_PROTO)?"! ":"",
1049: e->ip.proto);
1050: /* display matches stuff */
1051: if(e->target_offset)
1052: {
1053: IPT_MATCH_ITERATE(e, print_match);
1054: /*printf("\n");*/
1055: }
1056: printf("target = %s\n", target_str);
1057: target = (void *)e + e->target_offset;
1058: mr = (const struct ip_nat_multi_range *)&target->data[0];
1059: printf("ips ");
1060: printip(ntohl(mr->range[0].min_ip));
1061: printf(" ");
1062: printip(ntohl(mr->range[0].max_ip));
1063: printf("\nports %hu %hu\n", ntohs(mr->range[0].min.all),
1064: ntohs(mr->range[0].max.all));
1065: printf("flags = %x\n", mr->range[0].flags);
1066: }
1067: if(h)
1068: #ifdef IPTABLES_143
1069: iptc_free(h);
1070: #else
1071: iptc_free(&h);
1072: #endif
1073: return 0;
1074: }
1.1.1.2 misho 1075: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>