ifconfig(8): Add '-f' option to print in more formats
authorAaron LI <aly@aaronly.me>
Fri, 26 Jun 2020 06:02:58 +0000 (14:02 +0800)
committerAaron LI <aly@aaronly.me>
Fri, 26 Jun 2020 14:53:00 +0000 (22:53 +0800)
* Add the '-f' option to control the output formats of addresses,
  inet (IPv4) subnet masks, inet6 (IPv6) prefix length, Ethernet (MAC)
  address.  The following output formats are supported:

  - address: numeric (default), host, FQDN
  - inet (IPv4) subnet masks: CIDR, dotted-quad, and traditional hex
    output (default)
  - inet6 (IPv6) prefix length: CIDR, traditional integer format
    (default)
  - ether (MAC address): colon-separated (default), dash-separated

* The 'IFCONFIG_FORMAT' environment variable has the same usage as the
  '-f' option and can be overrided by the latter.

* Some minor cleanups.

Obtained from FreeBSD (revisions 301059 and 301185; review D2856), but
with my own minor modifications.

sbin/ifconfig/af_inet.c
sbin/ifconfig/af_inet6.c
sbin/ifconfig/af_link.c
sbin/ifconfig/ifconfig.8
sbin/ifconfig/ifconfig.c
sbin/ifconfig/ifconfig.h

index 1a4fd0e..61f6886 100644 (file)
 
 static struct ifaliasreq in_addreq;
 static struct ifreq in_ridreq;
+static char addr_buf[NI_MAXHOST];  /* for getnameinfo() */
 
 static void
-in_status(int s __unused, const struct rt_addrinfo * info)
+in_status(int s __unused, const struct rt_addrinfo *info)
 {
        struct sockaddr_in *sin, null_sin;
+       int error, n_flags;
 
        memset(&null_sin, 0, sizeof(null_sin));
 
@@ -62,26 +64,54 @@ in_status(int s __unused, const struct rt_addrinfo * info)
        if (sin == NULL)
                return;
 
-       printf("\tinet %s ", inet_ntoa(sin->sin_addr));
+       if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0)
+               n_flags = 0;
+       else if (f_addr != NULL && strcmp(f_addr, "host") == 0)
+               n_flags = NI_NOFQDN;
+       else
+               n_flags = NI_NUMERICHOST;
+
+       error = getnameinfo((struct sockaddr *)sin, sin->sin_len, addr_buf,
+                           sizeof(addr_buf), NULL, 0, n_flags);
+       if (error != 0)
+               inet_ntop(AF_INET, &sin->sin_addr, addr_buf, sizeof(addr_buf));
+
+       printf("\tinet %s", addr_buf);
 
        if (flags & IFF_POINTOPOINT) {
                /* note RTAX_BRD overlap with IFF_BROADCAST */
                sin = (struct sockaddr_in *)info->rti_info[RTAX_BRD];
                if (!sin)
                        sin = &null_sin;
-               printf("--> %s ", inet_ntoa(sin->sin_addr));
+               printf(" --> %s", inet_ntoa(sin->sin_addr));
        }
 
        sin = (struct sockaddr_in *)info->rti_info[RTAX_NETMASK];
        if (!sin)
                sin = &null_sin;
-       printf("netmask 0x%lx ", (unsigned long)ntohl(sin->sin_addr.s_addr));
+       if (f_inet != NULL && strcmp(f_inet, "cidr") == 0) {
+               int cidr = 32;
+               unsigned long smask = ntohl(sin->sin_addr.s_addr);
+
+               while ((smask & 1) == 0) {
+                       smask >>= 1;
+                       cidr--;
+                       if (cidr == 0)
+                               break;
+               }
+               printf("/%d", cidr);
+       } else if (f_inet != NULL && strcmp(f_inet, "dotted") == 0) {
+               printf(" netmask %s", inet_ntoa(sin->sin_addr));
+       } else {
+               printf(" netmask 0x%lx",
+                       (unsigned long)ntohl(sin->sin_addr.s_addr));
+       }
 
        if (flags & IFF_BROADCAST) {
                /* note RTAX_BRD overlap with IFF_POINTOPOINT */
                sin = (struct sockaddr_in *)info->rti_info[RTAX_BRD];
                if (sin && sin->sin_addr.s_addr != 0)
-                       printf("broadcast %s", inet_ntoa(sin->sin_addr));
+                       printf(" broadcast %s", inet_ntoa(sin->sin_addr));
        }
        putchar('\n');
 }
index 551748c..e4e6325 100644 (file)
@@ -66,7 +66,7 @@ static        int prefix(void *, int);
 static char *sec2str(time_t);
 static int explicit_prefix = 0;
 
-static char addr_buf[MAXHOSTNAMELEN *2 + 1];   /*for getnameinfo()*/
+static         char addr_buf[NI_MAXHOST];  /* for getnameinfo() */
 
 static void
 setifprefixlen(const char *addr, int dummy __unused, int s,
@@ -182,6 +182,7 @@ in6_status(int s __unused, const struct rt_addrinfo * info)
        u_int32_t flags6;
        struct in6_addrlifetime lifetime;
        struct timespec now;
+       int n_flags, prefixlen;
        int error;
        u_int32_t scopeid;
 
@@ -227,12 +228,19 @@ in6_status(int s __unused, const struct rt_addrinfo * info)
        }
        scopeid = sin->sin6_scope_id;
 
+       if (f_addr != NULL && strcmp(f_addr, "fqdn") == 0)
+               n_flags = 0;
+       else if (f_addr != NULL && strcmp(f_addr, "host") == 0)
+               n_flags = NI_NOFQDN;
+       else
+               n_flags = NI_NUMERICHOST;
+
        error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf,
-                           sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
+                           sizeof(addr_buf), NULL, 0, n_flags);
        if (error != 0)
                inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf,
                          sizeof(addr_buf));
-       printf("\tinet6 %s ", addr_buf);
+       printf("\tinet6 %s", addr_buf);
 
        if (flags & IFF_POINTOPOINT) {
                /* note RTAX_BRD overlap with IFF_BROADCAST */
@@ -262,15 +270,18 @@ in6_status(int s __unused, const struct rt_addrinfo * info)
                        if (error != 0)
                                inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf,
                                          sizeof(addr_buf));
-                       printf("--> %s ", addr_buf);
+                       printf(" --> %s", addr_buf);
                }
        }
 
        sin = (struct sockaddr_in6 *)info->rti_info[RTAX_NETMASK];
        if (!sin)
                sin = &null_sin;
-       printf("prefixlen %d", prefix(&sin->sin6_addr,
-               sizeof(struct in6_addr)));
+       prefixlen = prefix(&sin->sin6_addr, sizeof(struct in6_addr));
+       if (f_inet6 != NULL && strcmp(f_inet6, "cidr") == 0)
+               printf("/%d", prefixlen);
+       else
+               printf(" prefixlen %d", prefixlen);
 
        if ((flags6 & IN6_IFF_ANYCAST) != 0)
                printf(" anycast");
index 0d21555..e722e57 100644 (file)
@@ -54,10 +54,17 @@ link_status(int s __unused, const struct rt_addrinfo *info)
 
        if (sdl != NULL && sdl->sdl_alen > 0) {
                if (sdl->sdl_type == IFT_ETHER &&
-                   sdl->sdl_alen == ETHER_ADDR_LEN)
-                       printf("\tether %s\n",
-                           ether_ntoa((const struct ether_addr *)LLADDR(sdl)));
-               else {
+                   sdl->sdl_alen == ETHER_ADDR_LEN) {
+                       char *ether_addr = ether_ntoa(
+                               (const struct ether_addr *)LLADDR(sdl));
+
+                       if (f_ether != NULL && strcmp(f_ether, "dash") == 0) {
+                               char *fchar = ether_addr;
+                               while ((fchar = strchr(fchar, ':')) != NULL)
+                                       *fchar = '-';
+                       }
+                       printf("\tether %s\n", ether_addr);
+               } else {
                        int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
 
                        printf("\tlladdr %s\n", link_ntoa(sdl) + n);
index 3a2daa9..8486054 100644 (file)
@@ -28,7 +28,7 @@
 .\"     From: @(#)ifconfig.8   8.3 (Berkeley) 1/5/94
 .\" $FreeBSD: src/sbin/ifconfig/ifconfig.8,v 1.124 2006/10/10 09:44:08 ru Exp $
 .\"
-.Dd November 27, 2018
+.Dd June 26, 2020
 .Dt IFCONFIG 8
 .Os
 .Sh NAME
@@ -36,6 +36,7 @@
 .Nd configure network interface parameters
 .Sh SYNOPSIS
 .Nm
+.Op Fl f Ar type:format Ns Op Ar ,type:format
 .Op Fl L
 .Op Fl k
 .Op Fl m
@@ -143,12 +144,12 @@ The link-level
 address
 is specified as a series of colon-separated hex digits.
 This can be used to
-e.g.\& set a new MAC address on an ethernet interface, though the
-mechanism used is not ethernet-specific.
+e.g.\& set a new MAC address on an Ethernet interface, though the
+mechanism used is not Ethernet-specific.
 If the interface is already
 up when this option is used, it will be briefly brought down and
 then brought back up again in order to ensure that the receive
-filter in the underlying ethernet hardware is properly reprogrammed.
+filter in the underlying Ethernet hardware is properly reprogrammed.
 .It Ar address_family
 Specify the
 address family
@@ -182,6 +183,83 @@ for example,
 List the interfaces in the given group.
 .El
 .Pp
+The output format of
+.Nm
+can be controlled with the
+.Fl f
+option or the
+.Ev IFCONFIG_FORMAT
+environment variable.
+The format is specified as a comma-separated list of
+.Sy type:format
+pairs.
+The supported
+.Sy type
+and its associated
+.Sy format
+strings are:
+.Bl -tag -width indent
+.It Sy addr
+Adjust the display of inet and inet6 addresses:
+.Bl -tag -width default
+.It Sy default
+Display inet and inet6 addresses in the default format, i.e.,
+.Sy numeric .
+.It Sy fqdn
+Display inet and inet6 addresses as fully qualified domain names
+.Pq FQDN .
+.It Sy host
+Display inet and inet6 addresses as unqualified hostnames.
+.It Sy numeric
+Display inet and inet6 addresses in numeric format.
+.El
+.It Sy ether
+Adjust the display of link-level Ethernet (MAC) addresses:
+.Bl -tag -width default
+.It Sy colon
+Separate address segments with a colon.
+.It Sy dash
+Separate address segments with a dash.
+.It Sy default
+Display Ethernet addresses in the default format, i.e.,
+.Sy colon .
+.El
+.It Sy inet
+Adjust the display of inet address subnet masks:
+.Bl -tag -width default
+.It Sy cidr
+Display subnet masks in CIDR notation, for example:
+.br
+10.0.0.0/8 or 203.0.113.224/26
+.It Sy default
+Display subnet masks in the default format, i.e.,
+.Sy hex .
+.It Sy dotted
+Display subnet masks in dotted quad notation, for example:
+.br
+255.255.0.0, 255.255.255.192
+.It Sy hex
+Display subnet masks in hexidecimal, for example:
+.br
+0xffff0000, 0xffffffc0
+.El
+.It Sy inet6
+Adjust the display of inet6 address prefixes (subnet masks):
+.Bl -tag -width default
+.It Sy cidr
+Display subnet prefix in CIDR notation, for example:
+.br
+::1/128, fe80::1%lo0/64
+.It Sy default
+Display subnet prefix in the default format, i.e.,
+.Sy numeric
+.It Sy numeric
+Display subnet prefix in integer format, for example:
+.br
+prefixlen 64
+.El
+.El
+.Pp
 The following parameters may be set with
 .Nm :
 .Bl -tag -width indent
@@ -2106,7 +2184,7 @@ for a 6:4 ratio.
 Remember that this also controls packet bursting.
 .It Cm link0
 The link0 option enables transparent bridging mode.
-The bridge will make every effort to retain the ethernet header
+The bridge will make every effort to retain the Ethernet header
 when forwarding packets between interfaces, making the bridging
 function work more like a hardware bridge device.
 .It Cm link1
@@ -2191,7 +2269,7 @@ pseudo-interface.
 The
 .Xr vlan 4
 interface is assigned a
-copy of the parent interface's flags and the parent's ethernet address.
+copy of the parent interface's flags and the parent's Ethernet address.
 The
 .Cm vlandev
 and
index 353e622..89675d3 100644 (file)
@@ -84,6 +84,9 @@ int   printkeys = 0;          /* Print keying material for interfaces. */
 int    printifname = 0;        /* Print the name of the created interface. */
 int    exit_code = 0;
 
+/* Formatter strings */
+char   *f_inet, *f_inet6, *f_ether, *f_addr;
+
 static int ifconfig(int argc, char *const *argv, int iscreate,
                     const struct afswtch *afp);
 static void status(const struct afswtch *afp, int addrcount,
@@ -96,6 +99,8 @@ static struct afswtch *af_getbyname(const char *name);
 static struct afswtch *af_getbyfamily(int af);
 static void af_other_status(int);
 static void printifnamemaybe(void);
+static void freeformat(void);
+static void setformat(char *input);
 
 static struct option *opts = NULL;
 
@@ -120,8 +125,8 @@ usage(void)
        }
 
        fprintf(stderr,
-       "usage: ifconfig %s[-n] interface address_family [address [dest_address]]\n"
-       "                [parameters]\n"
+       "usage: ifconfig %s[-n] [-f type:format] interface address_family\n"
+       "                [address [dest_address]] [parameters]\n"
        "       ifconfig [-n] interface create\n"
        "       ifconfig [-n] interface destroy\n"
        "       ifconfig -a %s[-d] [-m] [-u] [-v] [address_family]\n"
@@ -131,12 +136,68 @@ usage(void)
        exit(1);
 }
 
-static void printifnamemaybe(void)
+static void
+printifnamemaybe(void)
 {
        if (printifname)
                printf("%s\n", name);
 }
 
+static void
+freeformat(void)
+{
+       if (f_inet != NULL)
+               free(f_inet);
+       if (f_inet6 != NULL)
+               free(f_inet6);
+       if (f_ether != NULL)
+               free(f_ether);
+       if (f_addr != NULL)
+               free(f_addr);
+}
+
+static void
+setformat(char *input)
+{
+       char *formatstr, *category, *modifier;
+       char **fp;
+
+       formatstr = strdup(input);
+       if (formatstr == NULL)
+               err(1, "no memory to set format");
+
+       while ((category = strsep(&formatstr, ",")) != NULL) {
+               modifier = strchr(category, ':');
+               if (modifier == NULL || modifier[1] == '\0') {
+                       warnx("skip invalid format specification: %s\n",
+                             category);
+                       continue;
+               }
+
+               modifier[0] = '\0';
+               modifier++;
+
+               fp = NULL;
+               if (strcmp(category, "addr") == 0)
+                       fp = &f_addr;
+               else if (strcmp(category, "ether") == 0)
+                       fp = &f_ether;
+               else if (strcmp(category, "inet") == 0)
+                       fp = &f_inet;
+               else if (strcmp(category, "inet6") == 0)
+                       fp = &f_inet6;
+
+               if (fp != NULL) {
+                       *fp = strdup(modifier);
+                       if (*fp == NULL)
+                               err(1, "strdup");
+               }
+       }
+
+       free(formatstr);
+}
+
+
 int
 main(int argc, char *argv[])
 {
@@ -148,6 +209,7 @@ main(int argc, char *argv[])
        struct ifa_msghdr *ifam;
        struct sockaddr_dl *sdl;
        char *buf, *lim, *next;
+       char *envformat;
        size_t needed;
        int mib[6];
        char options[1024];
@@ -156,6 +218,7 @@ main(int argc, char *argv[])
        size_t iflen;
 
        all = downonly = uponly = namesonly = verbose = noload = 0;
+       f_inet = f_inet6 = f_ether = f_addr = NULL;
 
        /*
         * Ensure we print interface name when expected to,
@@ -163,8 +226,12 @@ main(int argc, char *argv[])
         */
        atexit(printifnamemaybe);
 
+       envformat = getenv("IFCONFIG_FORMAT");
+       if (envformat != NULL)
+               setformat(envformat);
+
        /* Parse leading line options */
-       strlcpy(options, "adklmnuv", sizeof(options));
+       strlcpy(options, "adf:klmnuv", sizeof(options));
        for (p = opts; p != NULL; p = p->next)
                strlcat(options, p->opt, sizeof(options));
        while ((c = getopt(argc, argv, options)) != -1) {
@@ -175,6 +242,9 @@ main(int argc, char *argv[])
                case 'd':       /* restrict scan to "down" interfaces */
                        downonly++;
                        break;
+               case 'f':
+                       setformat(optarg);
+                       break;
                case 'k':
                        printkeys++;
                        break;
@@ -385,7 +455,9 @@ retry:
                else
                        status(afp, addrcount, sdl, ifm, ifam);
        }
+
        free(buf);
+       freeformat();
 
        if (namesonly && need_nl > 0)
                putchar('\n');
index 002d516..48f23f1 100644 (file)
@@ -143,6 +143,7 @@ extern      int flags;
 extern int newaddr;
 extern int verbose;
 extern int exit_code;
+extern char *f_inet, *f_inet6, *f_ether, *f_addr, *f_scope;
 
 void   setifcap(const char *, int value, int s, const struct afswtch *);