mtest.c revision 167346
1167346Sbms/*- 2167346Sbms * Copyright (c) 2007 Bruce M. Simpson. 3167346Sbms * Copyright (c) 2000 Wilbert De Graaf. 4167346Sbms * All rights reserved. 520529Sfenner * 6167346Sbms * Redistribution and use in source and binary forms, with or without 7167346Sbms * modification, are permitted provided that the following conditions 8167346Sbms * are met: 9167346Sbms * 1. Redistributions of source code must retain the above copyright 10167346Sbms * notice, this list of conditions and the following disclaimer. 11167346Sbms * 2. Redistributions in binary form must reproduce the above copyright 12167346Sbms * notice, this list of conditions and the following disclaimer in the 13167346Sbms * documentation and/or other materials provided with the distribution. 14167346Sbms * 15167346Sbms * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16167346Sbms * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17167346Sbms * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18167346Sbms * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19167346Sbms * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20167346Sbms * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21167346Sbms * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22167346Sbms * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23167346Sbms * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24167346Sbms * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25167346Sbms * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26167346Sbms * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27167346Sbms * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28167346Sbms * SUCH DAMAGE. 2920529Sfenner */ 3020529Sfenner 31167346Sbms/* 32167346Sbms * Diagnostic and test utility for IPv4 multicast sockets. 33167346Sbms */ 34167346Sbms 35117280Scharnier#include <sys/cdefs.h> 36117280Scharnier__FBSDID("$FreeBSD: head/usr.sbin/mtest/mtest.c 167346 2007-03-08 18:56:37Z bms $"); 3778720Sdd 3820529Sfenner#include <sys/types.h> 39167346Sbms#include <sys/errno.h> 4020529Sfenner#include <sys/socket.h> 4120531Sfenner#include <sys/time.h> 42167346Sbms#include <sys/ioctl.h> 43167346Sbms 4420529Sfenner#include <net/if.h> 4536440Sjulian#include <net/if_dl.h> 46167346Sbms#include <net/ethernet.h> 4720529Sfenner#include <netinet/in.h> 4820529Sfenner 49167346Sbms#include <arpa/inet.h> 50167346Sbms 51167346Sbms#include <stdlib.h> 52167346Sbms#include <stdio.h> 53167346Sbms#include <string.h> 54167346Sbms#include <ctype.h> 55167346Sbms#include <err.h> 56167346Sbms#include <unistd.h> 57167346Sbms 58167346Sbmsstatic void process_file(char *, int); 59167346Sbmsstatic void process_cmd(char*, int, FILE *fp); 60167346Sbmsstatic void usage(void); 61167346Sbms#ifdef WITH_IGMPV3 62167346Sbmsstatic int inaddr_cmp(const void *a, const void *b); 63167346Sbms#endif 64167346Sbms 65167346Sbms#define MAX_ADDRS 20 66167346Sbms#define STR_SIZE 20 67167346Sbms#define LINE_LENGTH 80 68167346Sbms 6930026Scharnierint 70167346Sbmsmain(int argc, char **argv) 71167346Sbms{ 72167346Sbms char line[LINE_LENGTH]; 73167346Sbms char *p; 74167346Sbms int i, s; 7520529Sfenner 76167346Sbms s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 77167346Sbms if (s == -1) 78167346Sbms err(1, "can't open socket"); 7920529Sfenner 80167346Sbms if (argc < 2) { 81167346Sbms if (isatty(STDIN_FILENO)) { 82167346Sbms printf("multicast membership test program; " 83167346Sbms "enter ? for list of commands\n"); 84167346Sbms } 85167346Sbms do { 86167346Sbms if (fgets(line, sizeof(line), stdin) != NULL) { 87167346Sbms if (line[0] != 'f') 88167346Sbms process_cmd(line, s, stdin); 89167346Sbms else { 90167346Sbms /* Get the filename */ 91167346Sbms for (i = 1; isblank(line[i]); i++); 92167346Sbms if ((p = (char*)strchr(line, '\n')) 93167346Sbms != NULL) 94167346Sbms *p = '\0'; 95167346Sbms process_file(&line[i], s); 96167346Sbms } 97167346Sbms } 98167346Sbms } while (!feof(stdin)); 99167346Sbms } else { 100167346Sbms for (i = 1; i < argc; i++) { 101167346Sbms process_file(argv[i], s); 102167346Sbms } 103167346Sbms } 10420529Sfenner 105167346Sbms exit (0); 106167346Sbms} 107167346Sbms 108167346Sbmsstatic void 109167346Sbmsprocess_file(char *fname, int s) 110167346Sbms{ 111167346Sbms char line[80]; 112167346Sbms FILE *fp; 113167346Sbms char *lineptr; 114167346Sbms 115167346Sbms fp = fopen(fname, "r"); 116167346Sbms if (fp == NULL) { 117167346Sbms warn("fopen"); 118167346Sbms return; 119167346Sbms } 120167346Sbms 121167346Sbms /* Skip comments and empty lines. */ 122167346Sbms while (fgets(line, sizeof(line), fp) != NULL) { 123167346Sbms lineptr = line; 124167346Sbms while (isblank(*lineptr)) 125167346Sbms lineptr++; 126167346Sbms if (*lineptr != '#' && *lineptr != '\n') 127167346Sbms process_cmd(lineptr, s, fp); 128167346Sbms } 129167346Sbms 130167346Sbms fclose(fp); 131167346Sbms} 132167346Sbms 133167346Sbmsstatic void 134167346Sbmsprocess_cmd(char *cmd, int s, FILE *fp __unused) 135167346Sbms{ 136167346Sbms char str1[STR_SIZE]; 137167346Sbms char str2[STR_SIZE]; 138167346Sbms#ifdef WITH_IGMPV3 139167346Sbms char str3[STR_SIZE]; 140167346Sbms char filtbuf[IP_MSFILTER_SIZE(MAX_ADDRS)]; 141167346Sbms#endif 142167346Sbms struct ifreq ifr; 143167346Sbms struct ip_mreq imr; 144167346Sbms#ifdef WITH_IGMPV3 145167346Sbms struct ip_mreq_source imrs; 146167346Sbms struct ip_msfilter *imsfp; 147167346Sbms#endif 148167346Sbms char *line; 149167346Sbms int n, opt, f, flags; 150167346Sbms 151167346Sbms line = cmd; 152167346Sbms while (isblank(*++line)) 153167346Sbms ; /* Skip whitespace. */ 154167346Sbms 155167346Sbms switch (*cmd) { 156167346Sbms case '?': 157167346Sbms usage(); 15820529Sfenner break; 15920529Sfenner 160167346Sbms case 'q': 161167346Sbms close(s); 162167346Sbms exit(0); 163167346Sbms 164167346Sbms case 's': 165167346Sbms if ((sscanf(line, "%d", &n) != 1) || (n < 1)) { 166167346Sbms printf("-1\n"); 167167346Sbms break; 168167346Sbms } 169167346Sbms sleep(n); 170167346Sbms printf("ok\n"); 17120529Sfenner break; 17220529Sfenner 173167346Sbms case 'j': 174167346Sbms case 'l': 175167346Sbms sscanf(line, "%s %s", str1, str2); 176167346Sbms if (((imr.imr_multiaddr.s_addr = inet_addr(str1)) == 177167346Sbms INADDR_NONE) || 178167346Sbms ((imr.imr_interface.s_addr = inet_addr(str2)) == 179167346Sbms INADDR_NONE)) { 180167346Sbms printf("-1\n"); 181167346Sbms break; 182167346Sbms } 183167346Sbms opt = (*cmd == 'j') ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; 184167346Sbms if (setsockopt( s, IPPROTO_IP, opt, &imr, 185167346Sbms sizeof(imr)) != 0) 186167346Sbms warn("setsockopt IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP"); 187167346Sbms else 188167346Sbms printf("ok\n"); 18920529Sfenner break; 19020529Sfenner 191167346Sbms case 'a': 192167346Sbms case 'd': { 193167346Sbms struct sockaddr_dl *dlp; 194167346Sbms struct ether_addr *ep; 195167346Sbms 196167346Sbms memset(&ifr, 0, sizeof(struct ifreq)); 19736440Sjulian dlp = (struct sockaddr_dl *)&ifr.ifr_addr; 19836440Sjulian dlp->sdl_len = sizeof(struct sockaddr_dl); 19936440Sjulian dlp->sdl_family = AF_LINK; 20036440Sjulian dlp->sdl_index = 0; 20136440Sjulian dlp->sdl_nlen = 0; 202167346Sbms dlp->sdl_alen = ETHER_ADDR_LEN; 20336440Sjulian dlp->sdl_slen = 0; 204167346Sbms if (sscanf(line, "%s %s", str1, str2) != 2) { 205167346Sbms warnc(EINVAL, "sscanf"); 206167346Sbms break; 207167346Sbms } 208167346Sbms ep = ether_aton(str2); 209167346Sbms if (ep == NULL) { 210167346Sbms warnc(EINVAL, "ether_aton"); 211167346Sbms break; 212167346Sbms } 213167346Sbms strlcpy(ifr.ifr_name, str1, IF_NAMESIZE); 214167346Sbms memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN); 215167346Sbms if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI, 216167346Sbms &ifr) == -1) 217167346Sbms warn("ioctl SIOCADDMULTI/SIOCDELMULTI"); 218167346Sbms else 219167346Sbms printf("ok\n"); 22020529Sfenner break; 221167346Sbms } 22220529Sfenner 223167346Sbms case 'm': 224167346Sbms printf("warning: IFF_ALLMULTI cannot be set from userland " 225167346Sbms "in FreeBSD; command ignored.\n"); 22620529Sfenner break; 227167346Sbms case 'p': 228167346Sbms if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) { 229167346Sbms printf("-1\n"); 230167346Sbms break; 231167346Sbms } 232167346Sbms if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) { 233167346Sbms warn("ioctl SIOCGIFFLAGS"); 234167346Sbms break; 235167346Sbms } 236167346Sbms flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); 237167346Sbms opt = IFF_PPROMISC; 238167346Sbms if (f == 0) { 239167346Sbms flags &= ~opt; 240167346Sbms } else { 241167346Sbms flags |= opt; 242167346Sbms } 243167346Sbms ifr.ifr_flags = flags & 0xffff; 244167346Sbms ifr.ifr_flagshigh = flags >> 16; 245167346Sbms if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) 246167346Sbms warn("ioctl SIOCGIFFLAGS"); 247167346Sbms else 248167346Sbms printf( "changed to 0x%08x\n", flags ); 249167346Sbms break; 25020529Sfenner 251167346Sbms#ifdef WITH_IGMPV3 252167346Sbms /* 253167346Sbms * Set the socket to include or exclude filter mode, and 254167346Sbms * add some sources to the filterlist, using the full-state, 255167346Sbms * or advanced api. 256167346Sbms */ 257167346Sbms case 'i': 258167346Sbms case 'e': 259167346Sbms if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) { 260167346Sbms printf("-1\n"); 261167346Sbms break; 262167346Sbms } 263167346Sbms imsfp = (struct ip_msfilter *)filtbuf; 264167346Sbms if (((imsfp->imsf_multiaddr.s_addr = inet_addr(str1)) == 265167346Sbms INADDR_NONE) || 266167346Sbms ((imsfp->imsf_interface.s_addr = inet_addr(str2)) == 267167346Sbms INADDR_NONE) || (n > MAX_ADDRS)) { 268167346Sbms printf("-1\n"); 269167346Sbms break; 270167346Sbms } 271167346Sbms imsfp->imsf_fmode = (*cmd == 'i') ? MCAST_INCLUDE : 272167346Sbms MCAST_EXCLUDE; 273167346Sbms imsfp->imsf_numsrc = n; 274167346Sbms for (i = 0; i < n; i++) { 275167346Sbms fgets(str1, sizeof(str1), fp); 276167346Sbms if ((imsfp->imsf_slist[i].s_addr = inet_addr(str1)) == 277167346Sbms INADDR_NONE) { 278167346Sbms printf("-1\n"); 279167346Sbms return; 280167346Sbms } 281167346Sbms } 282167346Sbms if (ioctl(s, SIOCSIPMSFILTER, imsfp) != 0) 283167346Sbms warn("setsockopt SIOCSIPMSFILTER"); 284167346Sbms else 285167346Sbms printf("ok\n"); 28620529Sfenner break; 28720529Sfenner 288167346Sbms /* 289167346Sbms * Allow or block traffic from a source, using the 290167346Sbms * delta based api. 291167346Sbms */ 292167346Sbms case 't': 293167346Sbms case 'b': 294167346Sbms sscanf(line, "%s %s %s", str1, str2, str3); 295167346Sbms if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) == 296167346Sbms INADDR_NONE) || 297167346Sbms ((imrs.imr_interface.s_addr = inet_addr(str2)) == 298167346Sbms INADDR_NONE) || 299167346Sbms ((imrs.imr_sourceaddr.s_addr = inet_addr(str3)) == 300167346Sbms INADDR_NONE)) { 301167346Sbms printf("-1\n"); 302167346Sbms break; 303167346Sbms } 304167346Sbms 305167346Sbms /* First determine out current filter mode. */ 306167346Sbms imsfp = (struct ip_msfilter *)filtbuf; 307167346Sbms imsfp->imsf_multiaddr.s_addr = imrs.imr_multiaddr.s_addr; 308167346Sbms imsfp->imsf_interface.s_addr = imrs.imr_interface.s_addr; 309167346Sbms imsfp->imsf_numsrc = 5; 310167346Sbms if (ioctl(s, SIOCSIPMSFILTER, imsfp) != 0) { 311167346Sbms /* It's only okay for 't' to fail */ 312167346Sbms if (*cmd != 't') { 313167346Sbms warn("ioctl SIOCSIPMSFILTER"); 314167346Sbms break; 315167346Sbms } else { 316167346Sbms imsfp->imsf_fmode = MCAST_INCLUDE; 317167346Sbms } 318167346Sbms } 319167346Sbms if (imsfp->imsf_fmode == MCAST_EXCLUDE) { 320167346Sbms /* Any source */ 321167346Sbms opt = (*cmd == 't') ? IP_UNBLOCK_SOURCE : 322167346Sbms IP_BLOCK_SOURCE; 323167346Sbms } else { 324167346Sbms /* Controlled source */ 325167346Sbms opt = (*cmd == 't') ? IP_ADD_SOURCE_MEMBERSHIP : 326167346Sbms IP_DROP_SOURCE_MEMBERSHIP; 327167346Sbms } 328167346Sbms if (setsockopt(s, IPPROTO_IP, opt, &imrs, sizeof(imrs)) == -1) 329167346Sbms warn("ioctl IP_ADD_SOURCE_MEMBERSHIP/IP_DROP_SOURCE_MEMBERSHIP/IP_UNBLOCK_SOURCE/IP_BLOCK_SOURCE"); 330167346Sbms else 331167346Sbms printf("ok\n"); 33220529Sfenner break; 33320529Sfenner 334167346Sbms case 'g': 335167346Sbms if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) { 336167346Sbms printf("-1\n"); 337167346Sbms break; 338167346Sbms } 339167346Sbms imsfp = (struct ip_msfilter *)filtbuf; 340167346Sbms if (((imsfp->imsf_multiaddr.s_addr = inet_addr(str1)) == 341167346Sbms INADDR_NONE) || 342167346Sbms ((imsfp->imsf_interface.s_addr = inet_addr(str2)) == 343167346Sbms INADDR_NONE) || (n < 0 || n > MAX_ADDRS)) { 344167346Sbms printf("-1\n"); 345167346Sbms break; 346167346Sbms } 347167346Sbms imsfp->imsf_numsrc = n; 348167346Sbms if (ioctl(s, SIOCSIPMSFILTER, imsfp) != 0) { 349167346Sbms warn("setsockopt SIOCSIPMSFILTER"); 350167346Sbms break; 351167346Sbms } 352167346Sbms printf("%s\n", (imsfp->imsf_fmode == MCAST_INCLUDE) ? 353167346Sbms "include" : "exclude"); 354167346Sbms printf("%d\n", imsfp->imsf_numsrc); 355167346Sbms if (n >= imsfp->imsf_numsrc) { 356167346Sbms n = imsfp->imsf_numsrc; 357167346Sbms qsort(imsfp->imsf_slist, n, sizeof(struct in_addr), 358167346Sbms &inaddr_cmp); 359167346Sbms for (i = 0; i < n; i++) 360167346Sbms printf("%s\n", inet_ntoa(imsfp->imsf_slist[i])); 361167346Sbms } 362167346Sbms break; 363167346Sbms#else /* !WITH_IGMPV3 */ 364167346Sbms case 'i': 365167346Sbms case 'e': 366167346Sbms case 't': 367167346Sbms case 'b': 368167346Sbms case 'g': 369167346Sbms printf("warning: IGMPv3 is not supported by this version " 370167346Sbms "of FreeBSD; command ignored.\n"); 371167346Sbms break; 372167346Sbms#endif /* WITH_IGMPV3 */ 37320529Sfenner 374167346Sbms case '\n': 375167346Sbms break; 376167346Sbms default: 377167346Sbms printf("invalid command\n"); 378167346Sbms break; 379167346Sbms } 380167346Sbms} 38120529Sfenner 382167346Sbmsstatic void 383167346Sbmsusage(void) 384167346Sbms{ 385167346Sbms 386167346Sbms printf("j g.g.g.g i.i.i.i - join IP multicast group\n"); 387167346Sbms printf("l g.g.g.g i.i.i.i - leave IP multicast group\n"); 388167346Sbms printf("a ifname e.e.e.e.e.e - add ether multicast address\n"); 389167346Sbms printf("d ifname e.e.e.e.e.e - delete ether multicast address\n"); 390167346Sbms printf("m ifname 1/0 - set/clear ether allmulti flag\n"); 391167346Sbms printf("p ifname 1/0 - set/clear ether promisc flag\n"); 392167346Sbms printf("i g.g.g.g i.i.i.i n - set n include mode src filter\n"); 393167346Sbms printf("e g.g.g.g i.i.i.i n - set n exclude mode src filter\n"); 394167346Sbms printf("t g.g.g.g i.i.i.i s.s.s.s - allow traffic from src\n"); 395167346Sbms printf("b g.g.g.g i.i.i.i s.s.s.s - block traffic from src\n"); 396167346Sbms printf("g g.g.g.g i.i.i.i n - get and show n src filters\n"); 397167346Sbms printf("f filename - read command(s) from file\n"); 398167346Sbms printf("s seconds - sleep for some time\n"); 399167346Sbms printf("q - quit\n"); 400167346Sbms} 401167346Sbms 402167346Sbms#ifdef WITH_IGMPV3 403167346Sbmsstatic int 404167346Sbmsinaddr_cmp(const void *a, const void *b) 405167346Sbms{ 406167346Sbms return((int)((const struct in_addr *)a)->s_addr - 407167346Sbms ((const struct in_addr *)b)->s_addr); 408167346Sbms} 409167346Sbms#endif 410