From nobody Tue May 16 19:39:32 2023 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4QLRQD68Rsz4BNxM; Tue, 16 May 2023 19:39:32 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4QLRQD5PYZz4G31; Tue, 16 May 2023 19:39:32 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1684265972; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=J/q4VoZIsl6ge4uNw1zON+oR9KGidYDGfS61avic2xw=; b=egOSy0h8z1wZTsfTjXjb2BoUoRQeoZViEnUp0juTyCxkgYHRKRDAQDq23RVpqu3xGrW6x7 FDF2XVY3NH0R6Zwa4I8NyZF1yJ/ziwX2QzDW/17lu7VnEzzfUKcFnDYLvm7REKD34aDAh/ /CxMQCJYQF1tW6IGir4hW7DtffIVE0MGU7b7yIO9oX66GPsnOMLIQ3yN9jOlqwHkmtkFq0 lLr8H6P0FTBX5DftCnPW5u1gVZmnl2Ukk/FCjE+vNq3pza9ElWrNx8u2pCsMxivKvgZnMs yUzR4UvVM4xY97xzbck+tsp0mLbihGBWY85XrgawOtHbQXbAb4jYQqtAGwTnKw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1684265972; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=J/q4VoZIsl6ge4uNw1zON+oR9KGidYDGfS61avic2xw=; b=p6CNqmNFEoLuIrIhUPJA5GxOhMMIgo41cxjwbXV1bG0jEloYnqnPisBsJaH2zEjiq0kUiH KI0gpqXCLfs1CirXs0P4f3irO9kVuvAIYVwYfSOaRfaIY85mF04VXcD1SdtS8sLC94p3iL 1DAYWukuAti/rYHbXNdDdVs51BGq0zEoj9K8jLmhrQ4M+u0IT1+QAxvITVFSmlatjL7M4i w1PYi0y3AIT8XpL6wEjCKMWobZxIRukVKjVRlruKEKjcUoLHm1AaZM1ggDVB6ZuiayrccU uJUglZXcqFnsPjPAB11w2k1rRbAeWnfCvLrq4CniCs5aEiA2DANKuc8GA46RSw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1684265972; a=rsa-sha256; cv=none; b=ghCafr2akqI6bFPYwgkUL/+QhZGCVW0FDYsPZttffJcUjjBHqr9NZiemvR7dKpLdqfK/9W GcEvCcuryCVihnBcHYywwjHPAwTeHhbpXU5BmuXfz3pEIaRvNjt+yj0FhuRBsZMOmFacRR ytWdSUba3uMNTzrCHUImahW43bDaeL7VwV6Rslsw6oXWPjVOfeCBeFbLxyyaR/SKJMoRAo hPqwO+RHxA5rhTaoyeiP8HoplCxX78toEgcGADyTymxcRmJ46Dm5D4A4Np1RHf2UctPV4J 2FhFEZTUciw95qvhOszQMROXdUgPj4iPDAONz6MkJEQ7pXz9/mdYoTx92RQGFQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4QLRQD3nQjzZnf; Tue, 16 May 2023 19:39:32 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 34GJdW3p024052; Tue, 16 May 2023 19:39:32 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 34GJdWN9024051; Tue, 16 May 2023 19:39:32 GMT (envelope-from git) Date: Tue, 16 May 2023 19:39:32 GMT Message-Id: <202305161939.34GJdWN9024051@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: "Alexander V. Chernikov" Subject: git: 4c91a5dfe483 - main - ifconfig: make interface and address listing use Netlink as transport List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: melifaro X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 4c91a5dfe48349c9238eeea6a5b9c11daca4e70d Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by melifaro: URL: https://cgit.FreeBSD.org/src/commit/?id=4c91a5dfe48349c9238eeea6a5b9c11daca4e70d commit 4c91a5dfe48349c9238eeea6a5b9c11daca4e70d Author: Alexander V. Chernikov AuthorDate: 2023-05-10 13:58:21 +0000 Commit: Alexander V. Chernikov CommitDate: 2023-05-16 19:39:13 +0000 ifconfig: make interface and address listing use Netlink as transport Differential Revision: https://reviews.freebsd.org/D40044 --- sbin/ifconfig/Makefile | 6 + sbin/ifconfig/af_inet.c | 57 ++++++ sbin/ifconfig/af_inet6.c | 67 ++++++ sbin/ifconfig/af_link.c | 53 +++++ sbin/ifconfig/ifconfig.c | 42 ++-- sbin/ifconfig/ifconfig.h | 31 +++ sbin/ifconfig/ifconfig_netlink.c | 427 +++++++++++++++++++++++++++++++++++++++ sbin/ifconfig/ifconfig_netlink.h | 37 ++++ 8 files changed, 702 insertions(+), 18 deletions(-) diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile index c48375b8c6d9..9b9df6ab9f78 100644 --- a/sbin/ifconfig/Makefile +++ b/sbin/ifconfig/Makefile @@ -71,6 +71,12 @@ LIBADD+= jail .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 index 6ce11fa2d673..4569c9c362e9 100644 --- a/sbin/ifconfig/af_inet.c +++ b/sbin/ifconfig/af_inet.c @@ -53,6 +53,7 @@ static const char rcsid[] = #include #include "ifconfig.h" +#include "ifconfig_netlink.h" static struct in_aliasreq in_addreq; static struct ifreq in_ridreq; @@ -80,6 +81,7 @@ print_addr(struct sockaddr_in *sin) printf("\tinet %s", addr_buf); } +#ifdef WITHOUT_NETLINK static void in_status(int s __unused, const struct ifaddrs *ifa) { @@ -129,6 +131,57 @@ in_status(int s __unused, const struct ifaddrs *ifa) 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 struct sockaddr_in * +satosin(struct sockaddr *sa) +{ + return ((struct sockaddr_in *)(void *)sa); +} + +static void +in_status_nl(struct ifconfig_args *args __unused, struct io_handler *h, + if_link_t *link, if_addr_t *ifa) +{ + struct sockaddr_in *sin = satosin(ifa->ifa_local); + int plen = ifa->ifa_prefixlen; + + print_addr(sin); + + if (link->ifi_flags & IFF_POINTOPOINT) { + struct sockaddr_in *dst = satosin(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 *brd = satosin(ifa->ifa_broadcast); + if (brd != NULL) + printf(" broadcast %s", inet_ntoa(brd->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 +288,11 @@ in_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres) 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 index 49049ba2c376..0f4e0e75e44a 100644 --- a/sbin/ifconfig/af_inet6.c +++ b/sbin/ifconfig/af_inet6.c @@ -57,6 +57,7 @@ static const char rcsid[] = #include /* 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 @@ print_lifetime(const char *prepend, time_t px_time, struct timespec *now) 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,67 @@ in6_status(int s __unused, const struct ifaddrs *ifa) 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 struct sockaddr_in6 * +satosin6(struct sockaddr *sa) +{ + return ((struct sockaddr_in6 *)(void *)sa); +} + +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; + uint32_t scopeid; + + if (ifa->ifa_local == NULL) { + /* Non-P2P address */ + scopeid = satosin6(ifa->ifa_address)->sin6_scope_id; + print_addr(satosin6(ifa->ifa_address)); + } else { + scopeid = satosin6(ifa->ifa_local)->sin6_scope_id; + print_addr(satosin6(ifa->ifa_local)); + print_p2p(satosin6(ifa->ifa_address)); + } + + print_mask(plen); + print_flags(ifa->ifaf_flags); + + if (scopeid != 0) + printf(" scopeid 0x%x", scopeid); + + 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 +594,11 @@ static struct cmd inet6_cmds[] = { 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 index f651ddc51cb4..52295453b4f0 100644 --- a/sbin/ifconfig/af_link.c +++ b/sbin/ifconfig/af_link.c @@ -51,6 +51,7 @@ static const char rcsid[] = #include #include "ifconfig.h" +#include "ifconfig_netlink.h" static struct ifreq link_ridreq; @@ -90,6 +91,7 @@ print_pcp(int s) 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,45 @@ pcp: print_pcp(s); } +#else +static uint8_t +convert_iftype(uint8_t iftype) +{ + switch (iftype) { + case IFT_IEEE8023ADLAG: + return (IFT_ETHER); + case IFT_INFINIBANDLAG: + return (IFT_INFINIBAND); + } + return (iftype); +} + +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 = convert_iftype(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"); + } + } + if (convert_iftype(link->ifi_type) == IFT_ETHER) + print_pcp(h->s); +} +#endif + static void link_getaddr(const char *addr, int which) { @@ -180,7 +221,11 @@ link_getaddr(const char *addr, int which) 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 +233,11 @@ static struct afswtch af_link = { 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 +245,11 @@ static struct afswtch af_ether = { 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.c b/sbin/ifconfig/ifconfig.c index 15a40f1c5601..7872c2b336a5 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -108,20 +108,14 @@ int exit_code = 0; /* 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 @@ void printifnamemaybe() 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 @@ match_afp(const struct afswtch *afp, int sa_family, const struct sockaddr_dl *sd 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 @@ match_if_flags(struct ifconfig_args *args, int if_flags) 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 @@ list_interfaces(struct ifconfig_args *args) 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 @@ af_getbyname(const char *name) return NULL; } -static struct afswtch * +struct afswtch * af_getbyfamily(int af) { struct afswtch *afp; @@ -843,7 +847,7 @@ af_getbyfamily(int af) return NULL; } -static void +void af_other_status(int s) { struct afswtch *afp; @@ -933,7 +937,7 @@ static void setifdstaddr(const char *, int, int, const struct afswtch *); 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 @@ print_ifcap_nv(struct ifconfig_args *args, int s) 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 @@ print_ifcap(struct ifconfig_args *args, int s) } } -static void +void print_ifstatus(int s) { struct ifstat ifs; @@ -1543,13 +1547,14 @@ print_ifstatus(int s) 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 @@ status(struct ifconfig_args *args, const struct sockaddr_dl *sdl, close(s); return; } +#endif -static void +void tunnel_status(int s) { af_all_tunnel_status(s); diff --git a/sbin/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h index e65c26a031e6..70a2c92199b6 100644 --- a/sbin/ifconfig/ifconfig.h +++ b/sbin/ifconfig/ifconfig.h @@ -150,6 +150,20 @@ enum { 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 @@ struct afswtch { * 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 @@ void sfp_status(int s, struct ifreq *ifr, int verbose); 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_netlink.c b/sbin/ifconfig/ifconfig_netlink.c new file mode 100644 index 000000000000..26a42b5866c5 --- /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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#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); +} + diff --git a/sbin/ifconfig/ifconfig_netlink.h b/sbin/ifconfig/ifconfig_netlink.h new file mode 100644 index 000000000000..1c762619a483 --- /dev/null +++ b/sbin/ifconfig/ifconfig_netlink.h @@ -0,0 +1,37 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2023 Alexander V. Chernikov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions *** 30 LINES SKIPPED ***