diff options
author | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
---|---|---|
committer | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
commit | 5fd83771641d15c418f747bd343ba6738d3875f7 (patch) | |
tree | 5abf0f78f680d9837dbd93d4d4c3933bb7509599 /network_cmds/mtest.tproj/mtest.c | |
download | apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip |
Import macOS userland
adv_cmds-176
basic_cmds-55
bootstrap_cmds-116.100.1
developer_cmds-66
diskdev_cmds-667.40.1
doc_cmds-53.60.1
file_cmds-321.40.3
mail_cmds-35
misc_cmds-34
network_cmds-606.40.1
patch_cmds-17
remote_cmds-63
shell_cmds-216.60.1
system_cmds-880.60.2
text_cmds-106
Diffstat (limited to 'network_cmds/mtest.tproj/mtest.c')
-rw-r--r-- | network_cmds/mtest.tproj/mtest.c | 825 |
1 files changed, 825 insertions, 0 deletions
diff --git a/network_cmds/mtest.tproj/mtest.c b/network_cmds/mtest.tproj/mtest.c new file mode 100644 index 0000000..9c1614e --- /dev/null +++ b/network_cmds/mtest.tproj/mtest.c @@ -0,0 +1,825 @@ +/*- + * Copyright (c) 2007-2009 Bruce Simpson. + * Copyright (c) 2000 Wilbert De Graaf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Diagnostic and test utility for multicast sockets. + * TODO: Support embedded KAME Scope ID in IPv6 group addresses. + * TODO: Use IPv4 link-local address when source address selection + * is implemented; use MCAST_JOIN_SOURCE for IPv4. + */ + +#define INET6 + +#include <sys/cdefs.h> + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/ethernet.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#ifdef INET6 +#include <netinet/in.h> +#include <netinet/ip6.h> +#endif + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <err.h> +#include <unistd.h> + +#include <arpa/inet.h> +#include <netdb.h> +#include <ifaddrs.h> + +#ifdef IP_ADD_SOURCE_MEMBERSHIP +#define HAS_SSM 1 +#endif + +union sockunion { + struct sockaddr_storage ss; + struct sockaddr sa; + struct sockaddr_dl sdl; + struct sockaddr_in sin; +#ifdef INET6 + struct sockaddr_in6 sin6; +#endif +}; +typedef union sockunion sockunion_t; + +union mrequnion { + struct ip_mreq mr; +#ifdef HAS_SSM + struct ip_mreq_source mrs; +#endif +#ifdef INET6 + struct ipv6_mreq mr6; +#ifdef HAS_SSM + struct group_source_req gr; +#endif +#endif +}; +typedef union mrequnion mrequnion_t; + +#define MAX_ADDRS 20 +#define STR_SIZE 64 +#define LINE_LENGTH 80 + +static int __ifindex_to_primary_ip(const uint32_t, struct in_addr *); +static uint32_t parse_cmd_args(sockunion_t *, sockunion_t *, + const char *, const char *, const char *); +static void process_file(char *, int, int); +static void process_cmd(char*, int, int, FILE *); +static int su_cmp(const void *, const void *); +static void usage(void); + +/* + * Ordering predicate for qsort(). + */ +static int +su_cmp(const void *a, const void *b) +{ + const sockunion_t *sua = (const sockunion_t *)a; + const sockunion_t *sub = (const sockunion_t *)b; + + assert(sua->sa.sa_family == sub->sa.sa_family); + + switch (sua->sa.sa_family) { + case AF_INET: + return ((int)(sua->sin.sin_addr.s_addr - + sub->sin.sin_addr.s_addr)); + break; +#ifdef INET6 + case AF_INET6: + return (memcmp(&sua->sin6.sin6_addr, &sub->sin6.sin6_addr, + sizeof(struct in6_addr))); + break; +#endif + default: + break; + } + + assert(sua->sa.sa_len == sub->sa.sa_len); + return (memcmp(sua, sub, sua->sa.sa_len)); +} + +/* + * Internal: Map an interface index to primary IPv4 address. + * This is somewhat inefficient. This is a useful enough operation + * that it probably belongs in the C library. + * Return zero if found, -1 on error, 1 on not found. + */ +static int +__ifindex_to_primary_ip(const uint32_t ifindex, struct in_addr *pina) +{ + char ifname[IFNAMSIZ]; + struct ifaddrs *ifa; + struct ifaddrs *ifaddrs; + sockunion_t *psu; + int retval; + + assert(ifindex != 0); + + retval = -1; + if (if_indextoname(ifindex, ifname) == NULL) + return (retval); + if (getifaddrs(&ifaddrs) < 0) + return (retval); + + /* + * Find the ifaddr entry corresponding to the interface name, + * and return the first matching IPv4 address. + */ + retval = 1; + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { + if (strcmp(ifa->ifa_name, ifname) != 0) + continue; + psu = (sockunion_t *)ifa->ifa_addr; + if (psu && psu->sa.sa_family == AF_INET) { + retval = 0; + memcpy(pina, &psu->sin.sin_addr, + sizeof(struct in_addr)); + break; + } + } + + if (retval != 0) + errno = EADDRNOTAVAIL; /* XXX */ + + freeifaddrs(ifaddrs); + return (retval); +} + +int +main(int argc, char **argv) +{ + char line[LINE_LENGTH]; + char *p; + int i, s, s6; + + s = -1; + s6 = -1; + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) + err(1, "can't open IPv4 socket"); +#ifdef INET6 + s6 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s6 == -1) + err(1, "can't open IPv6 socket"); +#endif + + if (argc < 2) { + if (isatty(STDIN_FILENO)) { + printf("multicast membership test program; " + "enter ? for list of commands\n"); + } + do { + if (fgets(line, sizeof(line), stdin) != NULL) { + if (line[0] != 'f') + process_cmd(line, s, s6, stdin); + else { + /* Get the filename */ + for (i = 1; isblank(line[i]); i++); + if ((p = (char*)strchr(line, '\n')) + != NULL) + *p = '\0'; + process_file(&line[i], s, s6); + } + } + } while (!feof(stdin)); + } else { + for (i = 1; i < argc; i++) { + process_file(argv[i], s, s6); + } + } + + if (s != -1) + close(s); + if (s6 != -1) + close(s6); + + exit (0); +} + +static void +process_file(char *fname, int s, int s6) +{ + char line[80]; + FILE *fp; + char *lineptr; + + fp = fopen(fname, "r"); + if (fp == NULL) { + warn("fopen"); + return; + } + + /* Skip comments and empty lines. */ + while (fgets(line, sizeof(line), fp) != NULL) { + lineptr = line; + while (isblank(*lineptr)) + lineptr++; + if (*lineptr != '#' && *lineptr != '\n') + process_cmd(lineptr, s, s6, fp); + } + + fclose(fp); +} + +/* + * Parse join/leave/allow/block arguments, given: + * str1: group (as AF_INET or AF_INET6 printable) + * str2: ifname + * str3: optional source address (may be NULL). + * This argument must have the same parsed address family as str1. + * Return the ifindex of ifname, or 0 if any parse element failed. + */ +static uint32_t +parse_cmd_args(sockunion_t *psu, sockunion_t *psu2, + const char *str1, const char *str2, const char *str3) +{ + struct addrinfo hints; + struct addrinfo *res; + uint32_t ifindex; + int af, error; + + assert(psu != NULL); + assert(str1 != NULL); + assert(str2 != NULL); + + af = AF_UNSPEC; + + ifindex = if_nametoindex(str2); + if (ifindex == 0) + return (0); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + memset(psu, 0, sizeof(sockunion_t)); + psu->sa.sa_family = AF_UNSPEC; + + error = getaddrinfo(str1, "0", &hints, &res); + if (error) { + warnx("getaddrinfo: %s", gai_strerror(error)); + return (0); + } + assert(res != NULL); + af = res->ai_family; + memcpy(psu, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + + /* sscanf() may pass the empty string. */ + if (psu2 != NULL && str3 != NULL && *str3 != '\0') { + memset(psu2, 0, sizeof(sockunion_t)); + psu2->sa.sa_family = AF_UNSPEC; + + /* look for following address family; str3 is *optional*. */ + hints.ai_family = af; + error = getaddrinfo(str3, "0", &hints, &res); + if (error) { + warnx("getaddrinfo: %s", gai_strerror(error)); + ifindex = 0; + } else { + if (af != res->ai_family) { + errno = EINVAL; /* XXX */ + ifindex = 0; + } + memcpy(psu2, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + } + } + + return (ifindex); +} + +static __inline int +af2sock(const int af, int s, int s6) +{ + + if (af == AF_INET) + return (s); +#ifdef INET6 + if (af == AF_INET6) + return (s6); +#endif + return (-1); +} + +static void +process_cmd(char *cmd, int s, int s6 __unused, FILE *fp __unused) +{ + char str1[STR_SIZE]; + char str2[STR_SIZE]; + char str3[STR_SIZE]; + mrequnion_t mr; + sockunion_t su, su2; + struct ifreq ifr; + char *line; + char *toptname; + void *optval; + uint32_t fmode, ifindex; + socklen_t optlen; + int af, error, i, level, n = 0, optname; +#ifndef __APPLE__ + int f, flags; +#endif /* __APPLE__ */ + + af = AF_UNSPEC; + su.sa.sa_family = AF_UNSPEC; + su2.sa.sa_family = AF_UNSPEC; + + line = cmd; + while (isblank(*++line)) + ; /* Skip whitespace. */ + + switch (*cmd) { + case '?': + usage(); + break; + + case 'q': + close(s); + exit(0); + + case 's': + if ((sscanf(line, "%d", &n) != 1) || (n < 1)) { + printf("-1\n"); + break; + } + sleep(n); + printf("ok\n"); + break; + + case 'j': + case 'l': + str3[0] = '\0'; + toptname = ""; + sscanf(line, "%s %s %s", str1, str2, str3); + ifindex = parse_cmd_args(&su, &su2, str1, str2, str3); + if (ifindex == 0) { + printf("-1\n"); + break; + } + af = su.sa.sa_family; + if (af == AF_INET) { + struct in_addr ina; + + error = __ifindex_to_primary_ip(ifindex, &ina); + if (error != 0) { + warn("primary_ip_lookup %s", str2); + printf("-1\n"); + break; + } + level = IPPROTO_IP; + +#ifdef HAS_SSM + if (su2.sa.sa_family != AF_UNSPEC) { + mr.mrs.imr_multiaddr = su.sin.sin_addr; + mr.mrs.imr_sourceaddr = su2.sin.sin_addr; + mr.mrs.imr_interface = ina; + optname = (*cmd == 'j') ? + IP_ADD_SOURCE_MEMBERSHIP : + IP_DROP_SOURCE_MEMBERSHIP; + toptname = (*cmd == 'j') ? + "IP_ADD_SOURCE_MEMBERSHIP" : + "IP_DROP_SOURCE_MEMBERSHIP"; + optval = (void *)&mr.mrs; + optlen = sizeof(mr.mrs); + } else { +#endif + mr.mr.imr_multiaddr = su.sin.sin_addr; + mr.mr.imr_interface = ina; + optname = (*cmd == 'j') ? + IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + toptname = (*cmd == 'j') ? + "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP"; + optval = (void *)&mr.mr; + optlen = sizeof(mr.mr); +#ifdef HAS_SSM + } +#endif + if (setsockopt(s, level, optname, optval, + optlen) == 0) { + printf("ok\n"); + break; + } else { + warn("setsockopt %s", toptname); + } + } +#ifdef INET6 + else +#endif /* INET6 */ +#ifdef INET6 + if (af == AF_INET6) { + level = IPPROTO_IPV6; +#ifdef HAS_SSM + if (su2.sa.sa_family != AF_UNSPEC) { + mr.gr.gsr_interface = ifindex; + mr.gr.gsr_group = su.ss; + mr.gr.gsr_source = su2.ss; + optname = (*cmd == 'j') ? + MCAST_JOIN_SOURCE_GROUP: + MCAST_LEAVE_SOURCE_GROUP; + toptname = (*cmd == 'j') ? + "MCAST_JOIN_SOURCE_GROUP": + "MCAST_LEAVE_SOURCE_GROUP"; + optval = (void *)&mr.gr; + optlen = sizeof(mr.gr); + } else { +#endif + mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr; + mr.mr6.ipv6mr_interface = ifindex; + optname = (*cmd == 'j') ? + IPV6_JOIN_GROUP : + IPV6_LEAVE_GROUP; + toptname = (*cmd == 'j') ? + "IPV6_JOIN_GROUP" : + "IPV6_LEAVE_GROUP"; + optval = (void *)&mr.mr6; + optlen = sizeof(mr.mr6); +#ifdef HAS_SSM + } +#endif + if (setsockopt(s6, level, optname, optval, + optlen) == 0) { + printf("ok\n"); + break; + } else { + warn("setsockopt %s", toptname); + } + } +#endif /* INET6 */ + /* FALLTHROUGH */ + printf("-1\n"); + break; + +#ifdef HAS_SSM + /* + * Set the socket to include or exclude filter mode, and + * add some sources to the filterlist, using the full-state API. + */ + case 'i': + case 'e': { + sockunion_t sources[MAX_ADDRS]; + struct addrinfo hints; + struct addrinfo *res; + char *cp; + int af1; + + n = 0; + fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE; + if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) { + printf("-1\n"); + break; + } + + ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL); + if (ifindex == 0 || n < 0 || n > MAX_ADDRS) { + printf("-1\n"); + break; + } + af = su.sa.sa_family; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = af; + hints.ai_socktype = SOCK_DGRAM; + + for (i = 0; i < n; i++) { + sockunion_t *psu = (sockunion_t *)&sources[i]; + /* + * Trim trailing whitespace, as getaddrinfo() + * can't cope with it. + */ + fgets(str1, sizeof(str1), fp); + cp = strchr(str1, '\n'); + if (cp != NULL) + *cp = '\0'; + + res = NULL; + error = getaddrinfo(str1, "0", &hints, &res); + if (error) + break; + assert(res != NULL); + + memset(psu, 0, sizeof(sockunion_t)); + af1 = res->ai_family; + if (af1 == af) + memcpy(psu, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + if (af1 != af) + break; + } + if (i < n) { + if (error) + warnx("getaddrinfo: %s", gai_strerror(error)); + printf("-1\n"); + break; + } + if (setsourcefilter(af2sock(af, s, s6), ifindex, + &su.sa, su.sa.sa_len, fmode, n, &sources[0].ss) != 0) + warn("setsourcefilter"); + else + printf("ok\n"); + } break; + + /* + * Allow or block traffic from a source, using the + * delta based api. + */ + case 't': + case 'b': { + str3[0] = '\0'; + toptname = ""; + sscanf(line, "%s %s %s", str1, str2, str3); + ifindex = parse_cmd_args(&su, &su2, str1, str2, str3); + if (ifindex == 0 || su2.sa.sa_family == AF_UNSPEC) { + printf("-1\n"); + break; + } + af = su.sa.sa_family; + + /* First determine our current filter mode. */ + n = 0; + if (getsourcefilter(af2sock(af, s, s6), ifindex, + &su.sa, su.sa.sa_len, &fmode, (uint32_t *)&n, NULL) != 0) { + warn("getsourcefilter"); + break; + } + if (af == AF_INET) { + struct in_addr ina; + + error = __ifindex_to_primary_ip(ifindex, &ina); + if (error != 0) { + warn("primary_ip_lookup %s", str2); + printf("-1\n"); + break; + } + level = IPPROTO_IP; + optval = (void *)&mr.mrs; + optlen = sizeof(mr.mrs); + mr.mrs.imr_multiaddr = su.sin.sin_addr; + mr.mrs.imr_sourceaddr = su2.sin.sin_addr; + mr.mrs.imr_interface = ina; + if (fmode == MCAST_EXCLUDE) { + /* Any-source mode socket membership. */ + optname = (*cmd == 't') ? + IP_UNBLOCK_SOURCE : + IP_BLOCK_SOURCE; + toptname = (*cmd == 't') ? + "IP_UNBLOCK_SOURCE" : + "IP_BLOCK_SOURCE"; + } else { + /* Source-specific mode socket membership. */ + optname = (*cmd == 't') ? + IP_ADD_SOURCE_MEMBERSHIP : + IP_DROP_SOURCE_MEMBERSHIP; + toptname = (*cmd == 't') ? + "IP_ADD_SOURCE_MEMBERSHIP" : + "IP_DROP_SOURCE_MEMBERSHIP"; + } + if (setsockopt(s, level, optname, optval, + optlen) == 0) { + printf("ok\n"); + break; + } else { + warn("setsockopt %s", toptname); + } + } +#ifdef INET6 + else +#endif /* INET6 */ +#ifdef INET6 + if (af == AF_INET6) { + level = IPPROTO_IPV6; + mr.gr.gsr_interface = ifindex; + mr.gr.gsr_group = su.ss; + mr.gr.gsr_source = su2.ss; + if (fmode == MCAST_EXCLUDE) { + /* Any-source mode socket membership. */ + optname = (*cmd == 't') ? + MCAST_UNBLOCK_SOURCE : + MCAST_BLOCK_SOURCE; + toptname = (*cmd == 't') ? + "MCAST_UNBLOCK_SOURCE" : + "MCAST_BLOCK_SOURCE"; + } else { + /* Source-specific mode socket membership. */ + optname = (*cmd == 't') ? + MCAST_JOIN_SOURCE_GROUP : + MCAST_LEAVE_SOURCE_GROUP; + toptname = (*cmd == 't') ? + "MCAST_JOIN_SOURCE_GROUP": + "MCAST_LEAVE_SOURCE_GROUP"; + } + optval = (void *)&mr.gr; + optlen = sizeof(mr.gr); + if (setsockopt(s6, level, optname, optval, + optlen) == 0) { + printf("ok\n"); + break; + } else { + warn("setsockopt %s", toptname); + } + } +#endif /* INET6 */ + /* FALLTHROUGH */ + printf("-1\n"); + } break; + + case 'g': { + sockunion_t sources[MAX_ADDRS]; + char addrbuf[NI_MAXHOST]; + int nreqsrc, nsrc; + + if ((sscanf(line, "%s %s %d", str1, str2, &nreqsrc)) != 3) { + printf("-1\n"); + break; + } + ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL); + if (ifindex == 0 || (n < 0 || n > MAX_ADDRS)) { + printf("-1\n"); + break; + } + + af = su.sa.sa_family; + nsrc = nreqsrc; + if (getsourcefilter(af2sock(af, s, s6), ifindex, &su.sa, + su.sa.sa_len, &fmode, (uint32_t *)&nsrc, + &sources[0].ss) != 0) { + warn("getsourcefilter"); + printf("-1\n"); + break; + } + printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" : + "exclude"); + printf("%d\n", nsrc); + + nsrc = MIN(nreqsrc, nsrc); + fprintf(stderr, "hexdump of sources:\n"); + uint8_t *bp = (uint8_t *)&sources[0]; + for (i = 0; i < (nsrc * sizeof(sources[0])); i++) { + fprintf(stderr, "%02x", bp[i]); + } + fprintf(stderr, "\nend hexdump\n"); + + qsort(sources, nsrc, sizeof (sockunion_t), su_cmp); + for (i = 0; i < nsrc; i++) { + sockunion_t *psu = (sockunion_t *)&sources[i]; + addrbuf[0] = '\0'; + error = getnameinfo(&psu->sa, psu->sa.sa_len, + addrbuf, sizeof(addrbuf), NULL, 0, + NI_NUMERICHOST); + if (error) + warnx("getnameinfo: %s", gai_strerror(error)); + else + printf("%s\n", addrbuf); + } + printf("ok\n"); + } break; +#endif + /* link-layer stuff follows. */ + + case 'a': + case 'd': { + struct sockaddr_dl *dlp; + struct ether_addr *ep; + + memset(&ifr, 0, sizeof(struct ifreq)); + dlp = (struct sockaddr_dl *)&ifr.ifr_addr; + dlp->sdl_len = sizeof(struct sockaddr_dl); + dlp->sdl_family = AF_LINK; + dlp->sdl_index = 0; + dlp->sdl_nlen = 0; + dlp->sdl_alen = ETHER_ADDR_LEN; + dlp->sdl_slen = 0; + if (sscanf(line, "%s %s", str1, str2) != 2) { + warnc(EINVAL, "sscanf"); + break; + } + ep = ether_aton(str2); + if (ep == NULL) { + warnc(EINVAL, "ether_aton"); + break; + } + strlcpy(ifr.ifr_name, str1, sizeof(ifr.ifr_name)); + memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN); + if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI, + &ifr) == -1) { + warn("ioctl SIOCADDMULTI/SIOCDELMULTI"); + printf("-1\n"); + } else + printf("ok\n"); + break; + } + + case 'm': + fprintf(stderr, + "warning: IFF_ALLMULTI cannot be set from userland " + "in Darwin; command ignored.\n"); + printf("-1\n"); + break; + +#ifndef __APPLE__ + case 'p': + if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) { + printf("-1\n"); + break; + } + if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) { + warn("ioctl SIOCGIFFLAGS"); + break; + } + flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); + if (f == 0) { + flags &= ~IFF_PPROMISC; + } else { + flags |= IFF_PPROMISC; + } + ifr.ifr_flags = flags & 0xffff; + ifr.ifr_flagshigh = flags >> 16; + if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) + warn("ioctl SIOCGIFFLAGS"); + else + printf( "changed to 0x%08x\n", flags ); + break; +#endif /* __APPLE__ */ + case '\n': + break; + default: + printf("invalid command\n"); + break; + } +} + +static void +usage(void) +{ + +#ifndef HAS_SSM + printf("j mcast-addr ifname - join IP multicast group\n"); + printf("l mcast-addr ifname - leave IP multicast group\n"); +#else /* HAS_SSM */ + printf("j mcast-addr ifname [src-addr] - join IP multicast group\n"); + printf("l mcast-addr ifname [src-addr] - leave IP multicast group\n"); + printf( + "i mcast-addr ifname n - set n include mode src filter\n"); + printf( + "e mcast-addr ifname n - set n exclude mode src filter\n"); + printf("t mcast-addr ifname src-addr - allow traffic from src\n"); + printf("b mcast-addr ifname src-addr - block traffic from src\n"); + printf("g mcast-addr ifname n - get and show n src filters\n"); +#endif + printf("a ifname mac-addr - add link multicast filter\n"); + printf("d ifname mac-addr - delete link multicast filter\n"); + printf("m ifname 1/0 - set/clear ether allmulti flag\n"); +#ifndef __APPLE__ + printf("p ifname 1/0 - set/clear ether promisc flag\n"); +#endif /* __APPLE__ */ + printf("f filename - read command(s) from file\n"); + printf("s seconds - sleep for some time\n"); + printf("q - quit\n"); +} + |