File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / iperf / src / iperf_tcp.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Sep 27 11:14:54 2023 UTC (18 months, 2 weeks ago) by misho
Branches: iperf, MAIN
CVS tags: v3_15, HEAD
Version 3.15

    1: /*
    2:  * iperf, Copyright (c) 2014-2022, The Regents of the University of
    3:  * California, through Lawrence Berkeley National Laboratory (subject
    4:  * to receipt of any required approvals from the U.S. Dept. of
    5:  * Energy).  All rights reserved.
    6:  *
    7:  * If you have questions about your rights to use or distribute this
    8:  * software, please contact Berkeley Lab's Technology Transfer
    9:  * Department at TTD@lbl.gov.
   10:  *
   11:  * NOTICE.  This software is owned by the U.S. Department of Energy.
   12:  * As such, the U.S. Government has been granted for itself and others
   13:  * acting on its behalf a paid-up, nonexclusive, irrevocable,
   14:  * worldwide license in the Software to reproduce, prepare derivative
   15:  * works, and perform publicly and display publicly.  Beginning five
   16:  * (5) years after the date permission to assert copyright is obtained
   17:  * from the U.S. Department of Energy, and subject to any subsequent
   18:  * five (5) year renewals, the U.S. Government is granted for itself
   19:  * and others acting on its behalf a paid-up, nonexclusive,
   20:  * irrevocable, worldwide license in the Software to reproduce,
   21:  * prepare derivative works, distribute copies to the public, perform
   22:  * publicly and display publicly, and to permit others to do so.
   23:  *
   24:  * This code is distributed under a BSD style license, see the LICENSE
   25:  * file for complete information.
   26:  */
   27: #include <stdio.h>
   28: #include <stdlib.h>
   29: #include <string.h>
   30: #include <errno.h>
   31: #include <unistd.h>
   32: #include <arpa/inet.h>
   33: #include <sys/socket.h>
   34: #include <sys/types.h>
   35: #include <netinet/in.h>
   36: #include <netdb.h>
   37: #include <sys/time.h>
   38: #include <sys/select.h>
   39: #include <limits.h>
   40: 
   41: #include "iperf.h"
   42: #include "iperf_api.h"
   43: #include "iperf_tcp.h"
   44: #include "net.h"
   45: #include "cjson.h"
   46: 
   47: #if defined(HAVE_FLOWLABEL)
   48: #include "flowlabel.h"
   49: #endif /* HAVE_FLOWLABEL */
   50: 
   51: /* iperf_tcp_recv
   52:  *
   53:  * receives the data for TCP
   54:  */
   55: int
   56: iperf_tcp_recv(struct iperf_stream *sp)
   57: {
   58:     int r;
   59: 
   60:     r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Ptcp);
   61: 
   62:     if (r < 0)
   63:         return r;
   64: 
   65:     /* Only count bytes received while we're in the correct state. */
   66:     if (sp->test->state == TEST_RUNNING) {
   67: 	sp->result->bytes_received += r;
   68: 	sp->result->bytes_received_this_interval += r;
   69:     }
   70:     else {
   71: 	if (sp->test->debug)
   72: 	    printf("Late receive, state = %d\n", sp->test->state);
   73:     }
   74: 
   75:     return r;
   76: }
   77: 
   78: 
   79: /* iperf_tcp_send
   80:  *
   81:  * sends the data for TCP
   82:  */
   83: int
   84: iperf_tcp_send(struct iperf_stream *sp)
   85: {
   86:     int r;
   87: 
   88:     if (!sp->pending_size)
   89: 	sp->pending_size = sp->settings->blksize;
   90: 
   91:     if (sp->test->zerocopy)
   92: 	r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->pending_size);
   93:     else
   94: 	r = Nwrite(sp->socket, sp->buffer, sp->pending_size, Ptcp);
   95: 
   96:     if (r < 0)
   97:         return r;
   98: 
   99:     sp->pending_size -= r;
  100:     sp->result->bytes_sent += r;
  101:     sp->result->bytes_sent_this_interval += r;
  102: 
  103:     if (sp->test->debug_level >=  DEBUG_LEVEL_DEBUG)
  104: 	printf("sent %d bytes of %d, pending %d, total %" PRIu64 "\n",
  105: 	    r, sp->settings->blksize, sp->pending_size, sp->result->bytes_sent);
  106: 
  107:     return r;
  108: }
  109: 
  110: 
  111: /* iperf_tcp_accept
  112:  *
  113:  * accept a new TCP stream connection
  114:  */
  115: int
  116: iperf_tcp_accept(struct iperf_test * test)
  117: {
  118:     int     s;
  119:     signed char rbuf = ACCESS_DENIED;
  120:     char    cookie[COOKIE_SIZE];
  121:     socklen_t len;
  122:     struct sockaddr_storage addr;
  123: 
  124:     len = sizeof(addr);
  125:     if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
  126:         i_errno = IESTREAMCONNECT;
  127:         return -1;
  128:     }
  129: 
  130:     if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) {
  131:         i_errno = IERECVCOOKIE;
  132:         return -1;
  133:     }
  134: 
  135:     if (strcmp(test->cookie, cookie) != 0) {
  136:         if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) {
  137:             iperf_err(test, "failed to send access denied from busy server to new connecting client, errno = %d\n", errno);
  138:         }
  139:         close(s);
  140:     }
  141: 
  142:     return s;
  143: }
  144: 
  145: 
  146: /* iperf_tcp_listen
  147:  *
  148:  * start up a listener for TCP stream connections
  149:  */
  150: int
  151: iperf_tcp_listen(struct iperf_test *test)
  152: {
  153:     int s, opt;
  154:     socklen_t optlen;
  155:     int saved_errno;
  156:     int rcvbuf_actual, sndbuf_actual;
  157: 
  158:     s = test->listener;
  159: 
  160:     /*
  161:      * If certain parameters are specified (such as socket buffer
  162:      * size), then throw away the listening socket (the one for which
  163:      * we just accepted the control connection) and recreate it with
  164:      * those parameters.  That way, when new data connections are
  165:      * set, they'll have all the correct parameters in place.
  166:      *
  167:      * It's not clear whether this is a requirement or a convenience.
  168:      */
  169:     if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) {
  170: 	struct addrinfo hints, *res;
  171: 	char portstr[6];
  172: 
  173:         FD_CLR(s, &test->read_set);
  174:         close(s);
  175: 
  176:         snprintf(portstr, 6, "%d", test->server_port);
  177:         memset(&hints, 0, sizeof(hints));
  178: 
  179: 	/*
  180: 	 * If binding to the wildcard address with no explicit address
  181: 	 * family specified, then force us to get an AF_INET6 socket.
  182: 	 * More details in the comments in netanounce().
  183: 	 */
  184: 	if (test->settings->domain == AF_UNSPEC && !test->bind_address) {
  185: 	    hints.ai_family = AF_INET6;
  186: 	}
  187: 	else {
  188: 	    hints.ai_family = test->settings->domain;
  189: 	}
  190:         hints.ai_socktype = SOCK_STREAM;
  191:         hints.ai_flags = AI_PASSIVE;
  192:         if ((gerror = getaddrinfo(test->bind_address, portstr, &hints, &res)) != 0) {
  193:             i_errno = IESTREAMLISTEN;
  194:             return -1;
  195:         }
  196: 
  197:         if ((s = socket(res->ai_family, SOCK_STREAM, 0)) < 0) {
  198: 	    freeaddrinfo(res);
  199:             i_errno = IESTREAMLISTEN;
  200:             return -1;
  201:         }
  202: 
  203:         if (test->no_delay) {
  204:             opt = 1;
  205:             if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
  206: 		saved_errno = errno;
  207: 		close(s);
  208: 		freeaddrinfo(res);
  209: 		errno = saved_errno;
  210:                 i_errno = IESETNODELAY;
  211:                 return -1;
  212:             }
  213:         }
  214:         // XXX: Setting MSS is very buggy!
  215:         if ((opt = test->settings->mss)) {
  216:             if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
  217: 		saved_errno = errno;
  218: 		close(s);
  219: 		freeaddrinfo(res);
  220: 		errno = saved_errno;
  221:                 i_errno = IESETMSS;
  222:                 return -1;
  223:             }
  224:         }
  225:         if ((opt = test->settings->socket_bufsize)) {
  226:             if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
  227: 		saved_errno = errno;
  228: 		close(s);
  229: 		freeaddrinfo(res);
  230: 		errno = saved_errno;
  231:                 i_errno = IESETBUF;
  232:                 return -1;
  233:             }
  234:             if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
  235: 		saved_errno = errno;
  236: 		close(s);
  237: 		freeaddrinfo(res);
  238: 		errno = saved_errno;
  239:                 i_errno = IESETBUF;
  240:                 return -1;
  241:             }
  242:         }
  243: #if defined(HAVE_SO_MAX_PACING_RATE)
  244:     /* If fq socket pacing is specified, enable it. */
  245:     if (test->settings->fqrate) {
  246: 	/* Convert bits per second to bytes per second */
  247: 	unsigned int fqrate = test->settings->fqrate / 8;
  248: 	if (fqrate > 0) {
  249: 	    if (test->debug) {
  250: 		printf("Setting fair-queue socket pacing to %u\n", fqrate);
  251: 	    }
  252: 	    if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) {
  253: 		warning("Unable to set socket pacing");
  254: 	    }
  255: 	}
  256:     }
  257: #endif /* HAVE_SO_MAX_PACING_RATE */
  258:     {
  259: 	unsigned int rate = test->settings->rate / 8;
  260: 	if (rate > 0) {
  261: 	    if (test->debug) {
  262: 		printf("Setting application pacing to %u\n", rate);
  263: 	    }
  264: 	}
  265:     }
  266:         opt = 1;
  267:         if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
  268: 	    saved_errno = errno;
  269:             close(s);
  270: 	    freeaddrinfo(res);
  271: 	    errno = saved_errno;
  272:             i_errno = IEREUSEADDR;
  273:             return -1;
  274:         }
  275: 
  276: 	/*
  277: 	 * If we got an IPv6 socket, figure out if it should accept IPv4
  278: 	 * connections as well.  See documentation in netannounce() for
  279: 	 * more details.
  280: 	 */
  281: #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
  282: 	if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET)) {
  283: 	    if (test->settings->domain == AF_UNSPEC)
  284: 		opt = 0;
  285: 	    else
  286: 		opt = 1;
  287: 	    if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
  288: 			   (char *) &opt, sizeof(opt)) < 0) {
  289: 		saved_errno = errno;
  290: 		close(s);
  291: 		freeaddrinfo(res);
  292: 		errno = saved_errno;
  293: 		i_errno = IEV6ONLY;
  294: 		return -1;
  295: 	    }
  296: 	}
  297: #endif /* IPV6_V6ONLY */
  298: 
  299:         if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
  300: 	    saved_errno = errno;
  301:             close(s);
  302: 	    freeaddrinfo(res);
  303: 	    errno = saved_errno;
  304:             i_errno = IESTREAMLISTEN;
  305:             return -1;
  306:         }
  307: 
  308:         freeaddrinfo(res);
  309: 
  310:         if (listen(s, INT_MAX) < 0) {
  311:             i_errno = IESTREAMLISTEN;
  312:             return -1;
  313:         }
  314: 
  315:         test->listener = s;
  316:     }
  317: 
  318:     /* Read back and verify the sender socket buffer size */
  319:     optlen = sizeof(sndbuf_actual);
  320:     if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) {
  321: 	saved_errno = errno;
  322: 	close(s);
  323: 	errno = saved_errno;
  324: 	i_errno = IESETBUF;
  325: 	return -1;
  326:     }
  327:     if (test->debug) {
  328: 	printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize);
  329:     }
  330:     if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) {
  331: 	i_errno = IESETBUF2;
  332: 	return -1;
  333:     }
  334: 
  335:     /* Read back and verify the receiver socket buffer size */
  336:     optlen = sizeof(rcvbuf_actual);
  337:     if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) {
  338: 	saved_errno = errno;
  339: 	close(s);
  340: 	errno = saved_errno;
  341: 	i_errno = IESETBUF;
  342: 	return -1;
  343:     }
  344:     if (test->debug) {
  345: 	printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize);
  346:     }
  347:     if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) {
  348: 	i_errno = IESETBUF2;
  349: 	return -1;
  350:     }
  351: 
  352:     if (test->json_output) {
  353: 	cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize);
  354: 	cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual);
  355: 	cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual);
  356:     }
  357: 
  358:     return s;
  359: }
  360: 
  361: 
  362: /* iperf_tcp_connect
  363:  *
  364:  * connect to a TCP stream listener
  365:  * This function is roughly similar to netdial(), and may indeed have
  366:  * been derived from it at some point, but it sets many TCP-specific
  367:  * options between socket creation and connection.
  368:  */
  369: int
  370: iperf_tcp_connect(struct iperf_test *test)
  371: {
  372:     struct addrinfo *server_res;
  373:     int s, opt;
  374:     socklen_t optlen;
  375:     int saved_errno;
  376:     int rcvbuf_actual, sndbuf_actual;
  377: 
  378:     s = create_socket(test->settings->domain, SOCK_STREAM, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, &server_res);
  379:     if (s < 0) {
  380: 	i_errno = IESTREAMCONNECT;
  381: 	return -1;
  382:     }
  383: 
  384:     /* Set socket options */
  385:     if (test->no_delay) {
  386:         opt = 1;
  387:         if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
  388: 	    saved_errno = errno;
  389: 	    close(s);
  390: 	    freeaddrinfo(server_res);
  391: 	    errno = saved_errno;
  392:             i_errno = IESETNODELAY;
  393:             return -1;
  394:         }
  395:     }
  396:     if ((opt = test->settings->mss)) {
  397:         if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
  398: 	    saved_errno = errno;
  399: 	    close(s);
  400: 	    freeaddrinfo(server_res);
  401: 	    errno = saved_errno;
  402:             i_errno = IESETMSS;
  403:             return -1;
  404:         }
  405:     }
  406:     if ((opt = test->settings->socket_bufsize)) {
  407:         if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
  408: 	    saved_errno = errno;
  409: 	    close(s);
  410: 	    freeaddrinfo(server_res);
  411: 	    errno = saved_errno;
  412:             i_errno = IESETBUF;
  413:             return -1;
  414:         }
  415:         if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
  416: 	    saved_errno = errno;
  417: 	    close(s);
  418: 	    freeaddrinfo(server_res);
  419: 	    errno = saved_errno;
  420:             i_errno = IESETBUF;
  421:             return -1;
  422:         }
  423:     }
  424: #if defined(HAVE_TCP_USER_TIMEOUT)
  425:     if ((opt = test->settings->snd_timeout)) {
  426:         if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
  427: 	    saved_errno = errno;
  428: 	    close(s);
  429: 	    freeaddrinfo(server_res);
  430: 	    errno = saved_errno;
  431:             i_errno = IESETUSERTIMEOUT;
  432:             return -1;
  433:         }
  434:     }
  435: #endif /* HAVE_TCP_USER_TIMEOUT */
  436: 
  437:     /* Read back and verify the sender socket buffer size */
  438:     optlen = sizeof(sndbuf_actual);
  439:     if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) {
  440: 	saved_errno = errno;
  441: 	close(s);
  442: 	freeaddrinfo(server_res);
  443: 	errno = saved_errno;
  444: 	i_errno = IESETBUF;
  445: 	return -1;
  446:     }
  447:     if (test->debug) {
  448: 	printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize);
  449:     }
  450:     if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) {
  451: 	i_errno = IESETBUF2;
  452: 	return -1;
  453:     }
  454: 
  455:     /* Read back and verify the receiver socket buffer size */
  456:     optlen = sizeof(rcvbuf_actual);
  457:     if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) {
  458: 	saved_errno = errno;
  459: 	close(s);
  460: 	freeaddrinfo(server_res);
  461: 	errno = saved_errno;
  462: 	i_errno = IESETBUF;
  463: 	return -1;
  464:     }
  465:     if (test->debug) {
  466: 	printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize);
  467:     }
  468:     if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) {
  469: 	i_errno = IESETBUF2;
  470: 	return -1;
  471:     }
  472: 
  473:     if (test->json_output) {
  474:     cJSON *sock_bufsize_item = cJSON_GetObjectItem(test->json_start, "sock_bufsize");
  475:     if (sock_bufsize_item == NULL) {
  476:     cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize);
  477:     }
  478: 
  479:     cJSON *sndbuf_actual_item = cJSON_GetObjectItem(test->json_start, "sndbuf_actual");
  480:     if (sndbuf_actual_item == NULL) {
  481: 	cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual);
  482:     }
  483:         
  484:     cJSON *rcvbuf_actual_item = cJSON_GetObjectItem(test->json_start, "rcvbuf_actual");
  485:     if (rcvbuf_actual_item == NULL) {
  486: 	cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual);
  487:     }
  488:     }
  489: 
  490: #if defined(HAVE_FLOWLABEL)
  491:     if (test->settings->flowlabel) {
  492:         if (server_res->ai_addr->sa_family != AF_INET6) {
  493: 	    saved_errno = errno;
  494: 	    close(s);
  495: 	    freeaddrinfo(server_res);
  496: 	    errno = saved_errno;
  497:             i_errno = IESETFLOW;
  498:             return -1;
  499: 	} else {
  500: 	    struct sockaddr_in6* sa6P = (struct sockaddr_in6*) server_res->ai_addr;
  501:             char freq_buf[sizeof(struct in6_flowlabel_req)];
  502:             struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf;
  503:             int freq_len = sizeof(*freq);
  504: 
  505:             memset(freq, 0, sizeof(*freq));
  506:             freq->flr_label = htonl(test->settings->flowlabel & IPV6_FLOWINFO_FLOWLABEL);
  507:             freq->flr_action = IPV6_FL_A_GET;
  508:             freq->flr_flags = IPV6_FL_F_CREATE;
  509:             freq->flr_share = IPV6_FL_S_ANY;
  510:             memcpy(&freq->flr_dst, &sa6P->sin6_addr, 16);
  511: 
  512:             if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) < 0) {
  513: 		saved_errno = errno;
  514:                 close(s);
  515:                 freeaddrinfo(server_res);
  516: 		errno = saved_errno;
  517:                 i_errno = IESETFLOW;
  518:                 return -1;
  519:             }
  520:             sa6P->sin6_flowinfo = freq->flr_label;
  521: 
  522:             opt = 1;
  523:             if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, &opt, sizeof(opt)) < 0) {
  524: 		saved_errno = errno;
  525:                 close(s);
  526:                 freeaddrinfo(server_res);
  527: 		errno = saved_errno;
  528:                 i_errno = IESETFLOW;
  529:                 return -1;
  530:             }
  531: 	}
  532:     }
  533: #endif /* HAVE_FLOWLABEL */
  534: 
  535: #if defined(HAVE_SO_MAX_PACING_RATE)
  536:     /* If socket pacing is specified try to enable it. */
  537:     if (test->settings->fqrate) {
  538: 	/* Convert bits per second to bytes per second */
  539: 	unsigned int fqrate = test->settings->fqrate / 8;
  540: 	if (fqrate > 0) {
  541: 	    if (test->debug) {
  542: 		printf("Setting fair-queue socket pacing to %u\n", fqrate);
  543: 	    }
  544: 	    if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) {
  545: 		warning("Unable to set socket pacing");
  546: 	    }
  547: 	}
  548:     }
  549: #endif /* HAVE_SO_MAX_PACING_RATE */
  550:     {
  551: 	unsigned int rate = test->settings->rate / 8;
  552: 	if (rate > 0) {
  553: 	    if (test->debug) {
  554: 		printf("Setting application pacing to %u\n", rate);
  555: 	    }
  556: 	}
  557:     }
  558: 
  559:     /* Set common socket options */
  560:     iperf_common_sockopts(test, s);
  561: 
  562:     if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
  563: 	saved_errno = errno;
  564: 	close(s);
  565: 	freeaddrinfo(server_res);
  566: 	errno = saved_errno;
  567:         i_errno = IESTREAMCONNECT;
  568:         return -1;
  569:     }
  570: 
  571:     freeaddrinfo(server_res);
  572: 
  573:     /* Send cookie for verification */
  574:     if (Nwrite(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
  575: 	saved_errno = errno;
  576: 	close(s);
  577: 	errno = saved_errno;
  578:         i_errno = IESENDCOOKIE;
  579:         return -1;
  580:     }
  581: 
  582:     return s;
  583: }

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