File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / ipf / ipfrdr.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:16:02 2012 UTC (12 years, 4 months ago) by misho
Branches: miniupnpd, elwix, MAIN
CVS tags: v1_6elwix, v1_5, HEAD
miniupnpd

    1: /* $Id: ipfrdr.c,v 1.1.1.1 2012/02/21 23:16:02 misho Exp $ */
    2: /* MiniUPnP project
    3:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
    4:  * (c) 2007 Darren Reed
    5:  * This software is subject to the conditions detailed
    6:  * in the LICENCE file provided within the distribution */
    7: 
    8: #include <sys/param.h>
    9: #include <sys/types.h>
   10: #include <sys/file.h>
   11: /*
   12:  * This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD.
   13:  * Needed here because on some systems <sys/uio.h> gets included by things
   14:  * like <sys/socket.h>
   15:  */
   16: #ifndef _KERNEL
   17: # define ADD_KERNEL
   18: # define _KERNEL
   19: # define KERNEL
   20: #endif
   21: #ifdef __OpenBSD__
   22: struct file;
   23: #endif
   24: #include <sys/uio.h>
   25: #ifdef ADD_KERNEL
   26: # undef _KERNEL
   27: # undef KERNEL
   28: #endif
   29: #include <sys/time.h>
   30: #include <sys/socket.h>
   31: #include <sys/syslog.h>
   32: #include <sys/ioctl.h>
   33: #include <net/if.h>
   34: #if __FreeBSD_version >= 300000
   35: # include <net/if_var.h>
   36: #endif
   37: #include <netinet/in.h>
   38: #include <netinet/in_systm.h>
   39: #include <netinet/ip.h>
   40: #include <netinet/ip_icmp.h>
   41: #ifndef	TCP_PAWS_IDLE	/* IRIX */
   42: # include <netinet/tcp.h>
   43: #endif
   44: #include <netinet/udp.h>
   45: 
   46: #include <arpa/inet.h>
   47: 
   48: #include <errno.h>
   49: #include <limits.h>
   50: #include <netdb.h>
   51: #include <stdlib.h>
   52: #include <fcntl.h>
   53: #include <syslog.h>
   54: #include <stddef.h>
   55: #include <stdio.h>
   56: #if !defined(__SVR4) && !defined(__svr4__) && defined(sun)
   57: # include <strings.h>
   58: #endif
   59: #include <string.h>
   60: #include <unistd.h>
   61: 
   62: #include "../config.h"
   63: #include "netinet/ipl.h"
   64: #include "netinet/ip_compat.h"
   65: #include "netinet/ip_fil.h"
   66: #include "netinet/ip_nat.h"
   67: #include "netinet/ip_state.h"
   68: 
   69: 
   70: #ifndef __P
   71: # ifdef __STDC__
   72: #  define	__P(x)	x
   73: # else
   74: #  define	__P(x)	()
   75: # endif
   76: #endif
   77: #ifndef __STDC__
   78: # undef		const
   79: # define	const
   80: #endif
   81: 
   82: #ifndef	U_32_T
   83: # define	U_32_T	1
   84: # if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \
   85: 	defined(__sgi)
   86: typedef	u_int32_t	u_32_t;
   87: # else
   88: #  if defined(__alpha__) || defined(__alpha) || defined(_LP64)
   89: typedef unsigned int	u_32_t;
   90: #  else
   91: #   if SOLARIS2 >= 6
   92: typedef uint32_t	u_32_t;
   93: #   else
   94: typedef unsigned int	u_32_t;
   95: #   endif
   96: #  endif
   97: # endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ || __sgi */
   98: #endif /* U_32_T */
   99: 
  100: 
  101: #if defined(__NetBSD__) || defined(__OpenBSD__) ||			\
  102:         (_BSDI_VERSION >= 199701) || (__FreeBSD_version >= 300000) ||	\
  103: 	SOLARIS || defined(__sgi) || defined(__osf__) || defined(linux)
  104: # include <stdarg.h>
  105: typedef	int	(* ioctlfunc_t) __P((int, ioctlcmd_t, ...));
  106: #else
  107: typedef	int	(* ioctlfunc_t) __P((dev_t, ioctlcmd_t, void *));
  108: #endif
  109: typedef	void	(* addfunc_t) __P((int, ioctlfunc_t, void *));
  110: typedef	int	(* copyfunc_t) __P((void *, void *, size_t));
  111: 
  112: 
  113: /*
  114:  * SunOS4
  115:  */
  116: #if defined(sun) && !defined(__SVR4) && !defined(__svr4__)
  117: extern	int	ioctl __P((int, int, void *));
  118: #endif
  119: 
  120: #include "../upnpglobalvars.h"
  121: 
  122: /* group name */
  123: static const char group_name[] = "miniupnpd";
  124: 
  125: static int dev = -1;
  126: static int dev_ipl = -1;
  127: 
  128: /* IPFilter cannot store redirection descriptions, so we use our
  129:  * own structure to store them */
  130: struct rdr_desc {
  131: 	struct rdr_desc * next;
  132: 	unsigned short eport;
  133: 	int proto;
  134: 	char str[];
  135: };
  136: 
  137: /* pointer to the chained list where descriptions are stored */
  138: static struct rdr_desc * rdr_desc_list;
  139: 
  140: static void
  141: add_redirect_desc(unsigned short eport, int proto, const char * desc)
  142: {
  143: 	struct rdr_desc * p;
  144: 	size_t l;
  145: 	
  146: 	if (desc != NULL) {
  147: 		l = strlen(desc) + 1;
  148: 		p = malloc(sizeof(struct rdr_desc) + l);
  149: 		if (p) {
  150: 			p->next = rdr_desc_list;
  151: 			p->eport = eport;
  152: 			p->proto = proto;
  153: 			memcpy(p->str, desc, l);
  154: 			rdr_desc_list = p;
  155: 		}
  156: 	}
  157: }
  158: 
  159: static void
  160: del_redirect_desc(unsigned short eport, int proto)
  161: {
  162: 	struct rdr_desc * p, * last;
  163: 
  164: 	last = NULL;
  165: 	for (p = rdr_desc_list; p; p = p->next) {
  166: 		if(p->eport == eport && p->proto == proto) {
  167: 			if (last == NULL)
  168: 				rdr_desc_list = p->next;
  169: 			else
  170: 				last->next = p->next;
  171: 			free(p);
  172: 			return;
  173: 		}
  174: 	}
  175: }
  176: 
  177: static void
  178: get_redirect_desc(unsigned short eport, int proto, char * desc, int desclen)
  179: {
  180: 	struct rdr_desc * p;
  181: 	
  182: 	if (desc == NULL || desclen == 0)
  183: 		return;
  184: 	for (p = rdr_desc_list; p; p = p->next) {
  185: 		if (p->eport == eport && p->proto == proto)
  186: 		{
  187: 			strncpy(desc, p->str, desclen);
  188: 			return;
  189: 		}
  190: 	}
  191: }
  192: 
  193: int init_redirect(void)
  194: {
  195: 	
  196: 	dev = open(IPNAT_NAME, O_RDWR);
  197: 	if (dev < 0) {
  198: 		syslog(LOG_ERR, "open(\"%s\"): %m", IPNAT_NAME);
  199: 		return -1;
  200: 	}
  201: 	dev_ipl = open(IPL_NAME, O_RDWR);
  202: 	if (dev_ipl < 0) {
  203: 		syslog(LOG_ERR, "open(\"%s\"): %m", IPL_NAME);
  204: 		return -1;
  205: 	}
  206: 	return 0;
  207: }
  208: 
  209: void shutdown_redirect(void)
  210: {
  211: 	
  212: 	if (dev >= 0) {
  213: 		close(dev);
  214: 		dev = -1;
  215: 	}
  216: 	if (dev_ipl >= 0) {
  217: 		close(dev_ipl);
  218: 		dev = -1;
  219: 	}
  220: 	return;
  221: }
  222: 
  223: int
  224: add_redirect_rule2(const char * ifname, unsigned short eport,
  225:     const char * iaddr, unsigned short iport, int proto,
  226:     const char * desc)
  227: {
  228: 	struct ipnat ipnat;
  229: 	struct ipfobj obj;
  230: 	int r;
  231: 
  232: 	if (dev < 0) {
  233: 		syslog(LOG_ERR, "%s not open", IPNAT_NAME);
  234: 		return -1;
  235: 	}
  236: 
  237: 	memset(&obj, 0, sizeof(obj));
  238: 	memset(&ipnat, 0, sizeof(ipnat));
  239: 
  240: 	ipnat.in_redir = NAT_REDIRECT;
  241: 	ipnat.in_p = proto;
  242: 	if (proto == IPPROTO_TCP)
  243: 		ipnat.in_flags = IPN_TCP;
  244: 	if (proto == IPPROTO_UDP)
  245: 		ipnat.in_flags = IPN_UDP;
  246: 	ipnat.in_dcmp = FR_EQUAL;
  247: 	ipnat.in_pmin = htons(eport);
  248: 	ipnat.in_pmax = htons(eport);
  249: 	ipnat.in_pnext = htons(iport);
  250: 	ipnat.in_v = 4;
  251: 	strlcpy(ipnat.in_tag.ipt_tag, group_name, IPFTAG_LEN);
  252: 
  253: #ifdef USE_IFNAME_IN_RULES
  254: 	if (ifname) {
  255: 		strlcpy(ipnat.in_ifnames[0], ifname, IFNAMSIZ);
  256: 		strlcpy(ipnat.in_ifnames[1], ifname, IFNAMSIZ);
  257: 	}
  258: #endif
  259: 
  260: 	inet_pton(AF_INET, iaddr, &ipnat.in_in[0].in4);
  261: 	ipnat.in_in[1].in4.s_addr = 0xffffffff;
  262: 
  263: 	obj.ipfo_rev = IPFILTER_VERSION;
  264: 	obj.ipfo_size = sizeof(ipnat);
  265: 	obj.ipfo_ptr = &ipnat;
  266: 	obj.ipfo_type = IPFOBJ_IPNAT;
  267: 
  268: 	r = ioctl(dev, SIOCADNAT, &obj);
  269: 	if (r == -1)
  270: 		syslog(LOG_ERR, "ioctl(SIOCADNAT): %m");
  271: 	else
  272: 		add_redirect_desc(eport, proto, desc);
  273: 	return r;
  274: }
  275: 
  276: /* get_redirect_rule()
  277:  * return value : 0 success (found)
  278:  * -1 = error or rule not found */
  279: int
  280: get_redirect_rule(const char * ifname, unsigned short eport, int proto,
  281:     char * iaddr, int iaddrlen, unsigned short * iport,
  282:     char * desc, int desclen,
  283:     u_int64_t * packets, u_int64_t * bytes)
  284: {
  285: 	ipfgeniter_t iter;
  286: 	ipfobj_t obj;
  287: 	ipnat_t ipn;
  288: 	int r;
  289: 
  290: 	memset(&obj, 0, sizeof(obj));
  291: 	obj.ipfo_rev = IPFILTER_VERSION;
  292: 	obj.ipfo_type = IPFOBJ_GENITER;
  293: 	obj.ipfo_size = sizeof(iter);
  294: 	obj.ipfo_ptr = &iter;
  295: 
  296: 	iter.igi_type = IPFGENITER_IPNAT;
  297: #if IPFILTER_VERSION > 4011300
  298: 	iter.igi_nitems = 1;
  299: #endif
  300: 	iter.igi_data = &ipn;
  301: 
  302: 	if (dev < 0) {
  303: 		syslog(LOG_ERR, "%s not open", IPNAT_NAME);
  304: 		return -1;
  305: 	}
  306: 
  307: 	r = -1;
  308: 	do {
  309: 		if (ioctl(dev, SIOCGENITER, &obj) == -1) {
  310: 			syslog(LOG_ERR, "ioctl(dev, SIOCGENITER): %m");
  311: 			break;
  312: 		}
  313: 		if (eport == ntohs(ipn.in_pmin) &&
  314: 		    eport == ntohs(ipn.in_pmax) &&
  315: 		    strcmp(ipn.in_tag.ipt_tag, group_name) == 0 &&
  316: 		    ipn.in_p == proto)
  317: 		{
  318: 			strlcpy(desc, "", desclen);
  319: 			if (packets != NULL)
  320: 				*packets = 0;
  321: 			if (bytes != NULL)
  322: 				*bytes = 0;
  323: 			if (iport != NULL)
  324: 				*iport = ntohs(ipn.in_pnext);
  325: 			if (desc != NULL)
  326: 				get_redirect_desc(eport, proto, desc, desclen);
  327: 			inet_ntop(AF_INET, &ipn.in_in[0].in4, iaddr, iaddrlen);
  328: 			r = 0;
  329: 		}
  330: 	} while (ipn.in_next != NULL);
  331: 	return r;
  332: }
  333: 
  334: 
  335: int
  336: get_redirect_rule_by_index(int index,
  337:     char * ifname, unsigned short * eport,
  338:     char * iaddr, int iaddrlen, unsigned short * iport,
  339:     int * proto, char * desc, int desclen,
  340:     u_int64_t * packets, u_int64_t * bytes)
  341: {
  342: 	ipfgeniter_t iter;
  343: 	ipfobj_t obj;
  344: 	ipnat_t ipn;
  345: 	int n, r;
  346: 
  347: 	if (index < 0)
  348: 		return -1;
  349: 
  350: 	if (dev < 0) {
  351: 		syslog(LOG_ERR, "%s not open", IPNAT_NAME);
  352: 		return -1;
  353: 	}
  354: 
  355: 	memset(&obj, 0, sizeof(obj));
  356: 	obj.ipfo_rev = IPFILTER_VERSION;
  357: 	obj.ipfo_ptr = &iter;
  358: 	obj.ipfo_size = sizeof(iter);
  359: 	obj.ipfo_type = IPFOBJ_GENITER;
  360: 
  361: 	iter.igi_type = IPFGENITER_IPNAT;
  362: #if IPFILTER_VERSION > 4011300
  363: 	iter.igi_nitems = 1;
  364: #endif
  365: 	iter.igi_data = &ipn;
  366: 
  367: 	n = 0;
  368: 	r = -1;
  369: 	do {
  370: 		if (ioctl(dev, SIOCGENITER, &obj) == -1) {
  371: 			syslog(LOG_ERR, "%s:ioctl(SIOCGENITER): %m",
  372: 			    "get_redirect_rule_by_index");
  373: 			break;
  374: 		}
  375: 
  376: 		if (strcmp(ipn.in_tag.ipt_tag, group_name) != 0)
  377: 			continue;
  378: 
  379: 		if (index == n++) {
  380: 			*proto = ipn.in_p;
  381: 			*eport = ntohs(ipn.in_pmax);
  382: 			*iport = ntohs(ipn.in_pnext);
  383: 
  384: 			if (ifname)
  385: 				strlcpy(ifname, ipn.in_ifnames[0], IFNAMSIZ);
  386: 			if (packets != NULL)
  387: 				*packets = 0;
  388: 			if (bytes != NULL)
  389: 				*bytes = 0;
  390: 			if (desc != NULL)
  391: 				get_redirect_desc(*eport, *proto, desc, desclen);
  392: 			inet_ntop(AF_INET, &ipn.in_in[0].in4, iaddr, iaddrlen);
  393: 			r = 0;
  394: 		}
  395: 	} while (ipn.in_next != NULL);
  396: 	return r;
  397: }
  398: 
  399: static int
  400: real_delete_redirect_rule(const char * ifname, unsigned short eport, int proto)
  401: {
  402: 	ipfgeniter_t iter;
  403: 	ipfobj_t obj;
  404: 	ipnat_t ipn;
  405: 	int r;
  406: 
  407: 	memset(&obj, 0, sizeof(obj));
  408: 	obj.ipfo_rev = IPFILTER_VERSION;
  409: 	obj.ipfo_type = IPFOBJ_GENITER;
  410: 	obj.ipfo_size = sizeof(iter);
  411: 	obj.ipfo_ptr = &iter;
  412: 
  413: 	iter.igi_type = IPFGENITER_IPNAT;
  414: #if IPFILTER_VERSION > 4011300
  415: 	iter.igi_nitems = 1;
  416: #endif
  417: 	iter.igi_data = &ipn;
  418: 
  419: 	if (dev < 0) {
  420: 		syslog(LOG_ERR, "%s not open", IPNAT_NAME);
  421: 		return -1;
  422: 	}
  423: 
  424: 	r = -1;
  425: 	do {
  426: 		if (ioctl(dev, SIOCGENITER, &obj) == -1) {
  427: 			syslog(LOG_ERR, "%s:ioctl(SIOCGENITER): %m",
  428: 			    "delete_redirect_rule");
  429: 			break;
  430: 		}
  431: 		if (eport == ntohs(ipn.in_pmin) &&
  432: 		    eport == ntohs(ipn.in_pmax) &&
  433: 		    strcmp(ipn.in_tag.ipt_tag, group_name) == 0 &&
  434: 		    ipn.in_p == proto)
  435: 		{
  436: 			obj.ipfo_rev = IPFILTER_VERSION;
  437: 			obj.ipfo_size = sizeof(ipn);
  438: 			obj.ipfo_ptr = &ipn;
  439: 			obj.ipfo_type = IPFOBJ_IPNAT;
  440: 			r = ioctl(dev, SIOCRMNAT, &obj);
  441: 			if (r == -1)
  442: 				syslog(LOG_ERR, "%s:ioctl(SIOCRMNAT): %m",
  443: 				    "delete_redirect_rule");
  444: 			/* Delete the desc even if the above failed */
  445: 			del_redirect_desc(eport, proto);
  446: 			break;
  447: 		}
  448: 	} while (ipn.in_next != NULL);
  449: 	return r;
  450: }
  451: 
  452: /* FIXME: For some reason, the iter isn't reset every other delete,
  453:  * so we attempt 2 deletes. */
  454: int
  455: delete_redirect_rule(const char * ifname, unsigned short eport, int proto)
  456: {
  457: 	int r;
  458: 
  459: 	r = real_delete_redirect_rule(ifname, eport, proto);
  460: 	if (r == -1)
  461: 		r = real_delete_redirect_rule(ifname, eport, proto);
  462: 	return r;
  463: }
  464: 
  465: /* thanks to Seth Mos for this function */
  466: int
  467: add_filter_rule2(const char * ifname, const char * iaddr,
  468:     unsigned short eport, unsigned short iport,
  469:     int proto, const char * desc)
  470: {
  471: 	ipfobj_t obj;
  472: 	frentry_t fr;
  473: 	fripf_t ipffr;
  474: 	int r;
  475: 
  476: 	if (dev_ipl < 0) {
  477: 		syslog(LOG_ERR, "%s not open", IPL_NAME);
  478: 		return -1;
  479: 	}
  480: 
  481: 	memset(&obj, 0, sizeof(obj));
  482: 	memset(&fr, 0, sizeof(fr));
  483: 	memset(&ipffr, 0, sizeof(ipffr));
  484: 
  485: 	fr.fr_flags = FR_PASS|FR_KEEPSTATE|FR_QUICK|FR_INQUE;
  486: 	if (GETFLAG(LOGPACKETSMASK))
  487: 		fr.fr_flags |= FR_LOG|FR_LOGFIRST;
  488: 	fr.fr_v = 4;
  489: 
  490: 	fr.fr_type = FR_T_IPF;
  491: 	fr.fr_dun.fru_ipf = &ipffr;
  492: 	fr.fr_dsize = sizeof(ipffr);
  493: 	fr.fr_isc = (void *)-1;
  494: 
  495: 	fr.fr_proto = proto;
  496: 	fr.fr_mproto = 0xff;
  497: 	fr.fr_dcmp = FR_EQUAL;
  498: 	fr.fr_dport = eport;
  499: #ifdef USE_IFNAME_IN_RULES
  500: 	if (ifname)
  501: 		strlcpy(fr.fr_ifnames[0], ifname, IFNAMSIZ);
  502: #endif
  503: 	strlcpy(fr.fr_group, group_name, sizeof(fr.fr_group));
  504: 
  505: 	if (proto == IPPROTO_TCP) {
  506: 		fr.fr_tcpf = TH_SYN;
  507: 		fr.fr_tcpfm = TH_SYN|TH_ACK|TH_RST|TH_FIN|TH_URG|TH_PUSH;
  508: 	}
  509: 
  510: 	inet_pton(AF_INET, iaddr, &fr.fr_daddr);
  511: 	fr.fr_dmask = 0xffffffff;
  512: 
  513: 	obj.ipfo_rev = IPFILTER_VERSION;
  514: 	obj.ipfo_ptr = &fr;
  515: 	obj.ipfo_size = sizeof(fr);
  516: 
  517: 	r = ioctl(dev_ipl, SIOCINAFR, &obj);
  518: 	if (r == -1) {
  519: 		if (errno == ESRCH)
  520: 			syslog(LOG_ERR,
  521: 			    "SIOCINAFR(missing 'head %s' rule?):%m",
  522: 			    group_name);
  523: 		else
  524: 			syslog(LOG_ERR, "SIOCINAFR:%m");
  525: 	}
  526: 	return r;
  527: }
  528: 
  529: int
  530: delete_filter_rule(const char * ifname, unsigned short eport, int proto)
  531: {
  532: 	ipfobj_t wobj, dobj;
  533: 	ipfruleiter_t rule;
  534: 	u_long darray[1000];
  535: 	u_long array[1000];
  536: 	friostat_t fio;
  537: 	frentry_t *fp;
  538: 	int r;
  539: 
  540: 	if (dev_ipl < 0) {
  541: 		syslog(LOG_ERR, "%s not open", IPL_NAME);
  542: 		return -1;
  543: 	}
  544: 
  545: 	wobj.ipfo_rev = IPFILTER_VERSION;
  546: 	wobj.ipfo_type = IPFOBJ_IPFSTAT;
  547: 	wobj.ipfo_size = sizeof(fio);
  548: 	wobj.ipfo_ptr = &fio;
  549: 
  550: 	if (ioctl(dev_ipl, SIOCGETFS, &wobj) == -1) {
  551: 		syslog(LOG_ERR, "ioctl(SIOCGETFS): %m");
  552: 		return -1;
  553: 	}
  554: 
  555: 	wobj.ipfo_rev = IPFILTER_VERSION;
  556: 	wobj.ipfo_ptr = &rule;
  557: 	wobj.ipfo_size = sizeof(rule);
  558: 	wobj.ipfo_type = IPFOBJ_IPFITER;
  559: 
  560: 	fp = (frentry_t *)array;
  561: 	fp->fr_dun.fru_data = darray;
  562: 	fp->fr_dsize = sizeof(darray);
  563: 
  564: 	rule.iri_inout = 0;
  565: 	rule.iri_active = fio.f_active;
  566: #if IPFILTER_VERSION > 4011300
  567: 	rule.iri_nrules = 1;
  568: 	rule.iri_v = 4;
  569: #endif
  570: 	rule.iri_rule = fp;
  571: 	strlcpy(rule.iri_group, group_name, sizeof(rule.iri_group));
  572: 
  573: 	dobj.ipfo_rev = IPFILTER_VERSION;
  574: 	dobj.ipfo_size = sizeof(*fp);
  575: 	dobj.ipfo_type = IPFOBJ_FRENTRY;
  576: 
  577: 	r = -1;
  578: 	do {
  579: 		memset(array, 0xff, sizeof(array));
  580: 
  581: 		if (ioctl(dev_ipl, SIOCIPFITER, &wobj) == -1) {
  582: 			syslog(LOG_ERR, "ioctl(SIOCIPFITER): %m");
  583: 			break;
  584: 		}
  585: 
  586: 		if (fp->fr_data != NULL)
  587: 			fp->fr_data = (char *)fp + sizeof(*fp);
  588: 		if ((fp->fr_type & ~FR_T_BUILTIN) == FR_T_IPF &&
  589: 		    fp->fr_dport == eport &&
  590: 		    fp->fr_proto == proto)
  591: 		{
  592: 			dobj.ipfo_ptr = fp;
  593: 
  594: 			r = ioctl(dev_ipl, SIOCRMAFR, &dobj);
  595: 			if (r == -1)
  596: 				syslog(LOG_ERR, "ioctl(SIOCRMAFR): %m");
  597: 			break;
  598: 		}
  599: 	} while (fp->fr_next != NULL);
  600: 	return r;
  601: }
  602: 

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