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