2 * Copyright (c) 2007 Bruce M. Simpson.
3 * Copyright (c) 2000 Wilbert De Graaf.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
32 * Diagnostic and test utility for IPv4 multicast sockets.
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
38 #include <sys/types.h>
39 #include <sys/errno.h>
40 #include <sys/socket.h>
42 #include <sys/ioctl.h>
45 #include <net/if_dl.h>
46 #include <net/ethernet.h>
47 #include <netinet/in.h>
49 #include <arpa/inet.h>
58 /* The following two socket options are private to the kernel and libc. */
60 #ifndef IP_SETMSFILTER
61 #define IP_SETMSFILTER 74 /* atomically set filter list */
63 #ifndef IP_GETMSFILTER
64 #define IP_GETMSFILTER 75 /* get filter list */
67 static void process_file(char *, int);
68 static void process_cmd(char*, int, FILE *fp);
69 static void usage(void);
71 static int inaddr_cmp(const void *a, const void *b);
76 #define LINE_LENGTH 80
79 main(int argc, char **argv)
81 char line[LINE_LENGTH];
85 s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
87 err(1, "can't open socket");
90 if (isatty(STDIN_FILENO)) {
91 printf("multicast membership test program; "
92 "enter ? for list of commands\n");
95 if (fgets(line, sizeof(line), stdin) != NULL) {
97 process_cmd(line, s, stdin);
99 /* Get the filename */
100 for (i = 1; isblank(line[i]); i++);
101 if ((p = (char*)strchr(line, '\n'))
104 process_file(&line[i], s);
107 } while (!feof(stdin));
109 for (i = 1; i < argc; i++) {
110 process_file(argv[i], s);
118 process_file(char *fname, int s)
124 fp = fopen(fname, "r");
130 /* Skip comments and empty lines. */
131 while (fgets(line, sizeof(line), fp) != NULL) {
133 while (isblank(*lineptr))
135 if (*lineptr != '#' && *lineptr != '\n')
136 process_cmd(lineptr, s, fp);
143 process_cmd(char *cmd, int s, FILE *fp __unused)
149 char filtbuf[IP_MSFILTER_SIZE(MAX_ADDRS)];
153 struct ip_mreq_source imrs;
155 struct ip_msfilter *imsfp;
158 int n, opt, f, flags;
161 while (isblank(*++line))
162 ; /* Skip whitespace. */
174 if ((sscanf(line, "%d", &n) != 1) || (n < 1)) {
184 sscanf(line, "%s %s", str1, str2);
185 if (((imr.imr_multiaddr.s_addr = inet_addr(str1)) ==
187 ((imr.imr_interface.s_addr = inet_addr(str2)) ==
192 opt = (*cmd == 'j') ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
193 if (setsockopt( s, IPPROTO_IP, opt, &imr,
195 warn("setsockopt IP_ADD_MEMBERSHIP/IP_DROP_MEMBERSHIP");
202 struct sockaddr_dl *dlp;
203 struct ether_addr *ep;
205 memset(&ifr, 0, sizeof(struct ifreq));
206 dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
207 dlp->sdl_len = sizeof(struct sockaddr_dl);
208 dlp->sdl_family = AF_LINK;
211 dlp->sdl_alen = ETHER_ADDR_LEN;
213 if (sscanf(line, "%s %s", str1, str2) != 2) {
214 warnc(EINVAL, "sscanf");
217 ep = ether_aton(str2);
219 warnc(EINVAL, "ether_aton");
222 strlcpy(ifr.ifr_name, str1, IF_NAMESIZE);
223 memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN);
224 if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI,
226 warn("ioctl SIOCADDMULTI/SIOCDELMULTI");
233 printf("warning: IFF_ALLMULTI cannot be set from userland "
234 "in FreeBSD; command ignored.\n");
237 if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) {
241 if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
242 warn("ioctl SIOCGIFFLAGS");
245 flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
252 ifr.ifr_flags = flags & 0xffff;
253 ifr.ifr_flagshigh = flags >> 16;
254 if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1)
255 warn("ioctl SIOCGIFFLAGS");
257 printf( "changed to 0x%08x\n", flags );
262 * Set the socket to include or exclude filter mode, and
263 * add some sources to the filterlist, using the full-state,
268 /* XXX: SIOCSIPMSFILTER will be made an internal API. */
269 if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
273 imsfp = (struct ip_msfilter *)filtbuf;
274 if (((imsfp->imsf_multiaddr.s_addr = inet_addr(str1)) ==
276 ((imsfp->imsf_interface.s_addr = inet_addr(str2)) ==
277 INADDR_NONE) || (n > MAX_ADDRS)) {
281 imsfp->imsf_fmode = (*cmd == 'i') ? MCAST_INCLUDE :
283 imsfp->imsf_numsrc = n;
284 for (i = 0; i < n; i++) {
285 fgets(str1, sizeof(str1), fp);
286 if ((imsfp->imsf_slist[i].s_addr = inet_addr(str1)) ==
292 if (ioctl(s, SIOCSIPMSFILTER, imsfp) != 0)
293 warn("setsockopt SIOCSIPMSFILTER");
297 #endif /* WITH_IGMPV3 */
300 * Allow or block traffic from a source, using the
302 * XXX: Currently we allow this to be used with the ASM-only
303 * implementation of RFC3678 in FreeBSD 7.
307 sscanf(line, "%s %s %s", str1, str2, str3);
308 if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) ==
310 ((imrs.imr_interface.s_addr = inet_addr(str2)) ==
312 ((imrs.imr_sourceaddr.s_addr = inet_addr(str3)) ==
319 /* XXX: SIOCSIPMSFILTER will be made an internal API. */
320 /* First determine out current filter mode. */
321 imsfp = (struct ip_msfilter *)filtbuf;
322 imsfp->imsf_multiaddr.s_addr = imrs.imr_multiaddr.s_addr;
323 imsfp->imsf_interface.s_addr = imrs.imr_interface.s_addr;
324 imsfp->imsf_numsrc = 5;
325 if (ioctl(s, SIOCSIPMSFILTER, imsfp) != 0) {
326 /* It's only okay for 't' to fail */
328 warn("ioctl SIOCSIPMSFILTER");
331 imsfp->imsf_fmode = MCAST_INCLUDE;
334 if (imsfp->imsf_fmode == MCAST_EXCLUDE) {
336 opt = (*cmd == 't') ? IP_UNBLOCK_SOURCE :
339 /* Controlled source */
340 opt = (*cmd == 't') ? IP_ADD_SOURCE_MEMBERSHIP :
341 IP_DROP_SOURCE_MEMBERSHIP;
343 #else /* !WITH_IGMPV3 */
345 * Don't look before we leap; we may only block or unblock
346 * sources on a socket in exclude mode.
348 opt = (*cmd == 't') ? IP_UNBLOCK_SOURCE : IP_BLOCK_SOURCE;
349 #endif /* WITH_IGMPV3 */
350 if (setsockopt(s, IPPROTO_IP, opt, &imrs, sizeof(imrs)) == -1)
351 warn("ioctl IP_ADD_SOURCE_MEMBERSHIP/IP_DROP_SOURCE_MEMBERSHIP/IP_UNBLOCK_SOURCE/IP_BLOCK_SOURCE");
358 /* XXX: SIOCSIPMSFILTER will be made an internal API. */
359 if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
363 imsfp = (struct ip_msfilter *)filtbuf;
364 if (((imsfp->imsf_multiaddr.s_addr = inet_addr(str1)) ==
366 ((imsfp->imsf_interface.s_addr = inet_addr(str2)) ==
367 INADDR_NONE) || (n < 0 || n > MAX_ADDRS)) {
371 imsfp->imsf_numsrc = n;
372 if (ioctl(s, SIOCSIPMSFILTER, imsfp) != 0) {
373 warn("setsockopt SIOCSIPMSFILTER");
376 printf("%s\n", (imsfp->imsf_fmode == MCAST_INCLUDE) ?
377 "include" : "exclude");
378 printf("%d\n", imsfp->imsf_numsrc);
379 if (n >= imsfp->imsf_numsrc) {
380 n = imsfp->imsf_numsrc;
381 qsort(imsfp->imsf_slist, n, sizeof(struct in_addr),
383 for (i = 0; i < n; i++)
384 printf("%s\n", inet_ntoa(imsfp->imsf_slist[i]));
387 #endif /* !WITH_IGMPV3 */
393 printf("warning: IGMPv3 is not supported by this version "
394 "of FreeBSD; command ignored.\n");
396 #endif /* WITH_IGMPV3 */
401 printf("invalid command\n");
410 printf("j g.g.g.g i.i.i.i - join IP multicast group\n");
411 printf("l g.g.g.g i.i.i.i - leave IP multicast group\n");
412 printf("a ifname e.e.e.e.e.e - add ether multicast address\n");
413 printf("d ifname e.e.e.e.e.e - delete ether multicast address\n");
414 printf("m ifname 1/0 - set/clear ether allmulti flag\n");
415 printf("p ifname 1/0 - set/clear ether promisc flag\n");
417 printf("i g.g.g.g i.i.i.i n - set n include mode src filter\n");
418 printf("e g.g.g.g i.i.i.i n - set n exclude mode src filter\n");
420 printf("t g.g.g.g i.i.i.i s.s.s.s - allow traffic from src\n");
421 printf("b g.g.g.g i.i.i.i s.s.s.s - block traffic from src\n");
423 printf("g g.g.g.g i.i.i.i n - get and show n src filters\n");
425 printf("f filename - read command(s) from file\n");
426 printf("s seconds - sleep for some time\n");
427 printf("q - quit\n");
432 inaddr_cmp(const void *a, const void *b)
434 return((int)((const struct in_addr *)a)->s_addr -
435 ((const struct in_addr *)b)->s_addr);