xref: /netbsd/usr.sbin/ifmcstat/ifmcstat.c (revision 2bc213a4)
1 /*	$NetBSD: ifmcstat.c,v 1.22 2020/03/03 08:56:05 nisimura Exp $	*/
2 
3 /*
4  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the project nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: ifmcstat.c,v 1.22 2020/03/03 08:56:05 nisimura Exp $");
33 
34 #include <err.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <fcntl.h>
39 #include <kvm.h>
40 #include <nlist.h>
41 #include <string.h>
42 #include <limits.h>
43 #include <util.h>
44 
45 #include <sys/types.h>
46 #include <sys/socket.h>
47 #include <sys/sysctl.h>
48 #include <net/if.h>
49 #include <net/if_types.h>
50 #include <net/if_dl.h>
51 #include <netinet/in.h>
52 #include <net/if_ether.h>
53 #include <netinet/in_var.h>
54 #include <arpa/inet.h>
55 
56 #include <netdb.h>
57 
58 static const char *inet6_n2a(void *);
59 static void print_ether_mcast(u_short);
60 static void print_inet6_mcast(u_short, const char *);
61 
62 static const char *
inet6_n2a(void * p)63 inet6_n2a(void *p)
64 {
65 	static char buf[NI_MAXHOST];
66 	struct sockaddr_in6 sin6;
67 	const int niflags = NI_NUMERICHOST;
68 
69 	memset(&sin6, 0, sizeof(sin6));
70 	sin6.sin6_family = AF_INET6;
71 	sin6.sin6_len = sizeof(struct sockaddr_in6);
72 	memcpy(&sin6.sin6_addr, p, sizeof(sin6.sin6_addr));
73 	inet6_getscopeid(&sin6, INET6_IS_ADDR_LINKLOCAL|
74 	    INET6_IS_ADDR_MC_LINKLOCAL);
75 	if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
76 			buf, sizeof(buf), NULL, 0, niflags) == 0)
77 		return buf;
78 	else
79 		return "(invalid)";
80 }
81 
82 static bool
check_inet6_availability(void)83 check_inet6_availability(void)
84 {
85 	size_t dummy;
86 
87 	return sysctlnametomib("net.inet6.multicast", NULL, &dummy) == 0;
88 }
89 
90 int
main(void)91 main(void)
92 {
93 	struct if_nameindex  *ifps;
94 	size_t i;
95 	bool inet6_available = check_inet6_availability();
96 
97 	ifps = if_nameindex();
98 	if (ifps == NULL)
99 		errx(1, "failed to obtain list of interfaces");
100 
101 	for (i = 0; ifps[i].if_name != NULL; ++i) {
102 		printf("%s:\n", ifps[i].if_name);
103 		if (inet6_available)
104 			print_inet6_mcast(ifps[i].if_index, ifps[i].if_name);
105 		print_ether_mcast(ifps[i].if_index);
106 	}
107 	if_freenameindex(ifps);
108 
109 	exit(0);
110 	/*NOTREACHED*/
111 }
112 
113 static void
print_hwaddr(const uint8_t * hwaddr,size_t len)114 print_hwaddr(const uint8_t *hwaddr, size_t len)
115 {
116 	while (len)
117 		printf("%02x%s", *hwaddr++, --len > 0 ? ":" : "");
118 }
119 
120 static void
print_ether_mcast(u_short ifindex)121 print_ether_mcast(u_short ifindex)
122 {
123 	static int ems_oids[4], sdl_oids[3];
124 	size_t i, ems_len, sdl_len;
125 	void *hwaddr;
126 	struct ether_multi_sysctl *ems;
127 
128 	if (ems_oids[0] == 0) {
129 		size_t oidlen = __arraycount(ems_oids);
130 		if (sysctlnametomib("net.ether.multicast", ems_oids, &oidlen) == -1)
131 			errx(1, "net.ether.multicast not found");
132 		if (oidlen != 3)
133 			errx(1, "Wrong OID path for net.ether.multicast");
134 	}
135 
136 	if (sdl_oids[0] == 0) {
137 		size_t oidlen = __arraycount(sdl_oids);
138 		if (sysctlnametomib("net.sdl", sdl_oids, &oidlen) == -1)
139 			errx(1, "net.sdl not found");
140 		if (oidlen != 2)
141 			errx(1, "Wrong OID path for net.sdl");
142 	}
143 
144 	sdl_oids[2] = ifindex;
145 	hwaddr = asysctl(sdl_oids, 3, &sdl_len);
146 
147 	if (sdl_len == 0) {
148 		free(hwaddr);
149 		return;
150 	}
151 	if (hwaddr == NULL) {
152 		warn("failed to read net.sdl");
153 	}
154 
155 	ems_oids[3] = ifindex;
156 	ems = asysctl(ems_oids, 4, &ems_len);
157 	ems_len /= sizeof(*ems);
158 
159 	if (ems == NULL && ems_len != 0) {
160 		warn("failed to read net.ether.multicast");
161 		return;
162 	}
163 
164 	printf("\tenaddr ");
165 	print_hwaddr(hwaddr, sdl_len);
166 	printf(" multicnt %zu\n", ems_len);
167 
168 	for (i = 0; i < ems_len; ++i) {
169 		printf("\t\t");
170 		print_hwaddr(ems[i].enm_addrlo, sdl_len);
171 		printf(" -- ");
172 		print_hwaddr(ems[i].enm_addrhi, sdl_len);
173 		printf(" refcount %d\n", ems[i].enm_refcount);
174 	}
175 	free(ems);
176 	free(hwaddr);
177 }
178 
179 static void
print_inet6_mcast(u_short ifindex,const char * ifname)180 print_inet6_mcast(u_short ifindex, const char *ifname)
181 {
182 	static int mcast_oids[4];
183 	const char *addr;
184 	uint8_t *mcast_addrs, *p, *last_p;
185 	uint32_t refcnt;
186 	size_t len;
187 
188 	if (mcast_oids[0] == 0) {
189 		size_t oidlen = __arraycount(mcast_oids);
190 		if (sysctlnametomib("net.inet6.multicast", mcast_oids,
191 		    &oidlen) == -1)
192 			errx(1, "net.inet6.multicast not found");
193 		if (oidlen != 3)
194 			errx(1, "Wrong OID path for net.inet6.multicast");
195 	}
196 
197 	mcast_oids[3] = ifindex;
198 
199 	mcast_addrs = asysctl(mcast_oids, 4, &len);
200 	if (mcast_addrs == NULL && len != 0) {
201 		warn("failed to read net.inet6.multicast");
202 		return;
203 	}
204 	if (len) {
205 		p = mcast_addrs;
206 		last_p = NULL;
207 		while (len >= 2 * sizeof(struct in6_addr) + sizeof(uint32_t)) {
208 			if (last_p == NULL ||
209 			    memcmp(p, last_p, sizeof(struct in6_addr)))
210 				printf("\tinet6 %s\n", inet6_n2a(p));
211 			last_p = p;
212 			p += sizeof(struct in6_addr);
213 			addr = inet6_n2a(p);
214 			p += sizeof(struct in6_addr);
215 			memcpy(&refcnt, p, sizeof(refcnt));
216 			p += sizeof(refcnt);
217 			printf("\t\tgroup %s refcount %" PRIu32 "\n", addr,
218 			    refcnt);
219 			len -= 2 * sizeof(struct in6_addr) + sizeof(uint32_t);
220 		}
221 	}
222 	free(mcast_addrs);
223 }
224