]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - tools/tools/netrate/netsend/netsend.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / tools / tools / netrate / netsend / netsend.c
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 static void
43 usage(void)
44 {
45
46         fprintf(stderr,
47             "netsend [ip] [port] [payloadsize] [rate] [duration]\n");
48         exit(-1);
49 }
50
51 #define MAX_RATE        100000000
52
53 static __inline void
54 timespec_add(struct timespec *tsa, struct timespec *tsb)
55 {
56
57         tsa->tv_sec += tsb->tv_sec;
58         tsa->tv_nsec += tsb->tv_nsec;
59         if (tsa->tv_nsec >= 1000000000) {
60                 tsa->tv_sec++;
61                 tsa->tv_nsec -= 1000000000;
62         }
63 }
64
65 static __inline int
66 timespec_ge(struct timespec *a, struct timespec *b)
67 {
68
69         if (a->tv_sec > b->tv_sec)
70                 return (1);
71         if (a->tv_sec < b->tv_sec)
72                 return (0);
73         if (a->tv_nsec >= b->tv_nsec)
74                 return (1);
75         return (0);
76 }
77
78 /*
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.
83  */
84 static int
85 wait_time(struct timespec ts, struct timespec *wakeup_ts, long long *waited)
86 {
87         struct timespec curtime;
88
89         curtime.tv_sec = 0;
90         curtime.tv_nsec = 0;
91
92         if (clock_gettime(CLOCK_REALTIME, &curtime) == -1) {
93                 perror("clock_gettime");
94                 return (-1);
95         }
96 #if 0
97         if (timespec_ge(&curtime, &ts))
98                 printf("warning: wait_time missed deadline without spinning\n");
99 #endif
100         while (timespec_ge(&ts, &curtime)) {
101                 if (waited != NULL)
102                         (*waited)++;
103                 if (clock_gettime(CLOCK_REALTIME, &curtime) == -1) {
104                         perror("clock_gettime");
105                         return (-1);
106                 }
107         }
108         if (wakeup_ts != NULL)
109                 *wakeup_ts = curtime;
110         return (0);
111 }
112
113 /*
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.
117  */
118 static int
119 timing_loop(int s, struct timespec interval, long duration, u_char *packet,
120     u_int packet_len)
121 {
122         struct timespec nexttime, starttime, tmptime;
123         long long waited;
124         u_int32_t counter;
125         long finishtime;
126         long send_errors, send_calls;
127
128         if (clock_getres(CLOCK_REALTIME, &tmptime) == -1) {
129                 perror("clock_getres");
130                 return (-1);
131         }
132
133         if (timespec_ge(&tmptime, &interval))
134                 fprintf(stderr,
135                     "warning: interval less than resolution (%jd.%09ld)\n",
136                     (intmax_t)tmptime.tv_sec, tmptime.tv_nsec);
137
138         if (clock_gettime(CLOCK_REALTIME, &starttime) == -1) {
139                 perror("clock_gettime");
140                 return (-1);
141         }
142         tmptime.tv_sec = 2;
143         tmptime.tv_nsec = 0;
144         timespec_add(&starttime, &tmptime);
145         starttime.tv_nsec = 0;
146         if (wait_time(starttime, NULL, NULL) == -1)
147                 return (-1);
148         nexttime = starttime;
149         finishtime = starttime.tv_sec + duration;
150
151         send_errors = send_calls = 0;
152         counter = 0;
153         waited = 0;
154         while (1) {
155                 timespec_add(&nexttime, &interval);
156                 if (wait_time(nexttime, &tmptime, &waited) == -1)
157                         return (-1);
158                 /*
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
166                  * skipped.
167                  *
168                  * XXXRW: Note alignment assumption.
169                  */
170                 if (packet_len >= 4) {
171                         *((u_int32_t *)packet) = htonl(counter);
172                         counter++;
173                 }
174                 if (send(s, packet, packet_len, 0) < 0)
175                         send_errors++;
176                 send_calls++;
177                 if (duration != 0 && tmptime.tv_sec >= finishtime)
178                         goto done;
179         }
180
181 done:
182         if (clock_gettime(CLOCK_REALTIME, &tmptime) == -1) {
183                 perror("clock_gettime");
184                 return (-1);
185         }
186
187         printf("\n");
188         printf("start:             %jd.%09ld\n", (intmax_t)starttime.tv_sec,
189             starttime.tv_nsec);
190         printf("finish:            %jd.%09ld\n", (intmax_t)tmptime.tv_sec,
191             tmptime.tv_nsec);
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) /
195             duration);
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));
200
201         return (0);
202 }
203
204 int
205 main(int argc, char *argv[])
206 {
207         long rate, payloadsize, port, duration;
208         struct timespec interval;
209         struct sockaddr_in sin;
210         char *dummy, *packet;
211         int s;
212
213         if (argc != 6)
214                 usage();
215
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) {
220                 perror(argv[1]);
221                 return (-1);
222         }
223
224         port = strtoul(argv[2], &dummy, 10);
225         if (port < 1 || port > 65535 || *dummy != '\0')
226                 usage();
227         sin.sin_port = htons(port);
228
229         payloadsize = strtoul(argv[3], &dummy, 10);
230         if (payloadsize < 0 || *dummy != '\0')
231                 usage();
232         if (payloadsize > 32768) {
233                 fprintf(stderr, "payloadsize > 32768\n");
234                 return (-1);
235         }
236
237         /*
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.
241          */
242         rate = strtoul(argv[4], &dummy, 10);
243         if (rate < 1 || *dummy != '\0')
244                 usage();
245         if (rate > MAX_RATE) {
246                 fprintf(stderr, "rate > %d\n", MAX_RATE);
247                 return (-1);
248         }
249
250         duration = strtoul(argv[5], &dummy, 10);
251         if (duration < 0 || *dummy != '\0')
252                 usage();
253
254         packet = malloc(payloadsize);
255         if (packet == NULL) {
256                 perror("malloc");
257                 return (-1);
258         }
259         bzero(packet, payloadsize);
260
261         if (rate == 0) {
262                 interval.tv_sec = 0;
263                 interval.tv_nsec = 0;
264         } else if (rate == 1) {
265                 interval.tv_sec = 1;
266                 interval.tv_nsec = 0;
267         } else {
268                 interval.tv_sec = 0;
269                 interval.tv_nsec = ((1 * 1000000000) / rate);
270         }
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);
274
275         s = socket(PF_INET, SOCK_DGRAM, 0);
276         if (s == -1) {
277                 perror("socket");
278                 return (-1);
279         }
280
281         if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
282                 perror("connect");
283                 return (-1);
284         }
285
286         return (timing_loop(s, interval, duration, packet, payloadsize));
287 }