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