Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tools/regression/netinet/msocket/msocket.c
39507 views
1
/*-
2
* Copyright (c) 2005 Robert N. M. Watson
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/types.h>
28
#include <sys/socket.h>
29
30
#include <netinet/in.h>
31
32
#include <arpa/inet.h>
33
34
#include <err.h>
35
#include <errno.h>
36
#include <fcntl.h>
37
#include <stdio.h>
38
#include <string.h>
39
#include <unistd.h>
40
41
/*
42
* Regression test for multicast sockets and options:
43
*
44
* - Check the defaults for ttl, if, and loopback. Make sure they can be set
45
* and then read.
46
*
47
* - Check that adding and removing multicast addresses seems to work.
48
*
49
* - Send a test message over loop back multicast and make sure it arrives.
50
*
51
* NB:
52
*
53
* Would be nice to use BPF or if_tap to actually check packet contents and
54
* layout, make sure that the ttl is set right, etc.
55
*
56
* Would be nice if attempts to use multicast options on TCP sockets returned
57
* an error, as the docs suggest it might.
58
*/
59
60
#ifdef WARN_TCP
61
#define WARN_SUCCESS 0x00000001 /* Set for TCP to warn on success. */
62
#else
63
#define WARN_SUCCESS 0x00000000
64
#endif
65
66
/*
67
* Multicast test address, picked arbitrarily. Will be used with the
68
* loopback interface.
69
*/
70
#define TEST_MADDR "224.100.100.100"
71
72
/*
73
* Test that a given IP socket option (optname) has a default value of
74
* 'defaultv', that we can set it to 'modifiedv', and use 'fakev' as a dummy
75
* value that shouldn't be returned at any point during the tests. Perform
76
* the tests on the raw socket, tcp socket, and upd socket passed.
77
* 'optstring' is used in printing warnings and errors as needed.
78
*/
79
static void
80
test_u_char(int optname, const char *optstring, u_char defaultv,
81
u_char modifiedv, u_char fakev, const char *socktype, int sock,
82
int flags)
83
{
84
socklen_t socklen;
85
u_char uc;
86
int ret;
87
88
/*
89
* Check that we read back the expected default.
90
*/
91
uc = fakev;
92
socklen = sizeof(uc);
93
94
ret = getsockopt(sock, IPPROTO_IP, optname, &uc, &socklen);
95
if (ret < 0)
96
err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",
97
socktype, optstring);
98
if (ret == 0 && (flags & WARN_SUCCESS))
99
warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",
100
socktype, optstring);
101
if (uc != defaultv)
102
errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) default is "
103
"%d not %d", socktype, optstring, uc, defaultv);
104
105
/*
106
* Set to a modifiedv value, read it back and make sure it got there.
107
*/
108
uc = modifiedv;
109
ret = setsockopt(sock, IPPROTO_IP, optname, &uc, sizeof(uc));
110
if (ret == -1)
111
err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, %s)",
112
socktype, optstring);
113
if (ret == 0 && (flags & WARN_SUCCESS))
114
warnx("WARN: setsockopt(%s, IPPROTO_IP, %s) returned 0",
115
socktype, optstring);
116
117
uc = fakev;
118
socklen = sizeof(uc);
119
ret = getsockopt(sock, IPPROTO_IP, optname, &uc, &socklen);
120
if (ret < 0)
121
err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",
122
socktype, optstring);
123
if (ret == 0 && (flags & WARN_SUCCESS))
124
warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",
125
socktype, optstring);
126
if (uc != modifiedv)
127
errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) set value is "
128
"%d not %d", socktype, optstring, uc, modifiedv);
129
}
130
131
/*
132
* test_in_addr() is like test_u_char(), only it runs on a struct in_addr
133
* (surprise).
134
*/
135
static void
136
test_in_addr(int optname, const char *optstring, struct in_addr defaultv,
137
struct in_addr modifiedv, struct in_addr fakev, const char *socktype,
138
int sock, int flags)
139
{
140
socklen_t socklen;
141
struct in_addr ia;
142
int ret;
143
144
/*
145
* Check that we read back the expected default.
146
*/
147
ia = fakev;
148
socklen = sizeof(ia);
149
150
ret = getsockopt(sock, IPPROTO_IP, optname, &ia, &socklen);
151
if (ret < 0)
152
err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",
153
socktype, optstring);
154
if (ret == 0 && (flags & WARN_SUCCESS))
155
warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",
156
socktype, optstring);
157
if (memcmp(&ia, &defaultv, sizeof(struct in_addr)))
158
errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) default is "
159
"%s not %s", socktype, optstring, inet_ntoa(ia),
160
inet_ntoa(defaultv));
161
162
/*
163
* Set to a modifiedv value, read it back and make sure it got there.
164
*/
165
ia = modifiedv;
166
ret = setsockopt(sock, IPPROTO_IP, optname, &ia, sizeof(ia));
167
if (ret == -1)
168
err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, %s)",
169
socktype, optstring);
170
if (ret == 0 && (flags & WARN_SUCCESS))
171
warnx("WARN: setsockopt(%s, IPPROTO_IP, %s) returned 0",
172
socktype, optstring);
173
174
ia = fakev;
175
socklen = sizeof(ia);
176
ret = getsockopt(sock, IPPROTO_IP, optname, &ia, &socklen);
177
if (ret < 0)
178
err(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s)",
179
socktype, optstring);
180
if (ret == 0 && (flags & WARN_SUCCESS))
181
warnx("WARN: getsockopt(%s, IPPROTO_IP, %s) returned 0",
182
socktype, optstring);
183
if (memcmp(&ia, &modifiedv, sizeof(struct in_addr)))
184
errx(-1, "FAIL: getsockopt(%s, IPPROTO_IP, %s) set value is "
185
"%s not %s", socktype, optstring, inet_ntoa(ia),
186
inet_ntoa(modifiedv));
187
}
188
189
static void
190
test_ttl(int raw_sock, int tcp_sock, int udp_sock)
191
{
192
193
test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243,
194
"raw_sock", raw_sock, 0);
195
test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243,
196
"tcp_sock", tcp_sock, WARN_SUCCESS);
197
test_u_char(IP_MULTICAST_TTL, "IP_MULTICAST_TTL", 1, 2, 243,
198
"udp_sock", udp_sock, 0);
199
}
200
201
static void
202
test_loop(int raw_sock, int tcp_sock, int udp_sock)
203
{
204
205
test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243,
206
"raw_sock", raw_sock, 0);
207
test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243,
208
"tcp_sock", tcp_sock, WARN_SUCCESS);
209
test_u_char(IP_MULTICAST_LOOP, "IP_MULTICAST_LOOP", 1, 0, 243,
210
"udp_sock", udp_sock, 0);
211
}
212
213
static void
214
test_if(int raw_sock, int tcp_sock, int udp_sock)
215
{
216
struct in_addr defaultv, modifiedv, fakev;
217
218
defaultv.s_addr = inet_addr("0.0.0.0");
219
220
/* Should be valid on all hosts. */
221
modifiedv.s_addr = inet_addr("127.0.0.1");
222
223
/* Should not happen. */
224
fakev.s_addr = inet_addr("255.255.255.255");
225
226
test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv,
227
fakev, "raw_sock", raw_sock, 0);
228
test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv,
229
fakev, "tcp_sock", tcp_sock, WARN_SUCCESS);
230
test_in_addr(IP_MULTICAST_IF, "IP_MULTICAST_IF", defaultv, modifiedv,
231
fakev, "udp_sock", udp_sock, 0);
232
}
233
234
/*
235
* Add a multicast address to an interface. Warn if appropriate. No query
236
* interface so can't check if it's there directly; instead we have to try
237
* to add it a second time and make sure we get back EADDRINUSE.
238
*/
239
static void
240
test_add_multi(int sock, const char *socktype, struct ip_mreq imr,
241
int flags)
242
{
243
char buf[128];
244
int ret;
245
246
ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr,
247
sizeof(imr));
248
if (ret < 0) {
249
strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
250
err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "
251
"%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));
252
}
253
if (ret == 0 && (flags & WARN_SUCCESS)) {
254
strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
255
warnx("WARN: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "
256
"%s, %s) returned 0", socktype, buf,
257
inet_ntoa(imr.imr_interface));
258
}
259
260
/* Try to add a second time to make sure it got there. */
261
ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr,
262
sizeof(imr));
263
if (ret == 0) {
264
strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
265
err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "
266
"%s, %s) dup returned 0", socktype, buf,
267
inet_ntoa(imr.imr_interface));
268
}
269
if (ret < 0 && errno != EADDRINUSE) {
270
strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
271
err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_ADD_MEMBERSHIP "
272
"%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));
273
}
274
}
275
276
/*
277
* Drop a multicast address from an interface. Warn if appropriate. No
278
* query interface so can't check if it's gone directly; instead we have to
279
* try to drop it a second time and make sure we get back EADDRNOTAVAIL.
280
*/
281
static void
282
test_drop_multi(int sock, const char *socktype, struct ip_mreq imr,
283
int flags)
284
{
285
char buf[128];
286
int ret;
287
288
ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr,
289
sizeof(imr));
290
if (ret < 0) {
291
strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
292
err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "
293
"%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));
294
}
295
if (ret == 0 && (flags & WARN_SUCCESS)) {
296
strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
297
warnx("WARN: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "
298
"%s, %s) returned 0", socktype, buf,
299
inet_ntoa(imr.imr_interface));
300
}
301
302
/* Try a second time to make sure it's gone. */
303
ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr,
304
sizeof(imr));
305
if (ret == 0) {
306
strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
307
err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "
308
"%s, %s) returned 0", socktype, buf,
309
inet_ntoa(imr.imr_interface));
310
}
311
if (ret < 0 && errno != EADDRNOTAVAIL) {
312
strlcpy(buf, inet_ntoa(imr.imr_multiaddr), 128);
313
err(-1, "FAIL: setsockopt(%s, IPPROTO_IP, IP_DROP_MEMBERSHIP "
314
"%s, %s)", socktype, buf, inet_ntoa(imr.imr_interface));
315
}
316
}
317
318
/*
319
* Should really also test trying to add an invalid address, delete one
320
* that's not there, etc.
321
*/
322
static void
323
test_addr(int raw_sock, int tcp_sock, int udp_sock)
324
{
325
struct ip_mreq imr;
326
327
/* Arbitrary. */
328
imr.imr_multiaddr.s_addr = inet_addr(TEST_MADDR);
329
330
/* Localhost should be OK. */
331
imr.imr_interface.s_addr = inet_addr("127.0.0.1");
332
333
test_add_multi(raw_sock, "raw_sock", imr, 0);
334
test_drop_multi(raw_sock, "raw_sock", imr, 0);
335
336
test_add_multi(tcp_sock, "raw_sock", imr, WARN_SUCCESS);
337
test_drop_multi(tcp_sock, "raw_sock", imr, WARN_SUCCESS);
338
339
test_add_multi(udp_sock, "raw_sock", imr, 0);
340
test_drop_multi(udp_sock, "raw_sock", imr, 0);
341
}
342
343
/*
344
* Test an actual simple UDP message - send a single byte to an address we're
345
* subscribed to, and hope to get it back. We create a new UDP socket for
346
* this purpose because we will need to bind it.
347
*/
348
#define UDP_PORT 5012
349
static void
350
test_udp(void)
351
{
352
struct sockaddr_in sin;
353
struct ip_mreq imr;
354
struct in_addr if_addr;
355
char message;
356
ssize_t len;
357
int sock;
358
359
sock = socket(PF_INET, SOCK_DGRAM, 0);
360
if (sock < 0)
361
err(-1, "FAIL: test_udp: socket(PF_INET, SOCK_DGRAM)");
362
363
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
364
err(-1, "FAIL: test_udp: fcntl(F_SETFL, O_NONBLOCK)");
365
366
bzero(&sin, sizeof(sin));
367
sin.sin_len = sizeof(sin);
368
sin.sin_family = AF_INET;
369
sin.sin_port = htons(UDP_PORT);
370
sin.sin_addr.s_addr = inet_addr(TEST_MADDR);
371
372
if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
373
err(-1, "FAIL: test_udp: bind(udp_sock, 127.0.0.1:%d",
374
UDP_PORT);
375
376
/* Arbitrary. */
377
imr.imr_multiaddr.s_addr = inet_addr(TEST_MADDR);
378
379
/* Localhost should be OK. */
380
imr.imr_interface.s_addr = inet_addr("127.0.0.1");
381
382
/*
383
* Tell socket what interface to send on -- use localhost.
384
*/
385
if_addr.s_addr = inet_addr("127.0.0.1");
386
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &if_addr,
387
sizeof(if_addr)) < 0)
388
err(-1, "test_udp: setsockopt(IPPROTO_IP, IP_MULTICAST_IF)");
389
390
test_add_multi(sock, "udp_sock", imr, 0);
391
392
bzero(&sin, sizeof(sin));
393
sin.sin_len = sizeof(sin);
394
sin.sin_family = AF_INET;
395
sin.sin_port = htons(UDP_PORT);
396
sin.sin_addr.s_addr = inet_addr(TEST_MADDR);
397
398
message = 'A';
399
len = sizeof(message);
400
len = sendto(sock, &message, len, 0, (struct sockaddr *)&sin,
401
sizeof(sin));
402
if (len < 0)
403
err(-1, "test_udp: sendto");
404
405
if (len != sizeof(message))
406
errx(-1, "test_udp: sendto: expected to send %d, instead %d",
407
sizeof(message), len);
408
409
message = 'B';
410
len = sizeof(sin);
411
len = recvfrom(sock, &message, sizeof(message), 0,
412
(struct sockaddr *)&sin, &len);
413
if (len < 0)
414
err(-1, "test_udp: recvfrom");
415
416
if (len != sizeof(message))
417
errx(-1, "test_udp: recvfrom: len %d != message len %d",
418
len, sizeof(message));
419
420
if (message != 'A')
421
errx(-1, "test_udp: recvfrom: expected 'A', got '%c'",
422
message);
423
424
test_drop_multi(sock, "udp_sock", imr, 0);
425
426
close(sock);
427
}
428
#undef UDP_PORT
429
430
int
431
main(int argc, char *argv[])
432
{
433
int raw_sock, tcp_sock, udp_sock;
434
435
if (geteuid() != 0)
436
errx(-1, "FAIL: root privilege required");
437
438
raw_sock = socket(PF_INET, SOCK_RAW, 0);
439
if (raw_sock == -1)
440
err(-1, "FAIL: socket(PF_INET, SOCK_RAW)");
441
442
tcp_sock = socket(PF_INET, SOCK_STREAM, 0);
443
if (raw_sock == -1)
444
err(-1, "FAIL: socket(PF_INET, SOCK_STREAM)");
445
446
udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
447
if (raw_sock == -1)
448
err(-1, "FAIL: socket(PF_INET, SOCK_DGRAM)");
449
450
test_ttl(raw_sock, tcp_sock, udp_sock);
451
test_loop(raw_sock, tcp_sock, udp_sock);
452
test_if(raw_sock, tcp_sock, udp_sock);
453
test_addr(raw_sock, tcp_sock, udp_sock);
454
455
close(udp_sock);
456
close(tcp_sock);
457
close(raw_sock);
458
459
test_udp();
460
461
return (0);
462
}
463
464