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>
51c344eff9SAlexander V. Chernikov #include <net/if_strings.h>
524c91a5dfSAlexander V. Chernikov #include <net/if_types.h>
534c91a5dfSAlexander V. Chernikov #include "ifconfig.h"
544c91a5dfSAlexander V. Chernikov #include "ifconfig_netlink.h"
554c91a5dfSAlexander V. Chernikov
564c91a5dfSAlexander V. Chernikov static const char *IFFBITS[] = {
574c91a5dfSAlexander V. Chernikov "UP", /* 00:0x1 IFF_UP*/
584c91a5dfSAlexander V. Chernikov "BROADCAST", /* 01:0x2 IFF_BROADCAST*/
594c91a5dfSAlexander V. Chernikov "DEBUG", /* 02:0x4 IFF_DEBUG*/
604c91a5dfSAlexander V. Chernikov "LOOPBACK", /* 03:0x8 IFF_LOOPBACK*/
614c91a5dfSAlexander V. Chernikov "POINTOPOINT", /* 04:0x10 IFF_POINTOPOINT*/
624c91a5dfSAlexander V. Chernikov "NEEDSEPOCH", /* 05:0x20 IFF_NEEDSEPOCH*/
634c91a5dfSAlexander V. Chernikov "RUNNING", /* 06:0x40 IFF_DRV_RUNNING*/
644c91a5dfSAlexander V. Chernikov "NOARP", /* 07:0x80 IFF_NOARP*/
654c91a5dfSAlexander V. Chernikov "PROMISC", /* 08:0x100 IFF_PROMISC*/
664c91a5dfSAlexander V. Chernikov "ALLMULTI", /* 09:0x200 IFF_ALLMULTI*/
674c91a5dfSAlexander V. Chernikov "DRV_OACTIVE", /* 10:0x400 IFF_DRV_OACTIVE*/
684c91a5dfSAlexander V. Chernikov "SIMPLEX", /* 11:0x800 IFF_SIMPLEX*/
694c91a5dfSAlexander V. Chernikov "LINK0", /* 12:0x1000 IFF_LINK0*/
704c91a5dfSAlexander V. Chernikov "LINK1", /* 13:0x2000 IFF_LINK1*/
714c91a5dfSAlexander V. Chernikov "LINK2", /* 14:0x4000 IFF_LINK2*/
724c91a5dfSAlexander V. Chernikov "MULTICAST", /* 15:0x8000 IFF_MULTICAST*/
734c91a5dfSAlexander V. Chernikov "CANTCONFIG", /* 16:0x10000 IFF_CANTCONFIG*/
744c91a5dfSAlexander V. Chernikov "PPROMISC", /* 17:0x20000 IFF_PPROMISC*/
754c91a5dfSAlexander V. Chernikov "MONITOR", /* 18:0x40000 IFF_MONITOR*/
764c91a5dfSAlexander V. Chernikov "STATICARP", /* 19:0x80000 IFF_STATICARP*/
774c91a5dfSAlexander V. Chernikov "STICKYARP", /* 20:0x100000 IFF_STICKYARP*/
784c91a5dfSAlexander V. Chernikov "DYING", /* 21:0x200000 IFF_DYING*/
794c91a5dfSAlexander V. Chernikov "RENAMING", /* 22:0x400000 IFF_RENAMING*/
80*00c9a680SMark Johnston "PALLMULTI", /* 23:0x800000 IFF_PALLMULTI*/
814c91a5dfSAlexander V. Chernikov "LOWER_UP", /* 24:0x1000000 IFF_NETLINK_1*/
824c91a5dfSAlexander V. Chernikov };
834c91a5dfSAlexander V. Chernikov
844c91a5dfSAlexander V. Chernikov static void
nl_init_socket(struct snl_state * ss)854c91a5dfSAlexander V. Chernikov nl_init_socket(struct snl_state *ss)
864c91a5dfSAlexander V. Chernikov {
874c91a5dfSAlexander V. Chernikov if (snl_init(ss, NETLINK_ROUTE))
884c91a5dfSAlexander V. Chernikov return;
894c91a5dfSAlexander V. Chernikov
904c91a5dfSAlexander V. Chernikov if (modfind("netlink") == -1 && errno == ENOENT) {
914c91a5dfSAlexander V. Chernikov /* Try to load */
924c91a5dfSAlexander V. Chernikov if (kldload("netlink") == -1)
934c91a5dfSAlexander V. Chernikov err(1, "netlink is not loaded and load attempt failed");
944c91a5dfSAlexander V. Chernikov if (snl_init(ss, NETLINK_ROUTE))
954c91a5dfSAlexander V. Chernikov return;
964c91a5dfSAlexander V. Chernikov }
974c91a5dfSAlexander V. Chernikov
984c91a5dfSAlexander V. Chernikov err(1, "unable to open netlink socket");
994c91a5dfSAlexander V. Chernikov }
1004c91a5dfSAlexander V. Chernikov
1014bf44dd7SAlexander V. Chernikov int
ifconfig_nl(if_ctx * ctx,int iscreate,const struct afswtch * uafp)102bbad5525SAlexander V. Chernikov ifconfig_nl(if_ctx *ctx, int iscreate,
1034bf44dd7SAlexander V. Chernikov const struct afswtch *uafp)
1044bf44dd7SAlexander V. Chernikov {
1054bf44dd7SAlexander V. Chernikov struct snl_state ss = {};
1064bf44dd7SAlexander V. Chernikov
1074bf44dd7SAlexander V. Chernikov nl_init_socket(&ss);
10885e0016aSAlexander V. Chernikov ctx->io_ss = &ss;
1094bf44dd7SAlexander V. Chernikov
110bbad5525SAlexander V. Chernikov int error = ifconfig_ioctl(ctx, iscreate, uafp);
1114bf44dd7SAlexander V. Chernikov
1124bf44dd7SAlexander V. Chernikov snl_free(&ss);
113bbad5525SAlexander V. Chernikov ctx->io_ss = NULL;
1144bf44dd7SAlexander V. Chernikov
1154bf44dd7SAlexander V. Chernikov return (error);
1164bf44dd7SAlexander V. Chernikov }
1174bf44dd7SAlexander V. Chernikov
1184c91a5dfSAlexander V. Chernikov struct ifa {
1194c91a5dfSAlexander V. Chernikov struct ifa *next;
1204c91a5dfSAlexander V. Chernikov uint32_t idx;
1214c91a5dfSAlexander V. Chernikov struct snl_parsed_addr addr;
1224c91a5dfSAlexander V. Chernikov };
1234c91a5dfSAlexander V. Chernikov
1244c91a5dfSAlexander V. Chernikov struct iface {
1254c91a5dfSAlexander V. Chernikov struct snl_parsed_link link;
1264c91a5dfSAlexander V. Chernikov struct ifa *ifa;
1274c91a5dfSAlexander V. Chernikov uint32_t ifa_count;
1284c91a5dfSAlexander V. Chernikov uint32_t idx;
1294c91a5dfSAlexander V. Chernikov };
1304c91a5dfSAlexander V. Chernikov
1314c91a5dfSAlexander V. Chernikov struct ifmap {
1324c91a5dfSAlexander V. Chernikov uint32_t size;
1334c91a5dfSAlexander V. Chernikov uint32_t count;
1344c91a5dfSAlexander V. Chernikov struct iface **ifaces;
1354c91a5dfSAlexander V. Chernikov };
1364c91a5dfSAlexander V. Chernikov
1374c91a5dfSAlexander V. Chernikov /*
1384c91a5dfSAlexander V. Chernikov * Returns ifmap ifindex->snl_parsed_link.
1394c91a5dfSAlexander V. Chernikov * Memory is allocated using snl temporary buffers
1404c91a5dfSAlexander V. Chernikov */
1414c91a5dfSAlexander V. Chernikov static struct ifmap *
prepare_ifmap(struct snl_state * ss)1424c91a5dfSAlexander V. Chernikov prepare_ifmap(struct snl_state *ss)
1434c91a5dfSAlexander V. Chernikov {
1444c91a5dfSAlexander V. Chernikov struct snl_writer nw = {};
1454c91a5dfSAlexander V. Chernikov
1464c91a5dfSAlexander V. Chernikov snl_init_writer(ss, &nw);
1474c91a5dfSAlexander V. Chernikov struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
1484c91a5dfSAlexander V. Chernikov hdr->nlmsg_flags |= NLM_F_DUMP;
1494c91a5dfSAlexander V. Chernikov snl_reserve_msg_object(&nw, struct ifinfomsg);
1504c91a5dfSAlexander V. Chernikov
1514f8f43b0SKristof Provost if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
1524c91a5dfSAlexander V. Chernikov return (NULL);
1534c91a5dfSAlexander V. Chernikov
1544c91a5dfSAlexander V. Chernikov uint32_t nlmsg_seq = hdr->nlmsg_seq;
1554c91a5dfSAlexander V. Chernikov struct ifmap *ifmap = snl_allocz(ss, sizeof(*ifmap));
1564c91a5dfSAlexander V. Chernikov struct snl_errmsg_data e = {};
1574c91a5dfSAlexander V. Chernikov
1584c91a5dfSAlexander V. Chernikov while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
1594c91a5dfSAlexander V. Chernikov struct iface *iface = snl_allocz(ss, sizeof(*iface));
1604c91a5dfSAlexander V. Chernikov
1614c91a5dfSAlexander V. Chernikov if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &iface->link))
1624c91a5dfSAlexander V. Chernikov continue;
1634c91a5dfSAlexander V. Chernikov if (iface->link.ifi_index >= ifmap->size) {
1644c91a5dfSAlexander V. Chernikov size_t new_size = MAX(ifmap->size, 32);
1654c91a5dfSAlexander V. Chernikov
1664c91a5dfSAlexander V. Chernikov while (new_size <= iface->link.ifi_index + 1)
1674c91a5dfSAlexander V. Chernikov new_size *= 2;
1684c91a5dfSAlexander V. Chernikov
1694c91a5dfSAlexander V. Chernikov struct iface **ifaces= snl_allocz(ss, new_size * sizeof(void *));
1704c91a5dfSAlexander V. Chernikov memcpy(ifaces, ifmap->ifaces, ifmap->size * sizeof(void *));
1714c91a5dfSAlexander V. Chernikov ifmap->ifaces = ifaces;
1724c91a5dfSAlexander V. Chernikov ifmap->size = new_size;
1734c91a5dfSAlexander V. Chernikov }
1744c91a5dfSAlexander V. Chernikov ifmap->ifaces[iface->link.ifi_index] = iface;
1754c91a5dfSAlexander V. Chernikov ifmap->count++;
1764c91a5dfSAlexander V. Chernikov iface->idx = ifmap->count;
1774c91a5dfSAlexander V. Chernikov }
1784c91a5dfSAlexander V. Chernikov return (ifmap);
1794c91a5dfSAlexander V. Chernikov }
1804c91a5dfSAlexander V. Chernikov
1814bf44dd7SAlexander V. Chernikov uint32_t
if_nametoindex_nl(struct snl_state * ss,const char * ifname)1824bf44dd7SAlexander V. Chernikov if_nametoindex_nl(struct snl_state *ss, const char *ifname)
1834bf44dd7SAlexander V. Chernikov {
1844bf44dd7SAlexander V. Chernikov struct snl_writer nw = {};
1854bf44dd7SAlexander V. Chernikov struct snl_parsed_link_simple link = {};
1864bf44dd7SAlexander V. Chernikov
1874bf44dd7SAlexander V. Chernikov snl_init_writer(ss, &nw);
1884bf44dd7SAlexander V. Chernikov struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
1894bf44dd7SAlexander V. Chernikov snl_reserve_msg_object(&nw, struct ifinfomsg);
1904bf44dd7SAlexander V. Chernikov snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname);
1914bf44dd7SAlexander V. Chernikov
1924f8f43b0SKristof Provost if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
1934bf44dd7SAlexander V. Chernikov return (0);
1944bf44dd7SAlexander V. Chernikov
1954bf44dd7SAlexander V. Chernikov hdr = snl_read_reply(ss, hdr->nlmsg_seq);
1964bf44dd7SAlexander V. Chernikov if (hdr->nlmsg_type != NL_RTM_NEWLINK)
1974bf44dd7SAlexander V. Chernikov return (0);
1984bf44dd7SAlexander V. Chernikov if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, &link))
1994bf44dd7SAlexander V. Chernikov return (0);
2004bf44dd7SAlexander V. Chernikov
2014bf44dd7SAlexander V. Chernikov return (link.ifi_index);
2024bf44dd7SAlexander V. Chernikov }
2034bf44dd7SAlexander V. Chernikov
20431016aa0SGleb Smirnoff ifType
convert_iftype(ifType iftype)20531016aa0SGleb Smirnoff convert_iftype(ifType iftype)
20631016aa0SGleb Smirnoff {
20731016aa0SGleb Smirnoff switch (iftype) {
20831016aa0SGleb Smirnoff case IFT_IEEE8023ADLAG:
20931016aa0SGleb Smirnoff return (IFT_ETHER);
21031016aa0SGleb Smirnoff case IFT_INFINIBANDLAG:
21131016aa0SGleb Smirnoff return (IFT_INFINIBAND);
21231016aa0SGleb Smirnoff default:
21331016aa0SGleb Smirnoff return (iftype);
21431016aa0SGleb Smirnoff }
21531016aa0SGleb Smirnoff }
21631016aa0SGleb Smirnoff
2174c91a5dfSAlexander V. Chernikov static void
prepare_ifaddrs(struct snl_state * ss,struct ifmap * ifmap)2184c91a5dfSAlexander V. Chernikov prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap)
2194c91a5dfSAlexander V. Chernikov {
2204c91a5dfSAlexander V. Chernikov struct snl_writer nw = {};
2214c91a5dfSAlexander V. Chernikov
2224c91a5dfSAlexander V. Chernikov snl_init_writer(ss, &nw);
2234c91a5dfSAlexander V. Chernikov struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETADDR);
2244c91a5dfSAlexander V. Chernikov hdr->nlmsg_flags |= NLM_F_DUMP;
2254c91a5dfSAlexander V. Chernikov snl_reserve_msg_object(&nw, struct ifaddrmsg);
2264c91a5dfSAlexander V. Chernikov
2274f8f43b0SKristof Provost if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
2284c91a5dfSAlexander V. Chernikov return;
2294c91a5dfSAlexander V. Chernikov
2304c91a5dfSAlexander V. Chernikov uint32_t nlmsg_seq = hdr->nlmsg_seq;
2314c91a5dfSAlexander V. Chernikov struct snl_errmsg_data e = {};
2324c91a5dfSAlexander V. Chernikov uint32_t count = 0;
2334c91a5dfSAlexander V. Chernikov
2344c91a5dfSAlexander V. Chernikov while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
2354c91a5dfSAlexander V. Chernikov struct ifa *ifa = snl_allocz(ss, sizeof(*ifa));
2364c91a5dfSAlexander V. Chernikov
2374c91a5dfSAlexander V. Chernikov if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &ifa->addr))
2384c91a5dfSAlexander V. Chernikov continue;
2394c91a5dfSAlexander V. Chernikov
2404c91a5dfSAlexander V. Chernikov const uint32_t ifindex = ifa->addr.ifa_index;
2414c91a5dfSAlexander V. Chernikov if (ifindex >= ifmap->size || ifmap->ifaces[ifindex] == NULL)
2424c91a5dfSAlexander V. Chernikov continue;
2434c91a5dfSAlexander V. Chernikov struct iface *iface = ifmap->ifaces[ifindex];
2444c91a5dfSAlexander V. Chernikov ifa->next = iface->ifa;
24512cfa3c1SAlexander V. Chernikov ifa->idx = ++count;
2464c91a5dfSAlexander V. Chernikov iface->ifa = ifa;
2474c91a5dfSAlexander V. Chernikov iface->ifa_count++;
2484c91a5dfSAlexander V. Chernikov }
2494c91a5dfSAlexander V. Chernikov }
2504c91a5dfSAlexander V. Chernikov
2514c91a5dfSAlexander V. Chernikov static bool
match_iface(struct ifconfig_args * args,struct iface * iface)2524c91a5dfSAlexander V. Chernikov match_iface(struct ifconfig_args *args, struct iface *iface)
2534c91a5dfSAlexander V. Chernikov {
2544c91a5dfSAlexander V. Chernikov if_link_t *link = &iface->link;
2554c91a5dfSAlexander V. Chernikov
2564c91a5dfSAlexander V. Chernikov if (args->ifname != NULL && strcmp(args->ifname, link->ifla_ifname))
2574c91a5dfSAlexander V. Chernikov return (false);
2584c91a5dfSAlexander V. Chernikov
2594c91a5dfSAlexander V. Chernikov if (!match_if_flags(args, link->ifi_flags))
2604c91a5dfSAlexander V. Chernikov return (false);
2614c91a5dfSAlexander V. Chernikov
2624c91a5dfSAlexander V. Chernikov if (!group_member(link->ifla_ifname, args->matchgroup, args->nogroup))
2634c91a5dfSAlexander V. Chernikov return (false);
2644c91a5dfSAlexander V. Chernikov
2654c91a5dfSAlexander V. Chernikov if (args->afp == NULL)
2664c91a5dfSAlexander V. Chernikov return (true);
2674c91a5dfSAlexander V. Chernikov
2684c91a5dfSAlexander V. Chernikov if (!strcmp(args->afp->af_name, "ether")) {
2694c91a5dfSAlexander V. Chernikov if (link->ifla_address == NULL)
2704c91a5dfSAlexander V. Chernikov return (false);
2714c91a5dfSAlexander V. Chernikov
2724c91a5dfSAlexander V. Chernikov struct sockaddr_dl sdl = {
2734c91a5dfSAlexander V. Chernikov .sdl_len = sizeof(struct sockaddr_dl),
2744c91a5dfSAlexander V. Chernikov .sdl_family = AF_LINK,
27531016aa0SGleb Smirnoff .sdl_type = convert_iftype(link->ifi_type),
2764c91a5dfSAlexander V. Chernikov .sdl_alen = NLA_DATA_LEN(link->ifla_address),
2774c91a5dfSAlexander V. Chernikov };
2784c91a5dfSAlexander V. Chernikov return (match_ether(&sdl));
2796f3947beSGleb Smirnoff } else if (args->afp->af_af == AF_LINK)
2806f3947beSGleb Smirnoff /*
2816f3947beSGleb Smirnoff * The rtnetlink(4) RTM_GETADDR does not list link level
2826f3947beSGleb Smirnoff * addresses, so latter cycle won't match anything. Short
2836f3947beSGleb Smirnoff * circuit on RTM_GETLINK has provided us an address.
2846f3947beSGleb Smirnoff */
2856f3947beSGleb Smirnoff return (link->ifla_address != NULL);
2864c91a5dfSAlexander V. Chernikov
2874c91a5dfSAlexander V. Chernikov for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
2884c91a5dfSAlexander V. Chernikov if (args->afp->af_af == ifa->addr.ifa_family)
2894c91a5dfSAlexander V. Chernikov return (true);
2904c91a5dfSAlexander V. Chernikov }
2914c91a5dfSAlexander V. Chernikov
2924c91a5dfSAlexander V. Chernikov return (false);
2934c91a5dfSAlexander V. Chernikov }
2944c91a5dfSAlexander V. Chernikov
2954c91a5dfSAlexander V. Chernikov /* Sort according to the kernel-provided order */
2964c91a5dfSAlexander V. Chernikov static int
cmp_iface(const void * _a,const void * _b)2974c91a5dfSAlexander V. Chernikov cmp_iface(const void *_a, const void *_b)
2984c91a5dfSAlexander V. Chernikov {
2994c91a5dfSAlexander V. Chernikov const struct iface *a = *((const void * const *)_a);
3004c91a5dfSAlexander V. Chernikov const struct iface *b = *((const void * const *)_b);
3014c91a5dfSAlexander V. Chernikov
3024c91a5dfSAlexander V. Chernikov return ((a->idx > b->idx) * 2 - 1);
3034c91a5dfSAlexander V. Chernikov }
3044c91a5dfSAlexander V. Chernikov
3054c91a5dfSAlexander V. Chernikov static int
cmp_ifaddr(const void * _a,const void * _b)3064c91a5dfSAlexander V. Chernikov cmp_ifaddr(const void *_a, const void *_b)
3074c91a5dfSAlexander V. Chernikov {
3084c91a5dfSAlexander V. Chernikov const struct ifa *a = *((const void * const *)_a);
3094c91a5dfSAlexander V. Chernikov const struct ifa *b = *((const void * const *)_b);
3104c91a5dfSAlexander V. Chernikov
3114c91a5dfSAlexander V. Chernikov if (a->addr.ifa_family != b->addr.ifa_family)
3124c91a5dfSAlexander V. Chernikov return ((a->addr.ifa_family > b->addr.ifa_family) * 2 - 1);
3134c91a5dfSAlexander V. Chernikov return ((a->idx > b->idx) * 2 - 1);
3144c91a5dfSAlexander V. Chernikov }
3154c91a5dfSAlexander V. Chernikov
3164c91a5dfSAlexander V. Chernikov static void
sort_iface_ifaddrs(struct snl_state * ss,struct iface * iface)3174c91a5dfSAlexander V. Chernikov sort_iface_ifaddrs(struct snl_state *ss, struct iface *iface)
3184c91a5dfSAlexander V. Chernikov {
3194c91a5dfSAlexander V. Chernikov if (iface->ifa_count == 0)
3204c91a5dfSAlexander V. Chernikov return;
3214c91a5dfSAlexander V. Chernikov
3224c91a5dfSAlexander V. Chernikov struct ifa **sorted_ifaddrs = snl_allocz(ss, iface->ifa_count * sizeof(void *));
3234c91a5dfSAlexander V. Chernikov struct ifa *ifa = iface->ifa;
3244c91a5dfSAlexander V. Chernikov
32544cd85d4SAlexander V. Chernikov for (uint32_t i = 0; i < iface->ifa_count; i++) {
3264c91a5dfSAlexander V. Chernikov struct ifa *ifa_next = ifa->next;
3274c91a5dfSAlexander V. Chernikov
3284c91a5dfSAlexander V. Chernikov sorted_ifaddrs[i] = ifa;
3294c91a5dfSAlexander V. Chernikov ifa->next = NULL;
3304c91a5dfSAlexander V. Chernikov ifa = ifa_next;
3314c91a5dfSAlexander V. Chernikov }
3324c91a5dfSAlexander V. Chernikov qsort(sorted_ifaddrs, iface->ifa_count, sizeof(void *), cmp_ifaddr);
3334c91a5dfSAlexander V. Chernikov ifa = sorted_ifaddrs[0];
3344c91a5dfSAlexander V. Chernikov iface->ifa = ifa;
33544cd85d4SAlexander V. Chernikov for (uint32_t i = 1; i < iface->ifa_count; i++) {
3364c91a5dfSAlexander V. Chernikov ifa->next = sorted_ifaddrs[i];
3374c91a5dfSAlexander V. Chernikov ifa = sorted_ifaddrs[i];
3384c91a5dfSAlexander V. Chernikov }
3394c91a5dfSAlexander V. Chernikov }
3404c91a5dfSAlexander V. Chernikov
3414c91a5dfSAlexander V. Chernikov static void
print_ifcaps(if_ctx * ctx,if_link_t * link)342c344eff9SAlexander V. Chernikov print_ifcaps(if_ctx *ctx, if_link_t *link)
343c344eff9SAlexander V. Chernikov {
344c344eff9SAlexander V. Chernikov uint32_t sz_u32 = roundup2(link->iflaf_caps.nla_bitset_size, 32) / 32;
345c344eff9SAlexander V. Chernikov
346c344eff9SAlexander V. Chernikov if (sz_u32 > 0) {
347c344eff9SAlexander V. Chernikov uint32_t *caps = link->iflaf_caps.nla_bitset_value;
348c344eff9SAlexander V. Chernikov
349c344eff9SAlexander V. Chernikov printf("\toptions=%x", caps[0]);
350c344eff9SAlexander V. Chernikov print_bits("IFCAPS", caps, sz_u32, ifcap_bit_names, nitems(ifcap_bit_names));
351c344eff9SAlexander V. Chernikov putchar('\n');
352c344eff9SAlexander V. Chernikov }
353c344eff9SAlexander V. Chernikov
354c344eff9SAlexander V. Chernikov if (ctx->args->supmedia && sz_u32 > 0) {
355c344eff9SAlexander V. Chernikov uint32_t *caps = link->iflaf_caps.nla_bitset_mask;
356c344eff9SAlexander V. Chernikov
357c344eff9SAlexander V. Chernikov printf("\tcapabilities=%x", caps[0]);
358c344eff9SAlexander V. Chernikov print_bits("IFCAPS", caps, sz_u32, ifcap_bit_names, nitems(ifcap_bit_names));
359c344eff9SAlexander V. Chernikov putchar('\n');
360c344eff9SAlexander V. Chernikov }
361c344eff9SAlexander V. Chernikov }
362c344eff9SAlexander V. Chernikov
363c344eff9SAlexander V. Chernikov static void
status_nl(if_ctx * ctx,struct iface * iface)3646e3a9d7fSAlexander V. Chernikov status_nl(if_ctx *ctx, struct iface *iface)
3654c91a5dfSAlexander V. Chernikov {
3664c91a5dfSAlexander V. Chernikov if_link_t *link = &iface->link;
3676e3a9d7fSAlexander V. Chernikov struct ifconfig_args *args = ctx->args;
368cd201c09SMike Karels char *drivername = NULL;
3694c91a5dfSAlexander V. Chernikov
3704c91a5dfSAlexander V. Chernikov printf("%s: ", link->ifla_ifname);
3714c91a5dfSAlexander V. Chernikov
3724c91a5dfSAlexander V. Chernikov printf("flags=%x", link->ifi_flags);
3734c91a5dfSAlexander V. Chernikov print_bits("IFF", &link->ifi_flags, 1, IFFBITS, nitems(IFFBITS));
3744c91a5dfSAlexander V. Chernikov
3757fa282e6SAlexander V. Chernikov print_metric(ctx);
3764c91a5dfSAlexander V. Chernikov printf(" mtu %d\n", link->ifla_mtu);
3774c91a5dfSAlexander V. Chernikov
3784c91a5dfSAlexander V. Chernikov if (link->ifla_ifalias != NULL)
3794c91a5dfSAlexander V. Chernikov printf("\tdescription: %s\n", link->ifla_ifalias);
3804c91a5dfSAlexander V. Chernikov
381c344eff9SAlexander V. Chernikov print_ifcaps(ctx, link);
382c6f0602fSAlexander V. Chernikov tunnel_status(ctx);
3834c91a5dfSAlexander V. Chernikov
3844c91a5dfSAlexander V. Chernikov if (args->allfamilies | (args->afp != NULL && args->afp->af_af == AF_LINK)) {
3854c91a5dfSAlexander V. Chernikov /* Start with link-level */
3864c91a5dfSAlexander V. Chernikov const struct afswtch *p = af_getbyfamily(AF_LINK);
3874c91a5dfSAlexander V. Chernikov if (p != NULL && link->ifla_address != NULL)
3886e3a9d7fSAlexander V. Chernikov p->af_status(ctx, link, NULL);
3894c91a5dfSAlexander V. Chernikov }
3904c91a5dfSAlexander V. Chernikov
3916e3a9d7fSAlexander V. Chernikov sort_iface_ifaddrs(ctx->io_ss, iface);
3924c91a5dfSAlexander V. Chernikov
3934c91a5dfSAlexander V. Chernikov for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
3944c91a5dfSAlexander V. Chernikov if (args->allfamilies) {
3954c91a5dfSAlexander V. Chernikov const struct afswtch *p = af_getbyfamily(ifa->addr.ifa_family);
3964c91a5dfSAlexander V. Chernikov
3974c91a5dfSAlexander V. Chernikov if (p != NULL)
3986e3a9d7fSAlexander V. Chernikov p->af_status(ctx, link, &ifa->addr);
3994c91a5dfSAlexander V. Chernikov } else if (args->afp->af_af == ifa->addr.ifa_family) {
4004c91a5dfSAlexander V. Chernikov const struct afswtch *p = args->afp;
4014c91a5dfSAlexander V. Chernikov
4026e3a9d7fSAlexander V. Chernikov p->af_status(ctx, link, &ifa->addr);
4034c91a5dfSAlexander V. Chernikov }
4044c91a5dfSAlexander V. Chernikov }
4054c91a5dfSAlexander V. Chernikov
4064c91a5dfSAlexander V. Chernikov /* TODO: convert to netlink */
4074c91a5dfSAlexander V. Chernikov if (args->allfamilies)
4086e3a9d7fSAlexander V. Chernikov af_other_status(ctx);
4094c91a5dfSAlexander V. Chernikov else if (args->afp->af_other_status != NULL)
4106e3a9d7fSAlexander V. Chernikov args->afp->af_other_status(ctx);
4114c91a5dfSAlexander V. Chernikov
41285e0016aSAlexander V. Chernikov print_ifstatus(ctx);
413cd201c09SMike Karels if (args->drivername || args->verbose) {
414cd201c09SMike Karels if (ifconfig_get_orig_name(lifh, link->ifla_ifname,
415cd201c09SMike Karels &drivername) != 0) {
416cd201c09SMike Karels if (ifconfig_err_errtype(lifh) == OTHER)
417cd201c09SMike Karels fprintf(stderr, "get original name: %s\n",
418cd201c09SMike Karels strerror(ifconfig_err_errno(lifh)));
419cd201c09SMike Karels else
420cd201c09SMike Karels fprintf(stderr,
421cd201c09SMike Karels "get original name: error type %d\n",
422cd201c09SMike Karels ifconfig_err_errtype(lifh));
423cd201c09SMike Karels exit_code = 1;
424cd201c09SMike Karels }
425cd201c09SMike Karels if (drivername != NULL)
426cd201c09SMike Karels printf("\tdrivername: %s\n", drivername);
427cd201c09SMike Karels free(drivername);
428cd201c09SMike Karels }
4294c91a5dfSAlexander V. Chernikov if (args->verbose > 0)
4300c2beef7SAlexander V. Chernikov sfp_status(ctx);
4314c91a5dfSAlexander V. Chernikov }
4324c91a5dfSAlexander V. Chernikov
4334c91a5dfSAlexander V. Chernikov static int
get_local_socket(void)4344c91a5dfSAlexander V. Chernikov get_local_socket(void)
4354c91a5dfSAlexander V. Chernikov {
4364c91a5dfSAlexander V. Chernikov int s = socket(AF_LOCAL, SOCK_DGRAM, 0);
4374c91a5dfSAlexander V. Chernikov
4384c91a5dfSAlexander V. Chernikov if (s < 0)
4394c91a5dfSAlexander V. Chernikov err(1, "socket(family %u,SOCK_DGRAM)", AF_LOCAL);
4404c91a5dfSAlexander V. Chernikov return (s);
4414c91a5dfSAlexander V. Chernikov }
4424c91a5dfSAlexander V. Chernikov
4434c91a5dfSAlexander V. Chernikov void
list_interfaces_nl(struct ifconfig_args * args)4444c91a5dfSAlexander V. Chernikov list_interfaces_nl(struct ifconfig_args *args)
4454c91a5dfSAlexander V. Chernikov {
4464c91a5dfSAlexander V. Chernikov struct snl_state ss = {};
4476e3a9d7fSAlexander V. Chernikov struct ifconfig_context _ctx = {
4486e3a9d7fSAlexander V. Chernikov .args = args,
4496e3a9d7fSAlexander V. Chernikov .io_s = get_local_socket(),
4506e3a9d7fSAlexander V. Chernikov .io_ss = &ss,
4516e3a9d7fSAlexander V. Chernikov };
4526e3a9d7fSAlexander V. Chernikov struct ifconfig_context *ctx = &_ctx;
4534c91a5dfSAlexander V. Chernikov
4544c91a5dfSAlexander V. Chernikov nl_init_socket(&ss);
4554c91a5dfSAlexander V. Chernikov
4564c91a5dfSAlexander V. Chernikov struct ifmap *ifmap = prepare_ifmap(&ss);
4574c91a5dfSAlexander V. Chernikov struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *));
45844cd85d4SAlexander V. Chernikov for (uint32_t i = 0, num = 0; i < ifmap->size; i++) {
4594c91a5dfSAlexander V. Chernikov if (ifmap->ifaces[i] != NULL) {
4604c91a5dfSAlexander V. Chernikov sorted_ifaces[num++] = ifmap->ifaces[i];
4614c91a5dfSAlexander V. Chernikov if (num == ifmap->count)
4624c91a5dfSAlexander V. Chernikov break;
4634c91a5dfSAlexander V. Chernikov }
4644c91a5dfSAlexander V. Chernikov }
4654c91a5dfSAlexander V. Chernikov qsort(sorted_ifaces, ifmap->count, sizeof(void *), cmp_iface);
4664c91a5dfSAlexander V. Chernikov prepare_ifaddrs(&ss, ifmap);
4674c91a5dfSAlexander V. Chernikov
46844cd85d4SAlexander V. Chernikov for (uint32_t i = 0, num = 0; i < ifmap->count; i++) {
4694c91a5dfSAlexander V. Chernikov struct iface *iface = sorted_ifaces[i];
4704c91a5dfSAlexander V. Chernikov
4714c91a5dfSAlexander V. Chernikov if (!match_iface(args, iface))
4724c91a5dfSAlexander V. Chernikov continue;
4734c91a5dfSAlexander V. Chernikov
47485e0016aSAlexander V. Chernikov ctx->ifname = iface->link.ifla_ifname;
4754c91a5dfSAlexander V. Chernikov
4764c91a5dfSAlexander V. Chernikov if (args->namesonly) {
4774c91a5dfSAlexander V. Chernikov if (num++ != 0)
4784c91a5dfSAlexander V. Chernikov printf(" ");
4794c91a5dfSAlexander V. Chernikov fputs(iface->link.ifla_ifname, stdout);
4804c91a5dfSAlexander V. Chernikov } else if (args->argc == 0)
4816e3a9d7fSAlexander V. Chernikov status_nl(ctx, iface);
48226056fa8SAlexander V. Chernikov else
483bbad5525SAlexander V. Chernikov ifconfig_ioctl(ctx, 0, args->afp);
4844c91a5dfSAlexander V. Chernikov }
4854c91a5dfSAlexander V. Chernikov if (args->namesonly)
4864c91a5dfSAlexander V. Chernikov printf("\n");
4874c91a5dfSAlexander V. Chernikov
4886e3a9d7fSAlexander V. Chernikov close(ctx->io_s);
4894c91a5dfSAlexander V. Chernikov snl_free(&ss);
4904c91a5dfSAlexander V. Chernikov }
4914c91a5dfSAlexander V. Chernikov
492