reflect.c revision 269097
1231858Sbz/*- 2231858Sbz * Copyright (c) 2012 Cisco Systems, Inc. 3231858Sbz * All rights reserved. 4231858Sbz * 5231858Sbz * This software was developed by Bjoern Zeeb under contract to 6231858Sbz * Cisco Systems, Inc.. 7231858Sbz * 8231858Sbz * Redistribution and use in source and binary forms, with or without 9231858Sbz * modification, are permitted provided that the following conditions 10231858Sbz * are met: 11231858Sbz * 1. Redistributions of source code must retain the above copyright 12231858Sbz * notice, this list of conditions and the following disclaimer. 13231858Sbz * 2. Redistributions in binary form must reproduce the above copyright 14231858Sbz * notice, this list of conditions and the following disclaimer in the 15231858Sbz * documentation and/or other materials provided with the distribution. 16231858Sbz * 17231858Sbz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18231858Sbz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19231858Sbz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20231858Sbz * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21231858Sbz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22231858Sbz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23231858Sbz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24231858Sbz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25231858Sbz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26231858Sbz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27231858Sbz * SUCH DAMAGE. 28231858Sbz * 29231858Sbz * $FreeBSD: head/tools/test/netfibs/reflect.c 269097 2014-07-25 20:49:59Z delphij $ 30231858Sbz */ 31231858Sbz 32231858Sbz#include <sys/socket.h> 33231858Sbz#include <sys/types.h> 34231858Sbz 35231858Sbz#include <arpa/inet.h> 36231858Sbz 37231858Sbz#include <netinet/in.h> 38231858Sbz 39231858Sbz 40231858Sbz#include <err.h> 41231858Sbz#include <errno.h> 42231858Sbz#include <limits.h> 43231858Sbz#include <stdio.h> 44231858Sbz#include <stdlib.h> 45231858Sbz#include <string.h> 46231858Sbz#include <sysexits.h> 47231858Sbz#include <unistd.h> 48231858Sbz 49231858Sbzstatic char *testcase; 50231858Sbzstatic int accepts; 51231858Sbzstatic int debug; 52231858Sbzstatic u_int fib = -1; 53231858Sbzstatic u_int reflectfib = -1; 54231858Sbzstatic uint16_t port = 6666; 55231858Sbzstatic char *addr; 56231858Sbzstatic int nostart; 57231858Sbz 58231858Sbzstatic int 59231858Sbzreflect_conn(int s, char *buf, size_t buflen, ssize_t l, struct sockaddr *sa, 60231858Sbz socklen_t salen) 61231858Sbz{ 62231858Sbz ssize_t m; 63231858Sbz 64231858Sbz if (l == -1) 65231858Sbz err(EX_OSERR, "read()"); 66231858Sbz if (l == 0) 67231858Sbz errx(EX_NOINPUT, "EOF"); 68231858Sbz if ((size_t)l > (buflen - 1)) 69231858Sbz errx(EX_DATAERR, "Input too long"); 70231858Sbz /* Nuke the \n from echo | netcat. */ 71231858Sbz buf[l-1] = '\0'; 72231858Sbz 73231858Sbz /* 74231858Sbz * Match three cases: (1) START, (2) DONE, (3) anything else. 75231858Sbz * For anything but START and DONE we just reflect everything. 76231858Sbz */ 77231858Sbz /* 78231858Sbz * We expected a "START testcase" on first connect. Otherwise it means 79231858Sbz * that we are out of sync. Exit to not produce weird results. 80231858Sbz */ 81231858Sbz if (accepts == 0 && nostart == 0) { 82231858Sbz if (strncmp(buf, "START ", 6) != 0) 83231858Sbz errx(EX_PROTOCOL, "Not received START on first " 84231858Sbz "connect: %s", buf); 85231858Sbz if (l < 8) 86231858Sbz errx(EX_PROTOCOL, "START without test case name: %s", 87231858Sbz buf); 88231858Sbz if (strcmp(buf+6, testcase) != 0) 89231858Sbz errx(EX_PROTOCOL, "START test case does not match " 90231858Sbz "'%s': '%s'", testcase, buf+6); 91231858Sbz } 92231858Sbz /* If debug is on, log. */ 93231858Sbz if (debug > 0) 94231858Sbz fprintf(stderr, "<< %s: %s\n", testcase, buf); 95231858Sbz 96231858Sbz if (reflectfib != (u_int)-1) 97269097Sdelphij l = snprintf(buf, buflen, "FIB %u\n", reflectfib); 98231858Sbz 99231858Sbz /* If debug is on, log. */ 100231858Sbz if (debug > 0) { 101231858Sbz buf[l-1] = '\0'; 102231858Sbz fprintf(stderr, ">> %s: %s\n", testcase, buf); 103231858Sbz } 104231858Sbz 105231858Sbz /* Reflect data with \n again. */ 106231858Sbz buf[l-1] = '\n'; 107231858Sbz 108231858Sbz if (sa != NULL) { 109231858Sbz m = sendto(s, buf, l, 0, sa, salen); 110231858Sbz } else 111231858Sbz m = write(s, buf, l); 112231858Sbz /* XXX This is simplified handling. */ 113231858Sbz if (m == -1 && sa != NULL && errno == EHOSTUNREACH) 114231858Sbz warn("ignored expected: sendto(%s, %zd)", buf, l); 115231858Sbz else if (m == -1 && (sa == NULL || errno != EHOSTUNREACH)) 116231858Sbz err(EX_OSERR, "write(%s, %zd)", buf, l); 117231858Sbz else if (m != l) 118231858Sbz err(EX_OSERR, "short write(%s, %zd) %zd", buf, l, m); 119231858Sbz 120231858Sbz 121231858Sbz accepts++; 122231858Sbz 123231858Sbz /* See if we got an end signal. */ 124231858Sbz if (strncmp(buf, "DONE", 4) == 0) 125231858Sbz return (-2); 126231858Sbz return (0); 127231858Sbz} 128231858Sbz 129231858Sbzstatic int 130231858Sbzreflect_tcp6_conn(int as) 131231858Sbz{ 132231858Sbz char buf[1500]; 133231858Sbz ssize_t l; 134231858Sbz int error, s; 135231858Sbz 136231858Sbz s = accept(as, NULL, NULL); 137231858Sbz if (s == -1) 138231858Sbz err(EX_OSERR, "accept()"); 139231858Sbz 140231858Sbz l = read(s, buf, sizeof(buf)); 141231858Sbz error = reflect_conn(s, buf, sizeof(buf), l, NULL, 0); 142231858Sbz close(s); 143231858Sbz 144231858Sbz return (error); 145231858Sbz} 146231858Sbz 147231858Sbzstatic int 148231858Sbzreflect_udp6_conn(int s) 149231858Sbz{ 150231858Sbz char buf[1500]; 151231858Sbz struct sockaddr_in6 from; 152231858Sbz socklen_t fromlen; 153231858Sbz ssize_t l; 154231858Sbz int error; 155231858Sbz 156231858Sbz fromlen = sizeof(from); 157231858Sbz l = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&from, 158231858Sbz &fromlen); 159231858Sbz#if 0 160231858Sbz if (l != -1) { 161231858Sbz rc = connect(s, (struct sockaddr *)&from, fromlen); 162231858Sbz if (rc == -1) { 163231858Sbz if (inet_ntop(PF_INET6, &from, buf, sizeof(buf)) == NULL) 164231858Sbz buf[0] = '\0'; 165231858Sbz err(EX_OSERR, "connect(%d, %s, %u)", s, buf, fromlen); 166231858Sbz } 167231858Sbz } 168231858Sbz#endif 169231858Sbz error = reflect_conn(s, buf, sizeof(buf), l, (struct sockaddr *)&from, 170231858Sbz fromlen); 171231858Sbz#if 0 172231858Sbz if (l != -1) { 173231858Sbz /* Undo the connect binding again. */ 174231858Sbz fromlen = sizeof(from); 175231858Sbz bzero(&from, fromlen); 176231858Sbz from.sin6_len = fromlen; 177231858Sbz from.sin6_family = AF_INET6; 178231858Sbz from.sin6_port = htons(port); /* This only gives us a ::1:port ::1:port binding */ 179231858Sbz rc = connect(s, (struct sockaddr *)&from, fromlen); 180231858Sbz if (rc == -1) { 181231858Sbz if (inet_ntop(PF_INET6, &from.sin6_addr, buf, 182231858Sbz sizeof(buf)) == NULL) 183231858Sbz buf[0] = '\0'; 184231858Sbz err(EX_OSERR, "un-connect(%d, %s, %u)", s, buf, fromlen); 185231858Sbz } 186231858Sbz } 187231858Sbz#endif 188231858Sbz 189231858Sbz return (error); 190231858Sbz} 191231858Sbz 192231858Sbzstatic int 193231858Sbzreflect_6(int domain, int type) 194231858Sbz{ 195231858Sbz struct sockaddr_in6 sin6; 196231858Sbz fd_set rset; 197231858Sbz int i, rc, s; 198231858Sbz 199231858Sbz /* Get us a listen socket. */ 200231858Sbz s = socket(domain, type, 0); 201231858Sbz if (s == -1) 202231858Sbz err(EX_OSERR, "socket()"); 203231858Sbz 204231858Sbz /* 205231858Sbz * In case a FIB was given on cmd line, set it. Let the kernel do the 206231858Sbz * the bounds check. 207231858Sbz */ 208231858Sbz if (fib != (u_int)-1) { 209231858Sbz rc = setsockopt(s, SOL_SOCKET, SO_SETFIB, &fib, sizeof(fib)); 210231858Sbz if (rc == -1) 211231858Sbz err(EX_OSERR, "setsockopt(SO_SETFIB)"); 212231858Sbz } 213231858Sbz 214231858Sbz /* Allow re-use. Otherwise restarting for the next test might error. */ 215231858Sbz i = 1; 216231858Sbz rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); 217231858Sbz if (rc == -1) 218231858Sbz err(EX_OSERR, "setsockopt(SO_REUSEADDR)"); 219231858Sbz i = 1; 220231858Sbz rc = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &i, sizeof(i)); 221231858Sbz if (rc == -1) 222231858Sbz err(EX_OSERR, "setsockopt(SO_REUSEPORT)"); 223231858Sbz 224231858Sbz /* Bind address and port or just port. */ 225231858Sbz sin6.sin6_len = sizeof(sin6); 226231858Sbz sin6.sin6_family = AF_INET6; 227231858Sbz sin6.sin6_port = htons(port); 228231858Sbz sin6.sin6_flowinfo = 0; 229231858Sbz bzero(&sin6.sin6_addr, sizeof(sin6.sin6_addr)); 230231858Sbz if (addr != NULL) { 231231858Sbz rc = inet_pton(PF_INET6, addr, &sin6.sin6_addr); 232231858Sbz if (rc == 0) 233231858Sbz errx(EX_USAGE, "inet_pton()"); 234231858Sbz else if (rc == -1) 235231858Sbz err(EX_OSERR, "inet_pton()"); 236231858Sbz else if (rc != 1) 237231858Sbz errx(EX_SOFTWARE, "inet_pton()"); 238231858Sbz } 239231858Sbz sin6.sin6_scope_id = 0; 240231858Sbz rc = bind(s, (struct sockaddr *)&sin6, sizeof(sin6)); 241231858Sbz if (rc == -1) 242231858Sbz err(EX_OSERR, "bind(%d)", s); 243231858Sbz 244231858Sbz if (type == SOCK_STREAM) { 245231858Sbz rc = listen(s, port); 246231858Sbz if (rc == -1) 247231858Sbz err(EX_OSERR, "listen(%d, %u)", s, port); 248231858Sbz } 249231858Sbz 250231858Sbz /* 251231858Sbz * We shall never do more than one connection in parallel so can keep 252231858Sbz * it simple. 253231858Sbz */ 254231858Sbz do { 255231858Sbz FD_ZERO(&rset); 256231858Sbz FD_SET(s, &rset); 257231858Sbz rc = select(s + 1, &rset, NULL, NULL, NULL); 258231858Sbz if (rc == -1 && errno != EINTR) 259231858Sbz err(EX_OSERR, "select()"); 260231858Sbz 261231858Sbz if (rc == 0 || errno == EINTR) 262231858Sbz continue; 263231858Sbz 264231858Sbz if (rc != 1) 265231858Sbz errx(EX_OSERR, "select() miscounted 1 to %d", rc); 266231858Sbz if (!FD_ISSET(s, &rset)) 267231858Sbz errx(EX_OSERR, "select() did not return our socket"); 268231858Sbz 269231858Sbz if (type == SOCK_STREAM) 270231858Sbz rc = reflect_tcp6_conn(s); 271231858Sbz else if (type == SOCK_DGRAM) 272231858Sbz rc = reflect_udp6_conn(s); 273231858Sbz else 274231858Sbz errx(EX_SOFTWARE, "Unsupported socket type %d", type); 275231858Sbz } while (rc == 0); 276231858Sbz /* Turn end flagging into no error. */ 277231858Sbz if (rc == -2) 278231858Sbz rc = 0; 279231858Sbz 280231858Sbz /* Close listen socket. */ 281231858Sbz close(s); 282231858Sbz 283231858Sbz return (rc); 284231858Sbz} 285231858Sbz 286231858Sbzstatic int 287231858Sbzreflect_tcp6(void) 288231858Sbz{ 289231858Sbz 290231858Sbz return (reflect_6(PF_INET6, SOCK_STREAM)); 291231858Sbz} 292231858Sbz 293231858Sbzstatic int 294231858Sbzreflect_udp6(void) 295231858Sbz{ 296231858Sbz 297231858Sbz return (reflect_6(PF_INET6, SOCK_DGRAM)); 298231858Sbz} 299231858Sbz 300231858Sbzint 301231858Sbzmain(int argc, char *argv[]) 302231858Sbz{ 303231858Sbz long long l; 304231858Sbz char *dummy, *afname; 305231858Sbz int ch, rc; 306231858Sbz 307231858Sbz afname = NULL; 308231858Sbz while ((ch = getopt(argc, argv, "A:dF:f:Np:t:T:")) != -1) { 309231858Sbz switch (ch) { 310231858Sbz case 'A': 311231858Sbz addr = optarg; 312231858Sbz break; 313231858Sbz case 'd': 314231858Sbz debug++; 315231858Sbz break; 316231858Sbz case 'F': 317231858Sbz l = strtoll(optarg, &dummy, 10); 318231858Sbz if (*dummy != '\0' || l < 0) 319231858Sbz errx(EX_USAGE, "Invalid FIB number"); 320231858Sbz fib = (u_int)l; 321231858Sbz break; 322231858Sbz case 'f': 323231858Sbz l = strtoll(optarg, &dummy, 10); 324231858Sbz if (*dummy != '\0' || l < 0) 325231858Sbz errx(EX_USAGE, "Invalid FIB number"); 326231858Sbz reflectfib = (u_int)l; 327231858Sbz break; 328231858Sbz case 'N': 329231858Sbz nostart=1; 330231858Sbz break; 331231858Sbz case 'p': 332231858Sbz l = strtoll(optarg, &dummy, 10); 333231858Sbz if (*dummy != '\0' || l < 0) 334231858Sbz errx(EX_USAGE, "Invalid port number"); 335231858Sbz port = (uint16_t)l; 336231858Sbz break; 337231858Sbz case 't': 338231858Sbz testcase = optarg; 339231858Sbz break; 340231858Sbz case 'T': 341231858Sbz afname = optarg; 342231858Sbz break; 343231858Sbz case '?': 344231858Sbz default: 345231858Sbz errx(EX_USAGE, "Unknown command line option at '%c'", 346231858Sbz optopt); 347231858Sbz /* NOTREACHED */ 348231858Sbz } 349231858Sbz } 350231858Sbz 351231858Sbz if (testcase == NULL) 352231858Sbz errx(EX_USAGE, "Mandatory option -t <testcase> not given"); 353231858Sbz if (afname == NULL) 354231858Sbz errx(EX_USAGE, "Mandatory option -T <afname> not given"); 355231858Sbz 356231858Sbz if (strcmp(afname, "TCP6") == 0) 357231858Sbz rc = reflect_tcp6(); 358231858Sbz else if (strcmp(afname, "UDP6") == 0) 359231858Sbz rc = reflect_udp6(); 360231858Sbz else 361231858Sbz errx(EX_USAGE, "Mandatory option -T %s not a valid option", 362231858Sbz afname); 363231858Sbz 364231858Sbz return (rc); 365231858Sbz} 366