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