1 /* $NetBSD: kttcp.c,v 1.5 2002/07/11 23:32:35 simonb Exp $ */ 2 3 /* 4 * Copyright (c) 2002 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Frank van der Linden and Jason R. Thorpe 8 * for Wasabi Systems, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed for the NetBSD Project by 21 * Wasabi Systems, Inc. 22 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 23 * or promote products derived from this software without specific prior 24 * written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/types.h> 40 #include <sys/param.h> 41 #include <sys/time.h> 42 #include <sys/resource.h> 43 #include <sys/socket.h> 44 #include <sys/ioctl.h> 45 #include <errno.h> 46 #include <netdb.h> 47 #include <unistd.h> 48 #include <stdio.h> 49 #include <err.h> 50 #include <fcntl.h> 51 #include <stdlib.h> 52 #include <limits.h> 53 #include <string.h> 54 55 #include "kttcpio.h" 56 57 #define KTTCP_PORT "22222" 58 #define KTTCP_XMITSIZE (10*1024*1024) 59 #define KTTCP_SOCKBUF_DEFAULT 65536 60 61 #define KTTCP_DEVICE "/dev/kttcp" 62 63 static void 64 usage(void) 65 { 66 fprintf(stderr, 67 "usage: kttcp -r [-b sockbufsize] [-p port] [-q] [-v]\n" 68 " [-4] [-6]\n" 69 " kttcp -t [-b sockbufsize] [-n bytes] [-q] [-v] [-p port]\n" 70 " [-4] [-6] host\n" 71 ); 72 exit(1); 73 } 74 75 static unsigned long long 76 get_bytes(const char *str) 77 { 78 unsigned long long bytes; 79 char *cp; 80 81 bytes = strtoull(str, &cp, 10); 82 if (bytes == ULLONG_MAX && errno == ERANGE) 83 err(1, "%s", str); 84 85 if (cp[0] != '\0') { 86 if (cp[1] != '\0') 87 errx(1, "invalid byte count: %s", str); 88 if (cp[0] == 'k' || cp[0] == 'K') 89 bytes *= 1024; 90 else if (cp[0] == 'm' || cp[0] == 'M') 91 bytes *= 1024 * 1024; 92 else if (cp[0] == 'g' || cp[0] == 'G') 93 bytes *= 1024 * 1024 * 1024; 94 else 95 errx(1, "invalid byte count modifier: %s", str); 96 } 97 98 return (bytes); 99 } 100 101 int 102 main(int argc, char *argv[]) 103 { 104 int c, error, s, verbose, s2, kfd; 105 int xmitset, family; 106 int bufsize; 107 int ai_flag; 108 char *host; 109 char *portstr; 110 struct kttcp_io_args kio; 111 struct addrinfo hints, *addr, *res; 112 struct sockaddr_storage ss; 113 struct rusage rustart, ruend; 114 struct timeval tvtmp; 115 unsigned long long ull, usecs, bytespersec, bitspersec, xmitsize; 116 char connecthost[NI_MAXHOST]; 117 socklen_t slen; 118 const int one = 1; 119 u_long cmd; 120 121 cmd = 0; 122 portstr = KTTCP_PORT; 123 verbose = 1; 124 xmitset = 0; 125 bufsize = KTTCP_SOCKBUF_DEFAULT; 126 xmitsize = KTTCP_XMITSIZE; 127 family = PF_UNSPEC; 128 while ((c = getopt(argc, argv, "46b:n:p:qrtvw:")) != -1) { 129 switch (c) { 130 case '4': 131 if (family != PF_UNSPEC) 132 usage(); 133 family = PF_INET; 134 break; 135 case '6': 136 if (family != PF_UNSPEC) 137 usage(); 138 family = PF_INET6; 139 break; 140 case 'b': 141 ull = get_bytes(optarg); 142 if (ull > INT_MAX) 143 errx(1, 144 "invalid socket buffer size: %s\n", optarg); 145 bufsize = ull; 146 break; 147 case 'n': 148 xmitsize = get_bytes(optarg); 149 xmitset = 1; 150 break; 151 case 'p': 152 portstr = optarg; 153 break; 154 case 'q': 155 verbose = 0; 156 break; 157 case 'r': 158 if (cmd != 0) 159 usage(); 160 cmd = KTTCP_IO_RECV; 161 break; 162 case 't': 163 if (cmd != 0) 164 usage(); 165 cmd = KTTCP_IO_SEND; 166 break; 167 case 'v': 168 verbose = 2; 169 break; 170 case '?': 171 default: 172 usage(); 173 } 174 } 175 if (cmd == 0) 176 usage(); 177 178 argc -= optind; 179 argv += optind; 180 181 if (cmd == KTTCP_IO_SEND) { 182 if (xmitsize <= 0 || argc < 1) 183 usage(); 184 host = argv[0]; 185 ai_flag = 0; 186 } else { 187 if (xmitset == 0) 188 xmitsize = KTTCP_MAX_XMIT; 189 host = NULL; 190 ai_flag = AI_PASSIVE; 191 } 192 193 if ((kfd = open(KTTCP_DEVICE, O_RDWR, 666)) == -1) 194 err(2, "open %s", KTTCP_DEVICE); 195 196 memset(&hints, 0, sizeof hints); 197 hints.ai_flags = ai_flag; 198 hints.ai_socktype = SOCK_STREAM; 199 hints.ai_family = family; 200 error = getaddrinfo(host, portstr, &hints, &addr); 201 202 if (error != 0) 203 errx(2, "%s", gai_strerror(error)); 204 205 s = -1; 206 for (res = addr; res != NULL; res = res->ai_next) { 207 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 208 if (s >= 0) 209 break; 210 } 211 if (res == NULL) 212 err(2, "can't create socket"); 213 214 printf("kttcp: socket buffer size: %d\n", bufsize); 215 216 if (cmd == KTTCP_IO_SEND) { 217 if (connect(s, res->ai_addr, res->ai_addrlen) < 0) 218 err(2, "connect"); 219 if (verbose) { 220 getnameinfo(res->ai_addr, res->ai_addrlen, 221 connecthost, sizeof connecthost, NULL, 0, 222 NI_NUMERICHOST); 223 printf("kttcp: connected to %s\n", connecthost); 224 } 225 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof (int)) 226 < 0) 227 err(2, "setsockopt sndbuf"); 228 kio.kio_socket = s; 229 } else { 230 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, 231 sizeof (int)) < 0) 232 err(2, "setsockopt reuseaddr"); 233 if (bind(s, res->ai_addr, res->ai_addrlen) < 0) 234 err(2, "bind"); 235 if (listen(s, 1) < 0) 236 err(2, "listen"); 237 if (verbose) 238 printf("kttcp: listening on port %s\n", portstr); 239 slen = sizeof ss; 240 s2 = accept(s, (struct sockaddr *)&ss, &slen); 241 if (s2 < 0) 242 err(2, "accept"); 243 if (verbose) { 244 getnameinfo((struct sockaddr *)&ss, ss.ss_len, 245 connecthost, sizeof connecthost, NULL, 0, 246 NI_NUMERICHOST); 247 printf("kttcp: connect from %s\n", connecthost); 248 } 249 if (setsockopt(s2, SOL_SOCKET, SO_RCVBUF, &bufsize, 250 sizeof (int)) < 0) 251 err(2, "setsockopt rcvbuf"); 252 kio.kio_socket = s2; 253 } 254 255 kio.kio_totalsize = xmitsize; 256 257 getrusage(RUSAGE_SELF, &rustart); 258 if (ioctl(kfd, cmd, &kio) == -1) 259 err(2, "kttcp i/o command"); 260 getrusage(RUSAGE_SELF, &ruend); 261 262 usecs = (unsigned long long)kio.kio_elapsed.tv_sec * 1000000; 263 usecs += kio.kio_elapsed.tv_usec; 264 265 bytespersec = kio.kio_bytesdone * 1000000LL / usecs; 266 bitspersec = bytespersec * NBBY; 267 printf("kttcp: %llu bytes in %ld.%03ld real seconds ==> %llu bytes/sec\n", 268 kio.kio_bytesdone, kio.kio_elapsed.tv_sec, 269 kio.kio_elapsed.tv_usec / 1000, bytespersec); 270 if (verbose > 1) { 271 timersub(&ruend.ru_stime, &rustart.ru_stime, &tvtmp); 272 bytespersec = kio.kio_bytesdone * 1000000LL / 273 (tvtmp.tv_sec * 1000000ULL + tvtmp.tv_usec); 274 printf("kttcp: %llu bytes in %ld.%03ld CPU seconds ==> %llu bytes/CPU sec\n", 275 kio.kio_bytesdone, tvtmp.tv_sec, tvtmp.tv_usec / 1000, bytespersec); 276 } 277 printf(" %g (%g) Megabits/sec\n", 278 ((double) bitspersec / 1024.0) / 1024.0, 279 ((double) bitspersec / 1000.0) / 1000.0); 280 281 timersub(&ruend.ru_utime, &rustart.ru_utime, &tvtmp); 282 /* XXX 283 * sometimes, this ends up as -1 * hz!? 284 */ 285 if (tvtmp.tv_sec < 0) 286 tvtmp.tv_sec = tvtmp.tv_usec = 0; 287 printf(" %ld.%02lduser", tvtmp.tv_sec, tvtmp.tv_usec / 10000); 288 ull = tvtmp.tv_sec * 1000000ULL + tvtmp.tv_usec; 289 290 timersub(&ruend.ru_stime, &rustart.ru_stime, &tvtmp); 291 printf(" %ld.%02ldsys", tvtmp.tv_sec, tvtmp.tv_usec / 10000); 292 ull += tvtmp.tv_sec * 1000000ULL + tvtmp.tv_usec; 293 294 printf(" %lld.%lldreal", usecs / 1000000, (usecs % 1000000) / 10000); 295 printf(" %lld%%", ull * 100 / usecs); 296 printf("\n"); 297 298 299 close(kio.kio_socket); 300 if (cmd == KTTCP_IO_RECV) 301 close(s); 302 close(kfd); 303 freeaddrinfo(addr); 304 305 return 0; 306 } 307