1136274Srwatson/*-
2136274Srwatson * Copyright (c) 2004 Robert N. M. Watson
3136274Srwatson * All rights reserved.
4136274Srwatson *
5136274Srwatson * Redistribution and use in source and binary forms, with or without
6136274Srwatson * modification, are permitted provided that the following conditions
7136274Srwatson * are met:
8136274Srwatson * 1. Redistributions of source code must retain the above copyright
9136274Srwatson *    notice, this list of conditions and the following disclaimer.
10136274Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11136274Srwatson *    notice, this list of conditions and the following disclaimer in the
12136274Srwatson *    documentation and/or other materials provided with the distribution.
13136274Srwatson *
14136274Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15136274Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16136274Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17136274Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18136274Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19136274Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20136274Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21136274Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22136274Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23136274Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24136274Srwatson * SUCH DAMAGE.
25136274Srwatson *
26136274Srwatson * $FreeBSD$
27136274Srwatson */
28136274Srwatson
29225334Smarius#include <sys/endian.h>
30136274Srwatson#include <sys/types.h>
31136274Srwatson#include <sys/socket.h>
32136274Srwatson#include <sys/time.h>
33136274Srwatson
34136274Srwatson#include <netinet/in.h>
35227345Scognet#include <netdb.h>			/* getaddrinfo */
36136274Srwatson
37136274Srwatson#include <signal.h>
38136274Srwatson#include <stdio.h>
39136274Srwatson#include <stdlib.h>
40136274Srwatson#include <string.h>
41227345Scognet#include <unistd.h>			/* close */
42136274Srwatson
43136274Srwatsonstatic void
44136274Srwatsonusage(void)
45136274Srwatson{
46136274Srwatson
47136274Srwatson	fprintf(stderr, "netblast [ip] [port] [payloadsize] [duration]\n");
48136274Srwatson	exit(-1);
49136274Srwatson}
50136274Srwatson
51136274Srwatsonstatic int	global_stop_flag;
52136274Srwatson
53136274Srwatsonstatic void
54141761Srwatsonsignal_handler(int signum __unused)
55136274Srwatson{
56136274Srwatson
57136274Srwatson	global_stop_flag = 1;
58136274Srwatson}
59136274Srwatson
60136274Srwatson/*
61136274Srwatson * Loop that blasts packets: begin by recording time information, resetting
62136274Srwatson * stats.  Set the interval timer for when we want to wake up.  Then go.
63136274Srwatson * SIGALRM will set a flag indicating it's time to stop.  Note that there's
64136274Srwatson * some overhead to the signal and timer setup, so the smaller the duration,
65136274Srwatson * the higher the relative overhead.
66136274Srwatson */
67136274Srwatsonstatic int
68136274Srwatsonblast_loop(int s, long duration, u_char *packet, u_int packet_len)
69136274Srwatson{
70136274Srwatson	struct timespec starttime, tmptime;
71136274Srwatson	struct itimerval it;
72136274Srwatson	u_int32_t counter;
73136274Srwatson	int send_errors, send_calls;
74136274Srwatson
75136274Srwatson	if (signal(SIGALRM, signal_handler) == SIG_ERR) {
76136274Srwatson		perror("signal");
77136274Srwatson		return (-1);
78136274Srwatson	}
79136274Srwatson
80136274Srwatson	if (clock_getres(CLOCK_REALTIME, &tmptime) == -1) {
81136274Srwatson		perror("clock_getres");
82136274Srwatson		return (-1);
83136274Srwatson	}
84136274Srwatson
85136274Srwatson	if (clock_gettime(CLOCK_REALTIME, &starttime) == -1) {
86136274Srwatson		perror("clock_gettime");
87136274Srwatson		return (-1);
88136274Srwatson	}
89136274Srwatson
90136274Srwatson	it.it_interval.tv_sec = 0;
91136274Srwatson	it.it_interval.tv_usec = 0;
92136274Srwatson	it.it_value.tv_sec = duration;
93136274Srwatson	it.it_value.tv_usec = 0;
94136274Srwatson
95136274Srwatson	if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
96136274Srwatson		perror("setitimer");
97136274Srwatson		return (-1);
98136274Srwatson	}
99136274Srwatson
100136274Srwatson	send_errors = send_calls = 0;
101136274Srwatson	counter = 0;
102136274Srwatson	while (global_stop_flag == 0) {
103136274Srwatson		/*
104136274Srwatson		 * We maintain and, if there's room, send a counter.  Note
105136274Srwatson		 * that even if the error is purely local, we still increment
106136274Srwatson		 * the counter, so missing sequence numbers on the receive
107136274Srwatson		 * side should not be assumed to be packets lost in transit.
108136274Srwatson		 * For example, if the UDP socket gets back an ICMP from a
109136274Srwatson		 * previous send, the error will turn up the current send
110136274Srwatson		 * operation, causing the current sequence number also to be
111136274Srwatson		 * skipped.
112136274Srwatson		 */
113136274Srwatson		if (packet_len >= 4) {
114225334Smarius			be32enc(packet, counter);
115136274Srwatson			counter++;
116136274Srwatson		}
117136274Srwatson		if (send(s, packet, packet_len, 0) < 0)
118136274Srwatson			send_errors++;
119136274Srwatson		send_calls++;
120136274Srwatson	}
121136274Srwatson
122136274Srwatson	if (clock_gettime(CLOCK_REALTIME, &tmptime) == -1) {
123136274Srwatson		perror("clock_gettime");
124136274Srwatson		return (-1);
125136274Srwatson	}
126136274Srwatson
127136274Srwatson	printf("\n");
128153470Sscottl	printf("start:             %zd.%09lu\n", starttime.tv_sec,
129136274Srwatson	    starttime.tv_nsec);
130153470Sscottl	printf("finish:            %zd.%09lu\n", tmptime.tv_sec,
131136274Srwatson	    tmptime.tv_nsec);
132136274Srwatson	printf("send calls:        %d\n", send_calls);
133136274Srwatson	printf("send errors:       %d\n", send_errors);
134136274Srwatson	printf("approx send rate:  %ld\n", (send_calls - send_errors) /
135136274Srwatson	    duration);
136136274Srwatson	printf("approx error rate: %d\n", (send_errors / send_calls));
137136274Srwatson
138136274Srwatson	return (0);
139136274Srwatson}
140136274Srwatson
141136274Srwatsonint
142136274Srwatsonmain(int argc, char *argv[])
143136274Srwatson{
144227345Scognet	long payloadsize, duration;
145227345Scognet	struct addrinfo hints, *res, *res0;
146136274Srwatson	char *dummy, *packet;
147227345Scognet	int port, s, error;
148227345Scognet	const char *cause = NULL;
149136274Srwatson
150136274Srwatson	if (argc != 5)
151136274Srwatson		usage();
152136274Srwatson
153227345Scognet	memset(&hints, 0, sizeof(hints));
154227345Scognet	hints.ai_family = PF_UNSPEC;
155227345Scognet	hints.ai_socktype = SOCK_DGRAM;
156136274Srwatson
157136274Srwatson	port = strtoul(argv[2], &dummy, 10);
158227345Scognet	if (port < 1 || port > 65535 || *dummy != '\0') {
159227345Scognet		fprintf(stderr, "Invalid port number: %s\n", argv[2]);
160136274Srwatson		usage();
161227345Scognet		/*NOTREACHED*/
162227345Scognet	}
163136274Srwatson
164136274Srwatson	payloadsize = strtoul(argv[3], &dummy, 10);
165136274Srwatson	if (payloadsize < 0 || *dummy != '\0')
166136274Srwatson		usage();
167136274Srwatson	if (payloadsize > 32768) {
168136274Srwatson		fprintf(stderr, "payloadsize > 32768\n");
169136274Srwatson		return (-1);
170227345Scognet		/*NOTREACHED*/
171136274Srwatson	}
172136274Srwatson
173136274Srwatson	duration = strtoul(argv[4], &dummy, 10);
174227345Scognet	if (duration < 0 || *dummy != '\0') {
175227345Scognet		fprintf(stderr, "Invalid duration time: %s\n", argv[4]);
176136274Srwatson		usage();
177227345Scognet		/*NOTREACHED*/
178227345Scognet	}
179136274Srwatson
180136274Srwatson	packet = malloc(payloadsize);
181136274Srwatson	if (packet == NULL) {
182136274Srwatson		perror("malloc");
183136274Srwatson		return (-1);
184227345Scognet		/*NOTREACHED*/
185136274Srwatson	}
186227345Scognet
187136274Srwatson	bzero(packet, payloadsize);
188227345Scognet	error = getaddrinfo(argv[1],argv[2], &hints, &res0);
189227345Scognet	if (error) {
190227345Scognet		perror(gai_strerror(error));
191136274Srwatson		return (-1);
192227345Scognet		/*NOTREACHED*/
193136274Srwatson	}
194227345Scognet	s = -1;
195227345Scognet	for (res = res0; res; res = res->ai_next) {
196227345Scognet		s = socket(res->ai_family, res->ai_socktype, 0);
197227345Scognet		if (s < 0) {
198227345Scognet			cause = "socket";
199227345Scognet			continue;
200227345Scognet		}
201136274Srwatson
202227345Scognet		if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
203227345Scognet			cause = "connect";
204227345Scognet			close(s);
205227345Scognet			s = -1;
206227345Scognet			continue;
207227345Scognet		}
208227345Scognet
209227345Scognet		break;  /* okay we got one */
210227345Scognet	}
211227345Scognet	if (s < 0) {
212227345Scognet		perror(cause);
213136274Srwatson		return (-1);
214227345Scognet		/*NOTREACHED*/
215136274Srwatson	}
216136274Srwatson
217227345Scognet	freeaddrinfo(res0);
218227345Scognet
219136274Srwatson	return (blast_loop(s, duration, packet, payloadsize));
220227345Scognet
221136274Srwatson}
222