File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / upnpredirect.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, 10 months ago) by misho
Branches: miniupnpd, elwix, MAIN
CVS tags: v1_8p0, v1_8, HEAD
1.8

    1: /* $Id: upnpredirect.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-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 <sys/types.h>
   12: #include <sys/socket.h>
   13: #include <netinet/in.h>
   14: #include <net/if.h>
   15: #include <arpa/inet.h>
   16: 
   17: #include <stdio.h>
   18: #include <ctype.h>
   19: #include <unistd.h>
   20: 
   21: #include "macros.h"
   22: #include "config.h"
   23: #include "upnpredirect.h"
   24: #include "upnpglobalvars.h"
   25: #include "upnpevents.h"
   26: #if defined(USE_NETFILTER)
   27: #include "netfilter/iptcrdr.h"
   28: #endif
   29: #if defined(USE_PF)
   30: #include "pf/obsdrdr.h"
   31: #endif
   32: #if defined(USE_IPF)
   33: #include "ipf/ipfrdr.h"
   34: #endif
   35: #if defined(USE_IPFW)
   36: #include "ipfw/ipfwrdr.h"
   37: #endif
   38: #ifdef USE_MINIUPNPDCTL
   39: #include <stdio.h>
   40: #include <unistd.h>
   41: #endif
   42: #ifdef ENABLE_LEASEFILE
   43: #include <sys/stat.h>
   44: #endif
   45: 
   46: /* from <inttypes.h> */
   47: #ifndef PRIu64
   48: #define PRIu64 "llu"
   49: #endif
   50: 
   51: /* proto_atoi()
   52:  * convert the string "UDP" or "TCP" to IPPROTO_UDP and IPPROTO_UDP */
   53: static int
   54: proto_atoi(const char * protocol)
   55: {
   56: 	int proto = IPPROTO_TCP;
   57: 	if(strcmp(protocol, "UDP") == 0)
   58: 		proto = IPPROTO_UDP;
   59: 	return proto;
   60: }
   61: 
   62: #ifdef ENABLE_LEASEFILE
   63: static int
   64: lease_file_add(unsigned short eport,
   65:                const char * iaddr,
   66:                unsigned short iport,
   67:                int proto,
   68:                const char * desc,
   69:                unsigned int timestamp)
   70: {
   71: 	FILE * fd;
   72: 
   73: 	if (lease_file == NULL) return 0;
   74: 
   75: 	fd = fopen( lease_file, "a");
   76: 	if (fd==NULL) {
   77: 		syslog(LOG_ERR, "could not open lease file: %s", lease_file);
   78: 		return -1;
   79: 	}
   80: 
   81: 	fprintf(fd, "%s:%hu:%s:%hu:%u:%s\n",
   82: 	        ((proto==IPPROTO_TCP)?"TCP":"UDP"), eport, iaddr, iport,
   83: 	        timestamp, desc);
   84: 	fclose(fd);
   85: 
   86: 	return 0;
   87: }
   88: 
   89: static int
   90: lease_file_remove(unsigned short eport, int proto)
   91: {
   92: 	FILE* fd, *fdt;
   93: 	int tmp;
   94: 	char buf[512];
   95: 	char str[32];
   96: 	char tmpfilename[128];
   97: 	int str_size, buf_size;
   98: 
   99: 
  100: 	if (lease_file == NULL) return 0;
  101: 
  102: 	if (strlen( lease_file) + 7 > sizeof(tmpfilename)) {
  103: 		syslog(LOG_ERR, "Lease filename is too long");
  104: 		return -1;
  105: 	}
  106: 
  107: 	strncpy( tmpfilename, lease_file, sizeof(tmpfilename) );
  108: 	strncat( tmpfilename, "XXXXXX", sizeof(tmpfilename) - strlen(tmpfilename));
  109: 
  110: 	fd = fopen( lease_file, "r");
  111: 	if (fd==NULL) {
  112: 		return 0;
  113: 	}
  114: 
  115: 	snprintf( str, sizeof(str), "%s:%u", ((proto==IPPROTO_TCP)?"TCP":"UDP"), eport);
  116: 	str_size = strlen(str);
  117: 
  118: 	tmp = mkstemp(tmpfilename);
  119: 	if (tmp==-1) {
  120: 		fclose(fd);
  121: 		syslog(LOG_ERR, "could not open temporary lease file");
  122: 		return -1;
  123: 	}
  124: 	fchmod(tmp, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  125: 	fdt = fdopen(tmp, "a");
  126: 
  127: 	buf[sizeof(buf)-1] = 0;
  128: 	while( fgets(buf, sizeof(buf)-1, fd) != NULL) {
  129: 		buf_size = strlen(buf);
  130: 
  131: 		if (buf_size < str_size || strncmp(str, buf, str_size)!=0) {
  132: 			fwrite(buf, buf_size, 1, fdt);
  133: 		}
  134: 	}
  135: 	fclose(fdt);
  136: 	fclose(fd);
  137: 
  138: 	if (rename(tmpfilename, lease_file) < 0) {
  139: 		syslog(LOG_ERR, "could not rename temporary lease file to %s", lease_file);
  140: 		remove(tmpfilename);
  141: 	}
  142: 
  143: 	return 0;
  144: 
  145: }
  146: 
  147: /* reload_from_lease_file()
  148:  * read lease_file and add the rules contained
  149:  */
  150: int reload_from_lease_file()
  151: {
  152: 	FILE * fd;
  153: 	char * p;
  154: 	unsigned short eport, iport;
  155: 	char * proto;
  156: 	char * iaddr;
  157: 	char * desc;
  158: 	char * rhost;
  159: 	unsigned int leaseduration;
  160: 	unsigned int timestamp;
  161: 	time_t current_time;
  162: 	char line[128];
  163: 	int r;
  164: 
  165: 	if(!lease_file) return -1;
  166: 	fd = fopen( lease_file, "r");
  167: 	if (fd==NULL) {
  168: 		syslog(LOG_ERR, "could not open lease file: %s", lease_file);
  169: 		return -1;
  170: 	}
  171: 	if(unlink(lease_file) < 0) {
  172: 		syslog(LOG_WARNING, "could not unlink file %s : %m", lease_file);
  173: 	}
  174: 
  175: 	current_time = time(NULL);
  176: 	while(fgets(line, sizeof(line), fd)) {
  177: 		syslog(LOG_DEBUG, "parsing lease file line '%s'", line);
  178: 		proto = line;
  179: 		p = strchr(line, ':');
  180: 		if(!p) {
  181: 			syslog(LOG_ERR, "unrecognized data in lease file");
  182: 			continue;
  183: 		}
  184: 		*(p++) = '\0';
  185: 		iaddr = strchr(p, ':');
  186: 		if(!iaddr) {
  187: 			syslog(LOG_ERR, "unrecognized data in lease file");
  188: 			continue;
  189: 		}
  190: 		*(iaddr++) = '\0';
  191: 		eport = (unsigned short)atoi(p);
  192: 		p = strchr(iaddr, ':');
  193: 		if(!p) {
  194: 			syslog(LOG_ERR, "unrecognized data in lease file");
  195: 			continue;
  196: 		}
  197: 		*(p++) = '\0';
  198: 		iport = (unsigned short)atoi(p);
  199: 		p = strchr(p, ':');
  200: 		if(!p) {
  201: 			syslog(LOG_ERR, "unrecognized data in lease file");
  202: 			continue;
  203: 		}
  204: 		*(p++) = '\0';
  205: 		desc = strchr(p, ':');
  206: 		if(!desc) {
  207: 			syslog(LOG_ERR, "unrecognized data in lease file");
  208: 			continue;
  209: 		}
  210: 		*(desc++) = '\0';
  211: 		/*timestamp = (unsigned int)atoi(p);*/
  212: 		timestamp = (unsigned int)strtoul(p, NULL, 10);
  213: 		/* trim description */
  214: 		while(isspace(*desc))
  215: 			desc++;
  216: 		p = desc;
  217: 		while(*(p+1))
  218: 			p++;
  219: 		while(isspace(*p) && (p > desc))
  220: 			*(p--) = '\0';
  221: 
  222: 		if(timestamp > 0) {
  223: 			if(timestamp <= (unsigned int)current_time) {
  224: 				syslog(LOG_NOTICE, "already expired lease in lease file");
  225: 				continue;
  226: 			} else {
  227: 				leaseduration = timestamp - current_time;
  228: 			}
  229: 		} else {
  230: 			leaseduration = 0;	/* default value */
  231: 		}
  232: 		rhost = NULL;
  233: 		r = upnp_redirect(rhost, eport, iaddr, iport, proto, desc, leaseduration);
  234: 		if(r == -1) {
  235: 			syslog(LOG_ERR, "Failed to redirect %hu -> %s:%hu protocol %s",
  236: 			       eport, iaddr, iport, proto);
  237: 		} else if(r == -2) {
  238: 			/* Add the redirection again to the lease file */
  239: 			lease_file_add(eport, iaddr, iport, proto_atoi(proto),
  240: 			               desc, timestamp);
  241: 		}
  242: 	}
  243: 	fclose(fd);
  244: 
  245: 	return 0;
  246: }
  247: #endif
  248: 
  249: /* upnp_redirect()
  250:  * calls OS/fw dependant implementation of the redirection.
  251:  * protocol should be the string "TCP" or "UDP"
  252:  * returns: 0 on success
  253:  *          -1 failed to redirect
  254:  *          -2 already redirected
  255:  *          -3 permission check failed
  256:  */
  257: int
  258: upnp_redirect(const char * rhost, unsigned short eport,
  259:               const char * iaddr, unsigned short iport,
  260:               const char * protocol, const char * desc,
  261:               unsigned int leaseduration)
  262: {
  263: 	int proto, r;
  264: 	char iaddr_old[32];
  265: 	unsigned short iport_old;
  266: 	struct in_addr address;
  267: 	unsigned int timestamp;
  268: 
  269: 	proto = proto_atoi(protocol);
  270: 	if(inet_aton(iaddr, &address) < 0) {
  271: 		syslog(LOG_ERR, "inet_aton(%s) : %m", iaddr);
  272: 		return -1;
  273: 	}
  274: 
  275: 	if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm,
  276: 	                                        eport, address, iport)) {
  277: 		syslog(LOG_INFO, "redirection permission check failed for "
  278: 		                 "%hu->%s:%hu %s", eport, iaddr, iport, protocol);
  279: 		return -3;
  280: 	}
  281: 	r = get_redirect_rule(ext_if_name, eport, proto,
  282: 	                      iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0,
  283: 	                      0, 0,
  284: 	                      &timestamp, 0, 0);
  285: 	if(r == 0) {
  286: 		/* if existing redirect rule matches redirect request return success
  287: 		 * xbox 360 does not keep track of the port it redirects and will
  288: 		 * redirect another port when receiving ConflictInMappingEntry */
  289: 		if(strcmp(iaddr, iaddr_old)==0 && iport==iport_old) {
  290: 			syslog(LOG_INFO, "ignoring redirect request as it matches existing redirect");
  291: 		} else {
  292: 
  293: 			syslog(LOG_INFO, "port %hu protocol %s already redirected to %s:%hu",
  294: 				eport, protocol, iaddr_old, iport_old);
  295: 			return -2;
  296: 		}
  297: 	} else {
  298: 		timestamp = (leaseduration > 0) ? time(NULL) + leaseduration : 0;
  299: 		syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s",
  300: 			eport, iaddr, iport, protocol, desc);
  301: 		return upnp_redirect_internal(rhost, eport, iaddr, iport, proto,
  302: 		                              desc, timestamp);
  303: 	}
  304: 
  305: 	return 0;
  306: }
  307: 
  308: int
  309: upnp_redirect_internal(const char * rhost, unsigned short eport,
  310:                        const char * iaddr, unsigned short iport,
  311:                        int proto, const char * desc,
  312:                        unsigned int timestamp)
  313: {
  314: 	/*syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s",
  315: 		eport, iaddr, iport, protocol, desc);			*/
  316: 	if(add_redirect_rule2(ext_if_name, rhost, eport, iaddr, iport, proto,
  317: 	                      desc, timestamp) < 0) {
  318: 		return -1;
  319: 	}
  320: 
  321: #ifdef ENABLE_LEASEFILE
  322: 	lease_file_add( eport, iaddr, iport, proto, desc, timestamp);
  323: #endif
  324: /*	syslog(LOG_INFO, "creating pass rule to %s:%hu protocol %s for: %s",
  325: 		iaddr, iport, protocol, desc);*/
  326: 	if(add_filter_rule2(ext_if_name, rhost, iaddr, eport, iport, proto, desc) < 0) {
  327: 		/* clean up the redirect rule */
  328: #if !defined(__linux__)
  329: 		delete_redirect_rule(ext_if_name, eport, proto);
  330: #endif
  331: 		return -1;
  332: 	}
  333: 	if(timestamp > 0) {
  334: 		if(!nextruletoclean_timestamp || (timestamp < nextruletoclean_timestamp))
  335: 			nextruletoclean_timestamp = timestamp;
  336: 	}
  337: #ifdef ENABLE_EVENTS
  338: 	/* the number of port mappings changed, we must
  339: 	 * inform the subscribers */
  340: 	upnp_event_var_change_notify(EWanIPC);
  341: #endif
  342: 	return 0;
  343: }
  344: 
  345: 
  346: 
  347: /* Firewall independant code which call the FW dependant code. */
  348: int
  349: upnp_get_redirection_infos(unsigned short eport, const char * protocol,
  350:                            unsigned short * iport,
  351:                            char * iaddr, int iaddrlen,
  352:                            char * desc, int desclen,
  353:                            char * rhost, int rhostlen,
  354:                            unsigned int * leaseduration)
  355: {
  356: 	int r;
  357: 	unsigned int timestamp;
  358: 	time_t current_time;
  359: 
  360: 	if(desc && (desclen > 0))
  361: 		desc[0] = '\0';
  362: 	if(rhost && (rhostlen > 0))
  363: 		rhost[0] = '\0';
  364: 	r = get_redirect_rule(ext_if_name, eport, proto_atoi(protocol),
  365: 	                      iaddr, iaddrlen, iport, desc, desclen,
  366: 	                      rhost, rhostlen, &timestamp,
  367: 	                      0, 0);
  368: 	if(r == 0 &&
  369: 	   timestamp > 0 &&
  370: 	   timestamp > (unsigned int)(current_time = time(NULL))) {
  371: 		*leaseduration = timestamp - current_time;
  372: 	} else {
  373: 		*leaseduration = 0;
  374: 	}
  375: 	return r;
  376: }
  377: 
  378: int
  379: upnp_get_redirection_infos_by_index(int index,
  380:                                     unsigned short * eport, char * protocol,
  381:                                     unsigned short * iport,
  382:                                     char * iaddr, int iaddrlen,
  383:                                     char * desc, int desclen,
  384:                                     char * rhost, int rhostlen,
  385:                                     unsigned int * leaseduration)
  386: {
  387: 	/*char ifname[IFNAMSIZ];*/
  388: 	int proto = 0;
  389: 	unsigned int timestamp;
  390: 	time_t current_time;
  391: 
  392: 	if(desc && (desclen > 0))
  393: 		desc[0] = '\0';
  394: 	if(rhost && (rhostlen > 0))
  395: 		rhost[0] = '\0';
  396: 	if(get_redirect_rule_by_index(index, 0/*ifname*/, eport, iaddr, iaddrlen,
  397: 	                              iport, &proto, desc, desclen,
  398: 	                              rhost, rhostlen, &timestamp,
  399: 	                              0, 0) < 0)
  400: 		return -1;
  401: 	else
  402: 	{
  403: 		current_time = time(NULL);
  404: 		*leaseduration = (timestamp > (unsigned int)current_time)
  405: 		                 ? (timestamp - current_time)
  406: 		                 : 0;
  407: 		if(proto == IPPROTO_TCP)
  408: 			memcpy(protocol, "TCP", 4);
  409: 		else
  410: 			memcpy(protocol, "UDP", 4);
  411: 		return 0;
  412: 	}
  413: }
  414: 
  415: /* called from natpmp.c too */
  416: int
  417: _upnp_delete_redir(unsigned short eport, int proto)
  418: {
  419: 	int r;
  420: #if defined(__linux__)
  421: 	r = delete_redirect_and_filter_rules(eport, proto);
  422: #else
  423: 	r = delete_redirect_rule(ext_if_name, eport, proto);
  424: 	delete_filter_rule(ext_if_name, eport, proto);
  425: #endif
  426: #ifdef ENABLE_LEASEFILE
  427: 	lease_file_remove( eport, proto);
  428: #endif
  429: 
  430: #ifdef ENABLE_EVENTS
  431: 	upnp_event_var_change_notify(EWanIPC);
  432: #endif
  433: 	return r;
  434: }
  435: 
  436: int
  437: upnp_delete_redirection(unsigned short eport, const char * protocol)
  438: {
  439: 	syslog(LOG_INFO, "removing redirect rule port %hu %s", eport, protocol);
  440: 	return _upnp_delete_redir(eport, proto_atoi(protocol));
  441: }
  442: 
  443: /* upnp_get_portmapping_number_of_entries()
  444:  * TODO: improve this code. */
  445: int
  446: upnp_get_portmapping_number_of_entries()
  447: {
  448: 	int n = 0, r = 0;
  449: 	unsigned short eport, iport;
  450: 	char protocol[4], iaddr[32], desc[64], rhost[32];
  451: 	unsigned int leaseduration;
  452: 	do {
  453: 		protocol[0] = '\0'; iaddr[0] = '\0'; desc[0] = '\0';
  454: 		r = upnp_get_redirection_infos_by_index(n, &eport, protocol, &iport,
  455: 		                                        iaddr, sizeof(iaddr),
  456: 		                                        desc, sizeof(desc),
  457: 		                                        rhost, sizeof(rhost),
  458: 		                                        &leaseduration);
  459: 		n++;
  460: 	} while(r==0);
  461: 	return (n-1);
  462: }
  463: 
  464: /* functions used to remove unused rules
  465:  * As a side effect, delete expired rules (based on LeaseDuration) */
  466: struct rule_state *
  467: get_upnp_rules_state_list(int max_rules_number_target)
  468: {
  469: 	/*char ifname[IFNAMSIZ];*/
  470: 	int proto;
  471: 	unsigned short iport;
  472: 	unsigned int timestamp;
  473: 	struct rule_state * tmp;
  474: 	struct rule_state * list = 0;
  475: 	struct rule_state * * p;
  476: 	int i = 0;
  477: 	time_t current_time;
  478: 
  479: 	/*ifname[0] = '\0';*/
  480: 	tmp = malloc(sizeof(struct rule_state));
  481: 	if(!tmp)
  482: 		return 0;
  483: 	current_time = time(NULL);
  484: 	nextruletoclean_timestamp = 0;
  485: 	while(get_redirect_rule_by_index(i, /*ifname*/0, &tmp->eport, 0, 0,
  486: 	                              &iport, &proto, 0, 0, 0,0, &timestamp,
  487: 								  &tmp->packets, &tmp->bytes) >= 0)
  488: 	{
  489: 		tmp->to_remove = 0;
  490: 		if(timestamp > 0) {
  491: 			/* need to remove this port mapping ? */
  492: 			if(timestamp <= (unsigned int)current_time)
  493: 				tmp->to_remove = 1;
  494: 			else if((nextruletoclean_timestamp <= (unsigned int)current_time)
  495: 			       || (timestamp < nextruletoclean_timestamp))
  496: 				nextruletoclean_timestamp = timestamp;
  497: 		}
  498: 		tmp->proto = (short)proto;
  499: 		/* add tmp to list */
  500: 		tmp->next = list;
  501: 		list = tmp;
  502: 		/* prepare next iteration */
  503: 		i++;
  504: 		tmp = malloc(sizeof(struct rule_state));
  505: 		if(!tmp)
  506: 			break;
  507: 	}
  508: 	free(tmp);
  509: 	/* remove the redirections that need to be removed */
  510: 	for(p = &list, tmp = list; tmp; tmp = *p)
  511: 	{
  512: 		if(tmp->to_remove)
  513: 		{
  514: 			syslog(LOG_NOTICE, "remove port mapping %hu %s because it has expired",
  515: 			       tmp->eport, (tmp->proto==IPPROTO_TCP)?"TCP":"UDP");
  516: 			_upnp_delete_redir(tmp->eport, tmp->proto);
  517: 			*p = tmp->next;
  518: 			free(tmp);
  519: 			i--;
  520: 		} else {
  521: 			p = &(tmp->next);
  522: 		}
  523: 	}
  524: 	/* return empty list if not enough redirections */
  525: 	if(i<=max_rules_number_target)
  526: 		while(list)
  527: 		{
  528: 			tmp = list;
  529: 			list = tmp->next;
  530: 			free(tmp);
  531: 		}
  532: 	/* return list */
  533: 	return list;
  534: }
  535: 
  536: void
  537: remove_unused_rules(struct rule_state * list)
  538: {
  539: 	char ifname[IFNAMSIZ];
  540: 	unsigned short iport;
  541: 	struct rule_state * tmp;
  542: 	u_int64_t packets;
  543: 	u_int64_t bytes;
  544: 	unsigned int timestamp;
  545: 	int n = 0;
  546: 
  547: 	while(list)
  548: 	{
  549: 		/* remove the rule if no traffic has used it */
  550: 		if(get_redirect_rule(ifname, list->eport, list->proto,
  551: 	                         0, 0, &iport, 0, 0, 0, 0, &timestamp,
  552: 		                     &packets, &bytes) >= 0)
  553: 		{
  554: 			if(packets == list->packets && bytes == list->bytes)
  555: 			{
  556: 				_upnp_delete_redir(list->eport, list->proto);
  557: 				n++;
  558: 			}
  559: 		}
  560: 		tmp = list;
  561: 		list = tmp->next;
  562: 		free(tmp);
  563: 	}
  564: 	if(n>0)
  565: 		syslog(LOG_NOTICE, "removed %d unused rules", n);
  566: }
  567: 
  568: /* upnp_get_portmappings_in_range()
  569:  * return a list of all "external" ports for which a port
  570:  * mapping exists */
  571: unsigned short *
  572: upnp_get_portmappings_in_range(unsigned short startport,
  573:                                unsigned short endport,
  574:                                const char * protocol,
  575:                                unsigned int * number)
  576: {
  577: 	int proto;
  578: 	proto = proto_atoi(protocol);
  579: 	if(!number)
  580: 		return NULL;
  581: 	return get_portmappings_in_range(startport, endport, proto, number);
  582: }
  583: 
  584: /* stuff for miniupnpdctl */
  585: #ifdef USE_MINIUPNPDCTL
  586: void
  587: write_ruleset_details(int s)
  588: {
  589: 	int proto = 0;
  590: 	unsigned short eport, iport;
  591: 	char desc[64];
  592: 	char iaddr[32];
  593: 	char rhost[32];
  594: 	unsigned int timestamp;
  595: 	u_int64_t packets;
  596: 	u_int64_t bytes;
  597: 	int i = 0;
  598: 	char buffer[256];
  599: 	int n;
  600: 
  601: 	write(s, "Ruleset :\n", 10);
  602: 	while(get_redirect_rule_by_index(i, 0/*ifname*/, &eport, iaddr, sizeof(iaddr),
  603: 	                                 &iport, &proto, desc, sizeof(desc),
  604: 	                                 rhost, sizeof(rhost),
  605: 	                                 &timestamp,
  606: 	                                 &packets, &bytes) >= 0)
  607: 	{
  608: 		n = snprintf(buffer, sizeof(buffer),
  609: 		             "%2d %s %s:%hu->%s:%hu "
  610: 		             "'%s' %u %" PRIu64 " %" PRIu64 "\n",
  611: 		             /*"'%s' %llu %llu\n",*/
  612: 		             i, proto==IPPROTO_TCP?"TCP":"UDP", rhost,
  613: 		             eport, iaddr, iport, desc, timestamp, packets, bytes);
  614: 		write(s, buffer, n);
  615: 		i++;
  616: 	}
  617: }
  618: #endif
  619: 

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