1167346Sbms/*- 2189340Sbms * Copyright (c) 2007-2009 Bruce Simpson. 3167346Sbms * Copyright (c) 2000 Wilbert De Graaf. 4167346Sbms * All rights reserved. 520529Sfenner * 6167346Sbms * Redistribution and use in source and binary forms, with or without 7167346Sbms * modification, are permitted provided that the following conditions 8167346Sbms * are met: 9167346Sbms * 1. Redistributions of source code must retain the above copyright 10167346Sbms * notice, this list of conditions and the following disclaimer. 11167346Sbms * 2. Redistributions in binary form must reproduce the above copyright 12167346Sbms * notice, this list of conditions and the following disclaimer in the 13167346Sbms * documentation and/or other materials provided with the distribution. 14167346Sbms * 15167346Sbms * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16167346Sbms * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17167346Sbms * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18167346Sbms * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19167346Sbms * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20167346Sbms * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21167346Sbms * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22167346Sbms * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23167346Sbms * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24167346Sbms * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25167346Sbms * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26167346Sbms * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27167346Sbms * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28167346Sbms * SUCH DAMAGE. 2920529Sfenner */ 3020529Sfenner 31167346Sbms/* 32191651Sbms * Diagnostic and test utility for multicast sockets. 33191651Sbms * XXX: This file currently assumes INET support in the base system. 34191651Sbms * TODO: Support embedded KAME Scope ID in IPv6 group addresses. 35191651Sbms * TODO: Use IPv4 link-local address when source address selection 36191651Sbms * is implemented; use MCAST_JOIN_SOURCE for IPv4. 37167346Sbms */ 38167346Sbms 39117280Scharnier#include <sys/cdefs.h> 40117280Scharnier__FBSDID("$FreeBSD$"); 4178720Sdd 4220529Sfenner#include <sys/types.h> 43191651Sbms#include <sys/param.h> 44167346Sbms#include <sys/errno.h> 4520529Sfenner#include <sys/socket.h> 4620531Sfenner#include <sys/time.h> 47167346Sbms#include <sys/ioctl.h> 48167346Sbms 4920529Sfenner#include <net/if.h> 5036440Sjulian#include <net/if_dl.h> 51167346Sbms#include <net/ethernet.h> 52191651Sbms#ifdef INET 5320529Sfenner#include <netinet/in.h> 54191651Sbms#include <netinet/in_systm.h> 55191651Sbms#include <netinet/ip.h> 56191651Sbms#include <netinet/ip_var.h> 57191651Sbms#endif 58191651Sbms#ifdef INET6 59191651Sbms#include <netinet/in.h> 60191651Sbms#include <netinet/ip6.h> 61191651Sbms#endif 6220529Sfenner 63191651Sbms#include <assert.h> 64167346Sbms#include <stdlib.h> 65167346Sbms#include <stdio.h> 66167346Sbms#include <string.h> 67167346Sbms#include <ctype.h> 68191651Sbms#include <errno.h> 69167346Sbms#include <err.h> 70167346Sbms#include <unistd.h> 71167346Sbms 72191651Sbms#include <arpa/inet.h> 73191651Sbms#include <netdb.h> 74191651Sbms#include <ifaddrs.h> 75167346Sbms 76191651Sbmsunion sockunion { 77191651Sbms struct sockaddr_storage ss; 78191651Sbms struct sockaddr sa; 79191651Sbms struct sockaddr_dl sdl; 80191651Sbms#ifdef INET 81191651Sbms struct sockaddr_in sin; 82191651Sbms#endif 83191651Sbms#ifdef INET6 84191651Sbms struct sockaddr_in6 sin6; 85191651Sbms#endif 86191651Sbms}; 87191651Sbmstypedef union sockunion sockunion_t; 88191651Sbms 89191651Sbmsunion mrequnion { 90191651Sbms#ifdef INET 91191651Sbms struct ip_mreq mr; 92191651Sbms struct ip_mreq_source mrs; 93191651Sbms#endif 94191651Sbms#ifdef INET6 95191651Sbms struct ipv6_mreq mr6; 96191651Sbms struct group_source_req gr; 97191651Sbms#endif 98191651Sbms}; 99191651Sbmstypedef union mrequnion mrequnion_t; 100191651Sbms 101167346Sbms#define MAX_ADDRS 20 102167346Sbms#define STR_SIZE 20 103167346Sbms#define LINE_LENGTH 80 104167346Sbms 105191651Sbms#ifdef INET 106191651Sbmsstatic int __ifindex_to_primary_ip(const uint32_t, struct in_addr *); 107191651Sbms#endif 108191651Sbmsstatic uint32_t parse_cmd_args(sockunion_t *, sockunion_t *, 109191651Sbms const char *, const char *, const char *); 110191651Sbmsstatic void process_file(char *, int, int); 111191651Sbmsstatic void process_cmd(char*, int, int, FILE *); 112191651Sbmsstatic int su_cmp(const void *, const void *); 113191651Sbmsstatic void usage(void); 114191651Sbms 115191651Sbms/* 116191651Sbms * Ordering predicate for qsort(). 117191651Sbms */ 118189340Sbmsstatic int 119191651Sbmssu_cmp(const void *a, const void *b) 120189340Sbms{ 121191651Sbms const sockunion_t *sua = (const sockunion_t *)a; 122191651Sbms const sockunion_t *sub = (const sockunion_t *)b; 123191651Sbms 124191651Sbms assert(sua->sa.sa_family == sub->sa.sa_family); 125191651Sbms 126191651Sbms switch (sua->sa.sa_family) { 127191651Sbms#ifdef INET 128191651Sbms case AF_INET: 129191651Sbms return ((int)(sua->sin.sin_addr.s_addr - 130191651Sbms sub->sin.sin_addr.s_addr)); 131191651Sbms break; 132191651Sbms#endif 133191651Sbms#ifdef INET6 134191651Sbms case AF_INET6: 135191651Sbms return (memcmp(&sua->sin6.sin6_addr, &sub->sin6.sin6_addr, 136191651Sbms sizeof(struct in6_addr))); 137191651Sbms break; 138191651Sbms#endif 139191651Sbms default: 140191651Sbms break; 141191651Sbms } 142191651Sbms 143191651Sbms assert(sua->sa.sa_len == sub->sa.sa_len); 144191651Sbms return (memcmp(sua, sub, sua->sa.sa_len)); 145189340Sbms} 146189340Sbms 147191651Sbms#ifdef INET 148191651Sbms/* 149191651Sbms * Internal: Map an interface index to primary IPv4 address. 150191651Sbms * This is somewhat inefficient. This is a useful enough operation 151191651Sbms * that it probably belongs in the C library. 152191651Sbms * Return zero if found, -1 on error, 1 on not found. 153191651Sbms */ 154191651Sbmsstatic int 155191651Sbms__ifindex_to_primary_ip(const uint32_t ifindex, struct in_addr *pina) 156191651Sbms{ 157191651Sbms char ifname[IFNAMSIZ]; 158191651Sbms struct ifaddrs *ifa; 159191651Sbms struct ifaddrs *ifaddrs; 160191651Sbms sockunion_t *psu; 161191651Sbms int retval; 162191651Sbms 163191651Sbms assert(ifindex != 0); 164191651Sbms 165191651Sbms retval = -1; 166191651Sbms if (if_indextoname(ifindex, ifname) == NULL) 167191651Sbms return (retval); 168191651Sbms if (getifaddrs(&ifaddrs) < 0) 169191651Sbms return (retval); 170191651Sbms 171191651Sbms /* 172191651Sbms * Find the ifaddr entry corresponding to the interface name, 173191651Sbms * and return the first matching IPv4 address. 174191651Sbms */ 175191651Sbms retval = 1; 176191651Sbms for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 177191651Sbms if (strcmp(ifa->ifa_name, ifname) != 0) 178191651Sbms continue; 179191651Sbms psu = (sockunion_t *)ifa->ifa_addr; 180191651Sbms if (psu && psu->sa.sa_family == AF_INET) { 181191651Sbms retval = 0; 182191651Sbms memcpy(pina, &psu->sin.sin_addr, 183191651Sbms sizeof(struct in_addr)); 184191651Sbms break; 185191651Sbms } 186191651Sbms } 187191651Sbms 188191651Sbms if (retval != 0) 189191651Sbms errno = EADDRNOTAVAIL; /* XXX */ 190191651Sbms 191191651Sbms freeifaddrs(ifaddrs); 192191651Sbms return (retval); 193191651Sbms} 194191651Sbms#endif /* INET */ 195191651Sbms 19630026Scharnierint 197167346Sbmsmain(int argc, char **argv) 198167346Sbms{ 199167346Sbms char line[LINE_LENGTH]; 200167346Sbms char *p; 201191651Sbms int i, s, s6; 20220529Sfenner 203191651Sbms s = -1; 204191651Sbms s6 = -1; 205191651Sbms#ifdef INET 206167346Sbms s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 207244538Skevlo if (s == -1 && errno != EAFNOSUPPORT) 208191651Sbms err(1, "can't open IPv4 socket"); 209191651Sbms#endif 210191651Sbms#ifdef INET6 211191651Sbms s6 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 212244538Skevlo if (s6 == -1 && errno != EAFNOSUPPORT) 213191651Sbms err(1, "can't open IPv6 socket"); 214191651Sbms#endif 215223510Sjhb if (s == -1 && s6 == -1) 216223510Sjhb errc(1, EPROTONOSUPPORT, "can't open socket"); 21720529Sfenner 218167346Sbms if (argc < 2) { 219167346Sbms if (isatty(STDIN_FILENO)) { 220167346Sbms printf("multicast membership test program; " 221167346Sbms "enter ? for list of commands\n"); 222167346Sbms } 223167346Sbms do { 224167346Sbms if (fgets(line, sizeof(line), stdin) != NULL) { 225167346Sbms if (line[0] != 'f') 226191651Sbms process_cmd(line, s, s6, stdin); 227167346Sbms else { 228167346Sbms /* Get the filename */ 229167346Sbms for (i = 1; isblank(line[i]); i++); 230167346Sbms if ((p = (char*)strchr(line, '\n')) 231167346Sbms != NULL) 232167346Sbms *p = '\0'; 233191651Sbms process_file(&line[i], s, s6); 234167346Sbms } 235167346Sbms } 236167346Sbms } while (!feof(stdin)); 237167346Sbms } else { 238167346Sbms for (i = 1; i < argc; i++) { 239191651Sbms process_file(argv[i], s, s6); 240167346Sbms } 241167346Sbms } 24220529Sfenner 243191651Sbms if (s != -1) 244191651Sbms close(s); 245191651Sbms if (s6 != -1) 246191651Sbms close(s6); 247191651Sbms 248167346Sbms exit (0); 249167346Sbms} 250167346Sbms 251167346Sbmsstatic void 252191651Sbmsprocess_file(char *fname, int s, int s6) 253167346Sbms{ 254167346Sbms char line[80]; 255167346Sbms FILE *fp; 256167346Sbms char *lineptr; 257167346Sbms 258167346Sbms fp = fopen(fname, "r"); 259167346Sbms if (fp == NULL) { 260167346Sbms warn("fopen"); 261167346Sbms return; 262167346Sbms } 263167346Sbms 264167346Sbms /* Skip comments and empty lines. */ 265167346Sbms while (fgets(line, sizeof(line), fp) != NULL) { 266167346Sbms lineptr = line; 267167346Sbms while (isblank(*lineptr)) 268167346Sbms lineptr++; 269167346Sbms if (*lineptr != '#' && *lineptr != '\n') 270191651Sbms process_cmd(lineptr, s, s6, fp); 271167346Sbms } 272167346Sbms 273167346Sbms fclose(fp); 274167346Sbms} 275167346Sbms 276191651Sbms/* 277191651Sbms * Parse join/leave/allow/block arguments, given: 278191651Sbms * str1: group (as AF_INET or AF_INET6 printable) 279191651Sbms * str2: ifname 280191651Sbms * str3: optional source address (may be NULL). 281191651Sbms * This argument must have the same parsed address family as str1. 282191651Sbms * Return the ifindex of ifname, or 0 if any parse element failed. 283191651Sbms */ 284191651Sbmsstatic uint32_t 285191651Sbmsparse_cmd_args(sockunion_t *psu, sockunion_t *psu2, 286191651Sbms const char *str1, const char *str2, const char *str3) 287191651Sbms{ 288191651Sbms struct addrinfo hints; 289191651Sbms struct addrinfo *res; 290191651Sbms uint32_t ifindex; 291191651Sbms int af, error; 292191651Sbms 293191651Sbms assert(psu != NULL); 294191651Sbms assert(str1 != NULL); 295191651Sbms assert(str2 != NULL); 296191651Sbms 297191651Sbms af = AF_UNSPEC; 298191651Sbms 299191651Sbms ifindex = if_nametoindex(str2); 300191651Sbms if (ifindex == 0) 301191651Sbms return (0); 302191651Sbms 303191651Sbms memset(&hints, 0, sizeof(struct addrinfo)); 304191651Sbms hints.ai_flags = AI_NUMERICHOST; 305191651Sbms hints.ai_family = PF_UNSPEC; 306191651Sbms hints.ai_socktype = SOCK_DGRAM; 307191651Sbms 308191651Sbms memset(psu, 0, sizeof(sockunion_t)); 309191651Sbms psu->sa.sa_family = AF_UNSPEC; 310191651Sbms 311191651Sbms error = getaddrinfo(str1, "0", &hints, &res); 312191651Sbms if (error) { 313191651Sbms warnx("getaddrinfo: %s", gai_strerror(error)); 314191651Sbms return (0); 315191651Sbms } 316191651Sbms assert(res != NULL); 317191651Sbms af = res->ai_family; 318191651Sbms memcpy(psu, res->ai_addr, res->ai_addrlen); 319191651Sbms freeaddrinfo(res); 320191651Sbms 321191651Sbms /* sscanf() may pass the empty string. */ 322191651Sbms if (psu2 != NULL && str3 != NULL && *str3 != '\0') { 323191651Sbms memset(psu2, 0, sizeof(sockunion_t)); 324191651Sbms psu2->sa.sa_family = AF_UNSPEC; 325191651Sbms 326191651Sbms /* look for following address family; str3 is *optional*. */ 327191651Sbms hints.ai_family = af; 328191651Sbms error = getaddrinfo(str3, "0", &hints, &res); 329191651Sbms if (error) { 330191651Sbms warnx("getaddrinfo: %s", gai_strerror(error)); 331191651Sbms ifindex = 0; 332191651Sbms } else { 333191651Sbms if (af != res->ai_family) { 334191651Sbms errno = EINVAL; /* XXX */ 335191651Sbms ifindex = 0; 336191651Sbms } 337191651Sbms memcpy(psu2, res->ai_addr, res->ai_addrlen); 338191651Sbms freeaddrinfo(res); 339191651Sbms } 340191651Sbms } 341191651Sbms 342191651Sbms return (ifindex); 343191651Sbms} 344191651Sbms 345191651Sbmsstatic __inline int 346191651Sbmsaf2sock(const int af, int s, int s6) 347191651Sbms{ 348191651Sbms 349191651Sbms#ifdef INET 350191651Sbms if (af == AF_INET) 351191651Sbms return (s); 352191651Sbms#endif 353191651Sbms#ifdef INET6 354191651Sbms if (af == AF_INET6) 355191651Sbms return (s6); 356191651Sbms#endif 357191651Sbms return (-1); 358191651Sbms} 359191651Sbms 360191651Sbmsstatic __inline int 361191651Sbmsaf2socklen(const int af) 362191651Sbms{ 363191651Sbms 364191651Sbms#ifdef INET 365191651Sbms if (af == AF_INET) 366191651Sbms return (sizeof(struct sockaddr_in)); 367191651Sbms#endif 368191651Sbms#ifdef INET6 369191651Sbms if (af == AF_INET6) 370191651Sbms return (sizeof(struct sockaddr_in6)); 371191651Sbms#endif 372191651Sbms return (-1); 373191651Sbms} 374191651Sbms 375167346Sbmsstatic void 376223510Sjhbprocess_cmd(char *cmd, int s, int s6, FILE *fp __unused) 377167346Sbms{ 378167346Sbms char str1[STR_SIZE]; 379167346Sbms char str2[STR_SIZE]; 380170613Sbms char str3[STR_SIZE]; 381191651Sbms mrequnion_t mr; 382191651Sbms sockunion_t su, su2; 383167346Sbms struct ifreq ifr; 384167346Sbms char *line; 385191651Sbms char *toptname; 386191651Sbms void *optval; 387191651Sbms uint32_t fmode, ifindex; 388191651Sbms socklen_t optlen; 389191651Sbms int af, error, f, flags, i, level, n, optname; 390167346Sbms 391191651Sbms af = AF_UNSPEC; 392191651Sbms su.sa.sa_family = AF_UNSPEC; 393191651Sbms su2.sa.sa_family = AF_UNSPEC; 394191651Sbms 395167346Sbms line = cmd; 396167346Sbms while (isblank(*++line)) 397167346Sbms ; /* Skip whitespace. */ 398167346Sbms 399167346Sbms switch (*cmd) { 400167346Sbms case '?': 401167346Sbms usage(); 40220529Sfenner break; 40320529Sfenner 404167346Sbms case 'q': 405167346Sbms close(s); 406167346Sbms exit(0); 407167346Sbms 408167346Sbms case 's': 409167346Sbms if ((sscanf(line, "%d", &n) != 1) || (n < 1)) { 410167346Sbms printf("-1\n"); 411167346Sbms break; 412167346Sbms } 413167346Sbms sleep(n); 414167346Sbms printf("ok\n"); 41520529Sfenner break; 41620529Sfenner 417167346Sbms case 'j': 418167346Sbms case 'l': 419189340Sbms str3[0] = '\0'; 420191651Sbms toptname = ""; 421189340Sbms sscanf(line, "%s %s %s", str1, str2, str3); 422191651Sbms ifindex = parse_cmd_args(&su, &su2, str1, str2, str3); 423191651Sbms if (ifindex == 0) { 424191651Sbms printf("-1\n"); 425191651Sbms break; 426191651Sbms } 427191651Sbms af = su.sa.sa_family; 428191651Sbms#ifdef INET 429191651Sbms if (af == AF_INET) { 430191651Sbms struct in_addr ina; 431191651Sbms 432191651Sbms error = __ifindex_to_primary_ip(ifindex, &ina); 433191651Sbms if (error != 0) { 434191651Sbms warn("primary_ip_lookup %s", str2); 435189340Sbms printf("-1\n"); 436189340Sbms break; 437189340Sbms } 438191651Sbms level = IPPROTO_IP; 439191651Sbms 440191651Sbms if (su2.sa.sa_family != AF_UNSPEC) { 441191651Sbms mr.mrs.imr_multiaddr = su.sin.sin_addr; 442191651Sbms mr.mrs.imr_sourceaddr = su2.sin.sin_addr; 443191651Sbms mr.mrs.imr_interface = ina; 444191651Sbms optname = (*cmd == 'j') ? 445191651Sbms IP_ADD_SOURCE_MEMBERSHIP : 446191651Sbms IP_DROP_SOURCE_MEMBERSHIP; 447191651Sbms toptname = (*cmd == 'j') ? 448189340Sbms "IP_ADD_SOURCE_MEMBERSHIP" : 449191651Sbms "IP_DROP_SOURCE_MEMBERSHIP"; 450191651Sbms optval = (void *)&mr.mrs; 451191651Sbms optlen = sizeof(mr.mrs); 452189340Sbms } else { 453191651Sbms mr.mr.imr_multiaddr = su.sin.sin_addr; 454191651Sbms mr.mr.imr_interface = ina; 455191651Sbms optname = (*cmd == 'j') ? 456191651Sbms IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; 457191651Sbms toptname = (*cmd == 'j') ? 458191651Sbms "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP"; 459191651Sbms optval = (void *)&mr.mr; 460191651Sbms optlen = sizeof(mr.mr); 461191651Sbms } 462223510Sjhb if (s < 0) { 463223510Sjhb warnc(EPROTONOSUPPORT, "setsockopt %s", 464223510Sjhb toptname); 465223510Sjhb } else if (setsockopt(s, level, optname, optval, 466191651Sbms optlen) == 0) { 467189340Sbms printf("ok\n"); 468191651Sbms break; 469191651Sbms } else { 470191651Sbms warn("setsockopt %s", toptname); 471189340Sbms } 472191651Sbms } 473191651Sbms#ifdef INET6 474191651Sbms else 475191651Sbms#endif /* INET with INET6 */ 476191651Sbms#endif /* INET */ 477191651Sbms#ifdef INET6 478191651Sbms if (af == AF_INET6) { 479191651Sbms level = IPPROTO_IPV6; 480191651Sbms if (su2.sa.sa_family != AF_UNSPEC) { 481191651Sbms mr.gr.gsr_interface = ifindex; 482191651Sbms mr.gr.gsr_group = su.ss; 483191651Sbms mr.gr.gsr_source = su2.ss; 484191651Sbms optname = (*cmd == 'j') ? 485191651Sbms MCAST_JOIN_SOURCE_GROUP: 486191651Sbms MCAST_LEAVE_SOURCE_GROUP; 487191651Sbms toptname = (*cmd == 'j') ? 488191651Sbms "MCAST_JOIN_SOURCE_GROUP": 489191651Sbms "MCAST_LEAVE_SOURCE_GROUP"; 490191651Sbms optval = (void *)&mr.gr; 491191651Sbms optlen = sizeof(mr.gr); 492191651Sbms } else { 493191651Sbms mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr; 494191651Sbms mr.mr6.ipv6mr_interface = ifindex; 495191651Sbms optname = (*cmd == 'j') ? 496191651Sbms IPV6_JOIN_GROUP : 497191651Sbms IPV6_LEAVE_GROUP; 498191651Sbms toptname = (*cmd == 'j') ? 499191651Sbms "IPV6_JOIN_GROUP" : 500191651Sbms "IPV6_LEAVE_GROUP"; 501191651Sbms optval = (void *)&mr.mr6; 502191651Sbms optlen = sizeof(mr.mr6); 503191651Sbms } 504223510Sjhb if (s6 < 0) { 505223510Sjhb warnc(EPROTONOSUPPORT, "setsockopt %s", 506223510Sjhb toptname); 507223510Sjhb } else if (setsockopt(s6, level, optname, optval, 508191651Sbms optlen) == 0) { 509191651Sbms printf("ok\n"); 510191651Sbms break; 511191651Sbms } else { 512191651Sbms warn("setsockopt %s", toptname); 513191651Sbms } 514191651Sbms } 515191651Sbms#endif /* INET6 */ 516191651Sbms /* FALLTHROUGH */ 517191651Sbms printf("-1\n"); 518191651Sbms break; 519191651Sbms 520191651Sbms /* 521191651Sbms * Set the socket to include or exclude filter mode, and 522191651Sbms * add some sources to the filterlist, using the full-state API. 523191651Sbms */ 524191651Sbms case 'i': 525191651Sbms case 'e': { 526191651Sbms sockunion_t sources[MAX_ADDRS]; 527191651Sbms struct addrinfo hints; 528191651Sbms struct addrinfo *res; 529191651Sbms char *cp; 530191651Sbms int af1; 531191651Sbms 532191651Sbms n = 0; 533191651Sbms fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE; 534191651Sbms if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) { 535191651Sbms printf("-1\n"); 536191651Sbms break; 537191651Sbms } 538191651Sbms 539191651Sbms ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL); 540191651Sbms if (ifindex == 0 || n < 0 || n > MAX_ADDRS) { 541191651Sbms printf("-1\n"); 542191651Sbms break; 543191651Sbms } 544191651Sbms af = su.sa.sa_family; 545223510Sjhb if (af2sock(af, s, s6) == -1) { 546223510Sjhb warnc(EPROTONOSUPPORT, "setsourcefilter"); 547223510Sjhb break; 548223510Sjhb } 549191651Sbms 550191651Sbms memset(&hints, 0, sizeof(struct addrinfo)); 551191651Sbms hints.ai_flags = AI_NUMERICHOST; 552191651Sbms hints.ai_family = af; 553191651Sbms hints.ai_socktype = SOCK_DGRAM; 554191651Sbms 555191651Sbms for (i = 0; i < n; i++) { 556191651Sbms sockunion_t *psu = (sockunion_t *)&sources[i]; 557191651Sbms /* 558191651Sbms * Trim trailing whitespace, as getaddrinfo() 559191651Sbms * can't cope with it. 560191651Sbms */ 561191651Sbms fgets(str1, sizeof(str1), fp); 562191651Sbms cp = strchr(str1, '\n'); 563191651Sbms if (cp != NULL) 564191651Sbms *cp = '\0'; 565191651Sbms 566191651Sbms res = NULL; 567191651Sbms error = getaddrinfo(str1, "0", &hints, &res); 568191651Sbms if (error) 569191651Sbms break; 570191651Sbms assert(res != NULL); 571191651Sbms 572191651Sbms memset(psu, 0, sizeof(sockunion_t)); 573191651Sbms af1 = res->ai_family; 574191651Sbms if (af1 == af) 575191651Sbms memcpy(psu, res->ai_addr, res->ai_addrlen); 576191651Sbms freeaddrinfo(res); 577191651Sbms if (af1 != af) 578191651Sbms break; 579191651Sbms } 580191651Sbms if (i < n) { 581191651Sbms if (error) 582191651Sbms warnx("getaddrinfo: %s", gai_strerror(error)); 583191651Sbms printf("-1\n"); 584191651Sbms break; 585191651Sbms } 586191651Sbms if (setsourcefilter(af2sock(af, s, s6), ifindex, 587191651Sbms &su.sa, su.sa.sa_len, fmode, n, &sources[0].ss) != 0) 588191651Sbms warn("setsourcefilter"); 589191651Sbms else 590191651Sbms printf("ok\n"); 591191651Sbms } break; 592191651Sbms 593191651Sbms /* 594191651Sbms * Allow or block traffic from a source, using the 595191651Sbms * delta based api. 596191651Sbms */ 597191651Sbms case 't': 598191651Sbms case 'b': { 599191651Sbms str3[0] = '\0'; 600191651Sbms toptname = ""; 601191651Sbms sscanf(line, "%s %s %s", str1, str2, str3); 602191651Sbms ifindex = parse_cmd_args(&su, &su2, str1, str2, str3); 603191651Sbms if (ifindex == 0 || su2.sa.sa_family == AF_UNSPEC) { 604191651Sbms printf("-1\n"); 605191651Sbms break; 606191651Sbms } 607191651Sbms af = su.sa.sa_family; 608223510Sjhb if (af2sock(af, s, s6) == -1) { 609223510Sjhb warnc(EPROTONOSUPPORT, "getsourcefilter"); 610223510Sjhb break; 611223510Sjhb } 612191651Sbms 613191651Sbms /* First determine our current filter mode. */ 614191651Sbms n = 0; 615191651Sbms if (getsourcefilter(af2sock(af, s, s6), ifindex, 616191651Sbms &su.sa, su.sa.sa_len, &fmode, &n, NULL) != 0) { 617191651Sbms warn("getsourcefilter"); 618191651Sbms break; 619191651Sbms } 620191651Sbms#ifdef INET 621191651Sbms if (af == AF_INET) { 622191651Sbms struct in_addr ina; 623191651Sbms 624191651Sbms error = __ifindex_to_primary_ip(ifindex, &ina); 625191651Sbms if (error != 0) { 626191651Sbms warn("primary_ip_lookup %s", str2); 627189340Sbms printf("-1\n"); 628189340Sbms break; 629189340Sbms } 630191651Sbms level = IPPROTO_IP; 631191651Sbms optval = (void *)&mr.mrs; 632191651Sbms optlen = sizeof(mr.mrs); 633191651Sbms mr.mrs.imr_multiaddr = su.sin.sin_addr; 634191651Sbms mr.mrs.imr_sourceaddr = su2.sin.sin_addr; 635191651Sbms mr.mrs.imr_interface = ina; 636191651Sbms if (fmode == MCAST_EXCLUDE) { 637191651Sbms /* Any-source mode socket membership. */ 638191651Sbms optname = (*cmd == 't') ? 639191651Sbms IP_UNBLOCK_SOURCE : 640191651Sbms IP_BLOCK_SOURCE; 641191651Sbms toptname = (*cmd == 't') ? 642191651Sbms "IP_UNBLOCK_SOURCE" : 643191651Sbms "IP_BLOCK_SOURCE"; 644189340Sbms } else { 645191651Sbms /* Source-specific mode socket membership. */ 646191651Sbms optname = (*cmd == 't') ? 647191651Sbms IP_ADD_SOURCE_MEMBERSHIP : 648191651Sbms IP_DROP_SOURCE_MEMBERSHIP; 649191651Sbms toptname = (*cmd == 't') ? 650191651Sbms "IP_ADD_SOURCE_MEMBERSHIP" : 651191651Sbms "IP_DROP_SOURCE_MEMBERSHIP"; 652191651Sbms } 653191651Sbms if (setsockopt(s, level, optname, optval, 654191651Sbms optlen) == 0) { 655189340Sbms printf("ok\n"); 656191651Sbms break; 657191651Sbms } else { 658191651Sbms warn("setsockopt %s", toptname); 659189340Sbms } 660167346Sbms } 661191651Sbms#ifdef INET6 662191651Sbms else 663191651Sbms#endif /* INET with INET6 */ 664191651Sbms#endif /* INET */ 665191651Sbms#ifdef INET6 666191651Sbms if (af == AF_INET6) { 667191651Sbms level = IPPROTO_IPV6; 668191651Sbms mr.gr.gsr_interface = ifindex; 669191651Sbms mr.gr.gsr_group = su.ss; 670191651Sbms mr.gr.gsr_source = su2.ss; 671191651Sbms if (fmode == MCAST_EXCLUDE) { 672191651Sbms /* Any-source mode socket membership. */ 673191651Sbms optname = (*cmd == 't') ? 674191651Sbms MCAST_UNBLOCK_SOURCE : 675191651Sbms MCAST_BLOCK_SOURCE; 676191651Sbms toptname = (*cmd == 't') ? 677191651Sbms "MCAST_UNBLOCK_SOURCE" : 678191651Sbms "MCAST_BLOCK_SOURCE"; 679191651Sbms } else { 680191651Sbms /* Source-specific mode socket membership. */ 681191651Sbms optname = (*cmd == 't') ? 682191651Sbms MCAST_JOIN_SOURCE_GROUP : 683191651Sbms MCAST_LEAVE_SOURCE_GROUP; 684191651Sbms toptname = (*cmd == 't') ? 685191651Sbms "MCAST_JOIN_SOURCE_GROUP": 686191651Sbms "MCAST_LEAVE_SOURCE_GROUP"; 687191651Sbms } 688191651Sbms optval = (void *)&mr.gr; 689191651Sbms optlen = sizeof(mr.gr); 690191651Sbms if (setsockopt(s6, level, optname, optval, 691191651Sbms optlen) == 0) { 692191651Sbms printf("ok\n"); 693191651Sbms break; 694191651Sbms } else { 695191651Sbms warn("setsockopt %s", toptname); 696191651Sbms } 697191651Sbms } 698191651Sbms#endif /* INET6 */ 699191651Sbms /* FALLTHROUGH */ 700191651Sbms printf("-1\n"); 701191651Sbms } break; 70220529Sfenner 703191651Sbms case 'g': { 704191651Sbms sockunion_t sources[MAX_ADDRS]; 705191651Sbms char addrbuf[NI_MAXHOST]; 706191651Sbms int nreqsrc, nsrc; 707191651Sbms 708191651Sbms if ((sscanf(line, "%s %s %d", str1, str2, &nreqsrc)) != 3) { 709191651Sbms printf("-1\n"); 710191651Sbms break; 711191651Sbms } 712191651Sbms ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL); 713191651Sbms if (ifindex == 0 || (n < 0 || n > MAX_ADDRS)) { 714191651Sbms printf("-1\n"); 715191651Sbms break; 716191651Sbms } 717191651Sbms 718191651Sbms af = su.sa.sa_family; 719223510Sjhb if (af2sock(af, s, s6) == -1) { 720223510Sjhb warnc(EPROTONOSUPPORT, "getsourcefilter"); 721223510Sjhb break; 722223510Sjhb } 723191651Sbms nsrc = nreqsrc; 724191651Sbms if (getsourcefilter(af2sock(af, s, s6), ifindex, &su.sa, 725191651Sbms su.sa.sa_len, &fmode, &nsrc, &sources[0].ss) != 0) { 726191651Sbms warn("getsourcefilter"); 727191651Sbms printf("-1\n"); 728191651Sbms break; 729191651Sbms } 730191651Sbms printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" : 731191651Sbms "exclude"); 732191651Sbms printf("%d\n", nsrc); 733191651Sbms 734191651Sbms nsrc = MIN(nreqsrc, nsrc); 735191651Sbms fprintf(stderr, "hexdump of sources:\n"); 736191651Sbms uint8_t *bp = (uint8_t *)&sources[0]; 737191651Sbms for (i = 0; i < (nsrc * sizeof(sources[0])); i++) { 738191651Sbms fprintf(stderr, "%02x", bp[i]); 739191651Sbms } 740191651Sbms fprintf(stderr, "\nend hexdump\n"); 741191651Sbms 742191651Sbms qsort(sources, nsrc, af2socklen(af), su_cmp); 743191651Sbms for (i = 0; i < nsrc; i++) { 744191651Sbms sockunion_t *psu = (sockunion_t *)&sources[i]; 745191651Sbms addrbuf[0] = '\0'; 746191651Sbms error = getnameinfo(&psu->sa, psu->sa.sa_len, 747191651Sbms addrbuf, sizeof(addrbuf), NULL, 0, 748191651Sbms NI_NUMERICHOST); 749191651Sbms if (error) 750191651Sbms warnx("getnameinfo: %s", gai_strerror(error)); 751191651Sbms else 752191651Sbms printf("%s\n", addrbuf); 753191651Sbms } 754191651Sbms printf("ok\n"); 755191651Sbms } break; 756191651Sbms 757191651Sbms /* link-layer stuff follows. */ 758191651Sbms 759167346Sbms case 'a': 760167346Sbms case 'd': { 761167346Sbms struct sockaddr_dl *dlp; 762167346Sbms struct ether_addr *ep; 763167346Sbms 764167346Sbms memset(&ifr, 0, sizeof(struct ifreq)); 76536440Sjulian dlp = (struct sockaddr_dl *)&ifr.ifr_addr; 76636440Sjulian dlp->sdl_len = sizeof(struct sockaddr_dl); 76736440Sjulian dlp->sdl_family = AF_LINK; 76836440Sjulian dlp->sdl_index = 0; 76936440Sjulian dlp->sdl_nlen = 0; 770167346Sbms dlp->sdl_alen = ETHER_ADDR_LEN; 77136440Sjulian dlp->sdl_slen = 0; 772167346Sbms if (sscanf(line, "%s %s", str1, str2) != 2) { 773167346Sbms warnc(EINVAL, "sscanf"); 774167346Sbms break; 775167346Sbms } 776167346Sbms ep = ether_aton(str2); 777167346Sbms if (ep == NULL) { 778167346Sbms warnc(EINVAL, "ether_aton"); 779167346Sbms break; 780167346Sbms } 781167346Sbms strlcpy(ifr.ifr_name, str1, IF_NAMESIZE); 782167346Sbms memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN); 783167346Sbms if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI, 784191651Sbms &ifr) == -1) { 785167346Sbms warn("ioctl SIOCADDMULTI/SIOCDELMULTI"); 786191651Sbms printf("-1\n"); 787191651Sbms } else 788167346Sbms printf("ok\n"); 78920529Sfenner break; 790167346Sbms } 79120529Sfenner 792167346Sbms case 'm': 793191651Sbms fprintf(stderr, 794191651Sbms "warning: IFF_ALLMULTI cannot be set from userland " 795167346Sbms "in FreeBSD; command ignored.\n"); 796191651Sbms printf("-1\n"); 79720529Sfenner break; 798189340Sbms 799167346Sbms case 'p': 800167346Sbms if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) { 801167346Sbms printf("-1\n"); 802167346Sbms break; 803167346Sbms } 804167346Sbms if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) { 805167346Sbms warn("ioctl SIOCGIFFLAGS"); 806167346Sbms break; 807167346Sbms } 808167346Sbms flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); 809167346Sbms if (f == 0) { 810191651Sbms flags &= ~IFF_PPROMISC; 811167346Sbms } else { 812191651Sbms flags |= IFF_PPROMISC; 813167346Sbms } 814167346Sbms ifr.ifr_flags = flags & 0xffff; 815167346Sbms ifr.ifr_flagshigh = flags >> 16; 816167346Sbms if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) 817167346Sbms warn("ioctl SIOCGIFFLAGS"); 818167346Sbms else 819167346Sbms printf( "changed to 0x%08x\n", flags ); 820167346Sbms break; 82120529Sfenner 822167346Sbms case '\n': 823167346Sbms break; 824167346Sbms default: 825167346Sbms printf("invalid command\n"); 826167346Sbms break; 827167346Sbms } 828167346Sbms} 82920529Sfenner 830167346Sbmsstatic void 831167346Sbmsusage(void) 832167346Sbms{ 833167346Sbms 834191651Sbms printf("j mcast-addr ifname [src-addr] - join IP multicast group\n"); 835191651Sbms printf("l mcast-addr ifname [src-addr] - leave IP multicast group\n"); 836191651Sbms printf( 837191651Sbms"i mcast-addr ifname n - set n include mode src filter\n"); 838191651Sbms printf( 839191651Sbms"e mcast-addr ifname n - set n exclude mode src filter\n"); 840191651Sbms printf("t mcast-addr ifname src-addr - allow traffic from src\n"); 841191651Sbms printf("b mcast-addr ifname src-addr - block traffic from src\n"); 842191651Sbms printf("g mcast-addr ifname n - get and show n src filters\n"); 843191651Sbms printf("a ifname mac-addr - add link multicast filter\n"); 844191651Sbms printf("d ifname mac-addr - delete link multicast filter\n"); 845167346Sbms printf("m ifname 1/0 - set/clear ether allmulti flag\n"); 846167346Sbms printf("p ifname 1/0 - set/clear ether promisc flag\n"); 847167346Sbms printf("f filename - read command(s) from file\n"); 848167346Sbms printf("s seconds - sleep for some time\n"); 849167346Sbms printf("q - quit\n"); 850167346Sbms} 851191651Sbms 852