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