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 * XXX: This file currently assumes INET support in the base system. 34 * TODO: Support embedded KAME Scope ID in IPv6 group addresses. 35 * TODO: Use IPv4 link-local address when source address selection 36 * is implemented; use MCAST_JOIN_SOURCE for IPv4. 37 */ 38 39 #include <sys/cdefs.h> 40 __FBSDID("$FreeBSD$"); 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 #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) 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) 213 err(1, "can't open IPv6 socket"); 214 #endif 215 216 if (argc < 2) { 217 if (isatty(STDIN_FILENO)) { 218 printf("multicast membership test program; " 219 "enter ? for list of commands\n"); 220 } 221 do { 222 if (fgets(line, sizeof(line), stdin) != NULL) { 223 if (line[0] != 'f') 224 process_cmd(line, s, s6, stdin); 225 else { 226 /* Get the filename */ 227 for (i = 1; isblank(line[i]); i++); 228 if ((p = (char*)strchr(line, '\n')) 229 != NULL) 230 *p = '\0'; 231 process_file(&line[i], s, s6); 232 } 233 } 234 } while (!feof(stdin)); 235 } else { 236 for (i = 1; i < argc; i++) { 237 process_file(argv[i], s, s6); 238 } 239 } 240 241 if (s != -1) 242 close(s); 243 if (s6 != -1) 244 close(s6); 245 246 exit (0); 247 } 248 249 static void 250 process_file(char *fname, int s, int s6) 251 { 252 char line[80]; 253 FILE *fp; 254 char *lineptr; 255 256 fp = fopen(fname, "r"); 257 if (fp == NULL) { 258 warn("fopen"); 259 return; 260 } 261 262 /* Skip comments and empty lines. */ 263 while (fgets(line, sizeof(line), fp) != NULL) { 264 lineptr = line; 265 while (isblank(*lineptr)) 266 lineptr++; 267 if (*lineptr != '#' && *lineptr != '\n') 268 process_cmd(lineptr, s, s6, fp); 269 } 270 271 fclose(fp); 272 } 273 274 /* 275 * Parse join/leave/allow/block arguments, given: 276 * str1: group (as AF_INET or AF_INET6 printable) 277 * str2: ifname 278 * str3: optional source address (may be NULL). 279 * This argument must have the same parsed address family as str1. 280 * Return the ifindex of ifname, or 0 if any parse element failed. 281 */ 282 static uint32_t 283 parse_cmd_args(sockunion_t *psu, sockunion_t *psu2, 284 const char *str1, const char *str2, const char *str3) 285 { 286 struct addrinfo hints; 287 struct addrinfo *res; 288 uint32_t ifindex; 289 int af, error; 290 291 assert(psu != NULL); 292 assert(str1 != NULL); 293 assert(str2 != NULL); 294 295 af = AF_UNSPEC; 296 297 ifindex = if_nametoindex(str2); 298 if (ifindex == 0) 299 return (0); 300 301 memset(&hints, 0, sizeof(struct addrinfo)); 302 hints.ai_flags = AI_NUMERICHOST; 303 hints.ai_family = PF_UNSPEC; 304 hints.ai_socktype = SOCK_DGRAM; 305 306 memset(psu, 0, sizeof(sockunion_t)); 307 psu->sa.sa_family = AF_UNSPEC; 308 309 error = getaddrinfo(str1, "0", &hints, &res); 310 if (error) { 311 warnx("getaddrinfo: %s", gai_strerror(error)); 312 return (0); 313 } 314 assert(res != NULL); 315 af = res->ai_family; 316 memcpy(psu, res->ai_addr, res->ai_addrlen); 317 freeaddrinfo(res); 318 319 /* sscanf() may pass the empty string. */ 320 if (psu2 != NULL && str3 != NULL && *str3 != '\0') { 321 memset(psu2, 0, sizeof(sockunion_t)); 322 psu2->sa.sa_family = AF_UNSPEC; 323 324 /* look for following address family; str3 is *optional*. */ 325 hints.ai_family = af; 326 error = getaddrinfo(str3, "0", &hints, &res); 327 if (error) { 328 warnx("getaddrinfo: %s", gai_strerror(error)); 329 ifindex = 0; 330 } else { 331 if (af != res->ai_family) { 332 errno = EINVAL; /* XXX */ 333 ifindex = 0; 334 } 335 memcpy(psu2, res->ai_addr, res->ai_addrlen); 336 freeaddrinfo(res); 337 } 338 } 339 340 return (ifindex); 341 } 342 343 static __inline int 344 af2sock(const int af, int s, int s6) 345 { 346 347 #ifdef INET 348 if (af == AF_INET) 349 return (s); 350 #endif 351 #ifdef INET6 352 if (af == AF_INET6) 353 return (s6); 354 #endif 355 return (-1); 356 } 357 358 static __inline int 359 af2socklen(const int af) 360 { 361 362 #ifdef INET 363 if (af == AF_INET) 364 return (sizeof(struct sockaddr_in)); 365 #endif 366 #ifdef INET6 367 if (af == AF_INET6) 368 return (sizeof(struct sockaddr_in6)); 369 #endif 370 return (-1); 371 } 372 373 static void 374 process_cmd(char *cmd, int s, int s6 __unused, FILE *fp __unused) 375 { 376 char str1[STR_SIZE]; 377 char str2[STR_SIZE]; 378 char str3[STR_SIZE]; 379 mrequnion_t mr; 380 sockunion_t su, su2; 381 struct ifreq ifr; 382 char *line; 383 char *toptname; 384 void *optval; 385 uint32_t fmode, ifindex; 386 socklen_t optlen; 387 int af, error, f, flags, i, level, n, optname; 388 389 af = AF_UNSPEC; 390 su.sa.sa_family = AF_UNSPEC; 391 su2.sa.sa_family = AF_UNSPEC; 392 393 line = cmd; 394 while (isblank(*++line)) 395 ; /* Skip whitespace. */ 396 397 switch (*cmd) { 398 case '?': 399 usage(); 400 break; 401 402 case 'q': 403 close(s); 404 exit(0); 405 406 case 's': 407 if ((sscanf(line, "%d", &n) != 1) || (n < 1)) { 408 printf("-1\n"); 409 break; 410 } 411 sleep(n); 412 printf("ok\n"); 413 break; 414 415 case 'j': 416 case 'l': 417 str3[0] = '\0'; 418 toptname = ""; 419 sscanf(line, "%s %s %s", str1, str2, str3); 420 ifindex = parse_cmd_args(&su, &su2, str1, str2, str3); 421 if (ifindex == 0) { 422 printf("-1\n"); 423 break; 424 } 425 af = su.sa.sa_family; 426 #ifdef INET 427 if (af == AF_INET) { 428 struct in_addr ina; 429 430 error = __ifindex_to_primary_ip(ifindex, &ina); 431 if (error != 0) { 432 warn("primary_ip_lookup %s", str2); 433 printf("-1\n"); 434 break; 435 } 436 level = IPPROTO_IP; 437 438 if (su2.sa.sa_family != AF_UNSPEC) { 439 mr.mrs.imr_multiaddr = su.sin.sin_addr; 440 mr.mrs.imr_sourceaddr = su2.sin.sin_addr; 441 mr.mrs.imr_interface = ina; 442 optname = (*cmd == 'j') ? 443 IP_ADD_SOURCE_MEMBERSHIP : 444 IP_DROP_SOURCE_MEMBERSHIP; 445 toptname = (*cmd == 'j') ? 446 "IP_ADD_SOURCE_MEMBERSHIP" : 447 "IP_DROP_SOURCE_MEMBERSHIP"; 448 optval = (void *)&mr.mrs; 449 optlen = sizeof(mr.mrs); 450 } else { 451 mr.mr.imr_multiaddr = su.sin.sin_addr; 452 mr.mr.imr_interface = ina; 453 optname = (*cmd == 'j') ? 454 IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; 455 toptname = (*cmd == 'j') ? 456 "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP"; 457 optval = (void *)&mr.mr; 458 optlen = sizeof(mr.mr); 459 } 460 if (setsockopt(s, level, optname, optval, 461 optlen) == 0) { 462 printf("ok\n"); 463 break; 464 } else { 465 warn("setsockopt %s", toptname); 466 } 467 } 468 #ifdef INET6 469 else 470 #endif /* INET with INET6 */ 471 #endif /* INET */ 472 #ifdef INET6 473 if (af == AF_INET6) { 474 level = IPPROTO_IPV6; 475 if (su2.sa.sa_family != AF_UNSPEC) { 476 mr.gr.gsr_interface = ifindex; 477 mr.gr.gsr_group = su.ss; 478 mr.gr.gsr_source = su2.ss; 479 optname = (*cmd == 'j') ? 480 MCAST_JOIN_SOURCE_GROUP: 481 MCAST_LEAVE_SOURCE_GROUP; 482 toptname = (*cmd == 'j') ? 483 "MCAST_JOIN_SOURCE_GROUP": 484 "MCAST_LEAVE_SOURCE_GROUP"; 485 optval = (void *)&mr.gr; 486 optlen = sizeof(mr.gr); 487 } else { 488 mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr; 489 mr.mr6.ipv6mr_interface = ifindex; 490 optname = (*cmd == 'j') ? 491 IPV6_JOIN_GROUP : 492 IPV6_LEAVE_GROUP; 493 toptname = (*cmd == 'j') ? 494 "IPV6_JOIN_GROUP" : 495 "IPV6_LEAVE_GROUP"; 496 optval = (void *)&mr.mr6; 497 optlen = sizeof(mr.mr6); 498 } 499 if (setsockopt(s6, level, optname, optval, 500 optlen) == 0) { 501 printf("ok\n"); 502 break; 503 } else { 504 warn("setsockopt %s", toptname); 505 } 506 } 507 #endif /* INET6 */ 508 /* FALLTHROUGH */ 509 printf("-1\n"); 510 break; 511 512 /* 513 * Set the socket to include or exclude filter mode, and 514 * add some sources to the filterlist, using the full-state API. 515 */ 516 case 'i': 517 case 'e': { 518 sockunion_t sources[MAX_ADDRS]; 519 struct addrinfo hints; 520 struct addrinfo *res; 521 char *cp; 522 int af1; 523 524 n = 0; 525 fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE; 526 if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) { 527 printf("-1\n"); 528 break; 529 } 530 531 ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL); 532 if (ifindex == 0 || n < 0 || n > MAX_ADDRS) { 533 printf("-1\n"); 534 break; 535 } 536 af = su.sa.sa_family; 537 538 memset(&hints, 0, sizeof(struct addrinfo)); 539 hints.ai_flags = AI_NUMERICHOST; 540 hints.ai_family = af; 541 hints.ai_socktype = SOCK_DGRAM; 542 543 for (i = 0; i < n; i++) { 544 sockunion_t *psu = (sockunion_t *)&sources[i]; 545 /* 546 * Trim trailing whitespace, as getaddrinfo() 547 * can't cope with it. 548 */ 549 fgets(str1, sizeof(str1), fp); 550 cp = strchr(str1, '\n'); 551 if (cp != NULL) 552 *cp = '\0'; 553 554 res = NULL; 555 error = getaddrinfo(str1, "0", &hints, &res); 556 if (error) 557 break; 558 assert(res != NULL); 559 560 memset(psu, 0, sizeof(sockunion_t)); 561 af1 = res->ai_family; 562 if (af1 == af) 563 memcpy(psu, res->ai_addr, res->ai_addrlen); 564 freeaddrinfo(res); 565 if (af1 != af) 566 break; 567 } 568 if (i < n) { 569 if (error) 570 warnx("getaddrinfo: %s", gai_strerror(error)); 571 printf("-1\n"); 572 break; 573 } 574 if (setsourcefilter(af2sock(af, s, s6), ifindex, 575 &su.sa, su.sa.sa_len, fmode, n, &sources[0].ss) != 0) 576 warn("setsourcefilter"); 577 else 578 printf("ok\n"); 579 } break; 580 581 /* 582 * Allow or block traffic from a source, using the 583 * delta based api. 584 */ 585 case 't': 586 case 'b': { 587 str3[0] = '\0'; 588 toptname = ""; 589 sscanf(line, "%s %s %s", str1, str2, str3); 590 ifindex = parse_cmd_args(&su, &su2, str1, str2, str3); 591 if (ifindex == 0 || su2.sa.sa_family == AF_UNSPEC) { 592 printf("-1\n"); 593 break; 594 } 595 af = su.sa.sa_family; 596 597 /* First determine our current filter mode. */ 598 n = 0; 599 if (getsourcefilter(af2sock(af, s, s6), ifindex, 600 &su.sa, su.sa.sa_len, &fmode, &n, NULL) != 0) { 601 warn("getsourcefilter"); 602 break; 603 } 604 #ifdef INET 605 if (af == AF_INET) { 606 struct in_addr ina; 607 608 error = __ifindex_to_primary_ip(ifindex, &ina); 609 if (error != 0) { 610 warn("primary_ip_lookup %s", str2); 611 printf("-1\n"); 612 break; 613 } 614 level = IPPROTO_IP; 615 optval = (void *)&mr.mrs; 616 optlen = sizeof(mr.mrs); 617 mr.mrs.imr_multiaddr = su.sin.sin_addr; 618 mr.mrs.imr_sourceaddr = su2.sin.sin_addr; 619 mr.mrs.imr_interface = ina; 620 if (fmode == MCAST_EXCLUDE) { 621 /* Any-source mode socket membership. */ 622 optname = (*cmd == 't') ? 623 IP_UNBLOCK_SOURCE : 624 IP_BLOCK_SOURCE; 625 toptname = (*cmd == 't') ? 626 "IP_UNBLOCK_SOURCE" : 627 "IP_BLOCK_SOURCE"; 628 } else { 629 /* Source-specific mode socket membership. */ 630 optname = (*cmd == 't') ? 631 IP_ADD_SOURCE_MEMBERSHIP : 632 IP_DROP_SOURCE_MEMBERSHIP; 633 toptname = (*cmd == 't') ? 634 "IP_ADD_SOURCE_MEMBERSHIP" : 635 "IP_DROP_SOURCE_MEMBERSHIP"; 636 } 637 if (setsockopt(s, level, optname, optval, 638 optlen) == 0) { 639 printf("ok\n"); 640 break; 641 } else { 642 warn("setsockopt %s", toptname); 643 } 644 } 645 #ifdef INET6 646 else 647 #endif /* INET with INET6 */ 648 #endif /* INET */ 649 #ifdef INET6 650 if (af == AF_INET6) { 651 level = IPPROTO_IPV6; 652 mr.gr.gsr_interface = ifindex; 653 mr.gr.gsr_group = su.ss; 654 mr.gr.gsr_source = su2.ss; 655 if (fmode == MCAST_EXCLUDE) { 656 /* Any-source mode socket membership. */ 657 optname = (*cmd == 't') ? 658 MCAST_UNBLOCK_SOURCE : 659 MCAST_BLOCK_SOURCE; 660 toptname = (*cmd == 't') ? 661 "MCAST_UNBLOCK_SOURCE" : 662 "MCAST_BLOCK_SOURCE"; 663 } else { 664 /* Source-specific mode socket membership. */ 665 optname = (*cmd == 't') ? 666 MCAST_JOIN_SOURCE_GROUP : 667 MCAST_LEAVE_SOURCE_GROUP; 668 toptname = (*cmd == 't') ? 669 "MCAST_JOIN_SOURCE_GROUP": 670 "MCAST_LEAVE_SOURCE_GROUP"; 671 } 672 optval = (void *)&mr.gr; 673 optlen = sizeof(mr.gr); 674 if (setsockopt(s6, level, optname, optval, 675 optlen) == 0) { 676 printf("ok\n"); 677 break; 678 } else { 679 warn("setsockopt %s", toptname); 680 } 681 } 682 #endif /* INET6 */ 683 /* FALLTHROUGH */ 684 printf("-1\n"); 685 } break; 686 687 case 'g': { 688 sockunion_t sources[MAX_ADDRS]; 689 char addrbuf[NI_MAXHOST]; 690 int nreqsrc, nsrc; 691 692 if ((sscanf(line, "%s %s %d", str1, str2, &nreqsrc)) != 3) { 693 printf("-1\n"); 694 break; 695 } 696 ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL); 697 if (ifindex == 0 || (n < 0 || n > MAX_ADDRS)) { 698 printf("-1\n"); 699 break; 700 } 701 702 af = su.sa.sa_family; 703 nsrc = nreqsrc; 704 if (getsourcefilter(af2sock(af, s, s6), ifindex, &su.sa, 705 su.sa.sa_len, &fmode, &nsrc, &sources[0].ss) != 0) { 706 warn("getsourcefilter"); 707 printf("-1\n"); 708 break; 709 } 710 printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" : 711 "exclude"); 712 printf("%d\n", nsrc); 713 714 nsrc = MIN(nreqsrc, nsrc); 715 fprintf(stderr, "hexdump of sources:\n"); 716 uint8_t *bp = (uint8_t *)&sources[0]; 717 for (i = 0; i < (nsrc * sizeof(sources[0])); i++) { 718 fprintf(stderr, "%02x", bp[i]); 719 } 720 fprintf(stderr, "\nend hexdump\n"); 721 722 qsort(sources, nsrc, af2socklen(af), su_cmp); 723 for (i = 0; i < nsrc; i++) { 724 sockunion_t *psu = (sockunion_t *)&sources[i]; 725 addrbuf[0] = '\0'; 726 error = getnameinfo(&psu->sa, psu->sa.sa_len, 727 addrbuf, sizeof(addrbuf), NULL, 0, 728 NI_NUMERICHOST); 729 if (error) 730 warnx("getnameinfo: %s", gai_strerror(error)); 731 else 732 printf("%s\n", addrbuf); 733 } 734 printf("ok\n"); 735 } break; 736 737 /* link-layer stuff follows. */ 738 739 case 'a': 740 case 'd': { 741 struct sockaddr_dl *dlp; 742 struct ether_addr *ep; 743 744 memset(&ifr, 0, sizeof(struct ifreq)); 745 dlp = (struct sockaddr_dl *)&ifr.ifr_addr; 746 dlp->sdl_len = sizeof(struct sockaddr_dl); 747 dlp->sdl_family = AF_LINK; 748 dlp->sdl_index = 0; 749 dlp->sdl_nlen = 0; 750 dlp->sdl_alen = ETHER_ADDR_LEN; 751 dlp->sdl_slen = 0; 752 if (sscanf(line, "%s %s", str1, str2) != 2) { 753 warnc(EINVAL, "sscanf"); 754 break; 755 } 756 ep = ether_aton(str2); 757 if (ep == NULL) { 758 warnc(EINVAL, "ether_aton"); 759 break; 760 } 761 strlcpy(ifr.ifr_name, str1, IF_NAMESIZE); 762 memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN); 763 if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI, 764 &ifr) == -1) { 765 warn("ioctl SIOCADDMULTI/SIOCDELMULTI"); 766 printf("-1\n"); 767 } else 768 printf("ok\n"); 769 break; 770 } 771 772 case 'm': 773 fprintf(stderr, 774 "warning: IFF_ALLMULTI cannot be set from userland " 775 "in FreeBSD; command ignored.\n"); 776 printf("-1\n"); 777 break; 778 779 case 'p': 780 if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) { 781 printf("-1\n"); 782 break; 783 } 784 if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) { 785 warn("ioctl SIOCGIFFLAGS"); 786 break; 787 } 788 flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); 789 if (f == 0) { 790 flags &= ~IFF_PPROMISC; 791 } else { 792 flags |= IFF_PPROMISC; 793 } 794 ifr.ifr_flags = flags & 0xffff; 795 ifr.ifr_flagshigh = flags >> 16; 796 if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) 797 warn("ioctl SIOCGIFFLAGS"); 798 else 799 printf( "changed to 0x%08x\n", flags ); 800 break; 801 802 case '\n': 803 break; 804 default: 805 printf("invalid command\n"); 806 break; 807 } 808 } 809 810 static void 811 usage(void) 812 { 813 814 printf("j mcast-addr ifname [src-addr] - join IP multicast group\n"); 815 printf("l mcast-addr ifname [src-addr] - leave IP multicast group\n"); 816 printf( 817 "i mcast-addr ifname n - set n include mode src filter\n"); 818 printf( 819 "e mcast-addr ifname n - set n exclude mode src filter\n"); 820 printf("t mcast-addr ifname src-addr - allow traffic from src\n"); 821 printf("b mcast-addr ifname src-addr - block traffic from src\n"); 822 printf("g mcast-addr ifname n - get and show n src filters\n"); 823 printf("a ifname mac-addr - add link multicast filter\n"); 824 printf("d ifname mac-addr - delete link multicast filter\n"); 825 printf("m ifname 1/0 - set/clear ether allmulti flag\n"); 826 printf("p ifname 1/0 - set/clear ether promisc flag\n"); 827 printf("f filename - read command(s) from file\n"); 828 printf("s seconds - sleep for some time\n"); 829 printf("q - quit\n"); 830 } 831 832