1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2007-2009 Bruce Simpson.
5 * Copyright (c) 2000 Wilbert De Graaf.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33/*
34 * Diagnostic and test utility for multicast sockets.
35 * XXX: This file currently assumes INET support in the base system.
36 * TODO: Support embedded KAME Scope ID in IPv6 group addresses.
37 * TODO: Use IPv4 link-local address when source address selection
38 * is implemented; use MCAST_JOIN_SOURCE for IPv4.
39 */
40
41#include <sys/cdefs.h>
42__FBSDID("$FreeBSD$");
43
44#include <sys/types.h>
45#include <sys/param.h>
46#include <sys/errno.h>
47#include <sys/socket.h>
48#include <sys/time.h>
49#include <sys/ioctl.h>
50
51#include <net/if.h>
52#include <net/if_dl.h>
53#include <net/ethernet.h>
54#ifdef INET
55#include <netinet/in.h>
56#include <netinet/in_systm.h>
57#include <netinet/ip.h>
58#include <netinet/ip_var.h>
59#endif
60#ifdef INET6
61#include <netinet/in.h>
62#include <netinet/ip6.h>
63#endif
64
65#include <assert.h>
66#include <stdlib.h>
67#include <stdio.h>
68#include <string.h>
69#include <ctype.h>
70#include <errno.h>
71#include <err.h>
72#include <unistd.h>
73
74#include <arpa/inet.h>
75#include <netdb.h>
76#include <ifaddrs.h>
77
78union sockunion {
79	struct sockaddr_storage	ss;
80	struct sockaddr		sa;
81	struct sockaddr_dl	sdl;
82#ifdef INET
83	struct sockaddr_in	sin;
84#endif
85#ifdef INET6
86	struct sockaddr_in6	sin6;
87#endif
88};
89typedef union sockunion sockunion_t;
90
91union mrequnion {
92#ifdef INET
93	struct ip_mreq	 	 mr;
94	struct ip_mreq_source	 mrs;
95#endif
96#ifdef INET6
97	struct ipv6_mreq	 mr6;
98	struct group_source_req	 gr;
99#endif
100};
101typedef union mrequnion mrequnion_t;
102
103#define	MAX_ADDRS	20
104#define	STR_SIZE	20
105#define	LINE_LENGTH	80
106
107#ifdef INET
108static int	__ifindex_to_primary_ip(const uint32_t, struct in_addr *);
109#endif
110static uint32_t	parse_cmd_args(sockunion_t *, sockunion_t *,
111		    const char *, const char *, const char *);
112static void	process_file(char *, int, int);
113static void	process_cmd(char*, int, int, FILE *);
114static int	su_cmp(const void *, const void *);
115static void	usage(void);
116
117/*
118 * Ordering predicate for qsort().
119 */
120static int
121su_cmp(const void *a, const void *b)
122{
123	const sockunion_t	*sua = (const sockunion_t *)a;
124	const sockunion_t	*sub = (const sockunion_t *)b;
125
126	assert(sua->sa.sa_family == sub->sa.sa_family);
127
128	switch (sua->sa.sa_family) {
129#ifdef INET
130	case AF_INET:
131		return ((int)(sua->sin.sin_addr.s_addr -
132		    sub->sin.sin_addr.s_addr));
133		break;
134#endif
135#ifdef INET6
136	case AF_INET6:
137		return (memcmp(&sua->sin6.sin6_addr, &sub->sin6.sin6_addr,
138		    sizeof(struct in6_addr)));
139		break;
140#endif
141	default:
142		break;
143	}
144
145	assert(sua->sa.sa_len == sub->sa.sa_len);
146	return (memcmp(sua, sub, sua->sa.sa_len));
147}
148
149#ifdef INET
150/*
151 * Internal: Map an interface index to primary IPv4 address.
152 * This is somewhat inefficient. This is a useful enough operation
153 * that it probably belongs in the C library.
154 * Return zero if found, -1 on error, 1 on not found.
155 */
156static int
157__ifindex_to_primary_ip(const uint32_t ifindex, struct in_addr *pina)
158{
159	char		 ifname[IFNAMSIZ];
160	struct ifaddrs	*ifa;
161	struct ifaddrs	*ifaddrs;
162	sockunion_t	*psu;
163	int		 retval;
164
165	assert(ifindex != 0);
166
167	retval = -1;
168	if (if_indextoname(ifindex, ifname) == NULL)
169		return (retval);
170	if (getifaddrs(&ifaddrs) < 0)
171		return (retval);
172
173	/*
174	 * Find the ifaddr entry corresponding to the interface name,
175	 * and return the first matching IPv4 address.
176	 */
177	retval = 1;
178	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
179		if (strcmp(ifa->ifa_name, ifname) != 0)
180			continue;
181		psu = (sockunion_t *)ifa->ifa_addr;
182		if (psu && psu->sa.sa_family == AF_INET) {
183			retval = 0;
184			memcpy(pina, &psu->sin.sin_addr,
185			    sizeof(struct in_addr));
186			break;
187		}
188	}
189
190	if (retval != 0)
191		errno = EADDRNOTAVAIL;	/* XXX */
192
193	freeifaddrs(ifaddrs);
194	return (retval);
195}
196#endif /* INET */
197
198int
199main(int argc, char **argv)
200{
201	char	 line[LINE_LENGTH];
202	char	*p;
203	int	 i, s, s6;
204
205	s = -1;
206	s6 = -1;
207#ifdef INET
208	s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
209	if (s == -1 && errno != EAFNOSUPPORT)
210		err(1, "can't open IPv4 socket");
211#endif
212#ifdef INET6
213	s6 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
214	if (s6 == -1 && errno != EAFNOSUPPORT)
215		err(1, "can't open IPv6 socket");
216#endif
217	if (s == -1 && s6 == -1)
218		errc(1, EPROTONOSUPPORT, "can't open socket");
219
220	if (argc < 2) {
221		if (isatty(STDIN_FILENO)) {
222			printf("multicast membership test program; "
223			    "enter ? for list of commands\n");
224		}
225		do {
226			if (fgets(line, sizeof(line), stdin) != NULL) {
227				if (line[0] != 'f')
228					process_cmd(line, s, s6, stdin);
229				else {
230					/* Get the filename */
231					for (i = 1; isblank(line[i]); i++);
232					if ((p = (char*)strchr(line, '\n'))
233					    != NULL)
234						*p = '\0';
235					process_file(&line[i], s, s6);
236				}
237			}
238		} while (!feof(stdin));
239	} else {
240		for (i = 1; i < argc; i++) {
241			process_file(argv[i], s, s6);
242		}
243	}
244
245	if (s != -1)
246		close(s);
247	if (s6 != -1)
248		close(s6);
249
250	exit (0);
251}
252
253static void
254process_file(char *fname, int s, int s6)
255{
256	char line[80];
257	FILE *fp;
258	char *lineptr;
259
260	fp = fopen(fname, "r");
261	if (fp == NULL) {
262		warn("fopen");
263		return;
264	}
265
266	/* Skip comments and empty lines. */
267	while (fgets(line, sizeof(line), fp) != NULL) {
268		lineptr = line;
269		while (isblank(*lineptr))
270			lineptr++;
271		if (*lineptr != '#' && *lineptr != '\n')
272			process_cmd(lineptr, s, s6, fp);
273	}
274
275	fclose(fp);
276}
277
278/*
279 * Parse join/leave/allow/block arguments, given:
280 *  str1: group (as AF_INET or AF_INET6 printable)
281 *  str2: ifname
282 *  str3: optional source address (may be NULL).
283 *   This argument must have the same parsed address family as str1.
284 * Return the ifindex of ifname, or 0 if any parse element failed.
285 */
286static uint32_t
287parse_cmd_args(sockunion_t *psu, sockunion_t *psu2,
288    const char *str1, const char *str2, const char *str3)
289{
290	struct addrinfo		 hints;
291	struct addrinfo		*res;
292	uint32_t		 ifindex;
293	int			 af, error;
294
295	assert(psu != NULL);
296	assert(str1 != NULL);
297	assert(str2 != NULL);
298
299	af = AF_UNSPEC;
300
301	ifindex = if_nametoindex(str2);
302	if (ifindex == 0)
303		return (0);
304
305	memset(&hints, 0, sizeof(struct addrinfo));
306	hints.ai_flags = AI_NUMERICHOST;
307	hints.ai_family = PF_UNSPEC;
308	hints.ai_socktype = SOCK_DGRAM;
309
310	memset(psu, 0, sizeof(sockunion_t));
311	psu->sa.sa_family = AF_UNSPEC;
312
313	error = getaddrinfo(str1, "0", &hints, &res);
314	if (error) {
315		warnx("getaddrinfo: %s", gai_strerror(error));
316		return (0);
317	}
318	assert(res != NULL);
319	af = res->ai_family;
320	memcpy(psu, res->ai_addr, res->ai_addrlen);
321	freeaddrinfo(res);
322
323	/* sscanf() may pass the empty string. */
324	if (psu2 != NULL && str3 != NULL && *str3 != '\0') {
325		memset(psu2, 0, sizeof(sockunion_t));
326		psu2->sa.sa_family = AF_UNSPEC;
327
328		/* look for following address family; str3 is *optional*. */
329		hints.ai_family = af;
330		error = getaddrinfo(str3, "0", &hints, &res);
331		if (error) {
332			warnx("getaddrinfo: %s", gai_strerror(error));
333			ifindex = 0;
334		} else {
335			if (af != res->ai_family) {
336				errno = EINVAL; /* XXX */
337				ifindex = 0;
338			}
339			memcpy(psu2, res->ai_addr, res->ai_addrlen);
340			freeaddrinfo(res);
341		}
342	}
343
344	return (ifindex);
345}
346
347static __inline int
348af2sock(const int af, int s, int s6)
349{
350
351#ifdef INET
352	if (af == AF_INET)
353		return (s);
354#endif
355#ifdef INET6
356	if (af == AF_INET6)
357		return (s6);
358#endif
359	return (-1);
360}
361
362static __inline int
363af2socklen(const int af)
364{
365
366#ifdef INET
367	if (af == AF_INET)
368		return (sizeof(struct sockaddr_in));
369#endif
370#ifdef INET6
371	if (af == AF_INET6)
372		return (sizeof(struct sockaddr_in6));
373#endif
374	return (-1);
375}
376
377static void
378process_cmd(char *cmd, int s, int s6, FILE *fp __unused)
379{
380	char			 str1[STR_SIZE];
381	char			 str2[STR_SIZE];
382	char			 str3[STR_SIZE];
383	mrequnion_t		 mr;
384	sockunion_t		 su, su2;
385	struct ifreq		 ifr;
386	char			*line;
387	char			*toptname;
388	void			*optval;
389	uint32_t		 fmode, ifindex;
390	socklen_t		 optlen;
391	size_t			 j;
392	int			 af, error, f, flags, i, level, n, optname;
393
394	af = AF_UNSPEC;
395	su.sa.sa_family = AF_UNSPEC;
396	su2.sa.sa_family = AF_UNSPEC;
397
398	line = cmd;
399	while (isblank(*++line))
400		;	/* Skip whitespace. */
401
402	n = 0;
403	switch (*cmd) {
404	case '?':
405		usage();
406		break;
407
408	case 'q':
409		close(s);
410		exit(0);
411
412	case 's':
413		if ((sscanf(line, "%d", &n) != 1) || (n < 1)) {
414			printf("-1\n");
415			break;
416		}
417		sleep(n);
418		printf("ok\n");
419		break;
420
421	case 'j':
422	case 'l':
423		str3[0] = '\0';
424		toptname = "";
425		sscanf(line, "%s %s %s", str1, str2, str3);
426		ifindex = parse_cmd_args(&su, &su2, str1, str2, str3);
427		if (ifindex == 0) {
428			printf("-1\n");
429			break;
430		}
431		af = su.sa.sa_family;
432#ifdef INET
433		if (af == AF_INET) {
434			struct in_addr ina;
435
436			error = __ifindex_to_primary_ip(ifindex, &ina);
437			if (error != 0) {
438				warn("primary_ip_lookup %s", str2);
439				printf("-1\n");
440				break;
441			}
442			level = IPPROTO_IP;
443
444			if (su2.sa.sa_family != AF_UNSPEC) {
445				mr.mrs.imr_multiaddr = su.sin.sin_addr;
446				mr.mrs.imr_sourceaddr = su2.sin.sin_addr;
447				mr.mrs.imr_interface = ina;
448				optname = (*cmd == 'j') ?
449				    IP_ADD_SOURCE_MEMBERSHIP :
450				    IP_DROP_SOURCE_MEMBERSHIP;
451				toptname = (*cmd == 'j') ?
452				    "IP_ADD_SOURCE_MEMBERSHIP" :
453				    "IP_DROP_SOURCE_MEMBERSHIP";
454				optval = (void *)&mr.mrs;
455				optlen = sizeof(mr.mrs);
456			} else {
457				mr.mr.imr_multiaddr = su.sin.sin_addr;
458				mr.mr.imr_interface = ina;
459				optname = (*cmd == 'j') ?
460				    IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
461				toptname = (*cmd == 'j') ?
462				    "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP";
463				optval = (void *)&mr.mr;
464				optlen = sizeof(mr.mr);
465			}
466			if (s < 0) {
467				warnc(EPROTONOSUPPORT, "setsockopt %s",
468				    toptname);
469			} else if (setsockopt(s, level, optname, optval,
470			    optlen) == 0) {
471				printf("ok\n");
472				break;
473			} else {
474				warn("setsockopt %s", toptname);
475			}
476		}
477#ifdef INET6
478		else
479#endif /* INET with INET6 */
480#endif /* INET */
481#ifdef INET6
482		if (af == AF_INET6) {
483			level = IPPROTO_IPV6;
484			if (su2.sa.sa_family != AF_UNSPEC) {
485				mr.gr.gsr_interface = ifindex;
486				mr.gr.gsr_group = su.ss;
487				mr.gr.gsr_source = su2.ss;
488				optname = (*cmd == 'j') ?
489				    MCAST_JOIN_SOURCE_GROUP:
490				    MCAST_LEAVE_SOURCE_GROUP;
491				toptname = (*cmd == 'j') ?
492				    "MCAST_JOIN_SOURCE_GROUP":
493				    "MCAST_LEAVE_SOURCE_GROUP";
494				optval = (void *)&mr.gr;
495				optlen = sizeof(mr.gr);
496			} else {
497				mr.mr6.ipv6mr_multiaddr = su.sin6.sin6_addr;
498				mr.mr6.ipv6mr_interface = ifindex;
499				optname = (*cmd == 'j') ?
500				    IPV6_JOIN_GROUP :
501				    IPV6_LEAVE_GROUP;
502				toptname = (*cmd == 'j') ?
503				    "IPV6_JOIN_GROUP" :
504				    "IPV6_LEAVE_GROUP";
505				optval = (void *)&mr.mr6;
506				optlen = sizeof(mr.mr6);
507			}
508			if (s6 < 0) {
509				warnc(EPROTONOSUPPORT, "setsockopt %s",
510				    toptname);
511			} else if (setsockopt(s6, level, optname, optval,
512			    optlen) == 0) {
513				printf("ok\n");
514				break;
515			} else {
516				warn("setsockopt %s", toptname);
517			}
518		}
519#endif /* INET6 */
520		/* FALLTHROUGH */
521		printf("-1\n");
522		break;
523
524	/*
525	 * Set the socket to include or exclude filter mode, and
526	 * add some sources to the filterlist, using the full-state API.
527	 */
528	case 'i':
529	case 'e': {
530		sockunion_t	 sources[MAX_ADDRS];
531		struct addrinfo	 hints;
532		struct addrinfo	*res;
533		char		*cp;
534		int		 af1;
535
536		n = 0;
537		fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE;
538		if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
539			printf("-1\n");
540			break;
541		}
542
543		ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
544		if (ifindex == 0 || n < 0 || n > MAX_ADDRS) {
545			printf("-1\n");
546			break;
547		}
548		af = su.sa.sa_family;
549		if (af2sock(af, s, s6) == -1) {
550			warnc(EPROTONOSUPPORT, "setsourcefilter");
551			break;
552		}
553
554		memset(&hints, 0, sizeof(struct addrinfo));
555		hints.ai_flags = AI_NUMERICHOST;
556		hints.ai_family = af;
557		hints.ai_socktype = SOCK_DGRAM;
558
559		for (i = 0; i < n; i++) {
560			sockunion_t *psu = (sockunion_t *)&sources[i];
561			/*
562			 * Trim trailing whitespace, as getaddrinfo()
563			 * can't cope with it.
564			 */
565			fgets(str1, sizeof(str1), fp);
566			cp = strchr(str1, '\n');
567			if (cp != NULL)
568				*cp = '\0';
569
570			res = NULL;
571			error = getaddrinfo(str1, "0", &hints, &res);
572			if (error)
573				break;
574			assert(res != NULL);
575
576			memset(psu, 0, sizeof(sockunion_t));
577			af1 = res->ai_family;
578			if (af1 == af)
579				memcpy(psu, res->ai_addr, res->ai_addrlen);
580			freeaddrinfo(res);
581			if (af1 != af)
582				break;
583		}
584		if (i < n) {
585			if (error)
586				warnx("getaddrinfo: %s", gai_strerror(error));
587			printf("-1\n");
588			break;
589		}
590		if (setsourcefilter(af2sock(af, s, s6), ifindex,
591		    &su.sa, su.sa.sa_len, fmode, n, &sources[0].ss) != 0)
592			warn("setsourcefilter");
593		else
594			printf("ok\n");
595	} break;
596
597	/*
598	 * Allow or block traffic from a source, using the
599	 * delta based api.
600	 */
601	case 't':
602	case 'b': {
603		str3[0] = '\0';
604		toptname = "";
605		sscanf(line, "%s %s %s", str1, str2, str3);
606		ifindex = parse_cmd_args(&su, &su2, str1, str2, str3);
607		if (ifindex == 0 || su2.sa.sa_family == AF_UNSPEC) {
608			printf("-1\n");
609			break;
610		}
611		af = su.sa.sa_family;
612		if (af2sock(af, s, s6) == -1) {
613			warnc(EPROTONOSUPPORT, "getsourcefilter");
614			break;
615		}
616
617		/* First determine our current filter mode. */
618		if (getsourcefilter(af2sock(af, s, s6), ifindex,
619		    &su.sa, su.sa.sa_len, &fmode, &n, NULL) != 0) {
620			warn("getsourcefilter");
621			break;
622		}
623#ifdef INET
624		if (af == AF_INET) {
625			struct in_addr ina;
626
627			error = __ifindex_to_primary_ip(ifindex, &ina);
628			if (error != 0) {
629				warn("primary_ip_lookup %s", str2);
630				printf("-1\n");
631				break;
632			}
633			level = IPPROTO_IP;
634			optval = (void *)&mr.mrs;
635			optlen = sizeof(mr.mrs);
636			mr.mrs.imr_multiaddr = su.sin.sin_addr;
637			mr.mrs.imr_sourceaddr = su2.sin.sin_addr;
638			mr.mrs.imr_interface = ina;
639			if (fmode == MCAST_EXCLUDE) {
640				/* Any-source mode socket membership. */
641				optname = (*cmd == 't') ?
642				    IP_UNBLOCK_SOURCE :
643				    IP_BLOCK_SOURCE;
644				toptname = (*cmd == 't') ?
645				    "IP_UNBLOCK_SOURCE" :
646				    "IP_BLOCK_SOURCE";
647			} else {
648				/* Source-specific mode socket membership. */
649				optname = (*cmd == 't') ?
650				    IP_ADD_SOURCE_MEMBERSHIP :
651				    IP_DROP_SOURCE_MEMBERSHIP;
652				toptname = (*cmd == 't') ?
653				    "IP_ADD_SOURCE_MEMBERSHIP" :
654				    "IP_DROP_SOURCE_MEMBERSHIP";
655			}
656			if (setsockopt(s, level, optname, optval,
657			    optlen) == 0) {
658				printf("ok\n");
659				break;
660			} else {
661				warn("setsockopt %s", toptname);
662			}
663		}
664#ifdef INET6
665		else
666#endif /* INET with INET6 */
667#endif /* INET */
668#ifdef INET6
669		if (af == AF_INET6) {
670			level = IPPROTO_IPV6;
671			mr.gr.gsr_interface = ifindex;
672			mr.gr.gsr_group = su.ss;
673			mr.gr.gsr_source = su2.ss;
674			if (fmode == MCAST_EXCLUDE) {
675				/* Any-source mode socket membership. */
676				optname = (*cmd == 't') ?
677				    MCAST_UNBLOCK_SOURCE :
678				    MCAST_BLOCK_SOURCE;
679				toptname = (*cmd == 't') ?
680				    "MCAST_UNBLOCK_SOURCE" :
681				    "MCAST_BLOCK_SOURCE";
682			} else {
683				/* Source-specific mode socket membership. */
684				optname = (*cmd == 't') ?
685				    MCAST_JOIN_SOURCE_GROUP :
686				    MCAST_LEAVE_SOURCE_GROUP;
687				toptname = (*cmd == 't') ?
688				    "MCAST_JOIN_SOURCE_GROUP":
689				    "MCAST_LEAVE_SOURCE_GROUP";
690			}
691			optval = (void *)&mr.gr;
692			optlen = sizeof(mr.gr);
693			if (setsockopt(s6, level, optname, optval,
694			    optlen) == 0) {
695				printf("ok\n");
696				break;
697			} else {
698				warn("setsockopt %s", toptname);
699			}
700		}
701#endif /* INET6 */
702		/* FALLTHROUGH */
703		printf("-1\n");
704	} break;
705
706	case 'g': {
707		sockunion_t	 sources[MAX_ADDRS];
708		char		 addrbuf[NI_MAXHOST];
709		int		 nreqsrc, nsrc;
710
711		if ((sscanf(line, "%s %s %d", str1, str2, &nreqsrc)) != 3) {
712			printf("-1\n");
713			break;
714		}
715		ifindex = parse_cmd_args(&su, NULL, str1, str2, NULL);
716		if (ifindex == 0 || (n < 0 || n > MAX_ADDRS)) {
717			printf("-1\n");
718			break;
719		}
720
721		af = su.sa.sa_family;
722		if (af2sock(af, s, s6) == -1) {
723			warnc(EPROTONOSUPPORT, "getsourcefilter");
724			break;
725		}
726		nsrc = nreqsrc;
727		if (getsourcefilter(af2sock(af, s, s6), ifindex, &su.sa,
728		    su.sa.sa_len, &fmode, &nsrc, &sources[0].ss) != 0) {
729			warn("getsourcefilter");
730			printf("-1\n");
731			break;
732		}
733		printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" :
734		    "exclude");
735		printf("%d\n", nsrc);
736
737		nsrc = MIN(nreqsrc, nsrc);
738		fprintf(stderr, "hexdump of sources:\n");
739		uint8_t *bp = (uint8_t *)&sources[0];
740		for (j = 0; j < (nsrc * sizeof(sources[0])); j++) {
741			fprintf(stderr, "%02x", bp[j]);
742		}
743		fprintf(stderr, "\nend hexdump\n");
744
745		qsort(sources, nsrc, af2socklen(af), su_cmp);
746		for (i = 0; i < nsrc; i++) {
747			sockunion_t *psu = (sockunion_t *)&sources[i];
748			addrbuf[0] = '\0';
749			error = getnameinfo(&psu->sa, psu->sa.sa_len,
750			    addrbuf, sizeof(addrbuf), NULL, 0,
751			    NI_NUMERICHOST);
752			if (error)
753				warnx("getnameinfo: %s", gai_strerror(error));
754			else
755				printf("%s\n", addrbuf);
756		}
757		printf("ok\n");
758	} break;
759
760	/* link-layer stuff follows. */
761
762	case 'a':
763	case 'd': {
764		struct sockaddr_dl	*dlp;
765		struct ether_addr	*ep;
766
767		memset(&ifr, 0, sizeof(struct ifreq));
768		dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
769		dlp->sdl_len = sizeof(struct sockaddr_dl);
770		dlp->sdl_family = AF_LINK;
771		dlp->sdl_index = 0;
772		dlp->sdl_nlen = 0;
773		dlp->sdl_alen = ETHER_ADDR_LEN;
774		dlp->sdl_slen = 0;
775		if (sscanf(line, "%s %s", str1, str2) != 2) {
776			warnc(EINVAL, "sscanf");
777			break;
778		}
779		ep = ether_aton(str2);
780		if (ep == NULL) {
781			warnc(EINVAL, "ether_aton");
782			break;
783		}
784		strlcpy(ifr.ifr_name, str1, IF_NAMESIZE);
785		memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN);
786		if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI,
787		    &ifr) == -1) {
788			warn("ioctl SIOCADDMULTI/SIOCDELMULTI");
789			printf("-1\n");
790		} else
791			printf("ok\n");
792		break;
793	}
794
795	case 'm':
796		fprintf(stderr,
797		    "warning: IFF_ALLMULTI cannot be set from userland "
798		    "in FreeBSD; command ignored.\n");
799		printf("-1\n");
800		break;
801
802	case 'p':
803		if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) {
804			printf("-1\n");
805			break;
806		}
807		if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
808			warn("ioctl SIOCGIFFLAGS");
809			break;
810		}
811		flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
812		if (f == 0) {
813			flags &= ~IFF_PPROMISC;
814		} else {
815			flags |= IFF_PPROMISC;
816		}
817		ifr.ifr_flags = flags & 0xffff;
818		ifr.ifr_flagshigh = flags >> 16;
819		if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1)
820			warn("ioctl SIOCGIFFLAGS");
821		else
822			printf( "changed to 0x%08x\n", flags );
823		break;
824
825	case '\n':
826		break;
827	default:
828		printf("invalid command\n");
829		break;
830	}
831}
832
833static void
834usage(void)
835{
836
837	printf("j mcast-addr ifname [src-addr] - join IP multicast group\n");
838	printf("l mcast-addr ifname [src-addr] - leave IP multicast group\n");
839	printf(
840"i mcast-addr ifname n          - set n include mode src filter\n");
841	printf(
842"e mcast-addr ifname n          - set n exclude mode src filter\n");
843	printf("t mcast-addr ifname src-addr  - allow traffic from src\n");
844	printf("b mcast-addr ifname src-addr  - block traffic from src\n");
845	printf("g mcast-addr ifname n        - get and show n src filters\n");
846	printf("a ifname mac-addr          - add link multicast filter\n");
847	printf("d ifname mac-addr          - delete link multicast filter\n");
848	printf("m ifname 1/0               - set/clear ether allmulti flag\n");
849	printf("p ifname 1/0               - set/clear ether promisc flag\n");
850	printf("f filename                 - read command(s) from file\n");
851	printf("s seconds                  - sleep for some time\n");
852	printf("q                          - quit\n");
853}
854
855