From: David van Moolenbroek Date: Wed, 15 Feb 2017 11:17:52 +0000 (+0000) Subject: Import NetBSD ifconfig(8) X-Git-Url: http://zhaoyanbai.com/repos/?a=commitdiff_plain;h=90b801219a391518dca4b04b17c88ad3b82b6ad9;p=minix.git Import NetBSD ifconfig(8) 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 --- diff --git a/distrib/sets/lists/minix-base/mi b/distrib/sets/lists/minix-base/mi index d08cca0e7..6739c0f9f 100644 --- a/distrib/sets/lists/minix-base/mi +++ b/distrib/sets/lists/minix-base/mi @@ -210,6 +210,7 @@ ./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 diff --git a/distrib/sets/lists/minix-debug/mi b/distrib/sets/lists/minix-debug/mi index dfa0f6a1b..cb436cccd 100644 --- a/distrib/sets/lists/minix-debug/mi +++ b/distrib/sets/lists/minix-debug/mi @@ -163,6 +163,7 @@ ./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 diff --git a/distrib/sets/lists/minix-man/mi b/distrib/sets/lists/minix-man/mi index a957cab59..e00b6f1bb 100644 --- a/distrib/sets/lists/minix-man/mi +++ b/distrib/sets/lists/minix-man/mi @@ -3409,7 +3409,7 @@ ./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 diff --git a/sbin/Makefile b/sbin/Makefile index d8907c231..0f38ce01f 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -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 index 000000000..2aef6ef46 --- /dev/null +++ b/sbin/ifconfig/Makefile @@ -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 + +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 diff --git a/sbin/ifconfig/Makefile.inc b/sbin/ifconfig/Makefile.inc new file mode 100644 index 000000000..a6e4ce9f7 --- /dev/null +++ b/sbin/ifconfig/Makefile.inc @@ -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 index 000000000..494b070c4 --- /dev/null +++ b/sbin/ifconfig/af_atalk.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: af_atalk.c,v 1.19 2013/10/19 00:35:30 christos Exp $"); +#endif /* not lint */ + +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..f4a00c764 --- /dev/null +++ b/sbin/ifconfig/af_inet.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: af_inet.c,v 1.17 2015/05/12 14:05:29 roy Exp $"); +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..e5474105a --- /dev/null +++ b/sbin/ifconfig/af_inet6.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: af_inet6.c,v 1.33 2015/05/12 14:05:29 roy Exp $"); +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..7c4066bc1 --- /dev/null +++ b/sbin/ifconfig/af_inetany.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: af_inetany.c,v 1.17 2012/12/30 22:52:35 christos Exp $"); +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(¶m->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(¶m->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(¶m->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(¶m->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(¶m->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 index 000000000..a040ea739 --- /dev/null +++ b/sbin/ifconfig/af_inetany.h @@ -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 +#include + +#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 index 000000000..18e0368a0 --- /dev/null +++ b/sbin/ifconfig/af_link.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: af_link.c,v 1.7 2014/01/19 22:31:13 matt Exp $"); +#endif /* not lint */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..fec580050 --- /dev/null +++ b/sbin/ifconfig/agr.c @@ -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 +#if !defined(lint) +__RCSID("$NetBSD: agr.c,v 1.15 2008/07/15 21:27:58 dyoung Exp $"); +#endif /* !defined(lint) */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..e9fcf2c8a --- /dev/null +++ b/sbin/ifconfig/carp.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: carp.c,v 1.13 2009/09/11 23:22:28 dyoung Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 = ""; + 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 index 000000000..6d5c690a4 --- /dev/null +++ b/sbin/ifconfig/env.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: env.c,v 1.9 2013/02/07 13:20:51 apb Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include + +#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 index 000000000..a5749e05f --- /dev/null +++ b/sbin/ifconfig/env.h @@ -0,0 +1,25 @@ +#ifndef _IFCONFIG_ENV_H +#define _IFCONFIG_ENV_H + +#include + +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 +#include +#include +#endif /* RUMP_ACTION */ + +#endif /* _IFCONFIG_ENV_H */ diff --git a/sbin/ifconfig/ether.c b/sbin/ifconfig/ether.c new file mode 100644 index 000000000..25d1f00bd --- /dev/null +++ b/sbin/ifconfig/ether.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: ether.c,v 1.2 2012/11/01 13:43:23 pgoyette Exp $"); +#endif /* not lint */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..d8ef47990 --- /dev/null +++ b/sbin/ifconfig/extern.h @@ -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 +#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 index 000000000..b20364ca2 --- /dev/null +++ b/sbin/ifconfig/ieee80211.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: ieee80211.c,v 1.28 2015/04/28 15:14:57 christos Exp $"); +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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("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("", + 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(" 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(" 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 index 000000000..5357cb56d --- /dev/null +++ b/sbin/ifconfig/ifconfig.8 @@ -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 index 000000000..1af088203 --- /dev/null +++ b/sbin/ifconfig/ifconfig.c @@ -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 +#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 +#include +#include +#include + +#include +#include +#include +#include +#include /* XXX */ +#include /* XXX */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..4a5d5d154 --- /dev/null +++ b/sbin/ifconfig/ifconfig_hostops.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: ifconfig_hostops.c,v 1.1 2010/12/13 17:35:08 pooka Exp $"); +#endif /* !lint */ + +#include +#include +#include + +#include + +#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 index 000000000..68afe8ca5 --- /dev/null +++ b/sbin/ifconfig/ifconfig_rumpops.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: ifconfig_rumpops.c,v 1.1 2010/12/13 17:35:08 pooka Exp $"); +#endif /* !lint */ + +#include +#include + +#include + +#include +#include +#include + +#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 index 000000000..b945cb348 --- /dev/null +++ b/sbin/ifconfig/media.c @@ -0,0 +1,464 @@ +#include +#ifndef lint +__RCSID("$NetBSD: media.c,v 1.6 2011/08/29 14:35:00 joerg Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#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 index 000000000..85ce6a678 --- /dev/null +++ b/sbin/ifconfig/media.h @@ -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 + +#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 index 000000000..112a5ecc0 --- /dev/null +++ b/sbin/ifconfig/parse.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: parse.c,v 1.18 2013/07/17 15:42:03 christos Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 index 000000000..231dfd971 --- /dev/null +++ b/sbin/ifconfig/parse.h @@ -0,0 +1,284 @@ +#ifndef _IFCONFIG_PARSE_H +#define _IFCONFIG_PARSE_H + +#include +#include +#include +#include +#include +#include + +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 index 000000000..c44f65621 --- /dev/null +++ b/sbin/ifconfig/pfsync.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: pfsync.c,v 1.1 2009/09/14 10:36:49 degroote Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..9d707ce37 --- /dev/null +++ b/sbin/ifconfig/prog_ops.h @@ -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 + +/* 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 index 000000000..6d808fc40 --- /dev/null +++ b/sbin/ifconfig/tunnel.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: tunnel.c,v 1.20 2013/10/19 15:59:15 christos Exp $"); +#endif /* not lint */ + +#include +#include +#include + +#include + +#ifdef INET6 +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..5bba576dc --- /dev/null +++ b/sbin/ifconfig/util.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: util.c,v 1.17 2013/10/19 00:35:30 christos Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include /* 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 index 000000000..ed8e55683 --- /dev/null +++ b/sbin/ifconfig/util.h @@ -0,0 +1,33 @@ +#ifndef _IFCONFIG_UTIL_H +#define _IFCONFIG_UTIL_H + +#include +#include +#include +#include + +#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 index 000000000..a5ddbcf13 --- /dev/null +++ b/sbin/ifconfig/vlan.c @@ -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 +#ifndef lint +__RCSID("$NetBSD: vlan.c,v 1.14 2014/09/15 06:46:04 ozaki-r Exp $"); +#endif /* not lint */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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' ? + "" : 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); +}