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