2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
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>
43 #include <sys/types.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
48 #include <net/ethernet.h>
50 #include <net/if_dl.h>
51 #include <net/if_types.h>
53 #include "ifconfig_netlink.h"
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*/
84 print_bits(const char *btype, uint32_t *v, const int v_count,
85 const char **names, const int n_count)
89 for (int i = 0; i < v_count * 32; i++) {
90 bool is_set = v[i / 32] & (1 << (i % 32));
99 printf("%s", names[i]);
101 printf("%s_%d", btype, i);
109 nl_init_socket(struct snl_state *ss)
111 if (snl_init(ss, NETLINK_ROUTE))
114 if (modfind("netlink") == -1 && errno == ENOENT) {
116 if (kldload("netlink") == -1)
117 err(1, "netlink is not loaded and load attempt failed");
118 if (snl_init(ss, NETLINK_ROUTE))
122 err(1, "unable to open netlink socket");
126 ifconfig_wrapper_nl(struct ifconfig_args *args, int iscreate,
127 const struct afswtch *uafp)
129 struct snl_state ss = {};
130 struct ifconfig_context ctx = {
138 int error = ifconfig(&ctx, iscreate, uafp);
148 struct snl_parsed_addr addr;
152 struct snl_parsed_link link;
161 struct iface **ifaces;
165 * Returns ifmap ifindex->snl_parsed_link.
166 * Memory is allocated using snl temporary buffers
168 static struct ifmap *
169 prepare_ifmap(struct snl_state *ss)
171 struct snl_writer nw = {};
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);
178 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
181 uint32_t nlmsg_seq = hdr->nlmsg_seq;
182 struct ifmap *ifmap = snl_allocz(ss, sizeof(*ifmap));
183 struct snl_errmsg_data e = {};
185 while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
186 struct iface *iface = snl_allocz(ss, sizeof(*iface));
188 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &iface->link))
190 if (iface->link.ifi_index >= ifmap->size) {
191 size_t new_size = MAX(ifmap->size, 32);
193 while (new_size <= iface->link.ifi_index + 1)
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;
201 ifmap->ifaces[iface->link.ifi_index] = iface;
203 iface->idx = ifmap->count;
209 if_nametoindex_nl(struct snl_state *ss, const char *ifname)
211 struct snl_writer nw = {};
212 struct snl_parsed_link_simple link = {};
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);
219 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
222 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
223 if (hdr->nlmsg_type != NL_RTM_NEWLINK)
225 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, &link))
228 return (link.ifi_index);
232 prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap)
234 struct snl_writer nw = {};
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);
241 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
244 uint32_t nlmsg_seq = hdr->nlmsg_seq;
245 struct snl_errmsg_data e = {};
248 while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
249 struct ifa *ifa = snl_allocz(ss, sizeof(*ifa));
251 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &ifa->addr))
254 const uint32_t ifindex = ifa->addr.ifa_index;
255 if (ifindex >= ifmap->size || ifmap->ifaces[ifindex] == NULL)
257 struct iface *iface = ifmap->ifaces[ifindex];
258 ifa->next = iface->ifa;
266 match_iface(struct ifconfig_args *args, struct iface *iface)
268 if_link_t *link = &iface->link;
270 if (args->ifname != NULL && strcmp(args->ifname, link->ifla_ifname))
273 if (!match_if_flags(args, link->ifi_flags))
276 if (!group_member(link->ifla_ifname, args->matchgroup, args->nogroup))
279 if (args->afp == NULL)
282 if (!strcmp(args->afp->af_name, "ether")) {
283 if (link->ifla_address == NULL)
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),
292 return (match_ether(&sdl));
295 for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
296 if (args->afp->af_af == ifa->addr.ifa_family)
303 /* Sort according to the kernel-provided order */
305 cmp_iface(const void *_a, const void *_b)
307 const struct iface *a = *((const void * const *)_a);
308 const struct iface *b = *((const void * const *)_b);
310 return ((a->idx > b->idx) * 2 - 1);
314 cmp_ifaddr(const void *_a, const void *_b)
316 const struct ifa *a = *((const void * const *)_a);
317 const struct ifa *b = *((const void * const *)_b);
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);
325 sort_iface_ifaddrs(struct snl_state *ss, struct iface *iface)
327 if (iface->ifa_count == 0)
330 struct ifa **sorted_ifaddrs = snl_allocz(ss, iface->ifa_count * sizeof(void *));
331 struct ifa *ifa = iface->ifa;
333 for (uint32_t i = 0; i < iface->ifa_count; i++) {
334 struct ifa *ifa_next = ifa->next;
336 sorted_ifaddrs[i] = ifa;
340 qsort(sorted_ifaddrs, iface->ifa_count, sizeof(void *), cmp_ifaddr);
341 ifa = sorted_ifaddrs[0];
343 for (uint32_t i = 1; i < iface->ifa_count; i++) {
344 ifa->next = sorted_ifaddrs[i];
345 ifa = sorted_ifaddrs[i];
350 status_nl(if_ctx *ctx, struct iface *iface)
352 if_link_t *link = &iface->link;
353 struct ifconfig_args *args = ctx->args;
355 printf("%s: ", link->ifla_ifname);
357 printf("flags=%x", link->ifi_flags);
358 print_bits("IFF", &link->ifi_flags, 1, IFFBITS, nitems(IFFBITS));
360 print_metric(ctx->io_s);
361 printf(" mtu %d\n", link->ifla_mtu);
363 if (link->ifla_ifalias != NULL)
364 printf("\tdescription: %s\n", link->ifla_ifalias);
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);
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);
378 sort_iface_ifaddrs(ctx->io_ss, iface);
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);
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;
389 p->af_status(ctx, link, &ifa->addr);
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);
399 print_ifstatus(ctx->io_s);
400 if (args->verbose > 0)
405 get_local_socket(void)
407 int s = socket(AF_LOCAL, SOCK_DGRAM, 0);
410 err(1, "socket(family %u,SOCK_DGRAM)", AF_LOCAL);
415 set_global_ifname(if_link_t *link)
417 size_t iflen = strlcpy(name, link->ifla_ifname, sizeof(name));
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));
425 list_interfaces_nl(struct ifconfig_args *args)
427 struct snl_state ss = {};
428 struct ifconfig_context _ctx = {
430 .io_s = get_local_socket(),
433 struct ifconfig_context *ctx = &_ctx;
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)
446 qsort(sorted_ifaces, ifmap->count, sizeof(void *), cmp_iface);
447 prepare_ifaddrs(&ss, ifmap);
449 for (uint32_t i = 0, num = 0; i < ifmap->count; i++) {
450 struct iface *iface = sorted_ifaces[i];
452 if (!match_iface(args, iface))
455 set_global_ifname(&iface->link);
457 if (args->namesonly) {
460 fputs(iface->link.ifla_ifname, stdout);
461 } else if (args->argc == 0)
462 status_nl(ctx, iface);
464 ifconfig(ctx, 0, args->afp);