xref: /freebsd/sbin/ifconfig/ifconfig_netlink.c (revision 6e3a9d7f)
14c91a5dfSAlexander V. Chernikov /*-
24c91a5dfSAlexander V. Chernikov  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
34c91a5dfSAlexander V. Chernikov  *
44c91a5dfSAlexander V. Chernikov  * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
54c91a5dfSAlexander V. Chernikov  *
64c91a5dfSAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
74c91a5dfSAlexander V. Chernikov  * modification, are permitted provided that the following conditions
84c91a5dfSAlexander V. Chernikov  * are met:
94c91a5dfSAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
104c91a5dfSAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
114c91a5dfSAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
124c91a5dfSAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
134c91a5dfSAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
144c91a5dfSAlexander V. Chernikov  *
154c91a5dfSAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
164c91a5dfSAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
174c91a5dfSAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
184c91a5dfSAlexander V. Chernikov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
194c91a5dfSAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
204c91a5dfSAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
214c91a5dfSAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
224c91a5dfSAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
234c91a5dfSAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
244c91a5dfSAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
254c91a5dfSAlexander V. Chernikov  * SUCH DAMAGE.
264c91a5dfSAlexander V. Chernikov  */
274c91a5dfSAlexander V. Chernikov 
284c91a5dfSAlexander V. Chernikov #include <stdio.h>
294c91a5dfSAlexander V. Chernikov #include <stdlib.h>
304c91a5dfSAlexander V. Chernikov #include <string.h>
314c91a5dfSAlexander V. Chernikov #include <stdbool.h>
324c91a5dfSAlexander V. Chernikov #include <err.h>
334c91a5dfSAlexander V. Chernikov #include <errno.h>
344c91a5dfSAlexander V. Chernikov #include <netdb.h>
354c91a5dfSAlexander V. Chernikov 
364c91a5dfSAlexander V. Chernikov #include <sys/bitcount.h>
374c91a5dfSAlexander V. Chernikov #include <sys/param.h>
384c91a5dfSAlexander V. Chernikov #include <sys/linker.h>
394c91a5dfSAlexander V. Chernikov #include <sys/module.h>
404c91a5dfSAlexander V. Chernikov #include <sys/socket.h>
414c91a5dfSAlexander V. Chernikov #include <sys/sysctl.h>
424c91a5dfSAlexander V. Chernikov #include <sys/time.h>
434c91a5dfSAlexander V. Chernikov #include <sys/types.h>
444c91a5dfSAlexander V. Chernikov 
454c91a5dfSAlexander V. Chernikov #include <netinet/in.h>
464c91a5dfSAlexander V. Chernikov #include <arpa/inet.h>
474c91a5dfSAlexander V. Chernikov 
484c91a5dfSAlexander V. Chernikov #include <net/ethernet.h>
494c91a5dfSAlexander V. Chernikov #include <net/if.h>
504c91a5dfSAlexander V. Chernikov #include <net/if_dl.h>
514c91a5dfSAlexander V. Chernikov #include <net/if_types.h>
524c91a5dfSAlexander V. Chernikov #include "ifconfig.h"
534c91a5dfSAlexander V. Chernikov #include "ifconfig_netlink.h"
544c91a5dfSAlexander V. Chernikov 
554c91a5dfSAlexander V. Chernikov static const char	*IFFBITS[] = {
564c91a5dfSAlexander V. Chernikov 	"UP",			/* 00:0x1 IFF_UP*/
574c91a5dfSAlexander V. Chernikov 	"BROADCAST",		/* 01:0x2 IFF_BROADCAST*/
584c91a5dfSAlexander V. Chernikov 	"DEBUG",		/* 02:0x4 IFF_DEBUG*/
594c91a5dfSAlexander V. Chernikov 	"LOOPBACK",		/* 03:0x8 IFF_LOOPBACK*/
604c91a5dfSAlexander V. Chernikov 	"POINTOPOINT",		/* 04:0x10 IFF_POINTOPOINT*/
614c91a5dfSAlexander V. Chernikov 	"NEEDSEPOCH",		/* 05:0x20 IFF_NEEDSEPOCH*/
624c91a5dfSAlexander V. Chernikov 	"RUNNING",		/* 06:0x40 IFF_DRV_RUNNING*/
634c91a5dfSAlexander V. Chernikov 	"NOARP",		/* 07:0x80 IFF_NOARP*/
644c91a5dfSAlexander V. Chernikov 	"PROMISC",		/* 08:0x100 IFF_PROMISC*/
654c91a5dfSAlexander V. Chernikov 	"ALLMULTI",		/* 09:0x200 IFF_ALLMULTI*/
664c91a5dfSAlexander V. Chernikov 	"DRV_OACTIVE",		/* 10:0x400 IFF_DRV_OACTIVE*/
674c91a5dfSAlexander V. Chernikov 	"SIMPLEX",		/* 11:0x800 IFF_SIMPLEX*/
684c91a5dfSAlexander V. Chernikov 	"LINK0",		/* 12:0x1000 IFF_LINK0*/
694c91a5dfSAlexander V. Chernikov 	"LINK1",		/* 13:0x2000 IFF_LINK1*/
704c91a5dfSAlexander V. Chernikov 	"LINK2",		/* 14:0x4000 IFF_LINK2*/
714c91a5dfSAlexander V. Chernikov 	"MULTICAST",		/* 15:0x8000 IFF_MULTICAST*/
724c91a5dfSAlexander V. Chernikov 	"CANTCONFIG",		/* 16:0x10000 IFF_CANTCONFIG*/
734c91a5dfSAlexander V. Chernikov 	"PPROMISC",		/* 17:0x20000 IFF_PPROMISC*/
744c91a5dfSAlexander V. Chernikov 	"MONITOR",		/* 18:0x40000 IFF_MONITOR*/
754c91a5dfSAlexander V. Chernikov 	"STATICARP",		/* 19:0x80000 IFF_STATICARP*/
764c91a5dfSAlexander V. Chernikov 	"STICKYARP",		/* 20:0x100000 IFF_STICKYARP*/
774c91a5dfSAlexander V. Chernikov 	"DYING",		/* 21:0x200000 IFF_DYING*/
784c91a5dfSAlexander V. Chernikov 	"RENAMING",		/* 22:0x400000 IFF_RENAMING*/
794c91a5dfSAlexander V. Chernikov 	"NOGROUP",		/* 23:0x800000 IFF_NOGROUP*/
804c91a5dfSAlexander V. Chernikov 	"LOWER_UP",		/* 24:0x1000000 IFF_NETLINK_1*/
814c91a5dfSAlexander V. Chernikov };
824c91a5dfSAlexander V. Chernikov 
834c91a5dfSAlexander V. Chernikov static void
844c91a5dfSAlexander V. Chernikov print_bits(const char *btype, uint32_t *v, const int v_count,
854c91a5dfSAlexander V. Chernikov     const char **names, const int n_count)
864c91a5dfSAlexander V. Chernikov {
874c91a5dfSAlexander V. Chernikov 	int num = 0;
884c91a5dfSAlexander V. Chernikov 
894c91a5dfSAlexander V. Chernikov 	for (int i = 0; i < v_count * 32; i++) {
904c91a5dfSAlexander V. Chernikov 		bool is_set = v[i / 32] & (1 << (i % 32));
914c91a5dfSAlexander V. Chernikov 		if (i == 31)
924c91a5dfSAlexander V. Chernikov 			v++;
934c91a5dfSAlexander V. Chernikov 		if (is_set) {
944c91a5dfSAlexander V. Chernikov 			if (num++ == 0)
954c91a5dfSAlexander V. Chernikov 				printf("<");
964c91a5dfSAlexander V. Chernikov 			if (num != 1)
974c91a5dfSAlexander V. Chernikov 				printf(",");
984c91a5dfSAlexander V. Chernikov 			if (i < n_count)
994c91a5dfSAlexander V. Chernikov 				printf("%s", names[i]);
1004c91a5dfSAlexander V. Chernikov 			else
1014c91a5dfSAlexander V. Chernikov 				printf("%s_%d", btype, i);
1024c91a5dfSAlexander V. Chernikov 		}
1034c91a5dfSAlexander V. Chernikov 	}
1044c91a5dfSAlexander V. Chernikov 	if (num > 0)
1054c91a5dfSAlexander V. Chernikov 		printf(">");
1064c91a5dfSAlexander V. Chernikov }
1074c91a5dfSAlexander V. Chernikov 
1084c91a5dfSAlexander V. Chernikov static void
1094c91a5dfSAlexander V. Chernikov nl_init_socket(struct snl_state *ss)
1104c91a5dfSAlexander V. Chernikov {
1114c91a5dfSAlexander V. Chernikov 	if (snl_init(ss, NETLINK_ROUTE))
1124c91a5dfSAlexander V. Chernikov 		return;
1134c91a5dfSAlexander V. Chernikov 
1144c91a5dfSAlexander V. Chernikov 	if (modfind("netlink") == -1 && errno == ENOENT) {
1154c91a5dfSAlexander V. Chernikov 		/* Try to load */
1164c91a5dfSAlexander V. Chernikov 		if (kldload("netlink") == -1)
1174c91a5dfSAlexander V. Chernikov 			err(1, "netlink is not loaded and load attempt failed");
1184c91a5dfSAlexander V. Chernikov 		if (snl_init(ss, NETLINK_ROUTE))
1194c91a5dfSAlexander V. Chernikov 			return;
1204c91a5dfSAlexander V. Chernikov 	}
1214c91a5dfSAlexander V. Chernikov 
1224c91a5dfSAlexander V. Chernikov 	err(1, "unable to open netlink socket");
1234c91a5dfSAlexander V. Chernikov }
1244c91a5dfSAlexander V. Chernikov 
1254c91a5dfSAlexander V. Chernikov struct ifa {
1264c91a5dfSAlexander V. Chernikov 	struct ifa		*next;
1274c91a5dfSAlexander V. Chernikov 	uint32_t		count;
1284c91a5dfSAlexander V. Chernikov 	uint32_t		idx;
1294c91a5dfSAlexander V. Chernikov 	struct snl_parsed_addr	addr;
1304c91a5dfSAlexander V. Chernikov };
1314c91a5dfSAlexander V. Chernikov 
1324c91a5dfSAlexander V. Chernikov struct iface {
1334c91a5dfSAlexander V. Chernikov 	struct snl_parsed_link	link;
1344c91a5dfSAlexander V. Chernikov 	struct ifa		*ifa;
1354c91a5dfSAlexander V. Chernikov 	uint32_t		ifa_count;
1364c91a5dfSAlexander V. Chernikov 	uint32_t		idx;
1374c91a5dfSAlexander V. Chernikov };
1384c91a5dfSAlexander V. Chernikov 
1394c91a5dfSAlexander V. Chernikov struct ifmap {
1404c91a5dfSAlexander V. Chernikov 	uint32_t		size;
1414c91a5dfSAlexander V. Chernikov 	uint32_t		count;
1424c91a5dfSAlexander V. Chernikov 	struct iface		**ifaces;
1434c91a5dfSAlexander V. Chernikov };
1444c91a5dfSAlexander V. Chernikov 
1454c91a5dfSAlexander V. Chernikov /*
1464c91a5dfSAlexander V. Chernikov  * Returns ifmap ifindex->snl_parsed_link.
1474c91a5dfSAlexander V. Chernikov  * Memory is allocated using snl temporary buffers
1484c91a5dfSAlexander V. Chernikov  */
1494c91a5dfSAlexander V. Chernikov static struct ifmap *
1504c91a5dfSAlexander V. Chernikov prepare_ifmap(struct snl_state *ss)
1514c91a5dfSAlexander V. Chernikov {
1524c91a5dfSAlexander V. Chernikov 	struct snl_writer nw = {};
1534c91a5dfSAlexander V. Chernikov 
1544c91a5dfSAlexander V. Chernikov 	snl_init_writer(ss, &nw);
1554c91a5dfSAlexander V. Chernikov 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
1564c91a5dfSAlexander V. Chernikov 	hdr->nlmsg_flags |= NLM_F_DUMP;
1574c91a5dfSAlexander V. Chernikov 	snl_reserve_msg_object(&nw, struct ifinfomsg);
1584c91a5dfSAlexander V. Chernikov 
1594c91a5dfSAlexander V. Chernikov 	if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
1604c91a5dfSAlexander V. Chernikov 		return (NULL);
1614c91a5dfSAlexander V. Chernikov 
1624c91a5dfSAlexander V. Chernikov 	uint32_t nlmsg_seq = hdr->nlmsg_seq;
1634c91a5dfSAlexander V. Chernikov 	struct ifmap *ifmap = snl_allocz(ss, sizeof(*ifmap));
1644c91a5dfSAlexander V. Chernikov 	struct snl_errmsg_data e = {};
1654c91a5dfSAlexander V. Chernikov 
1664c91a5dfSAlexander V. Chernikov 	while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
1674c91a5dfSAlexander V. Chernikov 		struct iface *iface = snl_allocz(ss, sizeof(*iface));
1684c91a5dfSAlexander V. Chernikov 
1694c91a5dfSAlexander V. Chernikov 		if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &iface->link))
1704c91a5dfSAlexander V. Chernikov 			continue;
1714c91a5dfSAlexander V. Chernikov 		if (iface->link.ifi_index >= ifmap->size) {
1724c91a5dfSAlexander V. Chernikov 			size_t new_size = MAX(ifmap->size, 32);
1734c91a5dfSAlexander V. Chernikov 
1744c91a5dfSAlexander V. Chernikov 			while (new_size <= iface->link.ifi_index + 1)
1754c91a5dfSAlexander V. Chernikov 				new_size *= 2;
1764c91a5dfSAlexander V. Chernikov 
1774c91a5dfSAlexander V. Chernikov 			struct iface **ifaces= snl_allocz(ss, new_size * sizeof(void *));
1784c91a5dfSAlexander V. Chernikov 			memcpy(ifaces, ifmap->ifaces, ifmap->size * sizeof(void *));
1794c91a5dfSAlexander V. Chernikov 			ifmap->ifaces = ifaces;
1804c91a5dfSAlexander V. Chernikov 			ifmap->size = new_size;
1814c91a5dfSAlexander V. Chernikov 		}
1824c91a5dfSAlexander V. Chernikov 		ifmap->ifaces[iface->link.ifi_index] = iface;
1834c91a5dfSAlexander V. Chernikov 		ifmap->count++;
1844c91a5dfSAlexander V. Chernikov 		iface->idx = ifmap->count;
1854c91a5dfSAlexander V. Chernikov 	}
1864c91a5dfSAlexander V. Chernikov 	return (ifmap);
1874c91a5dfSAlexander V. Chernikov }
1884c91a5dfSAlexander V. Chernikov 
1894c91a5dfSAlexander V. Chernikov static void
1904c91a5dfSAlexander V. Chernikov prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap)
1914c91a5dfSAlexander V. Chernikov {
1924c91a5dfSAlexander V. Chernikov 	struct snl_writer nw = {};
1934c91a5dfSAlexander V. Chernikov 
1944c91a5dfSAlexander V. Chernikov 	snl_init_writer(ss, &nw);
1954c91a5dfSAlexander V. Chernikov 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETADDR);
1964c91a5dfSAlexander V. Chernikov 	hdr->nlmsg_flags |= NLM_F_DUMP;
1974c91a5dfSAlexander V. Chernikov 	snl_reserve_msg_object(&nw, struct ifaddrmsg);
1984c91a5dfSAlexander V. Chernikov 
1994c91a5dfSAlexander V. Chernikov 	if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
2004c91a5dfSAlexander V. Chernikov 		return;
2014c91a5dfSAlexander V. Chernikov 
2024c91a5dfSAlexander V. Chernikov 	uint32_t nlmsg_seq = hdr->nlmsg_seq;
2034c91a5dfSAlexander V. Chernikov 	struct snl_errmsg_data e = {};
2044c91a5dfSAlexander V. Chernikov 	uint32_t count = 0;
2054c91a5dfSAlexander V. Chernikov 
2064c91a5dfSAlexander V. Chernikov 	while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
2074c91a5dfSAlexander V. Chernikov 		struct ifa *ifa = snl_allocz(ss, sizeof(*ifa));
2084c91a5dfSAlexander V. Chernikov 
2094c91a5dfSAlexander V. Chernikov 		if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &ifa->addr))
2104c91a5dfSAlexander V. Chernikov 			continue;
2114c91a5dfSAlexander V. Chernikov 
2124c91a5dfSAlexander V. Chernikov 		const uint32_t ifindex = ifa->addr.ifa_index;
2134c91a5dfSAlexander V. Chernikov 		if (ifindex >= ifmap->size || ifmap->ifaces[ifindex] == NULL)
2144c91a5dfSAlexander V. Chernikov 			continue;
2154c91a5dfSAlexander V. Chernikov 		struct iface *iface = ifmap->ifaces[ifindex];
2164c91a5dfSAlexander V. Chernikov 		ifa->next = iface->ifa;
2174c91a5dfSAlexander V. Chernikov 		ifa->count = ++count;
2184c91a5dfSAlexander V. Chernikov 		iface->ifa = ifa;
2194c91a5dfSAlexander V. Chernikov 		iface->ifa_count++;
2204c91a5dfSAlexander V. Chernikov 	}
2214c91a5dfSAlexander V. Chernikov }
2224c91a5dfSAlexander V. Chernikov 
2234c91a5dfSAlexander V. Chernikov static bool
2244c91a5dfSAlexander V. Chernikov match_iface(struct ifconfig_args *args, struct iface *iface)
2254c91a5dfSAlexander V. Chernikov {
2264c91a5dfSAlexander V. Chernikov 	if_link_t *link = &iface->link;
2274c91a5dfSAlexander V. Chernikov 
2284c91a5dfSAlexander V. Chernikov 	if (args->ifname != NULL && strcmp(args->ifname, link->ifla_ifname))
2294c91a5dfSAlexander V. Chernikov 		return (false);
2304c91a5dfSAlexander V. Chernikov 
2314c91a5dfSAlexander V. Chernikov 	if (!match_if_flags(args, link->ifi_flags))
2324c91a5dfSAlexander V. Chernikov 		return (false);
2334c91a5dfSAlexander V. Chernikov 
2344c91a5dfSAlexander V. Chernikov 	if (!group_member(link->ifla_ifname, args->matchgroup, args->nogroup))
2354c91a5dfSAlexander V. Chernikov 		return (false);
2364c91a5dfSAlexander V. Chernikov 
2374c91a5dfSAlexander V. Chernikov 	if (args->afp == NULL)
2384c91a5dfSAlexander V. Chernikov 		return (true);
2394c91a5dfSAlexander V. Chernikov 
2404c91a5dfSAlexander V. Chernikov 	if (!strcmp(args->afp->af_name, "ether")) {
2414c91a5dfSAlexander V. Chernikov 		if (link->ifla_address == NULL)
2424c91a5dfSAlexander V. Chernikov 			return (false);
2434c91a5dfSAlexander V. Chernikov 
2444c91a5dfSAlexander V. Chernikov 		struct sockaddr_dl sdl = {
2454c91a5dfSAlexander V. Chernikov 			.sdl_len = sizeof(struct sockaddr_dl),
2464c91a5dfSAlexander V. Chernikov 			.sdl_family = AF_LINK,
2474c91a5dfSAlexander V. Chernikov 			.sdl_type = link->ifi_type,
2484c91a5dfSAlexander V. Chernikov 			.sdl_alen = NLA_DATA_LEN(link->ifla_address),
2494c91a5dfSAlexander V. Chernikov 		};
2504c91a5dfSAlexander V. Chernikov 		return (match_ether(&sdl));
2514c91a5dfSAlexander V. Chernikov 	}
2524c91a5dfSAlexander V. Chernikov 
2534c91a5dfSAlexander V. Chernikov 	for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
2544c91a5dfSAlexander V. Chernikov 		if (args->afp->af_af == ifa->addr.ifa_family)
2554c91a5dfSAlexander V. Chernikov 			return (true);
2564c91a5dfSAlexander V. Chernikov 	}
2574c91a5dfSAlexander V. Chernikov 
2584c91a5dfSAlexander V. Chernikov 	return (false);
2594c91a5dfSAlexander V. Chernikov }
2604c91a5dfSAlexander V. Chernikov 
2614c91a5dfSAlexander V. Chernikov /* Sort according to the kernel-provided order */
2624c91a5dfSAlexander V. Chernikov static int
2634c91a5dfSAlexander V. Chernikov cmp_iface(const void *_a, const void *_b)
2644c91a5dfSAlexander V. Chernikov {
2654c91a5dfSAlexander V. Chernikov 	const struct iface *a = *((const void * const *)_a);
2664c91a5dfSAlexander V. Chernikov 	const struct iface *b = *((const void * const *)_b);
2674c91a5dfSAlexander V. Chernikov 
2684c91a5dfSAlexander V. Chernikov 	return ((a->idx > b->idx) * 2 - 1);
2694c91a5dfSAlexander V. Chernikov }
2704c91a5dfSAlexander V. Chernikov 
2714c91a5dfSAlexander V. Chernikov static int
2724c91a5dfSAlexander V. Chernikov cmp_ifaddr(const void *_a, const void *_b)
2734c91a5dfSAlexander V. Chernikov {
2744c91a5dfSAlexander V. Chernikov 	const struct ifa *a = *((const void * const *)_a);
2754c91a5dfSAlexander V. Chernikov 	const struct ifa *b = *((const void * const *)_b);
2764c91a5dfSAlexander V. Chernikov 
2774c91a5dfSAlexander V. Chernikov 	if (a->addr.ifa_family != b->addr.ifa_family)
2784c91a5dfSAlexander V. Chernikov 		return ((a->addr.ifa_family > b->addr.ifa_family) * 2 - 1);
2794c91a5dfSAlexander V. Chernikov 	return ((a->idx > b->idx) * 2 - 1);
2804c91a5dfSAlexander V. Chernikov }
2814c91a5dfSAlexander V. Chernikov 
2824c91a5dfSAlexander V. Chernikov static void
2834c91a5dfSAlexander V. Chernikov sort_iface_ifaddrs(struct snl_state *ss, struct iface *iface)
2844c91a5dfSAlexander V. Chernikov {
2854c91a5dfSAlexander V. Chernikov 	if (iface->ifa_count == 0)
2864c91a5dfSAlexander V. Chernikov 		return;
2874c91a5dfSAlexander V. Chernikov 
2884c91a5dfSAlexander V. Chernikov 	struct ifa **sorted_ifaddrs = snl_allocz(ss, iface->ifa_count * sizeof(void *));
2894c91a5dfSAlexander V. Chernikov 	struct ifa *ifa = iface->ifa;
2904c91a5dfSAlexander V. Chernikov 
29144cd85d4SAlexander V. Chernikov 	for (uint32_t i = 0; i < iface->ifa_count; i++) {
2924c91a5dfSAlexander V. Chernikov 		struct ifa *ifa_next = ifa->next;
2934c91a5dfSAlexander V. Chernikov 
2944c91a5dfSAlexander V. Chernikov 		sorted_ifaddrs[i] = ifa;
2954c91a5dfSAlexander V. Chernikov 		ifa->next = NULL;
2964c91a5dfSAlexander V. Chernikov 		ifa = ifa_next;
2974c91a5dfSAlexander V. Chernikov 	}
2984c91a5dfSAlexander V. Chernikov 	qsort(sorted_ifaddrs, iface->ifa_count, sizeof(void *), cmp_ifaddr);
2994c91a5dfSAlexander V. Chernikov 	ifa = sorted_ifaddrs[0];
3004c91a5dfSAlexander V. Chernikov 	iface->ifa = ifa;
30144cd85d4SAlexander V. Chernikov 	for (uint32_t i = 1; i < iface->ifa_count; i++) {
3024c91a5dfSAlexander V. Chernikov 		ifa->next = sorted_ifaddrs[i];
3034c91a5dfSAlexander V. Chernikov 		ifa = sorted_ifaddrs[i];
3044c91a5dfSAlexander V. Chernikov 	}
3054c91a5dfSAlexander V. Chernikov }
3064c91a5dfSAlexander V. Chernikov 
3074c91a5dfSAlexander V. Chernikov static void
308*6e3a9d7fSAlexander V. Chernikov status_nl(if_ctx *ctx, struct iface *iface)
3094c91a5dfSAlexander V. Chernikov {
3104c91a5dfSAlexander V. Chernikov 	if_link_t *link = &iface->link;
311*6e3a9d7fSAlexander V. Chernikov 	struct ifconfig_args *args = ctx->args;
3124c91a5dfSAlexander V. Chernikov 
3134c91a5dfSAlexander V. Chernikov 	printf("%s: ", link->ifla_ifname);
3144c91a5dfSAlexander V. Chernikov 
3154c91a5dfSAlexander V. Chernikov 	printf("flags=%x", link->ifi_flags);
3164c91a5dfSAlexander V. Chernikov 	print_bits("IFF", &link->ifi_flags, 1, IFFBITS, nitems(IFFBITS));
3174c91a5dfSAlexander V. Chernikov 
318*6e3a9d7fSAlexander V. Chernikov 	print_metric(ctx->io_s);
3194c91a5dfSAlexander V. Chernikov 	printf(" mtu %d\n", link->ifla_mtu);
3204c91a5dfSAlexander V. Chernikov 
3214c91a5dfSAlexander V. Chernikov 	if (link->ifla_ifalias != NULL)
3224c91a5dfSAlexander V. Chernikov 		printf("\tdescription: %s\n", link->ifla_ifalias);
3234c91a5dfSAlexander V. Chernikov 
3244c91a5dfSAlexander V. Chernikov 	/* TODO: convert to netlink */
3254c91a5dfSAlexander V. Chernikov 	strlcpy(ifr.ifr_name, link->ifla_ifname, sizeof(ifr.ifr_name));
326*6e3a9d7fSAlexander V. Chernikov 	print_ifcap(args, ctx->io_s);
327*6e3a9d7fSAlexander V. Chernikov 	tunnel_status(ctx->io_s);
3284c91a5dfSAlexander V. Chernikov 
3294c91a5dfSAlexander V. Chernikov 	if (args->allfamilies | (args->afp != NULL && args->afp->af_af == AF_LINK)) {
3304c91a5dfSAlexander V. Chernikov 		/* Start with link-level */
3314c91a5dfSAlexander V. Chernikov 		const struct afswtch *p = af_getbyfamily(AF_LINK);
3324c91a5dfSAlexander V. Chernikov 		if (p != NULL && link->ifla_address != NULL)
333*6e3a9d7fSAlexander V. Chernikov 			p->af_status(ctx, link, NULL);
3344c91a5dfSAlexander V. Chernikov 	}
3354c91a5dfSAlexander V. Chernikov 
336*6e3a9d7fSAlexander V. Chernikov 	sort_iface_ifaddrs(ctx->io_ss, iface);
3374c91a5dfSAlexander V. Chernikov 
3384c91a5dfSAlexander V. Chernikov 	for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
3394c91a5dfSAlexander V. Chernikov 		if (args->allfamilies) {
3404c91a5dfSAlexander V. Chernikov 			const struct afswtch *p = af_getbyfamily(ifa->addr.ifa_family);
3414c91a5dfSAlexander V. Chernikov 
3424c91a5dfSAlexander V. Chernikov 			if (p != NULL)
343*6e3a9d7fSAlexander V. Chernikov 				p->af_status(ctx, link, &ifa->addr);
3444c91a5dfSAlexander V. Chernikov 		} else if (args->afp->af_af == ifa->addr.ifa_family) {
3454c91a5dfSAlexander V. Chernikov 			const struct afswtch *p = args->afp;
3464c91a5dfSAlexander V. Chernikov 
347*6e3a9d7fSAlexander V. Chernikov 			p->af_status(ctx, link, &ifa->addr);
3484c91a5dfSAlexander V. Chernikov 		}
3494c91a5dfSAlexander V. Chernikov 	}
3504c91a5dfSAlexander V. Chernikov 
3514c91a5dfSAlexander V. Chernikov 	/* TODO: convert to netlink */
3524c91a5dfSAlexander V. Chernikov 	if (args->allfamilies)
353*6e3a9d7fSAlexander V. Chernikov 		af_other_status(ctx);
3544c91a5dfSAlexander V. Chernikov 	else if (args->afp->af_other_status != NULL)
355*6e3a9d7fSAlexander V. Chernikov 		args->afp->af_other_status(ctx);
3564c91a5dfSAlexander V. Chernikov 
357*6e3a9d7fSAlexander V. Chernikov 	print_ifstatus(ctx->io_s);
3584c91a5dfSAlexander V. Chernikov 	if (args->verbose > 0)
359*6e3a9d7fSAlexander V. Chernikov 		sfp_status(ctx->io_s, &ifr, args->verbose);
3604c91a5dfSAlexander V. Chernikov }
3614c91a5dfSAlexander V. Chernikov 
3624c91a5dfSAlexander V. Chernikov static int
3634c91a5dfSAlexander V. Chernikov get_local_socket(void)
3644c91a5dfSAlexander V. Chernikov {
3654c91a5dfSAlexander V. Chernikov 	int s = socket(AF_LOCAL, SOCK_DGRAM, 0);
3664c91a5dfSAlexander V. Chernikov 
3674c91a5dfSAlexander V. Chernikov 	if (s < 0)
3684c91a5dfSAlexander V. Chernikov 		err(1, "socket(family %u,SOCK_DGRAM)", AF_LOCAL);
3694c91a5dfSAlexander V. Chernikov 	return (s);
3704c91a5dfSAlexander V. Chernikov }
3714c91a5dfSAlexander V. Chernikov 
3724c91a5dfSAlexander V. Chernikov static void
3734c91a5dfSAlexander V. Chernikov set_global_ifname(if_link_t *link)
3744c91a5dfSAlexander V. Chernikov {
37544cd85d4SAlexander V. Chernikov 	size_t iflen = strlcpy(name, link->ifla_ifname, sizeof(name));
37644cd85d4SAlexander V. Chernikov 
3774c91a5dfSAlexander V. Chernikov 	if (iflen >= sizeof(name))
3784c91a5dfSAlexander V. Chernikov 		errx(1, "%s: cloning name too long", link->ifla_ifname);
3794c91a5dfSAlexander V. Chernikov 	strlcpy(ifr.ifr_name, link->ifla_ifname, sizeof(ifr.ifr_name));
3804c91a5dfSAlexander V. Chernikov }
3814c91a5dfSAlexander V. Chernikov 
3824c91a5dfSAlexander V. Chernikov void
3834c91a5dfSAlexander V. Chernikov list_interfaces_nl(struct ifconfig_args *args)
3844c91a5dfSAlexander V. Chernikov {
3854c91a5dfSAlexander V. Chernikov 	struct snl_state ss = {};
386*6e3a9d7fSAlexander V. Chernikov 	struct ifconfig_context _ctx = {
387*6e3a9d7fSAlexander V. Chernikov 		.args = args,
388*6e3a9d7fSAlexander V. Chernikov 		.io_s = get_local_socket(),
389*6e3a9d7fSAlexander V. Chernikov 		.io_ss = &ss,
390*6e3a9d7fSAlexander V. Chernikov 	};
391*6e3a9d7fSAlexander V. Chernikov 	struct ifconfig_context *ctx = &_ctx;
3924c91a5dfSAlexander V. Chernikov 
3934c91a5dfSAlexander V. Chernikov 	nl_init_socket(&ss);
3944c91a5dfSAlexander V. Chernikov 
3954c91a5dfSAlexander V. Chernikov 	struct ifmap *ifmap = prepare_ifmap(&ss);
3964c91a5dfSAlexander V. Chernikov 	struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *));
39744cd85d4SAlexander V. Chernikov 	for (uint32_t i = 0, num = 0; i < ifmap->size; i++) {
3984c91a5dfSAlexander V. Chernikov 		if (ifmap->ifaces[i] != NULL) {
3994c91a5dfSAlexander V. Chernikov 			sorted_ifaces[num++] = ifmap->ifaces[i];
4004c91a5dfSAlexander V. Chernikov 			if (num == ifmap->count)
4014c91a5dfSAlexander V. Chernikov 				break;
4024c91a5dfSAlexander V. Chernikov 		}
4034c91a5dfSAlexander V. Chernikov 	}
4044c91a5dfSAlexander V. Chernikov 	qsort(sorted_ifaces, ifmap->count, sizeof(void *), cmp_iface);
4054c91a5dfSAlexander V. Chernikov 	prepare_ifaddrs(&ss, ifmap);
4064c91a5dfSAlexander V. Chernikov 
40744cd85d4SAlexander V. Chernikov 	for (uint32_t i = 0, num = 0; i < ifmap->count; i++) {
4084c91a5dfSAlexander V. Chernikov 		struct iface *iface = sorted_ifaces[i];
4094c91a5dfSAlexander V. Chernikov 
4104c91a5dfSAlexander V. Chernikov 		if (!match_iface(args, iface))
4114c91a5dfSAlexander V. Chernikov 			continue;
4124c91a5dfSAlexander V. Chernikov 
4134c91a5dfSAlexander V. Chernikov 		set_global_ifname(&iface->link);
4144c91a5dfSAlexander V. Chernikov 
4154c91a5dfSAlexander V. Chernikov 		if (args->namesonly) {
4164c91a5dfSAlexander V. Chernikov 			if (num++ != 0)
4174c91a5dfSAlexander V. Chernikov 				printf(" ");
4184c91a5dfSAlexander V. Chernikov 			fputs(iface->link.ifla_ifname, stdout);
4194c91a5dfSAlexander V. Chernikov 		} else if (args->argc == 0)
420*6e3a9d7fSAlexander V. Chernikov 			status_nl(ctx, iface);
42126056fa8SAlexander V. Chernikov 		else
42226056fa8SAlexander V. Chernikov 			ifconfig(args->argc, args->argv, 0, args->afp);
4234c91a5dfSAlexander V. Chernikov 	}
4244c91a5dfSAlexander V. Chernikov 	if (args->namesonly)
4254c91a5dfSAlexander V. Chernikov 		printf("\n");
4264c91a5dfSAlexander V. Chernikov 
427*6e3a9d7fSAlexander V. Chernikov 	close(ctx->io_s);
4284c91a5dfSAlexander V. Chernikov 	snl_free(&ss);
4294c91a5dfSAlexander V. Chernikov }
4304c91a5dfSAlexander V. Chernikov 
431