]> git.saurik.com Git - apple/network_cmds.git/blobdiff - ifconfig.tproj/ifconfig.c
network_cmds-481.20.1.tar.gz
[apple/network_cmds.git] / ifconfig.tproj / ifconfig.c
index a69654ed525524737c6df7b891b7f71813a60690..1a708da087ed54f47bd612e910b3d98d417a8b3a 100644 (file)
@@ -1,3 +1,31 @@
+/*
+ * Copyright (c) 2009-2014 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
 /*
  * Copyright (c) 1983, 1993
  *     The Regents of the University of California.  All rights reserved.
 /*
  * Copyright (c) 1983, 1993
  *     The Regents of the University of California.  All rights reserved.
  * 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.
  * 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.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  * SUCH DAMAGE.
  */
 
  * SUCH DAMAGE.
  */
 
+#include <sys/cdefs.h>
+
 #ifndef lint
 #ifndef lint
-static const char copyright[] =
+__unused static const char copyright[] =
 "@(#) Copyright (c) 1983, 1993\n\
        The Regents of the University of California.  All rights reserved.\n";
 #endif /* not lint */
 
 "@(#) Copyright (c) 1983, 1993\n\
        The Regents of the University of California.  All rights reserved.\n";
 #endif /* not lint */
 
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94";
-#endif
-static const char rcsid[] =
-       "$Id: ifconfig.c,v 1.2 2001/08/03 16:27:01 vlubet Exp $";
-#endif /* not lint */
-
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/sysctl.h>
 #include <sys/time.h>
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/sysctl.h>
 #include <sys/time.h>
+#ifndef __APPLE__
+#include <sys/module.h>
+#include <sys/linker.h>
+#endif
 
 
+#include <net/ethernet.h>
 #include <net/if.h>
 #include <net/if_var.h>
 #include <net/if_dl.h>
 #include <net/if_types.h>
 #include <net/if.h>
 #include <net/if_var.h>
 #include <net/if_dl.h>
 #include <net/if_types.h>
+#include <net/if_mib.h>
 #include <net/route.h>
 #include <net/route.h>
+#include <net/pktsched/pktsched.h>
+#include <net/network_agent.h>
 
 /* IP */
 #include <netinet/in.h>
 
 /* IP */
 #include <netinet/in.h>
@@ -63,8 +89,7 @@ static const char rcsid[] =
 #include <arpa/inet.h>
 #include <netdb.h>
 
 #include <arpa/inet.h>
 #include <netdb.h>
 
-/* OSI */
-
+#include <ifaddrs.h>
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
@@ -76,227 +101,182 @@ static const char rcsid[] =
 
 #include "ifconfig.h"
 
 
 #include "ifconfig.h"
 
-struct ifreq           ifr, ridreq;
-struct ifaliasreq      addreq;
-struct sockaddr_in     netmask;
-
+/*
+ * Since "struct ifreq" is composed of various union members, callers
+ * should pay special attention to interprete the value.
+ * (.e.g. little/big endian difference in the structure.)
+ */
+struct ifreq ifr;
 
 
-char   name[32];
-int    flags;
-int    metric;
-int    mtu;
+char   name[IFNAMSIZ];
 int    setaddr;
 int    setaddr;
-int    setipdst;
+int    setmask;
 int    doalias;
 int    clearaddr;
 int    doalias;
 int    clearaddr;
-int    newaddr;
-
-struct afswtch;
-
-void   Perror __P((const char *cmd));
-
-int    ifconfig __P((int argc, char *const *argv, const struct afswtch *afp));
-void   notealias __P((const char *, int, int, const struct afswtch *afp));
-void   printb __P((const char *s, unsigned value, const char *bits));
-void   rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *));
-void   status __P((const struct afswtch *afp, int addrcount,
-                   struct sockaddr_dl *sdl, struct if_msghdr *ifm,
-                   struct ifa_msghdr *ifam));
-void   usage __P((void));
-
-typedef        void c_func __P((const char *cmd, int arg, int s, const struct afswtch *afp));
-
-c_func setifaddr, setifbroadaddr, setifdstaddr, setifnetmask;
-c_func setifipdst;
-c_func setifflags, setifmetric, setifmtu;
-
-
-#define        NEXTARG         0xffffff
-
-const
-struct cmd {
-       const   char *c_name;
-       int     c_parameter;            /* NEXTARG means next argv */
-       void    (*c_func) __P((const char *, int, int, const struct afswtch *afp));
-} cmds[] = {
-       { "up",         IFF_UP,         setifflags } ,
-       { "down",       -IFF_UP,        setifflags },
-       { "arp",        -IFF_NOARP,     setifflags },
-       { "-arp",       IFF_NOARP,      setifflags },
-       { "debug",      IFF_DEBUG,      setifflags },
-       { "-debug",     -IFF_DEBUG,     setifflags },
-       { "alias",      IFF_UP,         notealias },
-       { "-alias",     -IFF_UP,        notealias },
-       { "delete",     -IFF_UP,        notealias },
-#ifdef notdef
-#define        EN_SWABIPS      0x1000
-       { "swabips",    EN_SWABIPS,     setifflags },
-       { "-swabips",   -EN_SWABIPS,    setifflags },
-#endif
-       { "netmask",    NEXTARG,        setifnetmask },
-       { "range",      NEXTARG,        0 },
-       { "phase",      NEXTARG,        0 },
-       { "metric",     NEXTARG,        setifmetric },
-       { "broadcast",  NEXTARG,        setifbroadaddr },
-       { "ipdst",      NEXTARG,        setifipdst },
-       { "link0",      IFF_LINK0,      setifflags },
-       { "-link0",     -IFF_LINK0,     setifflags },
-       { "link1",      IFF_LINK1,      setifflags },
-       { "-link1",     -IFF_LINK1,     setifflags },
-       { "link2",      IFF_LINK2,      setifflags },
-       { "-link2",     -IFF_LINK2,     setifflags },
-#if USE_IF_MEDIA
-       { "media",      NEXTARG,        setmedia },
-       { "mediaopt",   NEXTARG,        setmediaopt },
-       { "-mediaopt",  NEXTARG,        unsetmediaopt },
-#endif
-       { "normal",     -IFF_LINK0,     setifflags },
-       { "compress",   IFF_LINK0,      setifflags },
-       { "noicmp",     IFF_LINK1,      setifflags },
-       { "mtu",        NEXTARG,        setifmtu },
-       { 0,            0,              setifaddr },
-       { 0,            0,              setifdstaddr },
-};
-
-/*
- * XNS support liberally adapted from code written at the University of
- * Maryland principally by James O'Toole and Chris Torek.
- */
-typedef        void af_status __P((int, struct rt_addrinfo *));
-typedef        void af_getaddr __P((const char *, int));
-
-af_status      in_status, ipx_status, at_status, ether_status;
-af_getaddr     in_getaddr, ipx_getaddr, at_getaddr;
-
-/* Known address families */
-const
-struct afswtch {
-       const char *af_name;
-       short af_af;
-       af_status *af_status;
-       af_getaddr *af_getaddr;
-       u_long af_difaddr;
-       u_long af_aifaddr;
-       caddr_t af_ridreq;
-       caddr_t af_addreq;
-} afs[] = {
-#define C(x) ((caddr_t) &x)
-       { "inet", AF_INET, in_status, in_getaddr,
-            SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) },
-       { "ether", AF_INET, ether_status, NULL },       /* XXX not real!! */
-#if 0  /* XXX conflicts with the media command */
-#if USE_IF_MEDIA
-       { "media", AF_INET, media_status, NULL },       /* XXX not real!! */
-#endif
-#endif
-       { 0,    0,          0,          0 }
-};
-
-/*
- * Expand the compacted form of addresses as returned via the
- * configuration read via sysctl().
- */
-
-#define ROUNDUP(a) \
-       ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
-#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+int    newaddr = 1;
+int    noload;
+int all;
+
+int bond_details = 0;
+int    supmedia = 0;
+#if TARGET_OS_EMBEDDED
+int    verbose = 1;
+int    showrtref = 1;
+#else /* !TARGET_OS_EMBEDDED */
+int    verbose = 0;
+int    showrtref = 0;
+#endif /* !TARGET_OS_EMBEDDED */
+int    printkeys = 0;          /* Print keying material for interfaces. */
+
+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,
+               struct ifaddrs *ifa);
+static char *bytes_to_str(unsigned long long bytes);
+static char *bps_to_str(unsigned long long rate);
+static char *ns_to_str(unsigned long long nsec);
+static void tunnel_status(int s);
+static void usage(void);
+static char *sched2str(unsigned int s);
+static char *tl2str(unsigned int s);
+static char *ift2str(unsigned int t, unsigned int f, unsigned int sf);
+
+static struct afswtch *af_getbyname(const char *name);
+static struct afswtch *af_getbyfamily(int af);
+static void af_other_status(int);
+
+static struct option *opts = NULL;
 
 void
 
 void
-rt_xaddrs(cp, cplim, rtinfo)
-       caddr_t cp, cplim;
-       struct rt_addrinfo *rtinfo;
+opt_register(struct option *p)
 {
 {
-       struct sockaddr *sa;
-       int i;
-
-       memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
-       for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
-               if ((rtinfo->rti_addrs & (1 << i)) == 0)
-                       continue;
-               rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
-               ADVANCE(cp, sa);
-       }
+       p->next = opts;
+       opts = p;
 }
 
 }
 
-
-void
-usage()
+static void
+usage(void)
 {
 {
-       fprintf(stderr, "%s\n%s\n%s\n%s\n",
-       "usage: ifconfig interface address_family [address [dest_address]]",
-       "                [parameters]",
-       "       ifconfig -a [-d] [-u] [address_family]",
-       "       ifconfig -l [-d] [-u] [address_family]");
+       char options[1024];
+       struct option *p;
+
+       /* XXX not right but close enough for now */
+       options[0] = '\0';
+       for (p = opts; p != NULL; p = p->next) {
+               strlcat(options, p->opt_usage, sizeof(options));
+               strlcat(options, " ", sizeof(options));
+       }
+
+       fprintf(stderr,
+       "usage: ifconfig %sinterface address_family [address [dest_address]]\n"
+       "                [parameters]\n"
+       "       ifconfig interface create\n"
+       "       ifconfig -a %s[-d] [-m] [-u] [-v] [address_family]\n"
+       "       ifconfig -l [-d] [-u] [address_family]\n"
+       "       ifconfig %s[-d] [-m] [-u] [-v]\n",
+               options, options, options);
        exit(1);
 }
 
 int
        exit(1);
 }
 
 int
-main(argc, argv)
-       int argc;
-       char *const *argv;
-{
-       int c;
-       int all, namesonly, downonly, uponly;
-       int foundit = 0, need_nl = 0;
-       const struct afswtch *afp = 0;
-       int addrcount;
-       struct  if_msghdr *ifm, *nextifm;
-       struct  ifa_msghdr *ifam;
-       struct  sockaddr_dl *sdl;
-       char    *buf, *lim, *next;
-
-
-       size_t needed;
-       int mib[6];
+main(int argc, char *argv[])
+{
+       int c, namesonly, downonly, uponly;
+       const struct afswtch *afp = NULL;
+       int ifindex;
+       struct ifaddrs *ifap, *ifa;
+       struct ifreq paifr;
+       const struct sockaddr_dl *sdl;
+       char options[1024], *cp;
+       const char *ifname;
+       struct option *p;
+       size_t iflen;
+
+       all = downonly = uponly = namesonly = noload = 0;
 
        /* Parse leading line options */
 
        /* Parse leading line options */
-       all = downonly = uponly = namesonly = 0;
-       while ((c = getopt(argc, argv, "adlmu")) != -1) {
+#ifndef __APPLE__
+       strlcpy(options, "adklmnuv", sizeof(options));
+#else
+       strlcpy(options, "abdlmruv", sizeof(options));
+#endif
+       for (p = opts; p != NULL; p = p->next)
+               strlcat(options, p->opt, sizeof(options));
+       while ((c = getopt(argc, argv, options)) != -1) {
                switch (c) {
                case 'a':       /* scan all interfaces */
                        all++;
                        break;
                switch (c) {
                case 'a':       /* scan all interfaces */
                        all++;
                        break;
+               case 'b':       /* bond detailed output */
+                       bond_details++;
+                       break;                          
+               case 'd':       /* restrict scan to "down" interfaces */
+                       downonly++;
+                       break;
+#ifndef __APPLE__
+               case 'k':
+                       printkeys++;
+                       break;
+#endif
                case 'l':       /* scan interface names only */
                        namesonly++;
                        break;
                case 'l':       /* scan interface names only */
                        namesonly++;
                        break;
-               case 'd':       /* restrict scan to "down" interfaces */
-                       downonly++;
+               case 'm':       /* show media choices in status */
+                       supmedia = 1;
+                       break;
+#ifndef __APPLE__
+               case 'n':       /* suppress module loading */
+                       noload++;
+                       break;
+#endif
+               case 'r':
+                       showrtref++;
                        break;
                case 'u':       /* restrict scan to "up" interfaces */
                        uponly++;
                        break;
                        break;
                case 'u':       /* restrict scan to "up" interfaces */
                        uponly++;
                        break;
-               case 'm':       /* show media choices in status */
-                       /* ignored for compatibility */
+               case 'v':
+                       verbose++;
                        break;
                default:
                        break;
                default:
-                       usage();
+                       for (p = opts; p != NULL; p = p->next)
+                               if (p->opt[0] == c) {
+                                       p->cb(optarg);
+                                       break;
+                               }
+                       if (p == NULL)
+                               usage();
                        break;
                }
        }
        argc -= optind;
        argv += optind;
 
                        break;
                }
        }
        argc -= optind;
        argv += optind;
 
-       /* -l cannot be used with -a or -m */
-       if (namesonly && all)
+       /* -l cannot be used with -a or -q or -m or -b */
+       if (namesonly &&
+           (all || supmedia || bond_details))
                usage();
 
        /* nonsense.. */
        if (uponly && downonly)
                usage();
 
                usage();
 
        /* nonsense.. */
        if (uponly && downonly)
                usage();
 
+       /* no arguments is equivalent to '-a' */
+       if (!namesonly && argc < 1)
+               all = 1;
+
        /* -a and -l allow an address family arg to limit the output */
        if (all || namesonly) {
                if (argc > 1)
                        usage();
 
        /* -a and -l allow an address family arg to limit the output */
        if (all || namesonly) {
                if (argc > 1)
                        usage();
 
+               ifname = NULL;
                if (argc == 1) {
                if (argc == 1) {
-                       for (afp = afs; afp->af_name; afp++)
-                               if (strcmp(afp->af_name, *argv) == 0) {
-                                       argc--, argv++;
-                                       break;
-                               }
-                       if (afp->af_name == NULL)
+                       afp = af_getbyname(*argv);
+                       if (afp == NULL)
                                usage();
                                usage();
+                       if (afp->af_name != NULL)
+                               argc--, argv++;
                        /* leave with afp non-zero */
                }
        } else {
                        /* leave with afp non-zero */
                }
        } else {
@@ -304,173 +284,316 @@ main(argc, argv)
                if (argc < 1)
                        usage();
 
                if (argc < 1)
                        usage();
 
-               strncpy(name, *argv, sizeof(name));
+               ifname = *argv;
                argc--, argv++;
                argc--, argv++;
+
+#ifdef notdef
+               /* check and maybe load support for this interface */
+               ifmaybeload(ifname);
+#endif
+               ifindex = if_nametoindex(ifname);
+               if (ifindex == 0) {
+                       /*
+                        * NOTE:  We must special-case the `create' command
+                        * right here as we would otherwise fail when trying
+                        * to find the interface.
+                        */
+                       if (argc > 0 && (strcmp(argv[0], "create") == 0 ||
+                           strcmp(argv[0], "plumb") == 0)) {
+                               iflen = strlcpy(name, ifname, sizeof(name));
+                               if (iflen >= sizeof(name))
+                                       errx(1, "%s: cloning name too long",
+                                           ifname);
+                               ifconfig(argc, argv, 1, NULL);
+                               exit(0);
+                       }
+                       errx(1, "interface %s does not exist", ifname);
+               }
        }
 
        /* Check for address family */
        if (argc > 0) {
        }
 
        /* Check for address family */
        if (argc > 0) {
-               for (afp = afs; afp->af_name; afp++)
-                       if (strcmp(afp->af_name, *argv) == 0) {
-                               argc--, argv++;
-                               break;
-                       }
-               if (afp->af_name == NULL)
-                       afp = NULL;     /* not a family, NULL */
+               afp = af_getbyname(*argv);
+               if (afp != NULL)
+                       argc--, argv++;
        }
 
        }
 
-       mib[0] = CTL_NET;
-       mib[1] = PF_ROUTE;
-       mib[2] = 0;
-       mib[3] = 0;     /* address family */
-       mib[4] = NET_RT_IFLIST;
-       mib[5] = 0;
-
-       /* if particular family specified, only ask about it */
-       if (afp)
-               mib[3] = afp->af_af;
-
-       if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
-               errx(1, "iflist-sysctl-estimate");
-       if ((buf = malloc(needed)) == NULL)
-               errx(1, "malloc");
-       if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
-               errx(1, "actual retrieval of interface table");
-       lim = buf + needed;
-
-       next = buf;
-       while (next < lim) {
-
-               ifm = (struct if_msghdr *)next;
-               
-               if (ifm->ifm_type == RTM_IFINFO) {
-                       sdl = (struct sockaddr_dl *)(ifm + 1);
-                       flags = ifm->ifm_flags;
-               } else {
-                       fprintf(stderr, "out of sync parsing NET_RT_IFLIST\n");
-                       fprintf(stderr, "expected %d, got %d\n", RTM_IFINFO,
-                               ifm->ifm_type);
-                       fprintf(stderr, "msglen = %d\n", ifm->ifm_msglen);
-                       fprintf(stderr, "buf:%p, next:%p, lim:%p\n", buf, next,
-                               lim);
-                       exit (1);
+       if (getifaddrs(&ifap) != 0)
+               err(EXIT_FAILURE, "getifaddrs");
+       cp = NULL;
+       ifindex = 0;
+       for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+               memset(&paifr, 0, sizeof(paifr));
+               strncpy(paifr.ifr_name, ifa->ifa_name, sizeof(paifr.ifr_name));
+               if (sizeof(paifr.ifr_addr) >= ifa->ifa_addr->sa_len) {
+                       memcpy(&paifr.ifr_addr, ifa->ifa_addr,
+                           ifa->ifa_addr->sa_len);
                }
 
                }
 
-               next += ifm->ifm_msglen;
-               ifam = NULL;
-               addrcount = 0;
-               while (next < lim) {
-
-                       nextifm = (struct if_msghdr *)next;
-
-                       if (nextifm->ifm_type != RTM_NEWADDR)
-                               break;
-
-                       if (ifam == NULL)
-                               ifam = (struct ifa_msghdr *)nextifm;
-
-                       addrcount++;
-                       next += nextifm->ifm_msglen;
+               if (ifname != NULL && strcmp(ifname, ifa->ifa_name) != 0)
+                       continue;
+               if (ifa->ifa_addr->sa_family == AF_LINK)
+                       sdl = (const struct sockaddr_dl *) ifa->ifa_addr;
+               else
+                       sdl = NULL;
+               if (cp != NULL && strcmp(cp, ifa->ifa_name) == 0)
+                       continue;
+               iflen = strlcpy(name, ifa->ifa_name, sizeof(name));
+               if (iflen >= sizeof(name)) {
+                       warnx("%s: interface name too long, skipping",
+                           ifa->ifa_name);
+                       continue;
                }
                }
+               cp = ifa->ifa_name;
 
 
-               if (all || namesonly) {
-                       if (uponly)
-                               if ((flags & IFF_UP) == 0)
-                                       continue; /* not up */
-                       if (downonly)
-                               if (flags & IFF_UP)
-                                       continue; /* not down */
-                       strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
-                       name[sdl->sdl_nlen] = '\0';
-                       if (namesonly) {
-                               if (afp == NULL ||
-                                       afp->af_status != ether_status ||
-                                       sdl->sdl_type == IFT_ETHER) {
-                                       if (need_nl)
-                                               putchar(' ');
-                                       fputs(name, stdout);
-                                       need_nl++;
-                               }
-                               continue;
-                       }
-               } else {
-                       if (strlen(name) != sdl->sdl_nlen)
-                               continue; /* not same len */
-                       if (strncmp(name, sdl->sdl_data, sdl->sdl_nlen) != 0)
-                               continue; /* not same name */
+               if (downonly && (ifa->ifa_flags & IFF_UP) != 0)
+                       continue;
+               if (uponly && (ifa->ifa_flags & IFF_UP) == 0)
+                       continue;
+               ifindex++;
+               /*
+                * Are we just listing the interfaces?
+                */
+               if (namesonly) {
+                       if (ifindex > 1)
+                               printf(" ");
+                       fputs(name, stdout);
+                       continue;
                }
 
                if (argc > 0)
                }
 
                if (argc > 0)
-                       ifconfig(argc, argv, afp);
+                       ifconfig(argc, argv, 0, afp);
                else
                else
-                       status(afp, addrcount, sdl, ifm, ifam);
+                       status(afp, sdl, ifa);
+       }
+       if (namesonly)
+               printf("\n");
+       freeifaddrs(ifap);
 
 
-               if (all == 0 && namesonly == 0) {
-                       foundit++; /* flag it as 'done' */
-                       break;
-               }
+       exit(0);
+}
+
+static struct afswtch *afs = NULL;
+
+void
+af_register(struct afswtch *p)
+{
+       p->af_next = afs;
+       afs = p;
+}
+
+static struct afswtch *
+af_getbyname(const char *name)
+{
+       struct afswtch *afp;
+
+       for (afp = afs; afp !=  NULL; afp = afp->af_next)
+               if (strcmp(afp->af_name, name) == 0)
+                       return afp;
+       return NULL;
+}
+
+static struct afswtch *
+af_getbyfamily(int af)
+{
+       struct afswtch *afp;
+
+       for (afp = afs; afp != NULL; afp = afp->af_next)
+               if (afp->af_af == af)
+                       return afp;
+       return NULL;
+}
+
+static void
+af_other_status(int s)
+{
+       struct afswtch *afp;
+       uint8_t afmask[howmany(AF_MAX, NBBY)];
+
+       memset(afmask, 0, sizeof(afmask));
+       for (afp = afs; afp != NULL; afp = afp->af_next) {
+               if (afp->af_other_status == NULL)
+                       continue;
+               if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af))
+                       continue;
+               afp->af_other_status(s);
+               setbit(afmask, afp->af_af);
        }
        }
-       free(buf);
+}
 
 
-       if (namesonly && need_nl > 0)
-               putchar('\n');
+static void
+af_all_tunnel_status(int s)
+{
+       struct afswtch *afp;
+       uint8_t afmask[howmany(AF_MAX, NBBY)];
 
 
-       if (all == 0 && namesonly == 0 && foundit == 0)
-               errx(1, "interface %s does not exist", name);
+       memset(afmask, 0, sizeof(afmask));
+       for (afp = afs; afp != NULL; afp = afp->af_next) {
+               if (afp->af_status_tunnel == NULL)
+                       continue;
+               if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af))
+                       continue;
+               afp->af_status_tunnel(s);
+               setbit(afmask, afp->af_af);
+       }
+}
 
 
+static struct cmd *cmds = NULL;
 
 
-       exit (0);
+void
+cmd_register(struct cmd *p)
+{
+       p->c_next = cmds;
+       cmds = p;
 }
 
 }
 
+static const struct cmd *
+cmd_lookup(const char *name)
+{
+#define        N(a)    (sizeof(a)/sizeof(a[0]))
+       const struct cmd *p;
+
+       for (p = cmds; p != NULL; p = p->c_next)
+               if (strcmp(name, p->c_name) == 0)
+                       return p;
+       return NULL;
+#undef N
+}
 
 
-int
-ifconfig(argc, argv, afp)
-       int argc;
-       char *const *argv;
-       const struct afswtch *afp;
+struct callback {
+       callback_func *cb_func;
+       void    *cb_arg;
+       struct callback *cb_next;
+};
+static struct callback *callbacks = NULL;
+
+void
+callback_register(callback_func *func, void *arg)
 {
 {
+       struct callback *cb;
+
+       cb = malloc(sizeof(struct callback));
+       if (cb == NULL)
+               errx(1, "unable to allocate memory for callback");
+       cb->cb_func = func;
+       cb->cb_arg = arg;
+       cb->cb_next = callbacks;
+       callbacks = cb;
+}
+
+/* specially-handled commands */
+static void setifaddr(const char *, int, int, const struct afswtch *);
+static const struct cmd setifaddr_cmd = DEF_CMD("ifaddr", 0, setifaddr);
+
+static void setifdstaddr(const char *, int, int, const struct afswtch *);
+static const struct cmd setifdstaddr_cmd =
+       DEF_CMD("ifdstaddr", 0, setifdstaddr);
+
+static int
+ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *afp)
+{
+       const struct afswtch *nafp;
+       struct callback *cb;
        int s;
 
        int s;
 
-       if (afp == NULL)
-               afp = &afs[0];
-       ifr.ifr_addr.sa_family = afp->af_af;
        strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
        strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+top:
+       if (afp == NULL)
+               afp = af_getbyname("inet");
+       ifr.ifr_addr.sa_family =
+               afp->af_af == AF_LINK || afp->af_af == AF_UNSPEC ?
+               AF_INET : afp->af_af;
 
        if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0)
 
        if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0)
-               err(1, "socket");
+               err(1, "socket(family %u,SOCK_DGRAM", ifr.ifr_addr.sa_family);
 
        while (argc > 0) {
 
        while (argc > 0) {
-               register const struct cmd *p;
-
-               for (p = cmds; p->c_name; p++)
-                       if (strcmp(*argv, p->c_name) == 0)
-                               break;
-               if (p->c_name == 0 && setaddr)
-                       p++;    /* got src, do dst */
-               if (p->c_func) {
+               const struct cmd *p;
+
+               p = cmd_lookup(*argv);
+               if (p == NULL) {
+                       /*
+                        * Not a recognized command, choose between setting
+                        * the interface address and the dst address.
+                        */
+                       p = (setaddr ? &setifdstaddr_cmd : &setifaddr_cmd);
+               }
+               if (p->c_u.c_func || p->c_u.c_func2) {
+                       if (iscreate && !p->c_iscloneop) { 
+                               /*
+                                * Push the clone create callback so the new
+                                * device is created and can be used for any
+                                * remaining arguments.
+                                */
+                               cb = callbacks;
+                               if (cb == NULL)
+                                       errx(1, "internal error, no callback");
+                               callbacks = cb->cb_next;
+                               cb->cb_func(s, cb->cb_arg);
+                               iscreate = 0;
+                               /*
+                                * Handle any address family spec that
+                                * immediately follows and potentially
+                                * recreate the socket.
+                                */
+                               nafp = af_getbyname(*argv);
+                               if (nafp != NULL) {
+                                       argc--, argv++;
+                                       if (nafp != afp) {
+                                               close(s);
+                                               afp = nafp;
+                                               goto top;
+                                       }
+                               }
+                       }
                        if (p->c_parameter == NEXTARG) {
                                if (argv[1] == NULL)
                                        errx(1, "'%s' requires argument",
                                            p->c_name);
                        if (p->c_parameter == NEXTARG) {
                                if (argv[1] == NULL)
                                        errx(1, "'%s' requires argument",
                                            p->c_name);
-                               (*p->c_func)(argv[1], 0, s, afp);
+                               p->c_u.c_func(argv[1], 0, s, afp);
                                argc--, argv++;
                                argc--, argv++;
+                       } else if (p->c_parameter == OPTARG) {
+                               p->c_u.c_func(argv[1], 0, s, afp);
+                               if (argv[1] != NULL)
+                                       argc--, argv++;
+                       } else if (p->c_parameter == NEXTARG2) {
+                               if (argc < 3)
+                                       errx(1, "'%s' requires 2 arguments",
+                                           p->c_name);
+                               p->c_u.c_func2(argv[1], argv[2], s, afp);
+                               argc -= 2, argv += 2;
                        } else
                        } else
-                               (*p->c_func)(*argv, p->c_parameter, s, afp);
+                               p->c_u.c_func(*argv, p->c_parameter, s, afp);
                }
                argc--, argv++;
        }
 
                }
                argc--, argv++;
        }
 
+       /*
+        * Do any post argument processing required by the address family.
+        */
+       if (afp->af_postproc != NULL)
+               afp->af_postproc(s, afp);
+       /*
+        * Do deferred callbacks registered while processing
+        * command-line arguments.
+        */
+       for (cb = callbacks; cb != NULL; cb = cb->cb_next)
+               cb->cb_func(s, cb->cb_arg);
+       /*
+        * Do deferred operations.
+        */
        if (clearaddr) {
                if (afp->af_ridreq == NULL || afp->af_difaddr == 0) {
                        warnx("interface %s cannot change %s addresses!",
                              name, afp->af_name);
        if (clearaddr) {
                if (afp->af_ridreq == NULL || afp->af_difaddr == 0) {
                        warnx("interface %s cannot change %s addresses!",
                              name, afp->af_name);
-                       clearaddr = NULL;
+                       clearaddr = 0;
                }
        }
        if (clearaddr) {
                int ret;
                strncpy(afp->af_ridreq, name, sizeof ifr.ifr_name);
                }
        }
        if (clearaddr) {
                int ret;
                strncpy(afp->af_ridreq, name, sizeof ifr.ifr_name);
-               if ((ret = ioctl(s, afp->af_difaddr, afp->af_ridreq)) < 0) {
+               ret = ioctl(s, afp->af_difaddr, afp->af_ridreq);
+               if (ret < 0) {
                        if (errno == EADDRNOTAVAIL && (doalias >= 0)) {
                                /* means no previous address for interface */
                        } else
                        if (errno == EADDRNOTAVAIL && (doalias >= 0)) {
                                /* means no previous address for interface */
                        } else
@@ -478,139 +601,193 @@ ifconfig(argc, argv, afp)
                }
        }
        if (newaddr) {
                }
        }
        if (newaddr) {
-               if (afp->af_ridreq == NULL || afp->af_difaddr == 0) {
+               if (afp->af_addreq == NULL || afp->af_aifaddr == 0) {
                        warnx("interface %s cannot change %s addresses!",
                              name, afp->af_name);
                        warnx("interface %s cannot change %s addresses!",
                              name, afp->af_name);
-                       newaddr = NULL;
+                       newaddr = 0;
                }
        }
                }
        }
-       if (newaddr) {
+       if (newaddr && (setaddr || setmask)) {
                strncpy(afp->af_addreq, name, sizeof ifr.ifr_name);
                if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0)
                        Perror("ioctl (SIOCAIFADDR)");
        }
                strncpy(afp->af_addreq, name, sizeof ifr.ifr_name);
                if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0)
                        Perror("ioctl (SIOCAIFADDR)");
        }
+
        close(s);
        return(0);
 }
        close(s);
        return(0);
 }
-#define RIDADDR 0
-#define ADDR   1
-#define MASK   2
-#define DSTADDR        3
 
 /*ARGSUSED*/
 
 /*ARGSUSED*/
-void
-setifaddr(addr, param, s, afp)
-       const char *addr;
-       int param;
-       int s;
-       const struct afswtch *afp;
+static void
+setifaddr(const char *addr, int param, int s, const struct afswtch *afp)
 {
 {
+       if (afp->af_getaddr == NULL)
+               return;
        /*
         * Delay the ioctl to set the interface addr until flags are all set.
         * The address interpretation may depend on the flags,
         * and the flags may change when the address is set.
         */
        /*
         * Delay the ioctl to set the interface addr until flags are all set.
         * The address interpretation may depend on the flags,
         * and the flags may change when the address is set.
         */
-       newaddr = 1;
        setaddr++;
        setaddr++;
-       if (doalias == 0)
+       if (doalias == 0 && afp->af_af != AF_LINK)
                clearaddr = 1;
                clearaddr = 1;
-       (*afp->af_getaddr)(addr, (doalias >= 0 ? ADDR : RIDADDR));
+       afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR));
 }
 
 }
 
-void
-setifnetmask(addr, dummy, s, afp)
-       const char *addr;
-       int dummy ;
-       int s;
-       const struct afswtch *afp;
+static void
+settunnel(const char *src, const char *dst, int s, const struct afswtch *afp)
+{
+       struct addrinfo *srcres, *dstres;
+       int ecode;
+
+       if (afp->af_settunnel == NULL) {
+               warn("address family %s does not support tunnel setup",
+                       afp->af_name);
+               return;
+       }
+
+       if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0)
+               errx(1, "error in parsing address string: %s",
+                   gai_strerror(ecode));
+
+       if ((ecode = getaddrinfo(dst, NULL, NULL, &dstres)) != 0)  
+               errx(1, "error in parsing address string: %s",
+                   gai_strerror(ecode));
+
+       if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family)
+               errx(1,
+                   "source and destination address families do not match");
+
+       afp->af_settunnel(s, srcres, dstres);
+
+       freeaddrinfo(srcres);
+       freeaddrinfo(dstres);
+}
+
+/* ARGSUSED */
+static void
+deletetunnel(const char *vname, int param, int s, const struct afswtch *afp)
 {
 {
-       (*afp->af_getaddr)(addr, MASK);
+
+       if (ioctl(s, SIOCDIFPHYADDR, &ifr) < 0)
+               err(1, "SIOCDIFPHYADDR");
 }
 
 }
 
-void
-setifbroadaddr(addr, dummy, s, afp)
-       const char *addr;
-       int dummy ;
-       int s;
-       const struct afswtch *afp;
+static void
+setifnetmask(const char *addr, int dummy __unused, int s,
+    const struct afswtch *afp)
 {
 {
-       (*afp->af_getaddr)(addr, DSTADDR);
+       if (afp->af_getaddr != NULL) {
+               setmask++;
+               afp->af_getaddr(addr, MASK);
+       }
 }
 
 }
 
-void
-setifipdst(addr, dummy, s, afp)
-       const char *addr;
-       int dummy ;
-       int s;
-       const struct afswtch *afp;
+static void
+setifbroadaddr(const char *addr, int dummy __unused, int s,
+    const struct afswtch *afp)
 {
 {
-       in_getaddr(addr, DSTADDR);
-       setipdst++;
+       if (afp->af_getaddr != NULL)
+               afp->af_getaddr(addr, DSTADDR);
+}
+
+static void
+setifipdst(const char *addr, int dummy __unused, int s,
+    const struct afswtch *afp)
+{
+       const struct afswtch *inet;
+
+       inet = af_getbyname("inet");
+       if (inet == NULL)
+               return;
+       inet->af_getaddr(addr, DSTADDR);
        clearaddr = 0;
        newaddr = 0;
 }
        clearaddr = 0;
        newaddr = 0;
 }
-#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr))
 
 
-void
-notealias(addr, param, s, afp)
-       const char *addr;
-       int param;
-       int s;
-       const struct afswtch *afp;
+static void
+notealias(const char *addr, int param, int s, const struct afswtch *afp)
 {
 {
+#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr))
        if (setaddr && doalias == 0 && param < 0)
        if (setaddr && doalias == 0 && param < 0)
-               bcopy((caddr_t)rqtosa(af_addreq),
-                     (caddr_t)rqtosa(af_ridreq),
-                     rqtosa(af_addreq)->sa_len);
+               if (afp->af_addreq != NULL && afp->af_ridreq != NULL)
+                       bcopy((caddr_t)rqtosa(af_addreq),
+                             (caddr_t)rqtosa(af_ridreq),
+                             rqtosa(af_addreq)->sa_len);
        doalias = param;
        if (param < 0) {
                clearaddr = 1;
                newaddr = 0;
        } else
                clearaddr = 0;
        doalias = param;
        if (param < 0) {
                clearaddr = 1;
                newaddr = 0;
        } else
                clearaddr = 0;
+#undef rqtosa
 }
 
 /*ARGSUSED*/
 }
 
 /*ARGSUSED*/
-void
-setifdstaddr(addr, param, s, afp)
-       const char *addr;
-       int param ;
-       int s;
-       const struct afswtch *afp;
+static void
+setifdstaddr(const char *addr, int param __unused, int s, 
+    const struct afswtch *afp)
 {
 {
-       (*afp->af_getaddr)(addr, DSTADDR);
+       if (afp->af_getaddr != NULL)
+               afp->af_getaddr(addr, DSTADDR);
 }
 
 }
 
-void
-setifflags(vname, value, s, afp)
-       const char *vname;
-       int value;
-       int s;
-       const struct afswtch *afp;
+/*
+ * Note: doing an SIOCIGIFFLAGS scribbles on the union portion
+ * of the ifreq structure, which may confuse other parts of ifconfig.
+ * Make a private copy so we can avoid that.
+ */
+static void
+setifflags(const char *vname, int value, int s, const struct afswtch *afp)
 {
 {
-       if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+       struct ifreq            my_ifr;
+       int flags;
+
+       bcopy((char *)&ifr, (char *)&my_ifr, sizeof(struct ifreq));
+
+       if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) {
                Perror("ioctl (SIOCGIFFLAGS)");
                exit(1);
        }
                Perror("ioctl (SIOCGIFFLAGS)");
                exit(1);
        }
-       strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
-       flags = ifr.ifr_flags;
-
+       strncpy(my_ifr.ifr_name, name, sizeof (my_ifr.ifr_name));
+       flags = my_ifr.ifr_flags;
+       
        if (value < 0) {
                value = -value;
                flags &= ~value;
        } else
                flags |= value;
        if (value < 0) {
                value = -value;
                flags &= ~value;
        } else
                flags |= value;
-       ifr.ifr_flags = flags;
-       if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0)
+       my_ifr.ifr_flags = flags & 0xffff;
+       if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0)
                Perror(vname);
 }
 
                Perror(vname);
 }
 
+#ifdef SIOCGIFCAP
 void
 void
-setifmetric(val, dummy, s, afp)
-       const char *val;
-       int dummy ;
-       int s;
-       const struct afswtch *afp;
+setifcap(const char *vname, int value, int s, const struct afswtch *afp)
+{
+       int flags;
+
+       if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) < 0) {
+               Perror("ioctl (SIOCGIFCAP)");
+               exit(1);
+       }
+       flags = ifr.ifr_curcap;
+       if (value < 0) {
+               value = -value;
+               flags &= ~value;
+       } else
+               flags |= value;
+       flags &= ifr.ifr_reqcap;
+       ifr.ifr_reqcap = flags;
+       if (ioctl(s, SIOCSIFCAP, (caddr_t)&ifr) < 0)
+               Perror(vname);
+}
+#endif
+
+static void
+setifmetric(const char *val, int dummy __unused, int s, 
+    const struct afswtch *afp)
 {
        strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
        ifr.ifr_metric = atoi(val);
 {
        strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
        ifr.ifr_metric = atoi(val);
@@ -618,12 +795,9 @@ setifmetric(val, dummy, s, afp)
                warn("ioctl (set metric)");
 }
 
                warn("ioctl (set metric)");
 }
 
-void
-setifmtu(val, dummy, s, afp)
-       const char *val;
-       int dummy ;
-       int s;
-       const struct afswtch *afp;
+static void
+setifmtu(const char *val, int dummy __unused, int s, 
+    const struct afswtch *afp)
 {
        strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
        ifr.ifr_mtu = atoi(val);
 {
        strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
        ifr.ifr_mtu = atoi(val);
@@ -631,164 +805,713 @@ setifmtu(val, dummy, s, afp)
                warn("ioctl (set mtu)");
 }
 
                warn("ioctl (set mtu)");
 }
 
+#ifndef __APPLE__
+static void
+setifname(const char *val, int dummy __unused, int s, 
+    const struct afswtch *afp)
+{
+       char *newname;
+
+       newname = strdup(val);
+       if (newname == NULL) {
+               warn("no memory to set ifname");
+               return;
+       }
+       ifr.ifr_data = newname;
+       if (ioctl(s, SIOCSIFNAME, (caddr_t)&ifr) < 0) {
+               warn("ioctl (set name)");
+               free(newname);
+               return;
+       }
+       strlcpy(name, newname, sizeof(name));
+       free(newname);
+}
+#endif
+
+static void
+setrouter(const char *vname, int value, int s, const struct afswtch *afp)
+{
+       if (afp->af_setrouter == NULL) {
+               warn("address family %s does not support router mode",
+                   afp->af_name);
+               return;
+       }
+
+       afp->af_setrouter(s, value);
+}
+
+static void
+setifdesc(const char *val, int dummy __unused, int s, const struct afswtch *afp)
+{
+       struct if_descreq ifdr;
+
+       bzero(&ifdr, sizeof (ifdr));
+       strncpy(ifdr.ifdr_name, name, sizeof (ifdr.ifdr_name));
+       ifdr.ifdr_len = strlen(val);
+       strncpy((char *)ifdr.ifdr_desc, val, sizeof (ifdr.ifdr_desc));
+
+       if (ioctl(s, SIOCSIFDESC, (caddr_t)&ifdr) < 0) {
+               warn("ioctl (set desc)");
+       }
+}
+
+static void
+settbr(const char *val, int dummy __unused, int s, const struct afswtch *afp)
+{
+       struct if_linkparamsreq iflpr;
+       long double bps;
+       u_int64_t rate;
+       u_int32_t percent = 0;
+       char *cp;
+
+       errno = 0;
+       bzero(&iflpr, sizeof (iflpr));
+       strncpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name));
+
+       bps = strtold(val, &cp);
+       if (val == cp || errno != 0) {
+               warn("Invalid value '%s'", val);
+               return;
+       }
+       rate = (u_int64_t)bps;
+       if (cp != NULL) {
+               if (!strcmp(cp, "b") || !strcmp(cp, "bps")) {
+                       ; /* nothing */
+               } else if (!strcmp(cp, "Kb") || !strcmp(cp, "Kbps")) {
+                       rate *= 1000;
+               } else if (!strcmp(cp, "Mb") || !strcmp(cp, "Mbps")) {
+                       rate *= 1000 * 1000;
+               } else if (!strcmp(cp, "Gb") || !strcmp(cp, "Gbps")) {
+                       rate *= 1000 * 1000 * 1000;
+               } else if (!strcmp(cp, "%")) {
+                       percent = rate;
+                       if (percent == 0 || percent > 100) {
+                               printf("Value out of range '%s'", val);
+                               return;
+                       }
+               } else if (*cp != '\0') {
+                       printf("Unknown unit '%s'", cp);
+                       return;
+               }
+       }
+       iflpr.iflpr_output_tbr_rate = rate;
+       iflpr.iflpr_output_tbr_percent = percent;
+       if (ioctl(s, SIOCSIFLINKPARAMS, &iflpr) < 0 &&
+           errno != ENOENT && errno != ENXIO && errno != ENODEV) {
+               warn("ioctl (set link params)");
+       } else if (errno == ENXIO) {
+               printf("TBR cannot be set on %s\n", name);
+       } else if (errno == ENOENT || rate == 0) {
+               printf("%s: TBR is now disabled\n", name);
+       } else if (errno == ENODEV) {
+               printf("%s: requires absolute TBR rate\n", name);
+       } else if (percent != 0) {
+               printf("%s: TBR rate set to %u%% of effective link rate\n",
+                   name, percent);
+       } else {
+               printf("%s: TBR rate set to %s\n", name, bps_to_str(rate));
+       }
+}
+
+static void
+setthrottle(const char *val, int dummy __unused, int s,
+    const struct afswtch *afp)
+{
+       struct if_throttlereq iftr;
+       char *cp;
+
+       errno = 0;
+       bzero(&iftr, sizeof (iftr));
+       strncpy(iftr.ifthr_name, name, sizeof (iftr.ifthr_name));
+
+       iftr.ifthr_level = strtold(val, &cp);
+       if (val == cp || errno != 0) {
+               warn("Invalid value '%s'", val);
+               return;
+       }
+
+       if (ioctl(s, SIOCSIFTHROTTLE, &iftr) < 0 && errno != ENXIO) {
+               warn("ioctl (set throttling level)");
+       } else if (errno == ENXIO) {
+               printf("throttling level cannot be set on %s\n", name);
+       } else {
+               printf("%s: throttling level set to %d\n", name,
+                   iftr.ifthr_level);
+       }
+}
+
+static void
+setlog(const char *val, int dummy __unused, int s,
+    const struct afswtch *afp)
+{
+       char *cp;
+
+       errno = 0;
+       strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+
+       ifr.ifr_log.ifl_level = strtold(val, &cp);
+       if (val == cp || errno != 0) {
+               warn("Invalid value '%s'", val);
+               return;
+       }
+       ifr.ifr_log.ifl_flags = (IFRLOGF_DLIL|IFRLOGF_FAMILY|IFRLOGF_DRIVER|
+           IFRLOGF_FIRMWARE);
+
+       if (ioctl(s, SIOCSIFLOG, &ifr) < 0)
+               warn("ioctl (set logging parameters)");
+}
+
+void
+setcl2k(const char *vname, int value, int s, const struct afswtch *afp)
+{
+       strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+       ifr.ifr_ifru.ifru_2kcl = value;
+       
+       if (ioctl(s, SIOCSIF2KCL, (caddr_t)&ifr) < 0)
+               Perror(vname);
+}
+
+void
+setexpensive(const char *vname, int value, int s, const struct afswtch *afp)
+{
+       strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+       ifr.ifr_ifru.ifru_expensive = value;
+       
+       if (ioctl(s, SIOCSIFEXPENSIVE, (caddr_t)&ifr) < 0)
+               Perror(vname);
+}
+
+
+void
+setecnmode(const char *val, int dummy __unused, int s,
+    const struct afswtch *afp)
+{
+       char *cp;
+
+       if (strcmp(val, "default") == 0)
+               ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_DEFAULT;
+       else if (strcmp(val, "enable") == 0)
+               ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_ENABLE;
+       else if (strcmp(val, "disable") == 0)
+               ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_DISABLE;
+       else {
+               ifr.ifr_ifru.ifru_ecn_mode = strtold(val, &cp);
+               if (val == cp || errno != 0) {
+                       warn("Invalid ECN mode value '%s'", val);
+                       return;
+               }
+       }
+       
+       strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+       
+       if (ioctl(s, SIOCSECNMODE, (caddr_t)&ifr) < 0)
+               Perror("ioctl(SIOCSECNMODE)");
+}
+
 
 #define        IFFBITS \
 
 #define        IFFBITS \
-"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6b6\7RUNNING" \
+"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \
 "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \
 "\20MULTICAST"
 
 "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \
 "\20MULTICAST"
 
+#define        IFEFBITS \
+"\020\1AUTOCONFIGURING\6IPV6_DISABLED\7ACCEPT_RTADV\10TXSTART\11RXPOLL" \
+"\12VLAN\13BOND\14ARPLL\15NOWINDOWSCALE\16NOAUTOIPV6LL\17EXPENSIVE\20ROUTER4" \
+"\21ROUTER6\22LOCALNET_PRIVATE\23ND6ALT\24RESTRICTED_RECV\25AWDL\26NOACKPRI" \
+"\27AWDL_RESTRICTED\30CL2K\31ECN_ENABLE\32ECN_DISABLE\35SENDLIST\36DIRECTLINK\40UPDOWNCHANGE"
+
+#define        IFCAPBITS \
+"\020\1RXCSUM\2TXCSUM\3VLAN_MTU\4VLAN_HWTAGGING\5JUMBO_MTU" \
+"\6TSO4\7TSO6\10LRO\11AV\12TXSTATUS"
+
+#define        IFRLOGF_BITS \
+"\020\1DLIL\21FAMILY\31DRIVER\35FIRMWARE"
+
 /*
  * Print the status of the interface.  If an address family was
 /*
  * Print the status of the interface.  If an address family was
- * specified, show it and it only; otherwise, show them all.
+ * specified, show only it; otherwise, show them all.
  */
  */
-void
-status(afp, addrcount, sdl, ifm, ifam)
-       const struct afswtch *afp;
-       int addrcount;
-       struct  sockaddr_dl *sdl;
-       struct if_msghdr *ifm;
-       struct ifa_msghdr *ifam;
-{
-       const struct afswtch *p = NULL;
-       struct  rt_addrinfo info;
+static void
+status(const struct afswtch *afp, const struct sockaddr_dl *sdl,
+       struct ifaddrs *ifa)
+{
+       struct ifaddrs *ift;
        int allfamilies, s;
        int allfamilies, s;
+       struct ifstat ifs;
+       struct if_descreq ifdr;
+       struct if_linkparamsreq iflpr;
+       int mib[6];
+       struct ifmibdata_supplemental ifmsupp;
+       size_t miblen = sizeof(struct ifmibdata_supplemental);
+       u_int64_t eflags = 0;
 
        if (afp == NULL) {
                allfamilies = 1;
 
        if (afp == NULL) {
                allfamilies = 1;
-               afp = &afs[0];
+               afp = af_getbyname("inet");
        } else
                allfamilies = 0;
 
        } else
                allfamilies = 0;
 
-       ifr.ifr_addr.sa_family = afp->af_af;
-       strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
-
-       if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0)
-               err(1, "socket");
+       ifr.ifr_addr.sa_family = afp->af_af == AF_LINK ? AF_INET : afp->af_af;
+       strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
 
 
-       /*
-        * XXX is it we are doing a SIOCGIFMETRIC etc for one family.
-        * is it possible that the metric and mtu can be different for
-        * each family?  If so, we have a format problem, because the
-        * metric and mtu is printed on the global the flags line.
-        */
-       if (ioctl(s, SIOCGIFMETRIC, (caddr_t)&ifr) < 0)
-               warn("ioctl (SIOCGIFMETRIC)");
-       else
-               metric = ifr.ifr_metric;
-
-       if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0)
-               warn("ioctl (SIOCGIFMTU)");
-       else
-               mtu = ifr.ifr_mtu;
+       s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0);
+       if (s < 0)
+               err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family);
 
        printf("%s: ", name);
 
        printf("%s: ", name);
-       printb("flags", flags, IFFBITS);
-       if (metric)
-               printf(" metric %d", metric);
-       if (mtu)
-               printf(" mtu %d", mtu);
+       printb("flags", ifa->ifa_flags, IFFBITS);
+       if (ioctl(s, SIOCGIFMETRIC, &ifr) != -1)
+               if (ifr.ifr_metric)
+                       printf(" metric %d", ifr.ifr_metric);
+       if (ioctl(s, SIOCGIFMTU, &ifr) != -1)
+               printf(" mtu %d", ifr.ifr_mtu);
+       if (showrtref && ioctl(s, SIOCGIFGETRTREFCNT, &ifr) != -1)
+               printf(" rtref %d", ifr.ifr_route_refcnt);
+    if (verbose) {
+        unsigned int ifindex = if_nametoindex(ifa->ifa_name);
+        if (ifindex != 0)
+            printf(" index %u", ifindex);
+    }
        putchar('\n');
 
        putchar('\n');
 
-       while (addrcount > 0) {
-               
-               info.rti_addrs = ifam->ifam_addrs;
+       if (verbose && ioctl(s, SIOCGIFEFLAGS, (caddr_t)&ifr) != -1 &&
+           (eflags = ifr.ifr_eflags) != 0) {
+               printb("\teflags", eflags, IFEFBITS);
+               putchar('\n');
+       }
 
 
-               /* Expand the compacted addresses */
-               rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
-                         &info);
+#ifdef SIOCGIFCAP      
+       if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) == 0) {
+               if (ifr.ifr_curcap != 0) {
+                       printb("\toptions", ifr.ifr_curcap, IFCAPBITS);
+                       putchar('\n');
+               }
+               if (supmedia && ifr.ifr_reqcap != 0) {
+                       printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS);
+                       putchar('\n');
+               }
+       }
+#endif
+       
+       tunnel_status(s);
 
 
-               if (!allfamilies) {
-                       if (afp->af_af == info.rti_info[RTAX_IFA]->sa_family &&
-#if USE_IF_MEDIA
-                           afp->af_status != media_status &&
+       for (ift = ifa; ift != NULL; ift = ift->ifa_next) {
+               if (ift->ifa_addr == NULL)
+                       continue;
+               if (strcmp(ifa->ifa_name, ift->ifa_name) != 0)
+                       continue;
+               if (allfamilies) {
+                       const struct afswtch *p;
+                       p = af_getbyfamily(ift->ifa_addr->sa_family);
+                       if (p != NULL && p->af_status != NULL)
+                               p->af_status(s, ift);
+               } else if (afp->af_af == ift->ifa_addr->sa_family)
+                       afp->af_status(s, ift);
+       }
+#if 0
+       if (allfamilies || afp->af_af == AF_LINK) {
+               const struct afswtch *lafp;
+
+               /*
+                * Hack; the link level address is received separately
+                * from the routing information so any address is not
+                * handled above.  Cobble together an entry and invoke
+                * the status method specially.
+                */
+               lafp = af_getbyname("lladdr");
+               if (lafp != NULL) {
+                       info.rti_info[RTAX_IFA] = (struct sockaddr *)sdl;
+                       lafp->af_status(s, &info);
+               }
+       }
 #endif
 #endif
-                           afp->af_status != ether_status) {
-                               p = afp;
-                               (*p->af_status)(s, &info);
+       if (allfamilies)
+               af_other_status(s);
+       else if (afp->af_other_status != NULL)
+               afp->af_other_status(s);
+
+       strncpy(ifs.ifs_name, name, sizeof ifs.ifs_name);
+       if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) 
+               printf("%s", ifs.ascii);
+
+       /* The rest is for when verbose is set; if not set, we're done */
+       if (!verbose)
+               goto done;
+
+       if (ioctl(s, SIOCGIFTYPE, &ifr) != -1) {
+               char *c = ift2str(ifr.ifr_type.ift_type,
+                   ifr.ifr_type.ift_family, ifr.ifr_type.ift_subfamily);
+               if (c != NULL)
+                       printf("\ttype: %s\n", c);
+       }
+
+       if (verbose > 0) {
+               struct if_agentidsreq ifar;
+               memset(&ifar, 0, sizeof(ifar));
+
+               strlcpy(ifar.ifar_name, name, sizeof(ifar.ifar_name));
+
+               if (ioctl(s, SIOCGIFAGENTIDS, &ifar) != -1) {
+                       if (ifar.ifar_count != 0) {
+                               ifar.ifar_uuids = calloc(ifar.ifar_count, sizeof(uuid_t));
+                               if (ifar.ifar_uuids != NULL) {
+                                       if (ioctl(s, SIOCGIFAGENTIDS, &ifar) != 1) {
+                                               for (int agent_i = 0; agent_i < ifar.ifar_count; agent_i++) {
+                                                       struct netagent_req nar;
+                                                       memset(&nar, 0, sizeof(nar));
+
+                                                       uuid_copy(nar.netagent_uuid, ifar.ifar_uuids[agent_i]);
+
+                                                       if (ioctl(s, SIOCGIFAGENTDATA, &nar) != 1) {
+                                                               printf("\tagent domain:%s type:%s flags:0x%x desc:\"%s\"\n",
+                                                                          nar.netagent_domain, nar.netagent_type,
+                                                                          nar.netagent_flags, nar.netagent_desc);
+                                                       }
+                                               }
+                                       }
+                                       free(ifar.ifar_uuids);
+                               }
                        }
                        }
-               } else for (p = afs; p->af_name; p++) {
-                       if (p->af_af == info.rti_info[RTAX_IFA]->sa_family &&
-#if USE_IF_MEDIA
-                           p->af_status != media_status &&
-#endif
-                           p->af_status != ether_status) 
-                               (*p->af_status)(s, &info);
                }
                }
-               addrcount--;
-               ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen);
-       }
-       if (allfamilies || afp->af_status == ether_status)
-               ether_status(s, (struct rt_addrinfo *)sdl);
-#if USE_IF_MEDIA
-       if (allfamilies || afp->af_status == media_status)
-               media_status(s, NULL);
-#endif
-       if (!allfamilies && !p && 
-           afp->af_status != ether_status)
-               warnx("%s has no %s interface address!", name, afp->af_name);
+       }
 
 
-       close(s);
-       return;
-}
+       if (ioctl(s, SIOCGIFLINKQUALITYMETRIC, &ifr) != -1) {
+               int lqm = ifr.ifr_link_quality_metric;
+               if (verbose > 1) {
+                       printf("\tlink quality: %d ", lqm);
+                       if (lqm == IFNET_LQM_THRESH_OFF)
+                               printf("(off)");
+                       else if (lqm == IFNET_LQM_THRESH_UNKNOWN)
+                               printf("(unknown)");
+                       else if (lqm > IFNET_LQM_THRESH_UNKNOWN &&
+                                lqm <= IFNET_LQM_THRESH_BAD)
+                               printf("(bad)");
+                       else if (lqm > IFNET_LQM_THRESH_UNKNOWN &&
+                                lqm <= IFNET_LQM_THRESH_POOR)
+                               printf("(poor)");
+                       else if (lqm > IFNET_LQM_THRESH_POOR &&
+                           lqm <= IFNET_LQM_THRESH_GOOD)
+                               printf("(good)");
+                       else
+                               printf("(?)");
+                       printf("\n");
+               } else if (lqm > IFNET_LQM_THRESH_UNKNOWN) {
+                       printf("\tlink quality: %d ", lqm);
+                       if (lqm <= IFNET_LQM_THRESH_BAD)
+                               printf("(bad)");
+                       else if (lqm <= IFNET_LQM_THRESH_POOR)
+                               printf("(poor)");
+                       else if (lqm <= IFNET_LQM_THRESH_GOOD)
+                               printf("(good)");
+                       else
+                               printf("(?)");
+                       printf("\n");
+               }
+       }
 
 
-void
-in_status(s, info)
-       int s ;
-       struct rt_addrinfo * info;
-{
-       struct sockaddr_in *sin, null_sin;
+       if (verbose > 0) {
+               if (ioctl(s, SIOCGIFINTERFACESTATE, &ifr) != -1) {
+                       printf("\tstate");
+                       if (ifr.ifr_interface_state.valid_bitmask &
+                           IF_INTERFACE_STATE_RRC_STATE_VALID) {
+                               uint8_t rrc_state = ifr.ifr_interface_state.rrc_state;
+                               
+                               printf(" rrc: %u ", rrc_state);
+                               if (rrc_state == IF_INTERFACE_STATE_RRC_STATE_CONNECTED)
+                                       printf("(connected)");
+                               else if (rrc_state == IF_INTERFACE_STATE_RRC_STATE_IDLE)
+                                       printf("(idle)");
+                               else
+                                       printf("(?)");
+                       }
+                       if (ifr.ifr_interface_state.valid_bitmask &
+                           IF_INTERFACE_STATE_INTERFACE_AVAILABILITY_VALID) {
+                               uint8_t ifavail = ifr.ifr_interface_state.interface_availability;
+                               
+                               printf(" availability: %u ", ifavail);
+                               if (ifavail == IF_INTERFACE_STATE_INTERFACE_AVAILABLE)
+                                       printf("(true)");
+                               else if (ifavail == IF_INTERFACE_STATE_INTERFACE_UNAVAILABLE)
+                                       printf("(false)");
+                               else
+                                       printf("(?)");
+                       } else {
+                               printf(" availability: (not valid)");
+                       }
+                       if (verbose > 1 &&
+                           ifr.ifr_interface_state.valid_bitmask &
+                           IF_INTERFACE_STATE_LQM_STATE_VALID) {
+                               int8_t lqm = ifr.ifr_interface_state.lqm_state;
+                               
+                               printf(" lqm: %d", lqm);
+                               
+                               if (lqm == IFNET_LQM_THRESH_OFF)
+                                       printf("(off)");
+                               else if (lqm == IFNET_LQM_THRESH_UNKNOWN)
+                                       printf("(unknown)");
+                               else if (lqm == IFNET_LQM_THRESH_BAD)
+                                       printf("(bad)");
+                               else if (lqm == IFNET_LQM_THRESH_POOR)
+                                       printf("(poor)");
+                               else if (lqm == IFNET_LQM_THRESH_GOOD)
+                                       printf("(good)");
+                               else
+                                       printf("(?)");
+                       }
+               }
+               printf("\n");
+       }
        
        
-       memset(&null_sin, 0, sizeof(null_sin));
+       bzero(&iflpr, sizeof (iflpr));
+       strncpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name));
+       if (ioctl(s, SIOCGIFLINKPARAMS, &iflpr) != -1) {
+               u_int64_t ibw_max = iflpr.iflpr_input_bw.max_bw;
+               u_int64_t ibw_eff = iflpr.iflpr_input_bw.eff_bw;
+               u_int64_t obw_max = iflpr.iflpr_output_bw.max_bw;
+               u_int64_t obw_eff = iflpr.iflpr_output_bw.eff_bw;
+               u_int64_t obw_tbr = iflpr.iflpr_output_tbr_rate;
+               u_int32_t obw_pct = iflpr.iflpr_output_tbr_percent;
+               u_int64_t ilt_max = iflpr.iflpr_input_lt.max_lt;
+               u_int64_t ilt_eff = iflpr.iflpr_input_lt.eff_lt;
+               u_int64_t olt_max = iflpr.iflpr_output_lt.max_lt;
+               u_int64_t olt_eff = iflpr.iflpr_output_lt.eff_lt;
+
+
+               if (eflags & IFEF_TXSTART) {
+                       u_int32_t flags = iflpr.iflpr_flags;
+                       u_int32_t sched = iflpr.iflpr_output_sched;
+                       struct if_throttlereq iftr;
+
+                       printf("\tscheduler: %s%s ",
+                           (flags & IFLPRF_ALTQ) ? "ALTQ_" : "",
+                           sched2str(sched));
+                       if (flags & IFLPRF_DRVMANAGED)
+                               printf("(driver managed)");
+                       printf("\n");
+
+                       bzero(&iftr, sizeof (iftr));
+                       strncpy(iftr.ifthr_name, name,
+                           sizeof (iftr.ifthr_name));
+                       if (ioctl(s, SIOCGIFTHROTTLE, &iftr) != -1 &&
+                           iftr.ifthr_level != IFNET_THROTTLE_OFF)
+                               printf("\tthrottling: level %d (%s)\n",
+                                   iftr.ifthr_level, tl2str(iftr.ifthr_level));
+               }
+
+               if (obw_tbr != 0 && obw_eff > obw_tbr)
+                       obw_eff = obw_tbr;
+
+               if (ibw_max != 0 || obw_max != 0) {
+                       if (ibw_max == obw_max && ibw_eff == obw_eff &&
+                           ibw_max == ibw_eff && obw_tbr == 0) {
+                               printf("\tlink rate: %s\n",
+                                   bps_to_str(ibw_max));
+                       } else {
+                               printf("\tuplink rate: %s [eff] / ",
+                                   bps_to_str(obw_eff));
+                               if (obw_tbr != 0) {
+                                       if (obw_pct == 0)
+                                               printf("%s [tbr] / ",
+                                                   bps_to_str(obw_tbr));
+                                       else
+                                               printf("%s [tbr %u%%] / ",
+                                                   bps_to_str(obw_tbr),
+                                                   obw_pct);
+                               }
+                               printf("%s", bps_to_str(obw_max));
+                               if (obw_tbr != 0)
+                                       printf(" [max]");
+                               printf("\n");
+                               if (ibw_eff == ibw_max) {
+                                       printf("\tdownlink rate: %s\n",
+                                           bps_to_str(ibw_max));
+                               } else {
+                                       printf("\tdownlink rate: "
+                                           "%s [eff] / ", bps_to_str(ibw_eff));
+                                       printf("%s [max]\n",
+                                           bps_to_str(ibw_max));
+                               }
+                       }
+               } else if (obw_tbr != 0) {
+                       printf("\tuplink rate: %s [tbr]\n",
+                           bps_to_str(obw_tbr));
+               }
+
+               if (ilt_max != 0 || olt_max != 0) {
+                       if (ilt_max == olt_max && ilt_eff == olt_eff &&
+                           ilt_max == ilt_eff) {
+                               printf("\tlink latency: %s\n",
+                                   ns_to_str(ilt_max));
+                       } else {
+                               if (olt_max != 0 && olt_eff == olt_max) {
+                                       printf("\tuplink latency: %s\n",
+                                           ns_to_str(olt_max));
+                               } else if (olt_max != 0) {
+                                       printf("\tuplink latency: "
+                                           "%s [eff] / ", ns_to_str(olt_eff));
+                                       printf("%s [max]\n",
+                                           ns_to_str(olt_max));
+                               }
+                               if (ilt_max != 0 && ilt_eff == ilt_max) {
+                                       printf("\tdownlink latency: %s\n",
+                                           ns_to_str(ilt_max));
+                               } else if (ilt_max != 0) {
+                                       printf("\tdownlink latency: "
+                                           "%s [eff] / ", ns_to_str(ilt_eff));
+                                       printf("%s [max]\n",
+                                           ns_to_str(ilt_max));
+                               }
+                       }
+               }
+       }
 
 
-       sin = (struct sockaddr_in *)info->rti_info[RTAX_IFA];
-       printf("\tinet %s ", inet_ntoa(sin->sin_addr));
+       /* Common OID prefix */
+       mib[0] = CTL_NET;
+       mib[1] = PF_LINK;
+       mib[2] = NETLINK_GENERIC;
+       mib[3] = IFMIB_IFDATA;
+       mib[4] = if_nametoindex(name);
+       mib[5] = IFDATA_SUPPLEMENTAL;
+       if (sysctl(mib, 6, &ifmsupp, &miblen, (void *)0, 0) == -1)
+               err(1, "sysctl IFDATA_SUPPLEMENTAL");
+
+       if (ifmsupp.ifmd_data_extended.ifi_alignerrs != 0) {
+               printf("\tunaligned pkts: %llu\n",
+                   ifmsupp.ifmd_data_extended.ifi_alignerrs);
+       }
+       if (ifmsupp.ifmd_data_extended.ifi_dt_bytes != 0) {
+               printf("\tdata milestone interval: %s\n",
+                   bytes_to_str(ifmsupp.ifmd_data_extended.ifi_dt_bytes));
+       }
 
 
-       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));
+       bzero(&ifdr, sizeof (ifdr));
+       strncpy(ifdr.ifdr_name, name, sizeof (ifdr.ifdr_name));
+       if (ioctl(s, SIOCGIFDESC, &ifdr) != -1 && ifdr.ifdr_len) {
+               printf("\tdesc: %s\n", ifdr.ifdr_desc);
        }
 
        }
 
-       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 (ioctl(s, SIOCGIFLOG, &ifr) != -1 && ifr.ifr_log.ifl_level) {
+               printf("\tlogging: level %d ", ifr.ifr_log.ifl_level);
+               printb("facilities", ifr.ifr_log.ifl_flags, IFRLOGF_BITS);
+               putchar('\n');
+       }
 
 
-       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));
+       if (ioctl(s, SIOCGIFDELEGATE, &ifr) != -1 && ifr.ifr_delegated) {
+               char delegatedif[IFNAMSIZ+1];
+               if (if_indextoname(ifr.ifr_delegated, delegatedif) != NULL)
+                       printf("\teffective interface: %s\n", delegatedif);
        }
        }
-       putchar('\n');
+
+       if(ioctl(s, SIOCGSTARTDELAY, &ifr) != -1) {
+               if (ifr.ifr_start_delay_qlen > 0 &&
+                   ifr.ifr_start_delay_timeout > 0) {
+                       printf("\ttxstart qlen: %u packets "
+                           "timeout: %u microseconds\n",
+                           ifr.ifr_start_delay_qlen,
+                           ifr.ifr_start_delay_timeout/1000);
+               }
+       }
+
+done:
+       close(s);
+       return;
 }
 
 }
 
-void
-ether_status(s, info)
-       int s ;
-       struct rt_addrinfo *info;
+#define        KILOBYTES       1024
+#define        MEGABYTES       (KILOBYTES * KILOBYTES)
+#define        GIGABYTES       (KILOBYTES * KILOBYTES * KILOBYTES)
+
+static char *
+bytes_to_str(unsigned long long bytes)
 {
 {
-       char *cp;
-       int n;
-       struct sockaddr_dl *sdl = (struct sockaddr_dl *)info;
+        static char buf[32];
+        const char *u;
+        long double n = bytes, t;
+
+        if (bytes >= GIGABYTES) {
+                t = n / GIGABYTES;
+                u = "GB";
+        } else if (n >= MEGABYTES) {
+                t = n / MEGABYTES;
+                u = "MB";
+        } else if (n >= KILOBYTES) {
+                t = n / KILOBYTES;
+                u = "KB";
+        } else {
+                t = n;
+                u = "bytes";
+        }
+
+        snprintf(buf, sizeof (buf), "%-4.2Lf %s", t, u);
+        return (buf);
+}
 
 
-       cp = (char *)LLADDR(sdl);
-       if ((n = sdl->sdl_alen) > 0) {
-               if (sdl->sdl_type == IFT_ETHER)
-                       printf ("\tether ");
-               else
-                       printf ("\tlladdr ");
-               while (--n >= 0)
-                       printf("%02x%c",*cp++ & 0xff, n>0? ':' : ' ');
-               putchar('\n');
-       }
+#define        GIGABIT_PER_SEC 1000000000      /* gigabit per second */
+#define MEGABIT_PER_SEC        1000000         /* megabit per second */
+#define        KILOBIT_PER_SEC 1000            /* kilobit per second */
+
+static char *
+bps_to_str(unsigned long long rate)
+{
+        static char buf[32];
+        const char *u;
+        long double n = rate, t;
+
+        if (rate >= GIGABIT_PER_SEC) {
+                t = n / GIGABIT_PER_SEC;
+                u = "Gbps";
+        } else if (n >= MEGABIT_PER_SEC) {
+                t = n / MEGABIT_PER_SEC;
+                u = "Mbps";
+        } else if (n >= KILOBIT_PER_SEC) {
+                t = n / KILOBIT_PER_SEC;
+                u = "Kbps";
+        } else {
+                t = n;
+                u = "bps ";
+        }
+
+        snprintf(buf, sizeof (buf), "%-4.2Lf %4s", t, u);
+        return (buf);
+}
+
+#define        NSEC_PER_SEC    1000000000      /* nanosecond per second */
+#define        USEC_PER_SEC    1000000         /* microsecond per second */
+#define        MSEC_PER_SEC    1000            /* millisecond per second */
+
+static char *
+ns_to_str(unsigned long long nsec)
+{
+        static char buf[32];
+        const char *u;
+        long double n = nsec, t;
+
+        if (nsec >= NSEC_PER_SEC) {
+                t = n / NSEC_PER_SEC;
+                u = "sec ";
+        } else if (n >= USEC_PER_SEC) {
+                t = n / USEC_PER_SEC;
+                u = "msec";
+        } else if (n >= MSEC_PER_SEC) {
+                t = n / MSEC_PER_SEC;
+                u = "usec";
+        } else {
+                t = n;
+                u = "nsec";
+        }
+
+        snprintf(buf, sizeof (buf), "%-4.2Lf %4s", t, u);
+        return (buf);
+}
+
+static void
+tunnel_status(int s)
+{
+       af_all_tunnel_status(s);
 }
 
 void
 }
 
 void
-Perror(cmd)
-       const char *cmd;
+Perror(const char *cmd)
 {
        switch (errno) {
 
 {
        switch (errno) {
 
@@ -805,46 +1528,14 @@ Perror(cmd)
        }
 }
 
        }
 }
 
-#define SIN(x) ((struct sockaddr_in *) &(x))
-struct sockaddr_in *sintab[] = {
-SIN(ridreq.ifr_addr), SIN(addreq.ifra_addr),
-SIN(addreq.ifra_mask), SIN(addreq.ifra_broadaddr)};
-
-void
-in_getaddr(s, which)
-       const char *s;
-       int which;
-{
-       register struct sockaddr_in *sin = sintab[which];
-       struct hostent *hp;
-       struct netent *np;
-
-       sin->sin_len = sizeof(*sin);
-       if (which != MASK)
-               sin->sin_family = AF_INET;
-
-       if (inet_aton(s, &sin->sin_addr))
-               return;
-       if ((hp = gethostbyname(s)) != 0)
-               bcopy(hp->h_addr, (char *)&sin->sin_addr, 
-                   MIN(hp->h_length, sizeof(sin->sin_addr)));
-       else if ((np = getnetbyname(s)) != 0)
-               sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
-       else
-               errx(1, "%s: bad value", s);
-}
-
 /*
  * Print a value a la the %b format of the kernel's printf
  */
 void
 /*
  * Print a value a la the %b format of the kernel's printf
  */
 void
-printb(s, v, bits)
-       const char *s;
-       register unsigned v;
-       register const char *bits;
+printb(const char *s, unsigned v, const char *bits)
 {
 {
-       register int i, any = 0;
-       register char c;
+       int i, any = 0;
+       char c;
 
        if (bits && *bits == 8)
                printf("%s=%o", s, v);
 
        if (bits && *bits == 8)
                printf("%s=%o", s, v);
@@ -868,4 +1559,299 @@ printb(s, v, bits)
        }
 }
 
        }
 }
 
+#ifndef __APPLE__
+void
+ifmaybeload(const char *name)
+{
+#define MOD_PREFIX_LEN         3       /* "if_" */
+       struct module_stat mstat;
+       int fileid, modid;
+       char ifkind[IFNAMSIZ + MOD_PREFIX_LEN], ifname[IFNAMSIZ], *dp;
+       const char *cp;
+
+       /* loading suppressed by the user */
+       if (noload)
+               return;
+
+       /* trim the interface number off the end */
+       strlcpy(ifname, name, sizeof(ifname));
+       for (dp = ifname; *dp != 0; dp++)
+               if (isdigit(*dp)) {
+                       *dp = 0;
+                       break;
+               }
+
+       /* turn interface and unit into module name */
+       strlcpy(ifkind, "if_", sizeof(ifkind));
+       strlcpy(ifkind + MOD_PREFIX_LEN, ifname,
+           sizeof(ifkind) - MOD_PREFIX_LEN);
+
+       /* scan files in kernel */
+       mstat.version = sizeof(struct module_stat);
+       for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) {
+               /* scan modules in file */
+               for (modid = kldfirstmod(fileid); modid > 0;
+                    modid = modfnext(modid)) {
+                       if (modstat(modid, &mstat) < 0)
+                               continue;
+                       /* strip bus name if present */
+                       if ((cp = strchr(mstat.name, '/')) != NULL) {
+                               cp++;
+                       } else {
+                               cp = mstat.name;
+                       }
+                       /* already loaded? */
+                       if (strncmp(ifname, cp, strlen(ifname) + 1) == 0 ||
+                           strncmp(ifkind, cp, strlen(ifkind) + 1) == 0)
+                               return;
+               }
+       }
+
+       /* not present, we should try to load it */
+       kldload(ifkind);
+}
+#endif
 
 
+static struct cmd basic_cmds[] = {
+       DEF_CMD("up",           IFF_UP,         setifflags),
+       DEF_CMD("down",         -IFF_UP,        setifflags),
+       DEF_CMD("arp",          -IFF_NOARP,     setifflags),
+       DEF_CMD("-arp",         IFF_NOARP,      setifflags),
+       DEF_CMD("debug",        IFF_DEBUG,      setifflags),
+       DEF_CMD("-debug",       -IFF_DEBUG,     setifflags),
+#ifdef IFF_PPROMISC
+       DEF_CMD("promisc",      IFF_PPROMISC,   setifflags),
+       DEF_CMD("-promisc",     -IFF_PPROMISC,  setifflags),
+#endif /* IFF_PPROMISC */
+       DEF_CMD("add",          IFF_UP,         notealias),
+       DEF_CMD("alias",        IFF_UP,         notealias),
+       DEF_CMD("-alias",       -IFF_UP,        notealias),
+       DEF_CMD("delete",       -IFF_UP,        notealias),
+       DEF_CMD("remove",       -IFF_UP,        notealias),
+#ifdef notdef
+#define        EN_SWABIPS      0x1000
+       DEF_CMD("swabips",      EN_SWABIPS,     setifflags),
+       DEF_CMD("-swabips",     -EN_SWABIPS,    setifflags),
+#endif
+       DEF_CMD_ARG("netmask",                  setifnetmask),
+       DEF_CMD_ARG("metric",                   setifmetric),
+       DEF_CMD_ARG("broadcast",                setifbroadaddr),
+       DEF_CMD_ARG("ipdst",                    setifipdst),
+       DEF_CMD_ARG2("tunnel",                  settunnel),
+       DEF_CMD("-tunnel", 0,                   deletetunnel),
+       DEF_CMD("deletetunnel", 0,              deletetunnel),
+       DEF_CMD("link0",        IFF_LINK0,      setifflags),
+       DEF_CMD("-link0",       -IFF_LINK0,     setifflags),
+       DEF_CMD("link1",        IFF_LINK1,      setifflags),
+       DEF_CMD("-link1",       -IFF_LINK1,     setifflags),
+       DEF_CMD("link2",        IFF_LINK2,      setifflags),
+       DEF_CMD("-link2",       -IFF_LINK2,     setifflags),
+#ifdef IFF_MONITOR
+       DEF_CMD("monitor",      IFF_MONITOR:,   setifflags),
+       DEF_CMD("-monitor",     -IFF_MONITOR,   setifflags),
+#endif /* IFF_MONITOR */
+#ifdef IFF_STATICARP
+       DEF_CMD("staticarp",    IFF_STATICARP,  setifflags),
+       DEF_CMD("-staticarp",   -IFF_STATICARP, setifflags),
+#endif /* IFF_STATICARP */
+#ifdef IFCAP_RXCSUM
+       DEF_CMD("rxcsum",       IFCAP_RXCSUM,   setifcap),
+       DEF_CMD("-rxcsum",      -IFCAP_RXCSUM,  setifcap),
+#endif /* IFCAP_RXCSUM */
+#ifdef IFCAP_TXCSUM
+       DEF_CMD("txcsum",       IFCAP_TXCSUM,   setifcap),
+       DEF_CMD("-txcsum",      -IFCAP_TXCSUM,  setifcap),
+#endif /* IFCAP_TXCSUM */
+#ifdef IFCAP_NETCONS
+       DEF_CMD("netcons",      IFCAP_NETCONS,  setifcap),
+       DEF_CMD("-netcons",     -IFCAP_NETCONS, setifcap),
+#endif /* IFCAP_NETCONS */
+#ifdef IFCAP_POLLING
+       DEF_CMD("polling",      IFCAP_POLLING,  setifcap),
+       DEF_CMD("-polling",     -IFCAP_POLLING, setifcap),
+#endif /* IFCAP_POLLING */
+#ifdef IFCAP_TSO
+       DEF_CMD("tso",          IFCAP_TSO,      setifcap),
+       DEF_CMD("-tso",         -IFCAP_TSO,     setifcap),
+#endif /* IFCAP_TSO */
+#ifdef IFCAP_LRO
+       DEF_CMD("lro",          IFCAP_LRO,      setifcap),
+       DEF_CMD("-lro",         -IFCAP_LRO,     setifcap),
+#endif /* IFCAP_LRO */
+#ifdef IFCAP_WOL
+       DEF_CMD("wol",          IFCAP_WOL,      setifcap),
+       DEF_CMD("-wol",         -IFCAP_WOL,     setifcap),
+#endif /* IFCAP_WOL */
+#ifdef IFCAP_WOL_UCAST
+       DEF_CMD("wol_ucast",    IFCAP_WOL_UCAST,        setifcap),
+       DEF_CMD("-wol_ucast",   -IFCAP_WOL_UCAST,       setifcap),
+#endif /* IFCAP_WOL_UCAST */
+#ifdef IFCAP_WOL_MCAST
+       DEF_CMD("wol_mcast",    IFCAP_WOL_MCAST,        setifcap),
+       DEF_CMD("-wol_mcast",   -IFCAP_WOL_MCAST,       setifcap),
+#endif /* IFCAP_WOL_MCAST */
+#ifdef IFCAP_WOL_MAGIC
+       DEF_CMD("wol_magic",    IFCAP_WOL_MAGIC,        setifcap),
+       DEF_CMD("-wol_magic",   -IFCAP_WOL_MAGIC,       setifcap),
+#endif /* IFCAP_WOL_MAGIC */
+       DEF_CMD("normal",       -IFF_LINK0,     setifflags),
+       DEF_CMD("compress",     IFF_LINK0,      setifflags),
+       DEF_CMD("noicmp",       IFF_LINK1,      setifflags),
+       DEF_CMD_ARG("mtu",                      setifmtu),
+#ifdef notdef
+       DEF_CMD_ARG("name",                     setifname),
+#endif /* notdef */
+#ifdef IFCAP_AV
+       DEF_CMD("av", IFCAP_AV, setifcap),
+       DEF_CMD("-av", -IFCAP_AV, setifcap),
+#endif /* IFCAP_AV */
+       DEF_CMD("router",       1,              setrouter),
+       DEF_CMD("-router",      0,              setrouter),
+       DEF_CMD_ARG("desc",                     setifdesc),
+       DEF_CMD_ARG("tbr",                      settbr),
+       DEF_CMD_ARG("throttle",                 setthrottle),
+       DEF_CMD_ARG("log",                      setlog),
+       DEF_CMD("cl2k", 1,                      setcl2k),
+       DEF_CMD("-cl2k",        0,              setcl2k),
+       DEF_CMD("expensive",    1,              setexpensive),
+       DEF_CMD("-expensive",   0,              setexpensive),
+       DEF_CMD_ARG("ecn",                      setecnmode),
+};
+
+static __constructor void
+ifconfig_ctor(void)
+{
+#define        N(a)    (sizeof(a) / sizeof(a[0]))
+       int i;
+
+       for (i = 0; i < N(basic_cmds);  i++)
+               cmd_register(&basic_cmds[i]);
+#undef N
+}
+
+static char *
+sched2str(unsigned int s)
+{
+       char *c;
+
+       switch (s) {
+       case PKTSCHEDT_NONE:
+               c = "NONE";
+               break;
+       case PKTSCHEDT_CBQ:
+               c = "CBQ";
+               break;
+       case PKTSCHEDT_HFSC:
+               c = "HFSC";
+               break;
+       case PKTSCHEDT_PRIQ:
+               c = "PRIQ";
+               break;
+       case PKTSCHEDT_FAIRQ:
+               c = "FAIRQ";
+               break;
+       case PKTSCHEDT_TCQ:
+               c = "TCQ";
+               break;
+       case PKTSCHEDT_QFQ:
+               c = "QFQ";
+               break;
+       default:
+               c = "UNKNOWN";
+               break;
+       }
+
+       return (c);
+}
+
+static char *
+tl2str(unsigned int s)
+{
+       char *c;
+
+       switch (s) {
+       case IFNET_THROTTLE_OFF:
+               c = "off";
+               break;
+       case IFNET_THROTTLE_OPPORTUNISTIC:
+               c = "opportunistic";
+               break;
+       default:
+               c = "unknown";
+               break;
+       }
+
+       return (c);
+}
+
+static char *
+ift2str(unsigned int t, unsigned int f, unsigned int sf)
+{
+       static char buf[256];
+       char *c = NULL;
+
+       switch (t) {
+       case IFT_ETHER:
+               switch (sf) {
+               case IFRTYPE_SUBFAMILY_USB:
+                       c = "USB Ethernet";
+                       break;
+               case IFRTYPE_SUBFAMILY_BLUETOOTH:
+                       c = "Bluetooth PAN";
+                       break;
+               case IFRTYPE_SUBFAMILY_WIFI:
+                       c = "Wi-Fi";
+                       break;
+               case IFRTYPE_SUBFAMILY_THUNDERBOLT:
+                       c = "IP over Thunderbolt";
+                       break;
+               case IFRTYPE_SUBFAMILY_ANY:
+               default:
+                       c = "Ethernet";
+                       break;
+               }
+               break;
+
+       case IFT_IEEE1394:
+               c = "IP over FireWire";
+               break;
+
+       case IFT_PKTAP:
+               c = "Packet capture";
+               break;
+
+       case IFT_CELLULAR:
+               c = "Cellular";
+               break;
+
+       case IFT_BRIDGE:
+       case IFT_PFLOG:
+       case IFT_PFSYNC:
+       case IFT_OTHER:
+       case IFT_PPP:
+       case IFT_LOOP:
+       case IFT_GIF:
+       case IFT_STF:
+       case IFT_L2VLAN:
+       case IFT_IEEE8023ADLAG:
+       default:
+               break;
+       }
+
+       if (verbose > 1) {
+               if (c == NULL) {
+                       (void) snprintf(buf, sizeof (buf),
+                           "0x%x family: %u subfamily: %u",
+                           ifr.ifr_type.ift_type, ifr.ifr_type.ift_family,
+                           ifr.ifr_type.ift_subfamily);
+               } else {
+                       (void) snprintf(buf, sizeof (buf),
+                           "%s (0x%x) family: %u subfamily: %u", c,
+                           ifr.ifr_type.ift_type, ifr.ifr_type.ift_family,
+                           ifr.ifr_type.ift_subfamily);
+               }
+               c = buf;
+       }
+
+       return (c);
+}