--- embedaddon/miniupnpd/netfilter/iptcrdr.c 2012/02/21 23:16:02 1.1 +++ embedaddon/miniupnpd/netfilter/iptcrdr.c 2012/05/29 12:55:57 1.1.1.2 @@ -1,7 +1,7 @@ -/* $Id: iptcrdr.c,v 1.1 2012/02/21 23:16:02 misho Exp $ */ +/* $Id: iptcrdr.c,v 1.1.1.2 2012/05/29 12:55:57 misho Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2008 Thomas Bernard + * (c) 2006-2011 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include @@ -20,6 +20,15 @@ #if IPTABLES_143 /* IPTABLES API version >= 1.4.3 */ + +/* added in order to compile on gentoo : + * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=2183 */ +#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) +#define __must_be_array(a) \ + BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(typeof(a), typeof(&a[0]))) +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) +#define LIST_POISON2 ((void *) 0x00200200 ) + #include #define ip_nat_multi_range nf_nat_multi_range #define ip_nat_range nf_nat_range @@ -34,9 +43,24 @@ #define IPTC_HANDLE iptc_handle_t #endif +/* IPT_ALIGN was renamed XT_ALIGN in iptables-1.4.11 */ +#ifndef IPT_ALIGN +#define IPT_ALIGN XT_ALIGN +#endif + #include "iptcrdr.h" #include "../upnpglobalvars.h" +/* local functions declarations */ +static int +addnatrule(int proto, unsigned short eport, + const char * iaddr, unsigned short iport, + const char * rhost); + +static int +add_filter_rule(int proto, const char * rhost, + const char * iaddr, unsigned short iport); + /* dummy init and shutdown functions */ int init_redirect(void) { @@ -60,6 +84,7 @@ static int snprintip(char * dst, size_t size, uint32_t * own structure to store them */ struct rdr_desc { struct rdr_desc * next; + unsigned int timestamp; unsigned short eport; short proto; char str[]; @@ -68,26 +93,30 @@ struct rdr_desc { /* pointer to the chained list where descriptions are stored */ static struct rdr_desc * rdr_desc_list = 0; +/* add a description to the list of redirection descriptions */ static void -add_redirect_desc(unsigned short eport, int proto, const char * desc) +add_redirect_desc(unsigned short eport, int proto, + const char * desc, unsigned int timestamp) { struct rdr_desc * p; size_t l; -/* if(desc) - {*/ - if ((l = strlen(desc) + 1) == 1) l = 5; - p = malloc(sizeof(struct rdr_desc) + l); - if(p) - { - p->next = rdr_desc_list; - p->eport = eport; - p->proto = (short)proto; - if(desc) memcpy(p->str, desc, l); else memcpy(p->str, "upnp", 4); - rdr_desc_list = p; - } -/* }*/ + /* set a default description if none given */ + if(!desc) + desc = "miniupnpd"; + l = strlen(desc) + 1; + p = malloc(sizeof(struct rdr_desc) + l); + if(p) + { + p->next = rdr_desc_list; + p->timestamp = timestamp; + p->eport = eport; + p->proto = (short)proto; + memcpy(p->str, desc, l); + rdr_desc_list = p; + } } +/* delete a description from the list */ static void del_redirect_desc(unsigned short eport, int proto) { @@ -110,28 +139,35 @@ del_redirect_desc(unsigned short eport, int proto) } } +/* go through the list to find the description */ static void get_redirect_desc(unsigned short eport, int proto, - char * desc, int desclen) + char * desc, int desclen, + unsigned int * timestamp) { struct rdr_desc * p; - if(!desc || (desclen == 0)) - return; for(p = rdr_desc_list; p; p = p->next) { if(p->eport == eport && p->proto == (short)proto) { - strncpy(desc, p->str, desclen); + if(desc) + strncpy(desc, p->str, desclen); + if(timestamp) + *timestamp = p->timestamp; return; } } /* if no description was found, return miniupnpd as default */ - strncpy(desc, "miniupnpd", desclen); + if(desc) + strncpy(desc, "miniupnpd", desclen); + if(timestamp) + *timestamp = 0; } -int +#if USE_INDEX_FROM_DESC_LIST +static int get_redirect_desc_by_index(int index, unsigned short * eport, int * proto, - char * desc, int desclen) + char * desc, int desclen, unsigned int * timestamp) { int i = 0; struct rdr_desc * p; @@ -144,30 +180,35 @@ get_redirect_desc_by_index(int index, unsigned short * *eport = p->eport; *proto = (int)p->proto; strncpy(desc, p->str, desclen); + if(timestamp) + *timestamp = p->timestamp; return 0; } } return -1; } +#endif /* add_redirect_rule2() */ int -add_redirect_rule2(const char * ifname, unsigned short eport, +add_redirect_rule2(const char * ifname, + const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, - const char * desc) + const char * desc, unsigned int timestamp) { - int r = addnatrule(proto, eport, iaddr, iport); + int r = addnatrule(proto, eport, iaddr, iport, rhost); if(r >= 0) - add_redirect_desc(eport, proto, desc); + add_redirect_desc(eport, proto, desc, timestamp); return r; } int -add_filter_rule2(const char * ifname, const char * iaddr, +add_filter_rule2(const char * ifname, + const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc) { - return add_filter_rule(proto, iaddr, iport); + return add_filter_rule(proto, rhost, iaddr, iport); } /* get_redirect_rule() @@ -176,6 +217,8 @@ int get_redirect_rule(const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, + char * rhost, int rhostlen, + unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int r = -1; @@ -231,13 +274,15 @@ get_redirect_rule(const char * ifname, unsigned short mr = (const struct ip_nat_multi_range *)&target->data[0]; snprintip(iaddr, iaddrlen, ntohl(mr->range[0].min_ip)); *iport = ntohs(mr->range[0].min.all); - /*if(desc) - strncpy(desc, "miniupnpd", desclen);*/ - get_redirect_desc(eport, proto, desc, desclen); + get_redirect_desc(eport, proto, desc, desclen, timestamp); if(packets) *packets = e->counters.pcnt; if(bytes) *bytes = e->counters.bcnt; + /* rhost */ + if(e->ip.src.s_addr && rhost) { + snprintip(rhost, rhostlen, ntohl(e->ip.src.s_addr)); + } r = 0; break; } @@ -259,14 +304,20 @@ get_redirect_rule_by_index(int index, char * ifname, unsigned short * eport, char * iaddr, int iaddrlen, unsigned short * iport, int * proto, char * desc, int desclen, + char * rhost, int rhostlen, + unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int r = -1; - r = get_redirect_desc_by_index(index, eport, proto, desc, desclen); +#if USE_INDEX_FROM_DESC_LIST + r = get_redirect_desc_by_index(index, eport, proto, + desc, desclen, timestamp); if (r==0) + { r = get_redirect_rule(ifname, *eport, *proto, iaddr, iaddrlen, iport, 0, 0, packets, bytes); -#if 0 + } +#else int i = 0; IPTC_HANDLE h; const struct ipt_entry * e; @@ -318,13 +369,19 @@ get_redirect_rule_by_index(int index, mr = (const struct ip_nat_multi_range *)&target->data[0]; snprintip(iaddr, iaddrlen, ntohl(mr->range[0].min_ip)); *iport = ntohs(mr->range[0].min.all); - /*if(desc) - strncpy(desc, "miniupnpd", desclen);*/ - get_redirect_desc(*eport, *proto, desc, desclen); + get_redirect_desc(*eport, *proto, desc, desclen, timestamp); if(packets) *packets = e->counters.pcnt; if(bytes) *bytes = e->counters.bcnt; + /* rhost */ + if(rhost && rhostlen > 0) { + if(e->ip.src.s_addr) { + snprintip(rhost, rhostlen, ntohl(e->ip.src.s_addr)); + } else { + rhost[0] = '\0'; + } + } r = 0; break; } @@ -337,46 +394,84 @@ get_redirect_rule_by_index(int index, #else iptc_free(&h); #endif -#endif /*0*/ +#endif return r; } /* delete_rule_and_commit() : * subfunction used in delete_redirect_and_filter_rules() */ +static int +delete_rule_and_commit(unsigned int index, IPTC_HANDLE h, + const char * miniupnpd_chain, + const char * logcaller) +{ + int r = 0; +#ifdef IPTABLES_143 + if(!iptc_delete_num_entry(miniupnpd_chain, index, h)) +#else + if(!iptc_delete_num_entry(miniupnpd_chain, index, &h)) +#endif + { + syslog(LOG_ERR, "%s() : iptc_delete_num_entry(): %s\n", + logcaller, iptc_strerror(errno)); + r = -1; + } +#ifdef IPTABLES_143 + else if(!iptc_commit(h)) +#else + else if(!iptc_commit(&h)) +#endif + { + syslog(LOG_ERR, "%s() : iptc_commit(): %s\n", + logcaller, iptc_strerror(errno)); + r = -1; + } + if(h) +#ifdef IPTABLES_143 + iptc_free(h); +#else + iptc_free(&h); +#endif + return r; +} + +/* delete_redirect_and_filter_rules() + */ int -delete_rule_and_commit(const char * table, - const char * miniupnpd_chain, - unsigned short eport, int proto, - const char * logcaller) +delete_redirect_and_filter_rules(unsigned short eport, int proto) { int r = -1; unsigned index = 0; unsigned i = 0; IPTC_HANDLE h; const struct ipt_entry * e; + const struct ipt_entry_target * target; + const struct ip_nat_multi_range * mr; const struct ipt_entry_match *match; + unsigned short iport = 0; + uint32_t iaddr = 0; - h = iptc_init(table); + h = iptc_init("nat"); if(!h) { - syslog(LOG_ERR, "get_index_rules() : " - "iptc_init(%s) failed : %s", - table, + syslog(LOG_ERR, "delete_redirect_and_filter_rules() : " + "iptc_init() failed : %s", iptc_strerror(errno)); return -1; } - if(!iptc_is_chain(miniupnpd_chain, h)) + /* First step : find the right nat rule */ + if(!iptc_is_chain(miniupnpd_nat_chain, h)) { - syslog(LOG_ERR, "chain %s not found", miniupnpd_chain); + syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain); } else { #ifdef IPTABLES_143 - for(e = iptc_first_rule(miniupnpd_chain, h); + for(e = iptc_first_rule(miniupnpd_nat_chain, h); e; e = iptc_next_rule(e, h), i++) #else - for(e = iptc_first_rule(miniupnpd_chain, &h); + for(e = iptc_first_rule(miniupnpd_nat_chain, &h); e; e = iptc_next_rule(e, &h), i++) #endif @@ -398,8 +493,14 @@ delete_rule_and_commit(const char * table, if(eport != info->dpts[0]) continue; } - r = 0; + /* get the index, the internal address and the internal port + * of the rule */ index = i; + target = (void *)e + e->target_offset; + mr = (const struct ip_nat_multi_range *)&target->data[0]; + iaddr = mr->range[0].min_ip; + iport = ntohs(mr->range[0].min.all); + r = 0; break; } } @@ -410,51 +511,63 @@ delete_rule_and_commit(const char * table, #else iptc_free(&h); #endif - if ((r == 0) && (h = iptc_init(table))) { - syslog(LOG_INFO, "Trying to delete rules at index %u", index); + if(r == 0) + { + syslog(LOG_INFO, "Trying to delete nat rule at index %u", index); /* Now delete both rules */ + /* first delete the nat rule */ + h = iptc_init("nat"); + if(h) + { + r = delete_rule_and_commit(index, h, miniupnpd_nat_chain, "delete_redirect_rule"); + } + if((r == 0) && (h = iptc_init("filter"))) + { + i = 0; + /* we must find the right index for the filter rule */ #ifdef IPTABLES_143 - if(!iptc_delete_num_entry(miniupnpd_chain, index, h)) + for(e = iptc_first_rule(miniupnpd_forward_chain, h); + e; + e = iptc_next_rule(e, h), i++) #else - if(!iptc_delete_num_entry(miniupnpd_chain, index, &h)) + for(e = iptc_first_rule(miniupnpd_forward_chain, &h); + e; + e = iptc_next_rule(e, &h), i++) #endif - { - syslog(LOG_ERR, "%s() : iptc_delete_num_entry(): %s\n", - logcaller, iptc_strerror(errno)); - r = -1; + { + if(proto==e->ip.proto) + { + match = (const struct ipt_entry_match *)&e->elems; + /*syslog(LOG_DEBUG, "filter rule #%u: %s %s", + i, match->u.user.name, inet_ntoa(e->ip.dst));*/ + if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN)) + { + const struct ipt_tcp * info; + info = (const struct ipt_tcp *)match->data; + if(iport != info->dpts[0]) + continue; + } + else + { + const struct ipt_udp * info; + info = (const struct ipt_udp *)match->data; + if(iport != info->dpts[0]) + continue; + } + if(iaddr != e->ip.dst.s_addr) + continue; + index = i; + break; + } + } + syslog(LOG_INFO, "Trying to delete filter rule at index %u", index); + r = delete_rule_and_commit(index, h, miniupnpd_forward_chain, "delete_filter_rule"); + } } -#ifdef IPTABLES_143 - else if(!iptc_commit(h)) -#else - else if(!iptc_commit(&h)) -#endif - { - syslog(LOG_ERR, "%s() : iptc_commit(): %s\n", - logcaller, iptc_strerror(errno)); - r = -1; - } - if(h) -#ifdef IPTABLES_143 - iptc_free(h); -#else - iptc_free(&h); -#endif - } + del_redirect_desc(eport, proto); return r; } -/* delete_redirect_and_filter_rules() - */ -int -delete_redirect_and_filter_rules(unsigned short eport, int proto) -{ - int r = -1; - if ((r = delete_rule_and_commit("nat", miniupnpd_nat_chain, eport, proto, "delete_redirect_rule") && - delete_rule_and_commit("filter", miniupnpd_forward_chain, eport, proto, "delete_filter_rule")) == 0) - del_redirect_desc(eport, proto); - return r; -} - /* ==================================== */ /* TODO : add the -m state --state NEW,ESTABLISHED,RELATED * only for the filter rule */ @@ -468,7 +581,7 @@ get_tcp_match(unsigned short dport) + IPT_ALIGN(sizeof(struct ipt_tcp)); match = calloc(1, size); match->u.match_size = size; - strncpy(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN); + strncpy(match->u.user.name, "tcp", sizeof(match->u.user.name)); tcpinfo = (struct ipt_tcp *)match->data; tcpinfo->spts[0] = 0; /* all source ports */ tcpinfo->spts[1] = 0xFFFF; @@ -487,7 +600,7 @@ get_udp_match(unsigned short dport) + IPT_ALIGN(sizeof(struct ipt_udp)); match = calloc(1, size); match->u.match_size = size; - strncpy(match->u.user.name, "udp", IPT_FUNCTION_MAXNAMELEN); + strncpy(match->u.user.name, "udp", sizeof(match->u.user.name)); udpinfo = (struct ipt_udp *)match->data; udpinfo->spts[0] = 0; /* all source ports */ udpinfo->spts[1] = 0xFFFF; @@ -508,7 +621,7 @@ get_dnat_target(const char * daddr, unsigned short dpo + IPT_ALIGN(sizeof(struct ip_nat_multi_range)); target = calloc(1, size); target->u.target_size = size; - strncpy(target->u.user.name, "DNAT", IPT_FUNCTION_MAXNAMELEN); + strncpy(target->u.user.name, "DNAT", sizeof(target->u.user.name)); /* one ip_nat_range already included in ip_nat_multi_range */ mr = (struct ip_nat_multi_range *)&target->data[0]; mr->rangesize = 1; @@ -523,7 +636,9 @@ get_dnat_target(const char * daddr, unsigned short dpo /* iptc_init_verify_and_append() * return 0 on success, -1 on failure */ static int -iptc_init_verify_and_append(const char * table, const char * miniupnpd_chain, struct ipt_entry * e, +iptc_init_verify_and_append(const char * table, + const char * miniupnpd_chain, + struct ipt_entry * e, const char * logcaller) { IPTC_HANDLE h; @@ -536,8 +651,8 @@ iptc_init_verify_and_append(const char * table, const } if(!iptc_is_chain(miniupnpd_chain, h)) { - syslog(LOG_ERR, "%s : iptc_is_chain() error : %s\n", - logcaller, iptc_strerror(errno)); + syslog(LOG_ERR, "%s : chain %s not found", + logcaller, miniupnpd_chain); if(h) #ifdef IPTABLES_143 iptc_free(h); @@ -591,9 +706,10 @@ iptc_init_verify_and_append(const char * table, const /* add nat rule * iptables -t nat -A MINIUPNPD -p proto --dport eport -j DNAT --to iaddr:iport * */ -int +static int addnatrule(int proto, unsigned short eport, - const char * iaddr, unsigned short iport) + const char * iaddr, unsigned short iport, + const char * rhost) { int r = 0; struct ipt_entry * e; @@ -623,6 +739,12 @@ addnatrule(int proto, unsigned short eport, e->next_offset = sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size; + /* remote host */ + if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*"))) + { + e->ip.src.s_addr = inet_addr(rhost); + e->ip.smsk.s_addr = INADDR_NONE; + } r = iptc_init_verify_and_append("nat", miniupnpd_nat_chain, e, "addnatrule()"); free(target); @@ -640,14 +762,15 @@ get_accept_target(void) + IPT_ALIGN(sizeof(int)); target = calloc(1, size); target->u.user.target_size = size; - strncpy(target->u.user.name, "ACCEPT", IPT_FUNCTION_MAXNAMELEN); + strncpy(target->u.user.name, "ACCEPT", sizeof(target->u.user.name)); return target; } /* add_filter_rule() * */ -int -add_filter_rule(int proto, const char * iaddr, unsigned short iport) +static int +add_filter_rule(int proto, const char * rhost, + const char * iaddr, unsigned short iport) { int r = 0; struct ipt_entry * e; @@ -679,6 +802,12 @@ add_filter_rule(int proto, const char * iaddr, unsigne e->next_offset = sizeof(struct ipt_entry) + match->u.match_size + target->u.target_size; + /* remote host */ + if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*"))) + { + e->ip.src.s_addr = inet_addr(rhost); + e->ip.smsk.s_addr = INADDR_NONE; + } r = iptc_init_verify_and_append("filter", miniupnpd_forward_chain, e, "add_filter_rule()"); free(target); @@ -687,7 +816,101 @@ add_filter_rule(int proto, const char * iaddr, unsigne return r; } +/* return an (malloc'ed) array of "external" port for which there is + * a port mapping. number is the size of the array */ +unsigned short * +get_portmappings_in_range(unsigned short startport, unsigned short endport, + int proto, unsigned int * number) +{ + unsigned short * array; + unsigned int capacity; + unsigned short eport; + IPTC_HANDLE h; + const struct ipt_entry * e; + const struct ipt_entry_match *match; + + *number = 0; + capacity = 128; + array = calloc(capacity, sizeof(unsigned short)); + if(!array) + { + syslog(LOG_ERR, "get_portmappings_in_range() : calloc error"); + return NULL; + } + + h = iptc_init("nat"); + if(!h) + { + syslog(LOG_ERR, "get_redirect_rule_by_index() : " + "iptc_init() failed : %s", + iptc_strerror(errno)); + free(array); + return NULL; + } + if(!iptc_is_chain(miniupnpd_nat_chain, h)) + { + syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain); + free(array); + array = NULL; + } + else + { +#ifdef IPTABLES_143 + for(e = iptc_first_rule(miniupnpd_nat_chain, h); + e; + e = iptc_next_rule(e, h)) +#else + for(e = iptc_first_rule(miniupnpd_nat_chain, &h); + e; + e = iptc_next_rule(e, &h)) +#endif + { + if(proto == e->ip.proto) + { + match = (const struct ipt_entry_match *)&e->elems; + if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN)) + { + const struct ipt_tcp * info; + info = (const struct ipt_tcp *)match->data; + eport = info->dpts[0]; + } + else + { + const struct ipt_udp * info; + info = (const struct ipt_udp *)match->data; + eport = info->dpts[0]; + } + if(startport <= eport && eport <= endport) + { + if(*number >= capacity) + { + /* need to increase the capacity of the array */ + array = realloc(array, sizeof(unsigned short)*capacity); + if(!array) + { + syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%u) error", + (unsigned)sizeof(unsigned short)*capacity); + *number = 0; + break; + } + array[*number] = eport; + (*number)++; + } + } + } + } + } + if(h) +#ifdef IPTABLES_143 + iptc_free(h); +#else + iptc_free(&h); +#endif + return array; +} + /* ================================ */ +#ifdef DEBUG static int print_match(const struct ipt_entry_match *match) { @@ -752,6 +975,7 @@ list_redirect_rule(const char * ifname) const struct ipt_entry_target * target; const struct ip_nat_multi_range * mr; const char * target_str; + char addr[16], mask[16]; h = iptc_init("nat"); if(!h) @@ -783,10 +1007,16 @@ list_redirect_rule(const char * ifname) target_str = iptc_get_target(e, &h); #endif printf("===\n"); + inet_ntop(AF_INET, &e->ip.src, addr, sizeof(addr)); + inet_ntop(AF_INET, &e->ip.smsk, mask, sizeof(mask)); printf("src = %s%s/%s\n", (e->ip.invflags & IPT_INV_SRCIP)?"! ":"", - inet_ntoa(e->ip.src), inet_ntoa(e->ip.smsk)); + /*inet_ntoa(e->ip.src), inet_ntoa(e->ip.smsk)*/ + addr, mask); + inet_ntop(AF_INET, &e->ip.dst, addr, sizeof(addr)); + inet_ntop(AF_INET, &e->ip.dmsk, mask, sizeof(mask)); printf("dst = %s%s/%s\n", (e->ip.invflags & IPT_INV_DSTIP)?"! ":"", - inet_ntoa(e->ip.dst), inet_ntoa(e->ip.dmsk)); + /*inet_ntoa(e->ip.dst), inet_ntoa(e->ip.dmsk)*/ + addr, mask); /*printf("in_if = %s out_if = %s\n", e->ip.iniface, e->ip.outiface);*/ printf("in_if = "); print_iface(e->ip.iniface, e->ip.iniface_mask, @@ -822,4 +1052,4 @@ list_redirect_rule(const char * ifname) #endif return 0; } - +#endif