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