| |
| /* |
| * 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); |
| } |
| |