From 80af6b5d1f3b09540f20bb00ab899d2d722b12e6 Mon Sep 17 00:00:00 2001 From: Aaron LI Date: Fri, 26 Jun 2020 22:34:43 +0800 Subject: [PATCH] ifconfig(8): Support to filter output by interface groups Add options '-g ' and '-G ' to select and unselect interfaces by groups in the output of 'ifconfig -a', just like the existing '-d' and '-u' options to select only interfaces that are down or up, respectively. Note that '-g' and '-G' options can be used at the same time to apply both conditions and their arguments may contain shell patterns. Examples: * To exclude loopback from the list: ifconfig -a -G lo * To list interfaces whose group names begin with 't': ifconfig -a -g 't*' Obtained from FreeBSD (revision 361790; review D25029) --- sbin/ifconfig/ifconfig.8 | 26 +++++++++++- sbin/ifconfig/ifconfig.c | 91 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 112 insertions(+), 5 deletions(-) diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 78df0d9aa3..bf6dcaecd7 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -55,11 +55,13 @@ .Cm destroy .Nm .Fl a +.Op Fl G Ar nogroup .Op Fl L .Oo .Fl d | .Fl u .Oc +.Op Fl g Ar matchgroup .Op Fl m .Op Fl v .Op Ar address_family @@ -2367,9 +2369,29 @@ This flag instructs to display information about all interfaces in the system. The .Fl d -flag limits this to interfaces that are down, and +flag limits this to interfaces that are down, .Fl u -limits this to interfaces that are up. +limits this to interfaces that are up, +.Fl g +limits this to members of the specified group of interfaces, and +.Fl G +excludes members of the specified group from the list. +Both +.Fl g +and +.Fl G +flags may be specified to apply both conditions. +Only one +.Fl g +flag should be specified, as the later one overrides previous ones +(same for the +.Fl G +flag). +The argument of +.Fl g +or +.Fl G +flag may contain shell patterns but should be quoted in that case. When no arguments are given, .Fl a is implied. diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index 483e36ea76..0274a1c7e0 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -56,7 +56,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -88,6 +90,8 @@ 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 status(const struct afswtch *afp, const struct sockaddr_dl *sdl, @@ -146,7 +150,7 @@ usage(void) " [address [dest_address]] [parameters]\n" " ifconfig [-n] interface create\n" " ifconfig [-n] interface destroy\n" - " ifconfig -a %s[-d | -u] [-m] [-v] [address_family]\n" + " ifconfig -a %s[-G nogroup] [-d | -u] [-m] [-v] [address_family]\n" " ifconfig -l [-d | -u] [address_family]\n" " ifconfig %s[-d | -u] [-m] [-v]\n", options, options, options); @@ -355,7 +359,7 @@ main(int argc, char *argv[]) int ifindex, flags; const struct afswtch *afp = NULL; const struct sockaddr_dl *sdl; - const char *ifname; + const char *ifname, *matchgroup, *nogroup; struct ifa_order_elt *cur, *tmp; struct ifa_queue q = TAILQ_HEAD_INITIALIZER(q); struct ifaddrs *ifap, *sifap, *ifa; @@ -367,6 +371,7 @@ main(int argc, char *argv[]) all = downonly = uponly = namesonly = verbose = noload = 0; f_inet = f_inet6 = f_ether = f_addr = NULL; + matchgroup = nogroup = NULL; /* * Ensure we print interface name when expected to, @@ -379,7 +384,7 @@ main(int argc, char *argv[]) setformat(envformat); /* Parse leading line options */ - strlcpy(options, "adf:klmnuv", sizeof(options)); + strlcpy(options, "adf:G:klmnuv", sizeof(options)); for (p = opts; p != NULL; p = p->next) strlcat(options, p->opt, sizeof(options)); while ((c = getopt(argc, argv, options)) != -1) { @@ -393,6 +398,11 @@ main(int argc, char *argv[]) case 'f': setformat(optarg); break; + case 'G': + if (!all) + usage(); + nogroup = optarg; + break; case 'k': printkeys++; break; @@ -411,6 +421,12 @@ main(int argc, char *argv[]) case 'v': verbose++; break; + case 'g': + if (all) { + matchgroup = optarg; + break; + } + /* FALLTHROUGH (for ifgroup) */ default: for (p = opts; p != NULL; p = p->next) if (p->opt[0] == c) { @@ -556,6 +572,8 @@ main(int argc, char *argv[]) continue; if (uponly && (ifa->ifa_flags & IFF_UP) == 0) continue; + if (!group_member(ifa->ifa_name, matchgroup, nogroup)) + continue; if (ifa->ifa_addr->sa_family == AF_LINK) sdl = (const struct sockaddr_dl *)ifa->ifa_addr; @@ -589,6 +607,73 @@ main(int argc, char *argv[]) } +/* + * 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 +group_member(const char *ifname, const char *match, const char *nomatch) +{ + static int sock = -1; + + struct ifgroupreq ifgr; + struct ifg_req *ifg; + size_t len; + bool matched, nomatched; + + /* Sanity checks. */ + if (match == NULL && nomatch == NULL) + return (true); + if (ifname == NULL) + return (false); + + memset(&ifgr, 0, sizeof(ifgr)); + strlcpy(ifgr.ifgr_name, ifname, sizeof(ifgr.ifgr_name)); + + /* The socket is opened once. Let _exit() close it. */ + if (sock == -1) { + sock = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (sock == -1) + errx(1, "%s: socket(AF_LOCAL,SOCK_DGRAM)", __func__); + } + + /* Determine amount of memory for the list of groups. */ + if (ioctl(sock, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) { + if (errno == EINVAL || errno == ENOTTY) + return (false); + else + errx(1, "%s: SIOCGIFGROUP", __func__); + } + + /* Obtain the list of groups. */ + len = ifgr.ifgr_len; + ifgr.ifgr_groups = + (struct ifg_req *)calloc(len / sizeof(*ifg), sizeof(*ifg)); + if (ifgr.ifgr_groups == NULL) + errx(1, "%s: no memory", __func__); + if (ioctl(sock, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) + errx(1, "%s: SIOCGIFGROUP", __func__); + + /* Perform matching. */ + matched = false; + nomatched = true; + for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(*ifg); ifg++) { + len -= sizeof(struct ifg_req); + if (match) + matched |= !fnmatch(match, ifg->ifgrq_group, 0); + if (nomatch) + nomatched &= fnmatch(nomatch, ifg->ifgrq_group, 0); + } + + if (match && !nomatch) + return (matched); + if (!match && nomatch) + return (nomatched); + return (matched && nomatched); +} + + static struct afswtch *afs = NULL; void -- 2.41.0