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