netblast.c revision 153470
1202188Sed/*-
2202188Sed * Copyright (c) 2004 Robert N. M. Watson
3202188Sed * All rights reserved.
4202188Sed *
5202188Sed * Redistribution and use in source and binary forms, with or without
6202188Sed * modification, are permitted provided that the following conditions
7202188Sed * are met:
8202188Sed * 1. Redistributions of source code must retain the above copyright
9202188Sed *    notice, this list of conditions and the following disclaimer.
10202188Sed * 2. Redistributions in binary form must reproduce the above copyright
11202188Sed *    notice, this list of conditions and the following disclaimer in the
12202188Sed *    documentation and/or other materials provided with the distribution.
13202188Sed *
14202188Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15202188Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16202188Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17202188Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18202188Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19202188Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20202188Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21202188Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22202188Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23202188Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24202188Sed * SUCH DAMAGE.
25202188Sed *
26202188Sed * $FreeBSD: head/tools/tools/netrate/netblast/netblast.c 153470 2005-12-16 06:02:44Z scottl $
27202188Sed */
28202188Sed
29202188Sed#include <sys/types.h>
30202188Sed#include <sys/socket.h>
31202188Sed#include <sys/time.h>
32202188Sed
33202188Sed#include <netinet/in.h>
34202188Sed
35202188Sed#include <arpa/inet.h>
36202188Sed
37202188Sed#include <signal.h>
38202188Sed#include <stdio.h>
39202188Sed#include <stdlib.h>
40202188Sed#include <string.h>
41202188Sed
42202188Sedstatic void
43202188Sedusage(void)
44202188Sed{
45202188Sed
46202188Sed	fprintf(stderr, "netblast [ip] [port] [payloadsize] [duration]\n");
47202188Sed	exit(-1);
48202188Sed}
49202188Sed
50202188Sedstatic int	global_stop_flag;
51202188Sed
52202188Sedstatic void
53202188Sedsignal_handler(int signum __unused)
54202188Sed{
55202188Sed
56202188Sed	global_stop_flag = 1;
57202188Sed}
58202188Sed
59202188Sed/*
60202188Sed * Loop that blasts packets: begin by recording time information, resetting
61202188Sed * stats.  Set the interval timer for when we want to wake up.  Then go.
62202188Sed * SIGALRM will set a flag indicating it's time to stop.  Note that there's
63202188Sed * some overhead to the signal and timer setup, so the smaller the duration,
64202188Sed * the higher the relative overhead.
65202188Sed */
66202188Sedstatic int
67202188Sedblast_loop(int s, long duration, u_char *packet, u_int packet_len)
68202188Sed{
69202188Sed	struct timespec starttime, tmptime;
70202188Sed	struct itimerval it;
71202188Sed	u_int32_t counter;
72202188Sed	int send_errors, send_calls;
73202188Sed
74202188Sed	if (signal(SIGALRM, signal_handler) == SIG_ERR) {
75202188Sed		perror("signal");
76202188Sed		return (-1);
77202188Sed	}
78202188Sed
79202188Sed	if (clock_getres(CLOCK_REALTIME, &tmptime) == -1) {
80202188Sed		perror("clock_getres");
81202188Sed		return (-1);
82202188Sed	}
83202188Sed
84202188Sed	if (clock_gettime(CLOCK_REALTIME, &starttime) == -1) {
85202188Sed		perror("clock_gettime");
86202188Sed		return (-1);
87202188Sed	}
88202188Sed
89	it.it_interval.tv_sec = 0;
90	it.it_interval.tv_usec = 0;
91	it.it_value.tv_sec = duration;
92	it.it_value.tv_usec = 0;
93
94	if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
95		perror("setitimer");
96		return (-1);
97	}
98
99	send_errors = send_calls = 0;
100	counter = 0;
101	while (global_stop_flag == 0) {
102		/*
103		 * We maintain and, if there's room, send a counter.  Note
104		 * that even if the error is purely local, we still increment
105		 * the counter, so missing sequence numbers on the receive
106		 * side should not be assumed to be packets lost in transit.
107		 * For example, if the UDP socket gets back an ICMP from a
108		 * previous send, the error will turn up the current send
109		 * operation, causing the current sequence number also to be
110		 * skipped.
111		 *
112		 * XXXRW: Note alignment assumption.
113		 */
114		if (packet_len >= 4) {
115			*((u_int32_t *)packet) = htonl(counter);
116			counter++;
117		}
118		if (send(s, packet, packet_len, 0) < 0)
119			send_errors++;
120		send_calls++;
121	}
122
123	if (clock_gettime(CLOCK_REALTIME, &tmptime) == -1) {
124		perror("clock_gettime");
125		return (-1);
126	}
127
128	printf("\n");
129	printf("start:             %zd.%09lu\n", starttime.tv_sec,
130	    starttime.tv_nsec);
131	printf("finish:            %zd.%09lu\n", tmptime.tv_sec,
132	    tmptime.tv_nsec);
133	printf("send calls:        %d\n", send_calls);
134	printf("send errors:       %d\n", send_errors);
135	printf("approx send rate:  %ld\n", (send_calls - send_errors) /
136	    duration);
137	printf("approx error rate: %d\n", (send_errors / send_calls));
138
139	return (0);
140}
141
142int
143main(int argc, char *argv[])
144{
145	long payloadsize, port, duration;
146	struct sockaddr_in sin;
147	char *dummy, *packet;
148	int s;
149
150	if (argc != 5)
151		usage();
152
153	bzero(&sin, sizeof(sin));
154	sin.sin_len = sizeof(sin);
155	sin.sin_family = AF_INET;
156	if (inet_aton(argv[1], &sin.sin_addr) == 0) {
157		perror(argv[1]);
158		return (-1);
159	}
160
161	port = strtoul(argv[2], &dummy, 10);
162	if (port < 1 || port > 65535 || *dummy != '\0')
163		usage();
164	sin.sin_port = htons(port);
165
166	payloadsize = strtoul(argv[3], &dummy, 10);
167	if (payloadsize < 0 || *dummy != '\0')
168		usage();
169	if (payloadsize > 32768) {
170		fprintf(stderr, "payloadsize > 32768\n");
171		return (-1);
172	}
173
174	duration = strtoul(argv[4], &dummy, 10);
175	if (duration < 0 || *dummy != '\0')
176		usage();
177
178	packet = malloc(payloadsize);
179	if (packet == NULL) {
180		perror("malloc");
181		return (-1);
182	}
183	bzero(packet, payloadsize);
184
185	s = socket(PF_INET, SOCK_DGRAM, 0);
186	if (s == -1) {
187		perror("socket");
188		return (-1);
189	}
190
191	if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
192		perror("connect");
193		return (-1);
194	}
195
196	return (blast_loop(s, duration, packet, payloadsize));
197}
198