1a951c980SBrian Tierney /* 27d375156SJon Dugan * Copyright (c) 2009-2011, The Regents of the University of California, 37d375156SJon Dugan * through Lawrence Berkeley National Laboratory (subject to receipt of any 47d375156SJon Dugan * required approvals from the U.S. Dept. of Energy). All rights reserved. 57d375156SJon Dugan * 67d375156SJon Dugan * This code is distributed under a BSD style license, see the LICENSE file 77d375156SJon Dugan * for complete information. 8a951c980SBrian Tierney */ 9a951c980SBrian Tierney 10a951c980SBrian Tierney #include <stdio.h> 11a951c980SBrian Tierney #include <stdlib.h> 12a951c980SBrian Tierney #include <string.h> 13a951c980SBrian Tierney #include <errno.h> 14a951c980SBrian Tierney #include <unistd.h> 15a951c980SBrian Tierney #include <assert.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" 259673370fSJef Poskanzer #include "iperf_util.h" 26a951c980SBrian Tierney #include "iperf_udp.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) { 47ec2d0670SJef Poskanzer 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++; 70*b5e0751fSJef Poskanzer iperf_err(sp->test, "OUT OF ORDER - incoming packet = %d and received packet = %d AND SP = %d", 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 85ec2d0670SJef Poskanzer return result; 86ec2d0670SJef Poskanzer } 87ec2d0670SJef Poskanzer 88ec2d0670SJef Poskanzer 89ec2d0670SJef Poskanzer static void 90ec2d0670SJef Poskanzer send_timer_proc(TimerClientData client_data, struct timeval* nowP) 91ec2d0670SJef Poskanzer { 92ec2d0670SJef Poskanzer struct iperf_stream *sp = client_data.p; 93ec2d0670SJef Poskanzer 94ec2d0670SJef Poskanzer /* All we do here is set a flag saying that this UDP stream may be sent 95ec2d0670SJef Poskanzer ** to. The actual sending gets done in iperf_udp_send(), which then 96ec2d0670SJef Poskanzer ** resets the flag and makes a new adjusted timer. 97ec2d0670SJef Poskanzer */ 98ec2d0670SJef Poskanzer sp->send_timer = NULL; 99ec2d0670SJef Poskanzer sp->udp_green_light = 1; 100a951c980SBrian Tierney } 101a951c980SBrian Tierney 102a951c980SBrian Tierney 1038a0b5a5dSsethdelliott /* iperf_udp_send 1048a0b5a5dSsethdelliott * 1058a0b5a5dSsethdelliott * sends the data for UDP 1068a0b5a5dSsethdelliott */ 107a951c980SBrian Tierney int 108a951c980SBrian Tierney iperf_udp_send(struct iperf_stream *sp) 109a951c980SBrian Tierney { 1108556db5dSsethdelliott ssize_t result = 0; 111a951c980SBrian Tierney int64_t dtargus; 112ec2d0670SJef Poskanzer int64_t adjustus; 1138556db5dSsethdelliott uint64_t sec, usec, pcount; 1148556db5dSsethdelliott int size = sp->settings->blksize; 1158556db5dSsethdelliott struct timeval before, after; 116ec2d0670SJef Poskanzer TimerClientData cd; 117a951c980SBrian Tierney 118ec2d0670SJef Poskanzer if (sp->udp_green_light) { 119b60a49ddSsethdelliott gettimeofday(&before, 0); 120b60a49ddSsethdelliott 1218556db5dSsethdelliott ++sp->packet_count; 1228556db5dSsethdelliott sec = htonl(before.tv_sec); 1238556db5dSsethdelliott usec = htonl(before.tv_usec); 1248556db5dSsethdelliott pcount = htonl(sp->packet_count); 125a951c980SBrian Tierney 1268556db5dSsethdelliott memcpy(sp->buffer, &sec, sizeof(sec)); 1278556db5dSsethdelliott memcpy(sp->buffer+4, &usec, sizeof(usec)); 1288556db5dSsethdelliott memcpy(sp->buffer+8, &pcount, sizeof(pcount)); 129a951c980SBrian Tierney 130a951c980SBrian Tierney result = Nwrite(sp->socket, sp->buffer, size, Pudp); 131a951c980SBrian Tierney 1328556db5dSsethdelliott if (result < 0) 133ec2d0670SJef Poskanzer return -1; 1348556db5dSsethdelliott 1358556db5dSsethdelliott sp->result->bytes_sent += result; 1368556db5dSsethdelliott sp->result->bytes_sent_this_interval += result; 137aa89cda2Ssethdelliott 138ec2d0670SJef Poskanzer if (sp->settings->rate != 0) { 139b60a49ddSsethdelliott gettimeofday(&after, 0); 140ec2d0670SJef Poskanzer dtargus = (int64_t) (sp->settings->blksize) * SEC_TO_US * 8; 141ec2d0670SJef Poskanzer dtargus /= sp->settings->rate; 142ec2d0670SJef Poskanzer assert(dtargus != 0); 143a951c980SBrian Tierney adjustus = dtargus; 144a951c980SBrian Tierney adjustus += (before.tv_sec - after.tv_sec) * SEC_TO_US; 145a951c980SBrian Tierney adjustus += (before.tv_usec - after.tv_usec); 1468556db5dSsethdelliott if (adjustus > 0) { 147a951c980SBrian Tierney dtargus = adjustus; 148a951c980SBrian Tierney } 149ec2d0670SJef Poskanzer cd.p = sp; 150ec2d0670SJef Poskanzer sp->udp_green_light = 0; 151ec2d0670SJef Poskanzer sp->send_timer = tmr_create((struct timeval*) 0, send_timer_proc, cd, dtargus, 0); 152ec2d0670SJef Poskanzer if (sp->send_timer == NULL) 153ec2d0670SJef Poskanzer return -1; 154ec2d0670SJef Poskanzer } 155aa89cda2Ssethdelliott } 156aa89cda2Ssethdelliott 157ec2d0670SJef Poskanzer return result; 158a951c980SBrian Tierney } 159a951c980SBrian Tierney 160a951c980SBrian Tierney 161a951c980SBrian Tierney /**************************************************************************/ 162a951c980SBrian Tierney 1638a0b5a5dSsethdelliott /* iperf_udp_accept 164a951c980SBrian Tierney * 1658a0b5a5dSsethdelliott * accepts a new UDP connection 166a951c980SBrian Tierney */ 167a951c980SBrian Tierney int 168a951c980SBrian Tierney iperf_udp_accept(struct iperf_test *test) 169a951c980SBrian Tierney { 170a951c980SBrian Tierney struct sockaddr_in sa_peer; 1718556db5dSsethdelliott int buf; 172a951c980SBrian Tierney socklen_t len; 1738556db5dSsethdelliott int sz, s; 174a951c980SBrian Tierney 1758a0b5a5dSsethdelliott s = test->prot_listener; 176a951c980SBrian Tierney 177a951c980SBrian Tierney len = sizeof sa_peer; 1788a0b5a5dSsethdelliott if ((sz = recvfrom(test->prot_listener, &buf, sizeof(buf), 0, (struct sockaddr *) &sa_peer, &len)) < 0) { 179b60a49ddSsethdelliott i_errno = IESTREAMACCEPT; 180ec2d0670SJef Poskanzer return -1; 1818556db5dSsethdelliott } 182a951c980SBrian Tierney 1838556db5dSsethdelliott if (connect(s, (struct sockaddr *) &sa_peer, len) < 0) { 184b60a49ddSsethdelliott i_errno = IESTREAMACCEPT; 185ec2d0670SJef Poskanzer return -1; 186a951c980SBrian Tierney } 187a951c980SBrian Tierney 188f99bd3b2Ssethdelliott test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->server_port); 1898a0b5a5dSsethdelliott if (test->prot_listener < 0) { 1908a0b5a5dSsethdelliott i_errno = IESTREAMLISTEN; 191ec2d0670SJef Poskanzer return -1; 192b60a49ddSsethdelliott } 193a951c980SBrian Tierney 1948a0b5a5dSsethdelliott FD_SET(test->prot_listener, &test->read_set); 1958a0b5a5dSsethdelliott test->max_fd = (test->max_fd < test->prot_listener) ? test->prot_listener : test->max_fd; 196a951c980SBrian Tierney 1978556db5dSsethdelliott /* Let the client know we're ready "accept" another UDP "stream" */ 1988a0b5a5dSsethdelliott if (write(s, &buf, sizeof(buf)) < 0) { 199b60a49ddSsethdelliott i_errno = IESTREAMWRITE; 200ec2d0670SJef Poskanzer return -1; 201a951c980SBrian Tierney } 202a951c980SBrian Tierney 203ec2d0670SJef Poskanzer return s; 2048a0b5a5dSsethdelliott } 2058a0b5a5dSsethdelliott 2068a0b5a5dSsethdelliott 2078a0b5a5dSsethdelliott /* iperf_udp_listen 2088a0b5a5dSsethdelliott * 2098a0b5a5dSsethdelliott * start up a listener for UDP stream connections 2108a0b5a5dSsethdelliott */ 2118a0b5a5dSsethdelliott int 2128a0b5a5dSsethdelliott iperf_udp_listen(struct iperf_test *test) 2138a0b5a5dSsethdelliott { 2148a0b5a5dSsethdelliott int s; 2158a0b5a5dSsethdelliott 216f99bd3b2Ssethdelliott if ((s = netannounce(test->settings->domain, Pudp, test->bind_address, test->server_port)) < 0) { 2178a0b5a5dSsethdelliott i_errno = IESTREAMLISTEN; 218ec2d0670SJef Poskanzer return -1; 2198a0b5a5dSsethdelliott } 2208a0b5a5dSsethdelliott 221ec2d0670SJef Poskanzer return s; 2228a0b5a5dSsethdelliott } 2238a0b5a5dSsethdelliott 2248a0b5a5dSsethdelliott 2258a0b5a5dSsethdelliott /* iperf_udp_connect 2268a0b5a5dSsethdelliott * 2278a0b5a5dSsethdelliott * connect to a TCP stream listener 2288a0b5a5dSsethdelliott */ 2298a0b5a5dSsethdelliott int 2308a0b5a5dSsethdelliott iperf_udp_connect(struct iperf_test *test) 2318a0b5a5dSsethdelliott { 2328a0b5a5dSsethdelliott int s, buf; 2338a0b5a5dSsethdelliott 234f99bd3b2Ssethdelliott if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->server_hostname, test->server_port)) < 0) { 2358a0b5a5dSsethdelliott i_errno = IESTREAMCONNECT; 236ec2d0670SJef Poskanzer return -1; 2378a0b5a5dSsethdelliott } 2388a0b5a5dSsethdelliott 2398a0b5a5dSsethdelliott /* Write to the UDP stream to give the server this stream's credentials */ 2408a0b5a5dSsethdelliott if (write(s, &buf, sizeof(buf)) < 0) { 2418a0b5a5dSsethdelliott // XXX: Should this be changed to IESTREAMCONNECT? 2428a0b5a5dSsethdelliott i_errno = IESTREAMWRITE; 243ec2d0670SJef Poskanzer return -1; 2448a0b5a5dSsethdelliott } 2458a0b5a5dSsethdelliott /* Wait until the server confirms the client UDP write */ 2468a0b5a5dSsethdelliott // XXX: Should this read be TCP instead? 2478a0b5a5dSsethdelliott if (read(s, &buf, sizeof(buf)) < 0) { 2488a0b5a5dSsethdelliott i_errno = IESTREAMREAD; 249ec2d0670SJef Poskanzer return -1; 2508a0b5a5dSsethdelliott } 2518a0b5a5dSsethdelliott 252ec2d0670SJef Poskanzer return s; 2538a0b5a5dSsethdelliott } 2548a0b5a5dSsethdelliott 2558a0b5a5dSsethdelliott 2568a0b5a5dSsethdelliott /* iperf_udp_init 2578a0b5a5dSsethdelliott * 2588a0b5a5dSsethdelliott * initializer for UDP streams in TEST_START 2598a0b5a5dSsethdelliott */ 2608a0b5a5dSsethdelliott int 2618a0b5a5dSsethdelliott iperf_udp_init(struct iperf_test *test) 2628a0b5a5dSsethdelliott { 2638a0b5a5dSsethdelliott int64_t dtargus; 2648a0b5a5dSsethdelliott struct iperf_stream *sp; 265ec2d0670SJef Poskanzer TimerClientData cd; 2668a0b5a5dSsethdelliott 267ec2d0670SJef Poskanzer if (test->settings->rate == 0) { 268ec2d0670SJef Poskanzer SLIST_FOREACH(sp, &test->streams, streams) { 269ec2d0670SJef Poskanzer sp->udp_green_light = 1; 270ec2d0670SJef Poskanzer } 271ec2d0670SJef Poskanzer } else { 2728a0b5a5dSsethdelliott /* Calculate the send delay needed to hit target bandwidth (-b) */ 273112a907cSsethdelliott dtargus = (int64_t) test->settings->blksize * SEC_TO_US * 8; 274112a907cSsethdelliott dtargus /= test->settings->rate; 2758a0b5a5dSsethdelliott 2768a0b5a5dSsethdelliott assert(dtargus != 0); 2778a0b5a5dSsethdelliott 278dd4bc008Ssethdelliott SLIST_FOREACH(sp, &test->streams, streams) { 279ec2d0670SJef Poskanzer cd.p = sp; 280ec2d0670SJef Poskanzer sp->udp_green_light = 0; 281ec2d0670SJef Poskanzer sp->send_timer = tmr_create((struct timeval*) 0, send_timer_proc, cd, dtargus, 0); 2828a0b5a5dSsethdelliott if (sp->send_timer == NULL) 283ec2d0670SJef Poskanzer return -1; 284ec2d0670SJef Poskanzer } 2858a0b5a5dSsethdelliott } 286a951c980SBrian Tierney 287ec2d0670SJef Poskanzer return 0; 288a951c980SBrian Tierney } 289