2009-11-02 22:43:19 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2009, The Regents of the University of California, through
|
|
|
|
* Lawrence Berkeley National Laboratory (subject to receipt of any required
|
|
|
|
* approvals from the U.S. Dept. of Energy). All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
// XXX: Surely we do not need all these headers!
|
2009-11-02 22:43:19 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/resource.h>
|
|
|
|
#include <sched.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <setjmp.h>
|
|
|
|
|
|
|
|
#include "iperf.h"
|
|
|
|
#include "iperf_api.h"
|
2010-07-22 18:57:08 +00:00
|
|
|
#include "iperf_error.h"
|
2009-11-06 07:25:10 +00:00
|
|
|
#include "iperf_server_api.h"
|
2009-11-02 22:43:19 +00:00
|
|
|
#include "iperf_tcp.h"
|
|
|
|
#include "timer.h"
|
|
|
|
#include "net.h"
|
|
|
|
#include "tcp_window_size.h"
|
2010-07-09 00:29:51 +00:00
|
|
|
#include "iperf_util.h"
|
2009-11-02 22:43:19 +00:00
|
|
|
#include "locale.h"
|
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
// XXX: Does this belong here? We should probably declare this in iperf_api.c then put an extern in the header
|
2009-11-02 22:43:19 +00:00
|
|
|
jmp_buf env; /* to handle longjmp on signal */
|
|
|
|
|
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
/* iperf_tcp_recv
|
2009-11-02 22:43:19 +00:00
|
|
|
*
|
2010-07-22 18:57:08 +00:00
|
|
|
* receives the data for TCP
|
2009-11-02 22:43:19 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
iperf_tcp_recv(struct iperf_stream * sp)
|
|
|
|
{
|
2010-06-23 19:13:37 +00:00
|
|
|
int result = 0;
|
2010-06-21 15:08:47 +00:00
|
|
|
int size = sp->settings->blksize;
|
2009-11-02 22:43:19 +00:00
|
|
|
|
2010-06-23 19:13:37 +00:00
|
|
|
result = Nread(sp->socket, sp->buffer, size, Ptcp);
|
2010-07-22 18:57:08 +00:00
|
|
|
|
2010-07-20 22:27:50 +00:00
|
|
|
if (result < 0) {
|
|
|
|
return (-1);
|
2009-11-02 22:43:19 +00:00
|
|
|
}
|
2010-07-22 18:57:08 +00:00
|
|
|
|
2010-06-23 19:13:37 +00:00
|
|
|
sp->result->bytes_received += result;
|
|
|
|
sp->result->bytes_received_this_interval += result;
|
2009-11-02 22:43:19 +00:00
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
return (result);
|
2009-11-02 22:43:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
/* iperf_tcp_send
|
2009-11-02 22:43:19 +00:00
|
|
|
*
|
2010-07-22 18:57:08 +00:00
|
|
|
* sends the data for TCP
|
2009-11-02 22:43:19 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
iperf_tcp_send(struct iperf_stream * sp)
|
|
|
|
{
|
2010-06-18 21:08:50 +00:00
|
|
|
int result;
|
|
|
|
int size = sp->settings->blksize;
|
2009-11-02 22:43:19 +00:00
|
|
|
|
|
|
|
result = Nwrite(sp->socket, sp->buffer, size, Ptcp);
|
2010-07-22 18:57:08 +00:00
|
|
|
|
2010-07-20 22:27:50 +00:00
|
|
|
if (result < 0) {
|
|
|
|
return (-1);
|
|
|
|
}
|
2009-11-02 23:56:55 +00:00
|
|
|
|
2010-06-23 19:13:37 +00:00
|
|
|
sp->result->bytes_sent += result;
|
|
|
|
sp->result->bytes_sent_this_interval += result;
|
2009-11-02 22:43:19 +00:00
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
return (result);
|
2009-11-02 22:43:19 +00:00
|
|
|
}
|
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
|
|
|
|
// XXX: This function is now deprecated
|
2009-11-02 22:43:19 +00:00
|
|
|
/**************************************************************************/
|
|
|
|
struct iperf_stream *
|
|
|
|
iperf_new_tcp_stream(struct iperf_test * testp)
|
|
|
|
{
|
|
|
|
struct iperf_stream *sp;
|
|
|
|
|
|
|
|
sp = (struct iperf_stream *) iperf_new_stream(testp);
|
2010-06-23 19:13:37 +00:00
|
|
|
if (!sp) {
|
|
|
|
return (NULL);
|
2009-11-02 22:43:19 +00:00
|
|
|
}
|
|
|
|
sp->rcv = iperf_tcp_recv; /* pointer to receive function */
|
|
|
|
sp->snd = iperf_tcp_send; /* pointer to send function */
|
|
|
|
|
|
|
|
return sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
/* iperf_tcp_accept
|
2009-11-02 22:43:19 +00:00
|
|
|
*
|
2010-07-22 18:57:08 +00:00
|
|
|
* accept a new TCP stream connection
|
2009-11-02 22:43:19 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
iperf_tcp_accept(struct iperf_test * test)
|
|
|
|
{
|
2010-07-22 18:57:08 +00:00
|
|
|
int s;
|
|
|
|
int rbuf = ACCESS_DENIED;
|
|
|
|
char cookie[COOKIE_SIZE];
|
2009-11-02 22:43:19 +00:00
|
|
|
socklen_t len;
|
|
|
|
struct sockaddr_in addr;
|
|
|
|
|
|
|
|
len = sizeof(addr);
|
2010-07-22 18:57:08 +00:00
|
|
|
if ((s = accept(test->listener_tcp, (struct sockaddr *) &addr, &len)) < 0) {
|
|
|
|
i_errno = IESTREAMCONNECT;
|
|
|
|
return (-1);
|
2010-06-21 15:08:47 +00:00
|
|
|
}
|
2009-11-02 22:43:19 +00:00
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) {
|
|
|
|
i_errno = IERECVCOOKIE;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(test->default_settings->cookie, cookie) != 0) {
|
|
|
|
if (Nwrite(s, &rbuf, sizeof(char), Ptcp) < 0) {
|
|
|
|
i_errno = IESENDMESSAGE;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
close(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (s);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* iperf_tcp_listen
|
|
|
|
*
|
|
|
|
* start up a listener for TCP stream connections
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iperf_tcp_listen(struct iperf_test *test)
|
|
|
|
{
|
|
|
|
int s, opt;
|
|
|
|
struct sockaddr_in sa;
|
|
|
|
s = test->listener_tcp;
|
|
|
|
|
|
|
|
if (test->no_delay || test->default_settings->mss) {
|
|
|
|
FD_CLR(s, &test->read_set);
|
|
|
|
close(s);
|
|
|
|
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
|
|
|
i_errno = IESTREAMLISTEN;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
if (test->no_delay) {
|
|
|
|
opt = 1;
|
|
|
|
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
|
|
|
|
i_errno = IESETNODELAY;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
printf(" TCP NODELAY: on\n");
|
|
|
|
}
|
|
|
|
// XXX: Setting MSS is very buggy!
|
|
|
|
if ((opt = test->default_settings->mss)) {
|
|
|
|
if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
|
|
|
|
i_errno = IESETMSS;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
printf(" TCP MSS: %d\n", opt);
|
|
|
|
}
|
|
|
|
opt = 1;
|
|
|
|
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
|
|
|
|
i_errno = IEREUSEADDR;
|
|
|
|
return (-1);
|
|
|
|
}
|
2009-11-02 22:43:19 +00:00
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
sa.sin_family = AF_INET;
|
|
|
|
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
sa.sin_port = htons(test->server_port);
|
2010-06-23 19:13:37 +00:00
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
|
|
|
|
close(s);
|
|
|
|
i_errno = IESTREAMLISTEN;
|
|
|
|
return (-1);
|
|
|
|
}
|
2010-06-21 15:08:47 +00:00
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
if (listen(s, 5) < 0) {
|
|
|
|
i_errno = IESTREAMLISTEN;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
test->listener_tcp = s;
|
|
|
|
/*
|
|
|
|
test->max_fd = (s > test->max_fd) ? s : test->max_fd;
|
|
|
|
FD_SET(test->listener_tcp, &test->read_set);
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
return (s);
|
2009-11-02 22:43:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-07-22 18:57:08 +00:00
|
|
|
/* iperf_tcp_connect
|
|
|
|
*
|
|
|
|
* connect to a TCP stream listener
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iperf_tcp_connect(struct iperf_test *test)
|
|
|
|
{
|
|
|
|
int s, opt;
|
|
|
|
struct sockaddr_in sa;
|
|
|
|
struct hostent *hent;
|
|
|
|
|
|
|
|
if ((hent = gethostbyname(test->server_hostname)) == 0) {
|
|
|
|
i_errno = IESTREAMCONNECT;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
|
|
|
i_errno = IESTREAMCONNECT;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
sa.sin_family = AF_INET;
|
|
|
|
memcpy(&sa.sin_addr.s_addr, hent->h_addr, sizeof(sa.sin_addr.s_addr));
|
|
|
|
sa.sin_port = htons(test->server_port);
|
|
|
|
|
|
|
|
/* Set TCP options */
|
|
|
|
if (test->no_delay) {
|
|
|
|
opt = 1;
|
|
|
|
if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
|
|
|
|
i_errno = IESETNODELAY;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (opt = test->default_settings->mss) {
|
|
|
|
if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
|
|
|
|
i_errno = IESETMSS;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0 && errno != EINPROGRESS) {
|
|
|
|
i_errno = IESTREAMCONNECT;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send cookie for verification */
|
|
|
|
if (Nwrite(s, test->default_settings->cookie, COOKIE_SIZE, Ptcp) < 0) {
|
|
|
|
i_errno = IESENDCOOKIE;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (s);
|
|
|
|
}
|
|
|
|
|