1*5ef06edbSnisimura /* $NetBSD: ifmcstat.c,v 1.22 2020/03/03 08:56:05 nisimura Exp $ */
226c3d764Sitojun
345124457Sitojun /*
445124457Sitojun * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
545124457Sitojun * All rights reserved.
645124457Sitojun *
745124457Sitojun * Redistribution and use in source and binary forms, with or without
845124457Sitojun * modification, are permitted provided that the following conditions
945124457Sitojun * are met:
1045124457Sitojun * 1. Redistributions of source code must retain the above copyright
1145124457Sitojun * notice, this list of conditions and the following disclaimer.
1245124457Sitojun * 2. Redistributions in binary form must reproduce the above copyright
1345124457Sitojun * notice, this list of conditions and the following disclaimer in the
1445124457Sitojun * documentation and/or other materials provided with the distribution.
1545124457Sitojun * 3. Neither the name of the project nor the names of its contributors
1645124457Sitojun * may be used to endorse or promote products derived from this software
1745124457Sitojun * without specific prior written permission.
1845124457Sitojun *
1945124457Sitojun * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2045124457Sitojun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2145124457Sitojun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2245124457Sitojun * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2345124457Sitojun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2445124457Sitojun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2545124457Sitojun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2645124457Sitojun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2745124457Sitojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2845124457Sitojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2945124457Sitojun * SUCH DAMAGE.
3045124457Sitojun */
31539332ecSjoerg #include <sys/cdefs.h>
32*5ef06edbSnisimura __RCSID("$NetBSD: ifmcstat.c,v 1.22 2020/03/03 08:56:05 nisimura Exp $");
3345124457Sitojun
34539332ecSjoerg #include <err.h>
35539332ecSjoerg #include <errno.h>
3645124457Sitojun #include <stdio.h>
3745124457Sitojun #include <stdlib.h>
3845124457Sitojun #include <fcntl.h>
3945124457Sitojun #include <kvm.h>
4045124457Sitojun #include <nlist.h>
4145124457Sitojun #include <string.h>
426002e343Sitojun #include <limits.h>
43539332ecSjoerg #include <util.h>
4445124457Sitojun
4545124457Sitojun #include <sys/types.h>
4645124457Sitojun #include <sys/socket.h>
47539332ecSjoerg #include <sys/sysctl.h>
4845124457Sitojun #include <net/if.h>
494265f28eSitojun #include <net/if_types.h>
5045124457Sitojun #include <net/if_dl.h>
5145124457Sitojun #include <netinet/in.h>
5245124457Sitojun #include <net/if_ether.h>
5345124457Sitojun #include <netinet/in_var.h>
5445124457Sitojun #include <arpa/inet.h>
5545124457Sitojun
562925a0d6Sitojun #include <netdb.h>
572925a0d6Sitojun
58539332ecSjoerg static const char *inet6_n2a(void *);
59539332ecSjoerg static void print_ether_mcast(u_short);
60539332ecSjoerg static void print_inet6_mcast(u_short, const char *);
614d9ae69bSitojun
6279c2ddeeSjoerg static const char *
inet6_n2a(void * p)63539332ecSjoerg inet6_n2a(void *p)
6445124457Sitojun {
652925a0d6Sitojun static char buf[NI_MAXHOST];
662925a0d6Sitojun struct sockaddr_in6 sin6;
672925a0d6Sitojun const int niflags = NI_NUMERICHOST;
6845124457Sitojun
692925a0d6Sitojun memset(&sin6, 0, sizeof(sin6));
702925a0d6Sitojun sin6.sin6_family = AF_INET6;
712925a0d6Sitojun sin6.sin6_len = sizeof(struct sockaddr_in6);
72539332ecSjoerg memcpy(&sin6.sin6_addr, p, sizeof(sin6.sin6_addr));
7394a96ec4Schristos inet6_getscopeid(&sin6, INET6_IS_ADDR_LINKLOCAL|
7494a96ec4Schristos INET6_IS_ADDR_MC_LINKLOCAL);
752925a0d6Sitojun if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
762925a0d6Sitojun buf, sizeof(buf), NULL, 0, niflags) == 0)
772925a0d6Sitojun return buf;
782925a0d6Sitojun else
792925a0d6Sitojun return "(invalid)";
8045124457Sitojun }
8145124457Sitojun
8226e6c607Sozaki-r static bool
check_inet6_availability(void)8326e6c607Sozaki-r check_inet6_availability(void)
8426e6c607Sozaki-r {
8526e6c607Sozaki-r size_t dummy;
8626e6c607Sozaki-r
8726e6c607Sozaki-r return sysctlnametomib("net.inet6.multicast", NULL, &dummy) == 0;
8826e6c607Sozaki-r }
8926e6c607Sozaki-r
9079c2ddeeSjoerg int
main(void)9179c2ddeeSjoerg main(void)
9245124457Sitojun {
93539332ecSjoerg struct if_nameindex *ifps;
94539332ecSjoerg size_t i;
9526e6c607Sozaki-r bool inet6_available = check_inet6_availability();
9645124457Sitojun
97539332ecSjoerg ifps = if_nameindex();
98539332ecSjoerg if (ifps == NULL)
99539332ecSjoerg errx(1, "failed to obtain list of interfaces");
10026e6c607Sozaki-r
101539332ecSjoerg for (i = 0; ifps[i].if_name != NULL; ++i) {
102539332ecSjoerg printf("%s:\n", ifps[i].if_name);
10326e6c607Sozaki-r if (inet6_available)
104539332ecSjoerg print_inet6_mcast(ifps[i].if_index, ifps[i].if_name);
105539332ecSjoerg print_ether_mcast(ifps[i].if_index);
10645124457Sitojun }
107539332ecSjoerg if_freenameindex(ifps);
10845124457Sitojun
10945124457Sitojun exit(0);
11045124457Sitojun /*NOTREACHED*/
11145124457Sitojun }
11245124457Sitojun
11379c2ddeeSjoerg static void
print_hwaddr(const uint8_t * hwaddr,size_t len)114539332ecSjoerg print_hwaddr(const uint8_t *hwaddr, size_t len)
11545124457Sitojun {
116539332ecSjoerg while (len)
117*5ef06edbSnisimura printf("%02x%s", *hwaddr++, --len > 0 ? ":" : "");
11845124457Sitojun }
11945124457Sitojun
12079c2ddeeSjoerg static void
print_ether_mcast(u_short ifindex)121539332ecSjoerg print_ether_mcast(u_short ifindex)
12245124457Sitojun {
123539332ecSjoerg static int ems_oids[4], sdl_oids[3];
124539332ecSjoerg size_t i, ems_len, sdl_len;
125539332ecSjoerg void *hwaddr;
126539332ecSjoerg struct ether_multi_sysctl *ems;
12745124457Sitojun
128539332ecSjoerg if (ems_oids[0] == 0) {
129539332ecSjoerg size_t oidlen = __arraycount(ems_oids);
130539332ecSjoerg if (sysctlnametomib("net.ether.multicast", ems_oids, &oidlen) == -1)
131539332ecSjoerg errx(1, "net.ether.multicast not found");
132539332ecSjoerg if (oidlen != 3)
133539332ecSjoerg errx(1, "Wrong OID path for net.ether.multicast");
1344d9ae69bSitojun }
1354d9ae69bSitojun
136539332ecSjoerg if (sdl_oids[0] == 0) {
137539332ecSjoerg size_t oidlen = __arraycount(sdl_oids);
138539332ecSjoerg if (sysctlnametomib("net.sdl", sdl_oids, &oidlen) == -1)
139539332ecSjoerg errx(1, "net.sdl not found");
140539332ecSjoerg if (oidlen != 2)
141539332ecSjoerg errx(1, "Wrong OID path for net.sdl");
1424d9ae69bSitojun }
143539332ecSjoerg
144539332ecSjoerg sdl_oids[2] = ifindex;
1450142924aSjoerg hwaddr = asysctl(sdl_oids, 3, &sdl_len);
146539332ecSjoerg
147539332ecSjoerg if (sdl_len == 0) {
148539332ecSjoerg free(hwaddr);
149539332ecSjoerg return;
150539332ecSjoerg }
1510142924aSjoerg if (hwaddr == NULL) {
1520142924aSjoerg warn("failed to read net.sdl");
1530142924aSjoerg }
154539332ecSjoerg
155539332ecSjoerg ems_oids[3] = ifindex;
1560142924aSjoerg ems = asysctl(ems_oids, 4, &ems_len);
157539332ecSjoerg ems_len /= sizeof(*ems);
158539332ecSjoerg
1590142924aSjoerg if (ems == NULL && ems_len != 0) {
1600142924aSjoerg warn("failed to read net.ether.multicast");
1610142924aSjoerg return;
1620142924aSjoerg }
1630142924aSjoerg
164539332ecSjoerg printf("\tenaddr ");
165539332ecSjoerg print_hwaddr(hwaddr, sdl_len);
166539332ecSjoerg printf(" multicnt %zu\n", ems_len);
167539332ecSjoerg
168539332ecSjoerg for (i = 0; i < ems_len; ++i) {
169539332ecSjoerg printf("\t\t");
170*5ef06edbSnisimura print_hwaddr(ems[i].enm_addrlo, sdl_len);
171539332ecSjoerg printf(" -- ");
172*5ef06edbSnisimura print_hwaddr(ems[i].enm_addrhi, sdl_len);
173*5ef06edbSnisimura printf(" refcount %d\n", ems[i].enm_refcount);
174539332ecSjoerg }
175539332ecSjoerg free(ems);
176539332ecSjoerg free(hwaddr);
17745124457Sitojun }
17845124457Sitojun
17979c2ddeeSjoerg static void
print_inet6_mcast(u_short ifindex,const char * ifname)180539332ecSjoerg print_inet6_mcast(u_short ifindex, const char *ifname)
18145124457Sitojun {
18200a9cf74Sozaki-r static int mcast_oids[4];
183539332ecSjoerg const char *addr;
18400a9cf74Sozaki-r uint8_t *mcast_addrs, *p, *last_p;
185539332ecSjoerg uint32_t refcnt;
186539332ecSjoerg size_t len;
18774b10119Sjoerg
188539332ecSjoerg if (mcast_oids[0] == 0) {
189539332ecSjoerg size_t oidlen = __arraycount(mcast_oids);
190539332ecSjoerg if (sysctlnametomib("net.inet6.multicast", mcast_oids,
191539332ecSjoerg &oidlen) == -1)
192539332ecSjoerg errx(1, "net.inet6.multicast not found");
193539332ecSjoerg if (oidlen != 3)
194539332ecSjoerg errx(1, "Wrong OID path for net.inet6.multicast");
19574b10119Sjoerg }
196539332ecSjoerg
197539332ecSjoerg mcast_oids[3] = ifindex;
198539332ecSjoerg
1990142924aSjoerg mcast_addrs = asysctl(mcast_oids, 4, &len);
2000142924aSjoerg if (mcast_addrs == NULL && len != 0) {
2010142924aSjoerg warn("failed to read net.inet6.multicast");
2020142924aSjoerg return;
2030142924aSjoerg }
204539332ecSjoerg if (len) {
205539332ecSjoerg p = mcast_addrs;
206539332ecSjoerg last_p = NULL;
207539332ecSjoerg while (len >= 2 * sizeof(struct in6_addr) + sizeof(uint32_t)) {
208539332ecSjoerg if (last_p == NULL ||
209539332ecSjoerg memcmp(p, last_p, sizeof(struct in6_addr)))
210539332ecSjoerg printf("\tinet6 %s\n", inet6_n2a(p));
211539332ecSjoerg last_p = p;
212539332ecSjoerg p += sizeof(struct in6_addr);
213539332ecSjoerg addr = inet6_n2a(p);
214539332ecSjoerg p += sizeof(struct in6_addr);
215539332ecSjoerg memcpy(&refcnt, p, sizeof(refcnt));
216539332ecSjoerg p += sizeof(refcnt);
217539332ecSjoerg printf("\t\tgroup %s refcount %" PRIu32 "\n", addr,
218539332ecSjoerg refcnt);
219539332ecSjoerg len -= 2 * sizeof(struct in6_addr) + sizeof(uint32_t);
220539332ecSjoerg }
221539332ecSjoerg }
222539332ecSjoerg free(mcast_addrs);
22345124457Sitojun }
224