ifconfig.c revision 1.241
1/*	$NetBSD: ifconfig.c,v 1.241 2020/01/02 23:02:19 ryo Exp $	*/
2
3/*-
4 * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * Copyright (c) 1983, 1993
35 *	The Regents of the University of California.  All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 *    notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 *    notice, this list of conditions and the following disclaimer in the
44 *    documentation and/or other materials provided with the distribution.
45 * 3. Neither the name of the University nor the names of its contributors
46 *    may be used to endorse or promote products derived from this software
47 *    without specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 */
61
62#include <sys/cdefs.h>
63#ifndef lint
64__COPYRIGHT("@(#) Copyright (c) 1983, 1993\
65 The Regents of the University of California.  All rights reserved.");
66__RCSID("$NetBSD: ifconfig.c,v 1.241 2020/01/02 23:02:19 ryo Exp $");
67#endif /* not lint */
68
69#include <sys/param.h>
70#include <sys/queue.h>
71#include <sys/socket.h>
72#include <sys/ioctl.h>
73
74#include <net/if.h>
75#include <net/if_dl.h>
76#include <net/if_media.h>
77#include <net/if_ether.h>
78#include <netinet/in.h>		/* XXX */
79#include <netinet/in_var.h>	/* XXX */
80
81#include <netdb.h>
82
83#include <sys/protosw.h>
84
85#include <assert.h>
86#include <ctype.h>
87#include <err.h>
88#include <errno.h>
89#include <stdbool.h>
90#include <stddef.h>
91#include <stdio.h>
92#include <stdlib.h>
93#include <string.h>
94#include <unistd.h>
95#include <ifaddrs.h>
96#include <util.h>
97
98#include "extern.h"
99
100#include "media.h"
101#include "parse.h"
102#include "env.h"
103#include "prog_ops.h"
104
105#define WAIT_DAD	10000000 /* nanoseconds between each poll, 10ms */
106
107static bool bflag, dflag, hflag, sflag, uflag, Wflag, wflag;
108bool lflag, Nflag, vflag, zflag;
109static long wflag_secs, Wflag_secs;
110
111static char gflags[10 + 26 * 2 + 1] = "AabCdhlNsuvW:w:z";
112bool gflagset[10 + 26 * 2];
113
114static int carrier(prop_dictionary_t);
115static int clone_command(prop_dictionary_t, prop_dictionary_t);
116static void do_setifpreference(prop_dictionary_t);
117static int flag_index(int);
118static void init_afs(void);
119static int list_cloners(prop_dictionary_t, prop_dictionary_t);
120static int media_status_exec(prop_dictionary_t, prop_dictionary_t);
121static int wait_dad_exec(prop_dictionary_t, prop_dictionary_t);
122static int no_cmds_exec(prop_dictionary_t, prop_dictionary_t);
123static int notrailers(prop_dictionary_t, prop_dictionary_t);
124static void printall(const char *, prop_dictionary_t);
125static int setifaddr(prop_dictionary_t, prop_dictionary_t);
126static int setifbroadaddr(prop_dictionary_t, prop_dictionary_t);
127static int setifcaps(prop_dictionary_t, prop_dictionary_t);
128static int setifdstormask(prop_dictionary_t, prop_dictionary_t);
129static int setifflags(prop_dictionary_t, prop_dictionary_t);
130static int setifmetric(prop_dictionary_t, prop_dictionary_t);
131static int setifmtu(prop_dictionary_t, prop_dictionary_t);
132static int setifnetmask(prop_dictionary_t, prop_dictionary_t);
133static int setifprefixlen(prop_dictionary_t, prop_dictionary_t);
134static int setlinkstr(prop_dictionary_t, prop_dictionary_t);
135static int unsetlinkstr(prop_dictionary_t, prop_dictionary_t);
136static int setifdescr(prop_dictionary_t, prop_dictionary_t);
137static int unsetifdescr(prop_dictionary_t, prop_dictionary_t);
138static void status(const struct sockaddr *, prop_dictionary_t,
139    prop_dictionary_t);
140__dead static void usage(void);
141
142static const struct kwinst ifflagskw[] = {
143	  IFKW("arp", -IFF_NOARP)
144	, IFKW("debug", IFF_DEBUG)
145	, IFKW("link0", IFF_LINK0)
146	, IFKW("link1", IFF_LINK1)
147	, IFKW("link2", IFF_LINK2)
148	, {.k_word = "down", .k_type = KW_T_INT, .k_int = -IFF_UP}
149	, {.k_word = "up", .k_type = KW_T_INT, .k_int = IFF_UP}
150};
151
152static const struct kwinst ifcapskw[] = {
153	  IFKW("ip4csum-tx",	IFCAP_CSUM_IPv4_Tx)
154	, IFKW("ip4csum-rx",	IFCAP_CSUM_IPv4_Rx)
155	, IFKW("tcp4csum-tx",	IFCAP_CSUM_TCPv4_Tx)
156	, IFKW("tcp4csum-rx",	IFCAP_CSUM_TCPv4_Rx)
157	, IFKW("udp4csum-tx",	IFCAP_CSUM_UDPv4_Tx)
158	, IFKW("udp4csum-rx",	IFCAP_CSUM_UDPv4_Rx)
159	, IFKW("tcp6csum-tx",	IFCAP_CSUM_TCPv6_Tx)
160	, IFKW("tcp6csum-rx",	IFCAP_CSUM_TCPv6_Rx)
161	, IFKW("udp6csum-tx",	IFCAP_CSUM_UDPv6_Tx)
162	, IFKW("udp6csum-rx",	IFCAP_CSUM_UDPv6_Rx)
163	, IFKW("ip4csum",	IFCAP_CSUM_IPv4_Tx|IFCAP_CSUM_IPv4_Rx)
164	, IFKW("tcp4csum",	IFCAP_CSUM_TCPv4_Tx|IFCAP_CSUM_TCPv4_Rx)
165	, IFKW("udp4csum",	IFCAP_CSUM_UDPv4_Tx|IFCAP_CSUM_UDPv4_Rx)
166	, IFKW("tcp6csum",	IFCAP_CSUM_TCPv6_Tx|IFCAP_CSUM_TCPv6_Rx)
167	, IFKW("udp6csum",	IFCAP_CSUM_UDPv6_Tx|IFCAP_CSUM_UDPv6_Rx)
168	, IFKW("tso4",		IFCAP_TSOv4)
169	, IFKW("tso6",		IFCAP_TSOv6)
170};
171
172extern struct pbranch command_root;
173extern struct pbranch opt_command;
174extern struct pbranch opt_family, opt_silent_family;
175extern struct pkw cloning, silent_family, family, ifcaps, ifflags, misc;
176extern struct pstr parse_linkstr;
177
178struct pinteger parse_metric = PINTEGER_INITIALIZER(&parse_metric, "metric", 10,
179    setifmetric, "metric", &command_root.pb_parser);
180
181struct pinteger parse_mtu = PINTEGER_INITIALIZER(&parse_mtu, "mtu", 10,
182    setifmtu, "mtu", &command_root.pb_parser);
183
184struct pinteger parse_prefixlen = PINTEGER_INITIALIZER(&parse_prefixlen,
185    "prefixlen", 10, setifprefixlen, "prefixlen", &command_root.pb_parser);
186
187struct pinteger parse_preference = PINTEGER_INITIALIZER1(&parse_preference,
188    "preference", INT16_MIN, INT16_MAX, 10, NULL, "preference",
189    &command_root.pb_parser);
190
191struct paddr parse_netmask = PADDR_INITIALIZER(&parse_netmask, "netmask",
192    setifnetmask, "dstormask", NULL, NULL, NULL, &command_root.pb_parser);
193
194struct paddr parse_broadcast = PADDR_INITIALIZER(&parse_broadcast,
195    "broadcast address",
196    setifbroadaddr, "broadcast", NULL, NULL, NULL, &command_root.pb_parser);
197
198struct pstr parse_descr = PSTR_INITIALIZER1(&parse_descr, "descr",
199    setifdescr, "descr", false, &command_root.pb_parser);
200
201static const struct kwinst misckw[] = {
202	  {.k_word = "alias", .k_key = "alias", .k_deact = "alias",
203	   .k_type = KW_T_BOOL, .k_neg = true,
204	   .k_bool = true, .k_negbool = false,
205	   .k_nextparser = &command_root.pb_parser}
206	, {.k_word = "broadcast", .k_nextparser = &parse_broadcast.pa_parser}
207	, {.k_word = "delete", .k_key = "alias", .k_deact = "alias",
208	   .k_type = KW_T_BOOL, .k_bool = false,
209	   .k_nextparser = &command_root.pb_parser}
210	, {.k_word = "metric", .k_nextparser = &parse_metric.pi_parser}
211	, {.k_word = "mtu", .k_nextparser = &parse_mtu.pi_parser}
212	, {.k_word = "netmask", .k_nextparser = &parse_netmask.pa_parser}
213	, {.k_word = "preference", .k_act = "address",
214	   .k_nextparser = &parse_preference.pi_parser}
215	, {.k_word = "prefixlen", .k_nextparser = &parse_prefixlen.pi_parser}
216	, {.k_word = "trailers", .k_neg = true,
217	   .k_exec = notrailers, .k_nextparser = &command_root.pb_parser}
218	, {.k_word = "linkstr", .k_nextparser = &parse_linkstr.ps_parser }
219	, {.k_word = "-linkstr", .k_exec = unsetlinkstr,
220	   .k_nextparser = &command_root.pb_parser }
221	, {.k_word = "descr", .k_nextparser = &parse_descr.ps_parser}
222	, {.k_word = "description", .k_nextparser = &parse_descr.ps_parser}
223	, {.k_word = "-descr", .k_exec = unsetifdescr,
224	   .k_nextparser = &command_root.pb_parser}
225	, {.k_word = "-description", .k_exec = unsetifdescr,
226	   .k_nextparser = &command_root.pb_parser}
227};
228
229/* key: clonecmd */
230static const struct kwinst clonekw[] = {
231	{.k_word = "create", .k_type = KW_T_INT, .k_int = SIOCIFCREATE,
232	 .k_nextparser = &opt_silent_family.pb_parser},
233	{.k_word = "destroy", .k_type = KW_T_INT, .k_int = SIOCIFDESTROY}
234};
235
236static struct kwinst familykw[24];
237
238struct pterm cloneterm = PTERM_INITIALIZER(&cloneterm, "list cloners",
239    list_cloners, "none");
240
241struct pterm wait_dad = PTERM_INITIALIZER(&wait_dad, "wait DAD", wait_dad_exec,
242    "none");
243
244struct pterm no_cmds = PTERM_INITIALIZER(&no_cmds, "no commands", no_cmds_exec,
245    "none");
246
247struct pkw family_only =
248    PKW_INITIALIZER(&family_only, "family-only", NULL, "af", familykw,
249	__arraycount(familykw), &no_cmds.pt_parser);
250
251struct paddr address = PADDR_INITIALIZER(&address,
252    "local address (address 1)",
253    setifaddr, "address", "netmask", NULL, "address", &command_root.pb_parser);
254
255struct paddr dstormask = PADDR_INITIALIZER(&dstormask,
256    "destination/netmask (address 2)",
257    setifdstormask, "dstormask", NULL, "address", "dstormask",
258    &command_root.pb_parser);
259
260struct paddr broadcast = PADDR_INITIALIZER(&broadcast,
261    "broadcast address (address 3)",
262    setifbroadaddr, "broadcast", NULL, "dstormask", "broadcast",
263    &command_root.pb_parser);
264
265struct pstr parse_linkstr = PSTR_INITIALIZER(&parse_linkstr, "linkstr",
266    setlinkstr, "linkstr", &command_root.pb_parser);
267
268static SIMPLEQ_HEAD(, afswtch) aflist = SIMPLEQ_HEAD_INITIALIZER(aflist);
269
270static SIMPLEQ_HEAD(, usage_func) usage_funcs =
271    SIMPLEQ_HEAD_INITIALIZER(usage_funcs);
272static SIMPLEQ_HEAD(, status_func) status_funcs =
273    SIMPLEQ_HEAD_INITIALIZER(status_funcs);
274static SIMPLEQ_HEAD(, statistics_func) statistics_funcs =
275    SIMPLEQ_HEAD_INITIALIZER(statistics_funcs);
276static SIMPLEQ_HEAD(, cmdloop_branch) cmdloop_branches =
277    SIMPLEQ_HEAD_INITIALIZER(cmdloop_branches);
278
279struct branch opt_clone_brs[] = {
280	  {.b_nextparser = &cloning.pk_parser}
281	, {.b_nextparser = &opt_family.pb_parser}
282}, opt_silent_family_brs[] = {
283	  {.b_nextparser = &silent_family.pk_parser}
284	, {.b_nextparser = &command_root.pb_parser}
285}, opt_family_brs[] = {
286	  {.b_nextparser = &family.pk_parser}
287	, {.b_nextparser = &opt_command.pb_parser}
288}, command_root_brs[] = {
289	  {.b_nextparser = &ifflags.pk_parser}
290	, {.b_nextparser = &ifcaps.pk_parser}
291	, {.b_nextparser = &kwmedia.pk_parser}
292	, {.b_nextparser = &misc.pk_parser}
293	, {.b_nextparser = &address.pa_parser}
294	, {.b_nextparser = &dstormask.pa_parser}
295	, {.b_nextparser = &broadcast.pa_parser}
296	, {.b_nextparser = NULL}
297}, opt_command_brs[] = {
298	  {.b_nextparser = &no_cmds.pt_parser}
299	, {.b_nextparser = &command_root.pb_parser}
300};
301
302struct branch opt_family_only_brs[] = {
303	  {.b_nextparser = &no_cmds.pt_parser}
304	, {.b_nextparser = &family_only.pk_parser}
305};
306struct pbranch opt_family_only = PBRANCH_INITIALIZER(&opt_family_only,
307    "opt-family-only", opt_family_only_brs,
308    __arraycount(opt_family_only_brs), true);
309struct pbranch opt_command = PBRANCH_INITIALIZER(&opt_command,
310    "optional command",
311    opt_command_brs, __arraycount(opt_command_brs), true);
312
313struct pbranch command_root = PBRANCH_INITIALIZER(&command_root,
314    "command-root", command_root_brs, __arraycount(command_root_brs), true);
315
316struct piface iface_opt_family_only =
317    PIFACE_INITIALIZER(&iface_opt_family_only, "iface-opt-family-only",
318    NULL, "if", &opt_family_only.pb_parser);
319
320struct pkw family = PKW_INITIALIZER(&family, "family", NULL, "af",
321    familykw, __arraycount(familykw), &opt_command.pb_parser);
322
323struct pkw silent_family = PKW_INITIALIZER(&silent_family, "silent family",
324    NULL, "af", familykw, __arraycount(familykw), &command_root.pb_parser);
325
326struct pkw *family_users[] = {&family_only, &family, &silent_family};
327
328struct pkw ifcaps = PKW_INITIALIZER(&ifcaps, "ifcaps", setifcaps,
329    "ifcap", ifcapskw, __arraycount(ifcapskw), &command_root.pb_parser);
330
331struct pkw ifflags = PKW_INITIALIZER(&ifflags, "ifflags", setifflags,
332    "ifflag", ifflagskw, __arraycount(ifflagskw), &command_root.pb_parser);
333
334struct pkw cloning = PKW_INITIALIZER(&cloning, "cloning", clone_command,
335    "clonecmd", clonekw, __arraycount(clonekw), NULL);
336
337struct pkw misc = PKW_INITIALIZER(&misc, "misc", NULL, NULL,
338    misckw, __arraycount(misckw), NULL);
339
340struct pbranch opt_clone = PBRANCH_INITIALIZER(&opt_clone,
341    "opt-clone", opt_clone_brs, __arraycount(opt_clone_brs), true);
342
343struct pbranch opt_silent_family = PBRANCH_INITIALIZER(&opt_silent_family,
344    "optional silent family", opt_silent_family_brs,
345    __arraycount(opt_silent_family_brs), true);
346
347struct pbranch opt_family = PBRANCH_INITIALIZER(&opt_family,
348    "opt-family", opt_family_brs, __arraycount(opt_family_brs), true);
349
350struct piface iface_start = PIFACE_INITIALIZER(&iface_start,
351    "iface-opt-family", NULL, "if", &opt_clone.pb_parser);
352
353struct piface iface_only = PIFACE_INITIALIZER(&iface_only, "iface",
354    media_status_exec, "if", NULL);
355
356static bool
357flag_is_registered(const char *flags, int flag)
358{
359	return flags != NULL && strchr(flags, flag) != NULL;
360}
361
362static int
363check_flag(const char *flags, int flag)
364{
365	if (flag_is_registered(flags, flag)) {
366		errno = EEXIST;
367		return -1;
368	}
369
370	if (flag >= '0' && flag <= '9')
371		return 0;
372	if (flag >= 'a' && flag <= 'z')
373		return 0;
374	if (flag >= 'A' && flag <= 'Z')
375		return 0;
376
377	errno = EINVAL;
378	return -1;
379}
380
381void
382cmdloop_branch_init(cmdloop_branch_t *b, struct parser *p)
383{
384	b->b_parser = p;
385}
386
387void
388statistics_func_init(statistics_func_t *f, statistics_cb_t func)
389{
390	f->f_func = func;
391}
392
393void
394status_func_init(status_func_t *f, status_cb_t func)
395{
396	f->f_func = func;
397}
398
399void
400usage_func_init(usage_func_t *f, usage_cb_t func)
401{
402	f->f_func = func;
403}
404
405int
406register_cmdloop_branch(cmdloop_branch_t *b)
407{
408	SIMPLEQ_INSERT_TAIL(&cmdloop_branches, b, b_next);
409	return 0;
410}
411
412int
413register_statistics(statistics_func_t *f)
414{
415	SIMPLEQ_INSERT_TAIL(&statistics_funcs, f, f_next);
416	return 0;
417}
418
419int
420register_status(status_func_t *f)
421{
422	SIMPLEQ_INSERT_TAIL(&status_funcs, f, f_next);
423	return 0;
424}
425
426int
427register_usage(usage_func_t *f)
428{
429	SIMPLEQ_INSERT_TAIL(&usage_funcs, f, f_next);
430	return 0;
431}
432
433int
434register_family(struct afswtch *af)
435{
436	SIMPLEQ_INSERT_TAIL(&aflist, af, af_next);
437	return 0;
438}
439
440int
441register_flag(int flag)
442{
443	if (check_flag(gflags, flag) == -1)
444		return -1;
445
446	if (strlen(gflags) + 1 >= sizeof(gflags)) {
447		errno = ENOMEM;
448		return -1;
449	}
450
451	gflags[strlen(gflags)] = flag;
452
453	return 0;
454}
455
456static int
457flag_index(int flag)
458{
459	if (flag >= '0' && flag <= '9')
460		return flag - '0';
461	if (flag >= 'a' && flag <= 'z')
462		return 10 + flag - 'a';
463	if (flag >= 'A' && flag <= 'Z')
464		return 10 + 26 + flag - 'a';
465
466	errno = EINVAL;
467	return -1;
468}
469
470static bool
471set_flag(int flag)
472{
473	int idx;
474
475	if ((idx = flag_index(flag)) == -1)
476		return false;
477
478	return gflagset[idx] = true;
479}
480
481bool
482get_flag(int flag)
483{
484	int idx;
485
486	if ((idx = flag_index(flag)) == -1)
487		return false;
488
489	return gflagset[idx];
490}
491
492static struct parser *
493init_parser(void)
494{
495	cmdloop_branch_t *b;
496
497	if (parser_init(&iface_opt_family_only.pif_parser) == -1)
498		err(EXIT_FAILURE, "parser_init(iface_opt_family_only)");
499	if (parser_init(&iface_only.pif_parser) == -1)
500		err(EXIT_FAILURE, "parser_init(iface_only)");
501	if (parser_init(&iface_start.pif_parser) == -1)
502		err(EXIT_FAILURE, "parser_init(iface_start)");
503
504	SIMPLEQ_FOREACH(b, &cmdloop_branches, b_next)
505		pbranch_addbranch(&command_root, b->b_parser);
506
507	return &iface_start.pif_parser;
508}
509
510static int
511no_cmds_exec(prop_dictionary_t env, prop_dictionary_t oenv)
512{
513	const char *ifname;
514	unsigned short ignore;
515
516	/* ifname == NULL is ok.  It indicates 'ifconfig -a'. */
517	if ((ifname = getifname(env)) == NULL)
518		;
519	else if (getifflags(env, oenv, &ignore) == -1)
520		err(EXIT_FAILURE, "SIOCGIFFLAGS %s", ifname);
521
522	printall(ifname, env);
523	exit(EXIT_SUCCESS);
524}
525
526static int
527wait_dad_exec(prop_dictionary_t env, prop_dictionary_t oenv)
528{
529	bool waiting;
530	struct ifaddrs *ifaddrs, *ifa;
531	const struct timespec ts = { .tv_sec = 0, .tv_nsec = WAIT_DAD };
532	struct timespec now, end_det, end;
533	const struct afswtch *afp;
534
535	if (wflag_secs) {
536		const struct timespec tent =
537		    { .tv_sec = wflag_secs, .tv_nsec = 0};
538		const struct timespec det =
539		    { .tv_sec = Wflag_secs, .tv_nsec = 0};
540
541		if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
542			err(EXIT_FAILURE, "clock_gettime");
543		timespecadd(&now, &tent, &end);
544		if (Wflag_secs)
545			timespecadd(&now, &det, &end_det);
546		else
547			timespecclear(&end_det);
548	} else {
549		timespecclear(&end_det);
550		timespecclear(&end);
551	}
552
553	if (getifaddrs(&ifaddrs) == -1)
554		err(EXIT_FAILURE, "getifaddrs");
555
556	for (;;) {
557		waiting = false;
558		for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
559			if (ifa->ifa_addr == NULL)
560				continue;
561			afp = lookup_af_bynum(ifa->ifa_addr->sa_family);
562			if (afp &&
563			    ((afp->af_addr_tentative_or_detached &&
564			    ifa->ifa_flags & IFF_UP &&
565			    timespecisset(&end_det) &&
566			    timespeccmp(&now, &end_det, <) &&
567			    afp->af_addr_tentative_or_detached(ifa)) ||
568			    (afp->af_addr_tentative &&
569			    afp->af_addr_tentative(ifa))))
570			{
571				waiting = true;
572				break;
573			}
574		}
575		if (!waiting)
576			break;
577		nanosleep(&ts, NULL);
578		if (wflag_secs) {
579			if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
580				err(EXIT_FAILURE, "clock_gettime");
581			if (timespeccmp(&now, &end, >))
582				errx(EXIT_FAILURE, "timed out");
583		}
584	}
585
586	freeifaddrs(ifaddrs);
587	exit(EXIT_SUCCESS);
588}
589
590static int
591media_status_exec(prop_dictionary_t env, prop_dictionary_t oenv)
592{
593	const char *ifname;
594	unsigned short ignore;
595
596	/* ifname == NULL is ok.  It indicates 'ifconfig -a'. */
597	if ((ifname = getifname(env)) == NULL)
598		;
599	else if (getifflags(env, oenv, &ignore) == -1)
600		err(EXIT_FAILURE, "SIOCGIFFLAGS %s", ifname);
601
602	exit(carrier(env));
603}
604
605static void
606do_setifcaps(prop_dictionary_t env)
607{
608	struct ifcapreq ifcr;
609	prop_data_t d;
610
611	d = (prop_data_t )prop_dictionary_get(env, "ifcaps");
612	if (d == NULL)
613		return;
614
615	assert(sizeof(ifcr) == prop_data_size(d));
616
617	memcpy(&ifcr, prop_data_data_nocopy(d), sizeof(ifcr));
618	if (direct_ioctl(env, SIOCSIFCAP, &ifcr) == -1)
619		err(EXIT_FAILURE, "SIOCSIFCAP");
620}
621
622int
623main(int argc, char **argv)
624{
625	const struct afswtch *afp;
626	int af, s, e;
627	bool aflag = false, Cflag = false;
628	struct match match[32];
629	size_t nmatch;
630	struct parser *start;
631	int ch, narg = 0, rc;
632	prop_dictionary_t env, oenv;
633	const char *ifname;
634
635	memset(match, 0, sizeof(match));
636
637	init_afs();
638
639	start = init_parser();
640
641	/* Parse command-line options */
642	Nflag = vflag = zflag = false;
643	aflag = argc == 1 ? true : false;
644	if (aflag)
645		start = &opt_family_only.pb_parser;
646
647	while ((ch = getopt(argc, argv, gflags)) != -1) {
648		switch (ch) {
649		case 'A':
650			warnx("-A is deprecated");
651			break;
652
653		case 'a':
654			aflag = true;
655			break;
656
657		case 'b':
658			bflag = true;
659			break;
660
661		case 'C':
662			Cflag = true;
663			break;
664
665		case 'd':
666			dflag = true;
667			break;
668		case 'h':
669			hflag = true;
670			break;
671		case 'l':
672			lflag = true;
673			break;
674		case 'N':
675			Nflag = true;
676			break;
677
678		case 's':
679			sflag = true;
680			break;
681
682		case 'u':
683			uflag = true;
684			break;
685
686		case 'v':
687			vflag = true;
688			break;
689
690		case 'w':
691			wflag = true;
692			wflag_secs = strtoi(optarg, NULL, 10, 0, INT32_MAX, &e);
693			if (e)
694				errx(EXIT_FAILURE, "%s: not a number", optarg);
695			break;
696
697		case 'W':
698			Wflag = true;
699			Wflag_secs = strtoi(optarg, NULL, 10, 0, INT32_MAX, &e);
700			if (e)
701				errx(EXIT_FAILURE, "%s: not a number", optarg);
702			break;
703
704		case 'z':
705			zflag = true;
706			break;
707
708		default:
709			if (!set_flag(ch))
710				usage();
711			break;
712		}
713		switch (ch) {
714		case 'a':
715			start = &opt_family_only.pb_parser;
716			break;
717
718		case 'L':
719		case 'm':
720		case 'z':
721			if (start != &opt_family_only.pb_parser)
722				start = &iface_opt_family_only.pif_parser;
723			break;
724		case 'C':
725			start = &cloneterm.pt_parser;
726			break;
727		case 'l':
728			start = &no_cmds.pt_parser;
729			break;
730		case 's':
731			if (start != &no_cmds.pt_parser &&
732			    start != &opt_family_only.pb_parser)
733				start = &iface_only.pif_parser;
734			break;
735		case 'w':
736			start = &wait_dad.pt_parser;
737			break;
738		default:
739			break;
740		}
741	}
742	argc -= optind;
743	argv += optind;
744
745	/*
746	 * -l means "list all interfaces", and is mutually exclusive with
747	 * all other flags/commands.
748	 *
749	 * -C means "list all names of cloners", and it mutually exclusive
750	 * with all other flags/commands.
751	 *
752	 * -a means "print status of all interfaces".
753	 *
754	 * -w means "spin until DAD completes for all addreseses", and is
755	 * mutually exclusivewith all other flags/commands.
756	 */
757	if ((lflag || Cflag || wflag) &&
758	    (aflag || get_flag('m') || vflag || zflag))
759		usage();
760	if ((lflag || Cflag || wflag) && get_flag('L'))
761		usage();
762	if ((lflag && Cflag) || (lflag & wflag) || (Cflag && wflag))
763		usage();
764
765	nmatch = __arraycount(match);
766
767	rc = parse(argc, argv, start, match, &nmatch, &narg);
768	if (rc != 0)
769		usage();
770
771	if (prog_init && prog_init() == -1)
772		err(1, "rump client init");
773
774	if ((oenv = prop_dictionary_create()) == NULL)
775		err(EXIT_FAILURE, "%s: prop_dictionary_create", __func__);
776
777	if (matches_exec(match, oenv, nmatch) == -1)
778		err(EXIT_FAILURE, "exec_matches");
779
780	argc -= narg;
781	argv += narg;
782
783	env = (nmatch > 0) ? match[(int)nmatch - 1].m_env : NULL;
784	if (env == NULL)
785		env = oenv;
786	else {
787		env = prop_dictionary_augment(env, oenv);
788		if (env == NULL)
789			err(EXIT_FAILURE, "%s: prop_dictionary_augment",
790			    __func__);
791	}
792
793	/* Process any media commands that may have been issued. */
794	process_media_commands(env);
795
796	if ((af = getaf(env)) == -1)
797		af = AF_INET;
798
799	if ((s = getsock(af)) == -1)
800		err(EXIT_FAILURE, "%s: getsock", __func__);
801
802	if ((ifname = getifname(env)) == NULL)
803		err(EXIT_FAILURE, "%s: getifname", __func__);
804
805	if ((afp = lookup_af_bynum(af)) == NULL)
806		errx(EXIT_FAILURE, "%s: lookup_af_bynum", __func__);
807
808	assert(afp->af_addr_commit != NULL);
809	(*afp->af_addr_commit)(env, oenv);
810
811	do_setifpreference(env);
812	do_setifcaps(env);
813	do_setethercaps(env);
814
815	exit(EXIT_SUCCESS);
816}
817
818static void
819init_afs(void)
820{
821	size_t i;
822	const struct afswtch *afp;
823	struct kwinst kw = {.k_type = KW_T_INT};
824
825	SIMPLEQ_FOREACH(afp, &aflist, af_next) {
826		kw.k_word = afp->af_name;
827		kw.k_int = afp->af_af;
828		for (i = 0; i < __arraycount(familykw); i++) {
829			if (familykw[i].k_word == NULL) {
830				familykw[i] = kw;
831				break;
832			}
833		}
834	}
835}
836
837const struct afswtch *
838lookup_af_bynum(int afnum)
839{
840	const struct afswtch *afp;
841
842	SIMPLEQ_FOREACH(afp, &aflist, af_next) {
843		if (afp->af_af == afnum)
844			break;
845	}
846	return afp;
847}
848
849void
850printall(const char *ifname, prop_dictionary_t env0)
851{
852	struct ifaddrs *ifap, *ifa;
853	struct ifreq ifr;
854	const struct sockaddr *sdl = NULL;
855	prop_dictionary_t env, oenv;
856	int idx;
857	char *p;
858
859	if (env0 == NULL)
860		env = prop_dictionary_create();
861	else
862		env = prop_dictionary_copy_mutable(env0);
863
864	oenv = prop_dictionary_create();
865
866	if (env == NULL || oenv == NULL)
867		errx(EXIT_FAILURE, "%s: prop_dictionary_copy/create", __func__);
868
869	if (getifaddrs(&ifap) != 0)
870		err(EXIT_FAILURE, "getifaddrs");
871	p = NULL;
872	idx = 0;
873	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
874		memset(&ifr, 0, sizeof(ifr));
875		estrlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name));
876		if (sizeof(ifr.ifr_addr) >= ifa->ifa_addr->sa_len) {
877			memcpy(&ifr.ifr_addr, ifa->ifa_addr,
878			    ifa->ifa_addr->sa_len);
879		}
880
881		if (ifname != NULL && strcmp(ifname, ifa->ifa_name) != 0)
882			continue;
883		if (ifa->ifa_addr->sa_family == AF_LINK)
884			sdl = ifa->ifa_addr;
885		if (p && strcmp(p, ifa->ifa_name) == 0)
886			continue;
887		if (!prop_dictionary_set_cstring(env, "if", ifa->ifa_name))
888			continue;
889		p = ifa->ifa_name;
890
891		if (bflag && (ifa->ifa_flags & IFF_BROADCAST) == 0)
892			continue;
893		if (dflag && (ifa->ifa_flags & IFF_UP) != 0)
894			continue;
895		if (uflag && (ifa->ifa_flags & IFF_UP) == 0)
896			continue;
897
898		if (sflag && carrier(env))
899			continue;
900		idx++;
901		/*
902		 * Are we just listing the interfaces?
903		 */
904		if (lflag) {
905			if (idx > 1)
906				printf(" ");
907			fputs(ifa->ifa_name, stdout);
908			continue;
909		}
910
911		status(sdl, env, oenv);
912		sdl = NULL;
913	}
914	if (lflag)
915		printf("\n");
916	prop_object_release((prop_object_t)env);
917	prop_object_release((prop_object_t)oenv);
918	freeifaddrs(ifap);
919}
920
921static int
922list_cloners(prop_dictionary_t env, prop_dictionary_t oenv)
923{
924	struct if_clonereq ifcr;
925	char *cp, *buf;
926	int idx, s;
927
928	memset(&ifcr, 0, sizeof(ifcr));
929
930	s = getsock(AF_INET);
931
932	if (prog_ioctl(s, SIOCIFGCLONERS, &ifcr) == -1)
933		err(EXIT_FAILURE, "SIOCIFGCLONERS for count");
934
935	buf = malloc(ifcr.ifcr_total * IFNAMSIZ);
936	if (buf == NULL)
937		err(EXIT_FAILURE, "unable to allocate cloner name buffer");
938
939	ifcr.ifcr_count = ifcr.ifcr_total;
940	ifcr.ifcr_buffer = buf;
941
942	if (prog_ioctl(s, SIOCIFGCLONERS, &ifcr) == -1)
943		err(EXIT_FAILURE, "SIOCIFGCLONERS for names");
944
945	/*
946	 * In case some disappeared in the mean time, clamp it down.
947	 */
948	if (ifcr.ifcr_count > ifcr.ifcr_total)
949		ifcr.ifcr_count = ifcr.ifcr_total;
950
951	for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) {
952		if (idx > 0)
953			printf(" ");
954		printf("%s", cp);
955	}
956
957	printf("\n");
958	free(buf);
959	exit(EXIT_SUCCESS);
960}
961
962static int
963clone_command(prop_dictionary_t env, prop_dictionary_t oenv)
964{
965	int64_t cmd;
966
967	if (!prop_dictionary_get_int64(env, "clonecmd", &cmd)) {
968		errno = ENOENT;
969		return -1;
970	}
971
972	if (indirect_ioctl(env, (unsigned long)cmd, NULL) == -1) {
973		warn("%s", __func__);
974		return -1;
975	}
976	return 0;
977}
978
979/*ARGSUSED*/
980static int
981setifaddr(prop_dictionary_t env, prop_dictionary_t oenv)
982{
983	const struct paddr_prefix *pfx0;
984	struct paddr_prefix *pfx;
985	prop_data_t d;
986	int af;
987
988	if ((af = getaf(env)) == -1)
989		af = AF_INET;
990
991	d = (prop_data_t)prop_dictionary_get(env, "address");
992	assert(d != NULL);
993	pfx0 = prop_data_data_nocopy(d);
994
995	if (pfx0->pfx_len >= 0) {
996		pfx = prefixlen_to_mask(af, pfx0->pfx_len);
997		if (pfx == NULL)
998			err(EXIT_FAILURE, "prefixlen_to_mask");
999		free(pfx);
1000	}
1001
1002	return 0;
1003}
1004
1005static int
1006setifnetmask(prop_dictionary_t env, prop_dictionary_t oenv)
1007{
1008	prop_data_t d;
1009
1010	d = (prop_data_t)prop_dictionary_get(env, "dstormask");
1011	assert(d != NULL);
1012
1013	if (!prop_dictionary_set(oenv, "netmask", (prop_object_t)d))
1014		return -1;
1015
1016	return 0;
1017}
1018
1019static int
1020setifbroadaddr(prop_dictionary_t env, prop_dictionary_t oenv)
1021{
1022	prop_data_t d;
1023	unsigned short flags;
1024
1025	if (getifflags(env, oenv, &flags) == -1)
1026		err(EXIT_FAILURE, "%s: getifflags", __func__);
1027
1028	if ((flags & IFF_BROADCAST) == 0)
1029		errx(EXIT_FAILURE, "not a broadcast interface");
1030
1031	d = (prop_data_t)prop_dictionary_get(env, "broadcast");
1032	assert(d != NULL);
1033
1034	if (!prop_dictionary_set(oenv, "broadcast", (prop_object_t)d))
1035		return -1;
1036
1037	return 0;
1038}
1039
1040/*ARGSUSED*/
1041static int
1042notrailers(prop_dictionary_t env, prop_dictionary_t oenv)
1043{
1044	puts("Note: trailers are no longer sent, but always received");
1045	return 0;
1046}
1047
1048/*ARGSUSED*/
1049static int
1050setifdstormask(prop_dictionary_t env, prop_dictionary_t oenv)
1051{
1052	const char *key;
1053	prop_data_t d;
1054	unsigned short flags;
1055
1056	if (getifflags(env, oenv, &flags) == -1)
1057		err(EXIT_FAILURE, "%s: getifflags", __func__);
1058
1059	d = (prop_data_t)prop_dictionary_get(env, "dstormask");
1060	assert(d != NULL);
1061
1062	if ((flags & IFF_BROADCAST) == 0) {
1063		key = "dst";
1064	} else {
1065		key = "netmask";
1066	}
1067
1068	if (!prop_dictionary_set(oenv, key, (prop_object_t)d))
1069		return -1;
1070
1071	return 0;
1072}
1073
1074static int
1075setifflags(prop_dictionary_t env, prop_dictionary_t oenv)
1076{
1077	struct ifreq ifr;
1078	int64_t ifflag;
1079	bool rc;
1080
1081	rc = prop_dictionary_get_int64(env, "ifflag", &ifflag);
1082	assert(rc);
1083
1084	if (direct_ioctl(env, SIOCGIFFLAGS, &ifr) == -1)
1085		return -1;
1086
1087	if (ifflag < 0) {
1088		ifflag = -ifflag;
1089		ifr.ifr_flags &= ~ifflag;
1090	} else
1091		ifr.ifr_flags |= ifflag;
1092
1093	if (direct_ioctl(env, SIOCSIFFLAGS, &ifr) == -1)
1094		return -1;
1095
1096	return 0;
1097}
1098
1099static int
1100getifcaps(prop_dictionary_t env, prop_dictionary_t oenv, struct ifcapreq *oifcr)
1101{
1102	bool rc;
1103	struct ifcapreq ifcr;
1104	const struct ifcapreq *tmpifcr;
1105	prop_data_t capdata;
1106
1107	capdata = (prop_data_t)prop_dictionary_get(env, "ifcaps");
1108
1109	if (capdata != NULL) {
1110		tmpifcr = prop_data_data_nocopy(capdata);
1111		*oifcr = *tmpifcr;
1112		return 0;
1113	}
1114
1115	(void)direct_ioctl(env, SIOCGIFCAP, &ifcr);
1116	*oifcr = ifcr;
1117
1118	capdata = prop_data_create_data(&ifcr, sizeof(ifcr));
1119
1120	rc = prop_dictionary_set(oenv, "ifcaps", capdata);
1121
1122	prop_object_release((prop_object_t)capdata);
1123
1124	return rc ? 0 : -1;
1125}
1126
1127static int
1128setifcaps(prop_dictionary_t env, prop_dictionary_t oenv)
1129{
1130	int64_t ifcap;
1131	bool rc;
1132	prop_data_t capdata;
1133	struct ifcapreq ifcr;
1134
1135	rc = prop_dictionary_get_int64(env, "ifcap", &ifcap);
1136	assert(rc);
1137
1138	if (getifcaps(env, oenv, &ifcr) == -1)
1139		return -1;
1140
1141	if (ifcap < 0) {
1142		ifcap = -ifcap;
1143		ifcr.ifcr_capenable &= ~ifcap;
1144	} else
1145		ifcr.ifcr_capenable |= ifcap;
1146
1147	if ((capdata = prop_data_create_data(&ifcr, sizeof(ifcr))) == NULL)
1148		return -1;
1149
1150	rc = prop_dictionary_set(oenv, "ifcaps", capdata);
1151	prop_object_release((prop_object_t)capdata);
1152
1153	return rc ? 0 : -1;
1154}
1155
1156static int
1157setifmetric(prop_dictionary_t env, prop_dictionary_t oenv)
1158{
1159	struct ifreq ifr;
1160	bool rc;
1161	int64_t metric;
1162
1163	rc = prop_dictionary_get_int64(env, "metric", &metric);
1164	assert(rc);
1165
1166	ifr.ifr_metric = metric;
1167	if (direct_ioctl(env, SIOCSIFMETRIC, &ifr) == -1)
1168		warn("SIOCSIFMETRIC");
1169	return 0;
1170}
1171
1172static void
1173do_setifpreference(prop_dictionary_t env)
1174{
1175	struct if_addrprefreq ifap;
1176	prop_data_t d;
1177	const struct paddr_prefix *pfx;
1178
1179	memset(&ifap, 0, sizeof(ifap));
1180
1181	if (!prop_dictionary_get_int16(env, "preference",
1182	    &ifap.ifap_preference))
1183		return;
1184
1185	d = (prop_data_t)prop_dictionary_get(env, "address");
1186	assert(d != NULL);
1187
1188	pfx = prop_data_data_nocopy(d);
1189
1190	memcpy(&ifap.ifap_addr, &pfx->pfx_addr,
1191	    MIN(sizeof(ifap.ifap_addr), pfx->pfx_addr.sa_len));
1192	if (direct_ioctl(env, SIOCSIFADDRPREF, &ifap) == -1)
1193		warn("SIOCSIFADDRPREF");
1194}
1195
1196static int
1197setifmtu(prop_dictionary_t env, prop_dictionary_t oenv)
1198{
1199	int64_t mtu;
1200	bool rc;
1201	struct ifreq ifr;
1202
1203	rc = prop_dictionary_get_int64(env, "mtu", &mtu);
1204	assert(rc);
1205
1206	ifr.ifr_mtu = mtu;
1207	if (direct_ioctl(env, SIOCSIFMTU, &ifr) == -1)
1208		warn("SIOCSIFMTU");
1209
1210	return 0;
1211}
1212
1213static int
1214carrier(prop_dictionary_t env)
1215{
1216	struct ifmediareq ifmr;
1217
1218	memset(&ifmr, 0, sizeof(ifmr));
1219
1220	if (direct_ioctl(env, SIOCGIFMEDIA, &ifmr) == -1) {
1221		/*
1222		 * Interface doesn't support SIOC{G,S}IFMEDIA;
1223		 * assume ok.
1224		 */
1225		return EXIT_SUCCESS;
1226	}
1227	if ((ifmr.ifm_status & IFM_AVALID) == 0) {
1228		/*
1229		 * Interface doesn't report media-valid status.
1230		 * assume ok.
1231		 */
1232		return EXIT_SUCCESS;
1233	}
1234	/* otherwise, return ok for active, not-ok if not active. */
1235	if (ifmr.ifm_status & IFM_ACTIVE)
1236		return EXIT_SUCCESS;
1237	else
1238		return EXIT_FAILURE;
1239}
1240
1241static void
1242print_plural(const char *prefix, uint64_t n, const char *unit)
1243{
1244	printf("%s%" PRIu64 " %s%s", prefix, n, unit, (n == 1) ? "" : "s");
1245}
1246
1247static void
1248print_human_bytes(bool humanize, uint64_t n)
1249{
1250	char buf[5];
1251
1252	if (humanize) {
1253		(void)humanize_number(buf, sizeof(buf),
1254		    (int64_t)n, "", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL);
1255		printf(", %s byte%s", buf, (atof(buf) == 1.0) ? "" : "s");
1256	} else
1257		print_plural(", ", n, "byte");
1258}
1259
1260/*
1261 * Print the status of the interface.  If an address family was
1262 * specified, show it and it only; otherwise, show them all.
1263 */
1264
1265#define MAX_PRINT_LEN 58	/* XXX need a better way to determine this! */
1266
1267void
1268status(const struct sockaddr *sdl, prop_dictionary_t env,
1269    prop_dictionary_t oenv)
1270{
1271	const struct if_data *ifi;
1272	status_func_t *status_f;
1273	statistics_func_t *statistics_f;
1274	struct ifdatareq ifdr;
1275	struct ifreq ifr;
1276	struct ifdrv ifdrv;
1277	char fbuf[BUFSIZ];
1278	char *bp;
1279	int af, s;
1280	const char *ifname;
1281	struct ifcapreq ifcr;
1282	unsigned short flags;
1283	const struct afswtch *afp;
1284	char ifdescr[IFDESCRSIZE];
1285
1286	if ((af = getaf(env)) == -1) {
1287		afp = NULL;
1288		af = AF_UNSPEC;
1289	} else
1290		afp = lookup_af_bynum(af);
1291
1292	/* get out early if the family is unsupported by the kernel */
1293	if ((s = getsock(af)) == -1)
1294		err(EXIT_FAILURE, "%s: getsock", __func__);
1295
1296	if ((ifname = getifinfo(env, oenv, &flags)) == NULL)
1297		err(EXIT_FAILURE, "%s: getifinfo", __func__);
1298
1299	(void)snprintb(fbuf, sizeof(fbuf), IFFBITS, flags);
1300	printf("%s: flags=%s", ifname, fbuf);
1301
1302	estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1303	if (prog_ioctl(s, SIOCGIFMETRIC, &ifr) == -1)
1304		warn("SIOCGIFMETRIC %s", ifr.ifr_name);
1305	else if (ifr.ifr_metric != 0)
1306		printf(" metric %d", ifr.ifr_metric);
1307
1308	estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1309	if (prog_ioctl(s, SIOCGIFMTU, &ifr) != -1 && ifr.ifr_mtu != 0)
1310		printf(" mtu %d", ifr.ifr_mtu);
1311	printf("\n");
1312
1313	if (getifcaps(env, oenv, &ifcr) == -1)
1314		err(EXIT_FAILURE, "%s: getifcaps", __func__);
1315
1316	if (ifcr.ifcr_capabilities != 0) {
1317		(void)snprintb_m(fbuf, sizeof(fbuf), IFCAPBITS,
1318		    ifcr.ifcr_capabilities, MAX_PRINT_LEN);
1319		bp = fbuf;
1320		while (*bp != '\0') {
1321			printf("\tcapabilities=%s\n", bp);
1322			bp += strlen(bp) + 1;
1323		}
1324		(void)snprintb_m(fbuf, sizeof(fbuf), IFCAPBITS,
1325		    ifcr.ifcr_capenable, MAX_PRINT_LEN);
1326		bp = fbuf;
1327		while (*bp != '\0') {
1328			printf("\tenabled=%s\n", bp);
1329			bp += strlen(bp) + 1;
1330		}
1331	}
1332
1333	SIMPLEQ_FOREACH(status_f, &status_funcs, f_next)
1334		(*status_f->f_func)(env, oenv);
1335
1336	estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1337	ifr.ifr_buf = &ifdescr;
1338	ifr.ifr_buflen = sizeof(ifdescr);
1339	if (prog_ioctl(s, SIOCGIFDESCR, &ifr) == 0)
1340		printf("\tdescription: \"%s\"\n", (char *)ifr.ifr_buf);
1341
1342	print_link_addresses(env, true);
1343
1344	estrlcpy(ifdrv.ifd_name, ifname, sizeof(ifdrv.ifd_name));
1345	ifdrv.ifd_cmd = IFLINKSTR_QUERYLEN;
1346	ifdrv.ifd_len = 0;
1347	ifdrv.ifd_data = NULL;
1348	/* interface supports linkstr? */
1349	if (prog_ioctl(s, SIOCGLINKSTR, &ifdrv) != -1) {
1350		char *p;
1351
1352		p = malloc(ifdrv.ifd_len);
1353		if (p == NULL)
1354			err(EXIT_FAILURE, "malloc linkstr buf failed");
1355		ifdrv.ifd_data = p;
1356		ifdrv.ifd_cmd = 0;
1357		if (prog_ioctl(s, SIOCGLINKSTR, &ifdrv) == -1)
1358			err(EXIT_FAILURE, "failed to query linkstr");
1359		printf("\tlinkstr: %s\n", (char *)ifdrv.ifd_data);
1360		free(p);
1361	}
1362
1363	media_status(env, oenv);
1364
1365	if (!vflag && !zflag)
1366		goto proto_status;
1367
1368	estrlcpy(ifdr.ifdr_name, ifname, sizeof(ifdr.ifdr_name));
1369
1370	if (prog_ioctl(s, zflag ? SIOCZIFDATA : SIOCGIFDATA, &ifdr) == -1)
1371		err(EXIT_FAILURE, zflag ? "SIOCZIFDATA" : "SIOCGIFDATA");
1372
1373	ifi = &ifdr.ifdr_data;
1374
1375	print_plural("\tinput: ", ifi->ifi_ipackets, "packet");
1376	print_human_bytes(hflag, ifi->ifi_ibytes);
1377	if (ifi->ifi_imcasts)
1378		print_plural(", ", ifi->ifi_imcasts, "multicast");
1379	if (ifi->ifi_ierrors)
1380		print_plural(", ", ifi->ifi_ierrors, "error");
1381	if (ifi->ifi_iqdrops)
1382		print_plural(", ", ifi->ifi_iqdrops, "queue drop");
1383	if (ifi->ifi_noproto)
1384		printf(", %" PRIu64 " unknown protocol", ifi->ifi_noproto);
1385	print_plural("\n\toutput: ", ifi->ifi_opackets, "packet");
1386	print_human_bytes(hflag, ifi->ifi_obytes);
1387	if (ifi->ifi_omcasts)
1388		print_plural(", ", ifi->ifi_omcasts, "multicast");
1389	if (ifi->ifi_oerrors)
1390		print_plural(", ", ifi->ifi_oerrors, "error");
1391	if (ifi->ifi_collisions)
1392		print_plural(", ", ifi->ifi_collisions, "collision");
1393	printf("\n");
1394
1395	SIMPLEQ_FOREACH(statistics_f, &statistics_funcs, f_next)
1396		(*statistics_f->f_func)(env);
1397
1398 proto_status:
1399
1400	if (afp != NULL)
1401		(*afp->af_status)(env, oenv, true);
1402	else SIMPLEQ_FOREACH(afp, &aflist, af_next)
1403		(*afp->af_status)(env, oenv, false);
1404}
1405
1406static int
1407setifprefixlen(prop_dictionary_t env, prop_dictionary_t oenv)
1408{
1409	bool rc;
1410	int64_t plen;
1411	int af;
1412	struct paddr_prefix *pfx;
1413	prop_data_t d;
1414
1415	if ((af = getaf(env)) == -1)
1416		af = AF_INET;
1417
1418	rc = prop_dictionary_get_int64(env, "prefixlen", &plen);
1419	assert(rc);
1420
1421	pfx = prefixlen_to_mask(af, plen);
1422	if (pfx == NULL)
1423		err(EXIT_FAILURE, "prefixlen_to_mask");
1424
1425	d = prop_data_create_data(pfx, paddr_prefix_size(pfx));
1426	if (d == NULL)
1427		err(EXIT_FAILURE, "%s: prop_data_create_data", __func__);
1428
1429	if (!prop_dictionary_set(oenv, "netmask", (prop_object_t)d))
1430		err(EXIT_FAILURE, "%s: prop_dictionary_set", __func__);
1431
1432	free(pfx);
1433	return 0;
1434}
1435
1436static int
1437setlinkstr(prop_dictionary_t env, prop_dictionary_t oenv)
1438{
1439	struct ifdrv ifdrv;
1440	size_t linkstrlen;
1441	prop_data_t data;
1442	char *linkstr;
1443
1444	data = (prop_data_t)prop_dictionary_get(env, "linkstr");
1445	if (data == NULL) {
1446		errno = ENOENT;
1447		return -1;
1448	}
1449	linkstrlen = prop_data_size(data)+1;
1450
1451	linkstr = malloc(linkstrlen);
1452	if (linkstr == NULL)
1453		err(EXIT_FAILURE, "malloc linkstr space");
1454	if (getargstr(env, "linkstr", linkstr, linkstrlen) == -1)
1455		errx(EXIT_FAILURE, "getargstr linkstr failed");
1456
1457	ifdrv.ifd_cmd = 0;
1458	ifdrv.ifd_len = linkstrlen;
1459	ifdrv.ifd_data = __UNCONST(linkstr);
1460
1461	if (direct_ioctl(env, SIOCSLINKSTR, &ifdrv) == -1)
1462		err(EXIT_FAILURE, "SIOCSLINKSTR");
1463	free(linkstr);
1464
1465	return 0;
1466}
1467
1468static int
1469unsetlinkstr(prop_dictionary_t env, prop_dictionary_t oenv)
1470{
1471	struct ifdrv ifdrv;
1472
1473	memset(&ifdrv, 0, sizeof(ifdrv));
1474	ifdrv.ifd_cmd = IFLINKSTR_UNSET;
1475
1476	if (direct_ioctl(env, SIOCSLINKSTR, &ifdrv) == -1)
1477		err(EXIT_FAILURE, "SIOCSLINKSTR");
1478
1479	return 0;
1480}
1481
1482static int
1483setifdescr(prop_dictionary_t env, prop_dictionary_t oenv)
1484{
1485	struct ifreq ifr;
1486	size_t len;
1487	prop_data_t data;
1488	char *descr;
1489
1490	data = (prop_data_t)prop_dictionary_get(env, "descr");
1491	if (data == NULL) {
1492		errno = ENOENT;
1493		return -1;
1494	}
1495	len = prop_data_size(data) + 1;
1496
1497	if (len > IFDESCRSIZE)
1498		err(EXIT_FAILURE, "description too long");
1499
1500	descr = malloc(len);
1501	if (descr == NULL)
1502		err(EXIT_FAILURE, "malloc description space");
1503	if (getargstr(env, "descr", descr, len) == -1)
1504		errx(EXIT_FAILURE, "getargstr descr failed");
1505
1506
1507	ifr.ifr_buf = descr;
1508	ifr.ifr_buflen = len;
1509	if (direct_ioctl(env, SIOCSIFDESCR, &ifr) != 0)
1510		err(EXIT_FAILURE, "SIOCSIFDESCR");
1511
1512	free(descr);
1513
1514	return 0;
1515}
1516
1517static int
1518unsetifdescr(prop_dictionary_t env, prop_dictionary_t oenv)
1519{
1520	struct ifreq ifr;
1521	ifr.ifr_buf = NULL;
1522	ifr.ifr_buflen = 0;
1523
1524	if (direct_ioctl(env, SIOCSIFDESCR, &ifr) != 0)
1525		err(EXIT_FAILURE, "SIOCSIFDESCR");
1526
1527	return 0;
1528}
1529
1530
1531static void
1532usage(void)
1533{
1534	const char *progname = getprogname();
1535	usage_func_t *usage_f;
1536	prop_dictionary_t env;
1537
1538	if ((env = prop_dictionary_create()) == NULL)
1539		err(EXIT_FAILURE, "%s: prop_dictionary_create", __func__);
1540
1541	fprintf(stderr, "usage: %s [-h] %s[-v] [-z] %sinterface\n"
1542		"\t[ af [ address [ dest_addr ] ] [ netmask mask ] [ prefixlen n ]\n"
1543		"\t\t[ alias | -alias ] ]\n"
1544		"\t[ up ] [ down ] [ metric n ] [ mtu n ]\n", progname,
1545		flag_is_registered(gflags, 'm') ? "[-m] " : "",
1546		flag_is_registered(gflags, 'L') ? "[-L] " : "");
1547
1548	SIMPLEQ_FOREACH(usage_f, &usage_funcs, f_next)
1549		(*usage_f->f_func)(env);
1550
1551	fprintf(stderr,
1552		"\t[ arp | -arp ]\n"
1553		"\t[ preference n ]\n"
1554		"\t[ link0 | -link0 ] [ link1 | -link1 ] [ link2 | -link2 ]\n"
1555		"\t[ linkstr str | -linkstr ]\n"
1556		"\t[ description str | descr str | -description | -descr ]\n"
1557		"       %s -a [-b] [-d] [-h] %s[-u] [-v] [-z] [ af ]\n"
1558		"       %s -l [-b] [-d] [-s] [-u]\n"
1559		"       %s -C\n"
1560		"       %s -w n\n"
1561		"       %s interface create\n"
1562		"       %s interface destroy\n",
1563		progname, flag_is_registered(gflags, 'm') ? "[-m] " : "",
1564		progname, progname, progname, progname, progname);
1565
1566	prop_object_release((prop_object_t)env);
1567	exit(EXIT_FAILURE);
1568}
1569