1170614Sbms/*- 2170614Sbms * Copyright (c) 2007 Bruce M. Simpson 3170614Sbms * All rights reserved. 4170614Sbms * 5170614Sbms * Redistribution and use in source and binary forms, with or without 6170614Sbms * modification, are permitted provided that the following conditions 7170614Sbms * are met: 8170614Sbms * 1. Redistributions of source code must retain the above copyright 9170614Sbms * notice, this list of conditions and the following disclaimer. 10170614Sbms * 2. Redistributions in binary form must reproduce the above copyright 11170614Sbms * notice, this list of conditions and the following disclaimer in the 12170614Sbms * documentation and/or other materials provided with the distribution. 13170614Sbms * 14170614Sbms * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15170614Sbms * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16170614Sbms * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17170614Sbms * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18170614Sbms * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19170614Sbms * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20170614Sbms * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21170614Sbms * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22170614Sbms * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23170614Sbms * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24170614Sbms * SUCH DAMAGE. 25170614Sbms */ 26170614Sbms 27170614Sbms/* 28170614Sbms * Regression test utility for RFC 3678 Advanced Multicast API in FreeBSD. 29170614Sbms * 30170614Sbms * TODO: Test the SSM paths. 31170614Sbms * TODO: Support INET6. The code has been written to facilitate this later. 32170614Sbms * TODO: Merge multicast socket option tests from ipsockopt. 33170614Sbms */ 34170614Sbms 35170614Sbms#include <sys/cdefs.h> 36170614Sbms__FBSDID("$FreeBSD: releng/11.0/tools/regression/netinet/ipmulticast/ipmulticast.c 170614 2007-06-12 16:29:22Z bms $"); 37170614Sbms 38170614Sbms#include <sys/param.h> 39170614Sbms#include <sys/types.h> 40170614Sbms#include <sys/ioctl.h> 41170614Sbms#include <sys/socket.h> 42170614Sbms 43170614Sbms#include <net/if.h> 44170614Sbms#include <net/if_dl.h> 45170614Sbms#include <netinet/in.h> 46170614Sbms#include <arpa/inet.h> 47170614Sbms#include <netdb.h> 48170614Sbms 49170614Sbms#include <assert.h> 50170614Sbms#include <err.h> 51170614Sbms#include <errno.h> 52170614Sbms#include <getopt.h> 53170614Sbms#include <libgen.h> 54170614Sbms#include <pwd.h> 55170614Sbms#include <setjmp.h> 56170614Sbms#include <signal.h> 57170614Sbms#include <stddef.h> 58170614Sbms#include <stdio.h> 59170614Sbms#include <stdlib.h> 60170614Sbms#include <string.h> 61170614Sbms#include <sysexits.h> 62170614Sbms#include <time.h> 63170614Sbms#include <unistd.h> 64170614Sbms 65170614Sbms#ifndef __SOCKUNION_DECLARED 66170614Sbmsunion sockunion { 67170614Sbms struct sockaddr_storage ss; 68170614Sbms struct sockaddr sa; 69170614Sbms struct sockaddr_dl sdl; 70170614Sbms struct sockaddr_in sin; 71170614Sbms#ifdef INET6 72170614Sbms struct sockaddr_in6 sin6; 73170614Sbms#endif 74170614Sbms}; 75170614Sbmstypedef union sockunion sockunion_t; 76170614Sbms#define __SOCKUNION_DECLARED 77170614Sbms#endif /* __SOCKUNION_DECLARED */ 78170614Sbms 79170614Sbms#define ADDRBUF_LEN 16 80170614Sbms#define DEFAULT_GROUP_STR "238.1.1.0" 81170614Sbms#define DEFAULT_IFNAME "lo0" 82170614Sbms#define DEFAULT_IFADDR_STR "127.0.0.1" 83170614Sbms#define DEFAULT_PORT 6698 84170614Sbms#define DEFAULT_TIMEOUT 0 /* don't wait for traffic */ 85170614Sbms#define RXBUFSIZE 2048 86170614Sbms 87170614Sbmsstatic sockunion_t basegroup; 88170614Sbmsstatic const char *basegroup_str = NULL; 89170614Sbmsstatic int dobindaddr = 0; 90170614Sbmsstatic int dodebug = 1; 91170614Sbmsstatic int doipv4 = 0; 92170614Sbmsstatic int domiscopts = 0; 93170614Sbmsstatic int dorandom = 0; 94170614Sbmsstatic int doreuseport = 0; 95170614Sbmsstatic int dossm = 0; 96170614Sbmsstatic int dossf = 0; 97170614Sbmsstatic int doverbose = 0; 98170614Sbmsstatic sockunion_t ifaddr; 99170614Sbmsstatic const char *ifaddr_str = NULL; 100170614Sbmsstatic uint32_t ifindex = 0; 101170614Sbmsstatic const char *ifname = NULL; 102170614Sbmsstruct in_addr *ipv4_sources = NULL; 103170614Sbmsstatic jmp_buf jmpbuf; 104170614Sbmsstatic size_t nmcastgroups = IP_MAX_MEMBERSHIPS; 105170614Sbmsstatic size_t nmcastsources = 0; 106170614Sbmsstatic uint16_t portno = DEFAULT_PORT; 107170614Sbmsstatic char *progname = NULL; 108170614Sbmsstruct sockaddr_storage *ss_sources = NULL; 109170614Sbmsstatic uint32_t timeout = 0; 110170614Sbms 111170614Sbmsstatic int do_asm_ipv4(void); 112170614Sbmsstatic int do_asm_pim(void); 113170614Sbms#ifdef notyet 114170614Sbmsstatic int do_misc_opts(void); 115170614Sbms#endif 116170614Sbmsstatic int do_ssf_ipv4(void); 117170614Sbmsstatic int do_ssf_pim(void); 118170614Sbmsstatic int do_ssm_ipv4(void); 119170614Sbmsstatic int do_ssm_pim(void); 120170614Sbmsstatic int open_and_bind_socket(sockunion_t *); 121170614Sbmsstatic int recv_loop_with_match(int, sockunion_t *, sockunion_t *); 122170614Sbmsstatic void signal_handler(int); 123170614Sbmsstatic void usage(void); 124170614Sbms 125170614Sbms/* 126170614Sbms * Test the IPv4 set/getipv4sourcefilter() libc API functions. 127170614Sbms * Build a single socket. 128170614Sbms * Join a source group. 129170614Sbms * Repeatedly change the source filters via setipv4sourcefilter. 130170614Sbms * Read it back with getipv4sourcefilter up to IP_MAX_SOURCES 131170614Sbms * and check for inconsistency. 132170614Sbms */ 133170614Sbmsstatic int 134170614Sbmsdo_ssf_ipv4(void) 135170614Sbms{ 136170614Sbms 137170614Sbms fprintf(stderr, "not yet implemented\n"); 138170614Sbms return (0); 139170614Sbms} 140170614Sbms 141170614Sbms/* 142170614Sbms * Test the protocol-independent set/getsourcefilter() functions. 143170614Sbms */ 144170614Sbmsstatic int 145170614Sbmsdo_ssf_pim(void) 146170614Sbms{ 147170614Sbms 148170614Sbms fprintf(stderr, "not yet implemented\n"); 149170614Sbms return (0); 150170614Sbms} 151170614Sbms 152170614Sbms/* 153170614Sbms * Test the IPv4 ASM API. 154170614Sbms * Repeatedly join, block sources, unblock and leave groups. 155170614Sbms */ 156170614Sbmsstatic int 157170614Sbmsdo_asm_ipv4(void) 158170614Sbms{ 159170614Sbms int error; 160170614Sbms char gaddrbuf[ADDRBUF_LEN]; 161170614Sbms int i; 162170614Sbms sockunion_t laddr; 163170614Sbms struct ip_mreq mreq; 164170614Sbms struct ip_mreq_source mreqs; 165170614Sbms in_addr_t ngroupbase; 166170614Sbms char saddrbuf[ADDRBUF_LEN]; 167170614Sbms int sock; 168170614Sbms sockunion_t tmpgroup; 169170614Sbms sockunion_t tmpsource; 170170614Sbms 171170614Sbms memset(&mreq, 0, sizeof(struct ip_mreq)); 172170614Sbms memset(&mreqs, 0, sizeof(struct ip_mreq_source)); 173170614Sbms memset(&laddr, 0, sizeof(sockunion_t)); 174170614Sbms 175170614Sbms if (dobindaddr) { 176170614Sbms laddr = ifaddr; 177170614Sbms } else { 178170614Sbms laddr.sin.sin_family = AF_INET; 179170614Sbms laddr.sin.sin_len = sizeof(struct sockaddr_in); 180170614Sbms laddr.sin.sin_addr.s_addr = INADDR_ANY; 181170614Sbms } 182170614Sbms laddr.sin.sin_port = htons(portno); 183170614Sbms 184170614Sbms tmpgroup = basegroup; 185170614Sbms ngroupbase = ntohl(basegroup.sin.sin_addr.s_addr) + 1; /* XXX */ 186170614Sbms tmpgroup.sin.sin_addr.s_addr = htonl(ngroupbase); 187170614Sbms 188170614Sbms sock = open_and_bind_socket(&laddr); 189170614Sbms if (sock == -1) 190170614Sbms return (EX_OSERR); 191170614Sbms 192170614Sbms for (i = 0; i < (signed)nmcastgroups; i++) { 193170614Sbms mreq.imr_multiaddr.s_addr = htonl((ngroupbase + i)); 194170614Sbms mreq.imr_interface = ifaddr.sin.sin_addr; 195170614Sbms if (doverbose) { 196170614Sbms inet_ntop(AF_INET, &mreq.imr_multiaddr, gaddrbuf, 197170614Sbms sizeof(gaddrbuf)); 198170614Sbms fprintf(stderr, "IP_ADD_MEMBERSHIP %s %s\n", 199170614Sbms gaddrbuf, inet_ntoa(mreq.imr_interface)); 200170614Sbms } 201170614Sbms error = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, 202170614Sbms &mreq, sizeof(struct ip_mreq)); 203170614Sbms if (error < 0) { 204170614Sbms warn("setsockopt IP_ADD_MEMBERSHIP"); 205170614Sbms close(sock); 206170614Sbms return (EX_OSERR); 207170614Sbms } 208170614Sbms } 209170614Sbms 210170614Sbms /* 211170614Sbms * If no test sources auto-generated or specified on command line, 212170614Sbms * skip source filter portion of ASM test. 213170614Sbms */ 214170614Sbms if (nmcastsources == 0) 215170614Sbms goto skipsources; 216170614Sbms 217170614Sbms /* 218170614Sbms * Begin blocking sources on the first group chosen. 219170614Sbms */ 220170614Sbms for (i = 0; i < (signed)nmcastsources; i++) { 221170614Sbms mreqs.imr_multiaddr = tmpgroup.sin.sin_addr; 222170614Sbms mreqs.imr_interface = ifaddr.sin.sin_addr; 223170614Sbms mreqs.imr_sourceaddr = ipv4_sources[i]; 224170614Sbms if (doverbose) { 225170614Sbms inet_ntop(AF_INET, &mreqs.imr_multiaddr, gaddrbuf, 226170614Sbms sizeof(gaddrbuf)); 227170614Sbms inet_ntop(AF_INET, &mreqs.imr_sourceaddr, saddrbuf, 228170614Sbms sizeof(saddrbuf)); 229170614Sbms fprintf(stderr, "IP_BLOCK_SOURCE %s %s %s\n", 230170614Sbms gaddrbuf, inet_ntoa(mreqs.imr_interface), 231170614Sbms saddrbuf); 232170614Sbms } 233170614Sbms error = setsockopt(sock, IPPROTO_IP, IP_BLOCK_SOURCE, &mreqs, 234170614Sbms sizeof(struct ip_mreq_source)); 235170614Sbms if (error < 0) { 236170614Sbms warn("setsockopt IP_BLOCK_SOURCE"); 237170614Sbms close(sock); 238170614Sbms return (EX_OSERR); 239170614Sbms } 240170614Sbms } 241170614Sbms 242170614Sbms /* 243170614Sbms * Choose the first group and source for a match. 244170614Sbms * Enter the I/O loop. 245170614Sbms */ 246170614Sbms memset(&tmpsource, 0, sizeof(sockunion_t)); 247170614Sbms tmpsource.sin.sin_family = AF_INET; 248170614Sbms tmpsource.sin.sin_len = sizeof(struct sockaddr_in); 249170614Sbms tmpsource.sin.sin_addr = ipv4_sources[0]; 250170614Sbms 251170614Sbms error = recv_loop_with_match(sock, &tmpgroup, &tmpsource); 252170614Sbms 253170614Sbms /* 254170614Sbms * Unblock sources. 255170614Sbms */ 256170614Sbms for (i = nmcastsources-1; i >= 0; i--) { 257170614Sbms mreqs.imr_multiaddr = tmpgroup.sin.sin_addr; 258170614Sbms mreqs.imr_interface = ifaddr.sin.sin_addr; 259170614Sbms mreqs.imr_sourceaddr = ipv4_sources[i]; 260170614Sbms if (doverbose) { 261170614Sbms inet_ntop(AF_INET, &mreqs.imr_multiaddr, gaddrbuf, 262170614Sbms sizeof(gaddrbuf)); 263170614Sbms inet_ntop(AF_INET, &mreqs.imr_sourceaddr, saddrbuf, 264170614Sbms sizeof(saddrbuf)); 265170614Sbms fprintf(stderr, "IP_UNBLOCK_SOURCE %s %s %s\n", 266170614Sbms gaddrbuf, inet_ntoa(mreqs.imr_interface), 267170614Sbms saddrbuf); 268170614Sbms } 269170614Sbms error = setsockopt(sock, IPPROTO_IP, IP_UNBLOCK_SOURCE, &mreqs, 270170614Sbms sizeof(struct ip_mreq_source)); 271170614Sbms if (error < 0) { 272170614Sbms warn("setsockopt IP_UNBLOCK_SOURCE"); 273170614Sbms close(sock); 274170614Sbms return (EX_OSERR); 275170614Sbms } 276170614Sbms } 277170614Sbms 278170614Sbmsskipsources: 279170614Sbms /* 280170614Sbms * Leave groups. 281170614Sbms */ 282170614Sbms for (i = nmcastgroups-1; i >= 0; i--) { 283170614Sbms mreq.imr_multiaddr.s_addr = htonl((ngroupbase + i)); 284170614Sbms mreq.imr_interface = ifaddr.sin.sin_addr; 285170614Sbms if (doverbose) { 286170614Sbms inet_ntop(AF_INET, &mreq.imr_multiaddr, gaddrbuf, 287170614Sbms sizeof(gaddrbuf)); 288170614Sbms fprintf(stderr, "IP_DROP_MEMBERSHIP %s %s\n", 289170614Sbms gaddrbuf, inet_ntoa(mreq.imr_interface)); 290170614Sbms } 291170614Sbms error = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, 292170614Sbms &mreq, sizeof(struct ip_mreq)); 293170614Sbms if (error < 0) { 294170614Sbms warn("setsockopt IP_DROP_MEMBERSHIP"); 295170614Sbms close(sock); 296170614Sbms return (EX_OSERR); 297170614Sbms } 298170614Sbms } 299170614Sbms 300170614Sbms return (0); 301170614Sbms} 302170614Sbms 303170614Sbmsstatic int 304170614Sbmsdo_asm_pim(void) 305170614Sbms{ 306170614Sbms 307170614Sbms fprintf(stderr, "not yet implemented\n"); 308170614Sbms return (0); 309170614Sbms} 310170614Sbms 311170614Sbms#ifdef notyet 312170614Sbms/* 313170614Sbms * Test misceallaneous IPv4 options. 314170614Sbms */ 315170614Sbmsstatic int 316170614Sbmsdo_misc_opts(void) 317170614Sbms{ 318170614Sbms int sock; 319170614Sbms 320170614Sbms sock = open_and_bind_socket(NULL); 321170614Sbms if (sock == -1) 322170614Sbms return (EX_OSERR); 323170614Sbms test_ip_uchar(sock, socktypename, IP_MULTICAST_TTL, 324170614Sbms "IP_MULTICAST_TTL", 1); 325170614Sbms close(sock); 326170614Sbms 327170614Sbms sock = open_and_bind_socket(NULL); 328170614Sbms if (sock == -1) 329170614Sbms return (EX_OSERR); 330170614Sbms test_ip_boolean(sock, socktypename, IP_MULTICAST_LOOP, 331170614Sbms "IP_MULTICAST_LOOP", 1, BOOLEAN_ANYONE); 332170614Sbms close(sock); 333170614Sbms 334170614Sbms return (0); 335170614Sbms} 336170614Sbms#endif 337170614Sbms 338170614Sbms/* 339170614Sbms * Test the IPv4 SSM API. 340170614Sbms */ 341170614Sbmsstatic int 342170614Sbmsdo_ssm_ipv4(void) 343170614Sbms{ 344170614Sbms 345170614Sbms fprintf(stderr, "not yet implemented\n"); 346170614Sbms return (0); 347170614Sbms} 348170614Sbms 349170614Sbms/* 350170614Sbms * Test the protocol-independent SSM API with IPv4 addresses. 351170614Sbms */ 352170614Sbmsstatic int 353170614Sbmsdo_ssm_pim(void) 354170614Sbms{ 355170614Sbms 356170614Sbms fprintf(stderr, "not yet implemented\n"); 357170614Sbms return (0); 358170614Sbms} 359170614Sbms 360170614Sbmsint 361170614Sbmsmain(int argc, char *argv[]) 362170614Sbms{ 363170614Sbms struct addrinfo aih; 364170614Sbms struct addrinfo *aip; 365170614Sbms int ch; 366170614Sbms int error; 367170614Sbms int exitval; 368170614Sbms size_t i; 369170614Sbms struct in_addr *pina; 370170614Sbms struct sockaddr_storage *pbss; 371170614Sbms 372170614Sbms ifname = DEFAULT_IFNAME; 373170614Sbms ifaddr_str = DEFAULT_IFADDR_STR; 374170614Sbms basegroup_str = DEFAULT_GROUP_STR; 375170614Sbms ifname = DEFAULT_IFNAME; 376170614Sbms portno = DEFAULT_PORT; 377170614Sbms basegroup.ss.ss_family = AF_UNSPEC; 378170614Sbms ifaddr.ss.ss_family = AF_UNSPEC; 379170614Sbms 380170614Sbms progname = basename(argv[0]); 381170614Sbms while ((ch = getopt(argc, argv, "4bg:i:I:mM:p:rsS:tT:v")) != -1) { 382170614Sbms switch (ch) { 383170614Sbms case '4': 384170614Sbms doipv4 = 1; 385170614Sbms break; 386170614Sbms case 'b': 387170614Sbms dobindaddr = 1; 388170614Sbms break; 389170614Sbms case 'g': 390170614Sbms basegroup_str = optarg; 391170614Sbms break; 392170614Sbms case 'i': 393170614Sbms ifname = optarg; 394170614Sbms break; 395170614Sbms case 'I': 396170614Sbms ifaddr_str = optarg; 397170614Sbms break; 398170614Sbms case 'm': 399170614Sbms usage(); /* notyet */ 400170614Sbms /*NOTREACHED*/ 401170614Sbms domiscopts = 1; 402170614Sbms break; 403170614Sbms case 'M': 404170614Sbms nmcastgroups = atoi(optarg); 405170614Sbms break; 406170614Sbms case 'p': 407170614Sbms portno = atoi(optarg); 408170614Sbms break; 409170614Sbms case 'r': 410170614Sbms doreuseport = 1; 411170614Sbms break; 412170614Sbms case 'S': 413170614Sbms nmcastsources = atoi(optarg); 414170614Sbms break; 415170614Sbms case 's': 416170614Sbms dossm = 1; 417170614Sbms break; 418170614Sbms case 't': 419170614Sbms dossf = 1; 420170614Sbms break; 421170614Sbms case 'T': 422170614Sbms timeout = atoi(optarg); 423170614Sbms break; 424170614Sbms case 'v': 425170614Sbms doverbose = 1; 426170614Sbms break; 427170614Sbms default: 428170614Sbms usage(); 429170614Sbms break; 430170614Sbms /*NOTREACHED*/ 431170614Sbms } 432170614Sbms } 433170614Sbms argc -= optind; 434170614Sbms argv += optind; 435170614Sbms 436170614Sbms memset(&aih, 0, sizeof(struct addrinfo)); 437170614Sbms aih.ai_flags = AI_NUMERICHOST | AI_PASSIVE; 438170614Sbms aih.ai_family = PF_INET; 439170614Sbms aih.ai_socktype = SOCK_DGRAM; 440170614Sbms aih.ai_protocol = IPPROTO_UDP; 441170614Sbms 442170614Sbms /* 443170614Sbms * Fill out base group. 444170614Sbms */ 445170614Sbms aip = NULL; 446170614Sbms error = getaddrinfo(basegroup_str, NULL, &aih, &aip); 447170614Sbms if (error != 0) { 448170614Sbms fprintf(stderr, "%s: getaddrinfo: %s\n", progname, 449170614Sbms gai_strerror(error)); 450170614Sbms exit(EX_USAGE); 451170614Sbms } 452170614Sbms memcpy(&basegroup, aip->ai_addr, aip->ai_addrlen); 453170614Sbms if (dodebug) { 454170614Sbms fprintf(stderr, "debug: gai thinks %s is %s\n", 455170614Sbms basegroup_str, inet_ntoa(basegroup.sin.sin_addr)); 456170614Sbms } 457170614Sbms freeaddrinfo(aip); 458170614Sbms 459170614Sbms assert(basegroup.ss.ss_family == AF_INET); 460170614Sbms 461170614Sbms /* 462170614Sbms * If user specified interface as an address, and protocol 463170614Sbms * specific APIs were selected, parse it. 464170614Sbms * Otherwise, parse interface index from name if protocol 465170614Sbms * independent APIs were selected (the default). 466170614Sbms */ 467170614Sbms if (doipv4) { 468170614Sbms if (ifaddr_str == NULL) { 469170614Sbms warnx("required argument missing: ifaddr"); 470170614Sbms usage(); 471170614Sbms /* NOTREACHED */ 472170614Sbms } 473170614Sbms aip = NULL; 474170614Sbms error = getaddrinfo(ifaddr_str, NULL, &aih, &aip); 475170614Sbms if (error != 0) { 476170614Sbms fprintf(stderr, "%s: getaddrinfo: %s\n", progname, 477170614Sbms gai_strerror(error)); 478170614Sbms exit(EX_USAGE); 479170614Sbms } 480170614Sbms memcpy(&ifaddr, aip->ai_addr, aip->ai_addrlen); 481170614Sbms if (dodebug) { 482170614Sbms fprintf(stderr, "debug: gai thinks %s is %s\n", 483170614Sbms ifaddr_str, inet_ntoa(ifaddr.sin.sin_addr)); 484170614Sbms } 485170614Sbms freeaddrinfo(aip); 486170614Sbms } 487170614Sbms 488170614Sbms if (!doipv4) { 489170614Sbms if (ifname == NULL) { 490170614Sbms warnx("required argument missing: ifname"); 491170614Sbms usage(); 492170614Sbms /* NOTREACHED */ 493170614Sbms } 494170614Sbms ifindex = if_nametoindex(ifname); 495170614Sbms if (ifindex == 0) 496170614Sbms err(EX_USAGE, "if_nametoindex"); 497170614Sbms } 498170614Sbms 499170614Sbms /* 500170614Sbms * Introduce randomness into group base if specified. 501170614Sbms */ 502170614Sbms if (dorandom) { 503170614Sbms in_addr_t ngroupbase; 504170614Sbms 505170614Sbms srandomdev(); 506170614Sbms ngroupbase = ntohl(basegroup.sin.sin_addr.s_addr); 507170614Sbms ngroupbase |= ((random() % ((1 << 11) - 1)) << 16); 508170614Sbms basegroup.sin.sin_addr.s_addr = htonl(ngroupbase); 509170614Sbms } 510170614Sbms 511170614Sbms if (argc > 0) { 512170614Sbms nmcastsources = argc; 513170614Sbms if (doipv4) { 514170614Sbms ipv4_sources = calloc(nmcastsources, 515170614Sbms sizeof(struct in_addr)); 516170614Sbms if (ipv4_sources == NULL) { 517170614Sbms exitval = EX_OSERR; 518170614Sbms goto out; 519170614Sbms } 520170614Sbms } else { 521170614Sbms ss_sources = calloc(nmcastsources, 522170614Sbms sizeof(struct sockaddr_storage)); 523170614Sbms if (ss_sources == NULL) { 524170614Sbms exitval = EX_OSERR; 525170614Sbms goto out; 526170614Sbms } 527170614Sbms } 528170614Sbms } 529170614Sbms 530170614Sbms /* 531170614Sbms * Parse source list, if any were specified on the command line. 532170614Sbms */ 533170614Sbms assert(aih.ai_family == PF_INET); 534170614Sbms pbss = ss_sources; 535170614Sbms pina = ipv4_sources; 536170614Sbms for (i = 0; i < (size_t)argc; i++) { 537170614Sbms aip = NULL; 538170614Sbms error = getaddrinfo(argv[i], NULL, &aih, &aip); 539170614Sbms if (error != 0) { 540170614Sbms fprintf(stderr, "getaddrinfo: %s\n", 541170614Sbms gai_strerror(error)); 542170614Sbms exitval = EX_USAGE; 543170614Sbms goto out; 544170614Sbms } 545170614Sbms if (doipv4) { 546170614Sbms struct sockaddr_in *sin = 547170614Sbms (struct sockaddr_in *)aip->ai_addr; 548170614Sbms *pina++ = sin->sin_addr; 549170614Sbms } else { 550170614Sbms memcpy(pbss++, aip->ai_addr, aip->ai_addrlen); 551170614Sbms } 552170614Sbms freeaddrinfo(aip); 553170614Sbms } 554170614Sbms 555170614Sbms /* 556170614Sbms * Perform the regression tests which the user requested. 557170614Sbms */ 558170614Sbms#ifdef notyet 559170614Sbms if (domiscopts) { 560170614Sbms exitval = do_misc_opts(); 561170614Sbms if (exitval) 562170614Sbms goto out; 563170614Sbms } 564170614Sbms#endif 565170614Sbms if (doipv4) { 566170614Sbms /* IPv4 protocol specific API tests */ 567170614Sbms if (dossm) { 568170614Sbms /* Source-specific multicast */ 569170614Sbms exitval = do_ssm_ipv4(); 570170614Sbms if (exitval) 571170614Sbms goto out; 572170614Sbms if (dossf) { 573170614Sbms /* Do setipvsourcefilter() too */ 574170614Sbms exitval = do_ssf_ipv4(); 575170614Sbms } 576170614Sbms } else { 577170614Sbms /* Any-source multicast */ 578170614Sbms exitval = do_asm_ipv4(); 579170614Sbms } 580170614Sbms } else { 581170614Sbms /* Protocol independent API tests */ 582170614Sbms if (dossm) { 583170614Sbms /* Source-specific multicast */ 584170614Sbms exitval = do_ssm_pim(); 585170614Sbms if (exitval) 586170614Sbms goto out; 587170614Sbms if (dossf) { 588170614Sbms /* Do setsourcefilter() too */ 589170614Sbms exitval = do_ssf_pim(); 590170614Sbms } 591170614Sbms } else { 592170614Sbms /* Any-source multicast */ 593170614Sbms exitval = do_asm_pim(); 594170614Sbms } 595170614Sbms } 596170614Sbms 597170614Sbmsout: 598170614Sbms if (ipv4_sources != NULL) 599170614Sbms free(ipv4_sources); 600170614Sbms 601170614Sbms if (ss_sources != NULL) 602170614Sbms free(ss_sources); 603170614Sbms 604170614Sbms exit(exitval); 605170614Sbms} 606170614Sbms 607170614Sbmsstatic int 608170614Sbmsopen_and_bind_socket(sockunion_t *bsu) 609170614Sbms{ 610170614Sbms int error, optval, sock; 611170614Sbms 612170614Sbms sock = -1; 613170614Sbms 614170614Sbms sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 615170614Sbms if (sock == -1) { 616170614Sbms warn("socket"); 617170614Sbms return (-1); 618170614Sbms } 619170614Sbms 620170614Sbms if (doreuseport) { 621170614Sbms optval = 1; 622170614Sbms if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &optval, 623170614Sbms sizeof(optval)) < 0) { 624170614Sbms warn("setsockopt SO_REUSEPORT"); 625170614Sbms close(sock); 626170614Sbms return (-1); 627170614Sbms } 628170614Sbms } 629170614Sbms 630170614Sbms if (bsu != NULL) { 631170614Sbms error = bind(sock, &bsu->sa, bsu->sa.sa_len); 632170614Sbms if (error == -1) { 633170614Sbms warn("bind"); 634170614Sbms close(sock); 635170614Sbms return (-1); 636170614Sbms } 637170614Sbms } 638170614Sbms 639170614Sbms return (sock); 640170614Sbms} 641170614Sbms 642170614Sbms/* 643170614Sbms * Protocol-agnostic multicast I/O loop. 644170614Sbms * 645170614Sbms * Wait for 'timeout' seconds looking for traffic on group, so that manual 646170614Sbms * or automated regression tests (possibly running on another host) have an 647170614Sbms * opportunity to transmit within the group to test source filters. 648170614Sbms * 649170614Sbms * If the filter failed, this loop will report if we received traffic 650170614Sbms * from the source we elected to monitor. 651170614Sbms */ 652170614Sbmsstatic int 653170614Sbmsrecv_loop_with_match(int sock, sockunion_t *group, sockunion_t *source) 654170614Sbms{ 655170614Sbms int error; 656170614Sbms sockunion_t from; 657170614Sbms char groupname[NI_MAXHOST]; 658170614Sbms ssize_t len; 659170614Sbms size_t npackets; 660170614Sbms int jmpretval; 661170614Sbms char rxbuf[RXBUFSIZE]; 662170614Sbms char sourcename[NI_MAXHOST]; 663170614Sbms 664170614Sbms assert(source->sa.sa_family == AF_INET); 665170614Sbms 666170614Sbms /* 667170614Sbms * Return immediately if we don't need to wait for traffic. 668170614Sbms */ 669170614Sbms if (timeout == 0) 670170614Sbms return (0); 671170614Sbms 672170614Sbms error = getnameinfo(&group->sa, group->sa.sa_len, groupname, 673170614Sbms NI_MAXHOST, NULL, 0, NI_NUMERICHOST); 674170614Sbms if (error) { 675170614Sbms fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error)); 676170614Sbms return (error); 677170614Sbms } 678170614Sbms 679170614Sbms error = getnameinfo(&source->sa, source->sa.sa_len, sourcename, 680170614Sbms NI_MAXHOST, NULL, 0, NI_NUMERICHOST); 681170614Sbms if (error) { 682170614Sbms fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error)); 683170614Sbms return (error); 684170614Sbms } 685170614Sbms 686170614Sbms fprintf(stdout, 687170614Sbms "Waiting %d seconds for inbound traffic on group %s\n" 688170614Sbms "Expecting no traffic from blocked source: %s\n", 689170614Sbms (int)timeout, groupname, sourcename); 690170614Sbms 691170614Sbms signal(SIGINT, signal_handler); 692170614Sbms signal(SIGALRM, signal_handler); 693170614Sbms 694170614Sbms error = 0; 695170614Sbms npackets = 0; 696170614Sbms alarm(timeout); 697170614Sbms while (0 == (jmpretval = setjmp(jmpbuf))) { 698170614Sbms len = recvfrom(sock, rxbuf, RXBUFSIZE, 0, &from.sa, 699170614Sbms (socklen_t *)&from.sa.sa_len); 700170614Sbms if (dodebug) { 701170614Sbms fprintf(stderr, "debug: packet received from %s\n", 702170614Sbms inet_ntoa(from.sin.sin_addr)); 703170614Sbms } 704170614Sbms if (source && 705170614Sbms source->sin.sin_addr.s_addr == from.sin.sin_addr.s_addr) 706170614Sbms break; 707170614Sbms npackets++; 708170614Sbms } 709170614Sbms 710170614Sbms if (doverbose) { 711170614Sbms fprintf(stderr, "Number of datagrams received from " 712170614Sbms "non-blocked sources: %d\n", (int)npackets); 713170614Sbms } 714170614Sbms 715170614Sbms switch (jmpretval) { 716170614Sbms case SIGALRM: /* ok */ 717170614Sbms break; 718170614Sbms case SIGINT: /* go bye bye */ 719170614Sbms fprintf(stderr, "interrupted\n"); 720170614Sbms error = 20; 721170614Sbms break; 722170614Sbms case 0: /* Broke out of loop; saw a bad source. */ 723170614Sbms fprintf(stderr, "FAIL: got packet from blocked source\n"); 724170614Sbms error = EX_IOERR; 725170614Sbms break; 726170614Sbms default: 727170614Sbms warnx("recvfrom"); 728170614Sbms error = EX_OSERR; 729170614Sbms break; 730170614Sbms } 731170614Sbms 732170614Sbms signal(SIGINT, SIG_DFL); 733170614Sbms signal(SIGALRM, SIG_DFL); 734170614Sbms 735170614Sbms return (error); 736170614Sbms} 737170614Sbms 738170614Sbmsstatic void 739170614Sbmssignal_handler(int signo) 740170614Sbms{ 741170614Sbms 742170614Sbms longjmp(jmpbuf, signo); 743170614Sbms} 744170614Sbms 745170614Sbmsstatic void 746170614Sbmsusage(void) 747170614Sbms{ 748170614Sbms 749170614Sbms fprintf(stderr, "\nIP multicast regression test utility\n"); 750170614Sbms fprintf(stderr, 751170614Sbms"usage: %s [-4] [-b] [-g groupaddr] [-i ifname] [-I ifaddr] [-m]\n" 752170614Sbms" [-M ngroups] [-p portno] [-r] [-R] [-s] [-S nsources] [-t] [-T timeout]\n" 753170614Sbms" [-v] [blockaddr ...]\n\n", progname); 754170614Sbms fprintf(stderr, "-4: Use IPv4 API " 755170614Sbms "(default: Use protocol-independent API)\n"); 756170614Sbms fprintf(stderr, "-b: bind listening socket to ifaddr " 757170614Sbms "(default: INADDR_ANY)\n"); 758170614Sbms fprintf(stderr, "-g: Base IPv4 multicast group to join (default: %s)\n", 759170614Sbms DEFAULT_GROUP_STR); 760170614Sbms fprintf(stderr, "-i: interface for multicast joins (default: %s)\n", 761170614Sbms DEFAULT_IFNAME); 762170614Sbms fprintf(stderr, "-I: IPv4 address to join groups on, if using IPv4 " 763170614Sbms "API\n (default: %s)\n", DEFAULT_IFADDR_STR); 764170614Sbms#ifdef notyet 765170614Sbms fprintf(stderr, "-m: Test misc IPv4 multicast socket options " 766170614Sbms "(default: off)\n"); 767170614Sbms#endif 768170614Sbms fprintf(stderr, "-M: Number of multicast groups to join " 769170614Sbms "(default: %d)\n", (int)nmcastgroups); 770170614Sbms fprintf(stderr, "-p: Set local and remote port (default: %d)\n", 771170614Sbms DEFAULT_PORT); 772170614Sbms fprintf(stderr, "-r: Set SO_REUSEPORT on (default: off)\n"); 773170614Sbms fprintf(stderr, "-R: Randomize groups/sources (default: off)\n"); 774170614Sbms fprintf(stderr, "-s: Test source-specific API " 775170614Sbms "(default: test any-source API)\n"); 776170614Sbms fprintf(stderr, "-S: Number of multicast sources to generate if\n" 777170614Sbms " none specified on command line (default: %d)\n", 778170614Sbms (int)nmcastsources); 779170614Sbms fprintf(stderr, "-t: Test get/setNsourcefilter() (default: off)\n"); 780170614Sbms fprintf(stderr, "-T: Timeout to wait for blocked traffic on first " 781170614Sbms "group (default: %d)\n", DEFAULT_TIMEOUT); 782170614Sbms fprintf(stderr, "-v: Be verbose (default: off)\n"); 783170614Sbms fprintf(stderr, "\nRemaining arguments are treated as a list of IPv4 " 784170614Sbms "sources to filter.\n\n"); 785170614Sbms 786170614Sbms exit(EX_USAGE); 787170614Sbms} 788