File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / netfilter / iptcrdr.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 00:32:35 2013 UTC (10 years, 11 months ago) by misho
Branches: miniupnpd, elwix, MAIN
CVS tags: v1_8p0, v1_8, HEAD
1.8

    1: /* $Id: iptcrdr.c,v 1.1.1.3 2013/07/22 00:32:35 misho Exp $ */
    2: /* MiniUPnP project
    3:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
    4:  * (c) 2006-2011 Thomas Bernard
    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>
   16: #include <xtables.h>
   17: #include <libiptc/libiptc.h>
   18: 
   19: #include <linux/version.h>
   20: 
   21: #if IPTABLES_143
   22: /* IPTABLES API version >= 1.4.3 */
   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: 
   32: #if 0
   33: #include <linux/netfilter/nf_nat.h>
   34: #else
   35: #include "tiny_nf_nat.h"
   36: #endif
   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
   45: #if 0
   46: #include <linux/netfilter/nf_nat.h>
   47: #else
   48: #include "tiny_nf_nat.h"
   49: #endif
   50: #endif
   51: #define IPTC_HANDLE		iptc_handle_t
   52: #endif
   53: 
   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: 
   59: #include "../macros.h"
   60: #include "../config.h"
   61: #include "iptcrdr.h"
   62: #include "../upnpglobalvars.h"
   63: 
   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: 
   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;
   97: 	unsigned int timestamp;
   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: 
  106: /* add a description to the list of redirection descriptions */
  107: static void
  108: add_redirect_desc(unsigned short eport, int proto,
  109:                   const char * desc, unsigned int timestamp)
  110: {
  111: 	struct rdr_desc * p;
  112: 	size_t l;
  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: 	}
  127: }
  128: 
  129: /* delete a description from the list */
  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: 
  152: /* go through the list to find the description */
  153: static void
  154: get_redirect_desc(unsigned short eport, int proto,
  155:                   char * desc, int desclen,
  156:                   unsigned int * timestamp)
  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: 		{
  163: 			if(desc)
  164: 				strncpy(desc, p->str, desclen);
  165: 			if(timestamp)
  166: 				*timestamp = p->timestamp;
  167: 			return;
  168: 		}
  169: 	}
  170: 	/* if no description was found, return miniupnpd as default */
  171: 	if(desc)
  172: 		strncpy(desc, "miniupnpd", desclen);
  173: 	if(timestamp)
  174: 		*timestamp = 0;
  175: }
  176: 
  177: #if USE_INDEX_FROM_DESC_LIST
  178: static int
  179: get_redirect_desc_by_index(int index, unsigned short * eport, int * proto,
  180:                   char * desc, int desclen, unsigned int * timestamp)
  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);
  193: 			if(timestamp)
  194: 				*timestamp = p->timestamp;
  195: 			return 0;
  196: 		}
  197: 	}
  198: 	return -1;
  199: }
  200: #endif
  201: 
  202: /* add_redirect_rule2() */
  203: int
  204: add_redirect_rule2(const char * ifname,
  205:                    const char * rhost, unsigned short eport,
  206:                    const char * iaddr, unsigned short iport, int proto,
  207: 				   const char * desc, unsigned int timestamp)
  208: {
  209: 	int r;
  210: 	UNUSED(ifname);
  211: 
  212: 	r = addnatrule(proto, eport, iaddr, iport, rhost);
  213: 	if(r >= 0)
  214: 		add_redirect_desc(eport, proto, desc, timestamp);
  215: 	return r;
  216: }
  217: 
  218: int
  219: add_filter_rule2(const char * ifname,
  220:                  const char * rhost, const char * iaddr,
  221:                  unsigned short eport, unsigned short iport,
  222:                  int proto, const char * desc)
  223: {
  224: 	UNUSED(ifname);
  225: 	UNUSED(eport);
  226: 	UNUSED(desc);
  227: 
  228: 	return add_filter_rule(proto, rhost, iaddr, iport);
  229: }
  230: 
  231: /* get_redirect_rule()
  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,
  237:                   char * rhost, int rhostlen,
  238:                   unsigned int * timestamp,
  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;
  247: 	UNUSED(ifname);
  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;
  291: 				/* target = ipt_get_target(e); */
  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);
  295: 				get_redirect_desc(eport, proto, desc, desclen, timestamp);
  296: 				if(packets)
  297: 					*packets = e->counters.pcnt;
  298: 				if(bytes)
  299: 					*bytes = e->counters.bcnt;
  300: 				/* rhost */
  301: 				if(e->ip.src.s_addr && rhost) {
  302: 					snprintip(rhost, rhostlen, ntohl(e->ip.src.s_addr));
  303: 				}
  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: 
  318: /* get_redirect_rule_by_index()
  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,
  325:                            char * rhost, int rhostlen,
  326:                            unsigned int * timestamp,
  327:                            u_int64_t * packets, u_int64_t * bytes)
  328: {
  329: 	int r = -1;
  330: #if USE_INDEX_FROM_DESC_LIST
  331: 	r = get_redirect_desc_by_index(index, eport, proto,
  332: 	                               desc, desclen, timestamp);
  333: 	if (r==0)
  334: 	{
  335: 		r = get_redirect_rule(ifname, *eport, *proto, iaddr, iaddrlen, iport,
  336: 				      0, 0, packets, bytes);
  337: 	}
  338: #else
  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;
  345: 	UNUSED(ifname);
  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);
  391: 				get_redirect_desc(*eport, *proto, desc, desclen, timestamp);
  392: 				if(packets)
  393: 					*packets = e->counters.pcnt;
  394: 				if(bytes)
  395: 					*bytes = e->counters.bcnt;
  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: 				}
  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
  416: #endif
  417: 	return r;
  418: }
  419: 
  420: /* delete_rule_and_commit() :
  421:  * subfunction used in delete_redirect_and_filter_rules() */
  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:  */
  459: int
  460: delete_redirect_and_filter_rules(unsigned short eport, int proto)
  461: {
  462: 	int r = -1;
  463: 	unsigned index = 0;
  464: 	unsigned i = 0;
  465: 	IPTC_HANDLE h;
  466: 	const struct ipt_entry * e;
  467: 	const struct ipt_entry_target * target;
  468: 	const struct ip_nat_multi_range * mr;
  469: 	const struct ipt_entry_match *match;
  470: 	unsigned short iport = 0;
  471: 	uint32_t iaddr = 0;
  472: 
  473: 	h = iptc_init("nat");
  474: 	if(!h)
  475: 	{
  476: 		syslog(LOG_ERR, "delete_redirect_and_filter_rules() : "
  477: 		                "iptc_init() failed : %s",
  478: 		       iptc_strerror(errno));
  479: 		return -1;
  480: 	}
  481: 	/* First step : find the right nat rule */
  482: 	if(!iptc_is_chain(miniupnpd_nat_chain, h))
  483: 	{
  484: 		syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain);
  485: 	}
  486: 	else
  487: 	{
  488: #ifdef IPTABLES_143
  489: 		for(e = iptc_first_rule(miniupnpd_nat_chain, h);
  490: 		    e;
  491: 			e = iptc_next_rule(e, h), i++)
  492: #else
  493: 		for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
  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: 				}
  515: 				/* get the index, the internal address and the internal port
  516: 				 * of the rule */
  517: 				index = i;
  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;
  523: 				break;
  524: 			}
  525: 		}
  526: 	}
  527: 	if(h)
  528: #ifdef IPTABLES_143
  529: 		iptc_free(h);
  530: #else
  531: 		iptc_free(&h);
  532: #endif
  533: 	if(r == 0)
  534: 	{
  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 */
  547: #ifdef IPTABLES_143
  548: 			for(e = iptc_first_rule(miniupnpd_forward_chain, h);
  549: 			    e;
  550: 				e = iptc_next_rule(e, h), i++)
  551: #else
  552: 			for(e = iptc_first_rule(miniupnpd_forward_chain, &h);
  553: 			    e;
  554: 				e = iptc_next_rule(e, &h), i++)
  555: #endif
  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: 		}
  585: 	}
  586: 	del_redirect_desc(eport, proto);
  587: 	return r;
  588: }
  589: 
  590: /* ==================================== */
  591: /* TODO : add the -m state --state NEW,ESTABLISHED,RELATED
  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;
  603: 	strncpy(match->u.user.name, "tcp", sizeof(match->u.user.name));
  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;
  622: 	strncpy(match->u.user.name, "udp", sizeof(match->u.user.name));
  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;
  643: 	strncpy(target->u.user.name, "DNAT", sizeof(target->u.user.name));
  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
  658: iptc_init_verify_and_append(const char * table,
  659:                             const char * miniupnpd_chain,
  660:                             struct ipt_entry * e,
  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: 	{
  673: 		syslog(LOG_ERR, "%s : chain %s not found",
  674: 		       logcaller, miniupnpd_chain);
  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: 
  725: /* add nat rule
  726:  * iptables -t nat -A MINIUPNPD -p proto --dport eport -j DNAT --to iaddr:iport
  727:  * */
  728: static int
  729: addnatrule(int proto, unsigned short eport,
  730:            const char * iaddr, unsigned short iport,
  731:            const char * rhost)
  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;
  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: 	}
  767: 
  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;
  784: 	strncpy(target->u.user.name, "ACCEPT", sizeof(target->u.user.name));
  785: 	return target;
  786: }
  787: 
  788: /* add_filter_rule()
  789:  * */
  790: static int
  791: add_filter_rule(int proto, const char * rhost,
  792:                 const char * iaddr, unsigned short iport)
  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;
  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: 	}
  830: 
  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: 
  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: 
  931: /* ================================ */
  932: #ifdef DEBUG
  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;
  997: 	char addr[16], mask[16];
  998: 	(void)ifname;
  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");
 1030: 		inet_ntop(AF_INET, &e->ip.src, addr, sizeof(addr));
 1031: 		inet_ntop(AF_INET, &e->ip.smsk, mask, sizeof(mask));
 1032: 		printf("src = %s%s/%s\n", (e->ip.invflags & IPT_INV_SRCIP)?"! ":"",
 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));
 1037: 		printf("dst = %s%s/%s\n", (e->ip.invflags & IPT_INV_DSTIP)?"! ":"",
 1038: 		       /*inet_ntoa(e->ip.dst), inet_ntoa(e->ip.dmsk)*/
 1039: 		       addr, mask);
 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: }
 1075: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>