1a951c980SBrian Tierney 2a951c980SBrian Tierney /* 3a951c980SBrian Tierney * Copyright (c) 2009, The Regents of the University of California, through 4a951c980SBrian Tierney * Lawrence Berkeley National Laboratory (subject to receipt of any required 5a951c980SBrian Tierney * approvals from the U.S. Dept. of Energy). All rights reserved. 6a951c980SBrian Tierney */ 7a951c980SBrian Tierney 8a951c980SBrian Tierney 9a951c980SBrian Tierney #include <stdio.h> 10a951c980SBrian Tierney #include <stdlib.h> 11a951c980SBrian Tierney #include <string.h> 12a951c980SBrian Tierney #include <errno.h> 13a951c980SBrian Tierney #include <unistd.h> 14a951c980SBrian Tierney #include <assert.h> 15a951c980SBrian Tierney #include <sys/socket.h> 16a951c980SBrian Tierney #include <sys/types.h> 17a951c980SBrian Tierney #include <netinet/in.h> 18a951c980SBrian Tierney #include <stdint.h> 19a951c980SBrian Tierney #include <sys/time.h> 2066ce7ad4Ssethdelliott #include <sys/select.h> 21a951c980SBrian Tierney 22a951c980SBrian Tierney #include "iperf.h" 23a951c980SBrian Tierney #include "iperf_api.h" 24a951c980SBrian Tierney #include "iperf_udp.h" 25b60a49ddSsethdelliott #include "iperf_error.h" 26a951c980SBrian Tierney #include "timer.h" 27a951c980SBrian Tierney #include "net.h" 28a951c980SBrian Tierney 29a951c980SBrian Tierney 308a0b5a5dSsethdelliott /* iperf_udp_recv 31a951c980SBrian Tierney * 328a0b5a5dSsethdelliott * receives the data for UDP 33a951c980SBrian Tierney */ 34a951c980SBrian Tierney int 35a951c980SBrian Tierney iperf_udp_recv(struct iperf_stream *sp) 36a951c980SBrian Tierney { 378556db5dSsethdelliott int result; 38a951c980SBrian Tierney int size = sp->settings->blksize; 398556db5dSsethdelliott int sec, usec, pcount; 40a951c980SBrian Tierney double transit = 0, d = 0; 418556db5dSsethdelliott struct timeval sent_time, arrival_time; 42a951c980SBrian Tierney 43a951c980SBrian Tierney result = Nread(sp->socket, sp->buffer, size, Pudp); 44a951c980SBrian Tierney 458556db5dSsethdelliott if (result < 0) { 468556db5dSsethdelliott return (-1); 47a951c980SBrian Tierney } 488a0b5a5dSsethdelliott 49a951c980SBrian Tierney sp->result->bytes_received += result; 508556db5dSsethdelliott sp->result->bytes_received_this_interval += result; 518556db5dSsethdelliott 528556db5dSsethdelliott memcpy(&sec, sp->buffer, sizeof(sec)); 538556db5dSsethdelliott memcpy(&usec, sp->buffer+4, sizeof(usec)); 548556db5dSsethdelliott memcpy(&pcount, sp->buffer+8, sizeof(pcount)); 558556db5dSsethdelliott sec = ntohl(sec); 568556db5dSsethdelliott usec = ntohl(usec); 578556db5dSsethdelliott pcount = ntohl(pcount); 588556db5dSsethdelliott sent_time.tv_sec = sec; 598556db5dSsethdelliott sent_time.tv_usec = usec; 608556db5dSsethdelliott 618556db5dSsethdelliott /* Out of order packets */ 628556db5dSsethdelliott if (pcount >= sp->packet_count + 1) { 638556db5dSsethdelliott if (pcount > sp->packet_count + 1) { 648556db5dSsethdelliott sp->cnt_error += (pcount - 1) - sp->packet_count; 658556db5dSsethdelliott } 668556db5dSsethdelliott sp->packet_count = pcount; 678556db5dSsethdelliott } else { 688556db5dSsethdelliott sp->outoforder_packets++; 698556db5dSsethdelliott printf("OUT OF ORDER - incoming packet = %d and received packet = %d AND SP = %d\n", pcount, sp->packet_count, sp->socket); 708556db5dSsethdelliott } 71a951c980SBrian Tierney 72a951c980SBrian Tierney /* jitter measurement */ 73b60a49ddSsethdelliott gettimeofday(&arrival_time, NULL); 74b60a49ddSsethdelliott 758556db5dSsethdelliott transit = timeval_diff(&sent_time, &arrival_time); 76a951c980SBrian Tierney d = transit - sp->prev_transit; 77a951c980SBrian Tierney if (d < 0) 78a951c980SBrian Tierney d = -d; 79a951c980SBrian Tierney sp->prev_transit = transit; 808556db5dSsethdelliott // XXX: This is NOT the way to calculate jitter 818556db5dSsethdelliott // J = |(R1 - S1) - (R0 - S0)| [/ number of packets, for average] 82a951c980SBrian Tierney sp->jitter += (d - sp->jitter) / 16.0; 83a951c980SBrian Tierney 848a0b5a5dSsethdelliott return (result); 85a951c980SBrian Tierney } 86a951c980SBrian Tierney 87a951c980SBrian Tierney 888a0b5a5dSsethdelliott /* iperf_udp_send 898a0b5a5dSsethdelliott * 908a0b5a5dSsethdelliott * sends the data for UDP 918a0b5a5dSsethdelliott */ 92a951c980SBrian Tierney int 93a951c980SBrian Tierney iperf_udp_send(struct iperf_stream *sp) 94a951c980SBrian Tierney { 958556db5dSsethdelliott ssize_t result = 0; 96a951c980SBrian Tierney int64_t dtargus; 97a951c980SBrian Tierney int64_t adjustus = 0; 988556db5dSsethdelliott uint64_t sec, usec, pcount; 998556db5dSsethdelliott int size = sp->settings->blksize; 1008556db5dSsethdelliott struct timeval before, after; 101a951c980SBrian Tierney 102aa89cda2Ssethdelliott if (timer_expired(sp->send_timer)) { 103a951c980SBrian Tierney 104a951c980SBrian Tierney dtargus = (int64_t) (sp->settings->blksize) * SEC_TO_US * 8; 105a951c980SBrian Tierney dtargus /= sp->settings->rate; 106a951c980SBrian Tierney 107a951c980SBrian Tierney assert(dtargus != 0); 108aa89cda2Ssethdelliott 109b60a49ddSsethdelliott gettimeofday(&before, 0); 110b60a49ddSsethdelliott 1118556db5dSsethdelliott ++sp->packet_count; 1128556db5dSsethdelliott sec = htonl(before.tv_sec); 1138556db5dSsethdelliott usec = htonl(before.tv_usec); 1148556db5dSsethdelliott pcount = htonl(sp->packet_count); 115a951c980SBrian Tierney 1168556db5dSsethdelliott memcpy(sp->buffer, &sec, sizeof(sec)); 1178556db5dSsethdelliott memcpy(sp->buffer+4, &usec, sizeof(usec)); 1188556db5dSsethdelliott memcpy(sp->buffer+8, &pcount, sizeof(pcount)); 119a951c980SBrian Tierney 120a951c980SBrian Tierney result = Nwrite(sp->socket, sp->buffer, size, Pudp); 121a951c980SBrian Tierney 1228556db5dSsethdelliott if (result < 0) 123b60a49ddSsethdelliott return (-1); 1248556db5dSsethdelliott 1258556db5dSsethdelliott sp->result->bytes_sent += result; 1268556db5dSsethdelliott sp->result->bytes_sent_this_interval += result; 127aa89cda2Ssethdelliott 128b60a49ddSsethdelliott gettimeofday(&after, 0); 129a951c980SBrian Tierney 130a951c980SBrian Tierney adjustus = dtargus; 131a951c980SBrian Tierney adjustus += (before.tv_sec - after.tv_sec) * SEC_TO_US; 132a951c980SBrian Tierney adjustus += (before.tv_usec - after.tv_usec); 133a951c980SBrian Tierney 1348556db5dSsethdelliott if (adjustus > 0) { 135a951c980SBrian Tierney dtargus = adjustus; 136a951c980SBrian Tierney } 137aa89cda2Ssethdelliott 138b60a49ddSsethdelliott if (update_timer(sp->send_timer, 0, dtargus) < 0) 139b60a49ddSsethdelliott return (-1); 140aa89cda2Ssethdelliott } 141aa89cda2Ssethdelliott 1428a0b5a5dSsethdelliott return (result); 143a951c980SBrian Tierney } 144a951c980SBrian Tierney 145a951c980SBrian Tierney 146a951c980SBrian Tierney /**************************************************************************/ 147a951c980SBrian Tierney 1488a0b5a5dSsethdelliott /* iperf_udp_accept 149a951c980SBrian Tierney * 1508a0b5a5dSsethdelliott * accepts a new UDP connection 151a951c980SBrian Tierney */ 152a951c980SBrian Tierney int 153a951c980SBrian Tierney iperf_udp_accept(struct iperf_test *test) 154a951c980SBrian Tierney { 155a951c980SBrian Tierney struct sockaddr_in sa_peer; 1568556db5dSsethdelliott int buf; 157a951c980SBrian Tierney socklen_t len; 1588556db5dSsethdelliott int sz, s; 159a951c980SBrian Tierney 1608a0b5a5dSsethdelliott s = test->prot_listener; 161a951c980SBrian Tierney 162a951c980SBrian Tierney len = sizeof sa_peer; 1638a0b5a5dSsethdelliott if ((sz = recvfrom(test->prot_listener, &buf, sizeof(buf), 0, (struct sockaddr *) &sa_peer, &len)) < 0) { 164b60a49ddSsethdelliott i_errno = IESTREAMACCEPT; 1658556db5dSsethdelliott return (-1); 1668556db5dSsethdelliott } 167a951c980SBrian Tierney 1688556db5dSsethdelliott if (connect(s, (struct sockaddr *) &sa_peer, len) < 0) { 169b60a49ddSsethdelliott i_errno = IESTREAMACCEPT; 170b60a49ddSsethdelliott return (-1); 171a951c980SBrian Tierney } 172a951c980SBrian Tierney 173*f99bd3b2Ssethdelliott test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->server_port); 1748a0b5a5dSsethdelliott if (test->prot_listener < 0) { 1758a0b5a5dSsethdelliott i_errno = IESTREAMLISTEN; 176b60a49ddSsethdelliott return (-1); 177b60a49ddSsethdelliott } 178a951c980SBrian Tierney 1798a0b5a5dSsethdelliott FD_SET(test->prot_listener, &test->read_set); 1808a0b5a5dSsethdelliott test->max_fd = (test->max_fd < test->prot_listener) ? test->prot_listener : test->max_fd; 181a951c980SBrian Tierney 1828556db5dSsethdelliott /* Let the client know we're ready "accept" another UDP "stream" */ 1838a0b5a5dSsethdelliott if (write(s, &buf, sizeof(buf)) < 0) { 184b60a49ddSsethdelliott i_errno = IESTREAMWRITE; 185b60a49ddSsethdelliott return (-1); 186a951c980SBrian Tierney } 187a951c980SBrian Tierney 1888a0b5a5dSsethdelliott return (s); 1898a0b5a5dSsethdelliott } 1908a0b5a5dSsethdelliott 1918a0b5a5dSsethdelliott 1928a0b5a5dSsethdelliott /* iperf_udp_listen 1938a0b5a5dSsethdelliott * 1948a0b5a5dSsethdelliott * start up a listener for UDP stream connections 1958a0b5a5dSsethdelliott */ 1968a0b5a5dSsethdelliott int 1978a0b5a5dSsethdelliott iperf_udp_listen(struct iperf_test *test) 1988a0b5a5dSsethdelliott { 1998a0b5a5dSsethdelliott int s; 2008a0b5a5dSsethdelliott 201*f99bd3b2Ssethdelliott if ((s = netannounce(test->settings->domain, Pudp, test->bind_address, test->server_port)) < 0) { 2028a0b5a5dSsethdelliott i_errno = IESTREAMLISTEN; 2038a0b5a5dSsethdelliott return (-1); 2048a0b5a5dSsethdelliott } 2058a0b5a5dSsethdelliott 2068a0b5a5dSsethdelliott return (s); 2078a0b5a5dSsethdelliott } 2088a0b5a5dSsethdelliott 2098a0b5a5dSsethdelliott 2108a0b5a5dSsethdelliott /* iperf_udp_connect 2118a0b5a5dSsethdelliott * 2128a0b5a5dSsethdelliott * connect to a TCP stream listener 2138a0b5a5dSsethdelliott */ 2148a0b5a5dSsethdelliott int 2158a0b5a5dSsethdelliott iperf_udp_connect(struct iperf_test *test) 2168a0b5a5dSsethdelliott { 2178a0b5a5dSsethdelliott int s, buf; 2188a0b5a5dSsethdelliott 219*f99bd3b2Ssethdelliott if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->server_hostname, test->server_port)) < 0) { 2208a0b5a5dSsethdelliott i_errno = IESTREAMCONNECT; 2218a0b5a5dSsethdelliott return (-1); 2228a0b5a5dSsethdelliott } 2238a0b5a5dSsethdelliott 2248a0b5a5dSsethdelliott /* Write to the UDP stream to give the server this stream's credentials */ 2258a0b5a5dSsethdelliott if (write(s, &buf, sizeof(buf)) < 0) { 2268a0b5a5dSsethdelliott // XXX: Should this be changed to IESTREAMCONNECT? 2278a0b5a5dSsethdelliott i_errno = IESTREAMWRITE; 2288a0b5a5dSsethdelliott return (-1); 2298a0b5a5dSsethdelliott } 2308a0b5a5dSsethdelliott /* Wait until the server confirms the client UDP write */ 2318a0b5a5dSsethdelliott // XXX: Should this read be TCP instead? 2328a0b5a5dSsethdelliott if (read(s, &buf, sizeof(buf)) < 0) { 2338a0b5a5dSsethdelliott i_errno = IESTREAMREAD; 2348a0b5a5dSsethdelliott return (-1); 2358a0b5a5dSsethdelliott } 2368a0b5a5dSsethdelliott 2378a0b5a5dSsethdelliott return (s); 2388a0b5a5dSsethdelliott } 2398a0b5a5dSsethdelliott 2408a0b5a5dSsethdelliott 2418a0b5a5dSsethdelliott /* iperf_udp_init 2428a0b5a5dSsethdelliott * 2438a0b5a5dSsethdelliott * initializer for UDP streams in TEST_START 2448a0b5a5dSsethdelliott */ 2458a0b5a5dSsethdelliott int 2468a0b5a5dSsethdelliott iperf_udp_init(struct iperf_test *test) 2478a0b5a5dSsethdelliott { 2488a0b5a5dSsethdelliott int64_t dtargus; 2498a0b5a5dSsethdelliott struct iperf_stream *sp; 2508a0b5a5dSsethdelliott 2518a0b5a5dSsethdelliott /* Calculate the send delay needed to hit target bandwidth (-b) */ 252112a907cSsethdelliott dtargus = (int64_t) test->settings->blksize * SEC_TO_US * 8; 253112a907cSsethdelliott dtargus /= test->settings->rate; 2548a0b5a5dSsethdelliott 2558a0b5a5dSsethdelliott assert(dtargus != 0); 2568a0b5a5dSsethdelliott 2578a0b5a5dSsethdelliott for (sp = test->streams; sp; sp = sp->next) { 2588a0b5a5dSsethdelliott sp->send_timer = new_timer(dtargus / SEC_TO_US, dtargus % SEC_TO_US); 2598a0b5a5dSsethdelliott if (sp->send_timer == NULL) 2608a0b5a5dSsethdelliott return (-1); 2618a0b5a5dSsethdelliott } 262a951c980SBrian Tierney 263b60a49ddSsethdelliott return (0); 264a951c980SBrian Tierney } 265