Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F168241890
D40044.id121811.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
25 KB
Referenced Files
None
Subscribers
None
D40044.id121811.diff
View Options
diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile
--- a/sbin/ifconfig/Makefile
+++ b/sbin/ifconfig/Makefile
@@ -71,6 +71,12 @@
.endif
LIBADD+= nv
+.if ${MK_NETLINK_SUPPORT} != "no"
+SRCS+= ifconfig_netlink.c
+.else
+CFLAGS+=-DWITHOUT_NETLINK
+.endif
+
MAN= ifconfig.8
CFLAGS+= -Wall -Wmissing-prototypes -Wcast-qual -Wwrite-strings -Wnested-externs
diff --git a/sbin/ifconfig/af_inet.c b/sbin/ifconfig/af_inet.c
--- a/sbin/ifconfig/af_inet.c
+++ b/sbin/ifconfig/af_inet.c
@@ -53,6 +53,7 @@
#include <netdb.h>
#include "ifconfig.h"
+#include "ifconfig_netlink.h"
static struct in_aliasreq in_addreq;
static struct ifreq in_ridreq;
@@ -80,6 +81,7 @@
printf("\tinet %s", addr_buf);
}
+#ifdef WITHOUT_NETLINK
static void
in_status(int s __unused, const struct ifaddrs *ifa)
{
@@ -129,6 +131,52 @@
putchar('\n');
}
+#else
+static struct in_addr
+get_mask(int plen)
+{
+ struct in_addr a;
+
+ a.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0);
+
+ return (a);
+}
+
+static void
+in_status_nl(struct ifconfig_args *args __unused, struct io_handler *h,
+ if_link_t *link, if_addr_t *ifa)
+{
+ int plen = ifa->ifa_prefixlen;
+ struct sockaddr *a = ifa->ifa_local ? ifa->ifa_local : ifa->ifa_address;
+ struct sockaddr_in *sin = (struct sockaddr_in *)a;
+
+ print_addr(sin);
+
+ if (link->ifi_flags & IFF_POINTOPOINT) {
+ struct sockaddr_in *dst = (struct sockaddr_in *)ifa->ifa_address;
+
+ printf(" --> %s", inet_ntoa(dst->sin_addr));
+ }
+ if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) {
+ printf("/%d", plen);
+ } else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0)
+ printf(" netmask %s", inet_ntoa(get_mask(plen)));
+ else
+ printf(" netmask 0x%lx", (unsigned long)ntohl(get_mask(plen).s_addr));
+
+ if ((link->ifi_flags & IFF_BROADCAST) && plen != 0) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)ifa->ifa_broadcast;
+ if (sin != NULL)
+ printf(" broadcast %s", inet_ntoa(sin->sin_addr));
+ }
+
+ if (ifa->ifaf_vhid != 0)
+ printf(" vhid %d", ifa->ifaf_vhid);
+
+ putchar('\n');
+}
+#endif
+
#define SIN(x) ((struct sockaddr_in *) &(x))
static struct sockaddr_in *sintab[] = {
SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr),
@@ -235,7 +283,11 @@
static struct afswtch af_inet = {
.af_name = "inet",
.af_af = AF_INET,
+#ifdef WITHOUT_NETLINK
.af_status = in_status,
+#else
+ .af_status_nl = in_status_nl,
+#endif
.af_getaddr = in_getaddr,
.af_postproc = in_postproc,
.af_status_tunnel = in_status_tunnel,
diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c
--- a/sbin/ifconfig/af_inet6.c
+++ b/sbin/ifconfig/af_inet6.c
@@ -57,6 +57,7 @@
#include <netinet6/nd6.h> /* Define ND6_INFINITE_LIFETIME */
#include "ifconfig.h"
+#include "ifconfig_netlink.h"
static struct in6_ifreq in6_ridreq;
static struct in6_aliasreq in6_addreq =
@@ -242,6 +243,7 @@
printf(" %s", px_time < now->tv_sec ? "0" : sec2str(px_time - now->tv_sec));
}
+#ifdef WITHOUT_NETLINK
static void
in6_status(int s __unused, const struct ifaddrs *ifa)
{
@@ -313,6 +315,61 @@
putchar('\n');
}
+#else
+static void
+show_lifetime(struct ifa_cacheinfo *ci)
+{
+ struct timespec now;
+ uint32_t pl, vl;
+
+ if (ci == NULL)
+ return;
+
+ int count = ci->ifa_prefered != ND6_INFINITE_LIFETIME;
+ count += ci->ifa_valid != ND6_INFINITE_LIFETIME;
+ if (count == 0)
+ return;
+
+ pl = (ci->ifa_prefered == ND6_INFINITE_LIFETIME) ? 0 : ci->ifa_prefered;
+ vl = (ci->ifa_valid == ND6_INFINITE_LIFETIME) ? 0 : ci->ifa_valid;
+
+ clock_gettime(CLOCK_MONOTONIC_FAST, &now);
+ print_lifetime("pltime", pl + now.tv_sec, &now);
+ print_lifetime("vltime", vl + now.tv_sec, &now);
+}
+
+static void
+in6_status_nl(struct ifconfig_args *args __unused, struct io_handler *h,
+ if_link_t *link, if_addr_t *ifa)
+{
+ int plen = ifa->ifa_prefixlen;
+ struct sockaddr *a = ifa->ifa_local ? ifa->ifa_local : ifa->ifa_address;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)a;
+
+ print_addr(sin6);
+
+ if (link->ifi_flags & IFF_POINTOPOINT) {
+ struct sockaddr_in6 *sin = (struct sockaddr_in6 *)ifa->ifa_address;
+
+ if (sin != NULL && sin->sin6_family == AF_INET6)
+ print_p2p(sin);
+ }
+
+ print_mask(plen);
+ print_flags(ifa->ifaf_flags);
+
+ if (sin6->sin6_scope_id != 0)
+ printf(" scopeid 0x%x", sin6->sin6_scope_id);
+
+ show_lifetime(ifa->ifa_cacheinfo);
+
+ if (ifa->ifaf_vhid != 0)
+ printf(" vhid %d", ifa->ifaf_vhid);
+
+ putchar('\n');
+}
+#endif
+
#define SIN6(x) ((struct sockaddr_in6 *) &(x))
static struct sockaddr_in6 *sin6tab[] = {
SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr),
@@ -531,7 +588,11 @@
static struct afswtch af_inet6 = {
.af_name = "inet6",
.af_af = AF_INET6,
+#ifdef WITHOUT_NETLINK
.af_status = in6_status,
+#else
+ .af_status_nl = in6_status_nl,
+#endif
.af_getaddr = in6_getaddr,
.af_getprefix = in6_getprefix,
.af_other_status = nd6_status,
diff --git a/sbin/ifconfig/af_link.c b/sbin/ifconfig/af_link.c
--- a/sbin/ifconfig/af_link.c
+++ b/sbin/ifconfig/af_link.c
@@ -51,6 +51,7 @@
#include <net/ethernet.h>
#include "ifconfig.h"
+#include "ifconfig_netlink.h"
static struct ifreq link_ridreq;
@@ -90,6 +91,7 @@
printf("\tpcp %d\n", ifr.ifr_lan_pcp);
}
+#ifdef WITHOUT_NETLINK
static void
link_status(int s __unused, const struct ifaddrs *ifa)
{
@@ -143,6 +145,32 @@
print_pcp(s);
}
+#else
+static void
+link_status_nl(struct ifconfig_args *args __unused, struct io_handler *h,
+ if_link_t *link, if_addr_t *ifa __unused)
+{
+ if (link->ifla_address != NULL) {
+ struct sockaddr_dl sdl = {
+ .sdl_len = sizeof(struct sockaddr_dl),
+ .sdl_family = AF_LINK,
+ .sdl_type = link->ifi_type,
+ .sdl_alen = NLA_DATA_LEN(link->ifla_address),
+ };
+ memcpy(LLADDR(&sdl), NLA_DATA(link->ifla_address), sdl.sdl_alen);
+ print_lladdr(&sdl);
+
+ if (link->iflaf_orig_hwaddr != NULL) {
+ struct nlattr *hwaddr = link->iflaf_orig_hwaddr;
+
+ if (memcmp(NLA_DATA(hwaddr), NLA_DATA(link->ifla_address), sdl.sdl_alen))
+ print_ether((struct ether_addr *)NLA_DATA(hwaddr), "hwaddr");
+ }
+ }
+ print_pcp(h->s);
+}
+#endif
+
static void
link_getaddr(const char *addr, int which)
{
@@ -180,7 +208,11 @@
static struct afswtch af_link = {
.af_name = "link",
.af_af = AF_LINK,
+#ifdef WITHOUT_NETLINK
.af_status = link_status,
+#else
+ .af_status_nl = link_status_nl,
+#endif
.af_getaddr = link_getaddr,
.af_aifaddr = SIOCSIFLLADDR,
.af_addreq = &link_ridreq,
@@ -188,7 +220,11 @@
static struct afswtch af_ether = {
.af_name = "ether",
.af_af = AF_LINK,
+#ifdef WITHOUT_NETLINK
.af_status = link_status,
+#else
+ .af_status_nl = link_status_nl,
+#endif
.af_getaddr = link_getaddr,
.af_aifaddr = SIOCSIFLLADDR,
.af_addreq = &link_ridreq,
@@ -196,7 +232,11 @@
static struct afswtch af_lladdr = {
.af_name = "lladdr",
.af_af = AF_LINK,
+#ifdef WITHOUT_NETLINK
.af_status = link_status,
+#else
+ .af_status_nl = link_status_nl,
+#endif
.af_getaddr = link_getaddr,
.af_aifaddr = SIOCSIFLLADDR,
.af_addreq = &link_ridreq,
diff --git a/sbin/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h
--- a/sbin/ifconfig/ifconfig.h
+++ b/sbin/ifconfig/ifconfig.h
@@ -150,6 +150,20 @@
DSTADDR,
};
+struct snl_state;
+struct snl_parsed_addr;
+struct snl_parsed_link;
+typedef struct snl_parsed_link if_link_t;
+typedef struct snl_parsed_addr if_addr_t;
+struct ifconfig_args;
+struct io_handler {
+ int s; /* socket to use for ioctls */
+ struct snl_state *ss; /* NETLINK_ROUTE snl(3) socket */
+};
+
+typedef void af_status_nl_f(struct ifconfig_args *args, struct io_handler *h,
+ if_link_t *link, if_addr_t *ifa);
+
struct afswtch {
const char *af_name; /* as given on cmd line, e.g. "inet" */
short af_af; /* AF_* */
@@ -162,7 +176,11 @@
* is defined then it is invoked after all address status
* is presented.
*/
+#ifndef WITHOUT_NETLINK
+ af_status_nl_f *af_status_nl;
+#else
void (*af_status)(int, const struct ifaddrs *);
+#endif
void (*af_other_status)(int);
/* parse address method */
void (*af_getaddr)(const char *, int);
@@ -238,6 +256,19 @@
struct sockaddr_dl;
bool match_ether(const struct sockaddr_dl *sdl);
+bool match_if_flags(struct ifconfig_args *args, int if_flags);
+int ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *uafp);
+bool group_member(const char *ifname, const char *match, const char *nomatch);
+void print_ifcap(struct ifconfig_args *args, int s);
+void tunnel_status(int s);
+struct afswtch *af_getbyfamily(int af);
+void af_other_status(int s);
+void print_ifstatus(int s);
+void print_metric(int s);
+
+/* Netlink-related functions */
+void list_interfaces_nl(struct ifconfig_args *args);
+
/*
* XXX expose this so modules that neeed to know of any pending
* operations on ifmedia can avoid cmd line ordering confusion.
diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c
--- a/sbin/ifconfig/ifconfig.c
+++ b/sbin/ifconfig/ifconfig.c
@@ -108,20 +108,14 @@
/* Formatter Strings */
char *f_inet, *f_inet6, *f_ether, *f_addr;
-static bool group_member(const char *ifname, const char *match,
- const char *nomatch);
-static int ifconfig(int argc, char *const *argv, int iscreate,
- const struct afswtch *afp);
+static void list_interfaces_ioctl(struct ifconfig_args *args);
static void status(struct ifconfig_args *args, const struct sockaddr_dl *sdl,
struct ifaddrs *ifa);
-static void tunnel_status(int s);
static _Noreturn void usage(void);
static int getifflags(const char *ifname, int us, bool err_ok);
static struct afswtch *af_getbyname(const char *name);
-static struct afswtch *af_getbyfamily(int af);
-static void af_other_status(int);
void printifnamemaybe(void);
@@ -403,7 +397,15 @@
printf("%s\n", name);
}
-static void list_interfaces(struct ifconfig_args *args);
+static void
+list_interfaces(struct ifconfig_args *args)
+{
+#ifdef WITHOUT_NETLINK
+ list_interfaces_ioctl(args);
+#else
+ list_interfaces_nl(args);
+#endif
+}
int
main(int argc, char *argv[])
@@ -651,7 +653,7 @@
return (afp->af_af == sa_family);
}
-static bool
+bool
match_if_flags(struct ifconfig_args *args, int if_flags)
{
if ((if_flags & IFF_CANTCONFIG) != 0)
@@ -663,8 +665,9 @@
return (true);
}
+#ifdef WITHOUT_NETLINK
static void
-list_interfaces(struct ifconfig_args *args)
+list_interfaces_ioctl(struct ifconfig_args *args)
{
struct ifa_queue q = TAILQ_HEAD_INITIALIZER(q);
struct ifaddrs *ifap, *sifap, *ifa;
@@ -743,13 +746,14 @@
printf("\n");
freeifaddrs(ifap);
}
+#endif
/*
* Returns true if an interface should be listed because any its groups
* matches shell pattern "match" and none of groups matches pattern "nomatch".
* If any pattern is NULL, corresponding condition is skipped.
*/
-static bool
+bool
group_member(const char *ifname, const char *match, const char *nomatch)
{
static int sock = -1;
@@ -832,7 +836,7 @@
return NULL;
}
-static struct afswtch *
+struct afswtch *
af_getbyfamily(int af)
{
struct afswtch *afp;
@@ -843,7 +847,7 @@
return NULL;
}
-static void
+void
af_other_status(int s)
{
struct afswtch *afp;
@@ -933,7 +937,7 @@
static const struct cmd setifdstaddr_cmd =
DEF_CMD("ifdstaddr", 0, setifdstaddr);
-static int
+int
ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *uafp)
{
const struct afswtch *afp, *nafp;
@@ -1514,7 +1518,7 @@
Perror("ioctl (SIOCGIFCAP)");
}
-static void
+void
print_ifcap(struct ifconfig_args *args, int s)
{
if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) != 0)
@@ -1533,7 +1537,7 @@
}
}
-static void
+void
print_ifstatus(int s)
{
struct ifstat ifs;
@@ -1543,13 +1547,14 @@
printf("%s", ifs.ascii);
}
-static void
+void
print_metric(int s)
{
if (ioctl(s, SIOCGIFMETRIC, &ifr) != -1)
printf(" metric %d", ifr.ifr_metric);
}
+#ifdef WITHOUT_NETLINK
static void
print_mtu(int s)
{
@@ -1658,8 +1663,9 @@
close(s);
return;
}
+#endif
-static void
+void
tunnel_status(int s)
{
af_all_tunnel_status(s);
diff --git a/sbin/ifconfig/ifconfig_netlink.h b/sbin/ifconfig/ifconfig_netlink.h
new file mode 100644
--- /dev/null
+++ b/sbin/ifconfig/ifconfig_netlink.h
@@ -0,0 +1,36 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2023 Alexander V. Chernikov <melifaro@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <netlink/netlink.h>
+#include <netlink/netlink_route.h>
+#include <netlink/netlink_snl.h>
+#include <netlink/netlink_snl_route.h>
+#include <netlink/netlink_snl_route_compat.h>
+#include <netlink/netlink_snl_route_parsers.h>
+
diff --git a/sbin/ifconfig/ifconfig_netlink.c b/sbin/ifconfig/ifconfig_netlink.c
new file mode 100644
--- /dev/null
+++ b/sbin/ifconfig/ifconfig_netlink.c
@@ -0,0 +1,427 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include <sys/bitcount.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include "ifconfig.h"
+#include "ifconfig_netlink.h"
+
+static const char *IFFBITS[] = {
+ "UP", /* 00:0x1 IFF_UP*/
+ "BROADCAST", /* 01:0x2 IFF_BROADCAST*/
+ "DEBUG", /* 02:0x4 IFF_DEBUG*/
+ "LOOPBACK", /* 03:0x8 IFF_LOOPBACK*/
+ "POINTOPOINT", /* 04:0x10 IFF_POINTOPOINT*/
+ "NEEDSEPOCH", /* 05:0x20 IFF_NEEDSEPOCH*/
+ "RUNNING", /* 06:0x40 IFF_DRV_RUNNING*/
+ "NOARP", /* 07:0x80 IFF_NOARP*/
+ "PROMISC", /* 08:0x100 IFF_PROMISC*/
+ "ALLMULTI", /* 09:0x200 IFF_ALLMULTI*/
+ "DRV_OACTIVE", /* 10:0x400 IFF_DRV_OACTIVE*/
+ "SIMPLEX", /* 11:0x800 IFF_SIMPLEX*/
+ "LINK0", /* 12:0x1000 IFF_LINK0*/
+ "LINK1", /* 13:0x2000 IFF_LINK1*/
+ "LINK2", /* 14:0x4000 IFF_LINK2*/
+ "MULTICAST", /* 15:0x8000 IFF_MULTICAST*/
+ "CANTCONFIG", /* 16:0x10000 IFF_CANTCONFIG*/
+ "PPROMISC", /* 17:0x20000 IFF_PPROMISC*/
+ "MONITOR", /* 18:0x40000 IFF_MONITOR*/
+ "STATICARP", /* 19:0x80000 IFF_STATICARP*/
+ "STICKYARP", /* 20:0x100000 IFF_STICKYARP*/
+ "DYING", /* 21:0x200000 IFF_DYING*/
+ "RENAMING", /* 22:0x400000 IFF_RENAMING*/
+ "NOGROUP", /* 23:0x800000 IFF_NOGROUP*/
+ "LOWER_UP", /* 24:0x1000000 IFF_NETLINK_1*/
+};
+
+static void
+print_bits(const char *btype, uint32_t *v, const int v_count,
+ const char **names, const int n_count)
+{
+ int num = 0;
+
+ for (int i = 0; i < v_count * 32; i++) {
+ bool is_set = v[i / 32] & (1 << (i % 32));
+ if (i == 31)
+ v++;
+ if (is_set) {
+ if (num++ == 0)
+ printf("<");
+ if (num != 1)
+ printf(",");
+ if (i < n_count)
+ printf("%s", names[i]);
+ else
+ printf("%s_%d", btype, i);
+ }
+ }
+ if (num > 0)
+ printf(">");
+}
+
+static void
+nl_init_socket(struct snl_state *ss)
+{
+ if (snl_init(ss, NETLINK_ROUTE))
+ return;
+
+ if (modfind("netlink") == -1 && errno == ENOENT) {
+ /* Try to load */
+ if (kldload("netlink") == -1)
+ err(1, "netlink is not loaded and load attempt failed");
+ if (snl_init(ss, NETLINK_ROUTE))
+ return;
+ }
+
+ err(1, "unable to open netlink socket");
+}
+
+struct ifa {
+ struct ifa *next;
+ uint32_t count;
+ uint32_t idx;
+ struct snl_parsed_addr addr;
+};
+
+struct iface {
+ struct snl_parsed_link link;
+ struct ifa *ifa;
+ uint32_t ifa_count;
+ uint32_t idx;
+};
+
+struct ifmap {
+ uint32_t size;
+ uint32_t count;
+ struct iface **ifaces;
+};
+
+/*
+ * Returns ifmap ifindex->snl_parsed_link.
+ * Memory is allocated using snl temporary buffers
+ */
+static struct ifmap *
+prepare_ifmap(struct snl_state *ss)
+{
+ struct snl_writer nw = {};
+
+ snl_init_writer(ss, &nw);
+ struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
+ hdr->nlmsg_flags |= NLM_F_DUMP;
+ snl_reserve_msg_object(&nw, struct ifinfomsg);
+
+ if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
+ return (NULL);
+
+ uint32_t nlmsg_seq = hdr->nlmsg_seq;
+ struct ifmap *ifmap = snl_allocz(ss, sizeof(*ifmap));
+ struct snl_errmsg_data e = {};
+
+ while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
+ struct iface *iface = snl_allocz(ss, sizeof(*iface));
+
+ if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &iface->link))
+ continue;
+ if (iface->link.ifi_index >= ifmap->size) {
+ size_t new_size = MAX(ifmap->size, 32);
+
+ while (new_size <= iface->link.ifi_index + 1)
+ new_size *= 2;
+
+ struct iface **ifaces= snl_allocz(ss, new_size * sizeof(void *));
+ memcpy(ifaces, ifmap->ifaces, ifmap->size * sizeof(void *));
+ ifmap->ifaces = ifaces;
+ ifmap->size = new_size;
+ }
+ ifmap->ifaces[iface->link.ifi_index] = iface;
+ ifmap->count++;
+ iface->idx = ifmap->count;
+ }
+ return (ifmap);
+}
+
+static void
+prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap)
+{
+ struct snl_writer nw = {};
+
+ snl_init_writer(ss, &nw);
+ struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETADDR);
+ hdr->nlmsg_flags |= NLM_F_DUMP;
+ snl_reserve_msg_object(&nw, struct ifaddrmsg);
+
+ if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
+ return;
+
+ uint32_t nlmsg_seq = hdr->nlmsg_seq;
+ struct snl_errmsg_data e = {};
+ uint32_t count = 0;
+
+ while ((hdr = snl_read_reply_multi(ss, nlmsg_seq, &e)) != NULL) {
+ struct ifa *ifa = snl_allocz(ss, sizeof(*ifa));
+
+ if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &ifa->addr))
+ continue;
+
+ const uint32_t ifindex = ifa->addr.ifa_index;
+ if (ifindex >= ifmap->size || ifmap->ifaces[ifindex] == NULL)
+ continue;
+ struct iface *iface = ifmap->ifaces[ifindex];
+ ifa->next = iface->ifa;
+ ifa->count = ++count;
+ iface->ifa = ifa;
+ iface->ifa_count++;
+ }
+}
+
+static bool
+match_iface(struct ifconfig_args *args, struct iface *iface)
+{
+ if_link_t *link = &iface->link;
+
+ if (args->ifname != NULL && strcmp(args->ifname, link->ifla_ifname))
+ return (false);
+
+ if (!match_if_flags(args, link->ifi_flags))
+ return (false);
+
+ if (!group_member(link->ifla_ifname, args->matchgroup, args->nogroup))
+ return (false);
+
+ if (args->afp == NULL)
+ return (true);
+
+ if (!strcmp(args->afp->af_name, "ether")) {
+ if (link->ifla_address == NULL)
+ return (false);
+
+ struct sockaddr_dl sdl = {
+ .sdl_len = sizeof(struct sockaddr_dl),
+ .sdl_family = AF_LINK,
+ .sdl_type = link->ifi_type,
+ .sdl_alen = NLA_DATA_LEN(link->ifla_address),
+ };
+ return (match_ether(&sdl));
+ }
+
+ for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
+ if (args->afp->af_af == ifa->addr.ifa_family)
+ return (true);
+ }
+
+ return (false);
+}
+
+/* Sort according to the kernel-provided order */
+static int
+cmp_iface(const void *_a, const void *_b)
+{
+ const struct iface *a = *((const void * const *)_a);
+ const struct iface *b = *((const void * const *)_b);
+
+ return ((a->idx > b->idx) * 2 - 1);
+}
+
+static int
+cmp_ifaddr(const void *_a, const void *_b)
+{
+ const struct ifa *a = *((const void * const *)_a);
+ const struct ifa *b = *((const void * const *)_b);
+
+ if (a->addr.ifa_family != b->addr.ifa_family)
+ return ((a->addr.ifa_family > b->addr.ifa_family) * 2 - 1);
+ return ((a->idx > b->idx) * 2 - 1);
+}
+
+static void
+sort_iface_ifaddrs(struct snl_state *ss, struct iface *iface)
+{
+ if (iface->ifa_count == 0)
+ return;
+
+ struct ifa **sorted_ifaddrs = snl_allocz(ss, iface->ifa_count * sizeof(void *));
+ struct ifa *ifa = iface->ifa;
+
+ for (int i = 0; i < iface->ifa_count; i++) {
+ struct ifa *ifa_next = ifa->next;
+
+ sorted_ifaddrs[i] = ifa;
+ ifa->next = NULL;
+ ifa = ifa_next;
+ }
+ qsort(sorted_ifaddrs, iface->ifa_count, sizeof(void *), cmp_ifaddr);
+ ifa = sorted_ifaddrs[0];
+ iface->ifa = ifa;
+ for (int i = 1; i < iface->ifa_count; i++) {
+ ifa->next = sorted_ifaddrs[i];
+ ifa = sorted_ifaddrs[i];
+ }
+}
+
+static void
+status_nl(struct ifconfig_args *args, struct io_handler *h, struct iface *iface)
+{
+ if_link_t *link = &iface->link;
+
+ printf("%s: ", link->ifla_ifname);
+
+ printf("flags=%x", link->ifi_flags);
+ print_bits("IFF", &link->ifi_flags, 1, IFFBITS, nitems(IFFBITS));
+
+ print_metric(h->s);
+ printf(" mtu %d\n", link->ifla_mtu);
+
+ if (link->ifla_ifalias != NULL)
+ printf("\tdescription: %s\n", link->ifla_ifalias);
+
+ /* TODO: convert to netlink */
+ strlcpy(ifr.ifr_name, link->ifla_ifname, sizeof(ifr.ifr_name));
+ print_ifcap(args, h->s);
+ tunnel_status(h->s);
+
+ if (args->allfamilies | (args->afp != NULL && args->afp->af_af == AF_LINK)) {
+ /* Start with link-level */
+ const struct afswtch *p = af_getbyfamily(AF_LINK);
+ if (p != NULL && link->ifla_address != NULL)
+ p->af_status_nl(args, h, link, NULL);
+ }
+
+ sort_iface_ifaddrs(h->ss, iface);
+
+ for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) {
+ if (args->allfamilies) {
+ const struct afswtch *p = af_getbyfamily(ifa->addr.ifa_family);
+
+ if (p != NULL)
+ p->af_status_nl(args, h, link, &ifa->addr);
+ } else if (args->afp->af_af == ifa->addr.ifa_family) {
+ const struct afswtch *p = args->afp;
+
+ p->af_status_nl(args, h, link, &ifa->addr);
+ }
+ }
+
+ /* TODO: convert to netlink */
+ if (args->allfamilies)
+ af_other_status(h->s);
+ else if (args->afp->af_other_status != NULL)
+ args->afp->af_other_status(h->s);
+
+ print_ifstatus(h->s);
+ if (args->verbose > 0)
+ sfp_status(h->s, &ifr, args->verbose);
+}
+
+static int
+get_local_socket(void)
+{
+ int s = socket(AF_LOCAL, SOCK_DGRAM, 0);
+
+ if (s < 0)
+ err(1, "socket(family %u,SOCK_DGRAM)", AF_LOCAL);
+ return (s);
+}
+
+static void
+set_global_ifname(if_link_t *link)
+{
+ int iflen = strlcpy(name, link->ifla_ifname, sizeof(name));
+ if (iflen >= sizeof(name))
+ errx(1, "%s: cloning name too long", link->ifla_ifname);
+ strlcpy(ifr.ifr_name, link->ifla_ifname, sizeof(ifr.ifr_name));
+}
+
+void
+list_interfaces_nl(struct ifconfig_args *args)
+{
+ struct snl_state ss = {};
+
+ nl_init_socket(&ss);
+
+ struct ifmap *ifmap = prepare_ifmap(&ss);
+ struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *));
+ for (int i = 0, num = 0; i < ifmap->size; i++) {
+ if (ifmap->ifaces[i] != NULL) {
+ sorted_ifaces[num++] = ifmap->ifaces[i];
+ if (num == ifmap->count)
+ break;
+ }
+ }
+ qsort(sorted_ifaces, ifmap->count, sizeof(void *), cmp_iface);
+ prepare_ifaddrs(&ss, ifmap);
+
+ struct io_handler h = {
+ .s = get_local_socket(),
+ .ss = &ss,
+ };
+
+ for (int i = 0, num = 0; i < ifmap->count; i++) {
+ struct iface *iface = sorted_ifaces[i];
+
+ if (!match_iface(args, iface))
+ continue;
+
+ set_global_ifname(&iface->link);
+
+ if (args->namesonly) {
+ if (num++ != 0)
+ printf(" ");
+ fputs(iface->link.ifla_ifname, stdout);
+ } else if (args->argc == 0)
+ status_nl(args, &h, iface);
+ else
+ ifconfig(args->argc, args->argv, 0, args->afp);
+ }
+ if (args->namesonly)
+ printf("\n");
+
+ close(h.s);
+ snl_free(&ss);
+}
+
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Aug 28, 3:20 AM (2 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37432230
Default Alt Text
D40044.id121811.diff (25 KB)
Attached To
Mode
D40044: ifconfig: make interface & address listing use Netlink
Attached
Detach File
Event Timeline
Log In to Comment