1 /*- 2 * Copyright (c) 2012 Cisco Systems, Inc. 3 * All rights reserved. 4 * 5 * This software was developed by Bjoern Zeeb under contract to 6 * Cisco Systems, Inc.. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 #include <sys/socket.h> 33 #include <sys/types.h> 34 35 #include <arpa/inet.h> 36 37 #include <netinet/in.h> 38 39 40 #include <err.h> 41 #include <errno.h> 42 #include <limits.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <sysexits.h> 47 #include <unistd.h> 48 49 static char *testcase; 50 static int accepts; 51 static int debug; 52 static u_int fib = -1; 53 static u_int reflectfib = -1; 54 static uint16_t port = 6666; 55 static char *addr; 56 static int nostart; 57 58 static int 59 reflect_conn(int s, char *buf, size_t buflen, ssize_t l, struct sockaddr *sa, 60 socklen_t salen) 61 { 62 ssize_t m; 63 64 if (l == -1) 65 err(EX_OSERR, "read()"); 66 if (l == 0) 67 errx(EX_NOINPUT, "EOF"); 68 if ((size_t)l > (buflen - 1)) 69 errx(EX_DATAERR, "Input too long"); 70 /* Nuke the \n from echo | netcat. */ 71 buf[l-1] = '\0'; 72 73 /* 74 * Match three cases: (1) START, (2) DONE, (3) anything else. 75 * For anything but START and DONE we just reflect everything. 76 */ 77 /* 78 * We expected a "START testcase" on first connect. Otherwise it means 79 * that we are out of sync. Exit to not produce weird results. 80 */ 81 if (accepts == 0 && nostart == 0) { 82 if (strncmp(buf, "START ", 6) != 0) 83 errx(EX_PROTOCOL, "Not received START on first " 84 "connect: %s", buf); 85 if (l < 8) 86 errx(EX_PROTOCOL, "START without test case name: %s", 87 buf); 88 if (strcmp(buf+6, testcase) != 0) 89 errx(EX_PROTOCOL, "START test case does not match " 90 "'%s': '%s'", testcase, buf+6); 91 } 92 /* If debug is on, log. */ 93 if (debug > 0) 94 fprintf(stderr, "<< %s: %s\n", testcase, buf); 95 96 if (reflectfib != (u_int)-1) 97 l = snprintf(buf, buflen, "FIB %u\n", reflectfib); 98 99 /* If debug is on, log. */ 100 if (debug > 0) { 101 buf[l-1] = '\0'; 102 fprintf(stderr, ">> %s: %s\n", testcase, buf); 103 } 104 105 /* Reflect data with \n again. */ 106 buf[l-1] = '\n'; 107 108 if (sa != NULL) { 109 m = sendto(s, buf, l, 0, sa, salen); 110 } else 111 m = write(s, buf, l); 112 /* XXX This is simplified handling. */ 113 if (m == -1 && sa != NULL && errno == EHOSTUNREACH) 114 warn("ignored expected: sendto(%s, %zd)", buf, l); 115 else if (m == -1 && (sa == NULL || errno != EHOSTUNREACH)) 116 err(EX_OSERR, "write(%s, %zd)", buf, l); 117 else if (m != l) 118 err(EX_OSERR, "short write(%s, %zd) %zd", buf, l, m); 119 120 121 accepts++; 122 123 /* See if we got an end signal. */ 124 if (strncmp(buf, "DONE", 4) == 0) 125 return (-2); 126 return (0); 127 } 128 129 static int 130 reflect_tcp6_conn(int as) 131 { 132 char buf[1500]; 133 ssize_t l; 134 int error, s; 135 136 s = accept(as, NULL, NULL); 137 if (s == -1) 138 err(EX_OSERR, "accept()"); 139 140 l = read(s, buf, sizeof(buf)); 141 error = reflect_conn(s, buf, sizeof(buf), l, NULL, 0); 142 close(s); 143 144 return (error); 145 } 146 147 static int 148 reflect_udp6_conn(int s) 149 { 150 char buf[1500]; 151 struct sockaddr_in6 from; 152 socklen_t fromlen; 153 ssize_t l; 154 int error; 155 156 fromlen = sizeof(from); 157 l = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&from, 158 &fromlen); 159 #if 0 160 if (l != -1) { 161 rc = connect(s, (struct sockaddr *)&from, fromlen); 162 if (rc == -1) { 163 if (inet_ntop(PF_INET6, &from, buf, sizeof(buf)) == NULL) 164 buf[0] = '\0'; 165 err(EX_OSERR, "connect(%d, %s, %u)", s, buf, fromlen); 166 } 167 } 168 #endif 169 error = reflect_conn(s, buf, sizeof(buf), l, (struct sockaddr *)&from, 170 fromlen); 171 #if 0 172 if (l != -1) { 173 /* Undo the connect binding again. */ 174 fromlen = sizeof(from); 175 bzero(&from, fromlen); 176 from.sin6_len = fromlen; 177 from.sin6_family = AF_INET6; 178 from.sin6_port = htons(port); /* This only gives us a ::1:port ::1:port binding */ 179 rc = connect(s, (struct sockaddr *)&from, fromlen); 180 if (rc == -1) { 181 if (inet_ntop(PF_INET6, &from.sin6_addr, buf, 182 sizeof(buf)) == NULL) 183 buf[0] = '\0'; 184 err(EX_OSERR, "un-connect(%d, %s, %u)", s, buf, fromlen); 185 } 186 } 187 #endif 188 189 return (error); 190 } 191 192 static int 193 reflect_6(int domain, int type) 194 { 195 struct sockaddr_in6 sin6; 196 fd_set rset; 197 int i, rc, s; 198 199 /* Get us a listen socket. */ 200 s = socket(domain, type, 0); 201 if (s == -1) 202 err(EX_OSERR, "socket()"); 203 204 /* 205 * In case a FIB was given on cmd line, set it. Let the kernel do the 206 * the bounds check. 207 */ 208 if (fib != (u_int)-1) { 209 rc = setsockopt(s, SOL_SOCKET, SO_SETFIB, &fib, sizeof(fib)); 210 if (rc == -1) 211 err(EX_OSERR, "setsockopt(SO_SETFIB)"); 212 } 213 214 /* Allow re-use. Otherwise restarting for the next test might error. */ 215 i = 1; 216 rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); 217 if (rc == -1) 218 err(EX_OSERR, "setsockopt(SO_REUSEADDR)"); 219 i = 1; 220 rc = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &i, sizeof(i)); 221 if (rc == -1) 222 err(EX_OSERR, "setsockopt(SO_REUSEPORT)"); 223 224 /* Bind address and port or just port. */ 225 sin6.sin6_len = sizeof(sin6); 226 sin6.sin6_family = AF_INET6; 227 sin6.sin6_port = htons(port); 228 sin6.sin6_flowinfo = 0; 229 bzero(&sin6.sin6_addr, sizeof(sin6.sin6_addr)); 230 if (addr != NULL) { 231 rc = inet_pton(PF_INET6, addr, &sin6.sin6_addr); 232 if (rc == 0) 233 errx(EX_USAGE, "inet_pton()"); 234 else if (rc == -1) 235 err(EX_OSERR, "inet_pton()"); 236 else if (rc != 1) 237 errx(EX_SOFTWARE, "inet_pton()"); 238 } 239 sin6.sin6_scope_id = 0; 240 rc = bind(s, (struct sockaddr *)&sin6, sizeof(sin6)); 241 if (rc == -1) 242 err(EX_OSERR, "bind(%d)", s); 243 244 if (type == SOCK_STREAM) { 245 rc = listen(s, port); 246 if (rc == -1) 247 err(EX_OSERR, "listen(%d, %u)", s, port); 248 } 249 250 /* 251 * We shall never do more than one connection in parallel so can keep 252 * it simple. 253 */ 254 do { 255 FD_ZERO(&rset); 256 FD_SET(s, &rset); 257 rc = select(s + 1, &rset, NULL, NULL, NULL); 258 if (rc == -1 && errno != EINTR) 259 err(EX_OSERR, "select()"); 260 261 if (rc == 0 || errno == EINTR) 262 continue; 263 264 if (rc != 1) 265 errx(EX_OSERR, "select() miscounted 1 to %d", rc); 266 if (!FD_ISSET(s, &rset)) 267 errx(EX_OSERR, "select() did not return our socket"); 268 269 if (type == SOCK_STREAM) 270 rc = reflect_tcp6_conn(s); 271 else if (type == SOCK_DGRAM) 272 rc = reflect_udp6_conn(s); 273 else 274 errx(EX_SOFTWARE, "Unsupported socket type %d", type); 275 } while (rc == 0); 276 /* Turn end flagging into no error. */ 277 if (rc == -2) 278 rc = 0; 279 280 /* Close listen socket. */ 281 close(s); 282 283 return (rc); 284 } 285 286 static int 287 reflect_tcp6(void) 288 { 289 290 return (reflect_6(PF_INET6, SOCK_STREAM)); 291 } 292 293 static int 294 reflect_udp6(void) 295 { 296 297 return (reflect_6(PF_INET6, SOCK_DGRAM)); 298 } 299 300 int 301 main(int argc, char *argv[]) 302 { 303 long long l; 304 char *dummy, *afname; 305 int ch, rc; 306 307 afname = NULL; 308 while ((ch = getopt(argc, argv, "A:dF:f:Np:t:T:")) != -1) { 309 switch (ch) { 310 case 'A': 311 addr = optarg; 312 break; 313 case 'd': 314 debug++; 315 break; 316 case 'F': 317 l = strtoll(optarg, &dummy, 10); 318 if (*dummy != '\0' || l < 0) 319 errx(EX_USAGE, "Invalid FIB number"); 320 fib = (u_int)l; 321 break; 322 case 'f': 323 l = strtoll(optarg, &dummy, 10); 324 if (*dummy != '\0' || l < 0) 325 errx(EX_USAGE, "Invalid FIB number"); 326 reflectfib = (u_int)l; 327 break; 328 case 'N': 329 nostart=1; 330 break; 331 case 'p': 332 l = strtoll(optarg, &dummy, 10); 333 if (*dummy != '\0' || l < 0) 334 errx(EX_USAGE, "Invalid port number"); 335 port = (uint16_t)l; 336 break; 337 case 't': 338 testcase = optarg; 339 break; 340 case 'T': 341 afname = optarg; 342 break; 343 case '?': 344 default: 345 errx(EX_USAGE, "Unknown command line option at '%c'", 346 optopt); 347 /* NOTREACHED */ 348 } 349 } 350 351 if (testcase == NULL) 352 errx(EX_USAGE, "Mandatory option -t <testcase> not given"); 353 if (afname == NULL) 354 errx(EX_USAGE, "Mandatory option -T <afname> not given"); 355 356 if (strcmp(afname, "TCP6") == 0) 357 rc = reflect_tcp6(); 358 else if (strcmp(afname, "UDP6") == 0) 359 rc = reflect_udp6(); 360 else 361 errx(EX_USAGE, "Mandatory option -T %s not a valid option", 362 afname); 363 364 return (rc); 365 } 366