File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / ipfw / ipfwrdr.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:55:57 2012 UTC (12 years, 2 months ago) by misho
Branches: miniupnpd, elwix, MAIN
CVS tags: v1_6elwix, HEAD
miniupnpd 1.6+patches

    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>