]> Zhao Yanbai Git Server - minix.git/commitdiff
Import NetBSD ifconfig(8) 56/3456/1
authorDavid van Moolenbroek <david@minix3.org>
Wed, 15 Feb 2017 11:17:52 +0000 (11:17 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Thu, 9 Mar 2017 23:40:12 +0000 (23:40 +0000)
Not all of its functionality is actually implemented in the operating
system.  In addition, a few modules (agr, vlan) have been disabled
because we have not imported the necessary headers yet.

Change-Id: I4c9271065d640bd9112b4bd27e2652e1d51b18b4

33 files changed:
distrib/sets/lists/minix-base/mi
distrib/sets/lists/minix-debug/mi
distrib/sets/lists/minix-man/mi
sbin/Makefile
sbin/ifconfig/Makefile [new file with mode: 0644]
sbin/ifconfig/Makefile.inc [new file with mode: 0644]
sbin/ifconfig/af_atalk.c [new file with mode: 0644]
sbin/ifconfig/af_inet.c [new file with mode: 0644]
sbin/ifconfig/af_inet6.c [new file with mode: 0644]
sbin/ifconfig/af_inetany.c [new file with mode: 0644]
sbin/ifconfig/af_inetany.h [new file with mode: 0644]
sbin/ifconfig/af_link.c [new file with mode: 0644]
sbin/ifconfig/agr.c [new file with mode: 0644]
sbin/ifconfig/carp.c [new file with mode: 0644]
sbin/ifconfig/env.c [new file with mode: 0644]
sbin/ifconfig/env.h [new file with mode: 0644]
sbin/ifconfig/ether.c [new file with mode: 0644]
sbin/ifconfig/extern.h [new file with mode: 0644]
sbin/ifconfig/ieee80211.c [new file with mode: 0644]
sbin/ifconfig/ifconfig.8 [new file with mode: 0644]
sbin/ifconfig/ifconfig.c [new file with mode: 0644]
sbin/ifconfig/ifconfig_hostops.c [new file with mode: 0644]
sbin/ifconfig/ifconfig_rumpops.c [new file with mode: 0644]
sbin/ifconfig/media.c [new file with mode: 0644]
sbin/ifconfig/media.h [new file with mode: 0644]
sbin/ifconfig/parse.c [new file with mode: 0644]
sbin/ifconfig/parse.h [new file with mode: 0644]
sbin/ifconfig/pfsync.c [new file with mode: 0644]
sbin/ifconfig/prog_ops.h [new file with mode: 0644]
sbin/ifconfig/tunnel.c [new file with mode: 0644]
sbin/ifconfig/util.c [new file with mode: 0644]
sbin/ifconfig/util.h [new file with mode: 0644]
sbin/ifconfig/vlan.c [new file with mode: 0644]

index d08cca0e7a359ad86f814bb673e6ab70a78bc8a8..6739c0f9f103bc58cd7d9c8a77548ffd8fca5af9 100644 (file)
 ./sbin/fsck_ext2fs                                      minix-base
 ./sbin/fsck_mfs                                         minix-base
 ./sbin/halt                                             minix-base
+./sbin/ifconfig                                         minix-base
 ./sbin/init                                             minix-base
 ./sbin/minix-service                                    minix-base
 ./sbin/mkfs.mfs                                         minix-base
index dfa0f6a1b9e2aea97ea450b61db7af12e9277b4e..cb436cccd514eb77369fa2ab3939a8bee2d6ab95 100644 (file)
 ./usr/libdata/debug/sbin/fsck.debug                     minix-debug     debug
 ./usr/libdata/debug/sbin/fsck_ext2fs.debug              minix-debug     debug
 ./usr/libdata/debug/sbin/fsck_mfs.debug                 minix-debug     debug
+./usr/libdata/debug/sbin/ifconfig.debug                 minix-debug     debug
 ./usr/libdata/debug/sbin/init.debug                     minix-debug     debug
 ./usr/libdata/debug/sbin/minix-service.debug            minix-debug     debug
 ./usr/libdata/debug/sbin/mknod.debug                    minix-debug     debug
index a957cab595c7f14c3884e547f131290bf2c61fba..e00b6f1bb3a05a0661b6c76869a4a5c299fa5d25 100644 (file)
 ./usr/man/man8/halt.8                                   minix-man
 ./usr/man/man8/httpd.8                                  minix-man       obsolete
 ./usr/man/man8/i2cscan.8                                minix-man
-./usr/man/man8/ifconfig.8                               minix-man       obsolete
+./usr/man/man8/ifconfig.8                               minix-man
 ./usr/man/man8/in.httpd.8                               minix-man       obsolete
 ./usr/man/man8/inet.8                                   minix-man       obsolete
 ./usr/man/man8/init.8                                   minix-man
index d8907c231e3b2a0216815777a41715628b123086..0f38ce01f5d211871098c406127982579d310a64 100644 (file)
@@ -8,7 +8,7 @@
 
 SUBDIR= \
        chown \
-       fsck init \
+       fsck ifconfig init \
        mknod nologin \
        ping \
        reboot rcorder \
diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile
new file mode 100644 (file)
index 0000000..2aef6ef
--- /dev/null
@@ -0,0 +1,40 @@
+#      $NetBSD: Makefile,v 1.56 2015/05/19 08:14:38 ozaki-r Exp $
+#      @(#)Makefile    8.1 (Berkeley) 6/5/93
+
+# when making a change to this file, please check if the change is
+# also needed for src/distrib/utils/x_ifconfig.
+# such stuff should be into Makefile.inc.
+
+.include <bsd.own.mk>
+
+RUMPPRG=ifconfig
+MAN=   ifconfig.8
+
+#DBG+=-g
+SRCS= af_atalk.c af_link.c carp.c
+.if (${USE_INET6} != "no")
+CPPFLAGS+=     -DINET6
+SRCS+= af_inet6.c
+.endif
+
+.include "Makefile.inc"
+
+.PATH:         ${.CURDIR}/../../lib/libc/net
+RUMPSRCS= getifaddrs.c getnameinfo.c if_indextoname.c
+.if (${MKRUMP} != "no")
+CPPFLAGS+= -DRUMP_ACTION
+.endif
+
+.ifdef SMALLPROG
+CPPFLAGS+=-DSMALL
+.endif
+
+CPPFLAGS+=-I${NETBSDSRCDIR}/sys/dist/pf/
+SRCS+= pfsync.c
+
+.if ${MACHINE_ARCH} == "m68000"
+# XXX workaround for gcc -O1 bug (PR bin/40036 and toolchain/40066)
+COPTS.ifconfig.c+= -fno-loop-optimize
+.endif
+
+.include <bsd.prog.mk>
diff --git a/sbin/ifconfig/Makefile.inc b/sbin/ifconfig/Makefile.inc
new file mode 100644 (file)
index 0000000..a6e4ce9
--- /dev/null
@@ -0,0 +1,23 @@
+#      $NetBSD: Makefile.inc,v 1.9 2012/10/31 10:17:34 msaitoh Exp $
+
+# shared stuff with src/distrib/utils/x_ifconfig for install media.
+# stuff not required by install media should be into Makefile.
+
+DPADD+=${LIBUTIL}
+DPADD+=${LIBPROP}
+LDADD+=-lutil
+LDADD+=-lprop
+
+INCS+=af_inetany.h env.h extern.h media.h parse.h util.h
+SRCS+= af_inet.c
+SRCS+= af_inetany.c
+#SRCS+= agr.c
+SRCS+= env.c
+SRCS+= ether.c
+SRCS+= ieee80211.c
+SRCS+= ifconfig.c
+SRCS+= media.c
+SRCS+= parse.c
+SRCS+= tunnel.c
+SRCS+= util.c
+#SRCS+= vlan.c
diff --git a/sbin/ifconfig/af_atalk.c b/sbin/ifconfig/af_atalk.c
new file mode 100644 (file)
index 0000000..494b070
--- /dev/null
@@ -0,0 +1,249 @@
+/*     $NetBSD: af_atalk.c,v 1.19 2013/10/19 00:35:30 christos Exp $   */
+
+/*
+ * Copyright (c) 1983, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: af_atalk.c,v 1.19 2013/10/19 00:35:30 christos Exp $");
+#endif /* not lint */
+
+#include <sys/param.h> 
+#include <sys/ioctl.h> 
+#include <sys/socket.h>
+
+#include <net/if.h> 
+
+#include <netatalk/at.h>
+
+#include <netdb.h>
+
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <util.h>
+
+#include "env.h"
+#include "af_inetany.h"
+#include "parse.h"
+#include "extern.h"
+#include "prog_ops.h"
+
+#ifndef satocsat
+#define        satocsat(__sa) ((const struct sockaddr_at *)(__sa))
+#endif
+
+static void at_status(prop_dictionary_t, prop_dictionary_t, bool);
+static void at_commit_address(prop_dictionary_t, prop_dictionary_t);
+
+static void at_constructor(void) __attribute__((constructor));
+
+static struct afswtch ataf = {
+       .af_name = "atalk", .af_af = AF_APPLETALK, .af_status = at_status,
+       .af_addr_commit = at_commit_address
+};
+struct pinteger phase = PINTEGER_INITIALIZER1(&phase, "phase",
+    1, 2, 10, NULL, "phase", &command_root.pb_parser);
+
+struct pstr parse_range = PSTR_INITIALIZER(&range, "range", NULL, "range",
+    &command_root.pb_parser);
+
+static const struct kwinst atalkkw[] = {
+         {.k_word = "phase", .k_nextparser = &phase.pi_parser}
+       , {.k_word = "range", .k_nextparser = &parse_range.ps_parser}
+};
+
+struct pkw atalk = PKW_INITIALIZER(&atalk, "AppleTalk", NULL, NULL,
+    atalkkw, __arraycount(atalkkw), NULL);
+
+static cmdloop_branch_t branch;
+
+static void
+setatrange_impl(prop_dictionary_t env, prop_dictionary_t oenv,
+    struct netrange *nr)
+{
+       char range[24];
+       u_short first = 123, last = 123;
+
+       if (getargstr(env, "range", range, sizeof(range)) == -1)
+               return;
+
+       if (sscanf(range, "%hu-%hu", &first, &last) != 2 ||
+           first == 0 || last == 0 || first > last)
+               errx(EXIT_FAILURE, "%s: illegal net range: %u-%u", range,
+                   first, last);
+       nr->nr_firstnet = htons(first);
+       nr->nr_lastnet = htons(last);
+}
+
+static void
+at_commit_address(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ifreq ifr;
+       struct ifaliasreq ifra __attribute__((aligned(4)));
+       struct afparam atparam = {
+                 .req = BUFPARAM(ifra)
+               , .dgreq = BUFPARAM(ifr)
+               , .name = {
+                         {.buf = ifr.ifr_name,
+                          .buflen = sizeof(ifr.ifr_name)}
+                       , {.buf = ifra.ifra_name,
+                          .buflen = sizeof(ifra.ifra_name)}
+                 }
+               , .dgaddr = BUFPARAM(ifr.ifr_addr)
+               , .addr = BUFPARAM(ifra.ifra_addr)
+               , .dst = BUFPARAM(ifra.ifra_dstaddr)
+               , .brd = BUFPARAM(ifra.ifra_broadaddr)
+               , .mask = BUFPARAM(ifra.ifra_mask)
+               , .aifaddr = IFADDR_PARAM(SIOCAIFADDR)
+               , .difaddr = IFADDR_PARAM(SIOCDIFADDR)
+               , .gifaddr = IFADDR_PARAM(SIOCGIFADDR)
+               , .defmask = {.buf = NULL, .buflen = 0}
+       };
+       struct netrange nr = {.nr_phase = 2};   /* AppleTalk net range */
+       prop_data_t d, d0;
+       prop_dictionary_t ienv;
+       struct paddr_prefix *addr;
+       struct sockaddr_at *sat;
+
+       if ((d0 = (prop_data_t)prop_dictionary_get(env, "address")) == NULL)
+               return;
+
+       addr = prop_data_data(d0);
+
+       sat = (struct sockaddr_at *)&addr->pfx_addr;
+
+       (void)prop_dictionary_get_uint8(env, "phase", &nr.nr_phase);
+       /* Default range of one */
+       nr.nr_firstnet = nr.nr_lastnet = sat->sat_addr.s_net;
+       setatrange_impl(env, oenv, &nr);
+
+       if (ntohs(nr.nr_firstnet) > ntohs(sat->sat_addr.s_net) ||
+           ntohs(nr.nr_lastnet) < ntohs(sat->sat_addr.s_net))
+               errx(EXIT_FAILURE, "AppleTalk address is not in range");
+       memcpy(&sat->sat_zero, &nr, sizeof(nr));
+
+       /* Copy the new address to a temporary input environment */
+
+       d = prop_data_create_data_nocopy(addr, paddr_prefix_size(addr));
+       ienv = prop_dictionary_copy_mutable(env);
+
+       if (d == NULL)
+               err(EXIT_FAILURE, "%s: prop_data_create_data", __func__);
+       if (ienv == NULL)
+               err(EXIT_FAILURE, "%s: prop_dictionary_copy_mutable", __func__);
+
+       if (!prop_dictionary_set(ienv, "address", (prop_object_t)d))
+               err(EXIT_FAILURE, "%s: prop_dictionary_set", __func__);
+
+       /* copy to output environment for good measure */
+       if (!prop_dictionary_set(oenv, "address", (prop_object_t)d))
+               err(EXIT_FAILURE, "%s: prop_dictionary_set", __func__);
+
+       prop_object_release((prop_object_t)d);
+
+       memset(&ifr, 0, sizeof(ifr));
+       memset(&ifra, 0, sizeof(ifra));
+       commit_address(ienv, oenv, &atparam);
+
+       /* release temporary input environment */
+       prop_object_release((prop_object_t)ienv);
+}
+
+static void
+sat_print1(const char *prefix, const struct sockaddr *sa)
+{
+       char buf[40];
+
+       (void)getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, 0);
+       
+       printf("%s%s", prefix, buf);
+}
+
+static void
+at_status(prop_dictionary_t env, prop_dictionary_t oenv, bool force)
+{
+       struct sockaddr_at *sat;
+       struct ifreq ifr;
+       int s;
+       const char *ifname;
+       unsigned short flags;
+
+       if ((s = getsock(AF_APPLETALK)) == -1) {
+               if (errno == EAFNOSUPPORT)
+                       return;
+               err(EXIT_FAILURE, "getsock");
+       }
+       if ((ifname = getifinfo(env, oenv, &flags)) == NULL)
+               err(EXIT_FAILURE, "%s: getifinfo", __func__);
+
+       memset(&ifr, 0, sizeof(ifr));
+       estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       ifr.ifr_addr.sa_family = AF_APPLETALK;
+       if (prog_ioctl(s, SIOCGIFADDR, &ifr) != -1)
+               ;
+       else if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) {
+               if (!force)
+                       return;
+               memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
+       } else
+               warn("SIOCGIFADDR");
+       sat = (struct sockaddr_at *)&ifr.ifr_addr;
+
+       sat_print1("\tatalk ", &ifr.ifr_addr);
+
+       if (flags & IFF_POINTOPOINT) {
+               estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+               if (prog_ioctl(s, SIOCGIFDSTADDR, &ifr) == -1) {
+                       if (errno == EADDRNOTAVAIL)
+                               memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
+                       else
+                               warn("SIOCGIFDSTADDR");
+               }
+               sat_print1(" --> ", &ifr.ifr_dstaddr);
+       }
+       if (flags & IFF_BROADCAST) {
+               /* note RTAX_BRD overlap with IFF_POINTOPOINT */
+               /* note Appletalk broadcast is fixed. */
+               printf(" broadcast %u.%u", ntohs(sat->sat_addr.s_net),
+                       ATADDR_BCAST);
+       }
+       printf("\n");
+}
+
+static void
+at_constructor(void)
+{
+       register_family(&ataf);
+       cmdloop_branch_init(&branch, &atalk.pk_parser);
+       register_cmdloop_branch(&branch);
+}
diff --git a/sbin/ifconfig/af_inet.c b/sbin/ifconfig/af_inet.c
new file mode 100644 (file)
index 0000000..f4a00c7
--- /dev/null
@@ -0,0 +1,247 @@
+/*     $NetBSD: af_inet.c,v 1.17 2015/05/12 14:05:29 roy Exp $ */
+
+/*
+ * Copyright (c) 1983, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: af_inet.c,v 1.17 2015/05/12 14:05:29 roy Exp $");
+#endif /* not lint */
+
+#include <sys/param.h> 
+#include <sys/ioctl.h> 
+#include <sys/socket.h>
+
+#include <net/if.h> 
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+
+#include <arpa/inet.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <util.h>
+
+#include "env.h"
+#include "extern.h"
+#include "af_inetany.h"
+#include "prog_ops.h"
+
+static void in_constructor(void) __attribute__((constructor));
+static void in_status(prop_dictionary_t, prop_dictionary_t, bool);
+static void in_commit_address(prop_dictionary_t, prop_dictionary_t);
+static bool in_addr_tentative(struct ifaddrs *ifa);
+static void in_alias(const char *, prop_dictionary_t, prop_dictionary_t,
+    struct in_aliasreq *);
+
+static struct afswtch af = {
+       .af_name = "inet", .af_af = AF_INET, .af_status = in_status,
+       .af_addr_commit = in_commit_address,
+       .af_addr_tentative = in_addr_tentative
+};
+
+static void
+in_alias(const char *ifname, prop_dictionary_t env, prop_dictionary_t oenv,
+    struct in_aliasreq *creq)
+{
+       struct ifreq ifr;
+       bool alias;
+       int s;
+       unsigned short flags;
+       struct in_aliasreq in_addreq;
+       const struct sockaddr_in * const asin = &in_addreq.ifra_addr;
+       const struct sockaddr_in * const dsin = &in_addreq.ifra_dstaddr;
+       const struct sockaddr_in * const bsin = &in_addreq.ifra_broadaddr;
+       char hbuf[NI_MAXHOST];
+       const int niflag = Nflag ? 0 : NI_NUMERICHOST;
+
+       if (lflag)
+               return;
+
+       alias = true;
+
+       /* Get the non-alias address for this interface. */
+       if ((s = getsock(AF_INET)) == -1) {
+               if (errno == EAFNOSUPPORT)
+                       return;
+               err(EXIT_FAILURE, "socket");
+       }
+       memset(&ifr, 0, sizeof(ifr));
+       estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       if (prog_ioctl(s, SIOCGIFADDR, &ifr) == -1) {
+               if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT)
+                       return;
+               warn("SIOCGIFADDR");
+       }
+       /* If creq and ifr are the same address, this is not an alias. */
+       if (memcmp(&ifr.ifr_addr, &creq->ifra_addr, sizeof(ifr.ifr_addr)) == 0)
+               alias = false;
+       in_addreq = *creq;
+       if (prog_ioctl(s, SIOCGIFALIAS, &in_addreq) == -1) {
+               if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) {
+                       return;
+               } else
+                       warn("SIOCGIFALIAS");
+       }
+
+       if (getnameinfo((const struct sockaddr *)asin, asin->sin_len,
+                       hbuf, sizeof(hbuf), NULL, 0, niflag))
+               strlcpy(hbuf, "", sizeof(hbuf));        /* some message? */
+       printf("\tinet %s%s", alias ? "alias " : "", hbuf);
+
+       if (getifflags(env, oenv, &flags) == -1)
+               err(EXIT_FAILURE, "%s: getifflags", __func__);
+
+       if (flags & IFF_POINTOPOINT) {
+               if (getnameinfo((const struct sockaddr *)dsin, dsin->sin_len,
+                               hbuf, sizeof(hbuf), NULL, 0, niflag))
+                       strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */
+               printf(" -> %s", hbuf);
+       }
+
+       printf(" netmask 0x%x", ntohl(in_addreq.ifra_mask.sin_addr.s_addr));
+
+       if (flags & IFF_BROADCAST) {
+               if (getnameinfo((const struct sockaddr *)bsin, bsin->sin_len,
+                               hbuf, sizeof(hbuf), NULL, 0, niflag))
+                       strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */
+               printf(" broadcast %s", hbuf);
+       }
+
+#ifdef IN_IFF_TENTATIVE
+       memcpy(&ifr.ifr_addr, &creq->ifra_addr, creq->ifra_addr.sin_len);
+       if (prog_ioctl(s, SIOCGIFAFLAG_IN, &ifr) == -1) {
+               if (errno != EADDRNOTAVAIL)
+                       warn("SIOCGIFAFLAG_IN");
+       } else {
+               if (ifr.ifr_addrflags & IN_IFF_TENTATIVE)
+                       printf(" tentative");
+               if (ifr.ifr_addrflags & IN_IFF_DUPLICATED)
+                       printf(" duplicated");
+               if (ifr.ifr_addrflags & IN_IFF_DETACHED)
+                       printf(" detached");
+       }
+#endif
+}
+
+static void
+in_status(prop_dictionary_t env, prop_dictionary_t oenv, bool force)
+{
+       struct ifaddrs *ifap, *ifa;
+       struct in_aliasreq ifra;
+       bool printprefs = false;
+       const char *ifname;
+
+       if ((ifname = getifname(env)) == NULL)
+               err(EXIT_FAILURE, "%s: getifname", __func__);
+
+       if (getifaddrs(&ifap) != 0)
+               err(EXIT_FAILURE, "getifaddrs");
+
+       printprefs = ifa_any_preferences(ifname, ifap, AF_INET);
+
+       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+               if (strcmp(ifname, ifa->ifa_name) != 0)
+                       continue;
+               if (ifa->ifa_addr->sa_family != AF_INET)
+                       continue;
+               if (sizeof(ifra.ifra_addr) < ifa->ifa_addr->sa_len)
+                       continue;
+
+               memset(&ifra, 0, sizeof(ifra));
+               estrlcpy(ifra.ifra_name, ifa->ifa_name, sizeof(ifra.ifra_name));
+               memcpy(&ifra.ifra_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len);
+               in_alias(ifa->ifa_name, env, oenv, &ifra);
+               if (printprefs)
+                       ifa_print_preference(ifa->ifa_name, ifa->ifa_addr);
+               printf("\n");
+       }
+       freeifaddrs(ifap);
+}
+
+static void
+in_commit_address(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ifreq in_ifr;
+       struct in_aliasreq in_ifra;
+       struct afparam inparam = {
+                 .req = BUFPARAM(in_ifra)
+               , .dgreq = BUFPARAM(in_ifr)
+               , .name = {
+                         {.buf = in_ifr.ifr_name,
+                          .buflen = sizeof(in_ifr.ifr_name)}
+                       , {.buf = in_ifra.ifra_name,
+                          .buflen = sizeof(in_ifra.ifra_name)}
+                 }
+               , .dgaddr = BUFPARAM(in_ifr.ifr_addr)
+               , .addr = BUFPARAM(in_ifra.ifra_addr)
+               , .dst = BUFPARAM(in_ifra.ifra_dstaddr)
+               , .brd = BUFPARAM(in_ifra.ifra_broadaddr)
+               , .mask = BUFPARAM(in_ifra.ifra_mask)
+               , .aifaddr = IFADDR_PARAM(SIOCAIFADDR)
+               , .difaddr = IFADDR_PARAM(SIOCDIFADDR)
+               , .gifaddr = IFADDR_PARAM(SIOCGIFADDR)
+               , .defmask = {.buf = NULL, .buflen = 0}
+       };
+       memset(&in_ifr, 0, sizeof(in_ifr));
+       memset(&in_ifra, 0, sizeof(in_ifra));
+       commit_address(env, oenv, &inparam);
+}
+
+static bool
+in_addr_tentative(struct ifaddrs *ifa)
+{
+#ifdef IN_IFF_TENTATIVE
+       int s;
+       struct ifreq ifr;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name));
+       ifr.ifr_addr = *ifa->ifa_addr;
+       if ((s = getsock(AF_INET)) == -1)
+               err(EXIT_FAILURE, "%s: getsock", __func__);
+       if (prog_ioctl(s, SIOCGIFAFLAG_IN, &ifr) == -1)
+               err(EXIT_FAILURE, "SIOCGIFAFLAG_IN");
+       return ifr.ifr_addrflags & IN_IFF_TENTATIVE ? true : false;
+#else
+       return false;
+#endif
+}
+
+static void
+in_constructor(void)
+{
+       register_family(&af);
+}
diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c
new file mode 100644 (file)
index 0000000..e547410
--- /dev/null
@@ -0,0 +1,516 @@
+/*     $NetBSD: af_inet6.c,v 1.33 2015/05/12 14:05:29 roy Exp $        */
+
+/*
+ * Copyright (c) 1983, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: af_inet6.c,v 1.33 2015/05/12 14:05:29 roy Exp $");
+#endif /* not lint */
+
+#include <sys/param.h> 
+#include <sys/ioctl.h> 
+#include <sys/socket.h>
+
+#include <net/if.h> 
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/nd6.h>
+
+#include <err.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <util.h>
+
+#include "env.h"
+#include "extern.h"
+#include "parse.h"
+#include "extern.h"
+#include "af_inetany.h"
+#include "prog_ops.h"
+
+static void in6_constructor(void) __attribute__((constructor));
+static void in6_alias(const char *, prop_dictionary_t, prop_dictionary_t,
+    struct in6_ifreq *);
+static void in6_commit_address(prop_dictionary_t, prop_dictionary_t);
+
+static int setia6eui64_impl(prop_dictionary_t, struct in6_aliasreq *);
+static int setia6flags_impl(prop_dictionary_t, struct in6_aliasreq *);
+static int setia6pltime_impl(prop_dictionary_t, struct in6_aliasreq *);
+static int setia6vltime_impl(prop_dictionary_t, struct in6_aliasreq *);
+
+static int setia6lifetime(prop_dictionary_t, int64_t, time_t *, uint32_t *);
+
+static void in6_status(prop_dictionary_t, prop_dictionary_t, bool);
+static bool in6_addr_tentative(struct ifaddrs *ifa);
+
+static struct usage_func usage;
+static cmdloop_branch_t branch[2];
+
+static const struct kwinst ia6flagskw[] = {
+         IFKW("anycast",       IN6_IFF_ANYCAST)
+       , IFKW("deprecated",    IN6_IFF_DEPRECATED)
+};
+
+static struct pinteger parse_pltime = PINTEGER_INITIALIZER(&parse_pltime,
+    "pltime", 0, NULL, "pltime", &command_root.pb_parser);
+
+static struct pinteger parse_vltime = PINTEGER_INITIALIZER(&parse_vltime,
+    "vltime", 0, NULL, "vltime", &command_root.pb_parser);
+
+static const struct kwinst inet6kw[] = {
+         {.k_word = "pltime", .k_nextparser = &parse_pltime.pi_parser}
+       , {.k_word = "vltime", .k_nextparser = &parse_vltime.pi_parser}
+       , {.k_word = "eui64", .k_key = "eui64", .k_type = KW_T_BOOL,
+          .k_bool = true, .k_nextparser = &command_root.pb_parser}
+};
+
+struct pkw ia6flags = PKW_INITIALIZER(&ia6flags, "ia6flags", NULL,
+    "ia6flag", ia6flagskw, __arraycount(ia6flagskw), &command_root.pb_parser);
+struct pkw inet6 = PKW_INITIALIZER(&inet6, "IPv6 keywords", NULL,
+    NULL, inet6kw, __arraycount(inet6kw), NULL);
+
+static struct afswtch in6af = {
+       .af_name = "inet6", .af_af = AF_INET6, .af_status = in6_status,
+       .af_addr_commit = in6_commit_address,
+       .af_addr_tentative = in6_addr_tentative
+};
+
+static int
+prefix(void *val, int size)
+{
+       u_char *pname = (u_char *)val;
+       int byte, bit, plen = 0;
+
+       for (byte = 0; byte < size; byte++, plen += 8)
+               if (pname[byte] != 0xff)
+                       break;
+       if (byte == size)
+               return (plen);
+       for (bit = 7; bit != 0; bit--, plen++)
+               if (!(pname[byte] & (1 << bit)))
+                       break;
+       for (; bit != 0; bit--)
+               if (pname[byte] & (1 << bit))
+                       return(0);
+       byte++;
+       for (; byte < size; byte++)
+               if (pname[byte])
+                       return(0);
+       return (plen);
+}
+
+int
+setia6flags_impl(prop_dictionary_t env, struct in6_aliasreq *ifra)
+{
+       int64_t ia6flag;
+
+       if (!prop_dictionary_get_int64(env, "ia6flag", &ia6flag)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       if (ia6flag < 0) {
+               ia6flag = -ia6flag;
+               ifra->ifra_flags &= ~ia6flag;
+       } else
+               ifra->ifra_flags |= ia6flag;
+       return 0;
+}
+
+int
+setia6pltime_impl(prop_dictionary_t env, struct in6_aliasreq *ifra)
+{
+       int64_t pltime;
+
+       if (!prop_dictionary_get_int64(env, "pltime", &pltime)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       return setia6lifetime(env, pltime,
+           &ifra->ifra_lifetime.ia6t_preferred,
+           &ifra->ifra_lifetime.ia6t_pltime);
+}
+
+int
+setia6vltime_impl(prop_dictionary_t env, struct in6_aliasreq *ifra)
+{
+       int64_t vltime;
+
+       if (!prop_dictionary_get_int64(env, "vltime", &vltime)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       return setia6lifetime(env, vltime,
+               &ifra->ifra_lifetime.ia6t_expire,
+               &ifra->ifra_lifetime.ia6t_vltime);
+}
+
+static int
+setia6lifetime(prop_dictionary_t env, int64_t val, time_t *timep,
+    uint32_t *ivalp)
+{
+       time_t t;
+       int af;
+
+       if ((af = getaf(env)) == -1 || af != AF_INET6) {
+               errx(EXIT_FAILURE,
+                   "inet6 address lifetime not allowed for the AF");
+       }
+
+       t = time(NULL);
+       *timep = t + val;
+       *ivalp = val;
+       return 0;
+}
+
+int
+setia6eui64_impl(prop_dictionary_t env, struct in6_aliasreq *ifra)
+{
+       char buf[2][80];
+       struct ifaddrs *ifap, *ifa;
+       const struct sockaddr_in6 *sin6 = NULL;
+       const struct in6_addr *lladdr = NULL;
+       struct in6_addr *in6;
+       const char *ifname;
+       bool doit = false;
+       int af;
+
+       if (!prop_dictionary_get_bool(env, "eui64", &doit) || !doit) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       if ((ifname = getifname(env)) == NULL)
+               return -1;
+
+       af = getaf(env);
+       if (af != AF_INET6) {
+               errx(EXIT_FAILURE,
+                   "eui64 address modifier not allowed for the AF");
+       }
+       in6 = &ifra->ifra_addr.sin6_addr;
+       if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0) {
+               union {
+                       struct sockaddr_in6 sin6;
+                       struct sockaddr sa;
+               } any = {.sin6 = {.sin6_family = AF_INET6}};
+               memcpy(&any.sin6.sin6_addr, &in6addr_any,
+                   sizeof(any.sin6.sin6_addr));
+               (void)sockaddr_snprintf(buf[0], sizeof(buf[0]), "%a%%S",
+                   &any.sa);
+               (void)sockaddr_snprintf(buf[1], sizeof(buf[1]), "%a%%S",
+                   (const struct sockaddr *)&ifra->ifra_addr);
+               errx(EXIT_FAILURE, "interface index is already filled, %s | %s",
+                   buf[0], buf[1]);
+       }
+       if (getifaddrs(&ifap) != 0)
+               err(EXIT_FAILURE, "getifaddrs");
+       for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+               if (ifa->ifa_addr->sa_family == AF_INET6 &&
+                   strcmp(ifa->ifa_name, ifname) == 0) {
+                       sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr;
+                       if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+                               lladdr = &sin6->sin6_addr;
+                               break;
+                       }
+               }
+       }
+       if (lladdr == NULL)
+               errx(EXIT_FAILURE, "could not determine link local address"); 
+
+       memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8);
+
+       freeifaddrs(ifap);
+       return 0;
+}
+
+/* XXX not really an alias */
+void
+in6_alias(const char *ifname, prop_dictionary_t env, prop_dictionary_t oenv,
+    struct in6_ifreq *creq)
+{
+       struct in6_ifreq ifr6;
+       struct sockaddr_in6 *sin6;
+       char hbuf[NI_MAXHOST];
+       u_int32_t scopeid;
+       int s;
+       const int niflag = Nflag ? 0 : NI_NUMERICHOST;
+       unsigned short flags;
+
+       /* Get the non-alias address for this interface. */
+       if ((s = getsock(AF_INET6)) == -1) {
+               if (errno == EAFNOSUPPORT)
+                       return;
+               err(EXIT_FAILURE, "socket");
+       }
+
+       sin6 = &creq->ifr_addr;
+
+       inet6_getscopeid(sin6, INET6_IS_ADDR_LINKLOCAL);
+       scopeid = sin6->sin6_scope_id;
+       if (getnameinfo((const struct sockaddr *)sin6, sin6->sin6_len,
+                       hbuf, sizeof(hbuf), NULL, 0, niflag))
+               strlcpy(hbuf, "", sizeof(hbuf));        /* some message? */
+       printf("\tinet6 %s", hbuf);
+
+       if (getifflags(env, oenv, &flags) == -1)
+               err(EXIT_FAILURE, "%s: getifflags", __func__);
+
+       if (flags & IFF_POINTOPOINT) {
+               ifr6 = *creq;
+               if (prog_ioctl(s, SIOCGIFDSTADDR_IN6, &ifr6) == -1) {
+                       if (errno != EADDRNOTAVAIL)
+                               warn("SIOCGIFDSTADDR_IN6");
+                       memset(&ifr6.ifr_addr, 0, sizeof(ifr6.ifr_addr));
+                       ifr6.ifr_addr.sin6_family = AF_INET6;
+                       ifr6.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
+               }
+               sin6 = &ifr6.ifr_addr;
+               inet6_getscopeid(sin6, INET6_IS_ADDR_LINKLOCAL);
+               hbuf[0] = '\0';
+               if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
+                               hbuf, sizeof(hbuf), NULL, 0, niflag))
+                       strlcpy(hbuf, "", sizeof(hbuf)); /* some message? */
+               printf(" -> %s", hbuf);
+       }
+
+       ifr6 = *creq;
+       if (prog_ioctl(s, SIOCGIFNETMASK_IN6, &ifr6) == -1) {
+               if (errno != EADDRNOTAVAIL)
+                       warn("SIOCGIFNETMASK_IN6");
+       } else {
+               sin6 = &ifr6.ifr_addr;
+               printf(" prefixlen %d", prefix(&sin6->sin6_addr,
+                                              sizeof(struct in6_addr)));
+       }
+
+       ifr6 = *creq;
+       if (prog_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == -1) {
+               if (errno != EADDRNOTAVAIL)
+                       warn("SIOCGIFAFLAG_IN6");
+       } else {
+               if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST)
+                       printf(" anycast");
+               if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
+                       printf(" tentative");
+               if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED)
+                       printf(" duplicated");
+               if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DETACHED)
+                       printf(" detached");
+               if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
+                       printf(" deprecated");
+               if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_AUTOCONF)
+                       printf(" autoconf");
+               if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TEMPORARY)
+                       printf(" temporary");
+       }
+
+       if (scopeid)
+               printf(" scopeid 0x%x", scopeid);
+
+       if (get_flag('L')) {
+               struct in6_addrlifetime *lifetime;
+               ifr6 = *creq;
+               lifetime = &ifr6.ifr_ifru.ifru_lifetime;
+               if (prog_ioctl(s, SIOCGIFALIFETIME_IN6, &ifr6) == -1) {
+                       if (errno != EADDRNOTAVAIL)
+                               warn("SIOCGIFALIFETIME_IN6");
+               } else if (lifetime->ia6t_preferred || lifetime->ia6t_expire) {
+                       time_t t = time(NULL);
+                       printf(" pltime ");
+                       if (lifetime->ia6t_preferred) {
+                               printf("%lu",
+                                   (unsigned long)(lifetime->ia6t_preferred -
+                                       MIN(t, lifetime->ia6t_preferred)));
+                       } else
+                               printf("infty");
+
+                       printf(" vltime ");
+                       if (lifetime->ia6t_expire) {
+                               printf("%lu",
+                                   (unsigned long)(lifetime->ia6t_expire -
+                                       MIN(t, lifetime->ia6t_expire)));
+                       } else
+                               printf("infty");
+               }
+       }
+}
+
+static void
+in6_status(prop_dictionary_t env, prop_dictionary_t oenv, bool force)
+{
+       struct ifaddrs *ifap, *ifa;
+       struct in6_ifreq ifr;
+       const char *ifname;
+       bool printprefs = false;
+
+       if ((ifname = getifname(env)) == NULL)
+               err(EXIT_FAILURE, "%s: getifname", __func__);
+
+       if (getifaddrs(&ifap) != 0)
+               err(EXIT_FAILURE, "getifaddrs");
+       printprefs = ifa_any_preferences(ifname, ifap, AF_INET6);
+       for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+               if (strcmp(ifname, ifa->ifa_name) != 0)
+                       continue;
+               if (ifa->ifa_addr->sa_family != AF_INET6)
+                       continue;
+               if (sizeof(ifr.ifr_addr) < ifa->ifa_addr->sa_len)
+                       continue;
+
+               memset(&ifr, 0, sizeof(ifr));
+               estrlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name));
+               memcpy(&ifr.ifr_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len);
+               in6_alias(ifname, env, oenv, &ifr);
+               if (printprefs)
+                       ifa_print_preference(ifa->ifa_name, ifa->ifa_addr);
+               printf("\n");
+       }
+       freeifaddrs(ifap);
+}
+
+static int
+in6_pre_aifaddr(prop_dictionary_t env, const struct afparam *param)
+{
+       struct in6_aliasreq *ifra = param->req.buf;
+
+       setia6eui64_impl(env, ifra);
+       setia6vltime_impl(env, ifra);
+       setia6pltime_impl(env, ifra);
+       setia6flags_impl(env, ifra);
+       inet6_putscopeid(&ifra->ifra_addr, INET6_IS_ADDR_LINKLOCAL);
+       inet6_putscopeid(&ifra->ifra_dstaddr, INET6_IS_ADDR_LINKLOCAL);
+
+       return 0;
+}
+
+static void
+in6_commit_address(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct in6_ifreq in6_ifr = {
+               .ifr_addr = {
+                       .sin6_family = AF_INET6,
+                       .sin6_len = sizeof(in6_ifr.ifr_addr),
+                       .sin6_addr = {
+                               .s6_addr =
+                                   {0xff, 0xff, 0xff, 0xff,
+                                    0xff, 0xff, 0xff, 0xff}
+                       }
+               }
+       };
+       static struct sockaddr_in6 in6_defmask = {
+               .sin6_family = AF_INET6,
+               .sin6_len = sizeof(in6_defmask),
+               .sin6_addr = {
+                       .s6_addr = {0xff, 0xff, 0xff, 0xff,
+                                   0xff, 0xff, 0xff, 0xff}
+               }
+       };
+
+       struct in6_aliasreq in6_ifra = {
+               .ifra_prefixmask = {
+                       .sin6_family = AF_INET6,
+                       .sin6_len = sizeof(in6_ifra.ifra_prefixmask),
+                       .sin6_addr = {
+                               .s6_addr =
+                                   {0xff, 0xff, 0xff, 0xff,
+                                    0xff, 0xff, 0xff, 0xff}}},
+               .ifra_lifetime = {
+                         .ia6t_pltime = ND6_INFINITE_LIFETIME
+                       , .ia6t_vltime = ND6_INFINITE_LIFETIME
+               }
+       };
+       struct afparam in6param = {
+                 .req = BUFPARAM(in6_ifra)
+               , .dgreq = BUFPARAM(in6_ifr)
+               , .name = {
+                       {.buf = in6_ifr.ifr_name,
+                        .buflen = sizeof(in6_ifr.ifr_name)},
+                       {.buf = in6_ifra.ifra_name,
+                        .buflen = sizeof(in6_ifra.ifra_name)}
+                 }
+               , .dgaddr = BUFPARAM(in6_ifr.ifr_addr)
+               , .addr = BUFPARAM(in6_ifra.ifra_addr)
+               , .dst = BUFPARAM(in6_ifra.ifra_dstaddr)
+               , .brd = BUFPARAM(in6_ifra.ifra_broadaddr)
+               , .mask = BUFPARAM(in6_ifra.ifra_prefixmask)
+               , .aifaddr = IFADDR_PARAM(SIOCAIFADDR_IN6)
+               , .difaddr = IFADDR_PARAM(SIOCDIFADDR_IN6)
+               , .gifaddr = IFADDR_PARAM(SIOCGIFADDR_IN6)
+               , .defmask = BUFPARAM(in6_defmask)
+               , .pre_aifaddr = in6_pre_aifaddr
+       };
+       commit_address(env, oenv, &in6param);
+}
+
+static bool
+in6_addr_tentative(struct ifaddrs *ifa)
+{
+       int s;
+       struct in6_ifreq ifr;
+
+       if ((s = getsock(AF_INET6)) == -1)
+               err(EXIT_FAILURE, "%s: getsock", __func__);
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name));
+       ifr.ifr_addr = *(struct sockaddr_in6 *)ifa->ifa_addr;
+       if (prog_ioctl(s, SIOCGIFAFLAG_IN6, &ifr) == -1)
+               err(EXIT_FAILURE, "SIOCGIFAFLAG_IN6");
+       return ifr.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE ? true : false;
+}
+
+static void
+in6_usage(prop_dictionary_t env)
+{
+       fprintf(stderr,
+           "\t[ anycast | -anycast ] [ deprecated | -deprecated ]\n"
+           "\t[ pltime n ] [ vltime n ] "
+           "[ eui64 ]\n");
+}
+
+static void
+in6_constructor(void)
+{
+       if (register_flag('L') != 0)
+               err(EXIT_FAILURE, __func__);
+       register_family(&in6af);
+       usage_func_init(&usage, in6_usage);
+       register_usage(&usage);
+       cmdloop_branch_init(&branch[0], &ia6flags.pk_parser);
+       cmdloop_branch_init(&branch[1], &inet6.pk_parser);
+       register_cmdloop_branch(&branch[0]);
+       register_cmdloop_branch(&branch[1]);
+}
diff --git a/sbin/ifconfig/af_inetany.c b/sbin/ifconfig/af_inetany.c
new file mode 100644 (file)
index 0000000..7c4066b
--- /dev/null
@@ -0,0 +1,179 @@
+/*     $NetBSD: af_inetany.c,v 1.17 2012/12/30 22:52:35 christos Exp $ */
+
+/*-
+ * Copyright (c) 2008 David Young.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: af_inetany.c,v 1.17 2012/12/30 22:52:35 christos Exp $");
+#endif /* not lint */
+
+#include <sys/param.h> 
+#include <sys/ioctl.h> 
+#include <sys/socket.h>
+
+#include <net/if.h> 
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <util.h>
+
+#include "env.h"
+#include "extern.h"
+#include "af_inetany.h"
+#include "prog_ops.h"
+
+static void *
+loadbuf(const struct apbuf *b, const struct paddr_prefix *pfx)
+{
+       return memcpy(b->buf, &pfx->pfx_addr,
+                     MIN(b->buflen, pfx->pfx_addr.sa_len));
+}
+
+void
+commit_address(prop_dictionary_t env, prop_dictionary_t oenv,
+    const struct afparam *param)
+{
+       const char *ifname;
+       int af, rc, s;
+       bool alias, delete, replace;
+       prop_data_t d;
+       const struct paddr_prefix *addr, *brd, *dst, *mask;
+       unsigned short flags;
+
+       if ((af = getaf(env)) == -1)
+               af = AF_INET;
+
+       if ((s = getsock(af)) == -1)
+               err(EXIT_FAILURE, "%s: getsock", __func__);
+
+       if ((ifname = getifname(env)) == NULL)
+               err(EXIT_FAILURE, "%s: getifname", __func__);
+
+       strlcpy(param->name[0].buf, ifname, param->name[0].buflen);
+       strlcpy(param->name[1].buf, ifname, param->name[1].buflen);
+
+       if ((d = (prop_data_t)prop_dictionary_get(env, "address")) != NULL)
+               addr = prop_data_data_nocopy(d);
+       else if (!prop_dictionary_get_bool(env, "alias", &alias) || alias ||
+           param->gifaddr.cmd == 0)
+               return;
+       else if (prog_ioctl(s, param->gifaddr.cmd, param->dgreq.buf) == -1)
+               err(EXIT_FAILURE, "%s", param->gifaddr.desc);
+       else if (prog_ioctl(s, param->difaddr.cmd, param->dgreq.buf) == -1)
+               err(EXIT_FAILURE, "%s", param->difaddr.desc);
+       else
+               return;
+
+       if ((d = (prop_data_t)prop_dictionary_get(env, "dst")) != NULL)
+               dst = prop_data_data_nocopy(d);
+       else
+               dst = NULL;
+
+       if ((d = (prop_data_t)prop_dictionary_get(env, "netmask")) != NULL)
+               mask = prop_data_data_nocopy(d);
+       else
+               mask = NULL;
+
+       if ((d = (prop_data_t)prop_dictionary_get(env, "broadcast")) != NULL)
+               brd = prop_data_data_nocopy(d);
+       else
+               brd = NULL;
+
+       if (!prop_dictionary_get_bool(env, "alias", &alias)) {
+               delete = false;
+               replace = (param->gifaddr.cmd != 0);
+       } else {
+               replace = false;
+               delete = !alias;
+       }
+
+       loadbuf(&param->addr, addr);
+
+       /* TBD: read matching ifaddr from kernel, use the netmask as default
+        * TBD: handle preference
+        */
+       if (getifflags(env, oenv, &flags) == -1)
+               err(EXIT_FAILURE, "%s: getifflags", __func__);
+
+       switch (flags & (IFF_BROADCAST|IFF_POINTOPOINT)) {
+       case IFF_BROADCAST:
+               if (brd != NULL)
+                       loadbuf(&param->brd, brd);
+               /*FALLTHROUGH*/
+       case 0:
+               break;
+       case IFF_POINTOPOINT:
+               if (brd != NULL) {
+                       errx(EXIT_FAILURE, "%s is not a broadcast interface",
+                           ifname);
+               }
+               if (dst != NULL)
+                       loadbuf(&param->dst, dst);
+               break;
+       case IFF_BROADCAST|IFF_POINTOPOINT:
+               errx(EXIT_FAILURE, "unsupported interface flags");
+       }
+       if (param->mask.buf == NULL) {
+               if (mask != NULL)
+                       errx(EXIT_FAILURE, "netmask not supported");
+       } else if (mask != NULL)
+               loadbuf(&param->mask, mask);
+       else if (param->defmask.buf != NULL) {
+               memcpy(param->mask.buf, param->defmask.buf,
+                   MIN(param->mask.buflen, param->defmask.buflen));
+       }
+       if (replace) {
+               if (prog_ioctl(s, param->gifaddr.cmd, param->dgreq.buf) == 0) {
+                       rc = prog_ioctl(s, param->difaddr.cmd, param->dgreq.buf);
+                       if (rc == -1)
+                               err(EXIT_FAILURE, "%s", param->difaddr.desc);
+               } else if (errno == EADDRNOTAVAIL)
+                       ;       /* No address was assigned yet. */
+               else
+                       err(EXIT_FAILURE, "%s", param->gifaddr.desc);
+       } else if (delete) {
+               loadbuf(&param->dgaddr, addr);
+               if (prog_ioctl(s, param->difaddr.cmd, param->dgreq.buf) == -1)
+                       err(EXIT_FAILURE, "%s", param->difaddr.desc);
+               return;
+       }
+       if (param->pre_aifaddr != NULL &&
+           (*param->pre_aifaddr)(env, param) == -1)
+               err(EXIT_FAILURE, "pre-%s", param->aifaddr.desc);
+       if (prog_ioctl(s, param->aifaddr.cmd, param->req.buf) == -1)
+               err(EXIT_FAILURE, "%s", param->aifaddr.desc);
+}
diff --git a/sbin/ifconfig/af_inetany.h b/sbin/ifconfig/af_inetany.h
new file mode 100644 (file)
index 0000000..a040ea7
--- /dev/null
@@ -0,0 +1,59 @@
+/*     $NetBSD: af_inetany.h,v 1.4 2008/07/02 07:44:14 dyoung Exp $    */
+
+/*-
+ * Copyright (c) 2008 David Young.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _IFCONFIG_AF_INETANY_H
+#define _IFCONFIG_AF_INETANY_H
+
+#include <sys/types.h>
+#include <prop/proplib.h>
+
+#define        IFADDR_PARAM(__arg)     {.cmd = (__arg), .desc = #__arg}
+#define        BUFPARAM(__arg)         {.buf = &(__arg), .buflen = sizeof(__arg)}
+
+struct apbuf {
+       void *buf;
+       size_t buflen;
+};
+
+struct afparam {
+       struct {
+               char *buf;
+               size_t buflen;
+       } name[2];
+       struct apbuf dgaddr, addr, brd, dst, mask, req, dgreq, defmask,
+           pre_aifaddr_arg;
+       struct {
+               unsigned long cmd;
+               const char *desc;
+       } aifaddr, difaddr, gifaddr;
+       int (*pre_aifaddr)(prop_dictionary_t, const struct afparam *);
+};
+
+void   commit_address(prop_dictionary_t, prop_dictionary_t,
+    const struct afparam *);
+
+#endif /* _IFCONFIG_AF_INETANY_H */
diff --git a/sbin/ifconfig/af_link.c b/sbin/ifconfig/af_link.c
new file mode 100644 (file)
index 0000000..18e0368
--- /dev/null
@@ -0,0 +1,132 @@
+/*     $NetBSD: af_link.c,v 1.7 2014/01/19 22:31:13 matt Exp $ */
+
+/*-
+ * Copyright (c) 2008 David Young.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: af_link.c,v 1.7 2014/01/19 22:31:13 matt Exp $");
+#endif /* not lint */
+
+#include <sys/param.h> 
+#include <sys/ioctl.h> 
+#include <sys/socket.h>
+
+#include <net/if.h> 
+#include <net/if_dl.h> 
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <util.h>
+
+#include "env.h"
+#include "extern.h"
+#include "af_inetany.h"
+
+static void link_status(prop_dictionary_t, prop_dictionary_t, bool);
+static void link_commit_address(prop_dictionary_t, prop_dictionary_t);
+
+static const struct kwinst linkkw[] = {
+         {.k_word = "active", .k_key = "active", .k_type = KW_T_BOOL,
+          .k_bool = true, .k_nextparser = &command_root.pb_parser}
+};
+
+struct pkw link_pkw = PKW_INITIALIZER(&link_pkw, "link", NULL, NULL,
+    linkkw, __arraycount(linkkw), NULL);
+
+static struct afswtch af = {
+       .af_name = "link", .af_af = AF_LINK, .af_status = link_status,
+       .af_addr_commit = link_commit_address
+};
+
+static cmdloop_branch_t branch;
+
+static void link_constructor(void) __attribute__((constructor));
+
+static void
+link_status(prop_dictionary_t env, prop_dictionary_t oenv, bool force)
+{
+       print_link_addresses(env, false);
+}
+
+static int
+link_pre_aifaddr(prop_dictionary_t env, const struct afparam *param)
+{
+       bool active;
+       struct if_laddrreq *iflr = param->req.buf;
+
+       if (prop_dictionary_get_bool(env, "active", &active) && active)
+               iflr->flags |= IFLR_ACTIVE;
+
+       return 0;
+}
+
+static void
+link_commit_address(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct if_laddrreq dgreq = {
+               .addr = {
+                       .ss_family = AF_LINK,
+                       .ss_len = sizeof(dgreq.addr),
+               },
+       };
+       struct if_laddrreq req = {
+               .addr = {
+                       .ss_family = AF_LINK,
+                       .ss_len = sizeof(req.addr),
+               }
+       };
+       struct afparam linkparam = {
+                 .req = BUFPARAM(req)
+               , .dgreq = BUFPARAM(dgreq)
+               , .name = {
+                       {.buf = dgreq.iflr_name,
+                        .buflen = sizeof(dgreq.iflr_name)},
+                       {.buf = req.iflr_name,
+                        .buflen = sizeof(req.iflr_name)}
+                 }
+               , .dgaddr = BUFPARAM(dgreq.addr)
+               , .addr = BUFPARAM(req.addr)
+               , .aifaddr = IFADDR_PARAM(SIOCALIFADDR)
+               , .difaddr = IFADDR_PARAM(SIOCDLIFADDR)
+               , .gifaddr = IFADDR_PARAM(0)
+               , .pre_aifaddr = link_pre_aifaddr
+       };
+       commit_address(env, oenv, &linkparam);
+}
+
+static void
+link_constructor(void)
+{
+       register_family(&af);
+       cmdloop_branch_init(&branch, &link_pkw.pk_parser);
+       register_cmdloop_branch(&branch);
+}
diff --git a/sbin/ifconfig/agr.c b/sbin/ifconfig/agr.c
new file mode 100644 (file)
index 0000000..fec5800
--- /dev/null
@@ -0,0 +1,195 @@
+/*     $NetBSD: agr.c,v 1.15 2008/07/15 21:27:58 dyoung Exp $  */
+
+/*-
+ * Copyright (c)2005 YAMAMOTO Takashi,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: agr.c,v 1.15 2008/07/15 21:27:58 dyoung Exp $");
+#endif /* !defined(lint) */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net/agr/if_agrioctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <util.h>
+
+#include "env.h"
+#include "extern.h"
+#include "parse.h"
+#include "util.h"
+
+static int agrsetport(prop_dictionary_t, prop_dictionary_t);
+static void agr_constructor(void) __attribute__((constructor));
+static int checkifname(prop_dictionary_t);
+static void assertifname(prop_dictionary_t);
+
+static struct piface agrif = PIFACE_INITIALIZER(&agrif, "agr interface",
+    agrsetport, "agrport", &command_root.pb_parser);
+
+static const struct kwinst agrkw[] = {
+         {.k_word = "agrport", .k_type = KW_T_INT, .k_int = AGRCMD_ADDPORT,
+          .k_nextparser = &agrif.pif_parser}
+       , {.k_word = "-agrport", .k_type = KW_T_INT, .k_int = AGRCMD_REMPORT,
+          .k_nextparser = &agrif.pif_parser}
+};
+
+struct pkw agr = PKW_INITIALIZER(&agr, "agr", NULL, "agrcmd",
+    agrkw, __arraycount(agrkw), NULL);
+
+static int
+checkifname(prop_dictionary_t env)
+{
+       const char *ifname;
+
+       if ((ifname = getifname(env)) == NULL)
+               return 1;
+
+       return strncmp(ifname, "agr", 3) != 0 ||
+           !isdigit((unsigned char)ifname[3]);
+}
+
+static void
+assertifname(prop_dictionary_t env)
+{
+       if (checkifname(env))
+               errx(EXIT_FAILURE, "valid only with agr(4) interfaces");
+}
+
+int
+agrsetport(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       char buf[IFNAMSIZ];
+       struct agrreq ar;
+       const char *port;
+       int64_t cmd;
+
+       if (!prop_dictionary_get_int64(env, "agrcmd", &cmd)) {
+               warnx("%s.%d", __func__, __LINE__);
+               errno = ENOENT;
+               return -1;
+       }
+
+       if (!prop_dictionary_get_cstring_nocopy(env, "agrport", &port)) {
+               warnx("%s.%d", __func__, __LINE__);
+               errno = ENOENT;
+               return -1;
+       }
+       strlcpy(buf, port, sizeof(buf));
+
+       assertifname(env);
+       memset(&ar, 0, sizeof(ar));
+       ar.ar_version = AGRREQ_VERSION;
+       ar.ar_cmd = cmd;
+       ar.ar_buf = buf;
+       ar.ar_buflen = strlen(buf);
+
+       if (indirect_ioctl(env, SIOCSETAGR, &ar) == -1)
+               err(EXIT_FAILURE, "SIOCSETAGR");
+       return 0;
+}
+
+static void
+agr_status(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct agrreq ar;
+       void *buf = NULL;
+       size_t buflen = 0;
+       struct agrportlist *apl;
+       struct agrportinfo *api;
+       int i;
+
+       if (checkifname(env))
+               return;
+
+again:
+       memset(&ar, 0, sizeof(ar));
+       ar.ar_version = AGRREQ_VERSION;
+       ar.ar_cmd = AGRCMD_PORTLIST;
+       ar.ar_buf = buf;
+       ar.ar_buflen = buflen;
+
+       if (indirect_ioctl(env, SIOCGETAGR, &ar) == -1) {
+               if (errno != E2BIG) {
+                       warn("SIOCGETAGR");
+                       return;
+               }
+
+               free(buf);
+               buf = NULL;
+               buflen = 0;
+               goto again;
+       }
+
+       if (buf == NULL) {
+               buflen = ar.ar_buflen;
+               buf = malloc(buflen);
+               if (buf == NULL) {
+                       err(EXIT_FAILURE, "agr_status");
+               }
+               goto again;
+       }
+
+       apl = buf;
+       api = (void *)(apl + 1);
+
+       for (i = 0; i < apl->apl_nports; i++) {
+               char tmp[256];
+
+               snprintb(tmp, sizeof(tmp), AGRPORTINFO_BITS, api->api_flags);
+               printf("\tagrport: %s, flags=%s\n", api->api_ifname, tmp);
+               api++;
+       }
+}
+
+static status_func_t status;
+static usage_func_t usage;
+static cmdloop_branch_t branch;
+
+static void
+agr_usage(prop_dictionary_t env)
+{
+       fprintf(stderr, "\t[ agrport i ] [ -agrport i ]\n");
+}
+
+static void
+agr_constructor(void)
+{
+       status_func_init(&status, agr_status);
+       usage_func_init(&usage, agr_usage);
+       register_status(&status);
+       register_usage(&usage);
+       cmdloop_branch_init(&branch, &agr.pk_parser);
+       register_cmdloop_branch(&branch);
+}
diff --git a/sbin/ifconfig/carp.c b/sbin/ifconfig/carp.c
new file mode 100644 (file)
index 0000000..e9fcf2c
--- /dev/null
@@ -0,0 +1,295 @@
+/* $NetBSD: carp.c,v 1.13 2009/09/11 23:22:28 dyoung Exp $ */
+
+/*
+ * Copyright (c) 2002 Michael Shalayeff. All rights reserved.
+ * Copyright (c) 2003 Ryan McBride. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: carp.c,v 1.13 2009/09/11 23:22:28 dyoung Exp $");
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+#include <netinet/ip_carp.h>
+#include <net/route.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <util.h>
+
+#include "env.h"
+#include "parse.h"
+#include "extern.h"
+
+static status_func_t status;
+static usage_func_t usage;
+static cmdloop_branch_t branch;
+
+static void carp_constructor(void) __attribute__((constructor));
+static void carp_status(prop_dictionary_t, prop_dictionary_t);
+static int setcarp_advbase(prop_dictionary_t, prop_dictionary_t);
+static int setcarp_advskew(prop_dictionary_t, prop_dictionary_t);
+static int setcarp_passwd(prop_dictionary_t, prop_dictionary_t);
+static int setcarp_vhid(prop_dictionary_t, prop_dictionary_t);
+static int setcarp_state(prop_dictionary_t, prop_dictionary_t);
+static int setcarpdev(prop_dictionary_t, prop_dictionary_t);
+
+static const char *carp_states[] = { CARP_STATES };
+
+struct kwinst carpstatekw[] = {
+         {.k_word = "INIT", .k_nextparser = &command_root.pb_parser}
+       , {.k_word = "BACKUP", .k_nextparser = &command_root.pb_parser}
+       , {.k_word = "MASTER", .k_nextparser = &command_root.pb_parser}
+};
+
+struct pinteger parse_advbase = PINTEGER_INITIALIZER1(&parse_advbase, "advbase",
+    0, 255, 10, setcarp_advbase, "advbase", &command_root.pb_parser);
+
+struct pinteger parse_advskew = PINTEGER_INITIALIZER1(&parse_advskew, "advskew",
+    0, 254, 10, setcarp_advskew, "advskew", &command_root.pb_parser);
+
+struct piface carpdev = PIFACE_INITIALIZER(&carpdev, "carpdev", setcarpdev,
+    "carpdev", &command_root.pb_parser);
+
+struct pkw carpstate = PKW_INITIALIZER(&carpstate, "carp state", setcarp_state,
+    "carp_state", carpstatekw, __arraycount(carpstatekw),
+    &command_root.pb_parser);
+
+struct pstr pass = PSTR_INITIALIZER(&pass, "pass", setcarp_passwd,
+    "pass", &command_root.pb_parser);
+
+struct pinteger parse_vhid = PINTEGER_INITIALIZER1(&vhid, "vhid",
+    0, 255, 10, setcarp_vhid, "vhid", &command_root.pb_parser);
+
+static const struct kwinst carpkw[] = {
+         {.k_word = "advbase", .k_nextparser = &parse_advbase.pi_parser}
+       , {.k_word = "advskew", .k_nextparser = &parse_advskew.pi_parser}
+       , {.k_word = "carpdev", .k_nextparser = &carpdev.pif_parser}
+       , {.k_word = "-carpdev", .k_key = "carpdev", .k_type = KW_T_STR,
+          .k_str = "", .k_exec = setcarpdev,
+          .k_nextparser = &command_root.pb_parser}
+       , {.k_word = "pass", .k_nextparser = &pass.ps_parser}
+       , {.k_word = "state", .k_nextparser = &carpstate.pk_parser}
+       , {.k_word = "vhid", .k_nextparser = &parse_vhid.pi_parser}
+};
+
+struct pkw carp = PKW_INITIALIZER(&carp, "CARP", NULL, NULL,
+    carpkw, __arraycount(carpkw), NULL);
+
+static void
+carp_set(prop_dictionary_t env, struct carpreq *carpr)
+{
+       if (indirect_ioctl(env, SIOCSVH, carpr) == -1)
+               err(EXIT_FAILURE, "SIOCSVH");
+}
+
+static int
+carp_get1(prop_dictionary_t env, struct carpreq *carpr)
+{
+       memset(carpr, 0, sizeof(*carpr));
+
+       return indirect_ioctl(env, SIOCGVH, carpr);
+}
+
+static void
+carp_get(prop_dictionary_t env, struct carpreq *carpr)
+{
+       if (carp_get1(env, carpr) == -1)
+               err(EXIT_FAILURE, "SIOCGVH");
+}
+
+static void
+carp_status(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       const char *state;
+       struct carpreq carpr;
+
+       if (carp_get1(env, &carpr) == -1)
+               return;
+
+       if (carpr.carpr_vhid <= 0)
+               return;
+       if (carpr.carpr_state > CARP_MAXSTATE)
+               state = "<UNKNOWN>";
+       else
+               state = carp_states[carpr.carpr_state];
+
+       printf("\tcarp: %s carpdev %s vhid %d advbase %d advskew %d\n",
+           state, carpr.carpr_carpdev[0] != '\0' ?
+           carpr.carpr_carpdev : "none", carpr.carpr_vhid,
+           carpr.carpr_advbase, carpr.carpr_advskew);
+}
+
+int
+setcarp_passwd(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct carpreq carpr;
+       prop_data_t data;
+
+       data = (prop_data_t)prop_dictionary_get(env, "pass");
+       if (data == NULL) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       carp_get(env, &carpr);
+
+       memset(carpr.carpr_key, 0, sizeof(carpr.carpr_key));
+       /* XXX Should hash the password into the key here, perhaps? */
+       strlcpy((char *)carpr.carpr_key, prop_data_data_nocopy(data),
+           MIN(CARP_KEY_LEN, prop_data_size(data)));
+
+       carp_set(env, &carpr);
+       return 0;
+}
+
+int
+setcarp_vhid(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct carpreq carpr;
+       int64_t vhid;
+
+       if (!prop_dictionary_get_int64(env, "vhid", &vhid)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       carp_get(env, &carpr);
+
+       carpr.carpr_vhid = vhid;
+
+       carp_set(env, &carpr);
+       return 0;
+}
+
+int
+setcarp_advskew(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct carpreq carpr;
+       int64_t advskew;
+
+       if (!prop_dictionary_get_int64(env, "advskew", &advskew)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       carp_get(env, &carpr);
+
+       carpr.carpr_advskew = advskew;
+
+       carp_set(env, &carpr);
+       return 0;
+}
+
+/* ARGSUSED */
+int
+setcarp_advbase(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct carpreq carpr;
+       int64_t advbase;
+
+       if (!prop_dictionary_get_int64(env, "advbase", &advbase)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       carp_get(env, &carpr);
+
+       carpr.carpr_advbase = advbase;
+
+       carp_set(env, &carpr);
+       return 0;
+}
+
+/* ARGSUSED */
+static int
+setcarp_state(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct carpreq carpr;
+       int64_t carp_state;
+
+       if (!prop_dictionary_get_int64(env, "carp_state", &carp_state)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       carp_get(env, &carpr);
+
+       carpr.carpr_state = carp_state;
+
+       carp_set(env, &carpr);
+       return 0;
+}
+
+/* ARGSUSED */
+int
+setcarpdev(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct carpreq carpr;
+       prop_string_t s;
+
+       s = (prop_string_t)prop_dictionary_get(env, "carpdev");
+       if (s == NULL) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       carp_get(env, &carpr);
+
+       strlcpy(carpr.carpr_carpdev, prop_string_cstring_nocopy(s),
+           sizeof(carpr.carpr_carpdev));
+
+       carp_set(env, &carpr);
+       return 0;
+}
+
+static void
+carp_usage(prop_dictionary_t env)
+{
+       fprintf(stderr,
+           "\t[ advbase n ] [ advskew n ] [ carpdev iface ] "
+           "[ pass passphrase ] [ state state ] [ vhid n ]\n");
+
+}
+
+static void
+carp_constructor(void)
+{
+       cmdloop_branch_init(&branch, &carp.pk_parser);
+       register_cmdloop_branch(&branch);
+       status_func_init(&status, carp_status);
+       usage_func_init(&usage, carp_usage);
+       register_status(&status);
+       register_usage(&usage);
+}
diff --git a/sbin/ifconfig/env.c b/sbin/ifconfig/env.c
new file mode 100644 (file)
index 0000000..6d5c690
--- /dev/null
@@ -0,0 +1,180 @@
+/*     $NetBSD: env.c,v 1.9 2013/02/07 13:20:51 apb Exp $      */
+
+/*-
+ * Copyright (c) 2008 David Young.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: env.c,v 1.9 2013/02/07 13:20:51 apb Exp $");
+#endif /* not lint */
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <util.h>
+
+#include <net/if.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include "env.h"
+#include "util.h"
+#include "prog_ops.h"
+
+prop_dictionary_t
+prop_dictionary_augment(prop_dictionary_t bottom, prop_dictionary_t top)
+{
+       prop_object_iterator_t i;
+       prop_dictionary_t d;
+       prop_object_t ko, o;
+       prop_dictionary_keysym_t k;
+       const char *key;
+
+       d = prop_dictionary_copy_mutable(bottom);
+       if (d == NULL)
+               return NULL;
+
+       i = prop_dictionary_iterator(top);
+
+       while (i != NULL && (ko = prop_object_iterator_next(i)) != NULL) {
+               k = (prop_dictionary_keysym_t)ko;
+               key = prop_dictionary_keysym_cstring_nocopy(k);
+               o = prop_dictionary_get_keysym(top, k);
+               if (o == NULL || !prop_dictionary_set(d, key, o)) {
+                       prop_object_release((prop_object_t)d);
+                       d = NULL;
+                       break;
+               }
+       }
+       if (i != NULL)
+               prop_object_iterator_release(i);
+       if (d != NULL)
+               prop_dictionary_make_immutable(d);
+       return d;
+}
+
+int
+getifflags(prop_dictionary_t env, prop_dictionary_t oenv,
+    unsigned short *flagsp)
+{
+       struct ifreq ifr;
+       const char *ifname;
+       uint64_t ifflags;
+       int s;
+
+       if (prop_dictionary_get_uint64(env, "ifflags", &ifflags)) {
+               *flagsp = (unsigned short)ifflags;
+               return 0;
+       }
+
+       if ((s = getsock(AF_UNSPEC)) == -1)
+               return -1;
+
+       if ((ifname = getifname(env)) == NULL)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       if (prog_ioctl(s, SIOCGIFFLAGS, &ifr) == -1)
+               return -1;
+
+       *flagsp = (unsigned short)ifr.ifr_flags;
+
+       prop_dictionary_set_uint64(oenv, "ifflags",
+           (unsigned short)ifr.ifr_flags);
+
+       return 0;
+}
+
+const char *
+getifinfo(prop_dictionary_t env, prop_dictionary_t oenv, unsigned short *flagsp)
+{
+       if (getifflags(env, oenv, flagsp) == -1)
+               return NULL;
+
+       return getifname(env);
+}
+
+const char *
+getifname(prop_dictionary_t env)
+{
+       const char *s;
+
+       return prop_dictionary_get_cstring_nocopy(env, "if", &s) ? s : NULL;
+}
+
+ssize_t
+getargdata(prop_dictionary_t env, const char *key, uint8_t *buf, size_t buflen)
+{
+       prop_data_t data;
+       size_t datalen;
+
+       data = (prop_data_t)prop_dictionary_get(env, key);
+       if (data == NULL) {
+               errno = ENOENT;
+               return -1;
+       }
+       datalen = prop_data_size(data);
+       if (datalen > buflen) {
+               errno = ENAMETOOLONG; 
+               return -1;
+       }
+       memset(buf, 0, buflen);
+       memcpy(buf, prop_data_data_nocopy(data), datalen);
+       return datalen;
+}
+
+ssize_t
+getargstr(prop_dictionary_t env, const char *key, char *buf, size_t buflen)
+{
+       prop_data_t data;
+       size_t datalen;
+
+       data = (prop_data_t)prop_dictionary_get(env, key);
+       if (data == NULL) {
+               errno = ENOENT;
+               return -1;
+       }
+       datalen = prop_data_size(data);
+       if (datalen >= buflen) {
+               errno = ENAMETOOLONG; 
+               return -1;
+       }
+       memset(buf, 0, buflen);
+       memcpy(buf, prop_data_data_nocopy(data), datalen);
+       return datalen;
+}
+
+int
+getaf(prop_dictionary_t env)
+{
+       int64_t af;
+
+       if (!prop_dictionary_get_int64(env, "af", &af)) {
+               errno = ENOENT;
+               return -1;
+       }
+       return (int)af;
+}
diff --git a/sbin/ifconfig/env.h b/sbin/ifconfig/env.h
new file mode 100644 (file)
index 0000000..a5749e0
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef _IFCONFIG_ENV_H
+#define _IFCONFIG_ENV_H
+
+#include <prop/proplib.h>
+
+const char *getifname(prop_dictionary_t);
+ssize_t getargstr(prop_dictionary_t, const char *, char *, size_t);
+ssize_t getargdata(prop_dictionary_t, const char *, uint8_t *, size_t);
+int getaf(prop_dictionary_t);
+int getifflags(prop_dictionary_t, prop_dictionary_t, unsigned short *);
+const char *getifinfo(prop_dictionary_t, prop_dictionary_t, unsigned short *);
+prop_dictionary_t prop_dictionary_augment(prop_dictionary_t, prop_dictionary_t);
+
+/*
+ * XXX: this really doesn't belong in here, but env.h is conveniently
+ * included from all source modules *after* system headers, so it
+ * allows us to be lazy.  See Makefile for more details.
+ */
+#ifdef RUMP_ACTION
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+#include <rump/rumpclient.h>
+#endif /* RUMP_ACTION */
+
+#endif /* _IFCONFIG_ENV_H */
diff --git a/sbin/ifconfig/ether.c b/sbin/ifconfig/ether.c
new file mode 100644 (file)
index 0000000..25d1f00
--- /dev/null
@@ -0,0 +1,99 @@
+/*     $NetBSD: ether.c,v 1.2 2012/11/01 13:43:23 pgoyette Exp $       */
+
+/*
+ * Copyright (c) 1983, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: ether.c,v 1.2 2012/11/01 13:43:23 pgoyette Exp $");
+#endif /* not lint */
+
+#include <sys/param.h> 
+#include <sys/ioctl.h> 
+
+#include <net/if.h> 
+#include <net/if_ether.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <util.h>
+
+#include "env.h"
+#include "parse.h"
+#include "extern.h"
+#include "prog_ops.h"
+
+static void ether_status(prop_dictionary_t, prop_dictionary_t);
+static void ether_constructor(void) __attribute__((constructor));
+
+static status_func_t status;
+
+#define MAX_PRINT_LEN 55
+
+void
+ether_status(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct eccapreq eccr;
+       char fbuf[BUFSIZ];
+       char *bp;
+
+       memset(&eccr, 0, sizeof(eccr));
+
+       if (direct_ioctl(env, SIOCGETHERCAP, &eccr) == -1)
+               return;
+
+       if (eccr.eccr_capabilities != 0) {
+               (void)snprintb_m(fbuf, sizeof(fbuf), ECCAPBITS,
+                   eccr.eccr_capabilities, MAX_PRINT_LEN);
+               bp = fbuf;
+               while (*bp != '\0') {
+                       printf("\tec_capabilities=%s\n", &bp[2]);
+                       bp += strlen(bp) + 1;
+               }
+               (void)snprintb_m(fbuf, sizeof(fbuf), ECCAPBITS,
+                   eccr.eccr_capenable, MAX_PRINT_LEN);
+               bp = fbuf;
+               while (*bp != '\0') {
+                       printf("\tec_enabled=%s\n", &bp[2]);
+                       bp += strlen(bp) + 1;
+               }
+       }
+}
+
+static void
+ether_constructor(void)
+{
+
+       status_func_init(&status, ether_status);
+       register_status(&status);
+}
diff --git a/sbin/ifconfig/extern.h b/sbin/ifconfig/extern.h
new file mode 100644 (file)
index 0000000..d8ef479
--- /dev/null
@@ -0,0 +1,93 @@
+/*     $NetBSD: extern.h,v 1.14 2009/08/07 18:53:37 dyoung Exp $       */
+
+/*
+ * Copyright (c) 1983, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+#ifndef        _IFCONFIG_EXTERN_H
+#define        _IFCONFIG_EXTERN_H
+
+#include <prop/proplib.h>
+#include "util.h"
+
+#define        RIDADDR 0  
+#define        ADDR    1
+#define        MASK    2
+#define        DSTADDR 3
+
+typedef void (*usage_cb_t)(prop_dictionary_t);
+typedef void (*status_cb_t)(prop_dictionary_t, prop_dictionary_t);
+typedef void (*statistics_cb_t)(prop_dictionary_t);
+
+enum flag_type {
+         FLAG_T_MOD = 0
+       , FLAG_T_CMD = 1
+};
+
+typedef enum flag_type flag_type_t;
+
+struct statistics_func {
+       SIMPLEQ_ENTRY(statistics_func)  f_next;
+       statistics_cb_t                 f_func;
+};
+
+struct usage_func {
+       SIMPLEQ_ENTRY(usage_func)       f_next;
+       usage_cb_t                      f_func;
+};
+
+struct status_func {
+       SIMPLEQ_ENTRY(status_func)      f_next;
+       status_cb_t                     f_func;
+};
+
+struct cmdloop_branch {
+       SIMPLEQ_ENTRY(cmdloop_branch)   b_next;
+       struct parser                   *b_parser;
+};
+
+
+typedef struct statistics_func statistics_func_t;
+typedef struct status_func status_func_t;
+typedef struct usage_func usage_func_t;
+typedef struct cmdloop_branch cmdloop_branch_t;
+
+void cmdloop_branch_init(cmdloop_branch_t *, struct parser *);
+int register_family(struct afswtch *);
+int register_cmdloop_branch(cmdloop_branch_t *);
+void statistics_func_init(statistics_func_t *, statistics_cb_t);
+void status_func_init(status_func_t *, status_cb_t);
+void usage_func_init(usage_func_t *, usage_cb_t);
+int register_statistics(statistics_func_t *);
+int register_status(status_func_t *);
+int register_usage(usage_func_t *);
+int register_flag(int);
+bool get_flag(int);
+
+extern bool lflag, Nflag, vflag, zflag;
+
+#endif /* _IFCONFIG_EXTERN_H */
diff --git a/sbin/ifconfig/ieee80211.c b/sbin/ifconfig/ieee80211.c
new file mode 100644 (file)
index 0000000..b20364c
--- /dev/null
@@ -0,0 +1,1375 @@
+/*     $NetBSD: ieee80211.c,v 1.28 2015/04/28 15:14:57 christos Exp $  */
+
+/*
+ * Copyright (c) 1983, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: ieee80211.c,v 1.28 2015/04/28 15:14:57 christos Exp $");
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_ether.h>
+#include <net/if_media.h>
+#include <net/route.h>
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_ioctl.h>
+#include <net80211/ieee80211_netbsd.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "extern.h"
+#include "parse.h"
+#include "env.h"
+#include "util.h"
+#include "prog_ops.h"
+
+static void ieee80211_statistics(prop_dictionary_t);
+static void ieee80211_status(prop_dictionary_t, prop_dictionary_t);
+static void ieee80211_constructor(void) __attribute__((constructor));
+static int set80211(prop_dictionary_t env, uint16_t, int16_t, int16_t,
+    u_int8_t *);
+static u_int ieee80211_mhz2ieee(u_int, u_int);
+static int getmaxrate(const uint8_t [15], u_int8_t);
+static const char * getcaps(int);
+static void printie(const char*, const uint8_t *, size_t, int);
+static int copy_essid(char [], size_t, const u_int8_t *, size_t);
+static void scan_and_wait(prop_dictionary_t);
+static void list_scan(prop_dictionary_t);
+static int mappsb(u_int , u_int);
+static int mapgsm(u_int , u_int);
+
+static int sethidessid(prop_dictionary_t, prop_dictionary_t);
+static int setapbridge(prop_dictionary_t, prop_dictionary_t);
+static int setifssid(prop_dictionary_t, prop_dictionary_t);
+static int setifnwkey(prop_dictionary_t, prop_dictionary_t);
+static int unsetifnwkey(prop_dictionary_t, prop_dictionary_t);
+static int unsetifbssid(prop_dictionary_t, prop_dictionary_t);
+static int setifbssid(prop_dictionary_t, prop_dictionary_t);
+static int setifchan(prop_dictionary_t, prop_dictionary_t);
+static int setiffrag(prop_dictionary_t, prop_dictionary_t);
+static int setifpowersave(prop_dictionary_t, prop_dictionary_t);
+static int setifpowersavesleep(prop_dictionary_t, prop_dictionary_t);
+static int setifrts(prop_dictionary_t, prop_dictionary_t);
+static int scan_exec(prop_dictionary_t, prop_dictionary_t);
+
+static void printies(const u_int8_t *, int, int);
+static void printwmeparam(const char *, const u_int8_t *, size_t , int);
+static void printwmeinfo(const char *, const u_int8_t *, size_t , int);
+static const char * wpa_cipher(const u_int8_t *);
+static const char * wpa_keymgmt(const u_int8_t *);
+static void printwpaie(const char *, const u_int8_t *, size_t , int);
+static const char * rsn_cipher(const u_int8_t *);
+static const char * rsn_keymgmt(const u_int8_t *);
+static void printrsnie(const char *, const u_int8_t *, size_t , int);
+static void printssid(const char *, const u_int8_t *, size_t , int);
+static void printrates(const char *, const u_int8_t *, size_t , int);
+static void printcountry(const char *, const u_int8_t *, size_t , int);
+static int iswpaoui(const u_int8_t *);
+static int iswmeinfo(const u_int8_t *);
+static int iswmeparam(const u_int8_t *);
+static const char * iename(int);
+
+extern struct pinteger parse_chan, parse_frag, parse_rts;
+extern struct pstr parse_bssid, parse_ssid, parse_nwkey;
+extern struct pinteger parse_powersavesleep;
+
+static const struct kwinst ieee80211boolkw[] = {
+         {.k_word = "hidessid", .k_key = "hidessid", .k_neg = true,
+          .k_type = KW_T_BOOL, .k_bool = true, .k_negbool = false,
+          .k_exec = sethidessid}
+       , {.k_word = "apbridge", .k_key = "apbridge", .k_neg = true,
+          .k_type = KW_T_BOOL, .k_bool = true, .k_negbool = false,
+          .k_exec = setapbridge}
+       , {.k_word = "powersave", .k_key = "powersave", .k_neg = true,
+          .k_type = KW_T_BOOL, .k_bool = true, .k_negbool = false,
+          .k_exec = setifpowersave}
+};
+
+static const struct kwinst listskw[] = {
+       {.k_word = "scan", .k_exec = scan_exec}
+};
+
+static struct pkw lists = PKW_INITIALIZER(&lists, "ieee80211 lists", NULL,
+    "list", listskw, __arraycount(listskw), &command_root.pb_parser);
+
+static const struct kwinst kw80211kw[] = {
+         {.k_word = "bssid", .k_nextparser = &parse_bssid.ps_parser}
+       , {.k_word = "-bssid", .k_exec = unsetifbssid,
+          .k_nextparser = &command_root.pb_parser}
+       , {.k_word = "chan", .k_nextparser = &parse_chan.pi_parser}
+       , {.k_word = "-chan", .k_key = "chan", .k_type = KW_T_UINT,
+          .k_uint = IEEE80211_CHAN_ANY, .k_exec = setifchan,
+          .k_nextparser = &command_root.pb_parser}
+       , {.k_word = "frag", .k_nextparser = &parse_frag.pi_parser}
+       , {.k_word = "-frag", .k_key = "frag", .k_type = KW_T_INT,
+          .k_int = IEEE80211_FRAG_MAX, .k_exec = setiffrag,
+          .k_nextparser = &command_root.pb_parser}
+       , {.k_word = "list", .k_nextparser = &lists.pk_parser}
+       , {.k_word = "nwid", .k_nextparser = &parse_ssid.ps_parser}
+       , {.k_word = "nwkey", .k_nextparser = &parse_nwkey.ps_parser}
+       , {.k_word = "-nwkey", .k_exec = unsetifnwkey,
+          .k_nextparser = &command_root.pb_parser}
+       , {.k_word = "rts", .k_nextparser = &parse_rts.pi_parser}
+       , {.k_word = "-rts", .k_key = "rts", .k_type = KW_T_INT,
+          .k_int = IEEE80211_RTS_MAX, .k_exec = setifrts,
+          .k_nextparser = &command_root.pb_parser}
+       , {.k_word = "ssid", .k_nextparser = &parse_ssid.ps_parser}
+       , {.k_word = "powersavesleep",
+          .k_nextparser = &parse_powersavesleep.pi_parser}
+};
+
+struct pkw kw80211 = PKW_INITIALIZER(&kw80211, "802.11 keywords", NULL, NULL,
+    kw80211kw, __arraycount(kw80211kw), NULL);
+
+struct pkw ieee80211bool = PKW_INITIALIZER(&ieee80211bool, "ieee80211 boolean",
+    NULL, NULL, ieee80211boolkw, __arraycount(ieee80211boolkw),
+    &command_root.pb_parser);
+
+struct pinteger parse_chan = PINTEGER_INITIALIZER1(&parse_chan, "chan",
+    0, UINT16_MAX, 10, setifchan, "chan", &command_root.pb_parser);
+
+struct pinteger parse_rts = PINTEGER_INITIALIZER1(&parse_rts, "rts",
+    IEEE80211_RTS_MIN, IEEE80211_RTS_MAX, 10,
+    setifrts, "rts", &command_root.pb_parser);
+
+struct pinteger parse_frag = PINTEGER_INITIALIZER1(&parse_frag, "frag",
+    IEEE80211_FRAG_MIN, IEEE80211_FRAG_MAX, 10,
+    setiffrag, "frag", &command_root.pb_parser);
+
+struct pstr parse_ssid = PSTR_INITIALIZER(&parse_pass, "ssid", setifssid,
+    "ssid", &command_root.pb_parser);
+
+struct pinteger parse_powersavesleep =
+    PINTEGER_INITIALIZER1(&parse_powersavesleep, "powersavesleep",
+    0, INT_MAX, 10, setifpowersavesleep, "powersavesleep",
+    &command_root.pb_parser);
+
+struct pstr parse_nwkey = PSTR_INITIALIZER1(&parse_nwkey, "nwkey", setifnwkey,
+    "nwkey", false, &command_root.pb_parser);
+
+struct pstr parse_bssid = PSTR_INITIALIZER1(&parse_bssid, "bssid", setifbssid,
+    "bssid", false, &command_root.pb_parser);
+
+static int
+set80211(prop_dictionary_t env, uint16_t type, int16_t val, int16_t len,
+    u_int8_t *data)
+{
+       struct ieee80211req     ireq;
+
+       memset(&ireq, 0, sizeof(ireq));
+       ireq.i_type = type;
+       ireq.i_val = val;
+       ireq.i_len = len;
+       ireq.i_data = data;
+       if (direct_ioctl(env, SIOCS80211, &ireq) == -1) {
+               warn("SIOCS80211");
+               return -1;
+       }
+       return 0;
+}
+
+static int
+sethidessid(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       bool on, rc;
+
+       rc = prop_dictionary_get_bool(env, "hidessid", &on);
+       assert(rc);
+       return set80211(env, IEEE80211_IOC_HIDESSID, on ? 1 : 0, 0, NULL);
+}
+
+static int
+setapbridge(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       bool on, rc;
+
+       rc = prop_dictionary_get_bool(env, "apbridge", &on);
+       assert(rc);
+       return set80211(env, IEEE80211_IOC_APBRIDGE, on ? 1 : 0, 0, NULL);
+}
+
+static enum ieee80211_opmode
+get80211opmode(prop_dictionary_t env)
+{
+       struct ifmediareq ifmr;
+
+       memset(&ifmr, 0, sizeof(ifmr));
+       if (direct_ioctl(env, SIOCGIFMEDIA, &ifmr) == -1)
+               ;
+       else if (ifmr.ifm_current & IFM_IEEE80211_ADHOC)
+               return IEEE80211_M_IBSS;        /* XXX ahdemo */
+       else if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
+               return IEEE80211_M_HOSTAP;
+       else if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
+               return IEEE80211_M_MONITOR;
+
+       return IEEE80211_M_STA;
+}
+
+static int
+setifssid(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ieee80211_nwid nwid;
+       ssize_t len;
+
+       memset(&nwid, 0, sizeof(nwid));
+       if ((len = getargdata(env, "ssid", nwid.i_nwid,
+           sizeof(nwid.i_nwid))) == -1)
+               errx(EXIT_FAILURE, "%s: SSID too long", __func__);
+       nwid.i_len = (uint8_t)len;
+       if (indirect_ioctl(env, SIOCS80211NWID, &nwid) == -1)
+               err(EXIT_FAILURE, "SIOCS80211NWID");
+       return 0;
+}
+
+static int
+unsetifbssid(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ieee80211_bssid bssid;
+
+       memset(&bssid, 0, sizeof(bssid));
+
+       if (direct_ioctl(env, SIOCS80211BSSID, &bssid) == -1)
+               err(EXIT_FAILURE, "SIOCS80211BSSID");
+       return 0;
+}
+
+static int
+setifbssid(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       char buf[24];
+       struct ieee80211_bssid bssid;
+       struct ether_addr *ea;
+
+       if (getargstr(env, "bssid", buf, sizeof(buf)) == -1)
+               errx(EXIT_FAILURE, "%s: BSSID too long", __func__);
+
+       ea = ether_aton(buf);
+       if (ea == NULL) {
+               errx(EXIT_FAILURE, "malformed BSSID: %s", buf);
+               return -1;
+       }
+       memcpy(&bssid.i_bssid, ea->ether_addr_octet,
+           sizeof(bssid.i_bssid));
+
+       if (direct_ioctl(env, SIOCS80211BSSID, &bssid) == -1)
+               err(EXIT_FAILURE, "SIOCS80211BSSID");
+       return 0;
+}
+
+static int
+setifrts(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       bool rc;
+       int16_t val;
+
+       rc = prop_dictionary_get_int16(env, "rts", &val);
+       assert(rc);
+       if (set80211(env, IEEE80211_IOC_RTSTHRESHOLD, val, 0, NULL) == -1)
+               err(EXIT_FAILURE, "IEEE80211_IOC_RTSTHRESHOLD");
+       return 0;
+}
+
+static int
+setiffrag(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       bool rc;
+       int16_t val;
+
+       rc = prop_dictionary_get_int16(env, "frag", &val);
+       assert(rc);
+       if (set80211(env, IEEE80211_IOC_FRAGTHRESHOLD, val, 0, NULL) == -1)
+               err(EXIT_FAILURE, "IEEE80211_IOC_FRAGTHRESHOLD");
+       return 0;
+}
+
+static int
+setifchan(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       bool rc;
+       struct ieee80211chanreq channel;
+
+       rc = prop_dictionary_get_uint16(env, "chan", &channel.i_channel);
+       assert(rc);
+       if (direct_ioctl(env, SIOCS80211CHANNEL, &channel) == -1)
+               err(EXIT_FAILURE, "SIOCS80211CHANNEL");
+       return 0;
+}
+
+static int
+setifnwkey(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       const char *val;
+       char buf[256];
+       struct ieee80211_nwkey nwkey;
+       int i;
+       u_int8_t keybuf[IEEE80211_WEP_NKID][16];
+
+       if (getargstr(env, "nwkey", buf, sizeof(buf)) == -1)
+               errx(EXIT_FAILURE, "%s: nwkey too long", __func__);
+
+       val = buf;
+
+       nwkey.i_wepon = IEEE80211_NWKEY_WEP;
+       nwkey.i_defkid = 1;
+       for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+               nwkey.i_key[i].i_keylen = sizeof(keybuf[i]);
+               nwkey.i_key[i].i_keydat = keybuf[i];
+       }
+       if (strcasecmp("persist", val) == 0) {
+               /* use all values from persistent memory */
+               nwkey.i_wepon |= IEEE80211_NWKEY_PERSIST;
+               nwkey.i_defkid = 0;
+               for (i = 0; i < IEEE80211_WEP_NKID; i++)
+                       nwkey.i_key[i].i_keylen = -1;
+       } else if (strncasecmp("persist:", val, 8) == 0) {
+               val += 8;
+               /* program keys in persistent memory */
+               nwkey.i_wepon |= IEEE80211_NWKEY_PERSIST;
+               goto set_nwkey;
+       } else {
+  set_nwkey:
+               if (isdigit((unsigned char)val[0]) && val[1] == ':') {
+                       /* specifying a full set of four keys */
+                       nwkey.i_defkid = val[0] - '0';
+                       val += 2;
+                       for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+                               val = get_string(val, ",", keybuf[i],
+                                   &nwkey.i_key[i].i_keylen, true);
+                               if (val == NULL) {
+                                       errno = EINVAL;
+                                       return -1;
+                               }
+                       }
+                       if (*val != '\0') {
+                               errx(EXIT_FAILURE, "SIOCS80211NWKEY: too many keys.");
+                       }
+               } else {
+                       val = get_string(val, NULL, keybuf[0],
+                           &nwkey.i_key[0].i_keylen, true);
+                       if (val == NULL) {
+                               errno = EINVAL;
+                               return -1;
+                       }
+                       i = 1;
+               }
+       }
+       for (; i < IEEE80211_WEP_NKID; i++)
+               nwkey.i_key[i].i_keylen = 0;
+
+       if (direct_ioctl(env, SIOCS80211NWKEY, &nwkey) == -1)
+               err(EXIT_FAILURE, "SIOCS80211NWKEY");
+       return 0;
+}
+
+static int
+unsetifnwkey(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ieee80211_nwkey nwkey;
+       int i;
+
+       nwkey.i_wepon = 0;
+       nwkey.i_defkid = 1;
+       for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+               nwkey.i_key[i].i_keylen = 0;
+               nwkey.i_key[i].i_keydat = NULL;
+       }
+
+       if (direct_ioctl(env, SIOCS80211NWKEY, &nwkey) == -1)
+               err(EXIT_FAILURE, "SIOCS80211NWKEY");
+       return 0;
+}
+
+static int
+setifpowersave(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ieee80211_power power;
+       bool on, rc;
+
+       if (direct_ioctl(env, SIOCG80211POWER, &power) == -1)
+               err(EXIT_FAILURE, "SIOCG80211POWER");
+
+       rc = prop_dictionary_get_bool(env, "powersave", &on);
+       assert(rc);
+
+       power.i_enabled = on ? 1 : 0;
+       if (direct_ioctl(env, SIOCS80211POWER, &power) == -1) {
+               warn("SIOCS80211POWER");
+               return -1;
+       }
+       return 0;
+}
+
+static int
+setifpowersavesleep(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ieee80211_power power;
+       int64_t maxsleep;
+       bool rc;
+
+       rc = prop_dictionary_get_int64(env, "powersavesleep", &maxsleep);
+       assert(rc);
+
+       if (direct_ioctl(env, SIOCG80211POWER, &power) == -1)
+               err(EXIT_FAILURE, "SIOCG80211POWER");
+
+       power.i_maxsleep = maxsleep;
+       if (direct_ioctl(env, SIOCS80211POWER, &power) == -1)
+               err(EXIT_FAILURE, "SIOCS80211POWER");
+       return 0;
+}
+
+static int
+scan_exec(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ifreq ifr;
+
+       if (direct_ioctl(env, SIOCGIFFLAGS, &ifr) == -1) {
+               warn("ioctl(SIOCGIFFLAGS)");
+               return -1;
+       }
+
+       if ((ifr.ifr_flags & IFF_UP) == 0) 
+               errx(EXIT_FAILURE, "The interface must be up before scanning.");
+
+       scan_and_wait(env);
+       list_scan(env);
+
+       return 0;
+}
+
+static void
+ieee80211_statistics(prop_dictionary_t env)
+{
+#ifndef SMALL
+       struct ieee80211_stats stats;
+       struct ifreq ifr;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_buflen = sizeof(stats);
+       ifr.ifr_buf = (caddr_t)&stats;
+       if (direct_ioctl(env, (zflag) ? SIOCG80211ZSTATS : SIOCG80211STATS,
+           &ifr) == -1)
+               return;
+#define        STAT_PRINT(_member, _desc)      \
+       printf("\t" _desc ": %" PRIu32 "\n", stats._member)
+
+       STAT_PRINT(is_rx_badversion, "rx frame with bad version");
+       STAT_PRINT(is_rx_tooshort, "rx frame too short");
+       STAT_PRINT(is_rx_wrongbss, "rx from wrong bssid");
+       STAT_PRINT(is_rx_dup, "rx discard 'cuz dup");
+       STAT_PRINT(is_rx_wrongdir, "rx w/ wrong direction");
+       STAT_PRINT(is_rx_mcastecho, "rx discard 'cuz mcast echo");
+       STAT_PRINT(is_rx_notassoc, "rx discard 'cuz sta !assoc");
+       STAT_PRINT(is_rx_noprivacy, "rx w/ wep but privacy off");
+       STAT_PRINT(is_rx_unencrypted, "rx w/o wep and privacy on");
+       STAT_PRINT(is_rx_wepfail, "rx wep processing failed");
+       STAT_PRINT(is_rx_decap, "rx decapsulation failed");
+       STAT_PRINT(is_rx_mgtdiscard, "rx discard mgt frames");
+       STAT_PRINT(is_rx_ctl, "rx discard ctrl frames");
+       STAT_PRINT(is_rx_beacon, "rx beacon frames");
+       STAT_PRINT(is_rx_rstoobig, "rx rate set truncated");
+       STAT_PRINT(is_rx_elem_missing, "rx required element missing");
+       STAT_PRINT(is_rx_elem_toobig, "rx element too big");
+       STAT_PRINT(is_rx_elem_toosmall, "rx element too small");
+       STAT_PRINT(is_rx_elem_unknown, "rx element unknown");
+       STAT_PRINT(is_rx_badchan, "rx frame w/ invalid chan");
+       STAT_PRINT(is_rx_chanmismatch, "rx frame chan mismatch");
+       STAT_PRINT(is_rx_nodealloc, "rx frame dropped");
+       STAT_PRINT(is_rx_ssidmismatch, "rx frame ssid mismatch ");
+       STAT_PRINT(is_rx_auth_unsupported, "rx w/ unsupported auth alg");
+       STAT_PRINT(is_rx_auth_fail, "rx sta auth failure");
+       STAT_PRINT(is_rx_auth_countermeasures, "rx auth discard 'cuz CM");
+       STAT_PRINT(is_rx_assoc_bss, "rx assoc from wrong bssid");
+       STAT_PRINT(is_rx_assoc_notauth, "rx assoc w/o auth");
+       STAT_PRINT(is_rx_assoc_capmismatch, "rx assoc w/ cap mismatch");
+       STAT_PRINT(is_rx_assoc_norate, "rx assoc w/ no rate match");
+       STAT_PRINT(is_rx_assoc_badwpaie, "rx assoc w/ bad WPA IE");
+       STAT_PRINT(is_rx_deauth, "rx deauthentication");
+       STAT_PRINT(is_rx_disassoc, "rx disassociation");
+       STAT_PRINT(is_rx_badsubtype, "rx frame w/ unknown subtyp");
+       STAT_PRINT(is_rx_nobuf, "rx failed for lack of buf");
+       STAT_PRINT(is_rx_decryptcrc, "rx decrypt failed on crc");
+       STAT_PRINT(is_rx_ahdemo_mgt, "rx discard ahdemo mgt fram");
+       STAT_PRINT(is_rx_bad_auth, "rx bad auth request");
+       STAT_PRINT(is_rx_unauth, "rx on unauthorized port");
+       STAT_PRINT(is_rx_badkeyid, "rx w/ incorrect keyid");
+       STAT_PRINT(is_rx_ccmpreplay, "rx seq# violation (CCMP)");
+       STAT_PRINT(is_rx_ccmpformat, "rx format bad (CCMP)");
+       STAT_PRINT(is_rx_ccmpmic, "rx MIC check failed (CCMP)");
+       STAT_PRINT(is_rx_tkipreplay, "rx seq# violation (TKIP)");
+       STAT_PRINT(is_rx_tkipformat, "rx format bad (TKIP)");
+       STAT_PRINT(is_rx_tkipmic, "rx MIC check failed (TKIP)");
+       STAT_PRINT(is_rx_tkipicv, "rx ICV check failed (TKIP)");
+       STAT_PRINT(is_rx_badcipher, "rx failed 'cuz key type");
+       STAT_PRINT(is_rx_nocipherctx, "rx failed 'cuz key !setup");
+       STAT_PRINT(is_rx_acl, "rx discard 'cuz acl policy");
+
+       STAT_PRINT(is_tx_nobuf, "tx failed for lack of buf");
+       STAT_PRINT(is_tx_nonode, "tx failed for no node");
+       STAT_PRINT(is_tx_unknownmgt, "tx of unknown mgt frame");
+       STAT_PRINT(is_tx_badcipher, "tx failed 'cuz key type");
+       STAT_PRINT(is_tx_nodefkey, "tx failed 'cuz no defkey");
+       STAT_PRINT(is_tx_noheadroom, "tx failed 'cuz no space");
+       STAT_PRINT(is_tx_fragframes, "tx frames fragmented");
+       STAT_PRINT(is_tx_frags, "tx fragments created");
+
+       STAT_PRINT(is_scan_active, "active scans started");
+       STAT_PRINT(is_scan_passive, "passive scans started");
+       STAT_PRINT(is_node_timeout, "nodes timed out inactivity");
+       STAT_PRINT(is_crypto_nomem, "no memory for crypto ctx");
+       STAT_PRINT(is_crypto_tkip, "tkip crypto done in s/w");
+       STAT_PRINT(is_crypto_tkipenmic, "tkip en-MIC done in s/w");
+       STAT_PRINT(is_crypto_tkipdemic, "tkip de-MIC done in s/w");
+       STAT_PRINT(is_crypto_tkipcm, "tkip counter measures");
+       STAT_PRINT(is_crypto_ccmp, "ccmp crypto done in s/w");
+       STAT_PRINT(is_crypto_wep, "wep crypto done in s/w");
+       STAT_PRINT(is_crypto_setkey_cipher, "cipher rejected key");
+       STAT_PRINT(is_crypto_setkey_nokey, "no key index for setkey");
+       STAT_PRINT(is_crypto_delkey, "driver key delete failed");
+       STAT_PRINT(is_crypto_badcipher, "unknown cipher");
+       STAT_PRINT(is_crypto_nocipher, "cipher not available");
+       STAT_PRINT(is_crypto_attachfail, "cipher attach failed");
+       STAT_PRINT(is_crypto_swfallback, "cipher fallback to s/w");
+       STAT_PRINT(is_crypto_keyfail, "driver key alloc failed");
+       STAT_PRINT(is_crypto_enmicfail, "en-MIC failed");
+       STAT_PRINT(is_ibss_capmismatch, "merge failed-cap mismatch");
+       STAT_PRINT(is_ibss_norate, "merge failed-rate mismatch");
+       STAT_PRINT(is_ps_unassoc, "ps-poll for unassoc. sta");
+       STAT_PRINT(is_ps_badaid, "ps-poll w/ incorrect aid");
+       STAT_PRINT(is_ps_qempty, "ps-poll w/ nothing to send");
+       STAT_PRINT(is_ff_badhdr, "fast frame rx'd w/ bad hdr");
+       STAT_PRINT(is_ff_tooshort, "fast frame rx decap error");
+       STAT_PRINT(is_ff_split, "fast frame rx split error");
+       STAT_PRINT(is_ff_decap, "fast frames decap'd");
+       STAT_PRINT(is_ff_encap, "fast frames encap'd for tx");
+       STAT_PRINT(is_rx_badbintval, "rx frame w/ bogus bintval");
+#endif
+}
+
+static void
+ieee80211_status(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       int i, nwkey_verbose;
+       struct ieee80211_nwid nwid;
+       struct ieee80211_nwkey nwkey;
+       struct ieee80211_power power;
+       u_int8_t keybuf[IEEE80211_WEP_NKID][16];
+       struct ieee80211_bssid bssid;
+       struct ieee80211chanreq channel;
+       struct ieee80211req ireq;
+       struct ether_addr ea;
+       static const u_int8_t zero_macaddr[IEEE80211_ADDR_LEN];
+       enum ieee80211_opmode opmode = get80211opmode(env);
+
+       memset(&bssid, 0, sizeof(bssid));
+       memset(&nwkey, 0, sizeof(nwkey));
+       memset(&nwid, 0, sizeof(nwid));
+       memset(&nwid, 0, sizeof(nwid));
+
+       if (indirect_ioctl(env, SIOCG80211NWID, &nwid) == -1)
+               return;
+       if (nwid.i_len > IEEE80211_NWID_LEN) {
+               errx(EXIT_FAILURE, "SIOCG80211NWID: wrong length of nwid (%d)", nwid.i_len);
+       }
+       printf("\tssid ");
+       print_string(nwid.i_nwid, nwid.i_len);
+
+       if (opmode == IEEE80211_M_HOSTAP) {
+               ireq.i_type = IEEE80211_IOC_HIDESSID;
+               if (direct_ioctl(env, SIOCG80211, &ireq) != -1) {
+                        if (ireq.i_val)
+                                printf(" [hidden]");
+                        else if (vflag)
+                                printf(" [shown]");
+                }
+
+               ireq.i_type = IEEE80211_IOC_APBRIDGE;
+               if (direct_ioctl(env, SIOCG80211, &ireq) != -1) {
+                       if (ireq.i_val)
+                               printf(" apbridge");
+                       else if (vflag)
+                               printf(" -apbridge");
+               }
+        }
+
+       ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD;
+       if (direct_ioctl(env, SIOCG80211, &ireq) == -1)
+               ;
+       else if (ireq.i_val < IEEE80211_RTS_MAX)
+               printf(" rts %d", ireq.i_val);
+       else if (vflag)
+               printf(" -rts");
+
+       ireq.i_type = IEEE80211_IOC_FRAGTHRESHOLD;
+       if (direct_ioctl(env, SIOCG80211, &ireq) == -1)
+               ;
+       else if (ireq.i_val < IEEE80211_FRAG_MAX)
+               printf(" frag %d", ireq.i_val);
+       else if (vflag)
+               printf(" -frag");
+
+       memset(&nwkey, 0, sizeof(nwkey));
+       /* show nwkey only when WEP is enabled */
+       if (direct_ioctl(env, SIOCG80211NWKEY, &nwkey) == -1 ||
+           nwkey.i_wepon == 0) {
+               printf("\n");
+               goto skip_wep;
+       }
+
+       printf(" nwkey ");
+       /* try to retrieve WEP keys */
+       for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+               nwkey.i_key[i].i_keydat = keybuf[i];
+               nwkey.i_key[i].i_keylen = sizeof(keybuf[i]);
+       }
+       if (direct_ioctl(env, SIOCG80211NWKEY, &nwkey) == -1) {
+               printf("*****");
+       } else {
+               nwkey_verbose = 0;
+               /* check to see non default key or multiple keys defined */
+               if (nwkey.i_defkid != 1) {
+                       nwkey_verbose = 1;
+               } else {
+                       for (i = 1; i < IEEE80211_WEP_NKID; i++) {
+                               if (nwkey.i_key[i].i_keylen != 0) {
+                                       nwkey_verbose = 1;
+                                       break;
+                               }
+                       }
+               }
+               /* check extra ambiguity with keywords */
+               if (!nwkey_verbose) {
+                       if (nwkey.i_key[0].i_keylen >= 2 &&
+                           isdigit(nwkey.i_key[0].i_keydat[0]) &&
+                           nwkey.i_key[0].i_keydat[1] == ':')
+                               nwkey_verbose = 1;
+                       else if (nwkey.i_key[0].i_keylen >= 7 &&
+                           strncasecmp("persist",
+                           (const char *)nwkey.i_key[0].i_keydat, 7) == 0)
+                               nwkey_verbose = 1;
+               }
+               if (nwkey_verbose)
+                       printf("%d:", nwkey.i_defkid);
+               for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+                       if (i > 0)
+                               printf(",");
+                       if (nwkey.i_key[i].i_keylen < 0)
+                               printf("persist");
+                       else
+                               print_string(nwkey.i_key[i].i_keydat,
+                                   nwkey.i_key[i].i_keylen);
+                       if (!nwkey_verbose)
+                               break;
+               }
+       }
+       printf("\n");
+
+ skip_wep:
+       if (direct_ioctl(env, SIOCG80211POWER, &power) == -1)
+               goto skip_power;
+       printf("\tpowersave ");
+       if (power.i_enabled)
+               printf("on (%dms sleep)", power.i_maxsleep);
+       else
+               printf("off");
+       printf("\n");
+
+ skip_power:
+       if (direct_ioctl(env, SIOCG80211BSSID, &bssid) == -1)
+               return;
+       if (direct_ioctl(env, SIOCG80211CHANNEL, &channel) == -1)
+               return;
+       if (memcmp(bssid.i_bssid, zero_macaddr, IEEE80211_ADDR_LEN) == 0) {
+               if (channel.i_channel != (u_int16_t)-1)
+                       printf("\tchan %d\n", channel.i_channel);
+       } else {
+               memcpy(ea.ether_addr_octet, bssid.i_bssid,
+                   sizeof(ea.ether_addr_octet));
+               printf("\tbssid %s", ether_ntoa(&ea));
+               if (channel.i_channel != IEEE80211_CHAN_ANY)
+                       printf(" chan %d", channel.i_channel);
+               printf("\n");
+       }
+}
+
+static void
+scan_and_wait(prop_dictionary_t env)
+{
+       int sroute;
+
+       sroute = prog_socket(PF_ROUTE, SOCK_RAW, 0);
+       if (sroute < 0) {
+               warn("socket(PF_ROUTE,SOCK_RAW)");
+               return;
+       }
+       /* NB: only root can trigger a scan so ignore errors */
+       if (set80211(env, IEEE80211_IOC_SCAN_REQ, 0, 0, NULL) >= 0) {
+               char buf[2048];
+               struct if_announcemsghdr *ifan;
+               struct rt_msghdr *rtm;
+
+               do {
+                       if (prog_read(sroute, buf, sizeof(buf)) < 0) {
+                               warn("read(PF_ROUTE)");
+                               break;
+                       }
+                       rtm = (struct rt_msghdr *) buf;
+                       if (rtm->rtm_version != RTM_VERSION)
+                               break;
+                       ifan = (struct if_announcemsghdr *) rtm;
+               } while (rtm->rtm_type != RTM_IEEE80211 ||
+                   ifan->ifan_what != RTM_IEEE80211_SCAN);
+       }
+       prog_close(sroute);
+}
+
+static void
+list_scan(prop_dictionary_t env)
+{
+       u_int8_t buf[24*1024];
+       struct ieee80211req ireq;
+       char ssid[IEEE80211_NWID_LEN+1];
+       const u_int8_t *cp;
+       int len, ssidmax;
+
+       memset(&ireq, 0, sizeof(ireq));
+       ireq.i_type = IEEE80211_IOC_SCAN_RESULTS;
+       ireq.i_data = buf;
+       ireq.i_len = sizeof(buf);
+       if (direct_ioctl(env, SIOCG80211, &ireq) < 0)
+               errx(EXIT_FAILURE, "unable to get scan results");
+       len = ireq.i_len;
+       if (len < (int)sizeof(struct ieee80211req_scan_result))
+               return;
+
+       ssidmax = IEEE80211_NWID_LEN;
+       printf("%-*.*s  %-17.17s  %4s %4s  %-7s %3s %4s\n"
+               , ssidmax, ssidmax, "SSID"
+               , "BSSID"
+               , "CHAN"
+               , "RATE"
+               , "S:N"
+               , "INT"
+               , "CAPS"
+       );
+       cp = buf;
+       do {
+               const struct ieee80211req_scan_result *sr;
+               const uint8_t *vp;
+
+               sr = (const struct ieee80211req_scan_result *) cp;
+               vp = (const u_int8_t *)(sr+1);
+               printf("%-*.*s  %s  %3d  %3dM %3d:%-3d  %3d %-4.4s"
+                       , ssidmax
+                         , copy_essid(ssid, ssidmax, vp, sr->isr_ssid_len)
+                         , ssid
+                       , ether_ntoa((const struct ether_addr *) sr->isr_bssid)
+                       , ieee80211_mhz2ieee(sr->isr_freq, sr->isr_flags)
+                       , getmaxrate(sr->isr_rates, sr->isr_nrates)
+                       , sr->isr_rssi, sr->isr_noise
+                       , sr->isr_intval
+                       , getcaps(sr->isr_capinfo)
+               );
+               printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);
+               printf("\n");
+               cp += sr->isr_len, len -= sr->isr_len;
+       } while (len >= (int)sizeof(struct ieee80211req_scan_result));
+}
+/*
+ * Convert MHz frequency to IEEE channel number.
+ */
+static u_int
+ieee80211_mhz2ieee(u_int isrfreq, u_int isrflags)
+{
+       if ((isrflags & IEEE80211_CHAN_GSM) || (907 <= isrfreq && isrfreq <= 922))
+               return mapgsm(isrfreq, isrflags);
+       if (isrfreq == 2484)
+               return 14;
+       if (isrfreq < 2484)
+               return (isrfreq - 2407) / 5;
+       if (isrfreq < 5000) {
+               if (isrflags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER))
+                       return mappsb(isrfreq, isrflags);
+               else if (isrfreq > 4900)
+                       return (isrfreq - 4000) / 5;
+               else
+                       return 15 + ((isrfreq - 2512) / 20);
+       }
+       return (isrfreq - 5000) / 5;
+}
+
+static int
+getmaxrate(const u_int8_t rates[15], u_int8_t nrates)
+{
+       int i, maxrate = -1;
+
+       for (i = 0; i < nrates; i++) {
+               int rate = rates[i] & IEEE80211_RATE_VAL;
+               if (rate > maxrate)
+                       maxrate = rate;
+       }
+       return maxrate / 2;
+}
+
+static const char *
+getcaps(int capinfo)
+{
+       static char capstring[32];
+       char *cp = capstring;
+
+       if (capinfo & IEEE80211_CAPINFO_ESS)
+               *cp++ = 'E';
+       if (capinfo & IEEE80211_CAPINFO_IBSS)
+               *cp++ = 'I';
+       if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
+               *cp++ = 'c';
+       if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
+               *cp++ = 'C';
+       if (capinfo & IEEE80211_CAPINFO_PRIVACY)
+               *cp++ = 'P';
+       if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
+               *cp++ = 'S';
+       if (capinfo & IEEE80211_CAPINFO_PBCC)
+               *cp++ = 'B';
+       if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
+               *cp++ = 'A';
+       if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
+               *cp++ = 's';
+       if (capinfo & IEEE80211_CAPINFO_RSN)
+               *cp++ = 'R';
+       if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
+               *cp++ = 'D';
+       *cp = '\0';
+       return capstring;
+}
+
+static void
+printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
+{
+       printf("%s", tag);
+
+       maxlen -= strlen(tag)+2;
+       if ((int)(2*ielen) > maxlen)
+               maxlen--;
+       printf("<");
+       for (; ielen > 0; ie++, ielen--) {
+               if (maxlen-- <= 0)
+                       break;
+               printf("%02x", *ie);
+       }
+       if (ielen != 0)
+               printf("-");
+       printf(">");
+}
+
+#define LE_READ_2(p)                                   \
+       ((u_int16_t)                                    \
+        ((((const u_int8_t *)(p))[0]      ) |          \
+         (((const u_int8_t *)(p))[1] <<  8)))
+#define LE_READ_4(p)                                   \
+       ((u_int32_t)                                    \
+        ((((const u_int8_t *)(p))[0]      ) |          \
+         (((const u_int8_t *)(p))[1] <<  8) |          \
+         (((const u_int8_t *)(p))[2] << 16) |          \
+         (((const u_int8_t *)(p))[3] << 24)))
+
+/*
+ * NB: The decoding routines assume a properly formatted ie
+ *     which should be safe as the kernel only retains them
+ *     if they parse ok.
+ */
+
+static void
+printwmeparam(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
+{
+#define        MS(_v, _f)      (((_v) & _f) >> _f##_S)
+       static const char *acnames[] = { "BE", "BK", "VO", "VI" };
+       const struct ieee80211_wme_param *wme =
+           (const struct ieee80211_wme_param *) ie;
+       int i;
+
+       printf("%s", tag);
+       if (!vflag)
+               return;
+       printf("<qosinfo 0x%x", wme->param_qosInfo);
+       ie += offsetof(struct ieee80211_wme_param, params_acParams);
+       for (i = 0; i < WME_NUM_AC; i++) {
+               const struct ieee80211_wme_acparams *ac =
+                   &wme->params_acParams[i];
+
+               printf(" %s[%saifsn %u cwmin %u cwmax %u txop %u]"
+                       , acnames[i]
+                       , MS(ac->acp_aci_aifsn, WME_PARAM_ACM) ? "acm " : ""
+                       , MS(ac->acp_aci_aifsn, WME_PARAM_AIFSN)
+                       , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMIN)
+                       , MS(ac->acp_logcwminmax, WME_PARAM_LOGCWMAX)
+                       , LE_READ_2(&ac->acp_txop)
+               );
+       }
+       printf(">");
+#undef MS
+}
+
+static void
+printwmeinfo(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
+{
+       printf("%s", tag);
+       if (vflag) {
+               const struct ieee80211_wme_info *wme =
+                   (const struct ieee80211_wme_info *) ie;
+               printf("<version 0x%x info 0x%x>",
+                   wme->wme_version, wme->wme_info);
+       }
+}
+
+static const char *
+wpa_cipher(const u_int8_t *sel)
+{
+#define        WPA_SEL(x)      (((x)<<24)|WPA_OUI)
+       u_int32_t w = LE_READ_4(sel);
+
+       switch (w) {
+       case WPA_SEL(WPA_CSE_NULL):
+               return "NONE";
+       case WPA_SEL(WPA_CSE_WEP40):
+               return "WEP40";
+       case WPA_SEL(WPA_CSE_WEP104):
+               return "WEP104";
+       case WPA_SEL(WPA_CSE_TKIP):
+               return "TKIP";
+       case WPA_SEL(WPA_CSE_CCMP):
+               return "AES-CCMP";
+       }
+       return "?";             /* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+static const char *
+wpa_keymgmt(const u_int8_t *sel)
+{
+#define        WPA_SEL(x)      (((x)<<24)|WPA_OUI)
+       u_int32_t w = LE_READ_4(sel);
+
+       switch (w) {
+       case WPA_SEL(WPA_ASE_8021X_UNSPEC):
+               return "8021X-UNSPEC";
+       case WPA_SEL(WPA_ASE_8021X_PSK):
+               return "8021X-PSK";
+       case WPA_SEL(WPA_ASE_NONE):
+               return "NONE";
+       }
+       return "?";
+#undef WPA_SEL
+}
+
+static void
+printwpaie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
+{
+       u_int8_t len = ie[1];
+
+       printf("%s", tag);
+       if (vflag) {
+               const char *sep;
+               int n;
+
+               ie += 6, len -= 4;              /* NB: len is payload only */
+
+               printf("<v%u", LE_READ_2(ie));
+               ie += 2, len -= 2;
+
+               printf(" mc:%s", wpa_cipher(ie));
+               ie += 4, len -= 4;
+
+               /* unicast ciphers */
+               n = LE_READ_2(ie);
+               ie += 2, len -= 2;
+               sep = " uc:";
+               for (; n > 0; n--) {
+                       printf("%s%s", sep, wpa_cipher(ie));
+                       ie += 4, len -= 4;
+                       sep = "+";
+               }
+
+               /* key management algorithms */
+               n = LE_READ_2(ie);
+               ie += 2, len -= 2;
+               sep = " km:";
+               for (; n > 0; n--) {
+                       printf("%s%s", sep, wpa_keymgmt(ie));
+                       ie += 4, len -= 4;
+                       sep = "+";
+               }
+
+               if (len > 2)            /* optional capabilities */
+                       printf(", caps 0x%x", LE_READ_2(ie));
+               printf(">");
+       }
+}
+
+static const char *
+rsn_cipher(const u_int8_t *sel)
+{
+#define        RSN_SEL(x)      (((x)<<24)|RSN_OUI)
+       u_int32_t w = LE_READ_4(sel);
+
+       switch (w) {
+       case RSN_SEL(RSN_CSE_NULL):
+               return "NONE";
+       case RSN_SEL(RSN_CSE_WEP40):
+               return "WEP40";
+       case RSN_SEL(RSN_CSE_WEP104):
+               return "WEP104";
+       case RSN_SEL(RSN_CSE_TKIP):
+               return "TKIP";
+       case RSN_SEL(RSN_CSE_CCMP):
+               return "AES-CCMP";
+       case RSN_SEL(RSN_CSE_WRAP):
+               return "AES-OCB";
+       }
+       return "?";
+#undef WPA_SEL
+}
+
+static const char *
+rsn_keymgmt(const u_int8_t *sel)
+{
+#define        RSN_SEL(x)      (((x)<<24)|RSN_OUI)
+       u_int32_t w = LE_READ_4(sel);
+
+       switch (w) {
+       case RSN_SEL(RSN_ASE_8021X_UNSPEC):
+               return "8021X-UNSPEC";
+       case RSN_SEL(RSN_ASE_8021X_PSK):
+               return "8021X-PSK";
+       case RSN_SEL(RSN_ASE_NONE):
+               return "NONE";
+       }
+       return "?";
+#undef RSN_SEL
+}
+
+static void
+printrsnie(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
+{
+       const char *sep;
+       int n;
+
+       printf("%s", tag);
+       if (!vflag)
+               return;
+
+       ie += 2, ielen -= 2;
+
+       printf("<v%u", LE_READ_2(ie));
+       ie += 2, ielen -= 2;
+
+       printf(" mc:%s", rsn_cipher(ie));
+       ie += 4, ielen -= 4;
+
+       /* unicast ciphers */
+       n = LE_READ_2(ie);
+       ie += 2, ielen -= 2;
+       sep = " uc:";
+       for (; n > 0; n--) {
+               printf("%s%s", sep, rsn_cipher(ie));
+               ie += 4, ielen -= 4;
+               sep = "+";
+       }
+
+       /* key management algorithms */
+       n = LE_READ_2(ie);
+       ie += 2, ielen -= 2;
+       sep = " km:";
+       for (; n > 0; n--) {
+               printf("%s%s", sep, rsn_keymgmt(ie));
+               ie += 4, ielen -= 4;
+               sep = "+";
+       }
+
+       if (ielen > 2)          /* optional capabilities */
+               printf(", caps 0x%x", LE_READ_2(ie));
+       /* XXXPMKID */
+       printf(">");
+}
+
+/*
+ * Copy the ssid string contents into buf, truncating to fit.  If the
+ * ssid is entirely printable then just copy intact.  Otherwise convert
+ * to hexadecimal.  If the result is truncated then replace the last
+ * three characters with "...".
+ */
+static int
+copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
+{
+       const u_int8_t *p;
+       size_t maxlen, i;
+
+       if (essid_len > bufsize)
+               maxlen = bufsize;
+       else
+               maxlen = essid_len;
+       /* determine printable or not */
+       for (i = 0, p = essid; i < maxlen; i++, p++) {
+               if (*p < ' ' || *p > 0x7e)
+                       break;
+       }
+       if (i != maxlen) {              /* not printable, print as hex */
+               if (bufsize < 3)
+                       return 0;
+               strlcpy(buf, "0x", bufsize);
+               bufsize -= 2;
+               p = essid;
+               for (i = 0; i < maxlen && bufsize >= 2; i++) {
+                       sprintf(&buf[2+2*i], "%02x", p[i]);
+                       bufsize -= 2;
+               }
+               if (i != essid_len)
+                       memcpy(&buf[2+2*i-3], "...", 3);
+       } else {                        /* printable, truncate as needed */
+               memcpy(buf, essid, maxlen);
+               if (maxlen != essid_len)
+                       memcpy(&buf[maxlen-3], "...", 3);
+       }
+       return maxlen;
+}
+
+static void
+printssid(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
+{
+       char ssid[2*IEEE80211_NWID_LEN+1];
+
+       printf("%s<%.*s>", tag, copy_essid(ssid, maxlen, ie+2, ie[1]), ssid);
+}
+
+static void
+printrates(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
+{
+       const char *sep;
+       size_t i;
+
+       printf("%s", tag);
+       sep = "<";
+       for (i = 2; i < ielen; i++) {
+               printf("%s%s%d", sep,
+                   ie[i] & IEEE80211_RATE_BASIC ? "B" : "",
+                   ie[i] & IEEE80211_RATE_VAL);
+               sep = ",";
+       }
+       printf(">");
+}
+
+static void
+printcountry(const char *tag, const u_int8_t *ie, size_t ielen, int maxlen)
+{
+       const struct ieee80211_country_ie *cie =
+          (const struct ieee80211_country_ie *) ie;
+       int i, nbands, schan, nchan;
+
+       printf("%s<%c%c%c", tag, cie->cc[0], cie->cc[1], cie->cc[2]);
+       nbands = (cie->len - 3) / sizeof(cie->band[0]);
+       for (i = 0; i < nbands; i++) {
+               schan = cie->band[i].schan;
+               nchan = cie->band[i].nchan;
+               if (nchan != 1)
+                       printf(" %u-%u,%u", schan, schan + nchan-1,
+                           cie->band[i].maxtxpwr);
+               else
+                       printf(" %u,%u", schan, cie->band[i].maxtxpwr);
+       }
+       printf(">");
+}
+
+/* unaligned little endian access */
+#define LE_READ_4(p)                                   \
+       ((u_int32_t)                                    \
+        ((((const u_int8_t *)(p))[0]      ) |          \
+         (((const u_int8_t *)(p))[1] <<  8) |          \
+         (((const u_int8_t *)(p))[2] << 16) |          \
+         (((const u_int8_t *)(p))[3] << 24)))
+
+static int
+iswpaoui(const u_int8_t *frm)
+{
+       return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+static int
+iswmeinfo(const u_int8_t *frm)
+{
+       return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+               frm[6] == WME_INFO_OUI_SUBTYPE;
+}
+
+static int
+iswmeparam(const u_int8_t *frm)
+{
+       return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+               frm[6] == WME_PARAM_OUI_SUBTYPE;
+}
+
+static const char *
+iename(int elemid)
+{
+       switch (elemid) {
+       case IEEE80211_ELEMID_FHPARMS:  return " FHPARMS";
+       case IEEE80211_ELEMID_CFPARMS:  return " CFPARMS";
+       case IEEE80211_ELEMID_TIM:      return " TIM";
+       case IEEE80211_ELEMID_IBSSPARMS:return " IBSSPARMS";
+       case IEEE80211_ELEMID_CHALLENGE:return " CHALLENGE";
+       case IEEE80211_ELEMID_PWRCNSTR: return " PWRCNSTR";
+       case IEEE80211_ELEMID_PWRCAP:   return " PWRCAP";
+       case IEEE80211_ELEMID_TPCREQ:   return " TPCREQ";
+       case IEEE80211_ELEMID_TPCREP:   return " TPCREP";
+       case IEEE80211_ELEMID_SUPPCHAN: return " SUPPCHAN";
+       case IEEE80211_ELEMID_CHANSWITCHANN:return " CSA";
+       case IEEE80211_ELEMID_MEASREQ:  return " MEASREQ";
+       case IEEE80211_ELEMID_MEASREP:  return " MEASREP";
+       case IEEE80211_ELEMID_QUIET:    return " QUIET";
+       case IEEE80211_ELEMID_IBSSDFS:  return " IBSSDFS";
+       case IEEE80211_ELEMID_TPC:      return " TPC";
+       case IEEE80211_ELEMID_CCKM:     return " CCKM";
+       }
+       return " ???";
+}
+
+static void
+printies(const u_int8_t *vp, int ielen, int maxcols)
+{
+       while (ielen > 0) {
+               switch (vp[0]) {
+               case IEEE80211_ELEMID_SSID:
+                       if (vflag)
+                               printssid(" SSID", vp, 2+vp[1], maxcols);
+                       break;
+               case IEEE80211_ELEMID_RATES:
+               case IEEE80211_ELEMID_XRATES:
+                       if (vflag)
+                               printrates(vp[0] == IEEE80211_ELEMID_RATES ?
+                                   " RATES" : " XRATES", vp, 2+vp[1], maxcols);
+                       break;
+               case IEEE80211_ELEMID_DSPARMS:
+                       if (vflag)
+                               printf(" DSPARMS<%u>", vp[2]);
+                       break;
+               case IEEE80211_ELEMID_COUNTRY:
+                       if (vflag)
+                               printcountry(" COUNTRY", vp, 2+vp[1], maxcols);
+                       break;
+               case IEEE80211_ELEMID_ERP:
+                       if (vflag)
+                               printf(" ERP<0x%x>", vp[2]);
+                       break;
+               case IEEE80211_ELEMID_VENDOR:
+                       if (iswpaoui(vp))
+                               printwpaie(" WPA", vp, 2+vp[1], maxcols);
+                       else if (iswmeinfo(vp))
+                               printwmeinfo(" WME", vp, 2+vp[1], maxcols);
+                       else if (iswmeparam(vp))
+                               printwmeparam(" WME", vp, 2+vp[1], maxcols);
+                       else if (vflag)
+                               printie(" VEN", vp, 2+vp[1], maxcols);
+                       break;
+               case IEEE80211_ELEMID_RSN:
+                       printrsnie(" RSN", vp, 2+vp[1], maxcols);
+                       break;
+               default:
+                       if (vflag)
+                               printie(iename(vp[0]), vp, 2+vp[1], maxcols);
+                       break;
+               }
+               ielen -= 2+vp[1];
+               vp += 2+vp[1];
+       }
+}
+
+static int
+mapgsm(u_int isrfreq, u_int isrflags)
+{
+       isrfreq *= 10;
+       if (isrflags & IEEE80211_CHAN_QUARTER)
+               isrfreq += 5;
+       else if (isrflags & IEEE80211_CHAN_HALF)
+               isrfreq += 10;
+       else
+               isrfreq += 20;
+       /* NB: there is no 907/20 wide but leave room */
+       return (isrfreq - 906*10) / 5;
+}
+
+static int
+mappsb(u_int isrfreq, u_int isrflags)
+{
+       return 37 + ((isrfreq * 10) + ((isrfreq % 5) == 2 ? 5 : 0) - 49400) / 5;
+}
+
+static status_func_t status;
+static usage_func_t usage;
+static statistics_func_t statistics;
+static cmdloop_branch_t branch[2];
+
+static void
+ieee80211_usage(prop_dictionary_t env)
+{
+       fprintf(stderr,
+           "\t[ nwid network_id ] [ nwkey network_key | -nwkey ]\n"
+           "\t[ list scan ]\n"
+           "\t[ powersave | -powersave ] [ powersavesleep duration ]\n"
+           "\t[ hidessid | -hidessid ] [ apbridge | -apbridge ]\n");
+}
+
+static void
+ieee80211_constructor(void)
+{
+       cmdloop_branch_init(&branch[0], &ieee80211bool.pk_parser);
+       cmdloop_branch_init(&branch[1], &kw80211.pk_parser);
+       register_cmdloop_branch(&branch[0]);
+       register_cmdloop_branch(&branch[1]);
+       status_func_init(&status, ieee80211_status);
+       statistics_func_init(&statistics, ieee80211_statistics);
+       usage_func_init(&usage, ieee80211_usage);
+       register_status(&status);
+       register_statistics(&statistics);
+       register_usage(&usage);
+}
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
new file mode 100644 (file)
index 0000000..5357cb5
--- /dev/null
@@ -0,0 +1,920 @@
+.\"    $NetBSD: ifconfig.8,v 1.109 2014/10/20 14:50:09 roy Exp $
+.\"
+.\" Copyright (c) 1983, 1991, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 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. 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)ifconfig.8 8.4 (Berkeley) 6/1/94
+.\"
+.Dd October 12, 2014
+.Dt IFCONFIG 8
+.Os
+.Sh NAME
+.Nm ifconfig
+.Nd configure network interface parameters
+.Sh SYNOPSIS
+.Nm
+.Op Fl N
+.Ar interface address_family
+.Oo
+.Ar address
+.Op Ar dest_address
+.Oc
+.Op Ar parameters
+.Nm
+.Op Fl hLmNvz
+.Ar interface
+.Op Ar protocol_family
+.Nm
+.Fl a
+.Op Fl bdhLNmsuvz
+.Op Ar protocol_family
+.Nm
+.Fl l
+.Op Fl bdsu
+.Nm
+.Fl s
+.Ar interface
+.Nm
+.Fl w
+.Ar secs
+.Nm
+.Fl C
+.Sh DESCRIPTION
+.Nm
+is used to assign an address
+to a network interface and/or configure
+network interface parameters.
+.Nm
+must be used at boot time to define the network address
+of each interface present on a machine; it may also be used at
+a later time to redefine an interface's address
+or other operating parameters.
+.Pp
+Available operands for
+.Nm :
+.Bl -tag -width Ds
+.It Ar address
+For the
+.Tn DARPA-Internet
+family,
+the address is either a host name present in the host name data
+base,
+.Xr hosts 5 ,
+or a
+.Tn DARPA
+Internet address expressed in the Internet standard
+.Dq dot notation .
+For the Xerox Network Systems(tm) family,
+addresses are
+.Ar net:a.b.c.d.e.f ,
+where
+.Ar net
+is the assigned network number
+.Pq in decimal ,
+and each of the six bytes of the host number,
+.Ar a
+through
+.Ar f ,
+are specified in hexadecimal.
+The host number may be omitted on Ethernet interfaces,
+which use the hardware physical address,
+and on interfaces other than the first.
+For the
+.Tn ISO
+family, addresses are specified as a long hexadecimal string,
+as in the Xerox family.
+However, two consecutive dots imply a zero
+byte, and the dots are optional, if the user wishes to
+.Pq carefully
+count out long strings of digits in network byte order.
+.It Ar address_family
+Specifies the
+.Ar address_family
+which affects interpretation of the remaining parameters.
+Since an interface can receive transmissions in differing protocols
+with different naming schemes, specifying the address family is recommended.
+The address or protocol families currently
+supported are
+.Dq inet ,
+.Dq inet6 ,
+.Dq atalk ,
+.Dq iso ,
+and
+.Dq link .
+.It Ar interface
+The
+.Ar interface
+parameter is a string of the form
+.Dq name unit ,
+for example,
+.Dq en0
+.El
+.Pp
+The following parameters may be set with
+.Nm :
+.Bl -tag -width dest_addressxx
+.It Cm active
+This keyword applies when
+.Nm
+adds or modifies any link-layer address.
+It indicates that
+.Nm
+should
+.Dq activate
+the address.
+Activation makes an address the default source for transmissions
+on the interface.
+You may not delete the active address from an interface.
+You must activate some other address, first.
+.It Cm advbase Ar n
+If the driver is a
+.Xr carp 4
+pseudo-device, set the base advertisement interval to
+.Ar n
+seconds.
+This ia an 8-bit number; the default value is 1 second.
+.It Cm advskew Ar n
+If the driver is a
+.Xr carp 4
+pseudo-device, skew the advertisement interval by
+.Ar n .
+This is an 8-bit number; the default value is 0.
+.Pp
+Taken together the
+.Cm advbase
+indicate how frequently, in seconds, the host will advertise the fact that it
+considers itself the master of the virtual host.
+The formula is
+.Cm advbase
++
+.Pf ( Cm advskew
+/ 256).
+If the master does not advertise within three times this interval, this host
+will begin advertising as master.
+.It Cm alias
+Establish an additional network address for this interface.
+This is sometimes useful when changing network numbers, and
+one wishes to accept packets addressed to the old interface.
+.It Fl alias
+Remove the specified network address alias.
+.It Cm arp
+Enable the use of the Address Resolution Protocol in mapping
+between network level addresses and link level addresses
+.Pq default .
+This is currently implemented for mapping between
+.Tn DARPA
+Internet
+addresses and Ethernet addresses.
+.It Fl arp
+Disable the use of the Address Resolution Protocol.
+.It Cm anycast
+.Pq inet6 only
+Set the IPv6 anycast address bit.
+.It Fl anycast
+.Pq inet6 only
+Clear the IPv6 anycast address bit.
+.It Cm broadcast Ar mask
+.Pq Inet only
+Specify the address to use to represent broadcasts to the
+network.
+The default broadcast address is the address with a host part of all 1's.
+.It Cm carpdev Ar iface
+If the driver is a
+.Xr carp 4
+pseudo-device, attach it to
+.Ar iface .
+If not specified, the kernel will attempt to select an interface with
+a subnet matching that of the carp interface.
+.It Cm debug
+Enable driver dependent debugging code; usually, this turns on
+extra console error logging.
+.It Fl debug
+Disable driver dependent debugging code.
+.It Cm delete
+Remove the network address specified.
+This would be used if you incorrectly specified an alias, or it
+was no longer needed.
+If you have incorrectly set an NS address having the side effect
+of specifying the host portion, removing all NS addresses will
+allow you to respecify the host portion.
+.Cm delete
+does not work for IPv6 addresses.
+Use
+.Fl alias
+with explicit IPv6 address instead.
+.It Ar dest_address
+Specify the address of the correspondent on the other end
+of a point to point link.
+.It Cm down
+Mark an interface ``down''.
+When an interface is
+marked ``down'', the system will not attempt to
+transmit messages through that interface.
+If possible, the interface will be reset to disable reception as well.
+This action does not automatically disable routes using the interface.
+.It Cm ipdst
+This is used to specify an Internet host who is willing to receive
+ip packets encapsulating NS packets bound for a remote network.
+An apparent point to point link is constructed, and
+the address specified will be taken as the NS address and network
+of the destination.
+IP encapsulation of
+.Tn CLNP
+packets is done differently.
+.It Cm media Ar type
+Set the media type of the interface to
+.Ar type .
+Some interfaces support the mutually exclusive use of one of several
+different physical media connectors.
+For example, a 10Mb/s Ethernet
+interface might support the use of either
+.Tn AUI
+or twisted pair connectors.
+Setting the media type to
+.Dq 10base5
+or
+.Dq AUI
+would change the currently active connector to the AUI port.
+Setting it to
+.Dq 10baseT
+or
+.Dq UTP
+would activate twisted pair.
+Refer to the interfaces' driver
+specific man page for a complete list of the available types
+and the
+.Xr ifmedia 4
+manual page for a list of media types.
+See the
+.Fl m
+flag below.
+.It Cm mediaopt Ar opts
+Set the specified media options on the interface.
+.Ar opts
+is a comma delimited list of options to apply to the interface.
+Refer to the interfaces' driver specific man page for a complete
+list of available options.
+Also see the
+.Xr ifmedia 4
+manual page for a list of media options.
+.It Fl mediaopt Ar opts
+Disable the specified media options on the interface.
+.It Cm mode Ar mode
+If the driver supports the media selection system, set the specified
+operating mode on the interface to
+.Ar mode .
+For IEEE 802.11 wireless interfaces that support multiple operating modes
+this directive is used to select between 802.11a
+.Pq Dq 11a ,
+802.11b
+.Pq Dq 11b ,
+and 802.11g
+.Pq Dq 11g
+operating modes.
+.It Cm instance Ar minst
+Set the media instance to
+.Ar minst .
+This is useful for devices which have multiple physical layer interfaces
+.Pq PHYs .
+Setting the instance on such devices may not be strictly required
+by the network interface driver as the driver may take care of this
+automatically; see the driver's manual page for more information.
+.It Cm metric Ar n
+Set the routing metric of the interface to
+.Ar n ,
+default 0.
+The routing metric is used by the routing protocol
+.Pq Xr routed 8 .
+Higher metrics have the effect of making a route
+less favorable; metrics are counted as addition hops
+to the destination network or host.
+.It Cm mtu Ar n
+Set the maximum transmission unit of the interface to
+.Ar n .
+Most interfaces don't support this option.
+.It Cm netmask Ar mask
+.Pq inet, inet6, and ISO
+Specify how much of the address to reserve for subdividing
+networks into sub-networks.
+The mask includes the network part of the local address
+and the subnet part, which is taken from the host field of the address.
+The mask can be specified as a single hexadecimal number
+with a leading 0x, with a dot-notation Internet address,
+or with a pseudo-network name listed in the network table
+.Xr networks 5 .
+The mask contains 1's for the bit positions in the 32-bit address
+which are to be used for the network and subnet parts,
+and 0's for the host part.
+The mask should contain at least the standard network portion,
+and the subnet field should be contiguous with the network
+portion.
+.Pp
+For INET and INET6 addresses, the netmask can also be given with
+slash-notation after the address
+.Pq e.g 192.168.17.3/24 .
+.\" see
+.\" Xr eon 5 .
+.It Cm nsellength Ar n
+.Pf ( Tn ISO
+only)
+This specifies a trailing number of bytes for a received
+.Tn NSAP
+used for local identification, the remaining leading part of which is
+taken to be the
+.Tn NET
+.Pq Network Entity Title .
+The default value is 1, which is conformant to US
+.Tn GOSIP .
+When an ISO address is set in an ifconfig command,
+it is really the
+.Tn NSAP
+which is being specified.
+For example, in
+.Tn US GOSIP ,
+20 hex digits should be
+specified in the
+.Tn ISO NSAP
+to be assigned to the interface.
+There is some evidence that a number different from 1 may be useful
+for
+.Tn AFI
+37 type addresses.
+.It Cm state Ar state
+Explicitly force the
+.Xr carp 4
+pseudo-device to enter this state.
+Valid states are
+.Ar init ,
+.Ar backup ,
+and
+.Ar master .
+.It Cm frag Ar threshold
+.Pq IEEE 802.11 devices only
+Configure the fragmentation threshold for IEEE 802.11-based wireless
+network interfaces.
+.It Cm rts Ar threshold
+.Pq IEEE 802.11 devices only
+Configure the RTS/CTS threshold for IEEE 802.11-based wireless
+network interfaces.
+This controls the number of bytes used for the RTS/CTS handshake boundary.
+The
+.Ar threshold
+can be any value between 0 and 2347.
+The default is 2347, which indicates the RTS/CTS mechanism should not be used.
+.It Cm ssid Ar id
+.Pq IEEE 802.11 devices only
+Configure the Service Set Identifier (a.k.a. the network name)
+for IEEE 802.11-based wireless network interfaces.
+The
+.Ar id
+can either be any text string up to 32 characters in length,
+or a series of up to 64 hexadecimal digits preceded by
+.Dq 0x .
+Setting
+.Ar id
+to the empty string allows the interface to connect to any available
+access point.
+.It Cm nwid Ar id
+Synonym for
+.Dq ssid .
+.It Cm hidessid
+.Pq IEEE 802.11 devices only
+When operating as an access point, do not broadcast the SSID
+in beacon frames or respond to probe request frames unless
+they are directed to the ap (i.e., they include the ap's SSID).
+By default, the SSID is included in beacon frames and
+undirected probe request frames are answered.
+.It Fl hidessid
+.Pq IEEE 802.11 devices only
+When operating as an access point, broadcast the SSID
+in beacon frames and answer and respond to undirected probe
+request frames (default).
+.It Cm nwkey Ar key
+.Pq IEEE 802.11 devices only
+Enable WEP encryption for IEEE 802.11-based wireless network interfaces
+with the
+.Ar key .
+The
+.Ar key
+can either be a string, a series of hexadecimal digits preceded by
+.Dq 0x ,
+or a set of keys in the form
+.Ar n:k1,k2,k3,k4 ,
+where
+.Ar n
+specifies which of keys will be used for all transmitted packets,
+and four keys,
+.Ar k1
+through
+.Ar k4 ,
+are configured as WEP keys.
+Note that the order must be match within same network if multiple keys
+are used.
+For IEEE 802.11 wireless network, the length of each key is restricted to
+40 bits, i.e. 5-character string or 10 hexadecimal digits,
+while the WaveLAN/IEEE Gold cards accept the 104 bits
+.Pq 13 characters
+key.
+.It Cm nwkey Cm persist
+.Pq IEEE 802.11 devices only
+Enable WEP encryption for IEEE 802.11-based wireless network interfaces
+with the persistent key written in the network card.
+.It Cm nwkey Cm persist: Ns Ar key
+.Pq IEEE 802.11 devices only
+Write the
+.Ar key
+to the persistent memory of the network card, and
+enable WEP encryption for IEEE 802.11-based wireless network interfaces
+with the
+.Ar key .
+.It Fl nwkey
+.Pq IEEE 802.11 devices only
+Disable WEP encryption for IEEE 802.11-based wireless network interfaces.
+.It Cm apbridge
+.Pq IEEE 802.11 devices only
+When operating as an access point, pass packets between
+wireless clients directly (default).
+.It Fl apbridge
+.Pq IEEE 802.11 devices only
+When operating as an access point, pass packets through
+the system so that they can be forwared using some other mechanism.
+Disabling the internal bridging is useful when traffic
+is to be processed with packet filtering.
+.It Cm pass Ar passphrase
+If the driver is a
+.Xr carp 4
+pseudo-device, set the authentication key to
+.Ar passphrase .
+There is no passphrase by default
+.It Cm powersave
+.Pq IEEE 802.11 devices only
+Enable 802.11 power saving mode.
+.It Fl powersave
+.Pq IEEE 802.11 devices only
+Disable 802.11 power saving mode.
+.It Cm powersavesleep Ar duration
+.Pq IEEE 802.11 devices only
+Set the receiver sleep duration in milliseconds for 802.11 power saving mode.
+.It Cm bssid Ar bssid
+.Pq IEEE 802.11 devices only
+Set the desired BSSID for IEEE 802.11-based wireless network interfaces.
+.It Fl bssid
+.Pq IEEE 802.11 devices only
+Unset the desired BSSID for IEEE 802.11-based wireless network interfaces.
+The interface will automatically select a BSSID in this mode, which is
+the default.
+.It Cm chan Ar chan
+.Pq IEEE 802.11 devices only
+Select the channel
+.Pq radio frequency
+to be used for IEEE 802.11-based wireless network interfaces.
+.It Fl chan
+.Pq IEEE 802.11 devices only
+Unset the desired channel to be used
+for IEEE 802.11-based wireless network interfaces.
+It doesn't affect the channel to be created for IBSS or hostap mode.
+.It Cm list scan
+.Pq IEEE 802.11 devices only
+Display the access points and/or ad-hoc neighbors
+located in the vicinity.
+The
+.Fl v
+flag may be used to display long SSIDs.
+.Fl v
+also causes received information elements to be displayed symbolically.
+The interface must be up before any scanning operation.
+Only the super-user can use this command.
+.It Cm tunnel Ar src_addr Ns Oo Ar ,src_port Oc Ar dest_addr Ns Oo Ar ,dest_port
+.Oc
+.Pq IP tunnel devices only
+Configure the physical source and destination address for IP tunnel
+interfaces, including
+.Xr gif 4 .
+The arguments
+.Ar src_addr
+and
+.Ar dest_addr
+are interpreted as the outer source/destination for the encapsulating
+IPv4/IPv6 header.
+.Pp
+On a
+.Xr gre 4
+interface in UDP mode, the arguments
+.Ar src_port
+and
+.Ar dest_port
+are interpreted as the outer source/destination port for the encapsulating
+UDP header.
+.It Cm deletetunnel
+Unconfigure the physical source and destination address for IP tunnel
+interfaces previously configured with
+.Cm tunnel .
+.It Cm create
+Create the specified network pseudo-device.
+.It Cm destroy
+Destroy the specified network pseudo-device.
+.It Cm pltime Ar n
+.Pq inet6 only
+Set preferred lifetime for the address.
+.It Cm prefixlen Ar n
+.Pq inet and inet6 only
+Effect is similar to
+.Cm netmask .
+but you can specify by prefix length by digits.
+.It Cm deprecated
+.Pq inet6 only
+Set the IPv6 deprecated address bit.
+.It Fl deprecated
+.Pq inet6 only
+Clear the IPv6 deprecated address bit.
+.It Cm eui64
+.Pq inet6 only
+Fill interface index
+.Pq lowermost 64bit of an IPv6 address
+automatically.
+.It Cm link[0-2]
+Enable special processing of the link level of the interface.
+These three options are interface specific in actual effect, however,
+they are in general used to select special modes of operation.
+An example
+of this is to enable SLIP compression, or to select the connector type
+for some Ethernet cards.
+Refer to the man page for the specific driver
+for more information.
+.It Fl link[0-2]
+Disable special processing at the link level with the specified interface.
+.It Cm linkstr
+Set a link-level string parameter for the interface.
+This functionality varies from interface to interface.
+Refer to the man page for the specific driver
+for more information.
+.It Fl linkstr
+Remove an interface link-level string parameter.
+.It Cm up
+Mark an interface ``up''.
+This may be used to enable an interface after an ``ifconfig down.''
+It happens automatically when setting the first address on an interface.
+If the interface was reset when previously marked down,
+the hardware will be re-initialized.
+.It Cm vhid Ar n
+If the driver is a
+.Xr carp 4
+pseudo-device, set the virtual host ID to
+.Ar n .
+Acceptable values are 1 to 255.
+.It Cm vlan Ar vid
+If the interface is a
+.Xr vlan 4
+pseudo-interface, set the VLAN identifier to
+.Ar vid .
+These are the first 12 bits (0-4095) from a 16-bit integer used
+to create an 802.1Q VLAN header for packets sent from the
+.Xr vlan 4
+interface.
+Note that
+.Cm vlan
+and
+.Cm vlanif
+must be set at the same time.
+.It Cm vlanif Ar iface
+If the interface is a
+.Xr vlan 4
+pseudo-interface, associate the physical interface
+.Ar iface
+with it.
+Packets transmitted through the
+.Xr vlan 4
+interface will be diverted to the specified physical interface
+.Ar iface
+with 802.1Q VLAN encapsulation.
+Packets with 802.1Q encapsulation received
+by the physical interface with the correct VLAN tag will be diverted to the
+associated
+.Xr vlan 4
+pseudo-interface.
+The VLAN interface is assigned a copy of the physical
+interface's flags and
+.Tn Ethernet
+address.
+If the
+.Xr vlan 4
+interface already has a physical interface associated with it, this command
+will fail.
+To change the association to another physical interface, the
+existing association must be cleared first.
+Note that
+.Cm vlanif
+and
+.Cm vlan
+must be set at the same time.
+.It Cm -vlanif Ar iface
+Dissociate
+.Ar iface
+from the
+.Xr vlan 4
+interface.
+.It Cm agrport Ar iface
+Add
+.Ar iface
+to the
+.Xr agr 4
+interface.
+.It Cm -agrport Ar iface
+Remove
+.Ar iface
+from the
+.Xr agr 4
+interface.
+.It Cm vltime Ar n
+.Pq inet6 only
+Set valid lifetime for the address.
+.It Cm ip4csum
+Shorthand of
+.Dq ip4csum-tx ip4csum-rx
+.It Cm -ip4csum
+Shorthand of
+.Dq -ip4csum-tx -ip4csum-rx
+.It Cm tcp4csum
+Shorthand of
+.Dq tcp4csum-tx tcp4csum-rx
+.It Cm -tcp4csum
+Shorthand of
+.Dq -tcp4csum-tx -tcp4csum-rx
+.It Cm udp4csum
+Shorthand of
+.Dq udp4csum-tx udp4csum-rx
+.It Cm -udp4csum
+Shorthand of
+.Dq -udp4csum-tx -udp4csum-rx
+.It Cm tcp6csum
+Shorthand of
+.Dq tcp6csum-tx tcp6csum-rx
+.It Cm -tcp6csum
+Shorthand of
+.Dq -tcp6csum-tx -tcp6csum-rx
+.It Cm udp6csum
+Shorthand of
+.Dq udp6csum-tx udp6csum-rx
+.It Cm -udp6csum
+Shorthand of
+.Dq -udp6csum-tx -udp6csum-rx
+.It Cm ip4csum-tx
+Enable hardware-assisted IPv4 header checksums for the out-bound direction.
+.It Cm -ip4csum-tx
+Disable hardware-assisted IPv4 header checksums for the out-bound direction.
+.It Cm ip4csum-rx
+Enable hardware-assisted IPv4 header checksums for the in-bound direction.
+.It Cm -ip4csum-rx
+Disable hardware-assisted IPv4 header checksums for the in-bound direction.
+.It Cm tcp4csum-tx
+Enable hardware-assisted TCP/IPv4 checksums for the out-bound direction.
+.It Cm -tcp4csum-tx
+Disable hardware-assisted TCP/IPv4 checksums for the out-bound direction.
+.It Cm tcp4csum-rx
+Enable hardware-assisted TCP/IPv4 checksums for the in-bound direction.
+.It Cm -tcp4csum-rx
+Disable hardware-assisted TCP/IPv4 checksums for the in-bound direction.
+.It Cm udp4csum-tx
+Enable hardware-assisted UDP/IPv4 checksums for the out-bound direction.
+.It Cm -udp4csum-tx
+Disable hardware-assisted UDP/IPv4 checksums for the out-bound direction.
+.It Cm udp4csum-rx
+Enable hardware-assisted UDP/IPv4 checksums for the in-bound direction.
+.It Cm -udp4csum-rx
+Disable hardware-assisted UDP/IPv4 checksums for the in-bound direction.
+.It Cm tcp6csum-tx
+Enable hardware-assisted TCP/IPv6 checksums for the out-bound direction.
+.It Cm -tcp6csum-tx
+Disable hardware-assisted TCP/IPv6 checksums for the out-bound direction.
+.It Cm tcp6csum-rx
+Enable hardware-assisted TCP/IPv6 checksums for the in-bound direction.
+.It Cm -tcp6csum-rx
+Disable hardware-assisted TCP/IPv6 checksums for the in-bound direction.
+.It Cm udp6csum-tx
+Enable hardware-assisted UDP/IPv6 checksums for the out-bound direction.
+.It Cm -udp6csum-tx
+Disable hardware-assisted UDP/IPv6 checksums for the out-bound direction.
+.It Cm udp6csum-rx
+Enable hardware-assisted UDP/IPv6 checksums for the in-bound direction.
+.It Cm -udp6csum-rx
+Disable hardware-assisted UDP/IPv6 checksums for the in-bound direction.
+.It Cm tso4
+Enable hardware-assisted TCP/IPv4 segmentation on interfaces that
+support it.
+.It Cm -tso4
+Disable hardware-assisted TCP/IPv4 segmentation on interfaces that
+support it.
+.It Cm tso6
+Enable hardware-assisted TCP/IPv6 segmentation on interfaces that
+support it.
+.It Cm -tso6
+Disable hardware-assisted TCP/IPv6 segmentation on interfaces that
+support it.
+.It Cm maxupd Ar n
+If the driver is a
+.Xr pfsync 4
+pseudo-device, indicate the maximum number
+of updates for a single state which can be collapsed into one.
+This is an 8-bit number; the default value is 128.
+.It Cm syncdev Ar iface
+If the driver is a
+.Xr pfsync 4
+pseudo-device, use the specified interface
+to send and receive pfsync state synchronisation messages.
+.It Fl syncdev
+If the driver is a
+.Xr pfsync 4
+pseudo-device, stop sending pfsync state
+synchronisation messages over the network.
+.It Cm syncpeer Ar peer_address
+If the driver is a
+.Xr pfsync 4
+pseudo-device, make the pfsync link point-to-point rather than using
+multicast to broadcast the state synchronisation messages.
+The peer_address is the IP address of the other host taking part in
+the pfsync cluster.
+With this option,
+.Xr pfsync 4
+traffic can be protected using
+.Xr ipsec 4 .
+.It Fl syncpeer
+If the driver is a
+.Xr pfsync 4
+pseudo-device, broadcast the packets using multicast.
+.El
+.Pp
+.Nm
+displays the current configuration for a network interface
+when no optional parameters are supplied.
+If a protocol family is specified,
+.Nm
+will report only the details specific to that protocol
+family.
+.Pp
+If the
+.Fl s
+flag is passed before an interface name,
+.Nm
+will attempt to query the interface for its media status.
+If the
+interface supports reporting media status, and it reports that it does
+not appear to be connected to a network,
+.Nm
+will exit with status of 1
+.Pq false ;
+otherwise, it will exit with a
+zero
+.Pq true
+exit status.
+Not all interface drivers support media
+status reporting.
+.Pp
+If the
+.Fl m
+flag is passed before an interface name,
+.Nm
+will display all of the supported media for the specified interface.
+If the
+.Fl L
+flag is supplied, address lifetime is displayed for IPv6 addresses,
+as time offset string.
+.Pp
+Optionally, the
+.Fl a
+flag may be used instead of an interface name.
+This flag instructs
+.Nm
+to display information about all interfaces in the system.
+This is also the default behaviour when no arguments are given to
+.Nm
+on the command line.
+When
+.Fl a
+is used, the output can be modified by adding more flags:
+.Fl d
+limits this to interfaces that are down,
+.Fl u
+limits this to interfaces that are up,
+.Fl b
+limits this to broadcast interfaces, and
+.Fl s
+omits interfaces which appear not to be connected to a network.
+.Pp
+The
+.Fl l
+flag may be used to list all available interfaces on the system, with
+no other additional information.
+Use of this flag is mutually exclusive
+with all other flags and commands, except for
+.Fl d
+.Pq only list interfaces that are down ,
+.Fl u
+.Pq only list interfaces that are up ,
+.Fl s
+.Pq only list interfaces that may be connected ,
+.Fl b
+.Pq only list broadcast interfaces .
+.Pp
+The
+.Fl C
+flag may be used to list all of the interface cloners available on
+the system, with no additional information.
+Use of this flag is
+mutually exclusive with all other flags and commands.
+.Pp
+The
+.Fl v
+flag prints statistics on packets sent and received on the given
+interface.
+If
+.Fl h
+is used in conjunction with
+.Fl v ,
+the byte statistics will be printed in "human-readable" format.
+The
+.Fl z
+flag is identical to the
+.Fl v
+flag except that it zeros the interface input and output statistics
+after printing them.
+.Pp
+The
+.Fl w
+flag may be used to wait
+.Ar seconds
+seconds for the
+.Cm tentative
+flag to be removed from all addresses.
+0 seconds means to wait indefinitely until all addresses no longer have the
+.Cm tentative
+flag.
+.Pp
+The
+.Fl N
+flag is just the opposite of the
+.Fl n
+flag in
+.Xr netstat 1
+or in
+.Xr route 8 :
+it tells
+.Nm
+to try to resolve numbers to hostnames or to service names.
+The default
+.Nm
+behavior is to print numbers instead of names.
+.Pp
+Only the super-user may modify the configuration of a network interface.
+.Sh EXAMPLES
+Add a link-layer (MAC) address to an Ethernet:
+.Pp
+.Ic ifconfig sip0 link 00:11:22:33:44:55
+.Pp
+Add and activate a link-layer (MAC) address:
+.Pp
+.Ic ifconfig sip0 link 00:11:22:33:44:55 active
+.Sh DIAGNOSTICS
+Messages indicating the specified interface does not exist, the
+requested address is unknown, or the user is not privileged and
+tried to alter an interface's configuration.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr agr 4 ,
+.Xr carp 4 ,
+.Xr ifmedia 4 ,
+.Xr netintro 4 ,
+.Xr pfsync 4 ,
+.Xr vlan 4 ,
+.Xr ifconfig.if 5 ,
+.\" .Xr eon 5 ,
+.Xr rc 8 ,
+.Xr routed 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c
new file mode 100644 (file)
index 0000000..1af0882
--- /dev/null
@@ -0,0 +1,1476 @@
+/*     $NetBSD: ifconfig.c,v 1.235 2015/07/29 07:42:27 ozaki-r Exp $   */
+
+/*-
+ * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1983, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1983, 1993\
+ The Regents of the University of California.  All rights reserved.");
+__RCSID("$NetBSD: ifconfig.c,v 1.235 2015/07/29 07:42:27 ozaki-r Exp $");
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_ether.h>
+#include <netinet/in.h>                /* XXX */
+#include <netinet/in_var.h>    /* XXX */
+
+#include <netdb.h>
+
+#include <sys/protosw.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+#include <util.h>
+
+#include "extern.h"
+
+#include "media.h"
+#include "parse.h"
+#include "env.h"
+#include "prog_ops.h"
+
+#define WAIT_DAD       10000000 /* nanoseconds between each poll, 10ms */
+
+static bool bflag, dflag, hflag, sflag, uflag, wflag;
+bool lflag, Nflag, vflag, zflag;
+static long wflag_secs;
+
+static char gflags[10 + 26 * 2 + 1] = "AabCdhlNsuvw:z";
+bool gflagset[10 + 26 * 2];
+
+static int carrier(prop_dictionary_t);
+static int clone_command(prop_dictionary_t, prop_dictionary_t);
+static void do_setifpreference(prop_dictionary_t);
+static int flag_index(int);
+static void init_afs(void);
+static int list_cloners(prop_dictionary_t, prop_dictionary_t);
+static int media_status_exec(prop_dictionary_t, prop_dictionary_t);
+static int wait_dad_exec(prop_dictionary_t, prop_dictionary_t);
+static int no_cmds_exec(prop_dictionary_t, prop_dictionary_t);
+static int notrailers(prop_dictionary_t, prop_dictionary_t);
+static void printall(const char *, prop_dictionary_t);
+static int setifaddr(prop_dictionary_t, prop_dictionary_t);
+static int setifbroadaddr(prop_dictionary_t, prop_dictionary_t);
+static int setifcaps(prop_dictionary_t, prop_dictionary_t);
+static int setifdstormask(prop_dictionary_t, prop_dictionary_t);
+static int setifflags(prop_dictionary_t, prop_dictionary_t);
+static int setifmetric(prop_dictionary_t, prop_dictionary_t);
+static int setifmtu(prop_dictionary_t, prop_dictionary_t);
+static int setifnetmask(prop_dictionary_t, prop_dictionary_t);
+static int setifprefixlen(prop_dictionary_t, prop_dictionary_t);
+static int setlinkstr(prop_dictionary_t, prop_dictionary_t);
+static int unsetlinkstr(prop_dictionary_t, prop_dictionary_t);
+static void status(const struct sockaddr *, prop_dictionary_t,
+    prop_dictionary_t);
+__dead static void usage(void);
+
+static const struct kwinst ifflagskw[] = {
+         IFKW("arp", -IFF_NOARP)
+       , IFKW("debug", IFF_DEBUG)
+       , IFKW("link0", IFF_LINK0)
+       , IFKW("link1", IFF_LINK1)
+       , IFKW("link2", IFF_LINK2)
+       , {.k_word = "down", .k_type = KW_T_INT, .k_int = -IFF_UP}
+       , {.k_word = "up", .k_type = KW_T_INT, .k_int = IFF_UP}
+};
+
+static const struct kwinst ifcapskw[] = {
+         IFKW("ip4csum-tx",    IFCAP_CSUM_IPv4_Tx)
+       , IFKW("ip4csum-rx",    IFCAP_CSUM_IPv4_Rx)
+       , IFKW("tcp4csum-tx",   IFCAP_CSUM_TCPv4_Tx)
+       , IFKW("tcp4csum-rx",   IFCAP_CSUM_TCPv4_Rx)
+       , IFKW("udp4csum-tx",   IFCAP_CSUM_UDPv4_Tx)
+       , IFKW("udp4csum-rx",   IFCAP_CSUM_UDPv4_Rx)
+       , IFKW("tcp6csum-tx",   IFCAP_CSUM_TCPv6_Tx)
+       , IFKW("tcp6csum-rx",   IFCAP_CSUM_TCPv6_Rx)
+       , IFKW("udp6csum-tx",   IFCAP_CSUM_UDPv6_Tx)
+       , IFKW("udp6csum-rx",   IFCAP_CSUM_UDPv6_Rx)
+       , IFKW("ip4csum",       IFCAP_CSUM_IPv4_Tx|IFCAP_CSUM_IPv4_Rx)
+       , IFKW("tcp4csum",      IFCAP_CSUM_TCPv4_Tx|IFCAP_CSUM_TCPv4_Rx)
+       , IFKW("udp4csum",      IFCAP_CSUM_UDPv4_Tx|IFCAP_CSUM_UDPv4_Rx)
+       , IFKW("tcp6csum",      IFCAP_CSUM_TCPv6_Tx|IFCAP_CSUM_TCPv6_Rx)
+       , IFKW("udp6csum",      IFCAP_CSUM_UDPv6_Tx|IFCAP_CSUM_UDPv6_Rx)
+       , IFKW("tso4",          IFCAP_TSOv4)
+       , IFKW("tso6",          IFCAP_TSOv6)
+};
+
+extern struct pbranch command_root;
+extern struct pbranch opt_command;
+extern struct pbranch opt_family, opt_silent_family;
+extern struct pkw cloning, silent_family, family, ifcaps, ifflags, misc;
+extern struct pstr parse_linkstr;
+
+struct pinteger parse_metric = PINTEGER_INITIALIZER(&parse_metric, "metric", 10,
+    setifmetric, "metric", &command_root.pb_parser);
+
+struct pinteger parse_mtu = PINTEGER_INITIALIZER(&parse_mtu, "mtu", 10,
+    setifmtu, "mtu", &command_root.pb_parser);
+
+struct pinteger parse_prefixlen = PINTEGER_INITIALIZER(&parse_prefixlen,
+    "prefixlen", 10, setifprefixlen, "prefixlen", &command_root.pb_parser);
+
+struct pinteger parse_preference = PINTEGER_INITIALIZER1(&parse_preference,
+    "preference", INT16_MIN, INT16_MAX, 10, NULL, "preference",
+    &command_root.pb_parser);
+
+struct paddr parse_netmask = PADDR_INITIALIZER(&parse_netmask, "netmask",
+    setifnetmask, "dstormask", NULL, NULL, NULL, &command_root.pb_parser);
+
+struct paddr parse_broadcast = PADDR_INITIALIZER(&parse_broadcast,
+    "broadcast address",
+    setifbroadaddr, "broadcast", NULL, NULL, NULL, &command_root.pb_parser);
+
+static const struct kwinst misckw[] = {
+         {.k_word = "alias", .k_key = "alias", .k_deact = "alias",
+          .k_type = KW_T_BOOL, .k_neg = true,
+          .k_bool = true, .k_negbool = false,
+          .k_nextparser = &command_root.pb_parser}
+       , {.k_word = "broadcast", .k_nextparser = &parse_broadcast.pa_parser}
+       , {.k_word = "delete", .k_key = "alias", .k_deact = "alias",
+          .k_type = KW_T_BOOL, .k_bool = false,
+          .k_nextparser = &command_root.pb_parser}
+       , {.k_word = "metric", .k_nextparser = &parse_metric.pi_parser}
+       , {.k_word = "mtu", .k_nextparser = &parse_mtu.pi_parser}
+       , {.k_word = "netmask", .k_nextparser = &parse_netmask.pa_parser}
+       , {.k_word = "preference", .k_act = "address",
+          .k_nextparser = &parse_preference.pi_parser}
+       , {.k_word = "prefixlen", .k_nextparser = &parse_prefixlen.pi_parser}
+       , {.k_word = "trailers", .k_neg = true,
+          .k_exec = notrailers, .k_nextparser = &command_root.pb_parser}
+       , {.k_word = "linkstr", .k_nextparser = &parse_linkstr.ps_parser }
+       , {.k_word = "-linkstr", .k_exec = unsetlinkstr,
+          .k_nextparser = &command_root.pb_parser }
+};
+
+/* key: clonecmd */
+static const struct kwinst clonekw[] = {
+       {.k_word = "create", .k_type = KW_T_INT, .k_int = SIOCIFCREATE,
+        .k_nextparser = &opt_silent_family.pb_parser},
+       {.k_word = "destroy", .k_type = KW_T_INT, .k_int = SIOCIFDESTROY}
+};
+
+static struct kwinst familykw[24];
+
+struct pterm cloneterm = PTERM_INITIALIZER(&cloneterm, "list cloners",
+    list_cloners, "none");
+
+struct pterm wait_dad = PTERM_INITIALIZER(&wait_dad, "wait DAD", wait_dad_exec,
+    "none");
+
+struct pterm no_cmds = PTERM_INITIALIZER(&no_cmds, "no commands", no_cmds_exec,
+    "none");
+
+struct pkw family_only =
+    PKW_INITIALIZER(&family_only, "family-only", NULL, "af", familykw,
+       __arraycount(familykw), &no_cmds.pt_parser);
+
+struct paddr address = PADDR_INITIALIZER(&address,
+    "local address (address 1)",
+    setifaddr, "address", "netmask", NULL, "address", &command_root.pb_parser);
+
+struct paddr dstormask = PADDR_INITIALIZER(&dstormask,
+    "destination/netmask (address 2)",
+    setifdstormask, "dstormask", NULL, "address", "dstormask",
+    &command_root.pb_parser);
+
+struct paddr broadcast = PADDR_INITIALIZER(&broadcast,
+    "broadcast address (address 3)",
+    setifbroadaddr, "broadcast", NULL, "dstormask", "broadcast",
+    &command_root.pb_parser);
+
+struct pstr parse_linkstr = PSTR_INITIALIZER(&parse_linkstr, "linkstr",
+    setlinkstr, "linkstr", &command_root.pb_parser);
+
+static SIMPLEQ_HEAD(, afswtch) aflist = SIMPLEQ_HEAD_INITIALIZER(aflist);
+
+static SIMPLEQ_HEAD(, usage_func) usage_funcs =
+    SIMPLEQ_HEAD_INITIALIZER(usage_funcs);
+static SIMPLEQ_HEAD(, status_func) status_funcs =
+    SIMPLEQ_HEAD_INITIALIZER(status_funcs);
+static SIMPLEQ_HEAD(, statistics_func) statistics_funcs =
+    SIMPLEQ_HEAD_INITIALIZER(statistics_funcs);
+static SIMPLEQ_HEAD(, cmdloop_branch) cmdloop_branches =
+    SIMPLEQ_HEAD_INITIALIZER(cmdloop_branches);
+
+struct branch opt_clone_brs[] = {
+         {.b_nextparser = &cloning.pk_parser}
+       , {.b_nextparser = &opt_family.pb_parser}
+}, opt_silent_family_brs[] = {
+         {.b_nextparser = &silent_family.pk_parser}
+       , {.b_nextparser = &command_root.pb_parser}
+}, opt_family_brs[] = {
+         {.b_nextparser = &family.pk_parser}
+       , {.b_nextparser = &opt_command.pb_parser}
+}, command_root_brs[] = {
+         {.b_nextparser = &ifflags.pk_parser}
+       , {.b_nextparser = &ifcaps.pk_parser}
+       , {.b_nextparser = &kwmedia.pk_parser}
+       , {.b_nextparser = &misc.pk_parser}
+       , {.b_nextparser = &address.pa_parser}
+       , {.b_nextparser = &dstormask.pa_parser}
+       , {.b_nextparser = &broadcast.pa_parser}
+       , {.b_nextparser = NULL}
+}, opt_command_brs[] = {
+         {.b_nextparser = &no_cmds.pt_parser}
+       , {.b_nextparser = &command_root.pb_parser}
+};
+
+struct branch opt_family_only_brs[] = {
+         {.b_nextparser = &no_cmds.pt_parser}
+       , {.b_nextparser = &family_only.pk_parser}
+};
+struct pbranch opt_family_only = PBRANCH_INITIALIZER(&opt_family_only,
+    "opt-family-only", opt_family_only_brs,
+    __arraycount(opt_family_only_brs), true);
+struct pbranch opt_command = PBRANCH_INITIALIZER(&opt_command,
+    "optional command",
+    opt_command_brs, __arraycount(opt_command_brs), true);
+
+struct pbranch command_root = PBRANCH_INITIALIZER(&command_root,
+    "command-root", command_root_brs, __arraycount(command_root_brs), true);
+
+struct piface iface_opt_family_only =
+    PIFACE_INITIALIZER(&iface_opt_family_only, "iface-opt-family-only",
+    NULL, "if", &opt_family_only.pb_parser);
+
+struct pkw family = PKW_INITIALIZER(&family, "family", NULL, "af",
+    familykw, __arraycount(familykw), &opt_command.pb_parser);
+
+struct pkw silent_family = PKW_INITIALIZER(&silent_family, "silent family",
+    NULL, "af", familykw, __arraycount(familykw), &command_root.pb_parser);
+
+struct pkw *family_users[] = {&family_only, &family, &silent_family};
+
+struct pkw ifcaps = PKW_INITIALIZER(&ifcaps, "ifcaps", setifcaps,
+    "ifcap", ifcapskw, __arraycount(ifcapskw), &command_root.pb_parser);
+
+struct pkw ifflags = PKW_INITIALIZER(&ifflags, "ifflags", setifflags,
+    "ifflag", ifflagskw, __arraycount(ifflagskw), &command_root.pb_parser);
+
+struct pkw cloning = PKW_INITIALIZER(&cloning, "cloning", clone_command,
+    "clonecmd", clonekw, __arraycount(clonekw), NULL);
+
+struct pkw misc = PKW_INITIALIZER(&misc, "misc", NULL, NULL,
+    misckw, __arraycount(misckw), NULL);
+
+struct pbranch opt_clone = PBRANCH_INITIALIZER(&opt_clone,
+    "opt-clone", opt_clone_brs, __arraycount(opt_clone_brs), true);
+
+struct pbranch opt_silent_family = PBRANCH_INITIALIZER(&opt_silent_family,
+    "optional silent family", opt_silent_family_brs,
+    __arraycount(opt_silent_family_brs), true);
+
+struct pbranch opt_family = PBRANCH_INITIALIZER(&opt_family,
+    "opt-family", opt_family_brs, __arraycount(opt_family_brs), true);
+
+struct piface iface_start = PIFACE_INITIALIZER(&iface_start,
+    "iface-opt-family", NULL, "if", &opt_clone.pb_parser);
+
+struct piface iface_only = PIFACE_INITIALIZER(&iface_only, "iface",
+    media_status_exec, "if", NULL);
+
+static bool
+flag_is_registered(const char *flags, int flag)
+{
+       return flags != NULL && strchr(flags, flag) != NULL;
+}
+
+static int
+check_flag(const char *flags, int flag)
+{
+       if (flag_is_registered(flags, flag)) {
+               errno = EEXIST;
+               return -1;
+       }
+
+       if (flag >= '0' && flag <= '9')
+               return 0;
+       if (flag >= 'a' && flag <= 'z')
+               return 0;
+       if (flag >= 'A' && flag <= 'Z')
+               return 0;
+
+       errno = EINVAL;
+       return -1;
+}
+
+void
+cmdloop_branch_init(cmdloop_branch_t *b, struct parser *p)
+{
+       b->b_parser = p;
+}
+
+void
+statistics_func_init(statistics_func_t *f, statistics_cb_t func)
+{
+       f->f_func = func;
+}
+
+void
+status_func_init(status_func_t *f, status_cb_t func)
+{
+       f->f_func = func;
+}
+
+void
+usage_func_init(usage_func_t *f, usage_cb_t func)
+{
+       f->f_func = func;
+}
+
+int
+register_cmdloop_branch(cmdloop_branch_t *b)
+{
+       SIMPLEQ_INSERT_TAIL(&cmdloop_branches, b, b_next);
+       return 0;
+}
+
+int
+register_statistics(statistics_func_t *f)
+{
+       SIMPLEQ_INSERT_TAIL(&statistics_funcs, f, f_next);
+       return 0;
+}
+
+int
+register_status(status_func_t *f)
+{
+       SIMPLEQ_INSERT_TAIL(&status_funcs, f, f_next);
+       return 0;
+}
+
+int
+register_usage(usage_func_t *f)
+{
+       SIMPLEQ_INSERT_TAIL(&usage_funcs, f, f_next);
+       return 0;
+}
+
+int
+register_family(struct afswtch *af)
+{
+       SIMPLEQ_INSERT_TAIL(&aflist, af, af_next);
+       return 0;
+}
+
+int
+register_flag(int flag)
+{
+       if (check_flag(gflags, flag) == -1)
+               return -1;
+
+       if (strlen(gflags) + 1 >= sizeof(gflags)) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       gflags[strlen(gflags)] = flag;
+
+       return 0;
+}
+
+static int
+flag_index(int flag)
+{
+       if (flag >= '0' && flag <= '9')
+               return flag - '0';
+       if (flag >= 'a' && flag <= 'z')
+               return 10 + flag - 'a';
+       if (flag >= 'A' && flag <= 'Z')
+               return 10 + 26 + flag - 'a';
+
+       errno = EINVAL;
+       return -1;
+}
+
+static bool
+set_flag(int flag)
+{
+       int idx;
+
+       if ((idx = flag_index(flag)) == -1)
+               return false;
+
+       return gflagset[idx] = true;
+}
+
+bool
+get_flag(int flag)
+{
+       int idx;
+
+       if ((idx = flag_index(flag)) == -1)
+               return false;
+
+       return gflagset[idx];
+}
+
+static struct parser *
+init_parser(void)
+{
+       cmdloop_branch_t *b;
+
+       if (parser_init(&iface_opt_family_only.pif_parser) == -1)
+               err(EXIT_FAILURE, "parser_init(iface_opt_family_only)");
+       if (parser_init(&iface_only.pif_parser) == -1)
+               err(EXIT_FAILURE, "parser_init(iface_only)");
+       if (parser_init(&iface_start.pif_parser) == -1)
+               err(EXIT_FAILURE, "parser_init(iface_start)");
+
+       SIMPLEQ_FOREACH(b, &cmdloop_branches, b_next)
+               pbranch_addbranch(&command_root, b->b_parser);
+
+       return &iface_start.pif_parser;
+}
+
+static int
+no_cmds_exec(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       const char *ifname;
+       unsigned short ignore;
+
+       /* ifname == NULL is ok.  It indicates 'ifconfig -a'. */
+       if ((ifname = getifname(env)) == NULL)
+               ;
+       else if (getifflags(env, oenv, &ignore) == -1)
+               err(EXIT_FAILURE, "SIOCGIFFLAGS %s", ifname);
+
+       printall(ifname, env);
+       exit(EXIT_SUCCESS);
+}
+
+static int
+wait_dad_exec(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       bool waiting;
+       struct ifaddrs *ifaddrs, *ifa;
+       const struct timespec ts = { .tv_sec = 0, .tv_nsec = WAIT_DAD };
+       const struct timespec add = { .tv_sec = wflag_secs, .tv_nsec = 0};
+       struct timespec now, end = { .tv_sec = wflag_secs, .tv_nsec = 0};
+       const struct afswtch *afp;
+
+       if (wflag_secs) {
+               if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
+                       err(EXIT_FAILURE, "clock_gettime");
+               timespecadd(&now, &add, &end);
+       }
+
+       if (getifaddrs(&ifaddrs) == -1)
+               err(EXIT_FAILURE, "getifaddrs");
+
+       for (;;) {
+               waiting = false;
+               for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
+                       if (ifa->ifa_addr == NULL)
+                               continue;
+                       afp = lookup_af_bynum(ifa->ifa_addr->sa_family);
+                       if (afp && afp->af_addr_tentative &&
+                           afp->af_addr_tentative(ifa))
+                       {
+                               waiting = true;
+                               break;
+                       }
+               }
+               if (!waiting)
+                       break;
+               nanosleep(&ts, NULL);
+               if (wflag_secs) {
+                       if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
+                               err(EXIT_FAILURE, "clock_gettime");
+                       if (timespeccmp(&now, &end, >))
+                               errx(EXIT_FAILURE, "timed out");
+               }
+       }
+
+       freeifaddrs(ifaddrs);
+       exit(EXIT_SUCCESS);
+}
+
+static int
+media_status_exec(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       const char *ifname;
+       unsigned short ignore;
+
+       /* ifname == NULL is ok.  It indicates 'ifconfig -a'. */
+       if ((ifname = getifname(env)) == NULL)
+               ;
+       else if (getifflags(env, oenv, &ignore) == -1)
+               err(EXIT_FAILURE, "SIOCGIFFLAGS %s", ifname);
+
+       exit(carrier(env));
+}
+
+static void
+do_setifcaps(prop_dictionary_t env)
+{
+       struct ifcapreq ifcr;
+       prop_data_t d;
+
+       d = (prop_data_t )prop_dictionary_get(env, "ifcaps");
+       if (d == NULL)
+               return;
+
+       assert(sizeof(ifcr) == prop_data_size(d));
+
+       memcpy(&ifcr, prop_data_data_nocopy(d), sizeof(ifcr));
+       if (direct_ioctl(env, SIOCSIFCAP, &ifcr) == -1)
+               err(EXIT_FAILURE, "SIOCSIFCAP");
+}
+
+int
+main(int argc, char **argv)
+{
+       const struct afswtch *afp;
+       int af, s;
+       bool aflag = false, Cflag = false;
+       struct match match[32];
+       size_t nmatch;
+       struct parser *start;
+       int ch, narg = 0, rc;
+       prop_dictionary_t env, oenv;
+       const char *ifname;
+       char *end;
+
+       memset(match, 0, sizeof(match));
+
+       init_afs();
+
+       start = init_parser();
+
+       /* Parse command-line options */
+       Nflag = vflag = zflag = false;
+       aflag = argc == 1 ? true : false;
+       if (aflag)
+               start = &opt_family_only.pb_parser;
+
+       while ((ch = getopt(argc, argv, gflags)) != -1) {
+               switch (ch) {
+               case 'A':
+                       warnx("-A is deprecated");
+                       break;
+
+               case 'a':
+                       aflag = true;
+                       break;
+
+               case 'b':
+                       bflag = true;
+                       break;
+
+               case 'C':
+                       Cflag = true;
+                       break;
+
+               case 'd':
+                       dflag = true;
+                       break;
+               case 'h':
+                       hflag = true;
+                       break;
+               case 'l':
+                       lflag = true;
+                       break;
+               case 'N':
+                       Nflag = true;
+                       break;
+
+               case 's':
+                       sflag = true;
+                       break;
+
+               case 'u':
+                       uflag = true;
+                       break;
+
+               case 'v':
+                       vflag = true;
+                       break;
+
+               case 'w':
+                       wflag = true;
+                       wflag_secs = strtol(optarg, &end, 10);
+                       if ((end != NULL && *end != '\0') ||
+                           wflag_secs < 0 || wflag_secs >= INT32_MAX)
+                               errx(EXIT_FAILURE, "%s: not a number", optarg);
+                       break;
+
+               case 'z':
+                       zflag = true;
+                       break;
+
+               default:
+                       if (!set_flag(ch))
+                               usage();
+                       break;
+               }
+               switch (ch) {
+               case 'a':
+                       start = &opt_family_only.pb_parser;
+                       break;
+
+               case 'L':
+               case 'm':
+               case 'z':
+                       if (start != &opt_family_only.pb_parser)
+                               start = &iface_opt_family_only.pif_parser;
+                       break;
+               case 'C':
+                       start = &cloneterm.pt_parser;
+                       break;
+               case 'l':
+                       start = &no_cmds.pt_parser;
+                       break;
+               case 's':
+                       if (start != &no_cmds.pt_parser &&
+                           start != &opt_family_only.pb_parser)
+                               start = &iface_only.pif_parser;
+                       break;
+               case 'w':
+                       start = &wait_dad.pt_parser;
+                       break;
+               default:
+                       break;
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       /*
+        * -l means "list all interfaces", and is mutually exclusive with
+        * all other flags/commands.
+        *
+        * -C means "list all names of cloners", and it mutually exclusive
+        * with all other flags/commands.
+        *
+        * -a means "print status of all interfaces".
+        *
+        * -w means "spin until DAD completes for all addreseses", and is
+        * mutually exclusivewith all other flags/commands.
+        */
+       if ((lflag || Cflag || wflag) &&
+           (aflag || get_flag('m') || vflag || zflag))
+               usage();
+       if ((lflag || Cflag || wflag) && get_flag('L'))
+               usage();
+       if ((lflag && Cflag) || (lflag & wflag) || (Cflag && wflag))
+               usage();
+
+       nmatch = __arraycount(match);
+
+       rc = parse(argc, argv, start, match, &nmatch, &narg);
+       if (rc != 0)
+               usage();
+
+       if (prog_init && prog_init() == -1)
+               err(1, "rump client init");
+
+       if ((oenv = prop_dictionary_create()) == NULL)
+               err(EXIT_FAILURE, "%s: prop_dictionary_create", __func__);
+
+       if (matches_exec(match, oenv, nmatch) == -1)
+               err(EXIT_FAILURE, "exec_matches");
+
+       argc -= narg;
+       argv += narg;
+
+       env = (nmatch > 0) ? match[(int)nmatch - 1].m_env : NULL;
+       if (env == NULL)
+               env = oenv;
+       else {
+               env = prop_dictionary_augment(env, oenv);
+               if (env == NULL)
+                       err(EXIT_FAILURE, "%s: prop_dictionary_augment",
+                           __func__);
+       }
+
+       /* Process any media commands that may have been issued. */
+       process_media_commands(env);
+
+       if ((af = getaf(env)) == -1)
+               af = AF_INET;
+
+       if ((s = getsock(af)) == -1)
+               err(EXIT_FAILURE, "%s: getsock", __func__);
+
+       if ((ifname = getifname(env)) == NULL)
+               err(EXIT_FAILURE, "%s: getifname", __func__);
+
+       if ((afp = lookup_af_bynum(af)) == NULL)
+               errx(EXIT_FAILURE, "%s: lookup_af_bynum", __func__);
+
+       assert(afp->af_addr_commit != NULL);
+       (*afp->af_addr_commit)(env, oenv);
+
+       do_setifpreference(env);
+       do_setifcaps(env);
+
+       exit(EXIT_SUCCESS);
+}
+
+static void
+init_afs(void)
+{
+       size_t i;
+       const struct afswtch *afp;
+       struct kwinst kw = {.k_type = KW_T_INT};
+
+       SIMPLEQ_FOREACH(afp, &aflist, af_next) {
+               kw.k_word = afp->af_name;
+               kw.k_int = afp->af_af;
+               for (i = 0; i < __arraycount(familykw); i++) {
+                       if (familykw[i].k_word == NULL) {
+                               familykw[i] = kw;
+                               break;
+                       }
+               }
+       }
+}
+
+const struct afswtch *
+lookup_af_bynum(int afnum)
+{
+       const struct afswtch *afp;
+
+       SIMPLEQ_FOREACH(afp, &aflist, af_next) {
+               if (afp->af_af == afnum)
+                       break;
+       }
+       return afp;
+}
+
+void
+printall(const char *ifname, prop_dictionary_t env0)
+{
+       struct ifaddrs *ifap, *ifa;
+       struct ifreq ifr;
+       const struct sockaddr *sdl = NULL;
+       prop_dictionary_t env, oenv;
+       int idx;
+       char *p;
+
+       if (env0 == NULL)
+               env = prop_dictionary_create();
+       else
+               env = prop_dictionary_copy_mutable(env0);
+
+       oenv = prop_dictionary_create();
+
+       if (env == NULL || oenv == NULL)
+               errx(EXIT_FAILURE, "%s: prop_dictionary_copy/create", __func__);
+
+       if (getifaddrs(&ifap) != 0)
+               err(EXIT_FAILURE, "getifaddrs");
+       p = NULL;
+       idx = 0;
+       for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+               memset(&ifr, 0, sizeof(ifr));
+               estrlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name));
+               if (sizeof(ifr.ifr_addr) >= ifa->ifa_addr->sa_len) {
+                       memcpy(&ifr.ifr_addr, ifa->ifa_addr,
+                           ifa->ifa_addr->sa_len);
+               }
+
+               if (ifname != NULL && strcmp(ifname, ifa->ifa_name) != 0)
+                       continue;
+               if (ifa->ifa_addr->sa_family == AF_LINK)
+                       sdl = ifa->ifa_addr;
+               if (p && strcmp(p, ifa->ifa_name) == 0)
+                       continue;
+               if (!prop_dictionary_set_cstring(env, "if", ifa->ifa_name))
+                       continue;
+               p = ifa->ifa_name;
+
+               if (bflag && (ifa->ifa_flags & IFF_BROADCAST) == 0)
+                       continue;
+               if (dflag && (ifa->ifa_flags & IFF_UP) != 0)
+                       continue;
+               if (uflag && (ifa->ifa_flags & IFF_UP) == 0)
+                       continue;
+
+               if (sflag && carrier(env))
+                       continue;
+               idx++;
+               /*
+                * Are we just listing the interfaces?
+                */
+               if (lflag) {
+                       if (idx > 1)
+                               printf(" ");
+                       fputs(ifa->ifa_name, stdout);
+                       continue;
+               }
+
+               status(sdl, env, oenv);
+               sdl = NULL;
+       }
+       if (lflag)
+               printf("\n");
+       prop_object_release((prop_object_t)env);
+       prop_object_release((prop_object_t)oenv);
+       freeifaddrs(ifap);
+}
+
+static int
+list_cloners(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct if_clonereq ifcr;
+       char *cp, *buf;
+       int idx, s;
+
+       memset(&ifcr, 0, sizeof(ifcr));
+
+       s = getsock(AF_INET);
+
+       if (prog_ioctl(s, SIOCIFGCLONERS, &ifcr) == -1)
+               err(EXIT_FAILURE, "SIOCIFGCLONERS for count");
+
+       buf = malloc(ifcr.ifcr_total * IFNAMSIZ);
+       if (buf == NULL)
+               err(EXIT_FAILURE, "unable to allocate cloner name buffer");
+
+       ifcr.ifcr_count = ifcr.ifcr_total;
+       ifcr.ifcr_buffer = buf;
+
+       if (prog_ioctl(s, SIOCIFGCLONERS, &ifcr) == -1)
+               err(EXIT_FAILURE, "SIOCIFGCLONERS for names");
+
+       /*
+        * In case some disappeared in the mean time, clamp it down.
+        */
+       if (ifcr.ifcr_count > ifcr.ifcr_total)
+               ifcr.ifcr_count = ifcr.ifcr_total;
+
+       for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) {
+               if (idx > 0)
+                       printf(" ");
+               printf("%s", cp);
+       }
+
+       printf("\n");
+       free(buf);
+       exit(EXIT_SUCCESS);
+}
+
+static int
+clone_command(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       int64_t cmd;
+
+       if (!prop_dictionary_get_int64(env, "clonecmd", &cmd)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       if (indirect_ioctl(env, (unsigned long)cmd, NULL) == -1) {
+               warn("%s", __func__);
+               return -1;
+       }
+       return 0;
+}
+
+/*ARGSUSED*/
+static int
+setifaddr(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       const struct paddr_prefix *pfx0;
+       struct paddr_prefix *pfx;
+       prop_data_t d;
+       int af;
+
+       if ((af = getaf(env)) == -1)
+               af = AF_INET;
+
+       d = (prop_data_t)prop_dictionary_get(env, "address");
+       assert(d != NULL);
+       pfx0 = prop_data_data_nocopy(d);
+
+       if (pfx0->pfx_len >= 0) {
+               pfx = prefixlen_to_mask(af, pfx0->pfx_len);
+               if (pfx == NULL)
+                       err(EXIT_FAILURE, "prefixlen_to_mask");
+               free(pfx);
+       }
+
+       return 0;
+}
+
+static int
+setifnetmask(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       prop_data_t d;
+
+       d = (prop_data_t)prop_dictionary_get(env, "dstormask");
+       assert(d != NULL);
+
+       if (!prop_dictionary_set(oenv, "netmask", (prop_object_t)d))
+               return -1;
+
+       return 0;
+}
+
+static int
+setifbroadaddr(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       prop_data_t d;
+       unsigned short flags;
+
+       if (getifflags(env, oenv, &flags) == -1)
+               err(EXIT_FAILURE, "%s: getifflags", __func__);
+
+       if ((flags & IFF_BROADCAST) == 0)
+               errx(EXIT_FAILURE, "not a broadcast interface");
+
+       d = (prop_data_t)prop_dictionary_get(env, "broadcast");
+       assert(d != NULL);
+
+       if (!prop_dictionary_set(oenv, "broadcast", (prop_object_t)d))
+               return -1;
+
+       return 0;
+}
+
+/*ARGSUSED*/
+static int
+notrailers(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       puts("Note: trailers are no longer sent, but always received");
+       return 0;
+}
+
+/*ARGSUSED*/
+static int
+setifdstormask(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       const char *key;
+       prop_data_t d;
+       unsigned short flags;
+
+       if (getifflags(env, oenv, &flags) == -1)
+               err(EXIT_FAILURE, "%s: getifflags", __func__);
+
+       d = (prop_data_t)prop_dictionary_get(env, "dstormask");
+       assert(d != NULL);
+
+       if ((flags & IFF_BROADCAST) == 0) {
+               key = "dst";
+       } else {
+               key = "netmask";
+       }
+
+       if (!prop_dictionary_set(oenv, key, (prop_object_t)d))
+               return -1;
+
+       return 0;
+}
+
+static int
+setifflags(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ifreq ifr;
+       int64_t ifflag;
+       bool rc;
+
+       rc = prop_dictionary_get_int64(env, "ifflag", &ifflag);
+       assert(rc);
+
+       if (direct_ioctl(env, SIOCGIFFLAGS, &ifr) == -1)
+               return -1;
+
+       if (ifflag < 0) {
+               ifflag = -ifflag;
+               ifr.ifr_flags &= ~ifflag;
+       } else
+               ifr.ifr_flags |= ifflag;
+
+       if (direct_ioctl(env, SIOCSIFFLAGS, &ifr) == -1)
+               return -1;
+
+       return 0;
+}
+
+static int
+getifcaps(prop_dictionary_t env, prop_dictionary_t oenv, struct ifcapreq *oifcr)
+{
+       bool rc;
+       struct ifcapreq ifcr;
+       const struct ifcapreq *tmpifcr;
+       prop_data_t capdata;
+
+       capdata = (prop_data_t)prop_dictionary_get(env, "ifcaps");
+
+       if (capdata != NULL) {
+               tmpifcr = prop_data_data_nocopy(capdata);
+               *oifcr = *tmpifcr;
+               return 0;
+       }
+
+       (void)direct_ioctl(env, SIOCGIFCAP, &ifcr);
+       *oifcr = ifcr;
+
+       capdata = prop_data_create_data(&ifcr, sizeof(ifcr));
+
+       rc = prop_dictionary_set(oenv, "ifcaps", capdata);
+
+       prop_object_release((prop_object_t)capdata);
+
+       return rc ? 0 : -1;
+}
+
+static int
+setifcaps(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       int64_t ifcap;
+       bool rc;
+       prop_data_t capdata;
+       struct ifcapreq ifcr;
+
+       rc = prop_dictionary_get_int64(env, "ifcap", &ifcap);
+       assert(rc);
+
+       if (getifcaps(env, oenv, &ifcr) == -1)
+               return -1;
+
+       if (ifcap < 0) {
+               ifcap = -ifcap;
+               ifcr.ifcr_capenable &= ~ifcap;
+       } else
+               ifcr.ifcr_capenable |= ifcap;
+
+       if ((capdata = prop_data_create_data(&ifcr, sizeof(ifcr))) == NULL)
+               return -1;
+
+       rc = prop_dictionary_set(oenv, "ifcaps", capdata);
+       prop_object_release((prop_object_t)capdata);
+
+       return rc ? 0 : -1;
+}
+
+static int
+setifmetric(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ifreq ifr;
+       bool rc;
+       int64_t metric;
+
+       rc = prop_dictionary_get_int64(env, "metric", &metric);
+       assert(rc);
+
+       ifr.ifr_metric = metric;
+       if (direct_ioctl(env, SIOCSIFMETRIC, &ifr) == -1)
+               warn("SIOCSIFMETRIC");
+       return 0;
+}
+
+static void
+do_setifpreference(prop_dictionary_t env)
+{
+       struct if_addrprefreq ifap;
+       prop_data_t d;
+       const struct paddr_prefix *pfx;
+
+       memset(&ifap, 0, sizeof(ifap));
+
+       if (!prop_dictionary_get_int16(env, "preference",
+           &ifap.ifap_preference))
+               return;
+
+       d = (prop_data_t)prop_dictionary_get(env, "address");
+       assert(d != NULL);
+
+       pfx = prop_data_data_nocopy(d);
+
+       memcpy(&ifap.ifap_addr, &pfx->pfx_addr,
+           MIN(sizeof(ifap.ifap_addr), pfx->pfx_addr.sa_len));
+       if (direct_ioctl(env, SIOCSIFADDRPREF, &ifap) == -1)
+               warn("SIOCSIFADDRPREF");
+}
+
+static int
+setifmtu(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       int64_t mtu;
+       bool rc;
+       struct ifreq ifr;
+
+       rc = prop_dictionary_get_int64(env, "mtu", &mtu);
+       assert(rc);
+
+       ifr.ifr_mtu = mtu;
+       if (direct_ioctl(env, SIOCSIFMTU, &ifr) == -1)
+               warn("SIOCSIFMTU");
+
+       return 0;
+}
+
+static int
+carrier(prop_dictionary_t env)
+{
+       struct ifmediareq ifmr;
+
+       memset(&ifmr, 0, sizeof(ifmr));
+
+       if (direct_ioctl(env, SIOCGIFMEDIA, &ifmr) == -1) {
+               /*
+                * Interface doesn't support SIOC{G,S}IFMEDIA;
+                * assume ok.
+                */
+               return EXIT_SUCCESS;
+       }
+       if ((ifmr.ifm_status & IFM_AVALID) == 0) {
+               /*
+                * Interface doesn't report media-valid status.
+                * assume ok.
+                */
+               return EXIT_SUCCESS;
+       }
+       /* otherwise, return ok for active, not-ok if not active. */
+       if (ifmr.ifm_status & IFM_ACTIVE)
+               return EXIT_SUCCESS;
+       else
+               return EXIT_FAILURE;
+}
+
+static void
+print_plural(const char *prefix, uint64_t n, const char *unit)
+{
+       printf("%s%" PRIu64 " %s%s", prefix, n, unit, (n == 1) ? "" : "s");
+}
+
+static void
+print_human_bytes(bool humanize, uint64_t n)
+{
+       char buf[5];
+
+       if (humanize) {
+               (void)humanize_number(buf, sizeof(buf),
+                   (int64_t)n, "", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL);
+               printf(", %s byte%s", buf, (atof(buf) == 1.0) ? "" : "s");
+       } else
+               print_plural(", ", n, "byte");
+}
+
+/*
+ * Print the status of the interface.  If an address family was
+ * specified, show it and it only; otherwise, show them all.
+ */
+
+#define MAX_PRINT_LEN 58       /* XXX need a better way to determine this! */
+
+void
+status(const struct sockaddr *sdl, prop_dictionary_t env,
+    prop_dictionary_t oenv)
+{
+       const struct if_data *ifi;
+       status_func_t *status_f;
+       statistics_func_t *statistics_f;
+       struct ifdatareq ifdr;
+       struct ifreq ifr;
+       struct ifdrv ifdrv;
+       char fbuf[BUFSIZ];
+       char *bp;
+       int af, s;
+       const char *ifname;
+       struct ifcapreq ifcr;
+       unsigned short flags;
+       const struct afswtch *afp;
+
+       if ((af = getaf(env)) == -1) {
+               afp = NULL;
+               af = AF_UNSPEC;
+       } else
+               afp = lookup_af_bynum(af);
+
+       /* get out early if the family is unsupported by the kernel */
+       if ((s = getsock(af)) == -1)
+               err(EXIT_FAILURE, "%s: getsock", __func__);
+
+       if ((ifname = getifinfo(env, oenv, &flags)) == NULL)
+               err(EXIT_FAILURE, "%s: getifinfo", __func__);
+
+       (void)snprintb(fbuf, sizeof(fbuf), IFFBITS, flags);
+       printf("%s: flags=%s", ifname, fbuf);
+
+       estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       if (prog_ioctl(s, SIOCGIFMETRIC, &ifr) == -1)
+               warn("SIOCGIFMETRIC %s", ifr.ifr_name);
+       else if (ifr.ifr_metric != 0)
+               printf(" metric %d", ifr.ifr_metric);
+
+       estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       if (prog_ioctl(s, SIOCGIFMTU, &ifr) != -1 && ifr.ifr_mtu != 0)
+               printf(" mtu %d", ifr.ifr_mtu);
+       printf("\n");
+
+       if (getifcaps(env, oenv, &ifcr) == -1)
+               err(EXIT_FAILURE, "%s: getifcaps", __func__);
+
+       if (ifcr.ifcr_capabilities != 0) {
+               (void)snprintb_m(fbuf, sizeof(fbuf), IFCAPBITS,
+                   ifcr.ifcr_capabilities, MAX_PRINT_LEN);
+               bp = fbuf;
+               while (*bp != '\0') {
+                       printf("\tcapabilities=%s\n", &bp[2]);
+                       bp += strlen(bp) + 1;
+               }
+               (void)snprintb_m(fbuf, sizeof(fbuf), IFCAPBITS,
+                   ifcr.ifcr_capenable, MAX_PRINT_LEN);
+               bp = fbuf;
+               while (*bp != '\0') {
+                       printf("\tenabled=%s\n", &bp[2]);
+                       bp += strlen(bp) + 1;
+               }
+       }
+
+       SIMPLEQ_FOREACH(status_f, &status_funcs, f_next)
+               (*status_f->f_func)(env, oenv);
+
+       print_link_addresses(env, true);
+
+       estrlcpy(ifdrv.ifd_name, ifname, sizeof(ifdrv.ifd_name));
+       ifdrv.ifd_cmd = IFLINKSTR_QUERYLEN;
+       ifdrv.ifd_len = 0;
+       ifdrv.ifd_data = NULL;
+       /* interface supports linkstr? */
+       if (prog_ioctl(s, SIOCGLINKSTR, &ifdrv) != -1) {
+               char *p;
+
+               p = malloc(ifdrv.ifd_len);
+               if (p == NULL)
+                       err(EXIT_FAILURE, "malloc linkstr buf failed");
+               ifdrv.ifd_data = p;
+               ifdrv.ifd_cmd = 0;
+               if (prog_ioctl(s, SIOCGLINKSTR, &ifdrv) == -1)
+                       err(EXIT_FAILURE, "failed to query linkstr");
+               printf("\tlinkstr: %s\n", (char *)ifdrv.ifd_data);
+               free(p);
+       }
+
+       media_status(env, oenv);
+
+       if (!vflag && !zflag)
+               goto proto_status;
+
+       estrlcpy(ifdr.ifdr_name, ifname, sizeof(ifdr.ifdr_name));
+
+       if (prog_ioctl(s, zflag ? SIOCZIFDATA : SIOCGIFDATA, &ifdr) == -1)
+               err(EXIT_FAILURE, zflag ? "SIOCZIFDATA" : "SIOCGIFDATA");
+
+       ifi = &ifdr.ifdr_data;
+
+       print_plural("\tinput: ", ifi->ifi_ipackets, "packet");
+       print_human_bytes(hflag, ifi->ifi_ibytes);
+       if (ifi->ifi_imcasts)
+               print_plural(", ", ifi->ifi_imcasts, "multicast");
+       if (ifi->ifi_ierrors)
+               print_plural(", ", ifi->ifi_ierrors, "error");
+       if (ifi->ifi_iqdrops)
+               print_plural(", ", ifi->ifi_iqdrops, "queue drop");
+       if (ifi->ifi_noproto)
+               printf(", %" PRIu64 " unknown protocol", ifi->ifi_noproto);
+       print_plural("\n\toutput: ", ifi->ifi_opackets, "packet");
+       print_human_bytes(hflag, ifi->ifi_obytes);
+       if (ifi->ifi_omcasts)
+               print_plural(", ", ifi->ifi_omcasts, "multicast");
+       if (ifi->ifi_oerrors)
+               print_plural(", ", ifi->ifi_oerrors, "error");
+       if (ifi->ifi_collisions)
+               print_plural(", ", ifi->ifi_collisions, "collision");
+       printf("\n");
+
+       SIMPLEQ_FOREACH(statistics_f, &statistics_funcs, f_next)
+               (*statistics_f->f_func)(env);
+
+ proto_status:
+
+       if (afp != NULL)
+               (*afp->af_status)(env, oenv, true);
+       else SIMPLEQ_FOREACH(afp, &aflist, af_next)
+               (*afp->af_status)(env, oenv, false);
+}
+
+static int
+setifprefixlen(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       bool rc;
+       int64_t plen;
+       int af;
+       struct paddr_prefix *pfx;
+       prop_data_t d;
+
+       if ((af = getaf(env)) == -1)
+               af = AF_INET;
+
+       rc = prop_dictionary_get_int64(env, "prefixlen", &plen);
+       assert(rc);
+
+       pfx = prefixlen_to_mask(af, plen);
+       if (pfx == NULL)
+               err(EXIT_FAILURE, "prefixlen_to_mask");
+
+       d = prop_data_create_data(pfx, paddr_prefix_size(pfx));
+       if (d == NULL)
+               err(EXIT_FAILURE, "%s: prop_data_create_data", __func__);
+
+       if (!prop_dictionary_set(oenv, "netmask", (prop_object_t)d))
+               err(EXIT_FAILURE, "%s: prop_dictionary_set", __func__);
+
+       free(pfx);
+       return 0;
+}
+
+static int
+setlinkstr(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ifdrv ifdrv;
+       size_t linkstrlen;
+       prop_data_t data;
+       char *linkstr;
+
+       data = (prop_data_t)prop_dictionary_get(env, "linkstr");
+       if (data == NULL) {
+               errno = ENOENT;
+               return -1;
+       }
+       linkstrlen = prop_data_size(data)+1;
+
+       linkstr = malloc(linkstrlen);
+       if (linkstr == NULL)
+               err(EXIT_FAILURE, "malloc linkstr space");
+       if (getargstr(env, "linkstr", linkstr, linkstrlen) == -1)
+               errx(EXIT_FAILURE, "getargstr linkstr failed");
+
+       ifdrv.ifd_cmd = 0;
+       ifdrv.ifd_len = linkstrlen;
+       ifdrv.ifd_data = __UNCONST(linkstr);
+
+       if (direct_ioctl(env, SIOCSLINKSTR, &ifdrv) == -1)
+               err(EXIT_FAILURE, "SIOCSLINKSTR");
+       free(linkstr);
+
+       return 0;
+}
+
+static int
+unsetlinkstr(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ifdrv ifdrv;
+
+       memset(&ifdrv, 0, sizeof(ifdrv));
+       ifdrv.ifd_cmd = IFLINKSTR_UNSET;
+
+       if (direct_ioctl(env, SIOCSLINKSTR, &ifdrv) == -1)
+               err(EXIT_FAILURE, "SIOCSLINKSTR");
+
+       return 0;
+}
+
+static void
+usage(void)
+{
+       const char *progname = getprogname();
+       usage_func_t *usage_f;
+       prop_dictionary_t env;
+
+       if ((env = prop_dictionary_create()) == NULL)
+               err(EXIT_FAILURE, "%s: prop_dictionary_create", __func__);
+
+       fprintf(stderr, "usage: %s [-h] %s[-v] [-z] %sinterface\n"
+               "\t[ af [ address [ dest_addr ] ] [ netmask mask ] [ prefixlen n ]\n"
+               "\t\t[ alias | -alias ] ]\n"
+               "\t[ up ] [ down ] [ metric n ] [ mtu n ]\n", progname,
+               flag_is_registered(gflags, 'm') ? "[-m] " : "",
+               flag_is_registered(gflags, 'L') ? "[-L] " : "");
+
+       SIMPLEQ_FOREACH(usage_f, &usage_funcs, f_next)
+               (*usage_f->f_func)(env);
+
+       fprintf(stderr,
+               "\t[ arp | -arp ]\n"
+               "\t[ preference n ]\n"
+               "\t[ link0 | -link0 ] [ link1 | -link1 ] [ link2 | -link2 ]\n"
+               "       %s -a [-b] [-d] [-h] %s[-u] [-v] [-z] [ af ]\n"
+               "       %s -l [-b] [-d] [-s] [-u]\n"
+               "       %s -C\n"
+               "       %s -w n\n"
+               "       %s interface create\n"
+               "       %s interface destroy\n",
+               progname, flag_is_registered(gflags, 'm') ? "[-m] " : "",
+               progname, progname, progname, progname, progname);
+
+       prop_object_release((prop_object_t)env);
+       exit(EXIT_FAILURE);
+}
diff --git a/sbin/ifconfig/ifconfig_hostops.c b/sbin/ifconfig/ifconfig_hostops.c
new file mode 100644 (file)
index 0000000..4a5d5d1
--- /dev/null
@@ -0,0 +1,47 @@
+/*     $NetBSD: ifconfig_hostops.c,v 1.1 2010/12/13 17:35:08 pooka Exp $       */
+
+/*
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: ifconfig_hostops.c,v 1.1 2010/12/13 17:35:08 pooka Exp $");
+#endif /* !lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <unistd.h>
+
+#include "prog_ops.h"
+
+const struct prog_ops prog_ops = {
+       .op_socket = socket,
+       .op_ioctl = ioctl,
+       .op_read = read,
+       .op_close = close,
+};
diff --git a/sbin/ifconfig/ifconfig_rumpops.c b/sbin/ifconfig/ifconfig_rumpops.c
new file mode 100644 (file)
index 0000000..68afe8c
--- /dev/null
@@ -0,0 +1,52 @@
+/*     $NetBSD: ifconfig_rumpops.c,v 1.1 2010/12/13 17:35:08 pooka Exp $       */
+
+/*
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: ifconfig_rumpops.c,v 1.1 2010/12/13 17:35:08 pooka Exp $");
+#endif /* !lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <unistd.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+#include <rump/rumpclient.h>
+
+#include "prog_ops.h"
+
+const struct prog_ops prog_ops = {
+       .op_init =      rumpclient_init,
+
+       .op_socket =    rump_sys_socket,
+       .op_ioctl =     rump_sys_ioctl,
+       .op_read =      rump_sys_read,
+       .op_close =     rump_sys_close,
+};
diff --git a/sbin/ifconfig/media.c b/sbin/ifconfig/media.c
new file mode 100644 (file)
index 0000000..b945cb3
--- /dev/null
@@ -0,0 +1,464 @@
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: media.c,v 1.6 2011/08/29 14:35:00 joerg Exp $");
+#endif /* not lint */
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <util.h>
+
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <prop/proplib.h>
+
+#include "env.h"
+#include "extern.h"
+#include "media.h"
+#include "parse.h"
+#include "util.h"
+#include "prog_ops.h"
+
+static void init_current_media(prop_dictionary_t, prop_dictionary_t);
+static void media_constructor(void) __attribute__((constructor));
+static int setmedia(prop_dictionary_t, prop_dictionary_t);
+static int setmediainst(prop_dictionary_t, prop_dictionary_t);
+static int setmediamode(prop_dictionary_t, prop_dictionary_t);
+static int setmediaopt(prop_dictionary_t, prop_dictionary_t);
+static int unsetmediaopt(prop_dictionary_t, prop_dictionary_t);
+
+/*
+ * Media stuff.  Whenever a media command is first performed, the
+ * currently select media is grabbed for this interface.  If `media'
+ * is given, the current media word is modifed.  `mediaopt' commands
+ * only modify the set and clear words.  They then operate on the
+ * current media word later.
+ */
+static int     media_current;
+static int     mediaopt_set;
+static int     mediaopt_clear;
+
+static struct usage_func usage;
+
+static const int ifm_status_valid_list[] = IFM_STATUS_VALID_LIST;
+
+static const struct ifmedia_status_description ifm_status_descriptions[] =
+    IFM_STATUS_DESCRIPTIONS;
+
+static struct pstr mediamode = PSTR_INITIALIZER1(&mediamode, "mediamode",
+    setmediamode, "mediamode", false, &command_root.pb_parser);
+
+static struct pinteger mediainst = PINTEGER_INITIALIZER1(&mediainst,
+    "mediainst", 0, IFM_INST_MAX, 10, setmediainst, "mediainst",
+    &command_root.pb_parser);
+
+static struct pstr unmediaopt = PSTR_INITIALIZER1(&unmediaopt, "-mediaopt",
+    unsetmediaopt, "unmediaopt", false, &command_root.pb_parser);
+
+static struct pstr mediaopt = PSTR_INITIALIZER1(&mediaopt, "mediaopt",
+    setmediaopt, "mediaopt", false, &command_root.pb_parser);
+
+static struct pstr media = PSTR_INITIALIZER1(&media, "media", setmedia, "media",
+    false, &command_root.pb_parser);
+
+static const struct kwinst mediakw[] = {
+         {.k_word = "instance", .k_key = "anymedia", .k_type = KW_T_BOOL,
+          .k_bool = true, .k_act = "media", .k_deact = "mediainst",
+          .k_nextparser = &mediainst.pi_parser}
+       , {.k_word = "inst", .k_key = "anymedia", .k_type = KW_T_BOOL,
+          .k_bool = true, .k_act = "media", .k_deact = "mediainst",
+          .k_nextparser = &mediainst.pi_parser}
+       , {.k_word = "media", .k_key = "anymedia", .k_type = KW_T_BOOL,
+          .k_bool = true, .k_deact = "media", .k_altdeact = "anymedia",
+          .k_nextparser = &media.ps_parser}
+       , {.k_word = "mediaopt", .k_key = "anymedia", .k_type = KW_T_BOOL,
+          .k_bool = true, .k_deact = "mediaopt", .k_altdeact = "instance",
+          .k_nextparser = &mediaopt.ps_parser}
+       , {.k_word = "-mediaopt", .k_key = "anymedia", .k_type = KW_T_BOOL,
+          .k_bool = true, .k_deact = "unmediaopt", .k_altdeact = "media",
+          .k_nextparser = &unmediaopt.ps_parser}
+       , {.k_word = "mode", .k_key = "anymedia", .k_type = KW_T_BOOL,
+          .k_bool = true, .k_deact = "mode",
+          .k_nextparser = &mediamode.ps_parser}
+};
+
+struct pkw kwmedia = PKW_INITIALIZER(&kwmedia, "media keywords", NULL, NULL,
+    mediakw, __arraycount(mediakw), NULL);
+
+__dead static void
+media_error(int type, const char *val, const char *opt)
+{
+       errx(EXIT_FAILURE, "unknown %s media %s: %s",
+               get_media_type_string(type), opt, val);
+}
+
+void
+init_current_media(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       const char *ifname;
+       struct ifmediareq ifmr;
+
+       if ((ifname = getifname(env)) == NULL)
+               err(EXIT_FAILURE, "getifname");
+
+       /*
+        * If we have not yet done so, grab the currently-selected
+        * media.
+        */
+
+       if (prop_dictionary_get(env, "initmedia") == NULL) {
+               memset(&ifmr, 0, sizeof(ifmr));
+
+               if (direct_ioctl(env, SIOCGIFMEDIA, &ifmr) == -1) {
+                       /*
+                        * If we get E2BIG, the kernel is telling us
+                        * that there are more, so we can ignore it.
+                        */
+                       if (errno != E2BIG)
+                               err(EXIT_FAILURE, "SIOCGIFMEDIA");
+               }
+
+               if (!prop_dictionary_set_bool(oenv, "initmedia", true)) {
+                       err(EXIT_FAILURE, "%s: prop_dictionary_set_bool",
+                           __func__);
+               }
+               media_current = ifmr.ifm_current;
+       }
+
+       /* Sanity. */
+       if (IFM_TYPE(media_current) == 0)
+               errx(EXIT_FAILURE, "%s: no link type?", ifname);
+}
+
+void
+process_media_commands(prop_dictionary_t env)
+{
+       struct ifreq ifr;
+
+       if (prop_dictionary_get(env, "media") == NULL &&
+           prop_dictionary_get(env, "mediaopt") == NULL &&
+           prop_dictionary_get(env, "unmediaopt") == NULL &&
+           prop_dictionary_get(env, "mediamode") == NULL) {
+               /* Nothing to do. */
+               return;
+       }
+
+       /*
+        * Media already set up, and commands sanity-checked.  Set/clear
+        * any options, and we're ready to go.
+        */
+       media_current |= mediaopt_set;
+       media_current &= ~mediaopt_clear;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_media = media_current;
+
+       if (direct_ioctl(env, SIOCSIFMEDIA, &ifr) == -1)
+               err(EXIT_FAILURE, "SIOCSIFMEDIA");
+}
+
+static int
+setmedia(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       int type, subtype, inst;
+       prop_data_t data;
+       char *val;
+
+       init_current_media(env, oenv);
+
+       data = (prop_data_t)prop_dictionary_get(env, "media");
+       assert(data != NULL);
+
+       /* Only one media command may be given. */
+       /* Must not come after mode commands */
+       /* Must not come after mediaopt commands */
+
+       /*
+        * No need to check if `instance' has been issued; setmediainst()
+        * craps out if `media' has not been specified.
+        */
+
+       type = IFM_TYPE(media_current);
+       inst = IFM_INST(media_current);
+
+       val = strndup(prop_data_data_nocopy(data), prop_data_size(data));
+       if (val == NULL)
+               return -1;
+
+       /* Look up the subtype. */
+       subtype = get_media_subtype(type, val);
+       if (subtype == -1)
+               media_error(type, val, "subtype");
+
+       /* Build the new current media word. */
+       media_current = IFM_MAKEWORD(type, subtype, 0, inst);
+
+       /* Media will be set after other processing is complete. */
+       return 0;
+}
+
+static int
+setmediaopt(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       char *invalid;
+       prop_data_t data;
+       char *val;
+
+       init_current_media(env, oenv);
+
+       data = (prop_data_t)prop_dictionary_get(env, "mediaopt");
+       assert(data != NULL);
+
+       /* Can only issue `mediaopt' once. */
+       /* Can't issue `mediaopt' if `instance' has already been issued. */
+
+       val = strndup(prop_data_data_nocopy(data), prop_data_size(data));
+       if (val == NULL)
+               return -1;
+
+       mediaopt_set = get_media_options(media_current, val, &invalid);
+       free(val);
+       if (mediaopt_set == -1)
+               media_error(media_current, invalid, "option");
+
+       /* Media will be set after other processing is complete. */
+       return 0;
+}
+
+static int
+unsetmediaopt(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       char *invalid, *val;
+       prop_data_t data;
+
+       init_current_media(env, oenv);
+
+       data = (prop_data_t)prop_dictionary_get(env, "unmediaopt");
+       if (data == NULL) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       val = strndup(prop_data_data_nocopy(data), prop_data_size(data));
+       if (val == NULL)
+               return -1;
+
+       /*
+        * No need to check for A_MEDIAINST, since the test for A_MEDIA
+        * implicitly checks for A_MEDIAINST.
+        */
+
+       mediaopt_clear = get_media_options(media_current, val, &invalid);
+       free(val);
+       if (mediaopt_clear == -1)
+               media_error(media_current, invalid, "option");
+
+       /* Media will be set after other processing is complete. */
+       return 0;
+}
+
+static int
+setmediainst(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       int type, subtype, options;
+       int64_t inst;
+       bool rc;
+
+       init_current_media(env, oenv);
+
+       rc = prop_dictionary_get_int64(env, "mediainst", &inst);
+       assert(rc);
+
+       /* Can only issue `instance' once. */
+       /* Must have already specified `media' */
+
+       type = IFM_TYPE(media_current);
+       subtype = IFM_SUBTYPE(media_current);
+       options = IFM_OPTIONS(media_current);
+
+       media_current = IFM_MAKEWORD(type, subtype, options, inst);
+
+       /* Media will be set after other processing is complete. */
+       return 0;
+}
+
+static int
+setmediamode(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       int type, subtype, options, inst, mode;
+       prop_data_t data;
+       char *val;
+
+       init_current_media(env, oenv);
+
+       data = (prop_data_t)prop_dictionary_get(env, "mediamode");
+       assert(data != NULL);
+
+       type = IFM_TYPE(media_current);
+       subtype = IFM_SUBTYPE(media_current);
+       options = IFM_OPTIONS(media_current);
+       inst = IFM_INST(media_current);
+
+       val = strndup(prop_data_data_nocopy(data), prop_data_size(data));
+       if (val == NULL)
+               return -1;
+
+       mode = get_media_mode(type, val);
+       if (mode == -1)
+               media_error(type, val, "mode");
+
+       free(val);
+
+       media_current = IFM_MAKEWORD(type, subtype, options, inst) | mode;
+
+       /* Media will be set after other processing is complete. */
+       return 0;
+}
+
+void
+print_media_word(int ifmw, const char *opt_sep)
+{
+       const char *str;
+
+       printf("%s", get_media_subtype_string(ifmw));
+
+       /* Find mode. */
+       if (IFM_MODE(ifmw) != 0) {
+               str = get_media_mode_string(ifmw);
+               if (str != NULL)
+                       printf(" mode %s", str);
+       }
+
+       /* Find options. */
+       for (; (str = get_media_option_string(&ifmw)) != NULL; opt_sep = ",")
+               printf("%s%s", opt_sep, str);
+
+       if (IFM_INST(ifmw) != 0)
+               printf(" instance %d", IFM_INST(ifmw));
+}
+
+void
+media_status(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct ifmediareq ifmr;
+       int af, i, s;
+       int *media_list;
+       const char *ifname;
+
+       if ((ifname = getifname(env)) == NULL)
+               err(EXIT_FAILURE, "getifname");
+       if ((af = getaf(env)) == -1)
+               af = AF_UNSPEC;
+
+       /* get out early if the family is unsupported by the kernel */
+       if ((s = getsock(af)) == -1)
+               err(EXIT_FAILURE, "%s: getsock", __func__);
+
+       memset(&ifmr, 0, sizeof(ifmr));
+       estrlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+
+       if (prog_ioctl(s, SIOCGIFMEDIA, &ifmr) == -1) {
+               /*
+                * Interface doesn't support SIOC{G,S}IFMEDIA.
+                */
+               return;
+       }
+
+       if (ifmr.ifm_count == 0) {
+               warnx("%s: no media types?", ifname);
+               return;
+       }
+
+       media_list = (int *)malloc(ifmr.ifm_count * sizeof(int));
+       if (media_list == NULL)
+               err(EXIT_FAILURE, "malloc");
+       ifmr.ifm_ulist = media_list;
+
+       if (prog_ioctl(s, SIOCGIFMEDIA, &ifmr) == -1)
+               err(EXIT_FAILURE, "SIOCGIFMEDIA");
+
+       printf("\tmedia: %s ", get_media_type_string(ifmr.ifm_current));
+       print_media_word(ifmr.ifm_current, " ");
+       if (ifmr.ifm_active != ifmr.ifm_current) {
+               printf(" (");
+               print_media_word(ifmr.ifm_active, " ");
+               printf(")");
+       }
+       printf("\n");
+
+       if (ifmr.ifm_status & IFM_STATUS_VALID) {
+               const struct ifmedia_status_description *ifms;
+               int bitno, found = 0;
+
+               printf("\tstatus: ");
+               for (bitno = 0; ifm_status_valid_list[bitno] != 0; bitno++) {
+                       for (ifms = ifm_status_descriptions;
+                            ifms->ifms_valid != 0; ifms++) {
+                               if (ifms->ifms_type !=
+                                     IFM_TYPE(ifmr.ifm_current) ||
+                                   ifms->ifms_valid !=
+                                     ifm_status_valid_list[bitno])
+                                       continue;
+                               printf("%s%s", found ? ", " : "",
+                                   IFM_STATUS_DESC(ifms, ifmr.ifm_status));
+                               found = 1;
+
+                               /*
+                                * For each valid indicator bit, there's
+                                * only one entry for each media type, so
+                                * terminate the inner loop now.
+                                */
+                               break;
+                       }
+               }
+
+               if (found == 0)
+                       printf("unknown");
+               printf("\n");
+       }
+
+       if (get_flag('m')) {
+               int type, printed_type;
+
+               for (type = IFM_NMIN; type <= IFM_NMAX; type += IFM_NMIN) {
+                       for (i = 0, printed_type = 0; i < ifmr.ifm_count; i++) {
+                               if (IFM_TYPE(media_list[i]) != type)
+                                       continue;
+                               if (printed_type == 0) {
+                                       printf("\tsupported %s media:\n",
+                                           get_media_type_string(type));
+                                       printed_type = 1;
+                               }
+                               printf("\t\tmedia ");
+                               print_media_word(media_list[i], " mediaopt ");
+                               printf("\n");
+                       }
+               }
+       }
+
+       free(media_list);
+}
+
+static void
+media_usage(prop_dictionary_t env)
+{
+       fprintf(stderr,
+           "\t[ media type ] [ mediaopt opts ] [ -mediaopt opts ] "
+           "[ instance minst ]\n");
+}
+
+static void
+media_constructor(void)
+{
+       if (register_flag('m') != 0)
+               err(EXIT_FAILURE, __func__);
+       usage_func_init(&usage, media_usage);
+       register_usage(&usage);
+}
diff --git a/sbin/ifconfig/media.h b/sbin/ifconfig/media.h
new file mode 100644 (file)
index 0000000..85ce6a6
--- /dev/null
@@ -0,0 +1,16 @@
+/*     $NetBSD: media.h,v 1.1 2008/07/02 07:44:15 dyoung Exp $ */
+
+#ifndef        _IFCONFIG_MEDIA_H
+#define        _IFCONFIG_MEDIA_H
+
+#include <prop/proplib.h>
+
+#include "parse.h"
+
+extern struct pkw kwmedia;
+
+void   print_media_word(int, const char *);
+void   process_media_commands(prop_dictionary_t);
+void   media_status(prop_dictionary_t, prop_dictionary_t);
+
+#endif /* _IFCONFIG_MEDIA_H */
diff --git a/sbin/ifconfig/parse.c b/sbin/ifconfig/parse.c
new file mode 100644 (file)
index 0000000..112a5ec
--- /dev/null
@@ -0,0 +1,995 @@
+/*     $NetBSD: parse.c,v 1.18 2013/07/17 15:42:03 christos Exp $      */
+
+/*-
+ * Copyright (c) 2008 David Young.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: parse.c,v 1.18 2013/07/17 15:42:03 christos Exp $");
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+#include <sys/param.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <netatalk/at.h>
+
+#include "env.h"
+#include "parse.h"
+#include "util.h"
+
+#ifdef DEBUG
+#define dbg_warnx(__fmt, ...)  warnx(__fmt, __VA_ARGS__)
+#else
+#define dbg_warnx(__fmt, ...)  /* empty */
+#endif
+
+static int parser_default_init(struct parser *);
+static int pbranch_init(struct parser *);
+static int pkw_init(struct parser *);
+
+static int pterm_match(const struct parser *, const struct match *,
+    struct match *, int, const char *);
+
+static int paddr_match(const struct parser *, const struct match *,
+    struct match *, int, const char *);
+
+static int pbranch_match(const struct parser *, const struct match *,
+    struct match *, int, const char *);
+
+static int piface_match(const struct parser *, const struct match *,
+    struct match *, int, const char *);
+
+static int pstr_match(const struct parser *, const struct match *,
+    struct match *, int, const char *);
+
+static int pinteger_match(const struct parser *, const struct match *,
+    struct match *, int, const char *);
+
+static int pkw_match(const struct parser *, const struct match *,
+    struct match *, int, const char *);
+
+const struct parser_methods pterm_methods = {
+         .pm_match = pterm_match
+       , .pm_init = NULL
+};
+
+const struct parser_methods pstr_methods = {
+         .pm_match = pstr_match
+       , .pm_init = parser_default_init
+};
+
+const struct parser_methods pinteger_methods = {
+         .pm_match = pinteger_match
+       , .pm_init = parser_default_init
+};
+
+const struct parser_methods paddr_methods = {
+         .pm_match = paddr_match
+       , .pm_init = parser_default_init
+};
+
+const struct parser_methods piface_methods = {
+         .pm_match = piface_match
+       , .pm_init = parser_default_init
+};
+
+const struct parser_methods pbranch_methods = {
+         .pm_match = pbranch_match
+       , .pm_init = pbranch_init
+};
+
+const struct parser_methods pkw_methods = {
+         .pm_match = pkw_match
+       , .pm_init = pkw_init
+};
+
+static int
+match_setenv(const struct match *im, struct match *om, const char *key,
+    prop_object_t o)
+{
+       if (im == NULL)
+               om->m_env = prop_dictionary_create();
+       else
+               om->m_env = prop_dictionary_copy(im->m_env);
+
+       if (om->m_env == NULL)
+               goto delobj;
+
+       if (key != NULL && !prop_dictionary_set(om->m_env, key, o))
+               goto deldict;
+
+       if (o != NULL)
+               prop_object_release((prop_object_t)o);
+
+       return 0;
+deldict:
+       prop_object_release((prop_object_t)om->m_env);
+       om->m_env = NULL;
+delobj:
+       prop_object_release((prop_object_t)o);
+       errno = ENOMEM;
+       return -1;
+}
+
+int
+pstr_match(const struct parser *p, const struct match *im, struct match *om,
+    int argidx, const char *arg)
+{
+       prop_object_t o;
+       const struct pstr *ps = (const struct pstr *)p;
+       uint8_t buf[128];
+       int len;
+
+       if (arg == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       len = (int)sizeof(buf);
+       if (get_string(arg, NULL, buf, &len, ps->ps_hexok) == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       o = (prop_object_t)prop_data_create_data(buf, len);
+
+       if (o == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       if (match_setenv(im, om, ps->ps_key, o) == -1)
+               return -1;
+
+       om->m_argidx = argidx;
+       om->m_parser = p;
+       om->m_nextparser = p->p_nextparser;
+
+       return 0;
+}
+
+int
+pinteger_match(const struct parser *p, const struct match *im, struct match *om,
+    int argidx, const char *arg)
+{
+       prop_object_t o;
+       const struct pinteger *pi = (const struct pinteger *)p;
+       char *end;
+       int64_t val;
+
+       if (arg == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       val = strtoimax(arg, &end, pi->pi_base);
+       if ((val == INTMAX_MIN || val == INTMAX_MAX) && errno == ERANGE)
+               return -1;
+
+       if (*end != '\0') {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (val < pi->pi_min || val > pi->pi_max) {
+               errno = ERANGE;
+               return -1;
+       }
+
+       o = (prop_object_t)prop_number_create_integer(val);
+
+       if (o == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       if (match_setenv(im, om, pi->pi_key, o) == -1)
+               return -1;
+
+       om->m_argidx = argidx;
+       om->m_parser = p;
+       om->m_nextparser = p->p_nextparser;
+
+       return 0;
+}
+
+static int
+parse_linkaddr(const char *addr, struct sockaddr_storage *ss)
+{
+       static const size_t maxlen =
+           sizeof(*ss) - offsetof(struct sockaddr_dl, sdl_data[0]);
+       enum {
+               LLADDR_S_INITIAL = 0,
+               LLADDR_S_ONE_OCTET = 1,
+               LLADDR_S_TWO_OCTETS = 2,
+               LLADDR_S_COLON = 3
+       } state = LLADDR_S_INITIAL;
+       uint8_t octet = 0, val;
+       struct sockaddr_dl *sdl;
+       const char *p;
+       size_t i;
+
+       memset(ss, 0, sizeof(*ss));
+       ss->ss_family = AF_LINK;
+       sdl = (struct sockaddr_dl *)ss;
+
+       for (i = 0, p = addr; i < maxlen; p++) {
+               dbg_warnx("%s.%d: *p == %c, state %d", __func__, __LINE__, *p,
+                   state);
+               if (*p == '\0') {
+                       dbg_warnx("%s.%d", __func__, __LINE__);
+                       if (state != LLADDR_S_ONE_OCTET &&
+                           state != LLADDR_S_TWO_OCTETS)
+                               return -1;
+                       dbg_warnx("%s.%d", __func__, __LINE__);
+                       sdl->sdl_data[i++] = octet;
+                       sdl->sdl_len = offsetof(struct sockaddr_dl, sdl_data)
+                           + i * sizeof(sdl->sdl_data[0]);
+                       sdl->sdl_alen = i;
+                       return 0;
+               }
+               if (*p == ':') {
+                       dbg_warnx("%s.%d", __func__, __LINE__);
+                       if (state != LLADDR_S_ONE_OCTET &&
+                           state != LLADDR_S_TWO_OCTETS)
+                               return -1;
+                       dbg_warnx("%s.%d", __func__, __LINE__);
+                       sdl->sdl_data[i++] = octet;
+                       state = LLADDR_S_COLON;
+                       continue;
+               }
+               if ('a' <= *p && *p <= 'f')
+                       val = 10 + *p - 'a';
+               else if ('A' <= *p && *p <= 'F')
+                       val = 10 + *p - 'A';
+               else if ('0' <= *p && *p <= '9')
+                       val = *p - '0';
+               else
+                       return -1;
+
+               dbg_warnx("%s.%d", __func__, __LINE__);
+               if (state == LLADDR_S_ONE_OCTET) {
+                       state = LLADDR_S_TWO_OCTETS;
+                       octet <<= 4;
+                       octet |= val;
+               } else if (state != LLADDR_S_INITIAL && state != LLADDR_S_COLON)
+                       return -1;
+               else {
+                       state = LLADDR_S_ONE_OCTET;
+                       octet = val;
+               }
+               dbg_warnx("%s.%d", __func__, __LINE__);
+       }
+       return -1;
+}
+
+static int
+paddr_match(const struct parser *p, const struct match *im, struct match *om,
+    int argidx, const char *arg0)
+{
+       unsigned int net, node;
+       int nread;
+       union {
+               struct sockaddr sa;
+               struct sockaddr_at sat;
+               struct sockaddr_in sin;
+               struct sockaddr_storage ss;
+       } u;
+       const struct paddr *pa = (const struct paddr *)p;
+       prop_data_t d;
+       prop_object_t o;
+       int64_t af0;
+       int af, rc;
+       struct paddr_prefix *pfx, *mask;
+       const struct sockaddr *sa = NULL;
+       struct addrinfo hints, *result = NULL;
+       char *arg, *end, *plen = NULL, *servname0;
+       const char *servname;
+       long prefixlen = -1;
+       size_t len;
+
+       if (arg0 == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (pa->pa_activator != NULL &&
+           prop_dictionary_get(im->m_env, pa->pa_activator) == NULL)
+               return -1;
+
+       if (pa->pa_deactivator != NULL &&
+           prop_dictionary_get(im->m_env, pa->pa_deactivator) != NULL)
+               return -1;
+
+       if (!prop_dictionary_get_int64(im->m_env, "af", &af0))
+               af = AF_UNSPEC;
+       else
+               af = af0;
+
+       memset(&u, 0, sizeof(u));
+
+       switch (af) {
+       case AF_UNSPEC:
+       case AF_INET:
+       case AF_INET6:
+               if ((arg = strdup(arg0)) == NULL)
+                       return -1;
+
+               servname0 = arg;
+               (void)strsep(&servname0, ",");
+               servname = (servname0 == NULL) ? "0" : servname0;
+
+               if (pa->pa_maskkey == NULL)
+                       ;
+               else if ((plen = strrchr(arg, '/')) != NULL)
+                       *plen++ = '\0';
+
+               memset(&hints, 0, sizeof(hints));
+
+               hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
+               hints.ai_family = af;
+               hints.ai_socktype = SOCK_DGRAM;
+
+               for (;;) {
+                       rc = getaddrinfo(arg, servname, &hints, &result);
+                       if (rc == 0) {
+                               if (result->ai_next == NULL)
+                                       sa = result->ai_addr;
+                               else
+                                       errno = EMLINK;
+                               break;
+                       } else if ((hints.ai_flags & AI_NUMERICHOST) != 0 &&
+                           (af == AF_INET || af == AF_UNSPEC) &&
+                           inet_aton(arg, &u.sin.sin_addr) == 1) {
+                               u.sin.sin_family = AF_INET;
+                               u.sin.sin_len = sizeof(u.sin);
+                               sa = &u.sa;
+                               break;
+                       } else if ((hints.ai_flags & AI_NUMERICHOST) == 0 ||
+                                rc != EAI_NONAME) {
+                               errno = ENOENT;
+                               break;
+                       }
+                       hints.ai_flags &= ~AI_NUMERICHOST;
+               }
+
+
+               if (plen == NULL)
+                       prefixlen = -1;
+               else {
+                       prefixlen = strtol(plen, &end, 10);
+                       if (end != NULL && *end != '\0')
+                               sa = NULL;
+                       if (prefixlen < 0 || prefixlen >= UINT8_MAX) {
+                               errno = ERANGE;
+                               sa = NULL;
+                       }
+               }
+
+               free(arg);
+               if (sa != NULL || af != AF_UNSPEC)
+                       break;
+               /*FALLTHROUGH*/
+       case AF_APPLETALK:
+               if (sscanf(arg0, "%u.%u%n", &net, &node, &nread) == 2 &&
+                   net != 0 && net <= 0xffff && node != 0 && node <= 0xfe &&
+                   arg0[nread] == '\0') {
+                       u.sat.sat_family = AF_APPLETALK;
+                       u.sat.sat_len = sizeof(u.sat);
+                       u.sat.sat_addr.s_net = htons(net);
+                       u.sat.sat_addr.s_node = node;
+                       sa = &u.sa;
+               }
+               break;
+       case AF_LINK:
+               if (parse_linkaddr(arg0, &u.ss) == -1)
+                       sa = NULL;
+               else
+                       sa = &u.sa;
+               break;
+       }
+
+       if (sa == NULL)
+               return -1;
+
+       len = offsetof(struct paddr_prefix, pfx_addr) + sa->sa_len;
+
+       if ((pfx = malloc(len)) == NULL)
+               return -1;
+
+#if 0
+       {
+               int i;
+
+               for (i = 0; i < sa->sa_len; i++)
+                       printf(" %02x", ((const uint8_t *)sa)[i]);
+               printf("\n");
+       }
+#endif
+
+       pfx->pfx_len = (int16_t)prefixlen;
+       memcpy(&pfx->pfx_addr, sa, sa->sa_len);
+       af = sa->sa_family;
+
+       if (result != NULL)
+               freeaddrinfo(result);
+
+       o = (prop_object_t)prop_data_create_data(pfx, len);
+
+       free(pfx);
+
+       if (o == NULL)
+               return -1;
+
+       if (match_setenv(im, om, pa->pa_addrkey, o) == -1)
+               return -1;
+
+       if (pa->pa_maskkey != NULL && plen != NULL) {
+               size_t masklen;
+
+               if ((mask = prefixlen_to_mask(af, prefixlen)) == NULL) {
+                       err(EXIT_FAILURE, "%s: prefixlen_to_mask(%d, %ld)",
+                           __func__, af, prefixlen);
+                       return -1;
+               }
+
+               masklen = paddr_prefix_size(mask);
+
+               d = prop_data_create_data(mask, masklen);
+               free(mask);
+
+               if (d == NULL) {
+                       err(EXIT_FAILURE, "%s: prop_data_create_data",
+                           __func__);
+                       return -1;
+               }
+
+               rc = prop_dictionary_set(om->m_env, pa->pa_maskkey,
+                   (prop_object_t)d) ? 0 : -1;
+
+               prop_object_release((prop_object_t)d);
+
+               if (rc != 0) {
+                       err(EXIT_FAILURE, "%s: prop_dictionary_set", __func__);
+                       return rc;
+               }
+       }
+
+       om->m_argidx = argidx;
+       om->m_parser = p;
+       om->m_nextparser = p->p_nextparser;
+       return 0;
+}
+
+static int
+pterm_match(const struct parser *p, const struct match *im,
+    struct match *om, int argidx, const char *arg)
+{
+       const struct pterm *pt = (const struct pterm *)p;
+       prop_bool_t b;
+
+       if (arg != NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+       b = prop_bool_create(true);
+
+       if (match_setenv(im, om, pt->pt_key, (prop_object_t)b) == -1)
+               return -1;
+
+       om->m_argidx = argidx;
+       om->m_parser = p;
+       om->m_nextparser = NULL;
+       return 0;
+}
+
+static int
+piface_match(const struct parser *p, const struct match *im,
+    struct match *om, int argidx, const char *arg)
+{
+       const struct piface *pif = (const struct piface *)p;
+       prop_object_t o;
+
+       if (arg == NULL || strlen(arg) > IFNAMSIZ) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if ((o = (prop_object_t)prop_string_create_cstring(arg)) == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       if (match_setenv(im, om, pif->pif_key, o) == -1)
+               return -1;
+
+       om->m_argidx = argidx;
+       om->m_parser = p;
+       om->m_nextparser = p->p_nextparser;
+       return 0;
+}
+
+static void
+match_cleanup(struct match *dst)
+{
+       if (dst->m_env != NULL)
+               prop_object_release((prop_object_t)dst->m_env);
+       memset(dst, 0, sizeof(*dst));
+}
+
+static void
+match_copy(struct match *dst, const struct match *src)
+{
+       match_cleanup(dst);
+
+       prop_object_retain((prop_object_t)src->m_env);
+       *dst = *src;
+}
+
+static int
+pbranch_match(const struct parser *p, const struct match *im,
+    struct match *om, int argidx, const char *arg)
+{
+       const struct parser *nextp;
+       struct branch *b;
+       const struct pbranch *pb = (const struct pbranch *)p;
+       struct match tmpm;
+       int nforbid = 0, nmatch = 0, rc;
+       parser_match_t matchfunc;
+
+       memset(&tmpm, 0, sizeof(tmpm));
+
+       SIMPLEQ_FOREACH(b, &pb->pb_branches, b_next) {
+               nextp = b->b_nextparser;
+               dbg_warnx("%s: b->b_nextparser %p [%s]", __func__,
+                   nextp, nextp ? nextp->p_name : "(null)");
+               if (nextp == NULL) {
+                       if (arg == NULL) {
+                               nmatch++;
+                               match_setenv(im, om, NULL, NULL);
+                               om->m_nextparser = NULL;
+                               om->m_parser = p;
+                               om->m_argidx = argidx;
+                       }
+                       continue;
+               }
+               matchfunc = nextp->p_methods->pm_match;
+               rc = (*matchfunc)(nextp, im, &tmpm, argidx, arg);
+               if (rc == 0) {
+                       match_copy(om, &tmpm);
+                       match_cleanup(&tmpm);
+                       nmatch++;
+                       dbg_warnx("%s: branch %s ok", __func__, nextp->p_name); 
+                       if (pb->pb_match_first)
+                               break;
+               } else if (rc == 1) {
+                       nforbid++;
+                       if (pb->pb_match_first)
+                               break;
+               } else {
+                       dbg_warnx("%s: fail branch %s", __func__,
+                           nextp->p_name); 
+               }
+       }
+       switch (nmatch) {
+       case 0:
+               errno = ENOENT;
+               return (nforbid == 0) ? -1 : 1;
+       case 1:
+               dbg_warnx("%s: branch ok", __func__); 
+               return 0;
+       default:
+               match_cleanup(om);
+               errno = EMLINK;
+               return -1;
+       }
+}
+
+static int
+pkw_match(const struct parser *p, const struct match *im,
+    struct match *om, int argidx, const char *arg)
+{
+       prop_object_t o = NULL;
+       struct kwinst *k;
+       union kwval *u = NULL;
+       const struct pkw *pk = (const struct pkw *)p;
+
+       if (arg == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       SIMPLEQ_FOREACH(k, &pk->pk_keywords, k_next) {
+               if (k->k_act != NULL &&
+                   prop_dictionary_get(im->m_env, k->k_act) == NULL)
+                       continue;
+
+               if (k->k_neg && arg[0] == '-' &&
+                   strcmp(k->k_word, arg + 1) == 0)
+                       u = &k->k_negu;
+               else if (strcmp(k->k_word, arg) == 0)
+                       u = &k->k_u;
+               else
+                       continue;
+
+               if (k->k_altdeact != NULL &&
+                   prop_dictionary_get(im->m_env, k->k_altdeact) != NULL)
+                       return 1;
+
+               if (k->k_deact != NULL &&
+                   prop_dictionary_get(im->m_env, k->k_deact) != NULL)
+                       return 1;
+               break;
+       }
+       if (k == NULL) {
+               errno = ENOENT;
+               return -1;
+       }
+       switch (k->k_type) {
+       case KW_T_NONE:
+               break;
+       case KW_T_BOOL:
+               o = (prop_object_t)prop_bool_create(u->u_bool);
+               if (o == NULL)
+                       goto err;
+               break;
+       case KW_T_INT:
+               o = (prop_object_t)prop_number_create_integer(u->u_sint);
+               if (o == NULL)
+                       goto err;
+               break;
+       case KW_T_UINT:
+               o = (prop_object_t)prop_number_create_unsigned_integer(
+                   u->u_uint);
+               if (o == NULL)
+                       goto err;
+               break;
+       case KW_T_OBJ:
+               o = u->u_obj;
+               break;
+       case KW_T_STR:
+               o = (prop_object_t)prop_string_create_cstring_nocopy(u->u_str);
+               if (o == NULL)
+                       goto err;
+               break;
+       default:
+               errx(EXIT_FAILURE, "unknown keyword type %d", k->k_type);
+       }
+
+       if (match_setenv(im, om, (o == NULL) ? NULL : k->k_key, o) == -1)
+               return -1;
+
+       om->m_argidx = argidx;
+       om->m_parser = p;
+       om->m_nextparser = k->k_nextparser;
+       om->m_exec = k->k_exec;
+       return 0;
+err:
+       errno = ENOMEM;
+       return -1;
+}
+
+struct paddr *
+paddr_create(const char *name, parser_exec_t pexec, const char *addrkey,
+    const char *maskkey, struct parser *next)
+{
+       struct paddr *pa;
+
+       if ((pa = calloc(sizeof(*pa), 1)) == NULL)
+               return NULL;
+
+       pa->pa_parser.p_methods = &paddr_methods;
+       pa->pa_parser.p_exec = pexec;
+       pa->pa_parser.p_name = name;
+       pa->pa_parser.p_nextparser = next;
+
+       pa->pa_addrkey = addrkey;
+       pa->pa_maskkey = maskkey;
+
+       return pa;
+}
+
+struct piface *
+piface_create(const char *name, parser_exec_t pexec, const char *defkey,
+    struct parser *defnext)
+{
+       struct piface *pif;
+
+       if ((pif = calloc(sizeof(*pif), 1)) == NULL)
+               return NULL;
+
+       pif->pif_parser.p_methods = &piface_methods;
+       pif->pif_parser.p_exec = pexec;
+       pif->pif_parser.p_name = name;
+       pif->pif_parser.p_nextparser = defnext;
+
+       pif->pif_key = defkey;
+
+       return pif;
+}
+
+int
+pbranch_addbranch(struct pbranch *pb, struct parser *p)
+{
+       struct branch *b;
+
+       if ((b = malloc(sizeof(*b))) == NULL)
+               return -1;
+       b->b_nextparser = p;
+       SIMPLEQ_INSERT_HEAD(&pb->pb_branches, b, b_next);
+       pb->pb_parser.p_initialized = false;
+       return parser_init(&pb->pb_parser);
+}
+
+int
+pbranch_setbranches(struct pbranch *pb, const struct branch *brs, size_t nbr)
+{
+       struct branch *b;
+       size_t i;
+
+       dbg_warnx("%s: nbr %zu", __func__, nbr);
+
+       while ((b = SIMPLEQ_FIRST(&pb->pb_branches)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&pb->pb_branches, b_next);
+               free(b);
+       }
+
+       for (i = 0; i < nbr; i++) {
+               if ((b = malloc(sizeof(*b))) == NULL)
+                       goto err;
+               *b = brs[i];
+               dbg_warnx("%s: b->b_nextparser %p [%s]", __func__,
+                   b->b_nextparser, b->b_nextparser ? b->b_nextparser->p_name
+                   : "(null)");
+               SIMPLEQ_INSERT_TAIL(&pb->pb_branches, b, b_next);
+       }
+
+       return 0;
+err:
+       while ((b = SIMPLEQ_FIRST(&pb->pb_branches)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&pb->pb_branches, b_next);
+               free(b);
+       }
+       return -1;
+}
+
+static int
+pbranch_init(struct parser *p)
+{
+       struct branch *b;
+       struct pbranch *pb = (struct pbranch *)p;
+       struct parser *np;
+
+       if (pb->pb_nbrinit == 0)
+               ;
+       else if (pbranch_setbranches(pb, pb->pb_brinit, pb->pb_nbrinit) == -1)
+               return -1;
+
+       pb->pb_nbrinit = 0;
+
+       SIMPLEQ_FOREACH(b, &pb->pb_branches, b_next) {
+               np = b->b_nextparser;
+               if (np != NULL && parser_init(np) == -1)
+                       return -1;
+       }
+       return 0;
+}
+
+struct pbranch *
+pbranch_create(const char *name, const struct branch *brs, size_t nbr,
+    bool match_first)
+{
+       struct pbranch *pb;
+
+       dbg_warnx("%s: nbr %zu", __func__, nbr);
+
+       if ((pb = calloc(1, sizeof(*pb))) == NULL)
+               return NULL;
+
+       pb->pb_parser.p_methods = &pbranch_methods;
+       pb->pb_parser.p_name = name;
+
+       SIMPLEQ_INIT(&pb->pb_branches);
+
+       if (pbranch_setbranches(pb, brs, nbr) == -1)
+               goto post_pb_err;
+
+       pb->pb_match_first = match_first;
+       return pb;
+post_pb_err:
+       free(pb);
+       return NULL;
+}
+
+static int
+parser_default_init(struct parser *p)
+{
+       struct parser *np;
+
+       np = p->p_nextparser;
+       if (np != NULL && parser_init(np) == -1)
+               return -1;
+
+       return 0;
+}
+
+static int
+pkw_setwords(struct pkw *pk, parser_exec_t defexec, const char *defkey,
+    const struct kwinst *kws, size_t nkw, struct parser *defnext)
+{
+       struct kwinst *k;
+       size_t i;
+
+       for (i = 0; i < nkw; i++) {
+               if (kws[i].k_word == NULL)
+                       continue;
+               if ((k = malloc(sizeof(*k))) == NULL)
+                       goto post_pk_err;
+               *k = kws[i];
+               if (k->k_nextparser == NULL)
+                       k->k_nextparser = defnext;
+               if (k->k_key == NULL)
+                       k->k_key = defkey;
+               if (k->k_exec == NULL)
+                       k->k_exec = defexec;
+               SIMPLEQ_INSERT_TAIL(&pk->pk_keywords, k, k_next);
+       }
+       return 0;
+
+post_pk_err:
+       while ((k = SIMPLEQ_FIRST(&pk->pk_keywords)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&pk->pk_keywords, k_next);
+               free(k);
+       }
+       return -1;
+}
+
+static int
+pkw_init(struct parser *p)
+{
+       struct kwinst *k;
+       struct pkw *pk = (struct pkw *)p;
+       struct parser *np;
+
+       if (pk->pk_nkwinit == 0)
+               ;
+       else if (pkw_setwords(pk, pk->pk_execinit, pk->pk_keyinit,
+           pk->pk_kwinit, pk->pk_nkwinit, pk->pk_nextinit) == -1)
+               return -1;
+
+       pk->pk_nkwinit = 0;
+
+       SIMPLEQ_FOREACH(k, &pk->pk_keywords, k_next) {
+               np = k->k_nextparser;
+               if (np != NULL && parser_init(np) == -1)
+                       return -1;
+       }
+       return 0;
+}
+
+struct pkw *
+pkw_create(const char *name, parser_exec_t defexec, const char *defkey,
+    const struct kwinst *kws, size_t nkw, struct parser *defnext)
+{
+       struct pkw *pk;
+
+       if ((pk = calloc(1, sizeof(*pk))) == NULL)
+               return NULL;
+
+       pk->pk_parser.p_methods = &pkw_methods;
+       pk->pk_parser.p_exec = defexec;
+       pk->pk_parser.p_name = name;
+
+       SIMPLEQ_INIT(&pk->pk_keywords);
+
+       if (pkw_setwords(pk, defexec, defkey, kws, nkw, defnext) == -1)
+               goto err;
+
+       return pk;
+err:
+       free(pk);
+       return NULL;
+}
+
+int
+parse(int argc, char **argv, const struct parser *p0, struct match *matches,
+    size_t *nmatch, int *narg)
+{
+       int i, rc = 0;
+       struct match *lastm = NULL, *m = matches;
+       const struct parser *p = p0;
+
+       for (i = 0; i < argc && p != NULL; i++) {
+               if ((size_t)(m - matches) >= *nmatch) {
+                       errno = EFBIG;
+                       rc = -1;
+                       break;
+               }
+               rc = (*p->p_methods->pm_match)(p, lastm, m, i, argv[i]);
+               if (rc != 0)
+                       goto out;
+               p = m->m_nextparser;
+               lastm = m++;
+       }
+       for (; (size_t)(m - matches) < *nmatch && p != NULL; ) {
+               rc = (*p->p_methods->pm_match)(p, lastm, m, i, NULL);
+               if (rc != 0)
+                       break;
+               p = m->m_nextparser;
+               lastm = m++;
+       }
+out:
+       *nmatch = m - matches;
+       *narg = i;
+       return rc;
+}
+
+int
+matches_exec(const struct match *matches, prop_dictionary_t oenv, size_t nmatch)
+{
+       size_t i;
+       int rc = 0;
+       const struct match *m;
+       parser_exec_t pexec;
+       prop_dictionary_t d;
+
+       for (i = 0; i < nmatch; i++) {
+               m = &matches[i];
+               dbg_warnx("%s.%d: i %zu", __func__, __LINE__, i);
+               pexec = (m->m_parser->p_exec != NULL)
+                   ? m->m_parser->p_exec : m->m_exec;
+               if (pexec == NULL)
+                       continue;
+               dbg_warnx("%s.%d: m->m_parser->p_name %s", __func__, __LINE__,
+                   m->m_parser->p_name);
+               d = prop_dictionary_augment(m->m_env, oenv);
+               rc = (*pexec)(d, oenv);
+               prop_object_release((prop_object_t)d);
+               if (rc == -1)
+                       break;
+       }
+       return rc;
+}
+
+int
+parser_init(struct parser *p)
+{
+       if (p->p_initialized)
+               return 0;
+       p->p_initialized = true;
+       if (p->p_methods->pm_init == NULL)
+               return 0;
+       return (*p->p_methods->pm_init)(p);
+}
diff --git a/sbin/ifconfig/parse.h b/sbin/ifconfig/parse.h
new file mode 100644 (file)
index 0000000..231dfd9
--- /dev/null
@@ -0,0 +1,284 @@
+#ifndef _IFCONFIG_PARSE_H
+#define _IFCONFIG_PARSE_H
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/queue.h>
+#include <prop/proplib.h>
+#include <sys/socket.h>
+
+struct match;
+struct parser;
+
+extern struct pbranch command_root;
+
+typedef int (*parser_exec_t)(prop_dictionary_t, prop_dictionary_t);
+typedef int (*parser_match_t)(const struct parser *, const struct match *,
+    struct match *, int, const char *);
+typedef int (*parser_init_t)(struct parser *);
+
+struct match {
+       prop_dictionary_t       m_env;
+       const struct parser     *m_nextparser;
+       const struct parser     *m_parser;
+       int                     m_argidx;
+       parser_exec_t           m_exec;
+};
+
+/* method table */
+struct parser_methods {
+       parser_match_t  pm_match;
+       parser_init_t   pm_init;
+};
+
+struct parser {
+       const struct parser_methods     *p_methods;
+       parser_exec_t                   p_exec;
+       const char                      *p_name;
+       struct parser                   *p_nextparser;
+       bool                            p_initialized;
+};
+
+struct branch {
+       SIMPLEQ_ENTRY(branch)   b_next;
+       struct parser   *b_nextparser;
+};
+
+struct pbranch {
+       struct parser           pb_parser;
+       SIMPLEQ_HEAD(, branch)  pb_branches;
+       bool                    pb_match_first;
+       const struct branch     *pb_brinit;
+       size_t                  pb_nbrinit;
+};
+
+struct pterm {
+       struct parser           pt_parser;
+       const char              *pt_key;
+};
+
+extern const struct parser_methods paddr_methods;
+extern const struct parser_methods pbranch_methods;
+extern const struct parser_methods piface_methods;
+extern const struct parser_methods pinteger_methods;
+extern const struct parser_methods pstr_methods;
+extern const struct parser_methods pkw_methods;
+extern const struct parser_methods pterm_methods;
+
+#define        PTERM_INITIALIZER(__pt, __name, __exec, __key)                  \
+{                                                                      \
+       .pt_parser = {.p_name = (__name), .p_methods = &pterm_methods,  \
+                     .p_exec = (__exec)},                              \
+       .pt_key = (__key)                                               \
+}
+
+#define        PBRANCH_INITIALIZER(__pb, __name, __brs, __nbr, __match_first)  \
+{                                                                      \
+       .pb_parser = {.p_name = (__name), .p_methods = &pbranch_methods},\
+       .pb_branches = SIMPLEQ_HEAD_INITIALIZER((__pb)->pb_branches),   \
+       .pb_brinit = (__brs),                                           \
+       .pb_nbrinit = (__nbr),                                          \
+       .pb_match_first = (__match_first)                               \
+}
+
+#define        PSTR_INITIALIZER(__ps, __name, __defexec, __defkey, __defnext)  \
+    PSTR_INITIALIZER1((__ps), (__name), (__defexec), (__defkey),       \
+    true, (__defnext))
+
+#define        PSTR_INITIALIZER1(__ps, __name, __defexec, __defkey, __defhexok,\
+    __defnext)                                                         \
+{                                                                      \
+       .ps_parser = {.p_name = (__name), .p_methods = &pstr_methods,   \
+                      .p_exec = (__defexec),                           \
+                      .p_nextparser = (__defnext)},                    \
+       .ps_key = (__defkey),                                           \
+       .ps_hexok = (__defhexok)                                        \
+}
+
+#define        PADDR_INITIALIZER(__pa, __name, __defexec, __addrkey,           \
+    __maskkey, __activator, __deactivator, __defnext)          \
+{                                                                      \
+       .pa_parser = {.p_name = (__name), .p_methods = &paddr_methods,  \
+                      .p_exec = (__defexec),                           \
+                      .p_nextparser = (__defnext)},                    \
+       .pa_addrkey = (__addrkey),                                      \
+       .pa_maskkey = (__maskkey),                                      \
+       .pa_activator = (__activator),                                  \
+       .pa_deactivator = (__deactivator),                              \
+}
+
+#define        PIFACE_INITIALIZER(__pif, __name, __defexec, __defkey, __defnext)\
+{                                                                      \
+       .pif_parser = {.p_name = (__name), .p_methods = &piface_methods,\
+                      .p_exec = (__defexec),                           \
+                      .p_nextparser = (__defnext)},                    \
+       .pif_key = (__defkey)                                           \
+}
+
+#define        PINTEGER_INITIALIZER1(__pi, __name, __min, __max, __base,       \
+    __defexec, __defkey, __defnext)                                    \
+{                                                                      \
+       .pi_parser = {.p_name = (__name), .p_methods = &pinteger_methods,\
+                     .p_exec = (__defexec),                            \
+                     .p_nextparser = (__defnext),                      \
+                     .p_initialized = false},                          \
+       .pi_min = (__min),                                              \
+       .pi_max = (__max),                                              \
+       .pi_base = (__base),                                            \
+       .pi_key = (__defkey)                                            \
+}
+
+#define        PINTEGER_INITIALIZER(__pi, __name, __base, __defexec, __defkey, \
+    __defnext)                                                         \
+       PINTEGER_INITIALIZER1(__pi, __name, INTMAX_MIN, INTMAX_MAX,     \
+           __base, __defexec, __defkey, __defnext)
+
+#define        PKW_INITIALIZER(__pk, __name, __defexec, __defkey, __kws, __nkw,\
+       __defnext)                                                      \
+{                                                                      \
+       .pk_parser = {.p_name = (__name),                               \
+                     .p_exec = (__defexec),                            \
+                     .p_methods = &pkw_methods,                        \
+                     .p_initialized = false},                          \
+       .pk_keywords = SIMPLEQ_HEAD_INITIALIZER((__pk)->pk_keywords),   \
+       .pk_kwinit = (__kws),                                           \
+       .pk_nkwinit = (__nkw),                                          \
+       .pk_keyinit = (__defkey),                                       \
+       .pk_nextinit = (__defnext)                                      \
+}
+
+#define        IFKW(__word, __flag)                                    \
+{                                                              \
+       .k_word = (__word), .k_neg = true, .k_type = KW_T_INT,  \
+       .k_int = (__flag),                                      \
+       .k_negint = -(__flag)                                   \
+}
+
+#define        KW_T_NONE       0
+#define        KW_T_OBJ        1
+#define        KW_T_INT        2
+#define        KW_T_STR        3
+#define        KW_T_BOOL       4
+#define        KW_T_UINT       5
+
+struct kwinst {
+       SIMPLEQ_ENTRY(kwinst)   k_next;
+       int                     k_type;
+       const char              *k_word;
+       const char              *k_key;
+       const char              *k_act;
+       const char              *k_deact;
+       const char              *k_altdeact;
+       parser_exec_t           k_exec;
+       union kwval {
+               int64_t         u_sint;
+               uint64_t        u_uint;
+               const char      *u_str;
+               prop_object_t   u_obj;
+               bool            u_bool;
+       } k_u, k_negu;
+#define k_int  k_u.u_sint
+#define k_uint k_u.u_uint
+#define k_str  k_u.u_str
+#define k_obj  k_u.u_obj
+#define k_bool k_u.u_bool
+
+#define k_negint       k_negu.u_sint
+#define k_neguint      k_negu.u_uint
+#define k_negstr       k_negu.u_str
+#define k_negobj       k_negu.u_obj
+#define k_negbool      k_negu.u_bool
+
+       bool                    k_neg;  /* allow negative form, -keyword */
+       struct parser           *k_nextparser;
+};
+
+struct pkw {
+       struct parser           pk_parser;
+       const char              *pk_key;
+       const char              *pk_keyinit;
+       const struct kwinst     *pk_kwinit;
+       size_t                  pk_nkwinit;
+       SIMPLEQ_HEAD(, kwinst)  pk_keywords;
+};
+
+#define        pk_nextinit     pk_parser.p_nextparser
+#define        pk_execinit     pk_parser.p_exec
+
+struct pstr {
+       struct parser           ps_parser;
+       const char              *ps_key;
+       bool                    ps_hexok;
+};
+
+struct pinteger {
+       struct parser           pi_parser;
+       int64_t                 pi_min;
+       int64_t                 pi_max;
+       int                     pi_base;
+       const char              *pi_key;
+};
+
+struct intrange {
+       SIMPLEQ_ENTRY(intrange) r_next;
+       int64_t                 r_bottom;
+       int64_t                 r_top;
+       struct parser           *r_nextparser;
+};
+
+struct pranges {
+       struct parser           pr_parser;
+       SIMPLEQ_HEAD(, intrange)        pr_ranges;
+};
+
+struct paddr_prefix {
+       int16_t         pfx_len;
+       struct sockaddr pfx_addr;
+};
+
+static inline size_t
+paddr_prefix_size(const struct paddr_prefix *pfx)
+{
+       return offsetof(struct paddr_prefix, pfx_addr) + pfx->pfx_addr.sa_len;
+}
+
+struct paddr {
+       struct parser           pa_parser;
+       const char              *pa_addrkey;
+       const char              *pa_maskkey;
+       const char              *pa_activator;
+       const char              *pa_deactivator;
+};
+struct piface {
+       struct parser           pif_parser;
+       const char              *pif_key;
+};
+
+struct prest {
+       struct parser           pr_parser;
+};
+
+struct prest *prest_create(const char *);
+struct paddr *paddr_create(const char *, parser_exec_t, const char *,
+    const char *, struct parser *);
+struct pstr *pstr_create(const char *, parser_exec_t, const char *,
+    bool, struct parser *);
+struct piface *piface_create(const char *, parser_exec_t, const char *,
+    struct parser *);
+struct pkw *pkw_create(const char *, parser_exec_t,
+    const char *, const struct kwinst *, size_t, struct parser *);
+struct pranges *pranges_create(const char *, parser_exec_t, const char *,
+    const struct intrange *, size_t, struct parser *);
+struct pbranch *pbranch_create(const char *, const struct branch *, size_t,
+    bool);
+int pbranch_addbranch(struct pbranch *, struct parser *);
+int pbranch_setbranches(struct pbranch *, const struct branch *, size_t);
+
+int parse(int, char **, const struct parser *, struct match *, size_t *, int *);
+
+int matches_exec(const struct match *, prop_dictionary_t, size_t);
+int parser_init(struct parser *);
+
+#endif /* _IFCONFIG_PARSE_H */
diff --git a/sbin/ifconfig/pfsync.c b/sbin/ifconfig/pfsync.c
new file mode 100644 (file)
index 0000000..c44f656
--- /dev/null
@@ -0,0 +1,229 @@
+/*     $NetBSD: pfsync.c,v 1.1 2009/09/14 10:36:49 degroote Exp $      */
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: pfsync.c,v 1.1 2009/09/14 10:36:49 degroote Exp $");
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/pfvar.h>
+#include <net/if_pfsync.h>
+
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <util.h>
+
+#include "env.h"
+#include "parse.h"
+#include "extern.h"
+
+static status_func_t status;
+static usage_func_t usage;
+static cmdloop_branch_t branch;
+
+static void pfsync_constructor(void) __attribute__((constructor));
+static void pfsync_status(prop_dictionary_t, prop_dictionary_t);
+static int setpfsync_maxupd(prop_dictionary_t, prop_dictionary_t);
+static int setpfsync_peer(prop_dictionary_t, prop_dictionary_t);
+static int setpfsyncdev(prop_dictionary_t, prop_dictionary_t);
+
+struct pinteger parse_maxupd = PINTEGER_INITIALIZER1(&parse_maxupd, "maxupd",
+    0, 255, 10, setpfsync_maxupd, "maxupd", &command_root.pb_parser);
+
+struct piface pfsyncdev = PIFACE_INITIALIZER(&pfsyncdev, "syncdev", setpfsyncdev,
+    "syncdev", &command_root.pb_parser);
+
+struct paddr parse_sync_peer = PADDR_INITIALIZER(&parse_sync_peer, "syncpeer",
+               setpfsync_peer, "syncpeer", NULL, NULL, NULL, &command_root.pb_parser);
+
+static const struct kwinst pfsynckw[] = {
+       {.k_word = "maxupd", .k_nextparser = &parse_maxupd.pi_parser},
+       {.k_word = "syncdev", .k_nextparser = &pfsyncdev.pif_parser},
+       {.k_word = "-syncdev", .k_key = "syncdev", .k_type = KW_T_STR,
+           .k_str = "", .k_exec = setpfsyncdev,
+           .k_nextparser = &command_root.pb_parser},
+       {.k_word = "syncpeer", .k_nextparser = &parse_sync_peer.pa_parser},
+       {.k_word = "-syncpeer", .k_key = "syncpeer", .k_type = KW_T_STR,
+           .k_str = "", .k_exec = setpfsync_peer,
+           .k_nextparser = &command_root.pb_parser}
+};
+
+struct pkw pfsync = PKW_INITIALIZER(&pfsync, "pfsync", NULL, NULL,
+    pfsynckw, __arraycount(pfsynckw), NULL);
+
+static void
+pfsync_set(prop_dictionary_t env, struct pfsyncreq *pfsyncr)
+{
+       if (indirect_ioctl(env, SIOCSETPFSYNC, pfsyncr) == -1)
+               err(EXIT_FAILURE, "SIOCSETPFSYNC");
+}
+
+static int
+pfsync_get1(prop_dictionary_t env, struct pfsyncreq *pfsyncr)
+{
+       memset(pfsyncr, 0, sizeof(*pfsyncr));
+
+       return indirect_ioctl(env, SIOCGETPFSYNC, pfsyncr);
+}
+
+static void
+pfsync_get(prop_dictionary_t env, struct pfsyncreq *pfsyncr)
+{
+       if (pfsync_get1(env, pfsyncr) == -1)
+               err(EXIT_FAILURE, "SIOCGETPFSYNC");
+}
+
+static void
+pfsync_status(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct pfsyncreq pfsyncr;
+
+       if (pfsync_get1(env, &pfsyncr) == -1)
+               return;
+
+       if (pfsyncr.pfsyncr_syncdev[0] != '\0') {
+               printf("\tpfsync: syncdev: %s ", pfsyncr.pfsyncr_syncdev);
+               if (pfsyncr.pfsyncr_syncpeer.s_addr != INADDR_PFSYNC_GROUP)
+                       printf("syncpeer: %s ",
+                           inet_ntoa(pfsyncr.pfsyncr_syncpeer));
+               printf("maxupd: %d\n", pfsyncr.pfsyncr_maxupdates);
+       }
+}
+
+/* ARGSUSED */
+int
+setpfsync_maxupd(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct pfsyncreq pfsyncr;
+       uint8_t maxupd;
+
+       if (!prop_dictionary_get_uint8(env, "maxupd", &maxupd)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       pfsync_get(env, &pfsyncr);
+
+       pfsyncr.pfsyncr_maxupdates = maxupd;
+
+       pfsync_set(env, &pfsyncr);
+       return 0;
+}
+
+
+/* ARGSUSED */
+int
+setpfsyncdev(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct pfsyncreq pfsyncr;
+       const char *dev;
+
+       if (!prop_dictionary_get_cstring_nocopy(env, "syncdev", &dev)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       pfsync_get(env, &pfsyncr);
+
+       strlcpy(pfsyncr.pfsyncr_syncdev, dev, sizeof(pfsyncr.pfsyncr_syncdev));
+
+       pfsync_set(env, &pfsyncr);
+       return 0;
+}
+
+/* ARGSUSED */
+int
+setpfsync_peer(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct pfsyncreq pfsyncr;
+       prop_data_t data;
+       const struct paddr_prefix *peerpfx;
+       const struct sockaddr_in *s;
+
+       data = (prop_data_t)prop_dictionary_get(env, "syncpeer");
+       if (data == NULL) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       pfsync_get(env, &pfsyncr);
+
+       peerpfx = prop_data_data_nocopy(data);
+
+       if (peerpfx != NULL) {
+               // Only AF_INET is supported for now
+               if (peerpfx->pfx_addr.sa_family != AF_INET) {
+                       errno = ENOENT;
+                       return -1;
+               }
+
+
+               s = (const struct sockaddr_in*)&peerpfx->pfx_addr;
+
+               memcpy(&pfsyncr.pfsyncr_syncpeer.s_addr, &s->sin_addr,
+                   MIN(sizeof(pfsyncr.pfsyncr_syncpeer.s_addr),
+                   peerpfx->pfx_addr.sa_len));    
+       } else {
+               memset(&pfsyncr.pfsyncr_syncpeer.s_addr, 0,
+                   sizeof(pfsyncr.pfsyncr_syncpeer.s_addr));
+       }
+
+       pfsync_set(env, &pfsyncr);
+
+       return 0;
+}
+
+static void
+pfsync_usage(prop_dictionary_t env)
+{
+       fprintf(stderr,
+           "\t[ maxupd n ] [ syncdev iface ] [syncpeer peer_addr]\n");
+}
+
+static void
+pfsync_constructor(void)
+{
+       cmdloop_branch_init(&branch, &pfsync.pk_parser);
+       register_cmdloop_branch(&branch);
+       status_func_init(&status, pfsync_status);
+       usage_func_init(&usage, pfsync_usage);
+       register_status(&status);
+       register_usage(&usage);
+}
diff --git a/sbin/ifconfig/prog_ops.h b/sbin/ifconfig/prog_ops.h
new file mode 100644 (file)
index 0000000..9d707ce
--- /dev/null
@@ -0,0 +1,61 @@
+/*      $NetBSD: prog_ops.h,v 1.3 2010/12/13 21:48:01 pooka Exp $      */
+
+/*
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROG_OPS_H_
+#define _PROG_OPS_H_
+
+#include <sys/types.h>
+
+/* ifconfig is compiled outside of src/sbin/ifconfig too */
+#ifndef CRUNCHOPS
+struct prog_ops {
+       int (*op_init)(void);
+
+       int (*op_socket)(int, int, int);
+
+       int (*op_ioctl)(int, unsigned long, ...);
+       ssize_t (*op_read)(int, void *, size_t);
+
+       int (*op_close)(int);
+};
+extern const struct prog_ops prog_ops;
+
+#define prog_init prog_ops.op_init
+#define prog_socket prog_ops.op_socket
+#define prog_ioctl prog_ops.op_ioctl
+#define prog_read prog_ops.op_read
+#define prog_close prog_ops.op_close
+#else
+#define prog_init ((int (*)(void))NULL)
+#define prog_socket socket
+#define prog_ioctl ioctl
+#define prog_read read
+#define prog_close close
+#endif
+
+#endif /* _PROG_OPS_H_ */
diff --git a/sbin/ifconfig/tunnel.c b/sbin/ifconfig/tunnel.c
new file mode 100644 (file)
index 0000000..6d808fc
--- /dev/null
@@ -0,0 +1,204 @@
+/*     $NetBSD: tunnel.c,v 1.20 2013/10/19 15:59:15 christos Exp $     */
+
+/*
+ * Copyright (c) 1983, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: tunnel.c,v 1.20 2013/10/19 15:59:15 christos Exp $");
+#endif /* not lint */
+
+#include <sys/param.h> 
+#include <sys/ioctl.h> 
+#include <sys/socket.h>
+
+#include <net/if.h> 
+
+#ifdef INET6
+#include <netinet/in.h>
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <util.h>
+
+#include "env.h"
+#include "extern.h"
+#include "parse.h"
+#include "util.h"
+
+static status_func_t status;
+static usage_func_t usage;
+static cmdloop_branch_t branch;
+
+static void tunnel_constructor(void) __attribute__((constructor));
+static int settunnel(prop_dictionary_t, prop_dictionary_t);
+static int deletetunnel(prop_dictionary_t, prop_dictionary_t);
+static void tunnel_status(prop_dictionary_t, prop_dictionary_t);
+
+struct paddr tundst = PADDR_INITIALIZER(&tundst, "tundst", settunnel,
+    "tundst", NULL, NULL, NULL, &command_root.pb_parser);
+
+struct paddr tunsrc = PADDR_INITIALIZER(&tunsrc, "tunsrc", NULL,
+    "tunsrc", NULL, NULL, NULL, &tundst.pa_parser);
+
+static const struct kwinst tunnelkw[] = {
+         {.k_word = "deletetunnel", .k_exec = deletetunnel,
+          .k_nextparser = &command_root.pb_parser}
+       , {.k_word = "tunnel", .k_nextparser = &tunsrc.pa_parser}
+};
+
+struct pkw tunnel = PKW_INITIALIZER(&tunnel, "tunnel", NULL, NULL,
+    tunnelkw, __arraycount(tunnelkw), NULL);
+
+static int
+settunnel(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       const struct paddr_prefix *srcpfx, *dstpfx;
+       struct if_laddrreq req;
+       prop_data_t srcdata, dstdata;
+
+       srcdata = (prop_data_t)prop_dictionary_get(env, "tunsrc");
+       dstdata = (prop_data_t)prop_dictionary_get(env, "tundst");
+
+       if (srcdata == NULL || dstdata == NULL) {
+               warnx("%s.%d", __func__, __LINE__);
+               errno = ENOENT;
+               return -1;
+       }
+
+       srcpfx = prop_data_data_nocopy(srcdata);
+       dstpfx = prop_data_data_nocopy(dstdata);
+
+       if (srcpfx->pfx_addr.sa_family != dstpfx->pfx_addr.sa_family)
+               errx(EXIT_FAILURE,
+                   "source and destination address families do not match");
+
+       memset(&req, 0, sizeof(req));
+       memcpy(&req.addr, &srcpfx->pfx_addr,
+           MIN(sizeof(req.addr), srcpfx->pfx_addr.sa_len));
+       memcpy(&req.dstaddr, &dstpfx->pfx_addr,
+           MIN(sizeof(req.dstaddr), dstpfx->pfx_addr.sa_len));
+
+#ifdef INET6
+       if (req.addr.ss_family == AF_INET6) {
+               struct sockaddr_in6 *s6, *d;
+
+               s6 = (struct sockaddr_in6 *)&req.addr;
+               d = (struct sockaddr_in6 *)&req.dstaddr;
+               if (s6->sin6_scope_id != d->sin6_scope_id) {
+                       errx(EXIT_FAILURE, "scope mismatch");
+                       /* NOTREACHED */
+               }
+               if (IN6_IS_ADDR_MULTICAST(&d->sin6_addr) ||
+                   IN6_IS_ADDR_MULTICAST(&s6->sin6_addr))
+                       errx(EXIT_FAILURE, "tunnel src/dst is multicast");
+               /* embed scopeid */
+               inet6_putscopeid(s6, INET6_IS_ADDR_LINKLOCAL);
+               inet6_putscopeid(d, INET6_IS_ADDR_LINKLOCAL);
+       }
+#endif /* INET6 */
+
+       if (direct_ioctl(env, SIOCSLIFPHYADDR, &req) == -1)
+               warn("SIOCSLIFPHYADDR");
+       return 0;
+}
+
+static int
+deletetunnel(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       if (indirect_ioctl(env, SIOCDIFPHYADDR, NULL) == -1)
+               err(EXIT_FAILURE, "SIOCDIFPHYADDR");
+       return 0;
+}
+
+static void
+tunnel_status(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       char dstserv[sizeof(",65535")];
+       char srcserv[sizeof(",65535")];
+       char psrcaddr[NI_MAXHOST];
+       char pdstaddr[NI_MAXHOST];
+       const int niflag = Nflag ? 0 : (NI_NUMERICHOST|NI_NUMERICSERV);
+       struct if_laddrreq req;
+       const struct afswtch *afp;
+
+       psrcaddr[0] = pdstaddr[0] = '\0';
+
+       memset(&req, 0, sizeof(req));
+       if (direct_ioctl(env, SIOCGLIFPHYADDR, &req) == -1)
+               return;
+       afp = lookup_af_bynum(req.addr.ss_family);
+#ifdef INET6
+       if (req.addr.ss_family == AF_INET6)
+               inet6_getscopeid((struct sockaddr_in6 *)&req.addr,
+                   INET6_IS_ADDR_LINKLOCAL);
+#endif /* INET6 */
+       getnameinfo((struct sockaddr *)&req.addr, req.addr.ss_len,
+           psrcaddr, sizeof(psrcaddr), &srcserv[1], sizeof(srcserv) - 1,
+           niflag);
+
+#ifdef INET6
+       if (req.dstaddr.ss_family == AF_INET6)
+               inet6_getscopeid((struct sockaddr_in6 *)&req.dstaddr,
+                   INET6_IS_ADDR_LINKLOCAL);
+#endif
+       getnameinfo((struct sockaddr *)&req.dstaddr, req.dstaddr.ss_len,
+           pdstaddr, sizeof(pdstaddr), &dstserv[1], sizeof(dstserv) - 1,
+           niflag);
+
+       srcserv[0] = (strcmp(&srcserv[1], "0") == 0) ? '\0' : ',';
+       dstserv[0] = (strcmp(&dstserv[1], "0") == 0) ? '\0' : ',';
+
+       printf("\ttunnel %s %s%s --> %s%s\n", afp ? afp->af_name : "???",
+           psrcaddr, srcserv, pdstaddr, dstserv);
+}
+
+static void
+tunnel_usage(prop_dictionary_t env)
+{
+       fprintf(stderr,
+           "\t[ [ af ] tunnel src_addr dest_addr ] [ deletetunnel ]\n");
+}
+
+static void
+tunnel_constructor(void)
+{
+       cmdloop_branch_init(&branch, &tunnel.pk_parser);
+       register_cmdloop_branch(&branch);
+       status_func_init(&status, tunnel_status);
+       usage_func_init(&usage, tunnel_usage);
+       register_status(&status);
+       register_usage(&usage);
+}
diff --git a/sbin/ifconfig/util.c b/sbin/ifconfig/util.c
new file mode 100644 (file)
index 0000000..5bba576
--- /dev/null
@@ -0,0 +1,346 @@
+/*     $NetBSD: util.c,v 1.17 2013/10/19 00:35:30 christos Exp $       */
+
+/*-
+ * Copyright (c) 2008 David Young.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: util.c,v 1.17 2013/10/19 00:35:30 christos Exp $");
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ifaddrs.h>
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>                /* XXX */
+
+#include "env.h"
+#include "extern.h"
+#include "util.h"
+#include "prog_ops.h"
+
+int
+getsock(int naf)
+{
+       static int oaf = -1, s;
+
+       if (oaf == naf || (oaf != -1 && naf == AF_UNSPEC))
+               return s;
+
+       if (oaf != -1)
+               prog_close(s);
+
+       if (naf == AF_UNSPEC)
+               naf = AF_INET;
+
+       s = prog_socket(naf, SOCK_DGRAM, 0);
+       if (s == -1)
+               oaf = -1;
+       else
+               oaf = naf;
+       return s;
+}
+
+const char *
+get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp,
+    bool hexok)
+{
+       int len;
+       bool hexstr;
+       u_int8_t *p;
+
+       len = *lenp;
+       p = buf;
+       hexstr = hexok && val[0] == '0' && tolower((u_char)val[1]) == 'x';
+       if (hexstr)
+               val += 2;
+       for (;;) {
+               if (*val == '\0')
+                       break;
+               if (sep != NULL && strchr(sep, *val) != NULL) {
+                       val++;
+                       break;
+               }
+               if (hexstr) {
+                       if (!isxdigit((u_char)val[0]) ||
+                           !isxdigit((u_char)val[1])) {
+                               warnx("bad hexadecimal digits");
+                               return NULL;
+                       }
+               }
+               if (p >= buf + len) {
+                       if (hexstr)
+                               warnx("hexadecimal digits too long");
+                       else
+                               warnx("strings too long");
+                       return NULL;
+               }
+               if (hexstr) {
+#define        tohex(x)        (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
+                       *p++ = (tohex((u_char)val[0]) << 4) |
+                           tohex((u_char)val[1]);
+#undef tohex
+                       val += 2;
+               } else
+                       *p++ = *val++;
+       }
+       len = p - buf;
+       if (len < *lenp)
+               memset(p, 0, *lenp - len);
+       *lenp = len;
+       return val;
+}
+
+void
+print_string(const u_int8_t *buf, int len)
+{
+       int i;
+       bool hasspc;
+
+       i = 0;
+       hasspc = false;
+       if (len < 2 || buf[0] != '0' || tolower(buf[1]) != 'x') {
+               for (; i < len; i++) {
+                       if (!isprint(buf[i]))
+                               break;
+                       if (isspace(buf[i]))
+                               hasspc = true;
+               }
+       }
+       if (i == len) {
+               if (hasspc || len == 0)
+                       printf("\"%.*s\"", len, buf);
+               else
+                       printf("%.*s", len, buf);
+       } else {
+               printf("0x");
+               for (i = 0; i < len; i++)
+                       printf("%02x", buf[i]);
+       }
+}
+
+struct paddr_prefix *
+prefixlen_to_mask(int af, int plen)
+{
+       union {
+               struct sockaddr sa; 
+               struct sockaddr_in sin; 
+               struct sockaddr_in6 sin6; 
+       } u;
+       struct paddr_prefix *pfx;
+       size_t addrlen;
+       uint8_t *addr;
+       int nbit;
+
+       memset(&u, 0, sizeof(u));
+
+       switch (af) {
+       case AF_INET:
+               addrlen = sizeof(u.sin.sin_addr);
+               addr = (uint8_t *)&u.sin.sin_addr;
+               u.sa.sa_len = sizeof(u.sin);
+               break;
+       case AF_INET6:
+               addrlen = sizeof(u.sin6.sin6_addr);
+               addr = (uint8_t *)&u.sin6.sin6_addr;
+               u.sa.sa_len = sizeof(u.sin6);
+               break;
+       default:
+               errno = EINVAL;
+               return NULL;
+       }
+       u.sa.sa_family = af;
+
+       if (plen < 0 || (size_t)plen > addrlen * NBBY) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       if (plen == 0)
+               plen = addrlen * NBBY;
+
+       memset(addr, 0xff, (plen + NBBY - 1) / NBBY);
+
+       nbit = plen % NBBY;
+       if (nbit != 0)
+               addr[plen / NBBY] &= ~((uint8_t)0xff >> nbit);
+       pfx = malloc(offsetof(struct paddr_prefix, pfx_addr) + u.sa.sa_len);
+       if (pfx == NULL)
+               return NULL;
+       pfx->pfx_len = plen;
+       memcpy(&pfx->pfx_addr, &u.sa, u.sa.sa_len);
+
+       return pfx;
+}
+
+int
+direct_ioctl(prop_dictionary_t env, unsigned long cmd, void *data)
+{
+       const char *ifname;
+       int s;
+
+       if ((s = getsock(AF_UNSPEC)) == -1)
+               err(EXIT_FAILURE, "getsock");
+
+       if ((ifname = getifname(env)) == NULL)
+               err(EXIT_FAILURE, "getifname");
+
+       estrlcpy(data, ifname, IFNAMSIZ);
+
+       return prog_ioctl(s, cmd, data);
+}
+
+int
+indirect_ioctl(prop_dictionary_t env, unsigned long cmd, void *data)
+{
+       struct ifreq ifr;
+
+       memset(&ifr, 0, sizeof(ifr));
+
+       ifr.ifr_data = data;
+
+       return direct_ioctl(env, cmd, &ifr);
+}
+
+void
+print_link_addresses(prop_dictionary_t env, bool print_active_only)
+{
+       char hbuf[NI_MAXHOST];
+       const char *ifname;
+       int s;
+       struct ifaddrs *ifa, *ifap;
+       const struct sockaddr_dl *sdl;
+       struct if_laddrreq iflr;
+
+       if ((ifname = getifname(env)) == NULL)
+               err(EXIT_FAILURE, "%s: getifname", __func__);
+
+       if ((s = getsock(AF_LINK)) == -1)
+               err(EXIT_FAILURE, "%s: getsock", __func__);
+
+       if (getifaddrs(&ifap) == -1)
+               err(EXIT_FAILURE, "%s: getifaddrs", __func__);
+
+       memset(&iflr, 0, sizeof(iflr));
+
+       strlcpy(iflr.iflr_name, ifname, sizeof(iflr.iflr_name));
+
+       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+               if (strcmp(ifname, ifa->ifa_name) != 0)
+                       continue;
+               if (ifa->ifa_addr->sa_family != AF_LINK)
+                       continue;
+
+               sdl = satocsdl(ifa->ifa_addr);
+
+               memcpy(&iflr.addr, ifa->ifa_addr, MIN(ifa->ifa_addr->sa_len,
+                   sizeof(iflr.addr)));
+               iflr.flags = IFLR_PREFIX;
+               iflr.prefixlen = sdl->sdl_alen * NBBY;
+
+               if (prog_ioctl(s, SIOCGLIFADDR, &iflr) == -1)
+                       err(EXIT_FAILURE, "%s: ioctl", __func__);
+
+               if (((iflr.flags & IFLR_ACTIVE) != 0) != print_active_only)
+                       continue;
+
+               if (getnameinfo(ifa->ifa_addr, ifa->ifa_addr->sa_len,
+                       hbuf, sizeof(hbuf), NULL, 0,
+                       Nflag ? 0 : NI_NUMERICHOST) == 0 &&
+                   hbuf[0] != '\0') {
+                       printf("\t%s %s\n",
+                           print_active_only ? "address:" : "link", hbuf);
+               }
+       }
+       freeifaddrs(ifap);
+}
+
+int16_t
+ifa_get_preference(const char *ifname, const struct sockaddr *sa)
+{
+       struct if_addrprefreq ifap;
+       int s;
+
+       if ((s = getsock(sa->sa_family)) == -1) {
+               if (errno == EPROTONOSUPPORT)
+                       return 0;
+               err(EXIT_FAILURE, "socket");
+       }
+       memset(&ifap, 0, sizeof(ifap));
+       estrlcpy(ifap.ifap_name, ifname, sizeof(ifap.ifap_name));
+       memcpy(&ifap.ifap_addr, sa, MIN(sizeof(ifap.ifap_addr), sa->sa_len));
+       if (prog_ioctl(s, SIOCGIFADDRPREF, &ifap) == -1) {
+               if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT)
+                       return 0;
+               warn("SIOCGIFADDRPREF");
+       }
+       return ifap.ifap_preference;
+}
+
+void
+ifa_print_preference(const char *ifname, const struct sockaddr *sa)
+{
+       int16_t preference;
+
+       if (lflag)
+               return;
+
+       preference = ifa_get_preference(ifname, sa);
+       printf(" preference %" PRId16, preference);
+}
+
+bool
+ifa_any_preferences(const char *ifname, struct ifaddrs *ifap, int family)
+{
+       struct ifaddrs *ifa;
+
+       /* Print address preference numbers if any address has a non-zero
+        * preference assigned.
+        */
+       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+               if (strcmp(ifname, ifa->ifa_name) != 0)
+                       continue;
+               if (ifa->ifa_addr->sa_family != family)
+                       continue;
+               if (ifa_get_preference(ifa->ifa_name, ifa->ifa_addr) != 0)
+                       return true;
+       }
+       return false;
+}
diff --git a/sbin/ifconfig/util.h b/sbin/ifconfig/util.h
new file mode 100644 (file)
index 0000000..ed8e556
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _IFCONFIG_UTIL_H
+#define _IFCONFIG_UTIL_H
+
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ifaddrs.h>
+
+#include "parse.h"
+
+struct afswtch {
+       const char *af_name;
+       short af_af;
+       void (*af_status)(prop_dictionary_t, prop_dictionary_t, bool);
+       void (*af_addr_commit)(prop_dictionary_t, prop_dictionary_t);
+       bool (*af_addr_tentative)(struct ifaddrs *);
+       SIMPLEQ_ENTRY(afswtch)  af_next;
+};
+
+void print_link_addresses(prop_dictionary_t, bool);
+const char *get_string(const char *, const char *, u_int8_t *, int *, bool);
+const struct afswtch *lookup_af_byname(const char *);
+const struct afswtch *lookup_af_bynum(int);
+void   print_string(const u_int8_t *, int);
+int    getsock(int);
+struct paddr_prefix *prefixlen_to_mask(int, int);
+int direct_ioctl(prop_dictionary_t, unsigned long, void *);
+int indirect_ioctl(prop_dictionary_t, unsigned long, void *);
+bool ifa_any_preferences(const char *, struct ifaddrs *, int);
+void ifa_print_preference(const char *, const struct sockaddr *);
+int16_t ifa_get_preference(const char *, const struct sockaddr *);
+
+#endif /* _IFCONFIG_UTIL_H */
diff --git a/sbin/ifconfig/vlan.c b/sbin/ifconfig/vlan.c
new file mode 100644 (file)
index 0000000..a5ddbcf
--- /dev/null
@@ -0,0 +1,190 @@
+/*     $NetBSD: vlan.c,v 1.14 2014/09/15 06:46:04 ozaki-r Exp $        */
+
+/*
+ * Copyright (c) 1983, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: vlan.c,v 1.14 2014/09/15 06:46:04 ozaki-r Exp $");
+#endif /* not lint */
+
+#include <sys/param.h> 
+#include <sys/ioctl.h> 
+
+#include <net/if.h> 
+#include <net/if_ether.h>
+#include <net/if_vlanvar.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <util.h>
+
+#include "env.h"
+#include "extern.h"
+#include "util.h"
+
+static status_func_t status;
+static usage_func_t usage;
+static cmdloop_branch_t branch;
+
+static void vlan_constructor(void) __attribute__((constructor));
+static void vlan_status(prop_dictionary_t, prop_dictionary_t);
+
+static int setvlan(prop_dictionary_t, prop_dictionary_t);
+static int setvlanif(prop_dictionary_t, prop_dictionary_t);
+
+struct pinteger vlantag = PINTEGER_INITIALIZER1(&vlantag, "VLAN tag",
+    0, USHRT_MAX, 10, setvlan, "vlantag", &command_root.pb_parser);
+
+struct piface vlanif = PIFACE_INITIALIZER(&vlanif, "vlanif", setvlanif,
+    "vlanif", &command_root.pb_parser);
+
+static const struct kwinst vlankw[] = {
+         {.k_word = "vlan", .k_nextparser = &vlantag.pi_parser}
+       , {.k_word = "vlanif", .k_act = "vlantag",
+          .k_nextparser = &vlanif.pif_parser}
+       , {.k_word = "-vlanif", .k_key = "vlanif", .k_type = KW_T_STR,
+          .k_str = "", .k_exec = setvlanif}
+};
+
+struct pkw vlan = PKW_INITIALIZER(&vlan, "vlan", NULL, NULL,
+    vlankw, __arraycount(vlankw), NULL);
+
+static int
+checkifname(prop_dictionary_t env)
+{
+       const char *ifname;
+
+       if ((ifname = getifname(env)) == NULL)
+               return 1;
+
+       return strncmp(ifname, "vlan", 4) != 0 ||
+           !isdigit((unsigned char)ifname[4]);
+}
+
+static int
+getvlan(prop_dictionary_t env, struct vlanreq *vlr, bool quiet)
+{
+       memset(vlr, 0, sizeof(*vlr));
+
+       if (checkifname(env)) {
+               if (quiet)
+                       return -1;
+               errx(EXIT_FAILURE, "valid only with vlan(4) interfaces");
+       }
+
+       if (indirect_ioctl(env, SIOCGETVLAN, vlr) == -1)
+               return -1;
+
+       return 0;
+}
+
+int
+setvlan(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct vlanreq vlr;
+       int64_t tag;
+
+       if (getvlan(env, &vlr, false) == -1)
+               err(EXIT_FAILURE, "%s: getvlan", __func__);
+
+       if (!prop_dictionary_get_int64(env, "vlantag", &tag)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       vlr.vlr_tag = tag;
+
+       if (indirect_ioctl(env, SIOCSETVLAN, &vlr) == -1)
+               err(EXIT_FAILURE, "SIOCSETVLAN");
+       return 0;
+}
+
+int
+setvlanif(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct vlanreq vlr;
+       const char *parent;
+       int64_t tag;
+
+       if (getvlan(env, &vlr, false) == -1)
+               err(EXIT_FAILURE, "%s: getsock", __func__);
+
+       if (!prop_dictionary_get_cstring_nocopy(env, "vlanif", &parent)) {
+               errno = ENOENT;
+               return -1;
+       }
+       strlcpy(vlr.vlr_parent, parent, sizeof(vlr.vlr_parent));
+       if (strcmp(parent, "") == 0)
+               ;
+       else if (!prop_dictionary_get_int64(env, "vlantag", &tag)) {
+               errno = ENOENT;
+               return -1;
+       } else
+               vlr.vlr_tag = (unsigned short)tag;
+
+       if (indirect_ioctl(env, SIOCSETVLAN, &vlr) == -1)
+               err(EXIT_FAILURE, "SIOCSETVLAN");
+       return 0;
+}
+
+static void
+vlan_status(prop_dictionary_t env, prop_dictionary_t oenv)
+{
+       struct vlanreq vlr;
+
+       if (getvlan(env, &vlr, true) == -1)
+               return;
+
+       if (vlr.vlr_tag || vlr.vlr_parent[0] != '\0')
+               printf("\tvlan: %d parent: %s\n",
+                   vlr.vlr_tag, vlr.vlr_parent[0] == '\0' ?
+                   "<none>" : vlr.vlr_parent);
+}
+
+static void
+vlan_usage(prop_dictionary_t env)
+{
+       fprintf(stderr, "\t[ vlan n vlanif i ] [ -vlanif i ]\n");
+}
+
+static void
+vlan_constructor(void)
+{
+       cmdloop_branch_init(&branch, &vlan.pk_parser);
+       register_cmdloop_branch(&branch);
+       status_func_init(&status, vlan_status);
+       usage_func_init(&usage, vlan_usage);
+       register_status(&status);
+       register_usage(&usage);
+}