File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / netfilter / iptpinhole.c
Revision 1.1.1.1 (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: elwix, MAIN
CVS tags: v1_8p0, v1_8, HEAD
1.8

    1: /* $Id: iptpinhole.c,v 1.1.1.1 2013/07/22 00:32:35 misho Exp $ */
    2: /* MiniUPnP project
    3:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
    4:  * (c) 2012 Thomas Bernard
    5:  * This software is subject to the conditions detailed
    6:  * in the LICENCE file provided within the distribution */
    7: 
    8: #include <stdlib.h>
    9: #include <string.h>
   10: #include <syslog.h>
   11: #include <errno.h>
   12: #include <arpa/inet.h>
   13: #include <sys/queue.h>
   14: 
   15: #include "../config.h"
   16: #include "iptpinhole.h"
   17: #include "../upnpglobalvars.h"
   18: 
   19: #ifdef ENABLE_6FC_SERVICE
   20: 
   21: #include <xtables.h>
   22: #include <libiptc/libip6tc.h>
   23: #include "tiny_nf_nat.h"
   24: 
   25: #define IP6TC_HANDLE struct ip6tc_handle *
   26: 
   27: static int next_uid = 1;
   28: 
   29: static LIST_HEAD(pinhole_list_t, pinhole_t) pinhole_list;
   30: 
   31: static struct pinhole_t *
   32: get_pinhole(unsigned short uid);
   33: 
   34: struct pinhole_t {
   35: 	struct in6_addr saddr;
   36: 	struct in6_addr daddr;
   37: 	LIST_ENTRY(pinhole_t) entries;
   38: 	unsigned int timestamp;
   39: 	unsigned short sport;
   40: 	unsigned short dport;
   41: 	unsigned short uid;
   42: 	unsigned char proto;
   43: };
   44: 
   45: void init_iptpinhole(void)
   46: {
   47: 	LIST_INIT(&pinhole_list);
   48: }
   49: 
   50: void shutdown_iptpinhole(void)
   51: {
   52: 	/* TODO empty list */
   53: }
   54: 
   55: /* return uid */
   56: static int
   57: add_to_pinhole_list(struct in6_addr * saddr, unsigned short sport,
   58:                     struct in6_addr * daddr, unsigned short dport,
   59:                     int proto, unsigned int timestamp)
   60: {
   61: 	struct pinhole_t * p;
   62: 
   63: 	p = calloc(1, sizeof(struct pinhole_t));
   64: 	if(!p) {
   65: 		syslog(LOG_ERR, "add_to_pinhole_list calloc() error");
   66: 		return -1;
   67: 	}
   68: 	memcpy(&p->saddr, saddr, sizeof(struct in6_addr));
   69: 	p->sport = sport;
   70: 	memcpy(&p->daddr, daddr, sizeof(struct in6_addr));
   71: 	p->dport = dport;
   72: 	p->timestamp = timestamp;
   73: 	p->proto = (unsigned char)proto;
   74: 	LIST_INSERT_HEAD(&pinhole_list, p, entries);
   75: 	while(get_pinhole(next_uid) != NULL) {
   76: 		next_uid++;
   77: 		if(next_uid > 65535)
   78: 			next_uid = 1;
   79: 	}
   80: 	p->uid = next_uid;
   81: 	next_uid++;
   82: 	if(next_uid > 65535)
   83: 		next_uid = 1;
   84: 	return p->uid;
   85: }
   86: 
   87: static struct pinhole_t *
   88: get_pinhole(unsigned short uid)
   89: {
   90: 	struct pinhole_t * p;
   91: 
   92: 	for(p = pinhole_list.lh_first; p != NULL; p = p->entries.le_next) {
   93: 		if(p->uid == uid)
   94: 			return p;
   95: 	}
   96: 	return NULL;	/* not found */
   97: }
   98: 
   99: /* new_match()
  100:  * Allocate and set a new ip6t_entry_match structure
  101:  * The caller must free() it after usage */
  102: static struct ip6t_entry_match *
  103: new_match(int proto, unsigned short sport, unsigned short dport)
  104: {
  105: 	struct ip6t_entry_match *match;
  106: 	struct ip6t_tcp *info;	/* TODO : use ip6t_udp if needed */
  107: 	size_t size;
  108: 	const char * name;
  109: 	size =   XT_ALIGN(sizeof(struct ip6t_entry_match))
  110: 	       + XT_ALIGN(sizeof(struct ip6t_tcp));
  111: 	match = calloc(1, size);
  112: 	match->u.user.match_size = size;
  113: 	switch(proto) {
  114: 	case IPPROTO_TCP:
  115: 		name = "tcp";
  116: 		break;
  117: 	case IPPROTO_UDP:
  118: 		name = "udp";
  119: 		break;
  120: 	case IPPROTO_UDPLITE:
  121: 		name = "udplite";
  122: 		break;
  123: 	default:
  124: 		name = NULL;
  125: 	}
  126: 	if(name)
  127: 		strncpy(match->u.user.name, name, sizeof(match->u.user.name));
  128: 	else
  129: 		syslog(LOG_WARNING, "no name for protocol %d", proto);
  130: 	info = (struct ip6t_tcp *)match->data;
  131: 	if(sport) {
  132: 		info->spts[0] = sport;	/* specified source port */
  133: 		info->spts[1] = sport;
  134: 	} else {
  135: 		info->spts[0] = 0;		/* all source ports */
  136: 		info->spts[1] = 0xFFFF;
  137: 	}
  138: 	info->dpts[0] = dport;	/* specified destination port */
  139: 	info->dpts[1] = dport;
  140: 	return match;
  141: }
  142: 
  143: static struct ip6t_entry_target *
  144: get_accept_target(void)
  145: {
  146: 	struct ip6t_entry_target * target = NULL;
  147: 	size_t size;
  148: 	size =   XT_ALIGN(sizeof(struct ip6t_entry_target))
  149: 	       + XT_ALIGN(sizeof(int));
  150: 	target = calloc(1, size);
  151: 	target->u.user.target_size = size;
  152: 	strncpy(target->u.user.name, "ACCEPT", sizeof(target->u.user.name));
  153: 	return target;
  154: }
  155: 
  156: static int
  157: ip6tc_init_verify_append(const char * table,
  158:                          const char * chain,
  159:                          struct ip6t_entry * e)
  160: {
  161: 	IP6TC_HANDLE h;
  162: 
  163: 	h = ip6tc_init(table);
  164: 	if(!h) {
  165: 		syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno));
  166: 		return -1;
  167: 	}
  168: 	if(!ip6tc_is_chain(chain, h)) {
  169: 		syslog(LOG_ERR, "chain %s not found", chain);
  170: 		goto error;
  171: 	}
  172: 	if(!ip6tc_append_entry(chain, e, h)) {
  173: 		syslog(LOG_ERR, "ip6tc_append_entry() error : %s", ip6tc_strerror(errno));
  174: 		goto error;
  175: 	}
  176: 	if(!ip6tc_commit(h)) {
  177: 		syslog(LOG_ERR, "ip6tc_commit() error : %s", ip6tc_strerror(errno));
  178: 		goto error;
  179: 	}
  180: 	return 0;	/* ok */
  181: error:
  182: 	ip6tc_free(h);
  183: 	return -1;
  184: }
  185: 
  186: /*
  187: ip6tables -I %s %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j ACCEPT
  188: ip6tables -I %s %d -p %s -i %s --sport %hu -d %s --dport %hu -j ACCEPT
  189: 
  190: miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, rport, iaddr, iport
  191: 
  192: ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j TRACE
  193: ip6tables -t raw -I PREROUTING %d -p %s -i %s --sport %hu -d %s --dport %hu -j TRACE
  194: */
  195: int add_pinhole(const char * ifname,
  196:                 const char * rem_host, unsigned short rem_port,
  197:                 const char * int_client, unsigned short int_port,
  198:                 int proto, unsigned int timestamp)
  199: {
  200: 	int uid;
  201: 	struct ip6t_entry * e;
  202: 	struct ip6t_entry_match *match = NULL;
  203: 	struct ip6t_entry_target *target = NULL;
  204: 
  205: 	e = calloc(1, sizeof(struct ip6t_entry));
  206: 	e->ipv6.proto = proto;
  207: 
  208: 	if(ifname)
  209: 		strncpy(e->ipv6.iniface, ifname, IFNAMSIZ);
  210: 	if(rem_host) {
  211: 		inet_pton(AF_INET6, rem_host, &e->ipv6.src);
  212: 		memset(&e->ipv6.smsk, 0xff, sizeof(e->ipv6.smsk));
  213: 	}
  214: 	inet_pton(AF_INET6, int_client, &e->ipv6.dst);
  215: 	memset(&e->ipv6.dmsk, 0xff, sizeof(e->ipv6.dmsk));
  216: 
  217: 	/*e->nfcache = NFC_IP_DST_PT;*/
  218: 	/*e->nfcache |= NFC_UNKNOWN;*/
  219: 
  220: 	match = new_match(proto, rem_port, int_port);
  221: 	target = get_accept_target();
  222: 	e = realloc(e, sizeof(struct ip6t_entry)
  223: 	               + match->u.match_size
  224: 	               + target->u.target_size);
  225: 	memcpy(e->elems, match, match->u.match_size);
  226: 	memcpy(e->elems + match->u.match_size, target, target->u.target_size);
  227: 	e->target_offset = sizeof(struct ip6t_entry)
  228: 	                   + match->u.match_size;
  229: 	e->next_offset = sizeof(struct ip6t_entry)
  230: 	                 + match->u.match_size
  231: 					 + target->u.target_size;
  232: 	free(match);
  233: 	free(target);
  234: 
  235: 	if(ip6tc_init_verify_append("filter", miniupnpd_v6_filter_chain, e) < 0) {
  236: 		free(e);
  237: 		return -1;
  238: 	}
  239: 	uid = add_to_pinhole_list(&e->ipv6.src, rem_port,
  240: 	                          &e->ipv6.dst, int_port,
  241: 	                          proto, timestamp);
  242: 	free(e);
  243: 	return uid;
  244: }
  245: 
  246: int
  247: delete_pinhole(unsigned short uid)
  248: {
  249: 	struct pinhole_t * p;
  250: 	IP6TC_HANDLE h;
  251: 	const struct ip6t_entry * e;
  252: 	const struct ip6t_entry_match *match = NULL;
  253: 	/*const struct ip6t_entry_target *target = NULL;*/
  254: 	unsigned int index;
  255: 
  256: 	p = get_pinhole(uid);
  257: 	if(!p)
  258: 		return -2;	/* not found */
  259: 
  260: 	h = ip6tc_init("filter");
  261: 	if(!h) {
  262: 		syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno));
  263: 		return -1;
  264: 	}
  265: 	if(!ip6tc_is_chain(miniupnpd_v6_filter_chain, h)) {
  266: 		syslog(LOG_ERR, "chain %s not found", miniupnpd_v6_filter_chain);
  267: 		goto error;
  268: 	}
  269: 	index = 0;
  270: 	for(e = ip6tc_first_rule(miniupnpd_v6_filter_chain, h);
  271: 	    e;
  272: 	    e = ip6tc_next_rule(e, h)) {
  273: 		if((e->ipv6.proto == p->proto) &&
  274: 		   (0 == memcmp(&e->ipv6.src, &p->saddr, sizeof(e->ipv6.src))) &&
  275: 		   (0 == memcmp(&e->ipv6.dst, &p->daddr, sizeof(e->ipv6.dst)))) {
  276: 			const struct ip6t_tcp * info;
  277: 			match = (const struct ip6t_entry_match *)&e->elems;
  278: 			info = (const struct ip6t_tcp *)&match->data;
  279: 			if((info->spts[0] == p->sport) && (info->dpts[0] == p->dport)) {
  280: 				if(!ip6tc_delete_num_entry(miniupnpd_v6_filter_chain, index, h)) {
  281: 					syslog(LOG_ERR, "ip6tc_delete_num_entry(%s,%d,...): %s",
  282: 					       miniupnpd_v6_filter_chain, index, ip6tc_strerror(errno));
  283: 					goto error;
  284: 				}
  285: 				if(!ip6tc_commit(h)) {
  286: 					syslog(LOG_ERR, "ip6tc_commit(): %s",
  287: 					       ip6tc_strerror(errno));
  288: 					goto error;
  289: 				}
  290: 				ip6tc_free(h);
  291: 				LIST_REMOVE(p, entries);
  292: 				return 0;	/* ok */
  293: 			}
  294: 		}
  295: 		index++;
  296: 	}
  297: 	ip6tc_free(h);
  298: 	syslog(LOG_WARNING, "delete_pinhole() rule with PID=%hu not found", uid);
  299: 	return -2;	/* not found */
  300: error:
  301: 	ip6tc_free(h);
  302: 	return -1;
  303: }
  304: 
  305: int
  306: update_pinhole(unsigned short uid, unsigned int timestamp)
  307: {
  308: 	struct pinhole_t * p;
  309: 
  310: 	p = get_pinhole(uid);
  311: 	if(p) {
  312: 		p->timestamp = timestamp;
  313: 		return 0;
  314: 	} else {
  315: 		return -2;	/* Not found */
  316: 	}
  317: }
  318: 
  319: int
  320: get_pinhole_info(unsigned short uid,
  321:                  char * rem_host, int rem_hostlen, unsigned short * rem_port,
  322:                  char * int_client, int int_clientlen, unsigned short * int_port,
  323:                  int * proto, unsigned int * timestamp,
  324:                  u_int64_t * packets, u_int64_t * bytes)
  325: {
  326: 	struct pinhole_t * p;
  327: 
  328: 	p = get_pinhole(uid);
  329: 	if(!p)
  330: 		return -2;	/* Not found */
  331: 	if(rem_host) {
  332: 		if(inet_ntop(AF_INET6, &p->saddr, rem_host, rem_hostlen) == NULL)
  333: 			return -1;
  334: 	}
  335: 	if(rem_port)
  336: 		*rem_port = p->sport;
  337: 	if(int_client) {
  338: 		if(inet_ntop(AF_INET6, &p->daddr, int_client, int_clientlen) == NULL)
  339: 			return -1;
  340: 	}
  341: 	if(int_port)
  342: 		*int_port = p->dport;
  343: 	if(proto)
  344: 		*proto = p->proto;
  345: 	if(timestamp)
  346: 		*timestamp = p->timestamp;
  347: 	if(packets || bytes) {
  348: 		/* theses informations need to be read from netfilter */
  349: 		IP6TC_HANDLE h;
  350: 		const struct ip6t_entry * e;
  351: 		const struct ip6t_entry_match * match;
  352: 		h = ip6tc_init("filter");
  353: 		if(!h) {
  354: 			syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno));
  355: 			return -1;
  356: 		}
  357: 		for(e = ip6tc_first_rule(miniupnpd_v6_filter_chain, h);
  358: 		    e;
  359: 		    e = ip6tc_next_rule(e, h)) {
  360: 			if((e->ipv6.proto == p->proto) &&
  361: 			   (0 == memcmp(&e->ipv6.src, &p->saddr, sizeof(e->ipv6.src))) &&
  362: 			   (0 == memcmp(&e->ipv6.dst, &p->daddr, sizeof(e->ipv6.dst)))) {
  363: 				const struct ip6t_tcp * info;
  364: 				match = (const struct ip6t_entry_match *)&e->elems;
  365: 				info = (const struct ip6t_tcp *)&match->data;
  366: 				if((info->spts[0] == p->sport) && (info->dpts[0] == p->dport)) {
  367: 					if(packets)
  368: 						*packets = e->counters.pcnt;
  369: 					if(bytes)
  370: 						*bytes = e->counters.bcnt;
  371: 					break;
  372: 				}
  373: 			}
  374: 		}
  375: 		ip6tc_free(h);
  376: 	}
  377: 	return 0;
  378: }
  379: 
  380: int
  381: clean_pinhole_list(unsigned int * next_timestamp)
  382: {
  383: 	unsigned int min_ts = UINT_MAX;
  384: 	struct pinhole_t * p;
  385: 	time_t current_time;
  386: 	int n = 0;
  387: 
  388: 	current_time = time(NULL);
  389: 	p = pinhole_list.lh_first;
  390: 	while(p != NULL) {
  391: 		if(p->timestamp <= (unsigned int)current_time) {
  392: 			unsigned short uid = p->uid;
  393: 			syslog(LOG_INFO, "removing expired pinhole with uid=%hu", uid);
  394: 			p = p->entries.le_next;
  395: 			if(delete_pinhole(uid) == 0)
  396: 				n++;
  397: 			else
  398: 				break;
  399: 		} else {
  400: 			if(p->timestamp < min_ts)
  401: 				min_ts = p->timestamp;
  402: 			p = p->entries.le_next;
  403: 		}
  404: 	}
  405: 	if(next_timestamp && (min_ts != UINT_MAX))
  406: 		*next_timestamp = min_ts;
  407: 	return n;
  408: }
  409: 
  410: #endif
  411: 

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