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> 15*dd4bc008Ssethdelliott #include <sys/queue.h> 16a951c980SBrian Tierney #include <sys/socket.h> 17a951c980SBrian Tierney #include <sys/types.h> 18a951c980SBrian Tierney #include <netinet/in.h> 19a951c980SBrian Tierney #include <stdint.h> 20a951c980SBrian Tierney #include <sys/time.h> 2166ce7ad4Ssethdelliott #include <sys/select.h> 22a951c980SBrian Tierney 23a951c980SBrian Tierney #include "iperf.h" 24a951c980SBrian Tierney #include "iperf_api.h" 25a951c980SBrian Tierney #include "iperf_udp.h" 26b60a49ddSsethdelliott #include "iperf_error.h" 27a951c980SBrian Tierney #include "timer.h" 28a951c980SBrian Tierney #include "net.h" 29a951c980SBrian Tierney 30a951c980SBrian Tierney 318a0b5a5dSsethdelliott /* iperf_udp_recv 32a951c980SBrian Tierney * 338a0b5a5dSsethdelliott * receives the data for UDP 34a951c980SBrian Tierney */ 35a951c980SBrian Tierney int 36a951c980SBrian Tierney iperf_udp_recv(struct iperf_stream *sp) 37a951c980SBrian Tierney { 388556db5dSsethdelliott int result; 39a951c980SBrian Tierney int size = sp->settings->blksize; 408556db5dSsethdelliott int sec, usec, pcount; 41a951c980SBrian Tierney double transit = 0, d = 0; 428556db5dSsethdelliott struct timeval sent_time, arrival_time; 43a951c980SBrian Tierney 44a951c980SBrian Tierney result = Nread(sp->socket, sp->buffer, size, Pudp); 45a951c980SBrian Tierney 468556db5dSsethdelliott if (result < 0) { 478556db5dSsethdelliott return (-1); 48a951c980SBrian Tierney } 498a0b5a5dSsethdelliott 50a951c980SBrian Tierney sp->result->bytes_received += result; 518556db5dSsethdelliott sp->result->bytes_received_this_interval += result; 528556db5dSsethdelliott 538556db5dSsethdelliott memcpy(&sec, sp->buffer, sizeof(sec)); 548556db5dSsethdelliott memcpy(&usec, sp->buffer+4, sizeof(usec)); 558556db5dSsethdelliott memcpy(&pcount, sp->buffer+8, sizeof(pcount)); 568556db5dSsethdelliott sec = ntohl(sec); 578556db5dSsethdelliott usec = ntohl(usec); 588556db5dSsethdelliott pcount = ntohl(pcount); 598556db5dSsethdelliott sent_time.tv_sec = sec; 608556db5dSsethdelliott sent_time.tv_usec = usec; 618556db5dSsethdelliott 628556db5dSsethdelliott /* Out of order packets */ 638556db5dSsethdelliott if (pcount >= sp->packet_count + 1) { 648556db5dSsethdelliott if (pcount > sp->packet_count + 1) { 658556db5dSsethdelliott sp->cnt_error += (pcount - 1) - sp->packet_count; 668556db5dSsethdelliott } 678556db5dSsethdelliott sp->packet_count = pcount; 688556db5dSsethdelliott } else { 698556db5dSsethdelliott sp->outoforder_packets++; 708556db5dSsethdelliott printf("OUT OF ORDER - incoming packet = %d and received packet = %d AND SP = %d\n", pcount, sp->packet_count, sp->socket); 718556db5dSsethdelliott } 72a951c980SBrian Tierney 73a951c980SBrian Tierney /* jitter measurement */ 74b60a49ddSsethdelliott gettimeofday(&arrival_time, NULL); 75b60a49ddSsethdelliott 768556db5dSsethdelliott transit = timeval_diff(&sent_time, &arrival_time); 77a951c980SBrian Tierney d = transit - sp->prev_transit; 78a951c980SBrian Tierney if (d < 0) 79a951c980SBrian Tierney d = -d; 80a951c980SBrian Tierney sp->prev_transit = transit; 818556db5dSsethdelliott // XXX: This is NOT the way to calculate jitter 828556db5dSsethdelliott // J = |(R1 - S1) - (R0 - S0)| [/ number of packets, for average] 83a951c980SBrian Tierney sp->jitter += (d - sp->jitter) / 16.0; 84a951c980SBrian Tierney 858a0b5a5dSsethdelliott return (result); 86a951c980SBrian Tierney } 87a951c980SBrian Tierney 88a951c980SBrian Tierney 898a0b5a5dSsethdelliott /* iperf_udp_send 908a0b5a5dSsethdelliott * 918a0b5a5dSsethdelliott * sends the data for UDP 928a0b5a5dSsethdelliott */ 93a951c980SBrian Tierney int 94a951c980SBrian Tierney iperf_udp_send(struct iperf_stream *sp) 95a951c980SBrian Tierney { 968556db5dSsethdelliott ssize_t result = 0; 97a951c980SBrian Tierney int64_t dtargus; 98a951c980SBrian Tierney int64_t adjustus = 0; 998556db5dSsethdelliott uint64_t sec, usec, pcount; 1008556db5dSsethdelliott int size = sp->settings->blksize; 1018556db5dSsethdelliott struct timeval before, after; 102a951c980SBrian Tierney 103aa89cda2Ssethdelliott if (timer_expired(sp->send_timer)) { 104a951c980SBrian Tierney 105a951c980SBrian Tierney dtargus = (int64_t) (sp->settings->blksize) * SEC_TO_US * 8; 106a951c980SBrian Tierney dtargus /= sp->settings->rate; 107a951c980SBrian Tierney 108a951c980SBrian Tierney assert(dtargus != 0); 109aa89cda2Ssethdelliott 110b60a49ddSsethdelliott gettimeofday(&before, 0); 111b60a49ddSsethdelliott 1128556db5dSsethdelliott ++sp->packet_count; 1138556db5dSsethdelliott sec = htonl(before.tv_sec); 1148556db5dSsethdelliott usec = htonl(before.tv_usec); 1158556db5dSsethdelliott pcount = htonl(sp->packet_count); 116a951c980SBrian Tierney 1178556db5dSsethdelliott memcpy(sp->buffer, &sec, sizeof(sec)); 1188556db5dSsethdelliott memcpy(sp->buffer+4, &usec, sizeof(usec)); 1198556db5dSsethdelliott memcpy(sp->buffer+8, &pcount, sizeof(pcount)); 120a951c980SBrian Tierney 121a951c980SBrian Tierney result = Nwrite(sp->socket, sp->buffer, size, Pudp); 122a951c980SBrian Tierney 1238556db5dSsethdelliott if (result < 0) 124b60a49ddSsethdelliott return (-1); 1258556db5dSsethdelliott 1268556db5dSsethdelliott sp->result->bytes_sent += result; 1278556db5dSsethdelliott sp->result->bytes_sent_this_interval += result; 128aa89cda2Ssethdelliott 129b60a49ddSsethdelliott gettimeofday(&after, 0); 130a951c980SBrian Tierney 131a951c980SBrian Tierney adjustus = dtargus; 132a951c980SBrian Tierney adjustus += (before.tv_sec - after.tv_sec) * SEC_TO_US; 133a951c980SBrian Tierney adjustus += (before.tv_usec - after.tv_usec); 134a951c980SBrian Tierney 1358556db5dSsethdelliott if (adjustus > 0) { 136a951c980SBrian Tierney dtargus = adjustus; 137a951c980SBrian Tierney } 138aa89cda2Ssethdelliott 139b60a49ddSsethdelliott if (update_timer(sp->send_timer, 0, dtargus) < 0) 140b60a49ddSsethdelliott return (-1); 141aa89cda2Ssethdelliott } 142aa89cda2Ssethdelliott 1438a0b5a5dSsethdelliott return (result); 144a951c980SBrian Tierney } 145a951c980SBrian Tierney 146a951c980SBrian Tierney 147a951c980SBrian Tierney /**************************************************************************/ 148a951c980SBrian Tierney 1498a0b5a5dSsethdelliott /* iperf_udp_accept 150a951c980SBrian Tierney * 1518a0b5a5dSsethdelliott * accepts a new UDP connection 152a951c980SBrian Tierney */ 153a951c980SBrian Tierney int 154a951c980SBrian Tierney iperf_udp_accept(struct iperf_test *test) 155a951c980SBrian Tierney { 156a951c980SBrian Tierney struct sockaddr_in sa_peer; 1578556db5dSsethdelliott int buf; 158a951c980SBrian Tierney socklen_t len; 1598556db5dSsethdelliott int sz, s; 160a951c980SBrian Tierney 1618a0b5a5dSsethdelliott s = test->prot_listener; 162a951c980SBrian Tierney 163a951c980SBrian Tierney len = sizeof sa_peer; 1648a0b5a5dSsethdelliott if ((sz = recvfrom(test->prot_listener, &buf, sizeof(buf), 0, (struct sockaddr *) &sa_peer, &len)) < 0) { 165b60a49ddSsethdelliott i_errno = IESTREAMACCEPT; 1668556db5dSsethdelliott return (-1); 1678556db5dSsethdelliott } 168a951c980SBrian Tierney 1698556db5dSsethdelliott if (connect(s, (struct sockaddr *) &sa_peer, len) < 0) { 170b60a49ddSsethdelliott i_errno = IESTREAMACCEPT; 171b60a49ddSsethdelliott return (-1); 172a951c980SBrian Tierney } 173a951c980SBrian Tierney 174f99bd3b2Ssethdelliott test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->server_port); 1758a0b5a5dSsethdelliott if (test->prot_listener < 0) { 1768a0b5a5dSsethdelliott i_errno = IESTREAMLISTEN; 177b60a49ddSsethdelliott return (-1); 178b60a49ddSsethdelliott } 179a951c980SBrian Tierney 1808a0b5a5dSsethdelliott FD_SET(test->prot_listener, &test->read_set); 1818a0b5a5dSsethdelliott test->max_fd = (test->max_fd < test->prot_listener) ? test->prot_listener : test->max_fd; 182a951c980SBrian Tierney 1838556db5dSsethdelliott /* Let the client know we're ready "accept" another UDP "stream" */ 1848a0b5a5dSsethdelliott if (write(s, &buf, sizeof(buf)) < 0) { 185b60a49ddSsethdelliott i_errno = IESTREAMWRITE; 186b60a49ddSsethdelliott return (-1); 187a951c980SBrian Tierney } 188a951c980SBrian Tierney 1898a0b5a5dSsethdelliott return (s); 1908a0b5a5dSsethdelliott } 1918a0b5a5dSsethdelliott 1928a0b5a5dSsethdelliott 1938a0b5a5dSsethdelliott /* iperf_udp_listen 1948a0b5a5dSsethdelliott * 1958a0b5a5dSsethdelliott * start up a listener for UDP stream connections 1968a0b5a5dSsethdelliott */ 1978a0b5a5dSsethdelliott int 1988a0b5a5dSsethdelliott iperf_udp_listen(struct iperf_test *test) 1998a0b5a5dSsethdelliott { 2008a0b5a5dSsethdelliott int s; 2018a0b5a5dSsethdelliott 202f99bd3b2Ssethdelliott if ((s = netannounce(test->settings->domain, Pudp, test->bind_address, test->server_port)) < 0) { 2038a0b5a5dSsethdelliott i_errno = IESTREAMLISTEN; 2048a0b5a5dSsethdelliott return (-1); 2058a0b5a5dSsethdelliott } 2068a0b5a5dSsethdelliott 2078a0b5a5dSsethdelliott return (s); 2088a0b5a5dSsethdelliott } 2098a0b5a5dSsethdelliott 2108a0b5a5dSsethdelliott 2118a0b5a5dSsethdelliott /* iperf_udp_connect 2128a0b5a5dSsethdelliott * 2138a0b5a5dSsethdelliott * connect to a TCP stream listener 2148a0b5a5dSsethdelliott */ 2158a0b5a5dSsethdelliott int 2168a0b5a5dSsethdelliott iperf_udp_connect(struct iperf_test *test) 2178a0b5a5dSsethdelliott { 2188a0b5a5dSsethdelliott int s, buf; 2198a0b5a5dSsethdelliott 220f99bd3b2Ssethdelliott if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->server_hostname, test->server_port)) < 0) { 2218a0b5a5dSsethdelliott i_errno = IESTREAMCONNECT; 2228a0b5a5dSsethdelliott return (-1); 2238a0b5a5dSsethdelliott } 2248a0b5a5dSsethdelliott 2258a0b5a5dSsethdelliott /* Write to the UDP stream to give the server this stream's credentials */ 2268a0b5a5dSsethdelliott if (write(s, &buf, sizeof(buf)) < 0) { 2278a0b5a5dSsethdelliott // XXX: Should this be changed to IESTREAMCONNECT? 2288a0b5a5dSsethdelliott i_errno = IESTREAMWRITE; 2298a0b5a5dSsethdelliott return (-1); 2308a0b5a5dSsethdelliott } 2318a0b5a5dSsethdelliott /* Wait until the server confirms the client UDP write */ 2328a0b5a5dSsethdelliott // XXX: Should this read be TCP instead? 2338a0b5a5dSsethdelliott if (read(s, &buf, sizeof(buf)) < 0) { 2348a0b5a5dSsethdelliott i_errno = IESTREAMREAD; 2358a0b5a5dSsethdelliott return (-1); 2368a0b5a5dSsethdelliott } 2378a0b5a5dSsethdelliott 2388a0b5a5dSsethdelliott return (s); 2398a0b5a5dSsethdelliott } 2408a0b5a5dSsethdelliott 2418a0b5a5dSsethdelliott 2428a0b5a5dSsethdelliott /* iperf_udp_init 2438a0b5a5dSsethdelliott * 2448a0b5a5dSsethdelliott * initializer for UDP streams in TEST_START 2458a0b5a5dSsethdelliott */ 2468a0b5a5dSsethdelliott int 2478a0b5a5dSsethdelliott iperf_udp_init(struct iperf_test *test) 2488a0b5a5dSsethdelliott { 2498a0b5a5dSsethdelliott int64_t dtargus; 2508a0b5a5dSsethdelliott struct iperf_stream *sp; 2518a0b5a5dSsethdelliott 2528a0b5a5dSsethdelliott /* Calculate the send delay needed to hit target bandwidth (-b) */ 253112a907cSsethdelliott dtargus = (int64_t) test->settings->blksize * SEC_TO_US * 8; 254112a907cSsethdelliott dtargus /= test->settings->rate; 2558a0b5a5dSsethdelliott 2568a0b5a5dSsethdelliott assert(dtargus != 0); 2578a0b5a5dSsethdelliott 258*dd4bc008Ssethdelliott // for (sp = test->streams; sp; sp = sp->next) { 259*dd4bc008Ssethdelliott SLIST_FOREACH(sp, &test->streams, streams) { 2608a0b5a5dSsethdelliott sp->send_timer = new_timer(dtargus / SEC_TO_US, dtargus % SEC_TO_US); 2618a0b5a5dSsethdelliott if (sp->send_timer == NULL) 2628a0b5a5dSsethdelliott return (-1); 2638a0b5a5dSsethdelliott } 264a951c980SBrian Tierney 265b60a49ddSsethdelliott return (0); 266a951c980SBrian Tierney } 267