diff --git a/sbin/ifconfig/af_link.c b/sbin/ifconfig/af_link.c index 17de87539d9a..2a53daeb7d01 100644 --- a/sbin/ifconfig/af_link.c +++ b/sbin/ifconfig/af_link.c @@ -1,285 +1,274 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" #include "ifconfig_netlink.h" static struct ifreq link_ridreq; extern char *f_ether; static void print_ether(const struct ether_addr *addr, const char *prefix) { char *ether_format = ether_ntoa(addr); if (f_ether != NULL) { if (strcmp(f_ether, "dash") == 0) { char *format_char; while ((format_char = strchr(ether_format, ':')) != NULL) { *format_char = '-'; } } else if (strcmp(f_ether, "dotted") == 0) { /* Indices 0 and 1 is kept as is. */ ether_format[ 2] = ether_format[ 3]; ether_format[ 3] = ether_format[ 4]; ether_format[ 4] = '.'; ether_format[ 5] = ether_format[ 6]; ether_format[ 6] = ether_format[ 7]; ether_format[ 7] = ether_format[ 9]; ether_format[ 8] = ether_format[10]; ether_format[ 9] = '.'; ether_format[10] = ether_format[12]; ether_format[11] = ether_format[13]; ether_format[12] = ether_format[15]; ether_format[13] = ether_format[16]; ether_format[14] = '\0'; } } printf("\t%s %s\n", prefix, ether_format); } static void print_lladdr(struct sockaddr_dl *sdl) { if (match_ether(sdl)) { print_ether((struct ether_addr *)LLADDR(sdl), "ether"); } else { int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; printf("\tlladdr %s\n", link_ntoa(sdl) + n); } } static void print_pcp(if_ctx *ctx) { struct ifreq ifr = {}; if (ioctl_ctx_ifr(ctx, SIOCGLANPCP, &ifr) == 0 && ifr.ifr_lan_pcp != IFNET_PCP_NONE) printf("\tpcp %d\n", ifr.ifr_lan_pcp); } #ifdef WITHOUT_NETLINK static void link_status(if_ctx *ctx, const struct ifaddrs *ifa) { /* XXX no const 'cuz LLADDR is defined wrong */ struct sockaddr_dl *sdl; struct ifreq ifr; int rc, sock_hw; static const u_char laggaddr[6] = {0}; sdl = satosdl(ifa->ifa_addr); if (sdl == NULL || sdl->sdl_alen == 0) return; print_lladdr(sdl); /* * Best-effort (i.e. failures are silent) to get original * hardware address, as read by NIC driver at attach time. Only * applies to Ethernet NICs (IFT_ETHER). However, laggX * interfaces claim to be IFT_ETHER, and re-type their component * Ethernet NICs as IFT_IEEE8023ADLAG. So, check for both. If * the MAC is zeroed, then it's actually a lagg. */ if ((sdl->sdl_type != IFT_ETHER && sdl->sdl_type != IFT_IEEE8023ADLAG) || sdl->sdl_alen != ETHER_ADDR_LEN) return; strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name)); memcpy(&ifr.ifr_addr, ifa->ifa_addr, sizeof(ifa->ifa_addr->sa_len)); ifr.ifr_addr.sa_family = AF_LOCAL; if ((sock_hw = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) { warn("socket(AF_LOCAL,SOCK_DGRAM)"); return; } rc = ioctl(sock_hw, SIOCGHWADDR, &ifr); close(sock_hw); if (rc != 0) return; /* * If this is definitely a lagg device or the hwaddr * matches the link addr, don't bother. */ if (memcmp(ifr.ifr_addr.sa_data, laggaddr, sdl->sdl_alen) == 0 || memcmp(ifr.ifr_addr.sa_data, LLADDR(sdl), sdl->sdl_alen) == 0) goto pcp; print_ether((const struct ether_addr *)&ifr.ifr_addr.sa_data, "hwaddr"); pcp: print_pcp(ctx); } #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(if_ctx *ctx, 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(ctx); } #endif static void link_getaddr(const char *addr, int which) { char *temp; struct sockaddr_dl sdl; struct sockaddr *sa = &link_ridreq.ifr_addr; if (which != ADDR) errx(1, "can't set link-level netmask or broadcast"); if (!strcmp(addr, "random")) { sdl.sdl_len = sizeof(sdl); sdl.sdl_alen = ETHER_ADDR_LEN; sdl.sdl_nlen = 0; sdl.sdl_family = AF_LINK; arc4random_buf(&sdl.sdl_data, ETHER_ADDR_LEN); /* Non-multicast and claim it is locally administered. */ sdl.sdl_data[0] &= 0xfc; sdl.sdl_data[0] |= 0x02; } else { if ((temp = malloc(strlen(addr) + 2)) == NULL) errx(1, "malloc failed"); temp[0] = ':'; strcpy(temp + 1, addr); sdl.sdl_len = sizeof(sdl); link_addr(temp, &sdl); free(temp); } if (sdl.sdl_alen > sizeof(sa->sa_data)) errx(1, "malformed link-level address"); sa->sa_family = AF_LINK; sa->sa_len = sdl.sdl_alen; bcopy(LLADDR(&sdl), sa->sa_data, sdl.sdl_alen); } static struct afswtch af_link = { .af_name = "link", .af_af = AF_LINK, #ifdef WITHOUT_NETLINK .af_status = link_status, #else .af_status = link_status_nl, #endif .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, .af_exec = af_exec_ioctl, }; static struct afswtch af_ether = { .af_name = "ether", .af_af = AF_LINK, #ifdef WITHOUT_NETLINK .af_status = link_status, #else .af_status = link_status_nl, #endif .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, .af_exec = af_exec_ioctl, }; static struct afswtch af_lladdr = { .af_name = "lladdr", .af_af = AF_LINK, #ifdef WITHOUT_NETLINK .af_status = link_status, #else .af_status = link_status_nl, #endif .af_getaddr = link_getaddr, .af_aifaddr = SIOCSIFLLADDR, .af_addreq = &link_ridreq, .af_exec = af_exec_ioctl, }; static __constructor void link_ctor(void) { af_register(&af_link); af_register(&af_ether); af_register(&af_lladdr); } diff --git a/sbin/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h index 80e6b9bc2289..3303c4894ba7 100644 --- a/sbin/ifconfig/ifconfig.h +++ b/sbin/ifconfig/ifconfig.h @@ -1,326 +1,328 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 1997 Peter Wemm. * All rights reserved. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the FreeBSD Project * by Peter Wemm. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. * * so there! */ #pragma once #include #include +#include #define __constructor __attribute__((constructor)) #ifdef WITHOUT_NETLINK #define __netlink_used __unused #define __netlink_unused #else #define __netlink_used #define __netlink_unused __unused #endif struct afswtch; struct cmd; struct snl_state; struct ifconfig_args; struct ifconfig_context { struct ifconfig_args *args; const struct afswtch *afp; int io_s; /* fd to use for ioctl() */ struct snl_state *io_ss; /* NETLINK_ROUTE socket */ const char *ifname; /* Current interface name */ char _ifname_storage_ioctl[IFNAMSIZ]; }; typedef struct ifconfig_context if_ctx; typedef void c_func(if_ctx *ctx, const char *cmd, int arg); typedef void c_func2(if_ctx *ctx, const char *arg1, const char *arg2); typedef void c_func3(if_ctx *ctx, const char *cmd, const char *arg); struct cmd { const char *c_name; int c_parameter; #define NEXTARG 0xffffff /* has following arg */ #define NEXTARG2 0xfffffe /* has 2 following args */ #define OPTARG 0xfffffd /* has optional following arg */ #define SPARAM 0xfffffc /* parameter is string c_sparameter */ const char *c_sparameter; union { c_func *c_func; c_func2 *c_func2; c_func3 *c_func3; } c_u; int c_iscloneop; struct cmd *c_next; }; void cmd_register(struct cmd *); typedef void callback_func(if_ctx *, void *); void callback_register(callback_func *, void *); /* * Macros for initializing command handlers. */ #define DEF_CMD(name, param, func) { \ .c_name = (name), \ .c_parameter = (param), \ .c_u = { .c_func = (func) }, \ .c_iscloneop = 0, \ .c_next = NULL, \ } #define DEF_CMD_ARG(name, func) { \ .c_name = (name), \ .c_parameter = NEXTARG, \ .c_u = { .c_func = (func) }, \ .c_iscloneop = 0, \ .c_next = NULL, \ } #define DEF_CMD_OPTARG(name, func) { \ .c_name = (name), \ .c_parameter = OPTARG, \ .c_u = { .c_func = (func) }, \ .c_iscloneop = 0, \ .c_next = NULL, \ } #define DEF_CMD_ARG2(name, func) { \ .c_name = (name), \ .c_parameter = NEXTARG2, \ .c_u = { .c_func2 = (func) }, \ .c_iscloneop = 0, \ .c_next = NULL, \ } #define DEF_CMD_SARG(name, sparam, func) { \ .c_name = (name), \ .c_parameter = SPARAM, \ .c_sparameter = (sparam), \ .c_u = { .c_func3 = (func) }, \ .c_iscloneop = 0, \ .c_next = NULL, \ } #define DEF_CLONE_CMD(name, param, func) { \ .c_name = (name), \ .c_parameter = (param), \ .c_u = { .c_func = (func) }, \ .c_iscloneop = 1, \ .c_next = NULL, \ } #define DEF_CLONE_CMD_ARG(name, func) { \ .c_name = (name), \ .c_parameter = NEXTARG, \ .c_u = { .c_func = (func) }, \ .c_iscloneop = 1, \ .c_next = NULL, \ } #define DEF_CLONE_CMD_ARG2(name, func) { \ .c_name = (name), \ .c_parameter = NEXTARG2, \ .c_u = { .c_func2 = (func) }, \ .c_iscloneop = 1, \ .c_next = NULL, \ } #define ioctl_ctx(ctx, _req, ...) ioctl((ctx)->io_s, _req, ## __VA_ARGS__) int ioctl_ctx_ifr(if_ctx *ctx, unsigned long cmd, struct ifreq *ifr); struct ifaddrs; struct addrinfo; enum { RIDADDR = 0, ADDR = 1, MASK = 2, DSTADDR = 3, #ifdef WITHOUT_NETLINK BRDADDR = 3, #else BRDADDR = 4, #endif }; struct snl_parsed_addr; struct snl_parsed_link; typedef struct snl_parsed_link if_link_t; typedef struct snl_parsed_addr if_addr_t; typedef void af_setvhid_f(int vhid); typedef void af_status_nl_f(if_ctx *ctx, if_link_t *link, if_addr_t *ifa); typedef void af_status_f(if_ctx *ctx, const struct ifaddrs *); typedef void af_other_status_f(if_ctx *ctx); typedef void af_postproc_f(if_ctx *ctx, int newaddr, int ifflags); typedef int af_exec_f(if_ctx *ctx, unsigned long action, void *data); typedef void af_copyaddr_f(if_ctx *ctx, int to, int from); typedef void af_status_tunnel_f(if_ctx *ctx); typedef void af_settunnel_f(if_ctx *ctx, struct addrinfo *srcres, struct addrinfo *dstres); struct afswtch { const char *af_name; /* as given on cmd line, e.g. "inet" */ short af_af; /* AF_* */ /* * Status is handled one of two ways; if there is an * address associated with the interface then the * associated address family af_status method is invoked * with the appropriate addressin info. Otherwise, if * all possible info is to be displayed and af_other_status * is defined then it is invoked after all address status * is presented. */ #ifndef WITHOUT_NETLINK af_status_nl_f *af_status; #else af_status_f *af_status; #endif af_other_status_f *af_other_status; void (*af_getaddr)(const char *, int); af_copyaddr_f *af_copyaddr; /* Copy address between ADDR */ /* parse prefix method (IPv6) */ void (*af_getprefix)(const char *, int); af_postproc_f *af_postproc; af_setvhid_f *af_setvhid; /* Set CARP vhid for an address */ af_exec_f *af_exec; /* Handler to interact with kernel */ u_long af_difaddr; /* set dst if address ioctl */ u_long af_aifaddr; /* set if address ioctl */ void *af_ridreq; /* */ void *af_addreq; /* */ struct afswtch *af_next; /* XXX doesn't fit model */ af_status_tunnel_f *af_status_tunnel; af_settunnel_f *af_settunnel; }; void af_register(struct afswtch *); int af_exec_ioctl(if_ctx *ctx, unsigned long action, void *data); struct ifconfig_args { bool all; /* Match everything */ bool downonly; /* Down-only items */ bool uponly; /* Up-only items */ bool namesonly; /* Output only names */ bool noload; /* Do not load relevant kernel modules */ bool supmedia; /* Supported media */ bool printkeys; /* Print security keys */ bool allfamilies; /* Print all families */ int verbose; /* verbosity level */ int argc; char **argv; const char *ifname; /* Requested interface name */ const char *matchgroup; /* Group name to match */ const char *nogroup; /* Group name to exclude */ const struct afswtch *afp; /* AF we're operating on */ const char *jail_name; /* Jail name or jail id specified */ }; struct option { const char *opt; const char *opt_usage; void (*cb)(const char *arg); struct option *next; }; void opt_register(struct option *); extern ifconfig_handle_t *lifh; extern int allmedia; extern int exit_code; extern char *f_inet, *f_inet6, *f_ether, *f_addr; void clearifcap(if_ctx *ctx, const char *, int value); void setifcap(if_ctx *ctx, const char *, int value); void setifcapnv(if_ctx *ctx, const char *vname, const char *arg); void Perror(const char *cmd); void printb(const char *s, unsigned value, const char *bits); void ifmaybeload(struct ifconfig_args *args, const char *name); typedef int clone_match_func(const char *); typedef void clone_callback_func(if_ctx *, struct ifreq *); void clone_setdefcallback_prefix(const char *, clone_callback_func *); void clone_setdefcallback_filter(clone_match_func *, clone_callback_func *); void sfp_status(if_ctx *ctx); struct sockaddr_dl; bool match_ether(const struct sockaddr_dl *sdl); bool match_if_flags(struct ifconfig_args *args, int if_flags); int ifconfig_ioctl(if_ctx *ctx, int iscreate, const struct afswtch *uafp); bool group_member(const char *ifname, const char *match, const char *nomatch); void tunnel_status(if_ctx *ctx); struct afswtch *af_getbyfamily(int af); void af_other_status(if_ctx *ctx); void print_ifstatus(if_ctx *ctx); void print_metric(if_ctx *ctx); +ifType convert_iftype(ifType iftype); /* Netlink-related functions */ void list_interfaces_nl(struct ifconfig_args *args); int ifconfig_nl(if_ctx *ctx, int iscreate, const struct afswtch *uafp); uint32_t if_nametoindex_nl(struct snl_state *ss, const char *ifname); /* * XXX expose this so modules that need to know of any pending * operations on ifmedia can avoid cmd line ordering confusion. */ struct ifmediareq *ifmedia_getstate(if_ctx *ctx); void print_vhid(const struct ifaddrs *); void ifcreate_ioctl(if_ctx *ctx, struct ifreq *ifr); /* Helpers */ struct sockaddr_in; struct sockaddr_in6; struct sockaddr; static inline struct sockaddr_in6 * satosin6(struct sockaddr *sa) { return ((struct sockaddr_in6 *)(void *)sa); } static inline struct sockaddr_in * satosin(struct sockaddr *sa) { return ((struct sockaddr_in *)(void *)sa); } static inline struct sockaddr_dl * satosdl(struct sockaddr *sa) { return ((struct sockaddr_dl *)(void *)sa); } static inline const struct sockaddr_dl * satosdl_c(const struct sockaddr *sa) { return ((const struct sockaddr_dl *)(const void *)sa); } diff --git a/sbin/ifconfig/ifconfig_netlink.c b/sbin/ifconfig/ifconfig_netlink.c index 76dd99307f31..c3e8cd413409 100644 --- a/sbin/ifconfig/ifconfig_netlink.c +++ b/sbin/ifconfig/ifconfig_netlink.c @@ -1,478 +1,491 @@ /*- * 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 #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] & (1U << (i % 32)); 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"); } int ifconfig_nl(if_ctx *ctx, int iscreate, const struct afswtch *uafp) { struct snl_state ss = {}; nl_init_socket(&ss); ctx->io_ss = &ss; int error = ifconfig_ioctl(ctx, iscreate, uafp); snl_free(&ss); ctx->io_ss = NULL; return (error); } struct ifa { struct ifa *next; 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); } uint32_t if_nametoindex_nl(struct snl_state *ss, const char *ifname) { struct snl_writer nw = {}; struct snl_parsed_link_simple link = {}; snl_init_writer(ss, &nw); struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); snl_reserve_msg_object(&nw, struct ifinfomsg); snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname); if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) return (0); hdr = snl_read_reply(ss, hdr->nlmsg_seq); if (hdr->nlmsg_type != NL_RTM_NEWLINK) return (0); if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, &link)) return (0); return (link.ifi_index); } +ifType +convert_iftype(ifType iftype) +{ + switch (iftype) { + case IFT_IEEE8023ADLAG: + return (IFT_ETHER); + case IFT_INFINIBANDLAG: + return (IFT_INFINIBAND); + default: + return (iftype); + } +} + 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->idx = ++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_type = convert_iftype(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 (uint32_t 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 (uint32_t i = 1; i < iface->ifa_count; i++) { ifa->next = sorted_ifaddrs[i]; ifa = sorted_ifaddrs[i]; } } static void print_ifcaps(if_ctx *ctx, if_link_t *link) { uint32_t sz_u32 = roundup2(link->iflaf_caps.nla_bitset_size, 32) / 32; if (sz_u32 > 0) { uint32_t *caps = link->iflaf_caps.nla_bitset_value; printf("\toptions=%x", caps[0]); print_bits("IFCAPS", caps, sz_u32, ifcap_bit_names, nitems(ifcap_bit_names)); putchar('\n'); } if (ctx->args->supmedia && sz_u32 > 0) { uint32_t *caps = link->iflaf_caps.nla_bitset_mask; printf("\tcapabilities=%x", caps[0]); print_bits("IFCAPS", caps, sz_u32, ifcap_bit_names, nitems(ifcap_bit_names)); putchar('\n'); } } static void status_nl(if_ctx *ctx, struct iface *iface) { if_link_t *link = &iface->link; struct ifconfig_args *args = ctx->args; printf("%s: ", link->ifla_ifname); printf("flags=%x", link->ifi_flags); print_bits("IFF", &link->ifi_flags, 1, IFFBITS, nitems(IFFBITS)); print_metric(ctx); printf(" mtu %d\n", link->ifla_mtu); if (link->ifla_ifalias != NULL) printf("\tdescription: %s\n", link->ifla_ifalias); print_ifcaps(ctx, link); tunnel_status(ctx); 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(ctx, link, NULL); } sort_iface_ifaddrs(ctx->io_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(ctx, link, &ifa->addr); } else if (args->afp->af_af == ifa->addr.ifa_family) { const struct afswtch *p = args->afp; p->af_status(ctx, link, &ifa->addr); } } /* TODO: convert to netlink */ if (args->allfamilies) af_other_status(ctx); else if (args->afp->af_other_status != NULL) args->afp->af_other_status(ctx); print_ifstatus(ctx); if (args->verbose > 0) sfp_status(ctx); } 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); } void list_interfaces_nl(struct ifconfig_args *args) { struct snl_state ss = {}; struct ifconfig_context _ctx = { .args = args, .io_s = get_local_socket(), .io_ss = &ss, }; struct ifconfig_context *ctx = &_ctx; nl_init_socket(&ss); struct ifmap *ifmap = prepare_ifmap(&ss); struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *)); for (uint32_t 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); for (uint32_t i = 0, num = 0; i < ifmap->count; i++) { struct iface *iface = sorted_ifaces[i]; if (!match_iface(args, iface)) continue; ctx->ifname = iface->link.ifla_ifname; if (args->namesonly) { if (num++ != 0) printf(" "); fputs(iface->link.ifla_ifname, stdout); } else if (args->argc == 0) status_nl(ctx, iface); else ifconfig_ioctl(ctx, 0, args->afp); } if (args->namesonly) printf("\n"); close(ctx->io_s); snl_free(&ss); }