1 /*- 2 * Copyright (c) 2004 Robert N. M. Watson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include <sys/types.h> 30 #include <sys/socket.h> 31 #include <sys/time.h> 32 33 #include <netinet/in.h> 34 35 #include <arpa/inet.h> 36 37 #include <stdio.h> 38 #include <stdint.h> 39 #include <stdlib.h> 40 #include <string.h> 41 42 /* program arguments */ 43 struct _a { 44 int s; 45 struct timespec interval; 46 int port, port_max; 47 long duration; 48 struct sockaddr_in sin; 49 int packet_len; 50 void *packet; 51 }; 52 53 static void 54 usage(void) 55 { 56 57 fprintf(stderr, 58 "netsend [ip] [port[-port_max]] [payloadsize] [packet_rate] [duration]\n"); 59 exit(-1); 60 } 61 62 #define MAX_RATE 100000000 63 64 static __inline void 65 timespec_add(struct timespec *tsa, struct timespec *tsb) 66 { 67 68 tsa->tv_sec += tsb->tv_sec; 69 tsa->tv_nsec += tsb->tv_nsec; 70 if (tsa->tv_nsec >= 1000000000) { 71 tsa->tv_sec++; 72 tsa->tv_nsec -= 1000000000; 73 } 74 } 75 76 static __inline int 77 timespec_ge(struct timespec *a, struct timespec *b) 78 { 79 80 if (a->tv_sec > b->tv_sec) 81 return (1); 82 if (a->tv_sec < b->tv_sec) 83 return (0); 84 if (a->tv_nsec >= b->tv_nsec) 85 return (1); 86 return (0); 87 } 88 89 /* 90 * Busy wait spinning until we reach (or slightly pass) the desired time. 91 * Optionally return the current time as retrieved on the last time check 92 * to the caller. Optionally also increment a counter provided by the 93 * caller each time we loop. 94 */ 95 static int 96 wait_time(struct timespec ts, struct timespec *wakeup_ts, long long *waited) 97 { 98 struct timespec curtime; 99 100 curtime.tv_sec = 0; 101 curtime.tv_nsec = 0; 102 103 if (clock_gettime(CLOCK_REALTIME, &curtime) == -1) { 104 perror("clock_gettime"); 105 return (-1); 106 } 107 #if 0 108 if (timespec_ge(&curtime, &ts)) 109 printf("warning: wait_time missed deadline without spinning\n"); 110 #endif 111 while (timespec_ge(&ts, &curtime)) { 112 if (waited != NULL) 113 (*waited)++; 114 if (clock_gettime(CLOCK_REALTIME, &curtime) == -1) { 115 perror("clock_gettime"); 116 return (-1); 117 } 118 } 119 if (wakeup_ts != NULL) 120 *wakeup_ts = curtime; 121 return (0); 122 } 123 124 /* 125 * Calculate a second-aligned starting time for the packet stream. Busy 126 * wait between our calculated interval and dropping the provided packet 127 * into the socket. If we hit our duration limit, bail. 128 * We sweep the ports from a->port to a->port_max included. 129 * If the two ports are the same we connect() the socket upfront, which 130 * almost halves the cost of the sendto() call. 131 */ 132 static int 133 timing_loop(struct _a *a) 134 { 135 struct timespec nexttime, starttime, tmptime; 136 long long waited; 137 u_int32_t counter; 138 long finishtime; 139 long send_errors, send_calls; 140 /* do not call gettimeofday more than every 20us */ 141 long minres_ns = 20000; 142 int ic, gettimeofday_cycles; 143 int cur_port; 144 145 if (clock_getres(CLOCK_REALTIME, &tmptime) == -1) { 146 perror("clock_getres"); 147 return (-1); 148 } 149 150 if (timespec_ge(&tmptime, &a->interval)) 151 fprintf(stderr, 152 "warning: interval (%jd.%09ld) less than resolution (%jd.%09ld)\n", 153 (intmax_t)a->interval.tv_sec, a->interval.tv_nsec, 154 (intmax_t)tmptime.tv_sec, tmptime.tv_nsec); 155 if (a->interval.tv_nsec < minres_ns) { 156 gettimeofday_cycles = minres_ns/(tmptime.tv_nsec + 1); 157 fprintf(stderr, 158 "calling time every %d cycles\n", gettimeofday_cycles); 159 } else 160 gettimeofday_cycles = 0; 161 162 if (clock_gettime(CLOCK_REALTIME, &starttime) == -1) { 163 perror("clock_gettime"); 164 return (-1); 165 } 166 tmptime.tv_sec = 2; 167 tmptime.tv_nsec = 0; 168 timespec_add(&starttime, &tmptime); 169 starttime.tv_nsec = 0; 170 if (wait_time(starttime, NULL, NULL) == -1) 171 return (-1); 172 nexttime = starttime; 173 finishtime = starttime.tv_sec + a->duration; 174 175 send_errors = send_calls = 0; 176 counter = 0; 177 waited = 0; 178 ic = gettimeofday_cycles; 179 cur_port = a->port; 180 if (a->port == a->port_max) { 181 if (connect(a->s, (struct sockaddr *)&a->sin, sizeof(a->sin))) { 182 perror("connect"); 183 return (-1); 184 } 185 } 186 while (1) { 187 int ret; 188 189 timespec_add(&nexttime, &a->interval); 190 if (--ic <= 0) { 191 ic = gettimeofday_cycles; 192 if (wait_time(nexttime, &tmptime, &waited) == -1) 193 return (-1); 194 } 195 /* 196 * We maintain and, if there's room, send a counter. Note 197 * that even if the error is purely local, we still increment 198 * the counter, so missing sequence numbers on the receive 199 * side should not be assumed to be packets lost in transit. 200 * For example, if the UDP socket gets back an ICMP from a 201 * previous send, the error will turn up the current send 202 * operation, causing the current sequence number also to be 203 * skipped. 204 * The counter is incremented only on the initial port number, 205 * so all destinations will see the same set of packets. 206 * 207 * XXXRW: Note alignment assumption. 208 */ 209 if (cur_port == a->port && a->packet_len >= 4) { 210 *((u_int32_t *)a->packet) = htonl(counter); 211 counter++; 212 } 213 if (a->port == a->port_max) { /* socket already bound */ 214 ret = send(a->s, a->packet, a->packet_len, 0); 215 } else { 216 a->sin.sin_port = htons(cur_port++); 217 if (cur_port > a->port_max) 218 cur_port = a->port; 219 ret = sendto(a->s, a->packet, a->packet_len, 0, 220 (struct sockaddr *)&a->sin, sizeof(a->sin)); 221 } 222 if (ret < 0) 223 send_errors++; 224 send_calls++; 225 if (a->duration != 0 && tmptime.tv_sec >= finishtime) 226 goto done; 227 } 228 229 done: 230 if (clock_gettime(CLOCK_REALTIME, &tmptime) == -1) { 231 perror("clock_gettime"); 232 return (-1); 233 } 234 235 printf("\n"); 236 printf("start: %jd.%09ld\n", (intmax_t)starttime.tv_sec, 237 starttime.tv_nsec); 238 printf("finish: %jd.%09ld\n", (intmax_t)tmptime.tv_sec, 239 tmptime.tv_nsec); 240 printf("send calls: %ld\n", send_calls); 241 printf("send errors: %ld\n", send_errors); 242 printf("approx send rate: %ld pps\n", (send_calls - send_errors) / 243 a->duration); 244 printf("approx error rate: %ld\n", (send_errors / send_calls)); 245 printf("waited: %lld\n", waited); 246 printf("approx waits/sec: %lld\n", (long long)(waited / a->duration)); 247 printf("approx wait rate: %lld\n", (long long)(waited / send_calls)); 248 249 return (0); 250 } 251 252 int 253 main(int argc, char *argv[]) 254 { 255 long rate, payloadsize, port; 256 char *dummy; 257 struct _a a; /* arguments */ 258 259 bzero(&a, sizeof(a)); 260 261 if (argc != 6) 262 usage(); 263 264 a.sin.sin_len = sizeof(a.sin); 265 a.sin.sin_family = AF_INET; 266 if (inet_aton(argv[1], &a.sin.sin_addr) == 0) { 267 perror(argv[1]); 268 return (-1); 269 } 270 271 port = strtoul(argv[2], &dummy, 10); 272 if (port < 1 || port > 65535) 273 usage(); 274 if (*dummy != '\0' && *dummy != '-') 275 usage(); 276 a.sin.sin_port = htons(port); 277 a.port = a.port_max = port; 278 if (*dummy == '-') { /* set high port */ 279 port = strtoul(dummy + 1, &dummy, 10); 280 if (port < a.port || port > 65535) 281 usage(); 282 a.port_max = port; 283 } 284 285 payloadsize = strtoul(argv[3], &dummy, 10); 286 if (payloadsize < 0 || *dummy != '\0') 287 usage(); 288 if (payloadsize > 32768) { 289 fprintf(stderr, "payloadsize > 32768\n"); 290 return (-1); 291 } 292 a.packet_len = payloadsize; 293 294 /* 295 * Specify an arbitrary limit. It's exactly that, not selected by 296 * any particular strategy. '0' is a special value meaning "blast", 297 * and avoids the cost of a timing loop. 298 */ 299 rate = strtoul(argv[4], &dummy, 10); 300 if (rate < 0 || *dummy != '\0') 301 usage(); 302 if (rate > MAX_RATE) { 303 fprintf(stderr, "packet rate at most %d\n", MAX_RATE); 304 return (-1); 305 } 306 307 a.duration = strtoul(argv[5], &dummy, 10); 308 if (a.duration < 0 || *dummy != '\0') 309 usage(); 310 311 a.packet = malloc(payloadsize); 312 if (a.packet == NULL) { 313 perror("malloc"); 314 return (-1); 315 } 316 bzero(a.packet, payloadsize); 317 if (rate == 0) { 318 a.interval.tv_sec = 0; 319 a.interval.tv_nsec = 0; 320 } else if (rate == 1) { 321 a.interval.tv_sec = 1; 322 a.interval.tv_nsec = 0; 323 } else { 324 a.interval.tv_sec = 0; 325 a.interval.tv_nsec = ((1 * 1000000000) / rate); 326 } 327 328 printf("Sending packet of payload size %ld every %jd.%09lds for %ld " 329 "seconds\n", payloadsize, (intmax_t)a.interval.tv_sec, 330 a.interval.tv_nsec, a.duration); 331 332 a.s = socket(PF_INET, SOCK_DGRAM, 0); 333 if (a.s == -1) { 334 perror("socket"); 335 return (-1); 336 } 337 338 return (timing_loop(&a)); 339 } 340