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