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