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 struct ifaliasreq in_addreq;
 static struct ifreq in_ridreq;
+static char addr_buf[NI_MAXHOST];  /* for getnameinfo() */
 
 static void
 
 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;
 {
        struct sockaddr_in *sin, null_sin;
+       int error, n_flags;
 
        memset(&null_sin, 0, sizeof(null_sin));
 
 
        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;
 
        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;
 
        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;
        }
 
        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)
 
        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');
 }
        }
        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 *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,
 
 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;
        u_int32_t flags6;
        struct in6_addrlifetime lifetime;
        struct timespec now;
+       int n_flags, prefixlen;
        int error;
        u_int32_t scopeid;
 
        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;
 
        }
        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,
        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));
        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 */
 
        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));
                        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;
                }
        }
 
        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");
 
        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 &&
 
        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);
                        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 $
 .\"
 .\"     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
 .Dt IFCONFIG 8
 .Os
 .Sh NAME
@@ -36,6 +36,7 @@
 .Nd configure network interface parameters
 .Sh SYNOPSIS
 .Nm
 .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
 .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
 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
 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
 .It Ar address_family
 Specify the
 address family
@@ -182,6 +183,83 @@ for example,
 List the interfaces in the given group.
 .El
 .Pp
 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
 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.
 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
 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
 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
 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;
 
 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,
 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 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;
 
 
 static struct option *opts = NULL;
 
@@ -120,8 +125,8 @@ usage(void)
        }
 
        fprintf(stderr,
        }
 
        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"
        "       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);
 }
 
        exit(1);
 }
 
-static void printifnamemaybe(void)
+static void
+printifnamemaybe(void)
 {
        if (printifname)
                printf("%s\n", name);
 }
 
 {
        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[])
 {
 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;
        struct ifa_msghdr *ifam;
        struct sockaddr_dl *sdl;
        char *buf, *lim, *next;
+       char *envformat;
        size_t needed;
        int mib[6];
        char options[1024];
        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;
        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,
 
        /*
         * Ensure we print interface name when expected to,
@@ -163,8 +226,12 @@ main(int argc, char *argv[])
         */
        atexit(printifnamemaybe);
 
         */
        atexit(printifnamemaybe);
 
+       envformat = getenv("IFCONFIG_FORMAT");
+       if (envformat != NULL)
+               setformat(envformat);
+
        /* Parse leading line options */
        /* 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) {
        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 'd':       /* restrict scan to "down" interfaces */
                        downonly++;
                        break;
+               case 'f':
+                       setformat(optarg);
+                       break;
                case 'k':
                        printkeys++;
                        break;
                case 'k':
                        printkeys++;
                        break;
@@ -385,7 +455,9 @@ retry:
                else
                        status(afp, addrcount, sdl, ifm, ifam);
        }
                else
                        status(afp, addrcount, sdl, ifm, ifam);
        }
+
        free(buf);
        free(buf);
+       freeformat();
 
        if (namesonly && need_nl > 0)
                putchar('\n');
 
        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 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 *);
 
 
 void   setifcap(const char *, int value, int s, const struct afswtch *);