xref: /freebsd/sbin/ifconfig/ifconfig_netlink.c (revision a57ca37dd1848cd42844d9082c4a74c2ed57f68a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdbool.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <netdb.h>
35 
36 #include <sys/bitcount.h>
37 #include <sys/param.h>
38 #include <sys/linker.h>
39 #include <sys/module.h>
40 #include <sys/socket.h>
41 #include <sys/sysctl.h>
42 #include <sys/time.h>
43 #include <sys/types.h>
44 
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 
48 #include <net/ethernet.h>
49 #include <net/if.h>
50 #include <net/if_dl.h>
51 #include <net/if_types.h>
52 #include "ifconfig.h"
53 #include "ifconfig_netlink.h"
54 
55 static const char	*IFFBITS[] = {
56 	"UP",			/* 00:0x1 IFF_UP*/
57 	"BROADCAST",		/* 01:0x2 IFF_BROADCAST*/
58 	"DEBUG",		/* 02:0x4 IFF_DEBUG*/
59 	"LOOPBACK",		/* 03:0x8 IFF_LOOPBACK*/
60 	"POINTOPOINT",		/* 04:0x10 IFF_POINTOPOINT*/
61 	"NEEDSEPOCH",		/* 05:0x20 IFF_NEEDSEPOCH*/
62 	"RUNNING",		/* 06:0x40 IFF_DRV_RUNNING*/
63 	"NOARP",		/* 07:0x80 IFF_NOARP*/
64 	"PROMISC",		/* 08:0x100 IFF_PROMISC*/
65 	"ALLMULTI",		/* 09:0x200 IFF_ALLMULTI*/
66 	"DRV_OACTIVE",		/* 10:0x400 IFF_DRV_OACTIVE*/
67 	"SIMPLEX",		/* 11:0x800 IFF_SIMPLEX*/
68 	"LINK0",		/* 12:0x1000 IFF_LINK0*/
69 	"LINK1",		/* 13:0x2000 IFF_LINK1*/
70 	"LINK2",		/* 14:0x4000 IFF_LINK2*/
71 	"MULTICAST",		/* 15:0x8000 IFF_MULTICAST*/
72 	"CANTCONFIG",		/* 16:0x10000 IFF_CANTCONFIG*/
73 	"PPROMISC",		/* 17:0x20000 IFF_PPROMISC*/
74 	"MONITOR",		/* 18:0x40000 IFF_MONITOR*/
75 	"STATICARP",		/* 19:0x80000 IFF_STATICARP*/
76 	"STICKYARP",		/* 20:0x100000 IFF_STICKYARP*/
77 	"DYING",		/* 21:0x200000 IFF_DYING*/
78 	"RENAMING",		/* 22:0x400000 IFF_RENAMING*/
79 	"NOGROUP",		/* 23:0x800000 IFF_NOGROUP*/
80 	"LOWER_UP",		/* 24:0x1000000 IFF_NETLINK_1*/
81 };
82 
83 static void
84 print_bits(const char *btype, uint32_t *v, const int v_count,
85     const char **names, const int n_count)
86 {
87 	int num = 0;
88 
89 	for (int i = 0; i < v_count * 32; i++) {
90 		bool is_set = v[i / 32] & (1 << (i % 32));
91 		if (i == 31)
92 			v++;
93 		if (is_set) {
94 			if (num++ == 0)
95 				printf("<");
96 			if (num != 1)
97 				printf(",");
98 			if (i < n_count)
99 				printf("%s", names[i]);
100 			else
101 				printf("%s_%d", btype, i);
102 		}
103 	}
104 	if (num > 0)
105 		printf(">");
106 }
107 
108 static void
109 nl_init_socket(struct snl_state *ss)
110 {
111 	if (snl_init(ss, NETLINK_ROUTE))
112 		return;
113 
114 	if (modfind("netlink") == -1 && errno == ENOENT) {
115 		/* Try to load */
116 		if (kldload("netlink") == -1)
117 			err(1, "netlink is not loaded and load attempt failed");
118 		if (snl_init(ss, NETLINK_ROUTE))
119 			return;
120 	}
121 
122 	err(1, "unable to open netlink socket");
123 }
124 
125 int
126 ifconfig_wrapper_nl(struct ifconfig_args *args, int iscreate,
127     const struct afswtch *uafp)
128 {
129 	struct snl_state ss = {};
130 	struct ifconfig_context ctx = {
131 		.args = args,
132 		.io_s = -1,
133 		.io_ss = &ss,
134 	};
135 
136 	nl_init_socket(&ss);
137 
138 	int error = ifconfig(&ctx, iscreate, uafp);
139 
140 	snl_free(&ss);
141 
142 	return (error);
143 }
144 
145 struct ifa {
146 	struct ifa		*next;
147 	uint32_t		idx;
148 	struct snl_parsed_addr	addr;
149 };
150 
151 struct iface {
152 	struct snl_parsed_link	link;
153 	struct ifa		*ifa;
154 	uint32_t		ifa_count;
155 	uint32_t		idx;
156 };
157 
158 struct ifmap {
159 	uint32_t		size;
160 	uint32_t		count;
161 	struct iface		**ifaces;
162 };
163 
164 /*
165  * Returns ifmap ifindex->snl_parsed_link.
166  * Memory is allocated using snl temporary buffers
167  */
168 static struct ifmap *
169 prepare_ifmap(struct snl_state *ss)
170 {
171 	struct snl_writer nw = {};
172 
173 	snl_init_writer(ss, &nw);
174 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
175 	hdr->nlmsg_flags |= NLM_F_DUMP;
176 	snl_reserve_msg_object(&nw, struct ifinfomsg);
177 
178 	if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
179 		return (NULL);
180 
181 	uint32_t nlmsg_seq = hdr->nlmsg_seq;
182 	struct ifmap *ifmap = snl_allocz(ss, sizeof(*ifmap));
183 	struct snl_errmsg_data e = {};
184 
185 	while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
186 		struct iface *iface = snl_allocz(ss, sizeof(*iface));
187 
188 		if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &iface->link))
189 			continue;
190 		if (iface->link.ifi_index >= ifmap->size) {
191 			size_t new_size = MAX(ifmap->size, 32);
192 
193 			while (new_size <= iface->link.ifi_index + 1)
194 				new_size *= 2;
195 
196 			struct iface **ifaces= snl_allocz(ss, new_size * sizeof(void *));
197 			memcpy(ifaces, ifmap->ifaces, ifmap->size * sizeof(void *));
198 			ifmap->ifaces = ifaces;
199 			ifmap->size = new_size;
200 		}
201 		ifmap->ifaces[iface->link.ifi_index] = iface;
202 		ifmap->count++;
203 		iface->idx = ifmap->count;
204 	}
205 	return (ifmap);
206 }
207 
208 uint32_t
209 if_nametoindex_nl(struct snl_state *ss, const char *ifname)
210 {
211 	struct snl_writer nw = {};
212 	struct snl_parsed_link_simple link = {};
213 
214 	snl_init_writer(ss, &nw);
215 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
216 	snl_reserve_msg_object(&nw, struct ifinfomsg);
217 	snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname);
218 
219 	if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
220 		return (0);
221 
222 	hdr = snl_read_reply(ss, hdr->nlmsg_seq);
223 	if (hdr->nlmsg_type != NL_RTM_NEWLINK)
224 		return (0);
225 	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, &link))
226 		return (0);
227 
228 	return (link.ifi_index);
229 }
230 
231 static void
232 prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap)
233 {
234 	struct snl_writer nw = {};
235 
236 	snl_init_writer(ss, &nw);
237 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETADDR);
238 	hdr->nlmsg_flags |= NLM_F_DUMP;
239 	snl_reserve_msg_object(&nw, struct ifaddrmsg);
240 
241 	if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
242 		return;
243 
244 	uint32_t nlmsg_seq = hdr->nlmsg_seq;
245 	struct snl_errmsg_data e = {};
246 	uint32_t count = 0;
247 
248 	while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
249 		struct ifa *ifa = snl_allocz(ss, sizeof(*ifa));
250 
251 		if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &ifa->addr))
252 			continue;
253 
254 		const uint32_t ifindex = ifa->addr.ifa_index;
255 		if (ifindex >= ifmap->size || ifmap->ifaces[ifindex] == NULL)
256 			continue;
257 		struct iface *iface = ifmap->ifaces[ifindex];
258 		ifa->next = iface->ifa;
259 		ifa->idx = ++count;
260 		iface->ifa = ifa;
261 		iface->ifa_count++;
262 	}
263 }
264 
265 static bool
266 match_iface(struct ifconfig_args *args, struct iface *iface)
267 {
268 	if_link_t *link = &iface->link;
269 
270 	if (args->ifname != NULL && strcmp(args->ifname, link->ifla_ifname))
271 		return (false);
272 
273 	if (!match_if_flags(args, link->ifi_flags))
274 		return (false);
275 
276 	if (!group_member(link->ifla_ifname, args->matchgroup, args->nogroup))
277 		return (false);
278 
279 	if (args->afp == NULL)
280 		return (true);
281 
282 	if (!strcmp(args->afp->af_name, "ether")) {
283 		if (link->ifla_address == NULL)
284 			return (false);
285 
286 		struct sockaddr_dl sdl = {
287 			.sdl_len = sizeof(struct sockaddr_dl),
288 			.sdl_family = AF_LINK,
289 			.sdl_type = link->ifi_type,
290 			.sdl_alen = NLA_DATA_LEN(link->ifla_address),
291 		};
292 		return (match_ether(&sdl));
293 	}
294 
295 	for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
296 		if (args->afp->af_af == ifa->addr.ifa_family)
297 			return (true);
298 	}
299 
300 	return (false);
301 }
302 
303 /* Sort according to the kernel-provided order */
304 static int
305 cmp_iface(const void *_a, const void *_b)
306 {
307 	const struct iface *a = *((const void * const *)_a);
308 	const struct iface *b = *((const void * const *)_b);
309 
310 	return ((a->idx > b->idx) * 2 - 1);
311 }
312 
313 static int
314 cmp_ifaddr(const void *_a, const void *_b)
315 {
316 	const struct ifa *a = *((const void * const *)_a);
317 	const struct ifa *b = *((const void * const *)_b);
318 
319 	if (a->addr.ifa_family != b->addr.ifa_family)
320 		return ((a->addr.ifa_family > b->addr.ifa_family) * 2 - 1);
321 	return ((a->idx > b->idx) * 2 - 1);
322 }
323 
324 static void
325 sort_iface_ifaddrs(struct snl_state *ss, struct iface *iface)
326 {
327 	if (iface->ifa_count == 0)
328 		return;
329 
330 	struct ifa **sorted_ifaddrs = snl_allocz(ss, iface->ifa_count * sizeof(void *));
331 	struct ifa *ifa = iface->ifa;
332 
333 	for (uint32_t i = 0; i < iface->ifa_count; i++) {
334 		struct ifa *ifa_next = ifa->next;
335 
336 		sorted_ifaddrs[i] = ifa;
337 		ifa->next = NULL;
338 		ifa = ifa_next;
339 	}
340 	qsort(sorted_ifaddrs, iface->ifa_count, sizeof(void *), cmp_ifaddr);
341 	ifa = sorted_ifaddrs[0];
342 	iface->ifa = ifa;
343 	for (uint32_t i = 1; i < iface->ifa_count; i++) {
344 		ifa->next = sorted_ifaddrs[i];
345 		ifa = sorted_ifaddrs[i];
346 	}
347 }
348 
349 static void
350 status_nl(if_ctx *ctx, struct iface *iface)
351 {
352 	if_link_t *link = &iface->link;
353 	struct ifconfig_args *args = ctx->args;
354 
355 	printf("%s: ", link->ifla_ifname);
356 
357 	printf("flags=%x", link->ifi_flags);
358 	print_bits("IFF", &link->ifi_flags, 1, IFFBITS, nitems(IFFBITS));
359 
360 	print_metric(ctx->io_s);
361 	printf(" mtu %d\n", link->ifla_mtu);
362 
363 	if (link->ifla_ifalias != NULL)
364 		printf("\tdescription: %s\n", link->ifla_ifalias);
365 
366 	/* TODO: convert to netlink */
367 	strlcpy(ifr.ifr_name, link->ifla_ifname, sizeof(ifr.ifr_name));
368 	print_ifcap(args, ctx->io_s);
369 	tunnel_status(ctx->io_s);
370 
371 	if (args->allfamilies | (args->afp != NULL && args->afp->af_af == AF_LINK)) {
372 		/* Start with link-level */
373 		const struct afswtch *p = af_getbyfamily(AF_LINK);
374 		if (p != NULL && link->ifla_address != NULL)
375 			p->af_status(ctx, link, NULL);
376 	}
377 
378 	sort_iface_ifaddrs(ctx->io_ss, iface);
379 
380 	for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
381 		if (args->allfamilies) {
382 			const struct afswtch *p = af_getbyfamily(ifa->addr.ifa_family);
383 
384 			if (p != NULL)
385 				p->af_status(ctx, link, &ifa->addr);
386 		} else if (args->afp->af_af == ifa->addr.ifa_family) {
387 			const struct afswtch *p = args->afp;
388 
389 			p->af_status(ctx, link, &ifa->addr);
390 		}
391 	}
392 
393 	/* TODO: convert to netlink */
394 	if (args->allfamilies)
395 		af_other_status(ctx);
396 	else if (args->afp->af_other_status != NULL)
397 		args->afp->af_other_status(ctx);
398 
399 	print_ifstatus(ctx->io_s);
400 	if (args->verbose > 0)
401 		sfp_status(ctx);
402 }
403 
404 static int
405 get_local_socket(void)
406 {
407 	int s = socket(AF_LOCAL, SOCK_DGRAM, 0);
408 
409 	if (s < 0)
410 		err(1, "socket(family %u,SOCK_DGRAM)", AF_LOCAL);
411 	return (s);
412 }
413 
414 static void
415 set_global_ifname(if_link_t *link)
416 {
417 	size_t iflen = strlcpy(name, link->ifla_ifname, sizeof(name));
418 
419 	if (iflen >= sizeof(name))
420 		errx(1, "%s: cloning name too long", link->ifla_ifname);
421 	strlcpy(ifr.ifr_name, link->ifla_ifname, sizeof(ifr.ifr_name));
422 }
423 
424 void
425 list_interfaces_nl(struct ifconfig_args *args)
426 {
427 	struct snl_state ss = {};
428 	struct ifconfig_context _ctx = {
429 		.args = args,
430 		.io_s = get_local_socket(),
431 		.io_ss = &ss,
432 	};
433 	struct ifconfig_context *ctx = &_ctx;
434 
435 	nl_init_socket(&ss);
436 
437 	struct ifmap *ifmap = prepare_ifmap(&ss);
438 	struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *));
439 	for (uint32_t i = 0, num = 0; i < ifmap->size; i++) {
440 		if (ifmap->ifaces[i] != NULL) {
441 			sorted_ifaces[num++] = ifmap->ifaces[i];
442 			if (num == ifmap->count)
443 				break;
444 		}
445 	}
446 	qsort(sorted_ifaces, ifmap->count, sizeof(void *), cmp_iface);
447 	prepare_ifaddrs(&ss, ifmap);
448 
449 	for (uint32_t i = 0, num = 0; i < ifmap->count; i++) {
450 		struct iface *iface = sorted_ifaces[i];
451 
452 		if (!match_iface(args, iface))
453 			continue;
454 
455 		set_global_ifname(&iface->link);
456 
457 		if (args->namesonly) {
458 			if (num++ != 0)
459 				printf(" ");
460 			fputs(iface->link.ifla_ifname, stdout);
461 		} else if (args->argc == 0)
462 			status_nl(ctx, iface);
463 		else
464 			ifconfig(ctx, 0, args->afp);
465 	}
466 	if (args->namesonly)
467 		printf("\n");
468 
469 	close(ctx->io_s);
470 	snl_free(&ss);
471 }
472 
473