ifconfig: make interface and address listing use Netlink as transport
Differential Revision: https://reviews.freebsd.org/D40044
This commit is contained in:
parent
10b94e4064
commit
4c91a5dfe4
@ -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
|
||||
|
@ -53,6 +53,7 @@ static const char rcsid[] =
|
||||
#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 @@ 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,
|
||||
|
@ -57,6 +57,7 @@ static const char rcsid[] =
|
||||
#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 @@ 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,
|
||||
|
@ -51,6 +51,7 @@ static const char rcsid[] =
|
||||
#include <net/ethernet.h>
|
||||
|
||||
#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 @@ link_status(int s __unused, const struct ifaddrs *ifa)
|
||||
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,
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
427
sbin/ifconfig/ifconfig_netlink.c
Normal file
427
sbin/ifconfig/ifconfig_netlink.c
Normal file
@ -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);
|
||||
}
|
||||
|
37
sbin/ifconfig/ifconfig_netlink.h
Normal file
37
sbin/ifconfig/ifconfig_netlink.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*-
|
||||
* 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
|
||||
|
||||
#ifndef WITHOUT_NETLINK
|
||||
#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>
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user