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