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