netblast.c revision 225334
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: head/tools/tools/netrate/netblast/netblast.c 225334 2011-09-02 16:40:18Z marius $ 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 36#include <arpa/inet.h> 37 38#include <signal.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42 43static void 44usage(void) 45{ 46 47 fprintf(stderr, "netblast [ip] [port] [payloadsize] [duration]\n"); 48 exit(-1); 49} 50 51static int global_stop_flag; 52 53static void 54signal_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 */ 67static int 68blast_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 141int 142main(int argc, char *argv[]) 143{ 144 long payloadsize, port, duration; 145 struct sockaddr_in sin; 146 char *dummy, *packet; 147 int s; 148 149 if (argc != 5) 150 usage(); 151 152 bzero(&sin, sizeof(sin)); 153 sin.sin_len = sizeof(sin); 154 sin.sin_family = AF_INET; 155 if (inet_aton(argv[1], &sin.sin_addr) == 0) { 156 perror(argv[1]); 157 return (-1); 158 } 159 160 port = strtoul(argv[2], &dummy, 10); 161 if (port < 1 || port > 65535 || *dummy != '\0') 162 usage(); 163 sin.sin_port = htons(port); 164 165 payloadsize = strtoul(argv[3], &dummy, 10); 166 if (payloadsize < 0 || *dummy != '\0') 167 usage(); 168 if (payloadsize > 32768) { 169 fprintf(stderr, "payloadsize > 32768\n"); 170 return (-1); 171 } 172 173 duration = strtoul(argv[4], &dummy, 10); 174 if (duration < 0 || *dummy != '\0') 175 usage(); 176 177 packet = malloc(payloadsize); 178 if (packet == NULL) { 179 perror("malloc"); 180 return (-1); 181 } 182 bzero(packet, payloadsize); 183 184 s = socket(PF_INET, SOCK_DGRAM, 0); 185 if (s == -1) { 186 perror("socket"); 187 return (-1); 188 } 189 190 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 191 perror("connect"); 192 return (-1); 193 } 194 195 return (blast_loop(s, duration, packet, payloadsize)); 196} 197