summaryrefslogtreecommitdiff
path: root/regress
diff options
context:
space:
mode:
authorAlexander Bluhm <bluhm@cvs.openbsd.org>2019-10-24 22:55:08 +0000
committerAlexander Bluhm <bluhm@cvs.openbsd.org>2019-10-24 22:55:08 +0000
commit3eb9ecd525f93e5f4e45b006bbf90e0accd86cea (patch)
tree83c4546f6d23847c8e10baba4d8ba23575d1c1cd /regress
parent1693d622944f63eeabcb6002aa07e8fc72a75751 (diff)
Test the old ioctl(2) for interface addresses SIOCSIFADDR,
SIOCSIFNETMASK, SIOCSIFDSTADDR, SIOCSIFBRDADDR, and SIOCGIFADDR. Implement ifaddr.c which is a stripped down ifconfig(8) converted to the old interface.
Diffstat (limited to 'regress')
-rw-r--r--regress/sbin/ifconfig/Makefile118
-rw-r--r--regress/sbin/ifconfig/ifaddr.c1905
2 files changed, 2014 insertions, 9 deletions
diff --git a/regress/sbin/ifconfig/Makefile b/regress/sbin/ifconfig/Makefile
index c7092d2f8ee..ec8212ad8af 100644
--- a/regress/sbin/ifconfig/Makefile
+++ b/regress/sbin/ifconfig/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.1 2019/10/16 20:13:52 bluhm Exp $
+# $OpenBSD: Makefile,v 1.2 2019/10/24 22:55:07 bluhm Exp $
# Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org>
#
@@ -17,6 +17,7 @@
# test ifconfig address configuration for ethernet and point-to-point
IFCONFIG ?= ${SUDO} ${KTRACE} /sbin/ifconfig
+IFADDR = ${SUDO} ${KTRACE} ./ifaddr
ETHER_IF ?= vether99
ETHER_ADDR ?= 10.188.254.74
@@ -26,6 +27,8 @@ PPP_ADDR ?= 10.188.253.74
PPP_DEST ?= 10.188.253.75
PPP_NET = ${PPP_ADDR:C/\.[0-9][0-9]*$//}
+PROG = ifaddr
+
CLEANFILES = ifconfig.out ktrace.out
### ether
@@ -88,7 +91,7 @@ run-ether-duplicate:
${IFCONFIG} ${ETHER_IF} ${ETHER_NET}.1/24
/sbin/ifconfig ${ETHER_IF} >ifconfig.out
grep 'inet ${ETHER_NET}.1 ' ifconfig.out
- grep -c 'inet ${ETHER_NET}.1 ' ifconfig.out | grep 1
+ grep -c 'inet ' ifconfig.out | grep -q 1
REGRESS_TARGETS += run-ether-host
run-ether-host:
@@ -97,7 +100,7 @@ run-ether-host:
${IFCONFIG} ${ETHER_IF} ${ETHER_NET}.1/32
/sbin/ifconfig ${ETHER_IF} >ifconfig.out
grep 'inet ${ETHER_NET}.1 netmask 0xffffffff$$' ifconfig.out
- grep -c 'inet ${ETHER_NET}.1 ' ifconfig.out | grep 1
+ grep -c 'inet ' ifconfig.out | grep -q 1
REGRESS_TARGETS += run-ether-alias
run-ether-alias:
@@ -115,7 +118,7 @@ run-ether-alias-duplicate:
${IFCONFIG} ${ETHER_IF} ${ETHER_NET}.1/24 alias
/sbin/ifconfig ${ETHER_IF} >ifconfig.out
grep 'inet ${ETHER_NET}.1 ' ifconfig.out
- grep -c 'inet ${ETHER_NET}.1 ' ifconfig.out | grep 1
+ grep -c 'inet ' ifconfig.out | grep -q 1
REGRESS_TARGETS += run-ether-replace-first
run-ether-replace-first:
@@ -135,7 +138,7 @@ run-ether-alias-host:
${IFCONFIG} ${ETHER_IF} ${ETHER_NET}.1/32 alias
/sbin/ifconfig ${ETHER_IF} >ifconfig.out
grep 'inet ${ETHER_NET}.1 netmask 0xffffffff$$' ifconfig.out
- grep -c 'inet ${ETHER_NET}.1 ' ifconfig.out | grep 1
+ grep -c 'inet ' ifconfig.out | grep -q 1
REGRESS_TARGETS += run-ether-change-netmask
run-ether-change-netmask:
@@ -145,20 +148,20 @@ run-ether-change-netmask:
${IFCONFIG} ${ETHER_IF} ${ETHER_NET}.1/32
/sbin/ifconfig ${ETHER_IF} >ifconfig.out
grep 'inet ${ETHER_NET}.1 netmask 0xffffffff$$' ifconfig.out
- grep -c 'inet ${ETHER_NET}.1 ' ifconfig.out | grep 1
grep 'inet ${ETHER_NET}.2 ' ifconfig.out
+ grep -c 'inet ' ifconfig.out | grep -q 2
REGRESS_TARGETS += run-ether-delete-netmask
run-ether-delete-netmask:
@echo '======== $@ ========'
${IFCONFIG} ${ETHER_IF} ${ETHER_NET}.1/24
${IFCONFIG} ${ETHER_IF} ${ETHER_NET}.2/24 alias
- # ifconfig deletes .1 and changes .2 netmask
+ # XXX ifconfig deletes .1 and changes .2 netmask
${IFCONFIG} ${ETHER_IF} ${ETHER_NET}.2/32
/sbin/ifconfig ${ETHER_IF} >ifconfig.out
! grep 'inet ${ETHER_NET}.1 ' ifconfig.out
grep 'inet ${ETHER_NET}.2 netmask 0xffffffff$$' ifconfig.out
- grep -c 'inet ${ETHER_NET}.2 ' ifconfig.out | grep 1
+ grep -c 'inet ' ifconfig.out | grep -q 1
REGRESS_TARGETS += run-ether-alias-netmask
run-ether-alias-netmask:
@@ -169,7 +172,7 @@ run-ether-alias-netmask:
/sbin/ifconfig ${ETHER_IF} >ifconfig.out
grep 'inet ${ETHER_NET}.1 ' ifconfig.out
grep 'inet ${ETHER_NET}.2 netmask 0xffffffff$$' ifconfig.out
- grep -c 'inet ${ETHER_NET}.2 ' ifconfig.out | grep 1
+ grep -c 'inet ${ETHER_NET}.2 ' ifconfig.out | grep -q 1
REGRESS_TARGETS += run-ether-delete
run-ether-delete:
@@ -254,10 +257,107 @@ run-ppp-alias:
grep 'inet ${PPP_NET}.1 --> ${PPP_DEST} ' ifconfig.out
grep 'inet ${PPP_NET}.2 --> ${PPP_DEST} ' ifconfig.out
+### ifaddr
+
+REGRESS_TARGETS += run-ether-ifaddr-set
+run-ether-ifaddr-set:
+ @echo '======== $@ ========'
+ ${IFADDR} ${ETHER_IF} ${ETHER_ADDR}
+ /sbin/ifconfig ${ETHER_IF} >ifconfig.out
+ grep 'inet ${ETHER_ADDR} ' ifconfig.out
+
+REGRESS_TARGETS += run-ether-ifaddr-get
+run-ether-ifaddr-get:
+ @echo '======== $@ ========'
+ ${IFADDR} ${ETHER_IF} ${ETHER_ADDR}
+ ${KTRACE} ./ifaddr ${ETHER_IF} >ifconfig.out
+ grep 'inet ${ETHER_ADDR} ' ifconfig.out
+
+REGRESS_TARGETS += run-ether-ifaddr-netmask
+run-ether-ifaddr-netmask:
+ @echo '======== $@ ========'
+ ${IFADDR} ${ETHER_IF} ${ETHER_ADDR} netmask 255.255.255.0
+ /sbin/ifconfig ${ETHER_IF} >ifconfig.out
+ grep 'inet ${ETHER_ADDR} netmask 0xffffff00 ' ifconfig.out
+
+REGRESS_TARGETS += run-ether-ifaddr-prefixlen
+run-ether-ifaddr-prefixlen:
+ @echo '======== $@ ========'
+ ${IFADDR} ${ETHER_IF} ${ETHER_ADDR}/24
+ /sbin/ifconfig ${ETHER_IF} >ifconfig.out
+ grep 'inet ${ETHER_ADDR} netmask 0xffffff00 ' ifconfig.out
+
+REGRESS_TARGETS += run-ppp-ifaddr-destination
+run-ppp-ifaddr-destination:
+ @echo '======== $@ ========'
+ ${IFADDR} ${PPP_IF} ${PPP_ADDR} ${PPP_DEST}
+ /sbin/ifconfig ${PPP_IF} >ifconfig.out
+ grep 'inet ${PPP_ADDR} --> ${PPP_DEST} ' ifconfig.out
+
+REGRESS_TARGETS += run-ether-ifaddr-broadcast
+run-ether-ifaddr-broadcast:
+ @echo '======== $@ ========'
+ ${IFADDR} ${ETHER_IF} ${ETHER_ADDR} broadcast ${ETHER_NET}.255
+ /sbin/ifconfig ${ETHER_IF} >ifconfig.out
+ grep 'inet ${ETHER_ADDR} .* broadcast ${ETHER_NET}.255$$' ifconfig.out
+
+REGRESS_TARGETS += run-ether-ifaddr-alias
+run-ether-ifaddr-alias:
+ @echo '======== $@ ========'
+ ${IFADDR} ${ETHER_IF} ${ETHER_NET}.1/24
+ ${IFADDR} ${ETHER_IF} ${ETHER_NET}.2/24 alias
+ /sbin/ifconfig ${ETHER_IF} >ifconfig.out
+ grep 'inet ${ETHER_NET}.1 ' ifconfig.out
+ grep 'inet ${ETHER_NET}.2 ' ifconfig.out
+
+REGRESS_TARGETS += run-ether-ifaddr-change-netmask
+run-ether-ifaddr-change-netmask:
+ @echo '======== $@ ========'
+ ${IFADDR} ${ETHER_IF} ${ETHER_NET}.1/24
+ ${IFADDR} ${ETHER_IF} ${ETHER_NET}.2/24 alias
+ ${IFADDR} ${ETHER_IF} netmask 255.255.255.255
+ /sbin/ifconfig ${ETHER_IF} >ifconfig.out
+ grep 'inet ${ETHER_NET}.1 netmask 0xffffffff ' ifconfig.out
+ grep 'inet ${ETHER_NET}.2 netmask 0xffffff00 ' ifconfig.out
+
+REGRESS_TARGETS += run-ppp-ifaddr-change-destination
+run-ppp-ifaddr-change-destination:
+ @echo '======== $@ ========'
+ ${IFADDR} ${PPP_IF} ${PPP_NET}.1 ${PPP_NET}.11
+ ${IFADDR} ${PPP_IF} ${PPP_NET}.2 ${PPP_NET}.12 alias
+ ${IFADDR} ${PPP_IF} ipdst ${PPP_NET}.13
+ /sbin/ifconfig ${PPP_IF} >ifconfig.out
+ grep 'inet ${PPP_NET}.1 --> ${PPP_NET}.13 ' ifconfig.out
+ grep 'inet ${PPP_NET}.2 --> ${PPP_NET}.12 ' ifconfig.out
+
+REGRESS_TARGETS += run-ether-ifaddr-change-broadcast
+run-ether-ifaddr-change-broadcast:
+ @echo '======== $@ ========'
+ ${IFADDR} ${ETHER_IF} ${ETHER_NET}.1/24 broadcast ${ETHER_NET}.255
+ ${IFADDR} ${ETHER_IF} ${ETHER_NET}.2/24 broadcast ${ETHER_NET}.255 alias
+ ${IFADDR} ${ETHER_IF} broadcast 255.255.255.255
+ /sbin/ifconfig ${ETHER_IF} >ifconfig.out
+ grep 'inet ${ETHER_NET}.1 .* broadcast 255.255.255.255$$' ifconfig.out
+ grep 'inet ${ETHER_NET}.2 .* broadcast ${ETHER_NET}.255$$' ifconfig.out
+
+REGRESS_TARGETS += run-ether-ifaddr-duplicate
+run-ether-ifaddr-duplicate:
+ @echo '======== $@ ========'
+ ${IFADDR} ${ETHER_IF} ${ETHER_NET}.1/24
+ ${IFADDR} ${ETHER_IF} ${ETHER_NET}.2/16 alias
+ # XXX replace the first address and create two identical addresses
+ ${IFADDR} ${ETHER_IF} ${ETHER_NET}.2/24
+ /sbin/ifconfig ${ETHER_IF} >ifconfig.out
+ ! grep 'inet ${ETHER_NET}.1 ' ifconfig.out
+ grep 'inet ${ETHER_NET}.2 netmask 0xffffff00 ' ifconfig.out
+ grep -c 'inet ' ifconfig.out | grep -q 2
+
### setup cleanup
REGRESS_ROOT_TARGETS = ${REGRESS_TARGETS}
+${REGRESS_TARGETS:Mrun-*-ifaddr-*}: ifaddr
+
${REGRESS_TARGETS:Mrun-ether-*}: setup-ether
setup-ether:
@echo '======== $@ ========'
diff --git a/regress/sbin/ifconfig/ifaddr.c b/regress/sbin/ifconfig/ifaddr.c
new file mode 100644
index 00000000000..0cc223c7ab7
--- /dev/null
+++ b/regress/sbin/ifconfig/ifaddr.c
@@ -0,0 +1,1905 @@
+/* $OpenBSD: ifaddr.c,v 1.1 2019/10/24 22:55:07 bluhm Exp $ */
+
+/*
+ * This file has been copied from ifconfig and adapted to test
+ * SIOCSIFADDR, SIOCSIFNETMASK, SIOCSIFDSTADDR, SIOCSIFBRDADDR
+ * ioctls. Ususally ifconfig uses SIOCAIFADDR and SIOCDIFADDR, but
+ * the old kernel interface has to be tested, too.
+ */
+
+/*
+ * 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.
+ */
+
+/*-
+ * 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) 2019 Alexander Bluhm <bluhm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+#include <arpa/inet.h>
+#include <netinet/ip_ipsp.h>
+#include <netinet/if_ether.h>
+
+#include <netdb.h>
+
+#include <net/if_vlan_var.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <util.h>
+#include <ifaddrs.h>
+
+#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
+#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
+
+#define HWFEATURESBITS \
+ "\024\1CSUM_IPv4\2CSUM_TCPv4\3CSUM_UDPv4" \
+ "\5VLAN_MTU\6VLAN_HWTAGGING\10CSUM_TCPv6" \
+ "\11CSUM_UDPv6\20WOL"
+
+struct ifreq ifr, ridreq;
+struct in_aliasreq in_addreq;
+struct in6_ifreq ifr6;
+struct in6_ifreq in6_ridreq;
+struct in6_aliasreq in6_addreq;
+struct sockaddr_in netmask;
+
+char ifname[IFNAMSIZ];
+int flags, xflags, setaddr, setmask, setipdst, setbroad, doalias;
+u_long metric, mtu;
+int rdomainid;
+int llprio;
+int clearaddr, sock;
+int newaddr = 0;
+int af = AF_INET;
+int explicit_prefix = 0;
+int Lflag = 1;
+
+int showcapsflag;
+
+void notealias(const char *, int);
+void setifaddr(const char *, int);
+void setifrtlabel(const char *, int);
+void setifdstaddr(const char *, int);
+void addaf(const char *, int);
+void removeaf(const char *, int);
+void setifbroadaddr(const char *, int);
+void setifnetmask(const char *, int);
+void setifprefixlen(const char *, int);
+void settunnel(const char *, const char *);
+void settunneladdr(const char *, int);
+void deletetunnel(const char *, int);
+void settunnelinst(const char *, int);
+void unsettunnelinst(const char *, int);
+void settunnelttl(const char *, int);
+void setia6flags(const char *, int);
+void setia6pltime(const char *, int);
+void setia6vltime(const char *, int);
+void setia6lifetime(const char *, const char *);
+void setia6eui64(const char *, int);
+void setrdomain(const char *, int);
+void unsetrdomain(const char *, int);
+int prefix(void *val, int);
+int printgroup(char *, int);
+void setifipdst(const char *, int);
+void setignore(const char *, int);
+
+int actions; /* Actions performed */
+
+#define A_SILENT 0x8000000 /* doing operation, do not print */
+
+#define NEXTARG0 0xffffff
+#define NEXTARG 0xfffffe
+#define NEXTARG2 0xfffffd
+
+const struct cmd {
+ char *c_name;
+ int c_parameter; /* NEXTARG means next argv */
+ int c_action; /* defered action */
+ void (*c_func)(const char *, int);
+ void (*c_func2)(const char *, const char *);
+} cmds[] = {
+ { "alias", IFF_UP, 0, notealias },
+ { "-alias", -IFF_UP, 0, notealias },
+ { "delete", -IFF_UP, 0, notealias },
+ { "netmask", NEXTARG, 0, setifnetmask },
+ { "broadcast", NEXTARG, 0, setifbroadaddr },
+ { "prefixlen", NEXTARG, 0, setifprefixlen},
+ { "anycast", IN6_IFF_ANYCAST, 0, setia6flags },
+ { "-anycast", -IN6_IFF_ANYCAST, 0, setia6flags },
+ { "tentative", IN6_IFF_TENTATIVE, 0, setia6flags },
+ { "-tentative", -IN6_IFF_TENTATIVE, 0, setia6flags },
+ { "pltime", NEXTARG, 0, setia6pltime },
+ { "vltime", NEXTARG, 0, setia6vltime },
+ { "eui64", 0, 0, setia6eui64 },
+#ifndef SMALL
+ { "rtlabel", NEXTARG, 0, setifrtlabel },
+ { "-rtlabel", -1, 0, setifrtlabel },
+ { "rdomain", NEXTARG, 0, setrdomain },
+ { "-rdomain", 0, 0, unsetrdomain },
+ { "tunnel", NEXTARG2, 0, NULL, settunnel },
+ { "tunneladdr", NEXTARG, 0, settunneladdr },
+ { "-tunnel", 0, 0, deletetunnel },
+ /* deletetunnel is for backward compat, remove during 6.4-current */
+ { "deletetunnel", 0, 0, deletetunnel },
+ { "tunneldomain", NEXTARG, 0, settunnelinst },
+ { "-tunneldomain", 0, 0, unsettunnelinst },
+ { "tunnelttl", NEXTARG, 0, settunnelttl },
+ { "-inet", AF_INET, 0, removeaf },
+ { "-inet6", AF_INET6, 0, removeaf },
+ { "ipdst", NEXTARG, 0, setifipdst },
+#endif /* SMALL */
+ { NULL, /*src*/ 0, 0, setifaddr },
+ { NULL, /*dst*/ 0, 0, setifdstaddr },
+ { NULL, /*illegal*/0, 0, NULL },
+};
+
+#define IFFBITS \
+ "\024\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6STATICARP" \
+ "\7RUNNING\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX" \
+ "\15LINK0\16LINK1\17LINK2\20MULTICAST" \
+ "\23INET6_NOPRIVACY\24MPLS\25WOL\26AUTOCONF6\27INET6_NOSOII" \
+ "\30AUTOCONF4"
+
+int getinfo(struct ifreq *, int);
+void getsock(int);
+void printif(char *, int);
+void printb(char *, unsigned int, unsigned char *);
+void printb_status(unsigned short, unsigned char *);
+const char *get_linkstate(int, int);
+void status(int, struct sockaddr_dl *, int);
+__dead void usage(void);
+const char *get_string(const char *, const char *, u_int8_t *, int *);
+int len_string(const u_int8_t *, int);
+int print_string(const u_int8_t *, int);
+char *sec2str(time_t);
+
+unsigned long get_ts_map(int, int, int);
+
+void in_status(int);
+void in_getaddr(const char *, int);
+void in_getprefix(const char *, int);
+void in6_fillscopeid(struct sockaddr_in6 *);
+void in6_alias(struct in6_ifreq *);
+void in6_status(int);
+void in6_getaddr(const char *, int);
+void in6_getprefix(const char *, int);
+
+/* Known address families */
+const struct afswtch {
+ char *af_name;
+ short af_af;
+ void (*af_status)(int);
+ void (*af_getaddr)(const char *, int);
+ void (*af_getprefix)(const char *, int);
+ u_long af_difaddr;
+ u_long af_aifaddr;
+ caddr_t af_ridreq;
+ caddr_t af_addreq;
+} afs[] = {
+#define C(x) ((caddr_t) &x)
+ { "inet", AF_INET, in_status, in_getaddr, in_getprefix,
+ SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(in_addreq) },
+ { "inet6", AF_INET6, in6_status, in6_getaddr, in6_getprefix,
+ SIOCDIFADDR_IN6, SIOCAIFADDR_IN6, C(in6_ridreq), C(in6_addreq) },
+ { 0, 0, 0, 0 }
+};
+
+const struct afswtch *afp; /*the address family being set or asked about*/
+
+int ifaliases = 0;
+int aflag = 0;
+
+int
+main(int argc, char *argv[])
+{
+ const struct afswtch *rafp = NULL;
+ int create = 0;
+ int i;
+
+ /* If no args at all, print all interfaces. */
+ if (argc < 2) {
+ /* no filesystem visibility */
+ if (unveil("/", "") == -1)
+ err(1, "unveil");
+ if (unveil(NULL, NULL) == -1)
+ err(1, "unveil");
+ aflag = 1;
+ printif(NULL, 0);
+ return (0);
+ }
+ argc--, argv++;
+ if (*argv[0] == '-') {
+ int nomore = 0;
+
+ for (i = 1; argv[0][i]; i++) {
+ switch (argv[0][i]) {
+ case 'a':
+ aflag = 1;
+ nomore = 1;
+ break;
+ case 'A':
+ aflag = 1;
+ ifaliases = 1;
+ nomore = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ if (nomore == 0) {
+ argc--, argv++;
+ if (argc < 1)
+ usage();
+ if (strlcpy(ifname, *argv, sizeof(ifname)) >= IFNAMSIZ)
+ errx(1, "interface name '%s' too long", *argv);
+ }
+ } else if (strlcpy(ifname, *argv, sizeof(ifname)) >= IFNAMSIZ)
+ errx(1, "interface name '%s' too long", *argv);
+ argc--, argv++;
+
+ if (unveil("/etc/resolv.conf", "r") == -1)
+ err(1, "unveil");
+ if (unveil("/etc/hosts", "r") == -1)
+ err(1, "unveil");
+ if (unveil("/etc/services", "r") == -1)
+ err(1, "unveil");
+ if (unveil(NULL, NULL) == -1)
+ err(1, "unveil");
+
+ if (argc > 0) {
+ for (afp = rafp = afs; rafp->af_name; rafp++)
+ if (strcmp(rafp->af_name, *argv) == 0) {
+ afp = rafp;
+ argc--;
+ argv++;
+ break;
+ }
+ rafp = afp;
+ af = ifr.ifr_addr.sa_family = rafp->af_af;
+ }
+ (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+
+ /* initialization */
+ in6_addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+ in6_addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+
+ if (aflag == 0) {
+ create = (argc > 0) && strcmp(argv[0], "destroy") != 0;
+ (void)getinfo(&ifr, create);
+ }
+
+ if (argc != 0 && af == AF_INET6)
+ addaf(ifname, AF_INET6);
+
+ while (argc > 0) {
+ const struct cmd *p;
+
+ for (p = cmds; p->c_name; p++)
+ if (strcmp(*argv, p->c_name) == 0)
+ break;
+ if (p->c_name == 0 && setaddr)
+ for (i = setaddr; i > 0; i--) {
+ p++;
+ if (p->c_func == NULL)
+ errx(1, "%s: bad value", *argv);
+ }
+ if (p->c_func || p->c_func2) {
+ if (p->c_parameter == NEXTARG0) {
+ const struct cmd *p0;
+ int noarg = 1;
+
+ if (argv[1]) {
+ for (p0 = cmds; p0->c_name; p0++)
+ if (strcmp(argv[1],
+ p0->c_name) == 0) {
+ noarg = 0;
+ break;
+ }
+ } else
+ noarg = 0;
+
+ if (noarg == 0)
+ (*p->c_func)(NULL, 0);
+ else
+ goto nextarg;
+ } else if (p->c_parameter == NEXTARG) {
+nextarg:
+ if (argv[1] == NULL)
+ errx(1, "'%s' requires argument",
+ p->c_name);
+ (*p->c_func)(argv[1], 0);
+ argc--, argv++;
+ actions = actions | A_SILENT | p->c_action;
+ } else if (p->c_parameter == NEXTARG2) {
+ if ((argv[1] == NULL) ||
+ (argv[2] == NULL))
+ errx(1, "'%s' requires 2 arguments",
+ p->c_name);
+ (*p->c_func2)(argv[1], argv[2]);
+ argc -= 2;
+ argv += 2;
+ actions = actions | A_SILENT | p->c_action;
+ } else {
+ (*p->c_func)(*argv, p->c_parameter);
+ actions = actions | A_SILENT | p->c_action;
+ }
+ }
+ argc--, argv++;
+ }
+
+ if (argc == 0 && actions == 0) {
+ printif(ifr.ifr_name, aflag ? ifaliases : 1);
+ return (0);
+ }
+
+ if (af == AF_INET6 && explicit_prefix == 0) {
+ /*
+ * Aggregatable address architecture defines all prefixes
+ * are 64. So, it is convenient to set prefixlen to 64 if
+ * it is not specified. If we are setting a destination
+ * address on a point-to-point interface, 128 is required.
+ */
+ if (setipdst && (flags & IFF_POINTOPOINT))
+ setifprefixlen("128", 0);
+ else
+ setifprefixlen("64", 0);
+ /* in6_getprefix("64", MASK) if MASK is available here... */
+ }
+
+ if (doalias == 0 || (newaddr && clearaddr)) {
+ (void) strlcpy(rafp->af_ridreq, ifname, sizeof(ifr.ifr_name));
+ /* IPv4 only, inet6 does not have such ioctls */
+ if (setaddr) {
+ memcpy(&ridreq.ifr_addr, &in_addreq.ifra_addr,
+ in_addreq.ifra_addr.sin_len);
+ if (ioctl(sock, SIOCSIFADDR, rafp->af_ridreq) == -1)
+ err(1, "SIOCSIFADDR");
+ }
+ if (setmask) {
+ memcpy(&ridreq.ifr_addr, &in_addreq.ifra_mask,
+ in_addreq.ifra_mask.sin_len);
+ if (ioctl(sock, SIOCSIFNETMASK, rafp->af_ridreq) == -1)
+ err(1, "SIOCSIFNETMASK");
+ }
+ if (setipdst) {
+ memcpy(&ridreq.ifr_addr, &in_addreq.ifra_dstaddr,
+ in_addreq.ifra_dstaddr.sin_len);
+ if (ioctl(sock, SIOCSIFDSTADDR, rafp->af_ridreq) == -1)
+ err(1, "SIOCSIFDSTADDR");
+ }
+ if (setbroad) {
+ memcpy(&ridreq.ifr_addr, &in_addreq.ifra_broadaddr,
+ in_addreq.ifra_broadaddr.sin_len);
+ if (ioctl(sock, SIOCSIFBRDADDR, rafp->af_ridreq) == -1)
+ err(1, "SIOCSIFBRDADDR");
+ }
+ return (0);
+ }
+ if (clearaddr) {
+ (void) strlcpy(rafp->af_ridreq, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sock, rafp->af_difaddr, rafp->af_ridreq) == -1) {
+ if (errno == EADDRNOTAVAIL && (doalias >= 0)) {
+ /* means no previous address for interface */
+ } else
+ err(1, "SIOCDIFADDR");
+ }
+ }
+ if (newaddr) {
+ (void) strlcpy(rafp->af_addreq, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sock, rafp->af_aifaddr, rafp->af_addreq) == -1)
+ err(1, "SIOCAIFADDR");
+ }
+ return (0);
+}
+
+void
+getsock(int naf)
+{
+ static int oaf = -1;
+
+ if (oaf == naf)
+ return;
+ if (oaf != -1)
+ close(sock);
+ sock = socket(naf, SOCK_DGRAM, 0);
+ if (sock == -1)
+ oaf = -1;
+ else
+ oaf = naf;
+}
+
+int
+getinfo(struct ifreq *ifr, int create)
+{
+
+ getsock(af);
+ if (sock == -1)
+ err(1, "socket");
+ if (!isdigit((unsigned char)ifname[strlen(ifname) - 1]))
+ return (-1); /* ignore groups here */
+ if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)ifr) == -1) {
+ int oerrno = errno;
+
+ if (!create)
+ return (-1);
+ if (ioctl(sock, SIOCIFCREATE, (caddr_t)ifr) == -1) {
+ errno = oerrno;
+ return (-1);
+ }
+ if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)ifr) == -1)
+ return (-1);
+ }
+ flags = ifr->ifr_flags & 0xffff;
+ if (ioctl(sock, SIOCGIFXFLAGS, (caddr_t)ifr) == -1)
+ ifr->ifr_flags = 0;
+ xflags = ifr->ifr_flags;
+ if (ioctl(sock, SIOCGIFMETRIC, (caddr_t)ifr) == -1)
+ metric = 0;
+ else
+ metric = ifr->ifr_metric;
+ if (ioctl(sock, SIOCGIFMTU, (caddr_t)ifr) == -1)
+ mtu = 0;
+ else
+ mtu = ifr->ifr_mtu;
+#ifndef SMALL
+ if (ioctl(sock, SIOCGIFRDOMAIN, (caddr_t)ifr) == -1)
+ rdomainid = 0;
+ else
+ rdomainid = ifr->ifr_rdomainid;
+#endif
+ if (ioctl(sock, SIOCGIFLLPRIO, (caddr_t)ifr) == -1)
+ llprio = 0;
+ else
+ llprio = ifr->ifr_llprio;
+
+ return (0);
+}
+
+int
+printgroup(char *groupname, int ifaliases)
+{
+ struct ifgroupreq ifgr;
+ struct ifg_req *ifg;
+ int len, cnt = 0;
+
+ getsock(AF_INET);
+ bzero(&ifgr, sizeof(ifgr));
+ strlcpy(ifgr.ifgr_name, groupname, sizeof(ifgr.ifgr_name));
+ if (ioctl(sock, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) {
+ if (errno == EINVAL || errno == ENOTTY ||
+ errno == ENOENT)
+ return (-1);
+ else
+ err(1, "SIOCGIFGMEMB");
+ }
+
+ len = ifgr.ifgr_len;
+ if ((ifgr.ifgr_groups = calloc(1, len)) == NULL)
+ err(1, "printgroup");
+ if (ioctl(sock, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1)
+ err(1, "SIOCGIFGMEMB");
+
+ for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req);
+ ifg++) {
+ len -= sizeof(struct ifg_req);
+ printif(ifg->ifgrq_member, ifaliases);
+ cnt++;
+ }
+ free(ifgr.ifgr_groups);
+
+ return (cnt);
+}
+
+void
+printif(char *name, int ifaliases)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct if_data *ifdata;
+ const char *namep;
+ char *oname = NULL;
+ struct ifreq *ifrp;
+ int count = 0, noinet = 1;
+ size_t nlen = 0;
+
+ if (aflag)
+ name = NULL;
+ if (name) {
+ if ((oname = strdup(name)) == NULL)
+ err(1, "strdup");
+ nlen = strlen(oname);
+ /* is it a group? */
+ if (nlen && !isdigit((unsigned char)oname[nlen - 1]))
+ if (printgroup(oname, ifaliases) != -1) {
+ free(oname);
+ return;
+ }
+ }
+
+ if (getifaddrs(&ifap) != 0)
+ err(1, "getifaddrs");
+
+ namep = NULL;
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (oname) {
+ if (nlen && isdigit((unsigned char)oname[nlen - 1])) {
+ /* must have exact match */
+ if (strcmp(oname, ifa->ifa_name) != 0)
+ continue;
+ } else {
+ /* partial match OK if it ends w/ digit */
+ if (strncmp(oname, ifa->ifa_name, nlen) != 0 ||
+ !isdigit((unsigned char)ifa->ifa_name[nlen]))
+ continue;
+ }
+ }
+ /* quickhack: sizeof(ifr) < sizeof(ifr6) */
+ if (ifa->ifa_addr->sa_family == AF_INET6) {
+ memset(&ifr6, 0, sizeof(ifr6));
+ memcpy(&ifr6.ifr_addr, ifa->ifa_addr,
+ MINIMUM(sizeof(ifr6.ifr_addr), ifa->ifa_addr->sa_len));
+ ifrp = (struct ifreq *)&ifr6;
+ } else {
+ memset(&ifr, 0, sizeof(ifr));
+ memcpy(&ifr.ifr_addr, ifa->ifa_addr,
+ MINIMUM(sizeof(ifr.ifr_addr), ifa->ifa_addr->sa_len));
+ ifrp = &ifr;
+ }
+ strlcpy(ifname, ifa->ifa_name, sizeof(ifname));
+ strlcpy(ifrp->ifr_name, ifa->ifa_name, sizeof(ifrp->ifr_name));
+
+ if (ifa->ifa_addr->sa_family == AF_LINK) {
+ namep = ifa->ifa_name;
+ if (getinfo(ifrp, 0) < 0)
+ continue;
+ ifdata = ifa->ifa_data;
+ status(1, (struct sockaddr_dl *)ifa->ifa_addr,
+ ifdata->ifi_link_state);
+ count++;
+ noinet = 1;
+ continue;
+ }
+
+ if (!namep || !strcmp(namep, ifa->ifa_name)) {
+ const struct afswtch *p;
+
+ if (ifa->ifa_addr->sa_family == AF_INET &&
+ ifaliases == 0 && noinet == 0)
+ continue;
+ if ((p = afp) != NULL) {
+ if (ifa->ifa_addr->sa_family == p->af_af)
+ p->af_status(1);
+ } else {
+ for (p = afs; p->af_name; p++) {
+ if (ifa->ifa_addr->sa_family ==
+ p->af_af)
+ p->af_status(0);
+ }
+ }
+ count++;
+ if (ifa->ifa_addr->sa_family == AF_INET)
+ noinet = 0;
+ continue;
+ }
+ }
+ freeifaddrs(ifap);
+ free(oname);
+ if (count == 0) {
+ fprintf(stderr, "%s: no such interface\n", ifname);
+ exit(1);
+ }
+}
+
+#define RIDADDR 0
+#define ADDR 1
+#define MASK 2
+#define DSTADDR 3
+
+/*ARGSUSED*/
+void
+setifaddr(const char *addr, int param)
+{
+ /*
+ * Delay the ioctl to set the interface addr until flags are all set.
+ * The address interpretation may depend on the flags,
+ * and the flags may change when the address is set.
+ */
+ setaddr++;
+ if (doalias >= 0)
+ newaddr = 1;
+ if (doalias == 0)
+ clearaddr = 1;
+ afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR));
+}
+
+#ifndef SMALL
+void
+setifrtlabel(const char *label, int d)
+{
+ if (d != 0)
+ ifr.ifr_data = (caddr_t)(const char *)"";
+ else
+ ifr.ifr_data = (caddr_t)label;
+ if (ioctl(sock, SIOCSIFRTLABEL, &ifr) == -1)
+ warn("SIOCSIFRTLABEL");
+}
+#endif
+
+/* ARGSUSED */
+void
+setifnetmask(const char *addr, int ignored)
+{
+ setmask++;
+ afp->af_getaddr(addr, MASK);
+}
+
+/* ARGSUSED */
+void
+setifbroadaddr(const char *addr, int ignored)
+{
+ setbroad++;
+ afp->af_getaddr(addr, DSTADDR);
+}
+
+/* ARGSUSED */
+void
+setifipdst(const char *addr, int ignored)
+{
+ in_getaddr(addr, DSTADDR);
+ setipdst++;
+ clearaddr = 0;
+ newaddr = 0;
+}
+
+#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr))
+/*ARGSUSED*/
+void
+notealias(const char *addr, int param)
+{
+ if (setaddr && doalias == 0 && param < 0)
+ memcpy(rqtosa(af_ridreq), rqtosa(af_addreq),
+ rqtosa(af_addreq)->sa_len);
+ doalias = param;
+ if (param < 0) {
+ clearaddr = 1;
+ newaddr = 0;
+ } else
+ clearaddr = 0;
+}
+
+/*ARGSUSED*/
+void
+setifdstaddr(const char *addr, int param)
+{
+ setaddr++;
+ setipdst++;
+ afp->af_getaddr(addr, DSTADDR);
+}
+
+void
+addaf(const char *vname, int value)
+{
+ struct if_afreq ifar;
+
+ strlcpy(ifar.ifar_name, ifname, sizeof(ifar.ifar_name));
+ ifar.ifar_af = value;
+ if (ioctl(sock, SIOCIFAFATTACH, (caddr_t)&ifar) == -1)
+ warn("SIOCIFAFATTACH");
+}
+
+void
+removeaf(const char *vname, int value)
+{
+ struct if_afreq ifar;
+
+ strlcpy(ifar.ifar_name, ifname, sizeof(ifar.ifar_name));
+ ifar.ifar_af = value;
+ if (ioctl(sock, SIOCIFAFDETACH, (caddr_t)&ifar) == -1)
+ warn("SIOCIFAFDETACH");
+}
+
+void
+setia6flags(const char *vname, int value)
+{
+
+ if (value < 0) {
+ value = -value;
+ in6_addreq.ifra_flags &= ~value;
+ } else
+ in6_addreq.ifra_flags |= value;
+}
+
+void
+setia6pltime(const char *val, int d)
+{
+
+ setia6lifetime("pltime", val);
+}
+
+void
+setia6vltime(const char *val, int d)
+{
+
+ setia6lifetime("vltime", val);
+}
+
+void
+setia6lifetime(const char *cmd, const char *val)
+{
+ const char *errmsg = NULL;
+ time_t newval, t;
+
+ newval = strtonum(val, 0, 1000000, &errmsg);
+ if (errmsg)
+ errx(1, "invalid %s %s: %s", cmd, val, errmsg);
+
+ t = time(NULL);
+
+ if (afp->af_af != AF_INET6)
+ errx(1, "%s not allowed for this address family", cmd);
+ if (strcmp(cmd, "vltime") == 0) {
+ in6_addreq.ifra_lifetime.ia6t_expire = t + newval;
+ in6_addreq.ifra_lifetime.ia6t_vltime = newval;
+ } else if (strcmp(cmd, "pltime") == 0) {
+ in6_addreq.ifra_lifetime.ia6t_preferred = t + newval;
+ in6_addreq.ifra_lifetime.ia6t_pltime = newval;
+ }
+}
+
+void
+setia6eui64(const char *cmd, int val)
+{
+ struct ifaddrs *ifap, *ifa;
+ const struct sockaddr_in6 *sin6 = NULL;
+ const struct in6_addr *lladdr = NULL;
+ struct in6_addr *in6;
+
+ if (afp->af_af != AF_INET6)
+ errx(1, "%s not allowed for this address family", cmd);
+
+ addaf(ifname, AF_INET6);
+
+ in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr;
+ if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0)
+ errx(1, "interface index is already filled");
+ if (getifaddrs(&ifap) != 0)
+ err(1, "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)
+ errx(1, "could not determine link local address");
+
+ memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8);
+
+ freeifaddrs(ifap);
+}
+
+const char *
+get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
+{
+ int len = *lenp, hexstr;
+ u_int8_t *p = buf;
+
+ hexstr = (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 {
+ if (*val == '\\' &&
+ sep != NULL && strchr(sep, *(val + 1)) != NULL)
+ val++;
+ *p++ = *val++;
+ }
+ }
+ len = p - buf;
+ if (len < *lenp)
+ memset(p, 0, *lenp - len);
+ *lenp = len;
+ return val;
+}
+
+int
+len_string(const u_int8_t *buf, int len)
+{
+ int i = 0, hasspc = 0;
+
+ if (len < 2 || buf[0] != '0' || tolower(buf[1]) != 'x') {
+ for (; i < len; i++) {
+ /* Only print 7-bit ASCII keys */
+ if (buf[i] & 0x80 || !isprint(buf[i]))
+ break;
+ if (isspace(buf[i]))
+ hasspc++;
+ }
+ }
+ if (i == len) {
+ if (hasspc || len == 0)
+ return len + 2;
+ else
+ return len;
+ } else
+ return (len * 2) + 2;
+}
+
+int
+print_string(const u_int8_t *buf, int len)
+{
+ int i = 0, hasspc = 0;
+
+ if (len < 2 || buf[0] != '0' || tolower(buf[1]) != 'x') {
+ for (; i < len; i++) {
+ /* Only print 7-bit ASCII keys */
+ if (buf[i] & 0x80 || !isprint(buf[i]))
+ break;
+ if (isspace(buf[i]))
+ hasspc++;
+ }
+ }
+ if (i == len) {
+ if (hasspc || len == 0) {
+ printf("\"%.*s\"", len, buf);
+ return len + 2;
+ } else {
+ printf("%.*s", len, buf);
+ return len;
+ }
+ } else {
+ printf("0x");
+ for (i = 0; i < len; i++)
+ printf("%02x", buf[i]);
+ return (len * 2) + 2;
+ }
+}
+
+static void
+print_tunnel(const struct if_laddrreq *req)
+{
+ char psrcaddr[NI_MAXHOST];
+ char pdstaddr[NI_MAXHOST];
+ const char *ver = "";
+ const int niflag = NI_NUMERICHOST;
+
+ if (req == NULL) {
+ printf("(unset)");
+ return;
+ }
+
+ psrcaddr[0] = pdstaddr[0] = '\0';
+
+ if (getnameinfo((struct sockaddr *)&req->addr, req->addr.ss_len,
+ psrcaddr, sizeof(psrcaddr), 0, 0, niflag) != 0)
+ strlcpy(psrcaddr, "<error>", sizeof(psrcaddr));
+ if (req->addr.ss_family == AF_INET6)
+ ver = "6";
+
+ printf("inet%s %s", ver, psrcaddr);
+
+ if (req->dstaddr.ss_family != AF_UNSPEC) {
+ in_port_t dstport = 0;
+ const struct sockaddr_in *sin;
+ const struct sockaddr_in6 *sin6;
+
+ if (getnameinfo((struct sockaddr *)&req->dstaddr,
+ req->dstaddr.ss_len, pdstaddr, sizeof(pdstaddr),
+ 0, 0, niflag) != 0)
+ strlcpy(pdstaddr, "<error>", sizeof(pdstaddr));
+
+ printf(" -> %s", pdstaddr);
+
+ switch (req->dstaddr.ss_family) {
+ case AF_INET:
+ sin = (const struct sockaddr_in *)&req->dstaddr;
+ dstport = sin->sin_port;
+ break;
+ case AF_INET6:
+ sin6 = (const struct sockaddr_in6 *)&req->dstaddr;
+ dstport = sin6->sin6_port;
+ break;
+ }
+
+ if (dstport)
+ printf(":%u", ntohs(dstport));
+ }
+}
+
+/* ARGSUSED */
+static void
+phys_status(int force)
+{
+ struct if_laddrreq req;
+ struct if_laddrreq *r = &req;
+
+ memset(&req, 0, sizeof(req));
+ (void) strlcpy(req.iflr_name, ifname, sizeof(req.iflr_name));
+ if (ioctl(sock, SIOCGLIFPHYADDR, (caddr_t)&req) == -1) {
+ if (errno != EADDRNOTAVAIL)
+ return;
+
+ r = NULL;
+ }
+
+ printf("\ttunnel: ");
+ print_tunnel(r);
+
+ if (ioctl(sock, SIOCGLIFPHYTTL, (caddr_t)&ifr) == 0) {
+ if (ifr.ifr_ttl == -1)
+ printf(" ttl copy");
+ else if (ifr.ifr_ttl > 0)
+ printf(" ttl %d", ifr.ifr_ttl);
+ }
+
+ if (ioctl(sock, SIOCGLIFPHYDF, (caddr_t)&ifr) == 0)
+ printf(" %s", ifr.ifr_df ? "df" : "nodf");
+
+#ifndef SMALL
+ if (ioctl(sock, SIOCGLIFPHYECN, (caddr_t)&ifr) == 0)
+ printf(" %s", ifr.ifr_metric ? "ecn" : "noecn");
+
+ if (ioctl(sock, SIOCGLIFPHYRTABLE, (caddr_t)&ifr) == 0 &&
+ (rdomainid != 0 || ifr.ifr_rdomainid != 0))
+ printf(" rdomain %d", ifr.ifr_rdomainid);
+#endif
+ printf("\n");
+}
+
+#ifndef SMALL
+const uint64_t ifm_status_valid_list[] = IFM_STATUS_VALID_LIST;
+
+const struct ifmedia_status_description ifm_status_descriptions[] =
+ IFM_STATUS_DESCRIPTIONS;
+#endif
+
+const struct if_status_description if_status_descriptions[] =
+ LINK_STATE_DESCRIPTIONS;
+
+const char *
+get_linkstate(int mt, int link_state)
+{
+ const struct if_status_description *p;
+ static char buf[8];
+
+ for (p = if_status_descriptions; p->ifs_string != NULL; p++) {
+ if (LINK_STATE_DESC_MATCH(p, mt, link_state))
+ return (p->ifs_string);
+ }
+ snprintf(buf, sizeof(buf), "[#%d]", link_state);
+ return buf;
+}
+
+/*
+ * Print the status of the interface. If an address family was
+ * specified, show it and it only; otherwise, show them all.
+ */
+void
+status(int link, struct sockaddr_dl *sdl, int ls)
+{
+ const struct afswtch *p = afp;
+ struct ifmediareq ifmr;
+#ifndef SMALL
+ struct ifreq ifrdesc;
+ char ifdescr[IFDESCRSIZE];
+#endif
+ uint64_t *media_list;
+ char sep;
+
+
+ printf("%s: ", ifname);
+ printb("flags", flags | (xflags << 16), IFFBITS);
+ if (rdomainid)
+ printf(" rdomain %d", rdomainid);
+ if (metric)
+ printf(" metric %lu", metric);
+ if (mtu)
+ printf(" mtu %lu", mtu);
+ putchar('\n');
+ if (sdl != NULL && sdl->sdl_alen &&
+ (sdl->sdl_type == IFT_ETHER || sdl->sdl_type == IFT_CARP))
+ (void)printf("\tlladdr %s\n", ether_ntoa(
+ (struct ether_addr *)LLADDR(sdl)));
+
+ sep = '\t';
+#ifndef SMALL
+ (void) memset(&ifrdesc, 0, sizeof(ifrdesc));
+ (void) strlcpy(ifrdesc.ifr_name, ifname, sizeof(ifrdesc.ifr_name));
+ ifrdesc.ifr_data = (caddr_t)&ifdescr;
+ if (ioctl(sock, SIOCGIFDESCR, &ifrdesc) == 0 &&
+ strlen(ifrdesc.ifr_data))
+ printf("\tdescription: %s\n", ifrdesc.ifr_data);
+
+ if (sdl != NULL) {
+ printf("%cindex %u", sep, sdl->sdl_index);
+ sep = ' ';
+ }
+ if (ioctl(sock, SIOCGIFPRIORITY, &ifrdesc) == 0) {
+ printf("%cpriority %d", sep, ifrdesc.ifr_metric);
+ sep = ' ';
+ }
+#endif
+ printf("%cllprio %d\n", sep, llprio);
+
+ (void) memset(&ifmr, 0, sizeof(ifmr));
+ (void) strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+
+ if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
+ /*
+ * Interface doesn't support SIOC{G,S}IFMEDIA.
+ */
+ if (ls != LINK_STATE_UNKNOWN)
+ printf("\tstatus: %s\n",
+ get_linkstate(sdl->sdl_type, ls));
+ goto proto_status;
+ }
+
+ if (ifmr.ifm_count == 0) {
+ warnx("%s: no media types?", ifname);
+ goto proto_status;
+ }
+
+ media_list = calloc(ifmr.ifm_count, sizeof(*media_list));
+ if (media_list == NULL)
+ err(1, "calloc");
+ ifmr.ifm_ulist = media_list;
+
+ if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1)
+ err(1, "SIOCGIFMEDIA");
+
+#ifdef SMALL
+ printf("\tstatus: %s\n", get_linkstate(sdl->sdl_type, ls));
+#else
+ if (ifmr.ifm_status & IFM_AVALID) {
+ 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");
+ putchar('\n');
+ }
+
+#endif
+ free(media_list);
+
+ proto_status:
+ if (link == 0) {
+ if ((p = afp) != NULL) {
+ p->af_status(1);
+ } else for (p = afs; p->af_name; p++) {
+ ifr.ifr_addr.sa_family = p->af_af;
+ p->af_status(0);
+ }
+ }
+
+ phys_status(0);
+}
+
+/* ARGSUSED */
+void
+in_status(int force)
+{
+ struct sockaddr_in *sin, sin2;
+
+ getsock(AF_INET);
+ if (sock == -1) {
+ if (errno == EPROTONOSUPPORT)
+ return;
+ err(1, "socket");
+ }
+ (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ sin = (struct sockaddr_in *)&ifr.ifr_addr;
+
+ /*
+ * We keep the interface address and reset it before each
+ * ioctl() so we can get ifaliases information (as opposed
+ * to the primary interface netmask/dstaddr/broadaddr, if
+ * the ifr_addr field is zero).
+ */
+ memcpy(&sin2, &ifr.ifr_addr, sizeof(sin2));
+
+ (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sock, SIOCGIFADDR, (caddr_t)&ifr) == -1) {
+ warn("SIOCGIFADDR");
+ memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
+ }
+ (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ printf("\tinet %s", inet_ntoa(sin->sin_addr));
+ memcpy(&ifr.ifr_addr, &sin2, sizeof(sin2));
+ if (ioctl(sock, SIOCGIFNETMASK, (caddr_t)&ifr) == -1) {
+ if (errno != EADDRNOTAVAIL)
+ warn("SIOCGIFNETMASK");
+ memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
+ } else
+ netmask.sin_addr =
+ ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
+ if (flags & IFF_POINTOPOINT) {
+ memcpy(&ifr.ifr_addr, &sin2, sizeof(sin2));
+ if (ioctl(sock, SIOCGIFDSTADDR, (caddr_t)&ifr) == -1) {
+ if (errno == EADDRNOTAVAIL)
+ memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
+ else
+ warn("SIOCGIFDSTADDR");
+ }
+ (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ sin = (struct sockaddr_in *)&ifr.ifr_dstaddr;
+ printf(" --> %s", inet_ntoa(sin->sin_addr));
+ }
+ printf(" netmask 0x%x", ntohl(netmask.sin_addr.s_addr));
+ if (flags & IFF_BROADCAST) {
+ memcpy(&ifr.ifr_addr, &sin2, sizeof(sin2));
+ if (ioctl(sock, SIOCGIFBRDADDR, (caddr_t)&ifr) == -1) {
+ if (errno == EADDRNOTAVAIL)
+ memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
+ else
+ warn("SIOCGIFBRDADDR");
+ }
+ (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ if (sin->sin_addr.s_addr != 0)
+ printf(" broadcast %s", inet_ntoa(sin->sin_addr));
+ }
+ putchar('\n');
+}
+
+/* ARGSUSED */
+void
+setifprefixlen(const char *addr, int d)
+{
+ setmask++;
+ if (afp->af_getprefix)
+ afp->af_getprefix(addr, MASK);
+ explicit_prefix = 1;
+}
+
+void
+in6_fillscopeid(struct sockaddr_in6 *sin6)
+{
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+ sin6->sin6_scope_id =
+ ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
+ sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0;
+ }
+#endif /* __KAME__ */
+}
+
+/* XXX not really an alias */
+void
+in6_alias(struct in6_ifreq *creq)
+{
+ struct sockaddr_in6 *sin6;
+ struct in6_ifreq ifr6; /* shadows file static variable */
+ u_int32_t scopeid;
+ char hbuf[NI_MAXHOST];
+ const int niflag = NI_NUMERICHOST;
+
+ /* Get the non-alias address for this interface. */
+ getsock(AF_INET6);
+ if (sock == -1) {
+ if (errno == EPROTONOSUPPORT)
+ return;
+ err(1, "socket");
+ }
+
+ sin6 = (struct sockaddr_in6 *)&creq->ifr_addr;
+
+ in6_fillscopeid(sin6);
+ scopeid = sin6->sin6_scope_id;
+ if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
+ hbuf, sizeof(hbuf), NULL, 0, niflag) != 0)
+ strlcpy(hbuf, "", sizeof hbuf);
+ printf("\tinet6 %s", hbuf);
+
+ if (flags & IFF_POINTOPOINT) {
+ (void) memset(&ifr6, 0, sizeof(ifr6));
+ (void) strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
+ ifr6.ifr_addr = creq->ifr_addr;
+ if (ioctl(sock, SIOCGIFDSTADDR_IN6, (caddr_t)&ifr6) == -1) {
+ if (errno != EADDRNOTAVAIL)
+ warn("SIOCGIFDSTADDR_IN6");
+ (void) 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 = (struct sockaddr_in6 *)&ifr6.ifr_addr;
+ in6_fillscopeid(sin6);
+ if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
+ hbuf, sizeof(hbuf), NULL, 0, niflag) != 0)
+ strlcpy(hbuf, "", sizeof hbuf);
+ printf(" -> %s", hbuf);
+ }
+
+ (void) memset(&ifr6, 0, sizeof(ifr6));
+ (void) strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
+ ifr6.ifr_addr = creq->ifr_addr;
+ if (ioctl(sock, SIOCGIFNETMASK_IN6, (caddr_t)&ifr6) == -1) {
+ if (errno != EADDRNOTAVAIL)
+ warn("SIOCGIFNETMASK_IN6");
+ } else {
+ sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr;
+ printf(" prefixlen %d", prefix(&sin6->sin6_addr,
+ sizeof(struct in6_addr)));
+ }
+
+ (void) memset(&ifr6, 0, sizeof(ifr6));
+ (void) strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
+ ifr6.ifr_addr = creq->ifr_addr;
+ if (ioctl(sock, SIOCGIFAFLAG_IN6, (caddr_t)&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_PRIVACY)
+ printf(" autoconfprivacy");
+ }
+
+ if (scopeid)
+ printf(" scopeid 0x%x", scopeid);
+
+ if (Lflag) {
+ struct in6_addrlifetime *lifetime;
+
+ (void) memset(&ifr6, 0, sizeof(ifr6));
+ (void) strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
+ ifr6.ifr_addr = creq->ifr_addr;
+ lifetime = &ifr6.ifr_ifru.ifru_lifetime;
+ if (ioctl(sock, SIOCGIFALIFETIME_IN6, (caddr_t)&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("%s", lifetime->ia6t_preferred < t
+ ? "0" :
+ sec2str(lifetime->ia6t_preferred - t));
+ } else
+ printf("infty");
+
+ printf(" vltime ");
+ if (lifetime->ia6t_expire) {
+ printf("%s", lifetime->ia6t_expire < t
+ ? "0"
+ : sec2str(lifetime->ia6t_expire - t));
+ } else
+ printf("infty");
+ }
+ }
+
+ printf("\n");
+}
+
+void
+in6_status(int force)
+{
+ in6_alias((struct in6_ifreq *)&ifr6);
+}
+
+#ifndef SMALL
+void
+settunnel(const char *src, const char *dst)
+{
+ char buf[HOST_NAME_MAX+1 + sizeof (":65535")], *dstport;
+ const char *dstip;
+ struct addrinfo *srcres, *dstres;
+ int ecode;
+ struct if_laddrreq req;
+
+ if (strchr(dst, ':') == NULL || strchr(dst, ':') != strrchr(dst, ':')) {
+ /* no port or IPv6 */
+ dstip = dst;
+ dstport = NULL;
+ } else {
+ if (strlcpy(buf, dst, sizeof(buf)) >= sizeof(buf))
+ errx(1, "%s bad value", dst);
+ dstport = strchr(buf, ':');
+ *dstport++ = '\0';
+ dstip = buf;
+ }
+
+ if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0)
+ errx(1, "error in parsing address string: %s",
+ gai_strerror(ecode));
+
+ if ((ecode = getaddrinfo(dstip, dstport, NULL, &dstres)) != 0)
+ errx(1, "error in parsing address string: %s",
+ gai_strerror(ecode));
+
+ if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family)
+ errx(1,
+ "source and destination address families do not match");
+
+ memset(&req, 0, sizeof(req));
+ (void) strlcpy(req.iflr_name, ifname, sizeof(req.iflr_name));
+ memcpy(&req.addr, srcres->ai_addr, srcres->ai_addrlen);
+ memcpy(&req.dstaddr, dstres->ai_addr, dstres->ai_addrlen);
+ if (ioctl(sock, SIOCSLIFPHYADDR, &req) == -1)
+ warn("SIOCSLIFPHYADDR");
+
+ freeaddrinfo(srcres);
+ freeaddrinfo(dstres);
+}
+
+void
+settunneladdr(const char *addr, int ignored)
+{
+ struct addrinfo hints, *res;
+ struct if_laddrreq req;
+ ssize_t len;
+ int rv;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = 0;
+ hints.ai_flags = AI_PASSIVE;
+
+ rv = getaddrinfo(addr, NULL, &hints, &res);
+ if (rv != 0)
+ errx(1, "tunneladdr %s: %s", addr, gai_strerror(rv));
+
+ memset(&req, 0, sizeof(req));
+ len = strlcpy(req.iflr_name, ifname, sizeof(req.iflr_name));
+ if (len >= sizeof(req.iflr_name))
+ errx(1, "%s: Interface name too long", ifname);
+
+ memcpy(&req.addr, res->ai_addr, res->ai_addrlen);
+
+ req.dstaddr.ss_len = 2;
+ req.dstaddr.ss_family = AF_UNSPEC;
+
+ if (ioctl(sock, SIOCSLIFPHYADDR, &req) == -1)
+ warn("tunneladdr %s", addr);
+
+ freeaddrinfo(res);
+}
+
+/* ARGSUSED */
+void
+deletetunnel(const char *ignored, int alsoignored)
+{
+ if (ioctl(sock, SIOCDIFPHYADDR, &ifr) == -1)
+ warn("SIOCDIFPHYADDR");
+}
+
+void
+settunnelinst(const char *id, int param)
+{
+ const char *errmsg = NULL;
+ int rdomainid;
+
+ rdomainid = strtonum(id, 0, RT_TABLEID_MAX, &errmsg);
+ if (errmsg)
+ errx(1, "rdomain %s: %s", id, errmsg);
+
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_rdomainid = rdomainid;
+ if (ioctl(sock, SIOCSLIFPHYRTABLE, (caddr_t)&ifr) == -1)
+ warn("SIOCSLIFPHYRTABLE");
+}
+
+void
+unsettunnelinst(const char *ignored, int alsoignored)
+{
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_rdomainid = 0;
+ if (ioctl(sock, SIOCSLIFPHYRTABLE, (caddr_t)&ifr) == -1)
+ warn("SIOCSLIFPHYRTABLE");
+}
+
+void
+settunnelttl(const char *id, int param)
+{
+ const char *errmsg = NULL;
+ int ttl;
+
+ if (strcmp(id, "copy") == 0)
+ ttl = -1;
+ else {
+ ttl = strtonum(id, 0, 0xff, &errmsg);
+ if (errmsg)
+ errx(1, "tunnelttl %s: %s", id, errmsg);
+ }
+
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_ttl = ttl;
+ if (ioctl(sock, SIOCSLIFPHYTTL, (caddr_t)&ifr) == -1)
+ warn("SIOCSLIFPHYTTL");
+}
+
+void
+utf16_to_char(uint16_t *in, int inlen, char *out, size_t outlen)
+{
+ uint16_t c;
+
+ while (outlen > 0) {
+ c = inlen > 0 ? letoh16(*in) : 0;
+ if (c == 0 || --outlen == 0) {
+ /* always NUL terminate result */
+ *out = '\0';
+ break;
+ }
+ *out++ = isascii(c) ? (char)c : '?';
+ in++;
+ inlen--;
+ }
+}
+
+int
+char_to_utf16(const char *in, uint16_t *out, size_t outlen)
+{
+ int n = 0;
+ uint16_t c;
+
+ for (;;) {
+ c = *in++;
+
+ if (c == '\0') {
+ /*
+ * NUL termination is not required, but zero out the
+ * residual buffer
+ */
+ memset(out, 0, outlen);
+ return n;
+ }
+ if (outlen < sizeof (*out))
+ return -1;
+
+ *out++ = htole16(c);
+ n += sizeof (*out);
+ outlen -= sizeof (*out);
+ }
+}
+
+#endif
+
+#define SIN(x) ((struct sockaddr_in *) &(x))
+struct sockaddr_in *sintab[] = {
+SIN(ridreq.ifr_addr), SIN(in_addreq.ifra_addr),
+SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr)};
+
+void
+in_getaddr(const char *s, int which)
+{
+ struct sockaddr_in *sin = sintab[which], tsin;
+ struct hostent *hp;
+ int bits, l;
+ char p[3];
+
+ bzero(&tsin, sizeof(tsin));
+ sin->sin_len = sizeof(*sin);
+ if (which != MASK)
+ sin->sin_family = AF_INET;
+
+ if (which == ADDR && strrchr(s, '/') != NULL &&
+ (bits = inet_net_pton(AF_INET, s, &tsin.sin_addr,
+ sizeof(tsin.sin_addr))) != -1) {
+ l = snprintf(p, sizeof(p), "%d", bits);
+ if (l < 0 || l >= sizeof(p))
+ errx(1, "%d: bad prefixlen", bits);
+ setmask++;
+ in_getprefix(p, MASK);
+ memcpy(&sin->sin_addr, &tsin.sin_addr, sizeof(sin->sin_addr));
+ } else if (inet_aton(s, &sin->sin_addr) == 0) {
+ if ((hp = gethostbyname(s)))
+ memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
+ else
+ errx(1, "%s: bad value", s);
+ }
+ if (which == MASK && (ntohl(sin->sin_addr.s_addr) &
+ (~ntohl(sin->sin_addr.s_addr) >> 1)))
+ errx(1, "%s: non-contiguous mask", s);
+}
+
+/* ARGSUSED */
+void
+in_getprefix(const char *plen, int which)
+{
+ struct sockaddr_in *sin = sintab[which];
+ const char *errmsg = NULL;
+ u_char *cp;
+ int len;
+
+ len = strtonum(plen, 0, 32, &errmsg);
+ if (errmsg)
+ errx(1, "prefix %s: %s", plen, errmsg);
+
+ sin->sin_len = sizeof(*sin);
+ if (which != MASK)
+ sin->sin_family = AF_INET;
+ if ((len == 0) || (len == 32)) {
+ memset(&sin->sin_addr, 0xff, sizeof(struct in_addr));
+ return;
+ }
+ memset((void *)&sin->sin_addr, 0x00, sizeof(sin->sin_addr));
+ for (cp = (u_char *)&sin->sin_addr; len > 7; len -= 8)
+ *cp++ = 0xff;
+ if (len)
+ *cp = 0xff << (8 - len);
+}
+
+/*
+ * Print a value a la the %b format of the kernel's printf
+ */
+void
+printb(char *s, unsigned int v, unsigned char *bits)
+{
+ int i, any = 0;
+ unsigned char c;
+
+ if (bits && *bits == 8)
+ printf("%s=%o", s, v);
+ else
+ printf("%s=%x", s, v);
+
+ if (bits) {
+ bits++;
+ putchar('<');
+ while ((i = *bits++)) {
+ if (v & (1 << (i-1))) {
+ if (any)
+ putchar(',');
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ putchar(c);
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ putchar('>');
+ }
+}
+
+/*
+ * A simple version of printb for status output
+ */
+void
+printb_status(unsigned short v, unsigned char *bits)
+{
+ int i, any = 0;
+ unsigned char c;
+
+ if (bits) {
+ bits++;
+ while ((i = *bits++)) {
+ if (v & (1 << (i-1))) {
+ if (any)
+ putchar(',');
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ putchar(tolower(c));
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ }
+}
+
+#define SIN6(x) ((struct sockaddr_in6 *) &(x))
+struct sockaddr_in6 *sin6tab[] = {
+SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr),
+SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr)};
+
+void
+in6_getaddr(const char *s, int which)
+{
+ struct sockaddr_in6 *sin6 = sin6tab[which];
+ struct addrinfo hints, *res;
+ char buf[HOST_NAME_MAX+1 + sizeof("/128")], *pfxlen;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+
+ if (which == ADDR && strchr(s, '/') != NULL) {
+ if (strlcpy(buf, s, sizeof(buf)) >= sizeof(buf))
+ errx(1, "%s: bad value", s);
+ pfxlen = strchr(buf, '/');
+ *pfxlen++ = '\0';
+ s = buf;
+ setmask++;
+ in6_getprefix(pfxlen, MASK);
+ explicit_prefix = 1;
+ }
+
+ error = getaddrinfo(s, "0", &hints, &res);
+ if (error)
+ errx(1, "%s: %s", s, gai_strerror(error));
+ memcpy(sin6, res->ai_addr, res->ai_addrlen);
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
+ *(u_int16_t *)&sin6->sin6_addr.s6_addr[2] == 0 &&
+ sin6->sin6_scope_id) {
+ *(u_int16_t *)&sin6->sin6_addr.s6_addr[2] =
+ htons(sin6->sin6_scope_id & 0xffff);
+ sin6->sin6_scope_id = 0;
+ }
+#endif /* __KAME__ */
+ freeaddrinfo(res);
+}
+
+void
+in6_getprefix(const char *plen, int which)
+{
+ struct sockaddr_in6 *sin6 = sin6tab[which];
+ const char *errmsg = NULL;
+ u_char *cp;
+ int len;
+
+ len = strtonum(plen, 0, 128, &errmsg);
+ if (errmsg)
+ errx(1, "prefix %s: %s", plen, errmsg);
+
+ sin6->sin6_len = sizeof(*sin6);
+ if (which != MASK)
+ sin6->sin6_family = AF_INET6;
+ if ((len == 0) || (len == 128)) {
+ memset(&sin6->sin6_addr, 0xff, sizeof(struct in6_addr));
+ return;
+ }
+ memset((void *)&sin6->sin6_addr, 0x00, sizeof(sin6->sin6_addr));
+ for (cp = (u_char *)&sin6->sin6_addr; len > 7; len -= 8)
+ *cp++ = 0xff;
+ if (len)
+ *cp = 0xff << (8 - len);
+}
+
+int
+prefix(void *val, int size)
+{
+ u_char *nam = (u_char *)val;
+ int byte, bit, plen = 0;
+
+ for (byte = 0; byte < size; byte++, plen += 8)
+ if (nam[byte] != 0xff)
+ break;
+ if (byte == size)
+ return (plen);
+ for (bit = 7; bit != 0; bit--, plen++)
+ if (!(nam[byte] & (1 << bit)))
+ break;
+ for (; bit != 0; bit--)
+ if (nam[byte] & (1 << bit))
+ return (0);
+ byte++;
+ for (; byte < size; byte++)
+ if (nam[byte])
+ return (0);
+ return (plen);
+}
+
+/* Print usage and exit */
+__dead void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: ifaddr interface [address_family] "
+ "[address [dest_address]]\n"
+ "\t\t[parameters]\n");
+ exit(1);
+}
+
+#ifndef SMALL
+void
+printifhwfeatures(const char *unused, int show)
+{
+ struct if_data ifrdat;
+
+ if (!show) {
+ if (showcapsflag)
+ usage();
+ showcapsflag = 1;
+ return;
+ }
+ bzero(&ifrdat, sizeof(ifrdat));
+ ifr.ifr_data = (caddr_t)&ifrdat;
+ if (ioctl(sock, SIOCGIFDATA, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGIFDATA");
+ printb("\thwfeatures", (u_int)ifrdat.ifi_capabilities, HWFEATURESBITS);
+
+ if (ioctl(sock, SIOCGIFHARDMTU, (caddr_t)&ifr) != -1) {
+ if (ifr.ifr_hardmtu)
+ printf(" hardmtu %u", ifr.ifr_hardmtu);
+ }
+ putchar('\n');
+}
+#endif
+
+char *
+sec2str(time_t total)
+{
+ static char result[256];
+ char *p = result;
+ char *end = &result[sizeof(result)];
+
+ snprintf(p, end - p, "%lld", (long long)total);
+ return (result);
+}
+
+#ifndef SMALL
+void
+setrdomain(const char *id, int param)
+{
+ const char *errmsg = NULL;
+ int rdomainid;
+
+ rdomainid = strtonum(id, 0, RT_TABLEID_MAX, &errmsg);
+ if (errmsg)
+ errx(1, "rdomain %s: %s", id, errmsg);
+
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_rdomainid = rdomainid;
+ if (ioctl(sock, SIOCSIFRDOMAIN, (caddr_t)&ifr) == -1)
+ warn("SIOCSIFRDOMAIN");
+}
+
+void
+unsetrdomain(const char *ignored, int alsoignored)
+{
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_rdomainid = 0;
+ if (ioctl(sock, SIOCSIFRDOMAIN, (caddr_t)&ifr) == -1)
+ warn("SIOCSIFRDOMAIN");
+}
+#endif
+
+#ifdef SMALL
+void
+setignore(const char *id, int param)
+{
+ /* just digest the command */
+}
+#endif