]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/regression/netinet/msocket/msocket.c
Remove $FreeBSD$: two-line .h pattern
[FreeBSD/FreeBSD.git] / tools / regression / netinet / msocket / msocket.c
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 }