1135042Srwatson/*- 2135042Srwatson * Copyright (c) 2004 Robert N. M. Watson 3135042Srwatson * All rights reserved. 4135042Srwatson * 5135042Srwatson * Redistribution and use in source and binary forms, with or without 6135042Srwatson * modification, are permitted provided that the following conditions 7135042Srwatson * are met: 8135042Srwatson * 1. Redistributions of source code must retain the above copyright 9135042Srwatson * notice, this list of conditions and the following disclaimer. 10135042Srwatson * 2. Redistributions in binary form must reproduce the above copyright 11135042Srwatson * notice, this list of conditions and the following disclaimer in the 12135042Srwatson * documentation and/or other materials provided with the distribution. 13135042Srwatson * 14135042Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15135042Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16135042Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17135042Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18135042Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19135042Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20135042Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21135042Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22135042Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23135042Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24135042Srwatson * SUCH DAMAGE. 25135042Srwatson * 26135042Srwatson * $FreeBSD$ 27135042Srwatson */ 28135042Srwatson 29225334Smarius#include <sys/endian.h> 30135042Srwatson#include <sys/types.h> 31135042Srwatson#include <sys/socket.h> 32135042Srwatson#include <sys/time.h> 33135042Srwatson 34135042Srwatson#include <netinet/in.h> 35135042Srwatson 36135042Srwatson#include <arpa/inet.h> 37135042Srwatson 38135042Srwatson#include <stdio.h> 39145367Smux#include <stdint.h> 40135042Srwatson#include <stdlib.h> 41135042Srwatson#include <string.h> 42135042Srwatson 43198136Sluigi/* program arguments */ 44198136Sluigistruct _a { 45198136Sluigi int s; 46198136Sluigi struct timespec interval; 47198136Sluigi int port, port_max; 48198136Sluigi long duration; 49198136Sluigi struct sockaddr_in sin; 50198136Sluigi int packet_len; 51198136Sluigi void *packet; 52198136Sluigi}; 53198136Sluigi 54135042Srwatsonstatic void 55135042Srwatsonusage(void) 56135042Srwatson{ 57135042Srwatson 58135042Srwatson fprintf(stderr, 59198136Sluigi "netsend [ip] [port[-port_max]] [payloadsize] [packet_rate] [duration]\n"); 60135042Srwatson exit(-1); 61135042Srwatson} 62135042Srwatson 63135485Srwatson#define MAX_RATE 100000000 64135485Srwatson 65135042Srwatsonstatic __inline void 66135042Srwatsontimespec_add(struct timespec *tsa, struct timespec *tsb) 67135042Srwatson{ 68135042Srwatson 69135042Srwatson tsa->tv_sec += tsb->tv_sec; 70135042Srwatson tsa->tv_nsec += tsb->tv_nsec; 71135042Srwatson if (tsa->tv_nsec >= 1000000000) { 72135042Srwatson tsa->tv_sec++; 73135042Srwatson tsa->tv_nsec -= 1000000000; 74135042Srwatson } 75135042Srwatson} 76135042Srwatson 77135551Srwatsonstatic __inline int 78135551Srwatsontimespec_ge(struct timespec *a, struct timespec *b) 79135551Srwatson{ 80135551Srwatson 81135551Srwatson if (a->tv_sec > b->tv_sec) 82135551Srwatson return (1); 83135551Srwatson if (a->tv_sec < b->tv_sec) 84135551Srwatson return (0); 85135551Srwatson if (a->tv_nsec >= b->tv_nsec) 86135551Srwatson return (1); 87135551Srwatson return (0); 88135551Srwatson} 89135551Srwatson 90135042Srwatson/* 91135042Srwatson * Busy wait spinning until we reach (or slightly pass) the desired time. 92135042Srwatson * Optionally return the current time as retrieved on the last time check 93135551Srwatson * to the caller. Optionally also increment a counter provided by the 94135551Srwatson * caller each time we loop. 95135042Srwatson */ 96139121Skeramidastatic int 97135551Srwatsonwait_time(struct timespec ts, struct timespec *wakeup_ts, long long *waited) 98135042Srwatson{ 99135042Srwatson struct timespec curtime; 100135042Srwatson 101135042Srwatson curtime.tv_sec = 0; 102135042Srwatson curtime.tv_nsec = 0; 103135042Srwatson 104135551Srwatson if (clock_gettime(CLOCK_REALTIME, &curtime) == -1) { 105135551Srwatson perror("clock_gettime"); 106135551Srwatson return (-1); 107135551Srwatson } 108135551Srwatson#if 0 109135551Srwatson if (timespec_ge(&curtime, &ts)) 110135551Srwatson printf("warning: wait_time missed deadline without spinning\n"); 111135551Srwatson#endif 112135551Srwatson while (timespec_ge(&ts, &curtime)) { 113135551Srwatson if (waited != NULL) 114135551Srwatson (*waited)++; 115135551Srwatson if (clock_gettime(CLOCK_REALTIME, &curtime) == -1) { 116135042Srwatson perror("clock_gettime"); 117135042Srwatson return (-1); 118135042Srwatson } 119135042Srwatson } 120135042Srwatson if (wakeup_ts != NULL) 121135042Srwatson *wakeup_ts = curtime; 122135042Srwatson return (0); 123135042Srwatson} 124135042Srwatson 125135042Srwatson/* 126135042Srwatson * Calculate a second-aligned starting time for the packet stream. Busy 127135042Srwatson * wait between our calculated interval and dropping the provided packet 128135042Srwatson * into the socket. If we hit our duration limit, bail. 129198136Sluigi * We sweep the ports from a->port to a->port_max included. 130198136Sluigi * If the two ports are the same we connect() the socket upfront, which 131198136Sluigi * almost halves the cost of the sendto() call. 132135042Srwatson */ 133139121Skeramidastatic int 134198136Sluigitiming_loop(struct _a *a) 135135042Srwatson{ 136135551Srwatson struct timespec nexttime, starttime, tmptime; 137135551Srwatson long long waited; 138135042Srwatson u_int32_t counter; 139135042Srwatson long finishtime; 140139121Skeramida long send_errors, send_calls; 141198132Sluigi /* do not call gettimeofday more than every 20us */ 142198132Sluigi long minres_ns = 20000; 143198132Sluigi int ic, gettimeofday_cycles; 144198136Sluigi int cur_port; 145135042Srwatson 146135551Srwatson if (clock_getres(CLOCK_REALTIME, &tmptime) == -1) { 147135551Srwatson perror("clock_getres"); 148135551Srwatson return (-1); 149135551Srwatson } 150135551Srwatson 151198136Sluigi if (timespec_ge(&tmptime, &a->interval)) 152135551Srwatson fprintf(stderr, 153198132Sluigi "warning: interval (%jd.%09ld) less than resolution (%jd.%09ld)\n", 154198136Sluigi (intmax_t)a->interval.tv_sec, a->interval.tv_nsec, 155145367Smux (intmax_t)tmptime.tv_sec, tmptime.tv_nsec); 156198136Sluigi if (a->interval.tv_nsec < minres_ns) { 157198132Sluigi gettimeofday_cycles = minres_ns/(tmptime.tv_nsec + 1); 158198132Sluigi fprintf(stderr, 159198132Sluigi "calling time every %d cycles\n", gettimeofday_cycles); 160198132Sluigi } else 161198132Sluigi gettimeofday_cycles = 0; 162135551Srwatson 163135551Srwatson if (clock_gettime(CLOCK_REALTIME, &starttime) == -1) { 164135042Srwatson perror("clock_gettime"); 165135042Srwatson return (-1); 166135042Srwatson } 167135042Srwatson tmptime.tv_sec = 2; 168135042Srwatson tmptime.tv_nsec = 0; 169135042Srwatson timespec_add(&starttime, &tmptime); 170135042Srwatson starttime.tv_nsec = 0; 171135551Srwatson if (wait_time(starttime, NULL, NULL) == -1) 172135042Srwatson return (-1); 173135551Srwatson nexttime = starttime; 174198136Sluigi finishtime = starttime.tv_sec + a->duration; 175135042Srwatson 176135551Srwatson send_errors = send_calls = 0; 177135042Srwatson counter = 0; 178135551Srwatson waited = 0; 179198132Sluigi ic = gettimeofday_cycles; 180198136Sluigi cur_port = a->port; 181198136Sluigi if (a->port == a->port_max) { 182198136Sluigi if (connect(a->s, (struct sockaddr *)&a->sin, sizeof(a->sin))) { 183198136Sluigi perror("connect"); 184198136Sluigi return (-1); 185198136Sluigi } 186198136Sluigi } 187135042Srwatson while (1) { 188198136Sluigi int ret; 189198136Sluigi 190198136Sluigi timespec_add(&nexttime, &a->interval); 191198132Sluigi if (--ic <= 0) { 192198132Sluigi ic = gettimeofday_cycles; 193198132Sluigi if (wait_time(nexttime, &tmptime, &waited) == -1) 194198132Sluigi return (-1); 195198132Sluigi } 196135042Srwatson /* 197135042Srwatson * We maintain and, if there's room, send a counter. Note 198135042Srwatson * that even if the error is purely local, we still increment 199135042Srwatson * the counter, so missing sequence numbers on the receive 200135042Srwatson * side should not be assumed to be packets lost in transit. 201135042Srwatson * For example, if the UDP socket gets back an ICMP from a 202135042Srwatson * previous send, the error will turn up the current send 203135042Srwatson * operation, causing the current sequence number also to be 204135042Srwatson * skipped. 205198136Sluigi * The counter is incremented only on the initial port number, 206198136Sluigi * so all destinations will see the same set of packets. 207135042Srwatson */ 208198136Sluigi if (cur_port == a->port && a->packet_len >= 4) { 209225334Smarius be32enc(a->packet, counter); 210135042Srwatson counter++; 211135042Srwatson } 212198136Sluigi if (a->port == a->port_max) { /* socket already bound */ 213198136Sluigi ret = send(a->s, a->packet, a->packet_len, 0); 214198136Sluigi } else { 215198136Sluigi a->sin.sin_port = htons(cur_port++); 216198136Sluigi if (cur_port > a->port_max) 217198136Sluigi cur_port = a->port; 218198136Sluigi ret = sendto(a->s, a->packet, a->packet_len, 0, 219198136Sluigi (struct sockaddr *)&a->sin, sizeof(a->sin)); 220198136Sluigi } 221198136Sluigi if (ret < 0) 222135551Srwatson send_errors++; 223135551Srwatson send_calls++; 224198136Sluigi if (a->duration != 0 && tmptime.tv_sec >= finishtime) 225135551Srwatson goto done; 226135042Srwatson } 227135042Srwatson 228135551Srwatsondone: 229135551Srwatson if (clock_gettime(CLOCK_REALTIME, &tmptime) == -1) { 230135551Srwatson perror("clock_gettime"); 231135551Srwatson return (-1); 232135551Srwatson } 233135551Srwatson 234135551Srwatson printf("\n"); 235145367Smux printf("start: %jd.%09ld\n", (intmax_t)starttime.tv_sec, 236135551Srwatson starttime.tv_nsec); 237145367Smux printf("finish: %jd.%09ld\n", (intmax_t)tmptime.tv_sec, 238135551Srwatson tmptime.tv_nsec); 239139121Skeramida printf("send calls: %ld\n", send_calls); 240139121Skeramida printf("send errors: %ld\n", send_errors); 241198136Sluigi printf("approx send rate: %ld pps\n", (send_calls - send_errors) / 242198136Sluigi a->duration); 243139121Skeramida printf("approx error rate: %ld\n", (send_errors / send_calls)); 244135551Srwatson printf("waited: %lld\n", waited); 245198136Sluigi printf("approx waits/sec: %lld\n", (long long)(waited / a->duration)); 246145367Smux printf("approx wait rate: %lld\n", (long long)(waited / send_calls)); 247135551Srwatson 248135042Srwatson return (0); 249135042Srwatson} 250135042Srwatson 251135042Srwatsonint 252135042Srwatsonmain(int argc, char *argv[]) 253135042Srwatson{ 254198136Sluigi long rate, payloadsize, port; 255198136Sluigi char *dummy; 256198136Sluigi struct _a a; /* arguments */ 257135042Srwatson 258198136Sluigi bzero(&a, sizeof(a)); 259198136Sluigi 260135042Srwatson if (argc != 6) 261135042Srwatson usage(); 262135042Srwatson 263198136Sluigi a.sin.sin_len = sizeof(a.sin); 264198136Sluigi a.sin.sin_family = AF_INET; 265198136Sluigi if (inet_aton(argv[1], &a.sin.sin_addr) == 0) { 266135042Srwatson perror(argv[1]); 267135042Srwatson return (-1); 268135042Srwatson } 269135042Srwatson 270135042Srwatson port = strtoul(argv[2], &dummy, 10); 271198136Sluigi if (port < 1 || port > 65535) 272135042Srwatson usage(); 273198136Sluigi if (*dummy != '\0' && *dummy != '-') 274198136Sluigi usage(); 275198136Sluigi a.sin.sin_port = htons(port); 276198136Sluigi a.port = a.port_max = port; 277198136Sluigi if (*dummy == '-') { /* set high port */ 278198136Sluigi port = strtoul(dummy + 1, &dummy, 10); 279198136Sluigi if (port < a.port || port > 65535) 280198136Sluigi usage(); 281198136Sluigi a.port_max = port; 282198136Sluigi } 283135042Srwatson 284135042Srwatson payloadsize = strtoul(argv[3], &dummy, 10); 285135042Srwatson if (payloadsize < 0 || *dummy != '\0') 286135042Srwatson usage(); 287135042Srwatson if (payloadsize > 32768) { 288135042Srwatson fprintf(stderr, "payloadsize > 32768\n"); 289135042Srwatson return (-1); 290135042Srwatson } 291198136Sluigi a.packet_len = payloadsize; 292135042Srwatson 293135042Srwatson /* 294135042Srwatson * Specify an arbitrary limit. It's exactly that, not selected by 295198132Sluigi * any particular strategy. '0' is a special value meaning "blast", 296135551Srwatson * and avoids the cost of a timing loop. 297135042Srwatson */ 298135042Srwatson rate = strtoul(argv[4], &dummy, 10); 299198136Sluigi if (rate < 0 || *dummy != '\0') 300135042Srwatson usage(); 301135485Srwatson if (rate > MAX_RATE) { 302198136Sluigi fprintf(stderr, "packet rate at most %d\n", MAX_RATE); 303135042Srwatson return (-1); 304135042Srwatson } 305135042Srwatson 306198136Sluigi a.duration = strtoul(argv[5], &dummy, 10); 307198136Sluigi if (a.duration < 0 || *dummy != '\0') 308135042Srwatson usage(); 309135042Srwatson 310198136Sluigi a.packet = malloc(payloadsize); 311198136Sluigi if (a.packet == NULL) { 312135042Srwatson perror("malloc"); 313135042Srwatson return (-1); 314135042Srwatson } 315198136Sluigi bzero(a.packet, payloadsize); 316135551Srwatson if (rate == 0) { 317198136Sluigi a.interval.tv_sec = 0; 318198136Sluigi a.interval.tv_nsec = 0; 319135551Srwatson } else if (rate == 1) { 320198136Sluigi a.interval.tv_sec = 1; 321198136Sluigi a.interval.tv_nsec = 0; 322135042Srwatson } else { 323198136Sluigi a.interval.tv_sec = 0; 324198136Sluigi a.interval.tv_nsec = ((1 * 1000000000) / rate); 325135042Srwatson } 326198136Sluigi 327166567Srwatson printf("Sending packet of payload size %ld every %jd.%09lds for %ld " 328198136Sluigi "seconds\n", payloadsize, (intmax_t)a.interval.tv_sec, 329198136Sluigi a.interval.tv_nsec, a.duration); 330135042Srwatson 331198136Sluigi a.s = socket(PF_INET, SOCK_DGRAM, 0); 332198136Sluigi if (a.s == -1) { 333135042Srwatson perror("socket"); 334135042Srwatson return (-1); 335135042Srwatson } 336135042Srwatson 337198136Sluigi return (timing_loop(&a)); 338135042Srwatson} 339