1/*- 2 * Copyright (c) 2007-2009 Bruce Simpson. 3 * Copyright (c) 2000 Wilbert De Graaf. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31/* 32 * Diagnostic and test utility for multicast sockets. 33 * TODO: Support embedded KAME Scope ID in IPv6 group addresses. 34 * TODO: Use IPv4 link-local address when source address selection 35 * is implemented; use MCAST_JOIN_SOURCE for IPv4. 36 */ 37 38#define INET6 39 40#include <sys/cdefs.h> 41 42#include <sys/types.h> 43#include <sys/param.h> 44#include <sys/errno.h> 45#include <sys/socket.h> 46#include <sys/time.h> 47#include <sys/ioctl.h> 48 49#include <net/if.h> 50#include <net/if_dl.h> 51#include <net/ethernet.h> 52#include <netinet/in.h> 53#include <netinet/in_systm.h> 54#include <netinet/ip.h> 55#include <netinet/ip_var.h> 56#ifdef INET6 57#include <netinet/in.h> 58#include <netinet/ip6.h> 59#endif 60 61#include <assert.h> 62#include <stdlib.h> 63#include <stdio.h> 64#include <string.h> 65#include <ctype.h> 66#include <errno.h> 67#include <err.h> 68#include <unistd.h> 69 70#include <arpa/inet.h> 71#include <netdb.h> 72#include <ifaddrs.h> 73 74#ifdef IP_ADD_SOURCE_MEMBERSHIP 75#define HAS_SSM 1 76#endif 77 78union sockunion { 79 struct sockaddr_storage ss; 80 struct sockaddr sa; 81 struct sockaddr_dl sdl; 82 struct sockaddr_in sin; 83#ifdef INET6 84 struct sockaddr_in6 sin6; 85#endif 86}; 87typedef union sockunion sockunion_t; 88 89union mrequnion { 90 struct ip_mreq mr; 91#ifdef HAS_SSM 92 struct ip_mreq_source mrs; 93#endif 94#ifdef INET6 95 struct ipv6_mreq mr6; 96#ifdef HAS_SSM 97 struct group_source_req gr; 98#endif 99#endif 100}; 101typedef union mrequnion mrequnion_t; 102 103#define MAX_ADDRS 20 104#define STR_SIZE 64 105#define LINE_LENGTH 80 106 107static int __ifindex_to_primary_ip(const uint32_t, struct in_addr *); 108static uint32_t parse_cmd_args(sockunion_t *, sockunion_t *, 109 const char *, const char *, const char *); 110static void process_file(char *, int, int); 111static void process_cmd(char*, int, int, FILE *); 112static int su_cmp(const void *, const void *); 113static void usage(void); 114 115/* 116 * Ordering predicate for qsort(). 117 */ 118static int 119su_cmp(const void *a, const void *b) 120{ 121 const sockunion_t *sua = (const sockunion_t *)a; 122 const sockunion_t *sub = (const sockunion_t *)b; 123 124 assert(sua->sa.sa_family == sub->sa.sa_family); 125 126 switch (sua->sa.sa_family) { 127 case AF_INET: 128 return ((int)(sua->sin.sin_addr.s_addr - 129 sub->sin.sin_addr.s_addr)); 130 break; 131#ifdef INET6 132 case AF_INET6: 133 return (memcmp(&sua->sin6.sin6_addr, &sub->sin6.sin6_addr, 134 sizeof(struct in6_addr))); 135 break; 136#endif 137 default: 138 break; 139 } 140 141 assert(sua->sa.sa_len == sub->sa.sa_len); 142 return (memcmp(sua, sub, sua->sa.sa_len)); 143} 144 145/* 146 * Internal: Map an interface index to primary IPv4 address. 147 * This is somewhat inefficient. This is a useful enough operation 148 * that it probably belongs in the C library. 149 * Return zero if found, -1 on error, 1 on not found. 150 */ 151static int 152__ifindex_to_primary_ip(const uint32_t ifindex, struct in_addr *pina) 153{ 154 char ifname[IFNAMSIZ]; 155 struct ifaddrs *ifa; 156 struct ifaddrs *ifaddrs; 157 sockunion_t *psu; 158 int retval; 159 160 assert(ifindex != 0); 161 162 retval = -1; 163 if (if_indextoname(ifindex, ifname) == NULL) 164 return (retval); 165 if (getifaddrs(&ifaddrs) < 0) 166 return (retval); 167 168 /* 169 * Find the ifaddr entry corresponding to the interface name, 170 * and return the first matching IPv4 address. 171 */ 172 retval = 1; 173 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 174 if (strcmp(ifa->ifa_name, ifname) != 0) 175 continue; 176 psu = (sockunion_t *)ifa->ifa_addr; 177 if (psu && psu->sa.sa_family == AF_INET) { 178 retval = 0; 179 memcpy(pina, &psu->sin.sin_addr, 180 sizeof(struct in_addr)); 181 break; 182 } 183 } 184 185 if (retval != 0) 186 errno = EADDRNOTAVAIL; /* XXX */ 187 188 freeifaddrs(ifaddrs); 189 return (retval); 190} 191 192int 193main(int argc, char **argv) 194{ 195 char line[LINE_LENGTH]; 196 char *p; 197 int i, s, s6; 198 199 s = -1; 200 s6 = -1; 201 s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 202 if (s == -1) 203 err(1, "can't open IPv4 socket"); 204#ifdef INET6 205 s6 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 206 if (s6 == -1) 207 err(1, "can't open IPv6 socket"); 208#endif 209 210 if (argc < 2) { 211 if (isatty(STDIN_FILENO)) { 212 printf("multicast membership test program; " 213 "enter ? for list of commands\n"); 214 } 215 do { 216 if (fgets(line, sizeof(line), stdin) != NULL) { 217 if (line[0] != 'f') 218 process_cmd(line, s, s6, stdin); 219 else { 220 /* Get the filename */ 221 for (i = 1; isblank(line[i]); i++); 222 if ((p = (char*)strchr(line, '\n')) 223 != NULL) 224 *p = '\0'; 225 process_file(&line[i], s, s6); 226 } 227 } 228 } while (!feof(stdin)); 229 } else { 230 for (i = 1; i < argc; i++) { 231 process_file(argv[i], s, s6); 232 } 233 } 234 235 if (s != -1) 236 close(s); 237 if (s6 != -1) 238 close(s6); 239 240 exit (0); 241} 242 243static void 244process_file(char *fname, int s, int s6) 245{ 246 char line[80]; 247 FILE *fp; 248 char *lineptr; 249 250 fp = fopen(fname, "r"); 251 if (fp == NULL) { 252 warn("fopen"); 253 return; 254 } 255 256 /* Skip comments and empty lines. */ 257 while (fgets(line, sizeof(line), fp) != NULL) { 258 lineptr = line; 259 while (isblank(*lineptr)) 260 lineptr++; 261 if (*lineptr != '#' && *lineptr != '\n') 262 process_cmd(lineptr, s, s6, fp); 263 } 264 265 fclose(fp); 266} 267 268/* 269 * Parse join/leave/allow/block arguments, given: 270 * str1: group (as AF_INET or AF_INET6 printable) 271 * str2: ifname 272 * str3: optional source address (may be NULL). 273 * This argument must have the same parsed address family as str1. 274 * Return the ifindex of ifname, or 0 if any parse element failed. 275 */ 276static uint32_t 277parse_cmd_args(sockunion_t *psu, sockunion_t *psu2, 278 const char *str1, const char *str2, const char *str3) 279{ 280 struct addrinfo hints; 281 struct addrinfo *res; 282 uint32_t ifindex; 283 int af, error; 284 285 assert(psu != NULL); 286 assert(str1 != NULL); 287 assert(str2 != NULL); 288 289 af = AF_UNSPEC; 290 291 ifindex = if_nametoindex(str2); 292 if (ifindex == 0) 293 return (0); 294 295 memset(&hints, 0, sizeof(struct addrinfo)); 296 hints.ai_flags = AI_NUMERICHOST; 297 hints.ai_family = PF_UNSPEC; 298 hints.ai_socktype = SOCK_DGRAM; 299 300 memset(psu, 0, sizeof(sockunion_t)); 301 psu->sa.sa_family = AF_UNSPEC; 302 303 error = getaddrinfo(str1, "0", &hints, &res); 304 if (error) { 305 warnx("getaddrinfo: %s", gai_strerror(error)); 306 return (0); 307 } 308 assert(res != NULL); 309 af = res->ai_family; 310 memcpy(psu, res->ai_addr, res->ai_addrlen); 311 freeaddrinfo(res); 312 313 /* sscanf() may pass the empty string. */ 314 if (psu2 != NULL && str3 != NULL && *str3 != '\0') { 315 memset(psu2, 0, sizeof(sockunion_t)); 316 psu2->sa.sa_family = AF_UNSPEC; 317 318 /* look for following address family; str3 is *optional*. */ 319 hints.ai_family = af; 320 error = getaddrinfo(str3, "0", &hints, &res); 321 if (error) { 322 warnx("getaddrinfo: %s", gai_strerror(error)); 323 ifindex = 0; 324 } else { 325 if (af != res->ai_family) { 326 errno = EINVAL; /* XXX */ 327 ifindex = 0; 328 } 329 memcpy(psu2, res->ai_addr, res->ai_addrlen); 330 freeaddrinfo(res); 331 } 332 } 333 334 return (ifindex); 335} 336 337static __inline int 338af2sock(const int af, int s, int s6) 339{ 340 341 if (af == AF_INET) 342 return (s); 343#ifdef INET6 344 if (af == AF_INET6) 345 return (s6); 346#endif 347 return (-1); 348} 349 350static void 351process_cmd(char *cmd, int s, int s6 __unused, FILE *fp __unused) 352{ 353 char str1[STR_SIZE]; 354 char str2[STR_SIZE]; 355 char str3[STR_SIZE]; 356 mrequnion_t mr; 357 sockunion_t su, su2; 358 struct ifreq ifr; 359 char *line; 360 char *toptname; 361 void *optval; 362 uint32_t fmode, ifindex; 363 socklen_t optlen; 364 int af, error, i, level, n = 0, optname; 365#ifndef __APPLE__ 366 int f, flags; 367#endif /* __APPLE__ */ 368 369 af = AF_UNSPEC; 370 su.sa.sa_family = AF_UNSPEC; 371 su2.sa.sa_family = AF_UNSPEC; 372 373 line = cmd; 374 while (isblank(*++line)) 375 ; /* Skip whitespace. */ 376 377 switch (*cmd) { 378 case '?': 379 usage(); 380 break; 381 382 case 'q': 383 close(s); 384 exit(0); 385 386 case 's': 387 if ((sscanf(line, "%d", &n) != 1) || (n < 1)) { 388 printf("-1\n"); 389 break; 390 } 391 sleep(n); 392 printf("ok\n"); 393 break; 394 395 case 'j': 396 case 'l': 397 str3[0] = '\0'; 398 toptname = ""; 399 sscanf(line, "%s %s %s", str1, str2, str3); 400 ifindex = parse_cmd_args(&su, &su2, str1, str2, str3); 401 if (ifindex == 0) { 402 printf("-1\n"); 403 break; 404 } 405 af = su.sa.sa_family; 406 if (af == AF_INET) { 407 struct in_addr ina; 408 409 error = __ifindex_to_primary_ip(ifindex, &ina); 410 if (error != 0) { 411 warn("primary_ip_lookup %s", str2); 412 printf("-1\n"); 413 break; 414 } 415 level = IPPROTO_IP; 416 417#ifdef HAS_SSM 418 if (su2.sa.sa_family != AF_UNSPEC) { 419 mr.mrs.imr_multiaddr = su.sin.sin_addr; 420 mr.mrs.imr_sourceaddr = su2.sin.sin_addr; 421 mr.mrs.imr_interface = ina; 422 optname = (*cmd == 'j') ? 423 IP_ADD_SOURCE_MEMBERSHIP : 424 IP_DROP_SOURCE_MEMBERSHIP; 425 toptname = (*cmd == 'j') ? 426 "IP_ADD_SOURCE_MEMBERSHIP" : 427 "IP_DROP_SOURCE_MEMBERSHIP"; 428 optval = (void *)&mr.mrs; 429 optlen = sizeof(mr.mrs); 430 } else { 431#endif 432 mr.mr.imr_multiaddr = su.sin.sin_addr; 433 mr.mr.imr_interface = ina; 434 optname = (*cmd == 'j') ? 435 IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; 436 toptname = (*cmd == 'j') ? 437 "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP"; 438 optval = (void *)&mr.mr; 439 optlen = sizeof(mr.mr); 440#ifdef HAS_SSM 441 } 442#endif 443 if (setsockopt(s, level, optname, optval, 444 optlen) == 0) { 445 printf("ok\n"); 446 break; 447 } else { 448 warn("setsockopt %s", toptname); 449 } 450 } 451#ifdef INET6 452 else 453#endif /* INET6 */ 454#ifdef INET6 455 if (af == AF_INET6) { 456 level = IPPROTO_IPV6; 457#ifdef HAS_SSM 458 if (su2.sa.sa_family != AF_UNSPEC) { 459 mr.gr.gsr_interface = ifindex; 460 mr.gr.gsr_group = su.ss; 461 mr.gr.gsr_source = su2.ss; 462 optname = (*cmd == 'j') ? 463 MCAST_JOIN_SOURCE_GROUP: 464 MCAST_LEAVE_SOURCE_GROUP; 465 toptname = (*cmd == 'j') ? 466 "MCAST_JOIN_SOURCE_GROUP": 467 "MCAST_LEAVE_SOURCE_GROUP"; 468 optval = (void *)&mr.gr; 469 optlen = sizeof(mr.gr); 470 } else { 471#endif 472 mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr; 473 mr.mr6.ipv6mr_interface = ifindex; 474 optname = (*cmd == 'j') ? 475 IPV6_JOIN_GROUP : 476 IPV6_LEAVE_GROUP; 477 toptname = (*cmd == 'j') ? 478 "IPV6_JOIN_GROUP" : 479 "IPV6_LEAVE_GROUP"; 480 optval = (void *)&mr.mr6; 481 optlen = sizeof(mr.mr6); 482#ifdef HAS_SSM 483 } 484#endif 485 if (setsockopt(s6, level, optname, optval, 486 optlen) == 0) { 487 printf("ok\n"); 488 break; 489 } else { 490 warn("setsockopt %s", toptname); 491 } 492 } 493#endif /* INET6 */ 494 /* FALLTHROUGH */ 495 printf("-1\n"); 496 break; 497 498#ifdef HAS_SSM 499 /* 500 * Set the socket to include or exclude filter mode, and 501 * add some sources to the filterlist, using the full-state API. 502 */ 503 case 'i': 504 case 'e': { 505 sockunion_t sources[MAX_ADDRS]; 506 struct addrinfo hints; 507 struct addrinfo *res; 508 char *cp; 509 int af1; 510 511 n = 0; 512 fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE; 513 if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) { 514 printf("-1\n"); 515 break; 516 } 517 518 ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL); 519 if (ifindex == 0 || n < 0 || n > MAX_ADDRS) { 520 printf("-1\n"); 521 break; 522 } 523 af = su.sa.sa_family; 524 525 memset(&hints, 0, sizeof(struct addrinfo)); 526 hints.ai_flags = AI_NUMERICHOST; 527 hints.ai_family = af; 528 hints.ai_socktype = SOCK_DGRAM; 529 530 for (i = 0; i < n; i++) { 531 sockunion_t *psu = (sockunion_t *)&sources[i]; 532 /* 533 * Trim trailing whitespace, as getaddrinfo() 534 * can't cope with it. 535 */ 536 fgets(str1, sizeof(str1), fp); 537 cp = strchr(str1, '\n'); 538 if (cp != NULL) 539 *cp = '\0'; 540 541 res = NULL; 542 error = getaddrinfo(str1, "0", &hints, &res); 543 if (error) 544 break; 545 assert(res != NULL); 546 547 memset(psu, 0, sizeof(sockunion_t)); 548 af1 = res->ai_family; 549 if (af1 == af) 550 memcpy(psu, res->ai_addr, res->ai_addrlen); 551 freeaddrinfo(res); 552 if (af1 != af) 553 break; 554 } 555 if (i < n) { 556 if (error) 557 warnx("getaddrinfo: %s", gai_strerror(error)); 558 printf("-1\n"); 559 break; 560 } 561 if (setsourcefilter(af2sock(af, s, s6), ifindex, 562 &su.sa, su.sa.sa_len, fmode, n, &sources[0].ss) != 0) 563 warn("setsourcefilter"); 564 else 565 printf("ok\n"); 566 } break; 567 568 /* 569 * Allow or block traffic from a source, using the 570 * delta based api. 571 */ 572 case 't': 573 case 'b': { 574 str3[0] = '\0'; 575 toptname = ""; 576 sscanf(line, "%s %s %s", str1, str2, str3); 577 ifindex = parse_cmd_args(&su, &su2, str1, str2, str3); 578 if (ifindex == 0 || su2.sa.sa_family == AF_UNSPEC) { 579 printf("-1\n"); 580 break; 581 } 582 af = su.sa.sa_family; 583 584 /* First determine our current filter mode. */ 585 n = 0; 586 if (getsourcefilter(af2sock(af, s, s6), ifindex, 587 &su.sa, su.sa.sa_len, &fmode, (uint32_t *)&n, NULL) != 0) { 588 warn("getsourcefilter"); 589 break; 590 } 591 if (af == AF_INET) { 592 struct in_addr ina; 593 594 error = __ifindex_to_primary_ip(ifindex, &ina); 595 if (error != 0) { 596 warn("primary_ip_lookup %s", str2); 597 printf("-1\n"); 598 break; 599 } 600 level = IPPROTO_IP; 601 optval = (void *)&mr.mrs; 602 optlen = sizeof(mr.mrs); 603 mr.mrs.imr_multiaddr = su.sin.sin_addr; 604 mr.mrs.imr_sourceaddr = su2.sin.sin_addr; 605 mr.mrs.imr_interface = ina; 606 if (fmode == MCAST_EXCLUDE) { 607 /* Any-source mode socket membership. */ 608 optname = (*cmd == 't') ? 609 IP_UNBLOCK_SOURCE : 610 IP_BLOCK_SOURCE; 611 toptname = (*cmd == 't') ? 612 "IP_UNBLOCK_SOURCE" : 613 "IP_BLOCK_SOURCE"; 614 } else { 615 /* Source-specific mode socket membership. */ 616 optname = (*cmd == 't') ? 617 IP_ADD_SOURCE_MEMBERSHIP : 618 IP_DROP_SOURCE_MEMBERSHIP; 619 toptname = (*cmd == 't') ? 620 "IP_ADD_SOURCE_MEMBERSHIP" : 621 "IP_DROP_SOURCE_MEMBERSHIP"; 622 } 623 if (setsockopt(s, level, optname, optval, 624 optlen) == 0) { 625 printf("ok\n"); 626 break; 627 } else { 628 warn("setsockopt %s", toptname); 629 } 630 } 631#ifdef INET6 632 else 633#endif /* INET6 */ 634#ifdef INET6 635 if (af == AF_INET6) { 636 level = IPPROTO_IPV6; 637 mr.gr.gsr_interface = ifindex; 638 mr.gr.gsr_group = su.ss; 639 mr.gr.gsr_source = su2.ss; 640 if (fmode == MCAST_EXCLUDE) { 641 /* Any-source mode socket membership. */ 642 optname = (*cmd == 't') ? 643 MCAST_UNBLOCK_SOURCE : 644 MCAST_BLOCK_SOURCE; 645 toptname = (*cmd == 't') ? 646 "MCAST_UNBLOCK_SOURCE" : 647 "MCAST_BLOCK_SOURCE"; 648 } else { 649 /* Source-specific mode socket membership. */ 650 optname = (*cmd == 't') ? 651 MCAST_JOIN_SOURCE_GROUP : 652 MCAST_LEAVE_SOURCE_GROUP; 653 toptname = (*cmd == 't') ? 654 "MCAST_JOIN_SOURCE_GROUP": 655 "MCAST_LEAVE_SOURCE_GROUP"; 656 } 657 optval = (void *)&mr.gr; 658 optlen = sizeof(mr.gr); 659 if (setsockopt(s6, level, optname, optval, 660 optlen) == 0) { 661 printf("ok\n"); 662 break; 663 } else { 664 warn("setsockopt %s", toptname); 665 } 666 } 667#endif /* INET6 */ 668 /* FALLTHROUGH */ 669 printf("-1\n"); 670 } break; 671 672 case 'g': { 673 sockunion_t sources[MAX_ADDRS]; 674 char addrbuf[NI_MAXHOST]; 675 int nreqsrc, nsrc; 676 677 if ((sscanf(line, "%s %s %d", str1, str2, &nreqsrc)) != 3) { 678 printf("-1\n"); 679 break; 680 } 681 ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL); 682 if (ifindex == 0 || (n < 0 || n > MAX_ADDRS)) { 683 printf("-1\n"); 684 break; 685 } 686 687 af = su.sa.sa_family; 688 nsrc = nreqsrc; 689 if (getsourcefilter(af2sock(af, s, s6), ifindex, &su.sa, 690 su.sa.sa_len, &fmode, (uint32_t *)&nsrc, 691 &sources[0].ss) != 0) { 692 warn("getsourcefilter"); 693 printf("-1\n"); 694 break; 695 } 696 printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" : 697 "exclude"); 698 printf("%d\n", nsrc); 699 700 nsrc = MIN(nreqsrc, nsrc); 701 fprintf(stderr, "hexdump of sources:\n"); 702 uint8_t *bp = (uint8_t *)&sources[0]; 703 for (i = 0; i < (nsrc * sizeof(sources[0])); i++) { 704 fprintf(stderr, "%02x", bp[i]); 705 } 706 fprintf(stderr, "\nend hexdump\n"); 707 708 qsort(sources, nsrc, sizeof (sockunion_t), su_cmp); 709 for (i = 0; i < nsrc; i++) { 710 sockunion_t *psu = (sockunion_t *)&sources[i]; 711 addrbuf[0] = '\0'; 712 error = getnameinfo(&psu->sa, psu->sa.sa_len, 713 addrbuf, sizeof(addrbuf), NULL, 0, 714 NI_NUMERICHOST); 715 if (error) 716 warnx("getnameinfo: %s", gai_strerror(error)); 717 else 718 printf("%s\n", addrbuf); 719 } 720 printf("ok\n"); 721 } break; 722#endif 723 /* link-layer stuff follows. */ 724 725 case 'a': 726 case 'd': { 727 struct sockaddr_dl *dlp; 728 struct ether_addr *ep; 729 730 memset(&ifr, 0, sizeof(struct ifreq)); 731 dlp = (struct sockaddr_dl *)&ifr.ifr_addr; 732 dlp->sdl_len = sizeof(struct sockaddr_dl); 733 dlp->sdl_family = AF_LINK; 734 dlp->sdl_index = 0; 735 dlp->sdl_nlen = 0; 736 dlp->sdl_alen = ETHER_ADDR_LEN; 737 dlp->sdl_slen = 0; 738 if (sscanf(line, "%s %s", str1, str2) != 2) { 739 warnc(EINVAL, "sscanf"); 740 break; 741 } 742 ep = ether_aton(str2); 743 if (ep == NULL) { 744 warnc(EINVAL, "ether_aton"); 745 break; 746 } 747 strlcpy(ifr.ifr_name, str1, IF_NAMESIZE); 748 memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN); 749 if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI, 750 &ifr) == -1) { 751 warn("ioctl SIOCADDMULTI/SIOCDELMULTI"); 752 printf("-1\n"); 753 } else 754 printf("ok\n"); 755 break; 756 } 757 758 case 'm': 759 fprintf(stderr, 760 "warning: IFF_ALLMULTI cannot be set from userland " 761 "in Darwin; command ignored.\n"); 762 printf("-1\n"); 763 break; 764 765#ifndef __APPLE__ 766 case 'p': 767 if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) { 768 printf("-1\n"); 769 break; 770 } 771 if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) { 772 warn("ioctl SIOCGIFFLAGS"); 773 break; 774 } 775 flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); 776 if (f == 0) { 777 flags &= ~IFF_PPROMISC; 778 } else { 779 flags |= IFF_PPROMISC; 780 } 781 ifr.ifr_flags = flags & 0xffff; 782 ifr.ifr_flagshigh = flags >> 16; 783 if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) 784 warn("ioctl SIOCGIFFLAGS"); 785 else 786 printf( "changed to 0x%08x\n", flags ); 787 break; 788#endif /* __APPLE__ */ 789 case '\n': 790 break; 791 default: 792 printf("invalid command\n"); 793 break; 794 } 795} 796 797static void 798usage(void) 799{ 800 801#ifndef HAS_SSM 802 printf("j mcast-addr ifname - join IP multicast group\n"); 803 printf("l mcast-addr ifname - leave IP multicast group\n"); 804#else /* HAS_SSM */ 805 printf("j mcast-addr ifname [src-addr] - join IP multicast group\n"); 806 printf("l mcast-addr ifname [src-addr] - leave IP multicast group\n"); 807 printf( 808 "i mcast-addr ifname n - set n include mode src filter\n"); 809 printf( 810 "e mcast-addr ifname n - set n exclude mode src filter\n"); 811 printf("t mcast-addr ifname src-addr - allow traffic from src\n"); 812 printf("b mcast-addr ifname src-addr - block traffic from src\n"); 813 printf("g mcast-addr ifname n - get and show n src filters\n"); 814#endif 815 printf("a ifname mac-addr - add link multicast filter\n"); 816 printf("d ifname mac-addr - delete link multicast filter\n"); 817 printf("m ifname 1/0 - set/clear ether allmulti flag\n"); 818#ifndef __APPLE__ 819 printf("p ifname 1/0 - set/clear ether promisc flag\n"); 820#endif /* __APPLE__ */ 821 printf("f filename - read command(s) from file\n"); 822 printf("s seconds - sleep for some time\n"); 823 printf("q - quit\n"); 824} 825 826