Index: sbin/ifconfig/ifconfig.8 =================================================================== --- sbin/ifconfig/ifconfig.8 +++ sbin/ifconfig/ifconfig.8 @@ -28,7 +28,7 @@ .\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94 .\" $FreeBSD$ .\" -.Dd December 17, 2019 +.Dd May 27, 2020 .Dt IFCONFIG 8 .Os .Sh NAME @@ -56,6 +56,7 @@ .Fl a .Op Fl L .Op Fl d +.Op Fl g Ar groupname .Op Fl m .Op Fl u .Op Fl v @@ -2915,9 +2916,17 @@ 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, and +.Fl -g +limits this to members of specified group of interfaces. +.Sy groupname +may be prefixed with caret symbol (^) to list +interfaces not belonging to specified group +(see the +.Sx EXAMPLES +section). When no arguments are given, .Fl a is implied. @@ -3041,6 +3050,9 @@ .Pp Display inet and inet6 address subnet masks in CIDR notation .Dl # ifconfig -f inet:cidr,inet6:cidr +.Pp +Display interfaces that are up with the exception of loopack +.Dl # ifconfig -a -u -g ^lo .Sh DIAGNOSTICS Messages indicating the specified interface does not exist, the requested address is unknown, or the user is not privileged and Index: sbin/ifconfig/ifconfig.c =================================================================== --- sbin/ifconfig/ifconfig.c +++ sbin/ifconfig/ifconfig.c @@ -105,6 +105,7 @@ /* Formatter Strings */ char *f_inet, *f_inet6, *f_ether, *f_addr; +static bool group_member(const char *ifname, const char *match); 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, @@ -402,13 +403,15 @@ char options[1024], *cp, *envformat, *namecp = NULL; struct ifa_queue q = TAILQ_HEAD_INITIALIZER(q); struct ifa_order_elt *cur, *tmp; - const char *ifname; + const char *ifname, *matchgroup; + struct option *p; size_t iflen; int flags; all = downonly = uponly = namesonly = noload = verbose = 0; f_inet = f_inet6 = f_ether = f_addr = NULL; + matchgroup = NULL; envformat = getenv("IFCONFIG_FORMAT"); if (envformat != NULL) @@ -455,6 +458,14 @@ case 'v': verbose++; break; + case 'g': + if (all) { + if (optarg == NULL) + usage(); + matchgroup = optarg; + break; + } + /* FALLTHROUGH */ default: for (p = opts; p != NULL; p = p->next) if (p->opt[0] == c) { @@ -626,6 +637,8 @@ continue; if (uponly && (ifa->ifa_flags & IFF_UP) == 0) continue; + if (matchgroup && !group_member(ifa->ifa_name, matchgroup)) + continue; /* * Are we just listing the interfaces? */ @@ -670,6 +683,73 @@ exit(exit_code); } +/* + * Returns true if an interface ifname should be listed based on rules: + * 1) ifname is a member of interface group; + * 2) ifname is NOT a member of interface group prepended + * with caret: "^group". + */ +static bool +group_member(const char *ifname, const char *match) +{ + static int sock = -1; + + struct ifgroupreq ifgr; + struct ifg_req *ifg; + int len; + bool findmatch; + + /* Sanity checks. */ + if (ifname == NULL || match == NULL) + return (false); + + memset(&ifgr, 0, sizeof(ifgr)); + strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ); + + /* 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. */ + findmatch = true; + if (match[0] == '^') { + findmatch = false; + match++; + + /* Sanity check. */ + if (!*match) + return (false); + } + + for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(*ifg); ifg++) { + if (strcmp(ifg->ifgrq_group, match) == 0) + return (findmatch); + len -= sizeof(*ifg); + } + return (!findmatch); +} + static struct afswtch *afs = NULL; void