2 * Copyright (c) 2004 Robert N. M. Watson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
29 #include <sys/types.h>
30 #include <sys/socket.h>
33 #include <netinet/in.h>
35 #include <arpa/inet.h>
47 "netsend [ip] [port] [payloadsize] [rate] [duration]\n");
51 #define MAX_RATE 100000000
54 timespec_add(struct timespec *tsa, struct timespec *tsb)
57 tsa->tv_sec += tsb->tv_sec;
58 tsa->tv_nsec += tsb->tv_nsec;
59 if (tsa->tv_nsec >= 1000000000) {
61 tsa->tv_nsec -= 1000000000;
66 timespec_ge(struct timespec *a, struct timespec *b)
69 if (a->tv_sec > b->tv_sec)
71 if (a->tv_sec < b->tv_sec)
73 if (a->tv_nsec >= b->tv_nsec)
79 * Busy wait spinning until we reach (or slightly pass) the desired time.
80 * Optionally return the current time as retrieved on the last time check
81 * to the caller. Optionally also increment a counter provided by the
82 * caller each time we loop.
85 wait_time(struct timespec ts, struct timespec *wakeup_ts, long long *waited)
87 struct timespec curtime;
92 if (clock_gettime(CLOCK_REALTIME, &curtime) == -1) {
93 perror("clock_gettime");
97 if (timespec_ge(&curtime, &ts))
98 printf("warning: wait_time missed deadline without spinning\n");
100 while (timespec_ge(&ts, &curtime)) {
103 if (clock_gettime(CLOCK_REALTIME, &curtime) == -1) {
104 perror("clock_gettime");
108 if (wakeup_ts != NULL)
109 *wakeup_ts = curtime;
114 * Calculate a second-aligned starting time for the packet stream. Busy
115 * wait between our calculated interval and dropping the provided packet
116 * into the socket. If we hit our duration limit, bail.
119 timing_loop(int s, struct timespec interval, long duration, u_char *packet,
122 struct timespec nexttime, starttime, tmptime;
126 long send_errors, send_calls;
128 if (clock_getres(CLOCK_REALTIME, &tmptime) == -1) {
129 perror("clock_getres");
133 if (timespec_ge(&tmptime, &interval))
135 "warning: interval less than resolution (%jd.%09ld)\n",
136 (intmax_t)tmptime.tv_sec, tmptime.tv_nsec);
138 if (clock_gettime(CLOCK_REALTIME, &starttime) == -1) {
139 perror("clock_gettime");
144 timespec_add(&starttime, &tmptime);
145 starttime.tv_nsec = 0;
146 if (wait_time(starttime, NULL, NULL) == -1)
148 nexttime = starttime;
149 finishtime = starttime.tv_sec + duration;
151 send_errors = send_calls = 0;
155 timespec_add(&nexttime, &interval);
156 if (wait_time(nexttime, &tmptime, &waited) == -1)
159 * We maintain and, if there's room, send a counter. Note
160 * that even if the error is purely local, we still increment
161 * the counter, so missing sequence numbers on the receive
162 * side should not be assumed to be packets lost in transit.
163 * For example, if the UDP socket gets back an ICMP from a
164 * previous send, the error will turn up the current send
165 * operation, causing the current sequence number also to be
168 * XXXRW: Note alignment assumption.
170 if (packet_len >= 4) {
171 *((u_int32_t *)packet) = htonl(counter);
174 if (send(s, packet, packet_len, 0) < 0)
177 if (duration != 0 && tmptime.tv_sec >= finishtime)
182 if (clock_gettime(CLOCK_REALTIME, &tmptime) == -1) {
183 perror("clock_gettime");
188 printf("start: %jd.%09ld\n", (intmax_t)starttime.tv_sec,
190 printf("finish: %jd.%09ld\n", (intmax_t)tmptime.tv_sec,
192 printf("send calls: %ld\n", send_calls);
193 printf("send errors: %ld\n", send_errors);
194 printf("approx send rate: %ld\n", (send_calls - send_errors) /
196 printf("approx error rate: %ld\n", (send_errors / send_calls));
197 printf("waited: %lld\n", waited);
198 printf("approx waits/sec: %lld\n", (long long)(waited / duration));
199 printf("approx wait rate: %lld\n", (long long)(waited / send_calls));
205 main(int argc, char *argv[])
207 long rate, payloadsize, port, duration;
208 struct timespec interval;
209 struct sockaddr_in sin;
210 char *dummy, *packet;
216 bzero(&sin, sizeof(sin));
217 sin.sin_len = sizeof(sin);
218 sin.sin_family = AF_INET;
219 if (inet_aton(argv[1], &sin.sin_addr) == 0) {
224 port = strtoul(argv[2], &dummy, 10);
225 if (port < 1 || port > 65535 || *dummy != '\0')
227 sin.sin_port = htons(port);
229 payloadsize = strtoul(argv[3], &dummy, 10);
230 if (payloadsize < 0 || *dummy != '\0')
232 if (payloadsize > 32768) {
233 fprintf(stderr, "payloadsize > 32768\n");
238 * Specify an arbitrary limit. It's exactly that, not selected by
239 .* any particular strategy. '0' is a special value meaning "blast",
240 * and avoids the cost of a timing loop.
242 rate = strtoul(argv[4], &dummy, 10);
243 if (rate < 1 || *dummy != '\0')
245 if (rate > MAX_RATE) {
246 fprintf(stderr, "rate > %d\n", MAX_RATE);
250 duration = strtoul(argv[5], &dummy, 10);
251 if (duration < 0 || *dummy != '\0')
254 packet = malloc(payloadsize);
255 if (packet == NULL) {
259 bzero(packet, payloadsize);
263 interval.tv_nsec = 0;
264 } else if (rate == 1) {
266 interval.tv_nsec = 0;
269 interval.tv_nsec = ((1 * 1000000000) / rate);
271 printf("Sending packet of payload size %ld every %jd.%09lds for %ld "
272 "seconds\n", payloadsize, (intmax_t)interval.tv_sec,
273 interval.tv_nsec, duration);
275 s = socket(PF_INET, SOCK_DGRAM, 0);
281 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
286 return (timing_loop(s, interval, duration, packet, payloadsize));