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 27 #include <sys/endian.h> 28 #include <sys/types.h> 29 #include <sys/socket.h> 30 #include <sys/time.h> 31 32 #include <netinet/in.h> 33 #include <netdb.h> /* getaddrinfo */ 34 35 #include <signal.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> /* close */ 40 41 static void 42 usage(void) 43 { 44 45 fprintf(stderr, "netblast [ip] [port] [payloadsize] [duration]\n"); 46 exit(-1); 47 } 48 49 static int global_stop_flag; 50 51 static void 52 signal_handler(int signum __unused) 53 { 54 55 global_stop_flag = 1; 56 } 57 58 /* 59 * Loop that blasts packets: begin by recording time information, resetting 60 * stats. Set the interval timer for when we want to wake up. Then go. 61 * SIGALRM will set a flag indicating it's time to stop. Note that there's 62 * some overhead to the signal and timer setup, so the smaller the duration, 63 * the higher the relative overhead. 64 */ 65 static int 66 blast_loop(int s, long duration, u_char *packet, u_int packet_len) 67 { 68 struct timespec starttime, tmptime; 69 struct itimerval it; 70 u_int32_t counter; 71 int send_errors, send_calls; 72 73 if (signal(SIGALRM, signal_handler) == SIG_ERR) { 74 perror("signal"); 75 return (-1); 76 } 77 78 if (clock_getres(CLOCK_REALTIME, &tmptime) == -1) { 79 perror("clock_getres"); 80 return (-1); 81 } 82 83 if (clock_gettime(CLOCK_REALTIME, &starttime) == -1) { 84 perror("clock_gettime"); 85 return (-1); 86 } 87 88 it.it_interval.tv_sec = 0; 89 it.it_interval.tv_usec = 0; 90 it.it_value.tv_sec = duration; 91 it.it_value.tv_usec = 0; 92 93 if (setitimer(ITIMER_REAL, &it, NULL) < 0) { 94 perror("setitimer"); 95 return (-1); 96 } 97 98 send_errors = send_calls = 0; 99 counter = 0; 100 while (global_stop_flag == 0) { 101 /* 102 * We maintain and, if there's room, send a counter. Note 103 * that even if the error is purely local, we still increment 104 * the counter, so missing sequence numbers on the receive 105 * side should not be assumed to be packets lost in transit. 106 * For example, if the UDP socket gets back an ICMP from a 107 * previous send, the error will turn up the current send 108 * operation, causing the current sequence number also to be 109 * skipped. 110 */ 111 if (packet_len >= 4) { 112 be32enc(packet, counter); 113 counter++; 114 } 115 if (send(s, packet, packet_len, 0) < 0) 116 send_errors++; 117 send_calls++; 118 } 119 120 if (clock_gettime(CLOCK_REALTIME, &tmptime) == -1) { 121 perror("clock_gettime"); 122 return (-1); 123 } 124 125 printf("\n"); 126 printf("start: %zd.%09lu\n", starttime.tv_sec, 127 starttime.tv_nsec); 128 printf("finish: %zd.%09lu\n", tmptime.tv_sec, 129 tmptime.tv_nsec); 130 printf("send calls: %d\n", send_calls); 131 printf("send errors: %d\n", send_errors); 132 printf("approx send rate: %ld\n", (send_calls - send_errors) / 133 duration); 134 printf("approx error rate: %d\n", (send_errors / send_calls)); 135 136 return (0); 137 } 138 139 int 140 main(int argc, char *argv[]) 141 { 142 long payloadsize, duration; 143 struct addrinfo hints, *res, *res0; 144 char *dummy, *packet; 145 int port, s, error; 146 const char *cause = NULL; 147 148 if (argc != 5) 149 usage(); 150 151 memset(&hints, 0, sizeof(hints)); 152 hints.ai_family = PF_UNSPEC; 153 hints.ai_socktype = SOCK_DGRAM; 154 155 port = strtoul(argv[2], &dummy, 10); 156 if (port < 1 || port > 65535 || *dummy != '\0') { 157 fprintf(stderr, "Invalid port number: %s\n", argv[2]); 158 usage(); 159 /*NOTREACHED*/ 160 } 161 162 payloadsize = strtoul(argv[3], &dummy, 10); 163 if (payloadsize < 0 || *dummy != '\0') 164 usage(); 165 if (payloadsize > 32768) { 166 fprintf(stderr, "payloadsize > 32768\n"); 167 return (-1); 168 /*NOTREACHED*/ 169 } 170 171 duration = strtoul(argv[4], &dummy, 10); 172 if (duration < 0 || *dummy != '\0') { 173 fprintf(stderr, "Invalid duration time: %s\n", argv[4]); 174 usage(); 175 /*NOTREACHED*/ 176 } 177 178 packet = malloc(payloadsize); 179 if (packet == NULL) { 180 perror("malloc"); 181 return (-1); 182 /*NOTREACHED*/ 183 } 184 185 bzero(packet, payloadsize); 186 error = getaddrinfo(argv[1],argv[2], &hints, &res0); 187 if (error) { 188 perror(gai_strerror(error)); 189 return (-1); 190 /*NOTREACHED*/ 191 } 192 s = -1; 193 for (res = res0; res; res = res->ai_next) { 194 s = socket(res->ai_family, res->ai_socktype, 0); 195 if (s < 0) { 196 cause = "socket"; 197 continue; 198 } 199 200 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { 201 cause = "connect"; 202 close(s); 203 s = -1; 204 continue; 205 } 206 207 break; /* okay we got one */ 208 } 209 if (s < 0) { 210 perror(cause); 211 return (-1); 212 /*NOTREACHED*/ 213 } 214 215 freeaddrinfo(res0); 216 217 return (blast_loop(s, duration, packet, payloadsize)); 218 219 } 220