blob: 560a763a24bc830a55949004215cec9393b5a31f [file] [log] [blame]
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <sys/time.h>
#include <sys/select.h>
#include "iperf.h"
#include "iperf_api.h"
#include "iperf_tcp.h"
#include "iperf_error.h"
#include "net.h"
/* iperf_tcp_recv
*
* receives the data for TCP
*/
int
iperf_tcp_recv(struct iperf_stream *sp)
{
int result = 0;
int size = sp->settings->blksize;
result = Nread(sp->socket, sp->buffer, size, Ptcp);
if (result < 0) {
return (-1);
}
sp->result->bytes_received += result;
sp->result->bytes_received_this_interval += result;
return (result);
}
/* iperf_tcp_send
*
* sends the data for TCP
*/
int
iperf_tcp_send(struct iperf_stream *sp)
{
int result;
int size = sp->settings->blksize;
result = Nwrite(sp->socket, sp->buffer, size, Ptcp);
if (result < 0) {
return (-1);
}
sp->result->bytes_sent += result;
sp->result->bytes_sent_this_interval += result;
return (result);
}
/* iperf_tcp_accept
*
* accept a new TCP stream connection
*/
int
iperf_tcp_accept(struct iperf_test * test)
{
int s;
int rbuf = ACCESS_DENIED;
char cookie[COOKIE_SIZE];
socklen_t len;
struct sockaddr_in addr;
len = sizeof(addr);
if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
i_errno = IESTREAMCONNECT;
return (-1);
}
if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) {
i_errno = IERECVCOOKIE;
return (-1);
}
if (strcmp(test->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;
if (test->no_delay || test->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->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);
}
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);
if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
close(s);
i_errno = IESTREAMLISTEN;
return (-1);
}
if (listen(s, 5) < 0) {
i_errno = IESTREAMLISTEN;
return (-1);
}
test->listener = s;
}
return (s);
}
/* 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->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->cookie, COOKIE_SIZE, Ptcp) < 0) {
i_errno = IESENDCOOKIE;
return (-1);
}
return (s);
}