xref: /freebsd/sbin/ifconfig/ifconfig_netlink.c (revision c27f7d6b9cf6d4ab01cb3d0972726c14e0aca146)
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 #define _WANT_IFCAP_BIT_NAMES
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdbool.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <netdb.h>
37 
38 #include <sys/bitcount.h>
39 #include <sys/param.h>
40 #include <sys/linker.h>
41 #include <sys/module.h>
42 #include <sys/socket.h>
43 #include <sys/sysctl.h>
44 #include <sys/time.h>
45 #include <sys/types.h>
46 
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 
50 #include <net/ethernet.h>
51 #include <net/if.h>
52 #include <net/if_dl.h>
53 #include <net/if_strings.h>
54 #include <net/if_types.h>
55 #include "ifconfig.h"
56 #include "ifconfig_netlink.h"
57 
58 static const char	*IFFBITS[] = {
59 	"UP",			/* 00:0x1 IFF_UP*/
60 	"BROADCAST",		/* 01:0x2 IFF_BROADCAST*/
61 	"DEBUG",		/* 02:0x4 IFF_DEBUG*/
62 	"LOOPBACK",		/* 03:0x8 IFF_LOOPBACK*/
63 	"POINTOPOINT",		/* 04:0x10 IFF_POINTOPOINT*/
64 	"NEEDSEPOCH",		/* 05:0x20 IFF_NEEDSEPOCH*/
65 	"RUNNING",		/* 06:0x40 IFF_DRV_RUNNING*/
66 	"NOARP",		/* 07:0x80 IFF_NOARP*/
67 	"PROMISC",		/* 08:0x100 IFF_PROMISC*/
68 	"ALLMULTI",		/* 09:0x200 IFF_ALLMULTI*/
69 	"DRV_OACTIVE",		/* 10:0x400 IFF_DRV_OACTIVE*/
70 	"SIMPLEX",		/* 11:0x800 IFF_SIMPLEX*/
71 	"LINK0",		/* 12:0x1000 IFF_LINK0*/
72 	"LINK1",		/* 13:0x2000 IFF_LINK1*/
73 	"LINK2",		/* 14:0x4000 IFF_LINK2*/
74 	"MULTICAST",		/* 15:0x8000 IFF_MULTICAST*/
75 	"CANTCONFIG",		/* 16:0x10000 IFF_CANTCONFIG*/
76 	"PPROMISC",		/* 17:0x20000 IFF_PPROMISC*/
77 	"MONITOR",		/* 18:0x40000 IFF_MONITOR*/
78 	"STATICARP",		/* 19:0x80000 IFF_STATICARP*/
79 	"STICKYARP",		/* 20:0x100000 IFF_STICKYARP*/
80 	"DYING",		/* 21:0x200000 IFF_DYING*/
81 	"RENAMING",		/* 22:0x400000 IFF_RENAMING*/
82 	"PALLMULTI",		/* 23:0x800000 IFF_PALLMULTI*/
83 	"LOWER_UP",		/* 24:0x1000000 IFF_NETLINK_1*/
84 };
85 
86 static void
87 nl_init_socket(struct snl_state *ss)
88 {
89 	if (snl_init(ss, NETLINK_ROUTE))
90 		return;
91 
92 	if (modfind("netlink") == -1 && errno == ENOENT) {
93 		/* Try to load */
94 		if (kldload("netlink") == -1)
95 			err(1, "netlink is not loaded and load attempt failed");
96 		if (snl_init(ss, NETLINK_ROUTE))
97 			return;
98 	}
99 
100 	err(1, "unable to open netlink socket");
101 }
102 
103 int
104 ifconfig_nl(if_ctx *ctx, int iscreate,
105     const struct afswtch *uafp)
106 {
107 	struct snl_state ss = {};
108 
109 	nl_init_socket(&ss);
110 	ctx->io_ss = &ss;
111 
112 	int error = ifconfig_ioctl(ctx, iscreate, uafp);
113 
114 	snl_free(&ss);
115 	ctx->io_ss = NULL;
116 
117 	return (error);
118 }
119 
120 struct ifa {
121 	struct ifa		*next;
122 	uint32_t		idx;
123 	struct snl_parsed_addr	addr;
124 };
125 
126 struct iface {
127 	struct snl_parsed_link	link;
128 	struct ifa		*ifa;
129 	uint32_t		ifa_count;
130 	uint32_t		idx;
131 };
132 
133 struct ifmap {
134 	uint32_t		size;
135 	uint32_t		count;
136 	struct iface		**ifaces;
137 };
138 
139 /*
140  * Returns ifmap ifindex->snl_parsed_link.
141  * Memory is allocated using snl temporary buffers
142  */
143 static struct ifmap *
144 prepare_ifmap(struct snl_state *ss)
145 {
146 	struct snl_writer nw = {};
147 
148 	snl_init_writer(ss, &nw);
149 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
150 	hdr->nlmsg_flags |= NLM_F_DUMP;
151 	snl_reserve_msg_object(&nw, struct ifinfomsg);
152 
153 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
154 		return (NULL);
155 
156 	uint32_t nlmsg_seq = hdr->nlmsg_seq;
157 	struct ifmap *ifmap = snl_allocz(ss, sizeof(*ifmap));
158 	struct snl_errmsg_data e = {};
159 
160 	while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
161 		struct iface *iface = snl_allocz(ss, sizeof(*iface));
162 
163 		if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &iface->link))
164 			continue;
165 		if (iface->link.ifi_index >= ifmap->size) {
166 			size_t new_size = MAX(ifmap->size, 32);
167 
168 			while (new_size <= iface->link.ifi_index + 1)
169 				new_size *= 2;
170 
171 			struct iface **ifaces= snl_allocz(ss, new_size * sizeof(void *));
172 			memcpy(ifaces, ifmap->ifaces, ifmap->size * sizeof(void *));
173 			ifmap->ifaces = ifaces;
174 			ifmap->size = new_size;
175 		}
176 		ifmap->ifaces[iface->link.ifi_index] = iface;
177 		ifmap->count++;
178 		iface->idx = ifmap->count;
179 	}
180 	return (ifmap);
181 }
182 
183 uint32_t
184 if_nametoindex_nl(struct snl_state *ss, const char *ifname)
185 {
186 	struct snl_writer nw = {};
187 	struct snl_parsed_link_simple link = {};
188 
189 	snl_init_writer(ss, &nw);
190 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
191 	snl_reserve_msg_object(&nw, struct ifinfomsg);
192 	snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname);
193 
194 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
195 		return (0);
196 
197 	hdr = snl_read_reply(ss, hdr->nlmsg_seq);
198 	if (hdr->nlmsg_type != NL_RTM_NEWLINK)
199 		return (0);
200 	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, &link))
201 		return (0);
202 
203 	return (link.ifi_index);
204 }
205 
206 ifType
207 convert_iftype(ifType iftype)
208 {
209 	switch (iftype) {
210 	case IFT_IEEE8023ADLAG:
211 		return (IFT_ETHER);
212 	case IFT_INFINIBANDLAG:
213 		return (IFT_INFINIBAND);
214 	default:
215 		return (iftype);
216 	}
217 }
218 
219 static void
220 prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap)
221 {
222 	struct snl_writer nw = {};
223 
224 	snl_init_writer(ss, &nw);
225 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETADDR);
226 	hdr->nlmsg_flags |= NLM_F_DUMP;
227 	snl_reserve_msg_object(&nw, struct ifaddrmsg);
228 
229 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
230 		return;
231 
232 	uint32_t nlmsg_seq = hdr->nlmsg_seq;
233 	struct snl_errmsg_data e = {};
234 	uint32_t count = 0;
235 
236 	while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
237 		struct ifa *ifa = snl_allocz(ss, sizeof(*ifa));
238 
239 		if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &ifa->addr))
240 			continue;
241 
242 		const uint32_t ifindex = ifa->addr.ifa_index;
243 		if (ifindex >= ifmap->size || ifmap->ifaces[ifindex] == NULL)
244 			continue;
245 		struct iface *iface = ifmap->ifaces[ifindex];
246 		ifa->next = iface->ifa;
247 		ifa->idx = ++count;
248 		iface->ifa = ifa;
249 		iface->ifa_count++;
250 	}
251 }
252 
253 static bool
254 match_iface(struct ifconfig_args *args, struct iface *iface)
255 {
256 	if_link_t *link = &iface->link;
257 
258 	if (args->ifname != NULL && strcmp(args->ifname, link->ifla_ifname))
259 		return (false);
260 
261 	if (!match_if_flags(args, link->ifi_flags))
262 		return (false);
263 
264 	if (!group_member(link->ifla_ifname, args->matchgroup, args->nogroup))
265 		return (false);
266 
267 	if (args->afp == NULL)
268 		return (true);
269 
270 	if (!strcmp(args->afp->af_name, "ether")) {
271 		if (link->ifla_address == NULL)
272 			return (false);
273 
274 		struct sockaddr_dl sdl = {
275 			.sdl_len = sizeof(struct sockaddr_dl),
276 			.sdl_family = AF_LINK,
277 			.sdl_type = convert_iftype(link->ifi_type),
278 			.sdl_alen = NLA_DATA_LEN(link->ifla_address),
279 		};
280 		return (match_ether(&sdl));
281 	} else if (args->afp->af_af == AF_LINK)
282 		/*
283 		 * The rtnetlink(4) RTM_GETADDR does not list link level
284 		 * addresses, so latter cycle won't match anything.  Short
285 		 * circuit on RTM_GETLINK has provided us an address.
286 		 */
287 		return (link->ifla_address != NULL);
288 
289 	for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
290 		if (args->afp->af_af == ifa->addr.ifa_family)
291 			return (true);
292 	}
293 
294 	return (false);
295 }
296 
297 /* Sort according to the kernel-provided order */
298 static int
299 cmp_iface(const void *_a, const void *_b)
300 {
301 	const struct iface *a = *((const void * const *)_a);
302 	const struct iface *b = *((const void * const *)_b);
303 
304 	return ((a->idx > b->idx) * 2 - 1);
305 }
306 
307 static int
308 cmp_ifaddr(const void *_a, const void *_b)
309 {
310 	const struct ifa *a = *((const void * const *)_a);
311 	const struct ifa *b = *((const void * const *)_b);
312 
313 	if (a->addr.ifa_family != b->addr.ifa_family)
314 		return ((a->addr.ifa_family > b->addr.ifa_family) * 2 - 1);
315 	return ((a->idx > b->idx) * 2 - 1);
316 }
317 
318 static void
319 sort_iface_ifaddrs(struct snl_state *ss, struct iface *iface)
320 {
321 	if (iface->ifa_count == 0)
322 		return;
323 
324 	struct ifa **sorted_ifaddrs = snl_allocz(ss, iface->ifa_count * sizeof(void *));
325 	struct ifa *ifa = iface->ifa;
326 
327 	for (uint32_t i = 0; i < iface->ifa_count; i++) {
328 		struct ifa *ifa_next = ifa->next;
329 
330 		sorted_ifaddrs[i] = ifa;
331 		ifa->next = NULL;
332 		ifa = ifa_next;
333 	}
334 	qsort(sorted_ifaddrs, iface->ifa_count, sizeof(void *), cmp_ifaddr);
335 	ifa = sorted_ifaddrs[0];
336 	iface->ifa = ifa;
337 	for (uint32_t i = 1; i < iface->ifa_count; i++) {
338 		ifa->next = sorted_ifaddrs[i];
339 		ifa = sorted_ifaddrs[i];
340 	}
341 }
342 
343 static void
344 print_ifcaps(if_ctx *ctx, if_link_t *link)
345 {
346 	uint32_t sz_u32 = roundup2(link->iflaf_caps.nla_bitset_size, 32) / 32;
347 
348 	if (sz_u32 > 0) {
349 		uint32_t *caps = link->iflaf_caps.nla_bitset_value;
350 
351 		printf("\toptions=%x", caps[0]);
352 		print_bits("IFCAPS", caps, sz_u32, ifcap_bit_names, nitems(ifcap_bit_names));
353 		putchar('\n');
354 	}
355 
356 	if (ctx->args->supmedia && sz_u32 > 0) {
357 		uint32_t *caps = link->iflaf_caps.nla_bitset_mask;
358 
359 		printf("\tcapabilities=%x", caps[0]);
360 		print_bits("IFCAPS", caps, sz_u32, ifcap_bit_names, nitems(ifcap_bit_names));
361 		putchar('\n');
362 	}
363 }
364 
365 static void
366 status_nl(if_ctx *ctx, struct iface *iface)
367 {
368 	if_link_t *link = &iface->link;
369 	struct ifconfig_args *args = ctx->args;
370 	char *drivername = NULL;
371 
372 	printf("%s: ", link->ifla_ifname);
373 
374 	printf("flags=%x", link->ifi_flags);
375 	print_bits("IFF", &link->ifi_flags, 1, IFFBITS, nitems(IFFBITS));
376 
377 	print_metric(ctx);
378 	printf(" mtu %d\n", link->ifla_mtu);
379 
380 	if (link->ifla_ifalias != NULL)
381 		printf("\tdescription: %s\n", link->ifla_ifalias);
382 
383 	print_ifcaps(ctx, link);
384 	tunnel_status(ctx);
385 
386 	if (args->allfamilies | (args->afp != NULL && args->afp->af_af == AF_LINK)) {
387 		/* Start with link-level */
388 		const struct afswtch *p = af_getbyfamily(AF_LINK);
389 		if (p != NULL && link->ifla_address != NULL)
390 			p->af_status(ctx, link, NULL);
391 	}
392 
393 	sort_iface_ifaddrs(ctx->io_ss, iface);
394 
395 	for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
396 		if (args->allfamilies) {
397 			const struct afswtch *p = af_getbyfamily(ifa->addr.ifa_family);
398 
399 			if (p != NULL)
400 				p->af_status(ctx, link, &ifa->addr);
401 		} else if (args->afp->af_af == ifa->addr.ifa_family) {
402 			const struct afswtch *p = args->afp;
403 
404 			p->af_status(ctx, link, &ifa->addr);
405 		}
406 	}
407 
408 	/* TODO: convert to netlink */
409 	if (args->allfamilies)
410 		af_other_status(ctx);
411 	else if (args->afp->af_other_status != NULL)
412 		args->afp->af_other_status(ctx);
413 
414 	print_ifstatus(ctx);
415 	if (args->drivername || args->verbose) {
416 		if (ifconfig_get_orig_name(lifh, link->ifla_ifname,
417 		    &drivername) != 0) {
418 			if (ifconfig_err_errtype(lifh) == OTHER)
419 				fprintf(stderr, "get original name: %s\n",
420 				    strerror(ifconfig_err_errno(lifh)));
421 			else
422 				fprintf(stderr,
423 				    "get original name: error type %d\n",
424 				    ifconfig_err_errtype(lifh));
425 			exit_code = 1;
426 		}
427 		if (drivername != NULL)
428 			printf("\tdrivername: %s\n", drivername);
429 		free(drivername);
430 	}
431 	if (args->verbose > 0)
432 		sfp_status(ctx);
433 }
434 
435 static int
436 get_local_socket(void)
437 {
438 	int s = socket(AF_LOCAL, SOCK_DGRAM, 0);
439 
440 	if (s < 0)
441 		err(1, "socket(family %u,SOCK_DGRAM)", AF_LOCAL);
442 	return (s);
443 }
444 
445 void
446 list_interfaces_nl(struct ifconfig_args *args)
447 {
448 	struct snl_state ss = {};
449 	struct ifconfig_context _ctx = {
450 		.args = args,
451 		.io_s = get_local_socket(),
452 		.io_ss = &ss,
453 	};
454 	struct ifconfig_context *ctx = &_ctx;
455 
456 	nl_init_socket(&ss);
457 
458 	struct ifmap *ifmap = prepare_ifmap(&ss);
459 	struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *));
460 	for (uint32_t i = 0, num = 0; i < ifmap->size; i++) {
461 		if (ifmap->ifaces[i] != NULL) {
462 			sorted_ifaces[num++] = ifmap->ifaces[i];
463 			if (num == ifmap->count)
464 				break;
465 		}
466 	}
467 	qsort(sorted_ifaces, ifmap->count, sizeof(void *), cmp_iface);
468 	prepare_ifaddrs(&ss, ifmap);
469 
470 	for (uint32_t i = 0, num = 0; i < ifmap->count; i++) {
471 		struct iface *iface = sorted_ifaces[i];
472 
473 		if (!match_iface(args, iface))
474 			continue;
475 
476 		ctx->ifname = iface->link.ifla_ifname;
477 
478 		if (args->namesonly) {
479 			if (num++ != 0)
480 				printf(" ");
481 			fputs(iface->link.ifla_ifname, stdout);
482 		} else if (args->argc == 0)
483 			status_nl(ctx, iface);
484 		else
485 			ifconfig_ioctl(ctx, 0, args->afp);
486 	}
487 	if (args->namesonly)
488 		printf("\n");
489 
490 	close(ctx->io_s);
491 	snl_free(&ss);
492 }
493 
494