ifconfig.c revision 1.250
1/*	$NetBSD: ifconfig.c,v 1.250 2024/01/03 18:10:42 andvar 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.250 2024/01/03 18:10:42 andvar 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(prop_dictionary_t, prop_dictionary_t);
139__dead static void usage(void);
140
141static const struct kwinst ifflagskw[] = {
142	  IFKW("arp", -IFF_NOARP)
143	, IFKW("debug", IFF_DEBUG)
144	, IFKW("unnumbered", IFF_UNNUMBERED)
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_value(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 addresses", and is
755	 * mutually exclusive with 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	prop_dictionary_t env, oenv;
854	int idx;
855	char *p;
856
857	if (env0 == NULL)
858		env = prop_dictionary_create();
859	else
860		env = prop_dictionary_copy_mutable(env0);
861
862	oenv = prop_dictionary_create();
863
864	if (env == NULL || oenv == NULL)
865		errx(EXIT_FAILURE, "%s: prop_dictionary_copy/create", __func__);
866
867	if (getifaddrs(&ifap) != 0)
868		err(EXIT_FAILURE, "getifaddrs");
869	p = NULL;
870	idx = 0;
871	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
872		if (ifname != NULL && strcmp(ifname, ifa->ifa_name) != 0)
873			continue;
874		if (p && strcmp(p, ifa->ifa_name) == 0)
875			continue;
876		if (!prop_dictionary_set_string(env, "if", ifa->ifa_name))
877			continue;
878		p = ifa->ifa_name;
879
880		if (bflag && (ifa->ifa_flags & IFF_BROADCAST) == 0)
881			continue;
882		if (dflag && (ifa->ifa_flags & IFF_UP) != 0)
883			continue;
884		if (uflag && (ifa->ifa_flags & IFF_UP) == 0)
885			continue;
886
887		if (sflag && carrier(env) == LINK_STATE_DOWN)
888			continue;
889		idx++;
890		/*
891		 * Are we just listing the interfaces?
892		 */
893		if (lflag) {
894			if (idx > 1)
895				printf(" ");
896			fputs(ifa->ifa_name, stdout);
897			continue;
898		}
899
900		status(env, oenv);
901	}
902	if (lflag)
903		printf("\n");
904	prop_object_release((prop_object_t)env);
905	prop_object_release((prop_object_t)oenv);
906	freeifaddrs(ifap);
907}
908
909static int
910list_cloners(prop_dictionary_t env, prop_dictionary_t oenv)
911{
912	struct if_clonereq ifcr;
913	char *cp, *buf;
914	int idx, s;
915
916	memset(&ifcr, 0, sizeof(ifcr));
917
918	s = getsock(AF_INET);
919
920	if (prog_ioctl(s, SIOCIFGCLONERS, &ifcr) == -1)
921		err(EXIT_FAILURE, "SIOCIFGCLONERS for count");
922
923	buf = malloc(ifcr.ifcr_total * IFNAMSIZ);
924	if (buf == NULL)
925		err(EXIT_FAILURE, "unable to allocate cloner name buffer");
926
927	ifcr.ifcr_count = ifcr.ifcr_total;
928	ifcr.ifcr_buffer = buf;
929
930	if (prog_ioctl(s, SIOCIFGCLONERS, &ifcr) == -1)
931		err(EXIT_FAILURE, "SIOCIFGCLONERS for names");
932
933	/*
934	 * In case some disappeared in the mean time, clamp it down.
935	 */
936	if (ifcr.ifcr_count > ifcr.ifcr_total)
937		ifcr.ifcr_count = ifcr.ifcr_total;
938
939	for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) {
940		if (idx > 0)
941			printf(" ");
942		printf("%s", cp);
943	}
944
945	printf("\n");
946	free(buf);
947	exit(EXIT_SUCCESS);
948}
949
950static int
951clone_command(prop_dictionary_t env, prop_dictionary_t oenv)
952{
953	int64_t cmd;
954
955	if (!prop_dictionary_get_int64(env, "clonecmd", &cmd)) {
956		errno = ENOENT;
957		return -1;
958	}
959
960	if (indirect_ioctl(env, (unsigned long)cmd, NULL) == -1) {
961		warn("%s", __func__);
962		return -1;
963	}
964	return 0;
965}
966
967/*ARGSUSED*/
968static int
969setifaddr(prop_dictionary_t env, prop_dictionary_t oenv)
970{
971	const struct paddr_prefix *pfx0;
972	struct paddr_prefix *pfx;
973	prop_data_t d;
974	int af;
975
976	if ((af = getaf(env)) == -1)
977		af = AF_INET;
978
979	d = (prop_data_t)prop_dictionary_get(env, "address");
980	assert(d != NULL);
981	pfx0 = prop_data_value(d);
982
983	if (pfx0->pfx_len >= 0) {
984		pfx = prefixlen_to_mask(af, pfx0->pfx_len);
985		if (pfx == NULL)
986			err(EXIT_FAILURE, "prefixlen_to_mask");
987		free(pfx);
988	}
989
990	return 0;
991}
992
993static int
994setifnetmask(prop_dictionary_t env, prop_dictionary_t oenv)
995{
996	prop_data_t d;
997
998	d = (prop_data_t)prop_dictionary_get(env, "dstormask");
999	assert(d != NULL);
1000
1001	if (!prop_dictionary_set(oenv, "netmask", (prop_object_t)d))
1002		return -1;
1003
1004	return 0;
1005}
1006
1007static int
1008setifbroadaddr(prop_dictionary_t env, prop_dictionary_t oenv)
1009{
1010	prop_data_t d;
1011	unsigned short flags;
1012
1013	if (getifflags(env, oenv, &flags) == -1)
1014		err(EXIT_FAILURE, "%s: getifflags", __func__);
1015
1016	if ((flags & IFF_BROADCAST) == 0)
1017		errx(EXIT_FAILURE, "not a broadcast interface");
1018
1019	d = (prop_data_t)prop_dictionary_get(env, "broadcast");
1020	assert(d != NULL);
1021
1022	if (!prop_dictionary_set(oenv, "broadcast", (prop_object_t)d))
1023		return -1;
1024
1025	return 0;
1026}
1027
1028/*ARGSUSED*/
1029static int
1030notrailers(prop_dictionary_t env, prop_dictionary_t oenv)
1031{
1032	puts("Note: trailers are no longer sent, but always received");
1033	return 0;
1034}
1035
1036/*ARGSUSED*/
1037static int
1038setifdstormask(prop_dictionary_t env, prop_dictionary_t oenv)
1039{
1040	const char *key;
1041	prop_data_t d;
1042	unsigned short flags;
1043
1044	if (getifflags(env, oenv, &flags) == -1)
1045		err(EXIT_FAILURE, "%s: getifflags", __func__);
1046
1047	d = (prop_data_t)prop_dictionary_get(env, "dstormask");
1048	assert(d != NULL);
1049
1050	if ((flags & IFF_BROADCAST) == 0) {
1051		key = "dst";
1052	} else {
1053		key = "netmask";
1054	}
1055
1056	if (!prop_dictionary_set(oenv, key, (prop_object_t)d))
1057		return -1;
1058
1059	return 0;
1060}
1061
1062static int
1063setifflags(prop_dictionary_t env, prop_dictionary_t oenv)
1064{
1065	struct ifreq ifr;
1066	int64_t ifflag;
1067	bool rc;
1068
1069	rc = prop_dictionary_get_int64(env, "ifflag", &ifflag);
1070	assert(rc);
1071
1072	if (direct_ioctl(env, SIOCGIFFLAGS, &ifr) == -1)
1073		return -1;
1074
1075	if (ifflag < 0) {
1076		ifflag = -ifflag;
1077		ifr.ifr_flags &= ~ifflag;
1078	} else
1079		ifr.ifr_flags |= ifflag;
1080
1081	if (direct_ioctl(env, SIOCSIFFLAGS, &ifr) == -1)
1082		return -1;
1083
1084	return 0;
1085}
1086
1087static int
1088getifcaps(prop_dictionary_t env, prop_dictionary_t oenv, struct ifcapreq *oifcr)
1089{
1090	bool rc;
1091	struct ifcapreq ifcr;
1092	const struct ifcapreq *tmpifcr;
1093	prop_data_t capdata;
1094
1095	capdata = (prop_data_t)prop_dictionary_get(env, "ifcaps");
1096
1097	if (capdata != NULL) {
1098		tmpifcr = prop_data_value(capdata);
1099		*oifcr = *tmpifcr;
1100		return 0;
1101	}
1102
1103	(void)direct_ioctl(env, SIOCGIFCAP, &ifcr);
1104	*oifcr = ifcr;
1105
1106	capdata = prop_data_create_copy(&ifcr, sizeof(ifcr));
1107
1108	rc = prop_dictionary_set(oenv, "ifcaps", capdata);
1109
1110	prop_object_release((prop_object_t)capdata);
1111
1112	return rc ? 0 : -1;
1113}
1114
1115static int
1116setifcaps(prop_dictionary_t env, prop_dictionary_t oenv)
1117{
1118	int64_t ifcap;
1119	bool rc;
1120	prop_data_t capdata;
1121	struct ifcapreq ifcr;
1122
1123	rc = prop_dictionary_get_int64(env, "ifcap", &ifcap);
1124	assert(rc);
1125
1126	if (getifcaps(env, oenv, &ifcr) == -1)
1127		return -1;
1128
1129	if (ifcap < 0) {
1130		ifcap = -ifcap;
1131		ifcr.ifcr_capenable &= ~ifcap;
1132	} else
1133		ifcr.ifcr_capenable |= ifcap;
1134
1135	if ((capdata = prop_data_create_copy(&ifcr, sizeof(ifcr))) == NULL)
1136		return -1;
1137
1138	rc = prop_dictionary_set(oenv, "ifcaps", capdata);
1139	prop_object_release((prop_object_t)capdata);
1140
1141	return rc ? 0 : -1;
1142}
1143
1144static int
1145setifmetric(prop_dictionary_t env, prop_dictionary_t oenv)
1146{
1147	struct ifreq ifr;
1148	bool rc;
1149	int64_t metric;
1150
1151	rc = prop_dictionary_get_int64(env, "metric", &metric);
1152	assert(rc);
1153
1154	ifr.ifr_metric = metric;
1155	if (direct_ioctl(env, SIOCSIFMETRIC, &ifr) == -1)
1156		warn("SIOCSIFMETRIC");
1157	return 0;
1158}
1159
1160static void
1161do_setifpreference(prop_dictionary_t env)
1162{
1163	struct if_addrprefreq ifap;
1164	prop_data_t d;
1165	const struct paddr_prefix *pfx;
1166
1167	memset(&ifap, 0, sizeof(ifap));
1168
1169	if (!prop_dictionary_get_int16(env, "preference",
1170	    &ifap.ifap_preference))
1171		return;
1172
1173	d = (prop_data_t)prop_dictionary_get(env, "address");
1174	assert(d != NULL);
1175
1176	pfx = prop_data_value(d);
1177
1178	memcpy(&ifap.ifap_addr, &pfx->pfx_addr,
1179	    MIN(sizeof(ifap.ifap_addr), pfx->pfx_addr.sa_len));
1180	if (direct_ioctl(env, SIOCSIFADDRPREF, &ifap) == -1)
1181		warn("SIOCSIFADDRPREF");
1182}
1183
1184static int
1185setifmtu(prop_dictionary_t env, prop_dictionary_t oenv)
1186{
1187	int64_t mtu;
1188	bool rc;
1189	struct ifreq ifr;
1190
1191	rc = prop_dictionary_get_int64(env, "mtu", &mtu);
1192	assert(rc);
1193
1194	ifr.ifr_mtu = mtu;
1195	if (direct_ioctl(env, SIOCSIFMTU, &ifr) == -1)
1196		warn("SIOCSIFMTU");
1197
1198	return 0;
1199}
1200
1201static int
1202carrier(prop_dictionary_t env)
1203{
1204	struct ifdatareq ifdr = { .ifdr_data.ifi_link_state = 0 };
1205
1206	if (direct_ioctl(env, SIOCGIFDATA, &ifdr) == -1)
1207		return EXIT_FAILURE;
1208
1209	if (ifdr.ifdr_data.ifi_link_state == LINK_STATE_DOWN)
1210		return EXIT_FAILURE;
1211	else /* Assume UP if UNKNOWN */
1212		return EXIT_SUCCESS;
1213}
1214
1215static void
1216print_plural(const char *prefix, uint64_t n, const char *unit)
1217{
1218	printf("%s%" PRIu64 " %s%s", prefix, n, unit, (n == 1) ? "" : "s");
1219}
1220
1221static void
1222print_human_bytes(bool humanize, uint64_t n)
1223{
1224	char buf[5];
1225
1226	if (humanize) {
1227		(void)humanize_number(buf, sizeof(buf),
1228		    (int64_t)n, "", HN_AUTOSCALE, HN_NOSPACE | HN_DECIMAL);
1229		printf(", %s byte%s", buf, (atof(buf) == 1.0) ? "" : "s");
1230	} else
1231		print_plural(", ", n, "byte");
1232}
1233
1234/*
1235 * Print the status of the interface.  If an address family was
1236 * specified, show it and it only; otherwise, show them all.
1237 */
1238
1239#define MAX_PRINT_LEN 58	/* XXX need a better way to determine this! */
1240
1241void
1242status(prop_dictionary_t env, prop_dictionary_t oenv)
1243{
1244	status_func_t *status_f;
1245	statistics_func_t *statistics_f;
1246	struct ifdatareq ifdr;
1247	struct if_data *ifi;
1248	struct ifreq ifr;
1249	struct ifdrv ifdrv;
1250	char fbuf[BUFSIZ];
1251	char *bp;
1252	int af, s;
1253	const char *ifname;
1254	struct ifcapreq ifcr;
1255	unsigned short flags;
1256	const struct afswtch *afp;
1257	char ifdescr[IFDESCRSIZE];
1258
1259	if ((af = getaf(env)) == -1) {
1260		afp = NULL;
1261		af = AF_UNSPEC;
1262	} else
1263		afp = lookup_af_bynum(af);
1264
1265	/* get out early if the family is unsupported by the kernel */
1266	if ((s = getsock(af)) == -1)
1267		err(EXIT_FAILURE, "%s: getsock", __func__);
1268
1269	if ((ifname = getifinfo(env, oenv, &flags)) == NULL)
1270		err(EXIT_FAILURE, "%s: getifinfo", __func__);
1271
1272	(void)snprintb(fbuf, sizeof(fbuf), IFFBITS, flags);
1273	printf("%s: flags=%s", ifname, fbuf);
1274
1275	estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1276	if (prog_ioctl(s, SIOCGIFMETRIC, &ifr) == -1)
1277		warn("SIOCGIFMETRIC %s", ifr.ifr_name);
1278	else if (ifr.ifr_metric != 0)
1279		printf(" metric %d", ifr.ifr_metric);
1280
1281	estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1282	if (prog_ioctl(s, SIOCGIFMTU, &ifr) != -1 && ifr.ifr_mtu != 0)
1283		printf(" mtu %d", ifr.ifr_mtu);
1284	printf("\n");
1285
1286	if (getifcaps(env, oenv, &ifcr) == -1)
1287		err(EXIT_FAILURE, "%s: getifcaps", __func__);
1288
1289	if (ifcr.ifcr_capabilities != 0) {
1290		(void)snprintb_m(fbuf, sizeof(fbuf), IFCAPBITS,
1291		    ifcr.ifcr_capabilities, MAX_PRINT_LEN);
1292		bp = fbuf;
1293		while (*bp != '\0') {
1294			printf("\tcapabilities=%s\n", bp);
1295			bp += strlen(bp) + 1;
1296		}
1297		(void)snprintb_m(fbuf, sizeof(fbuf), IFCAPBITS,
1298		    ifcr.ifcr_capenable, MAX_PRINT_LEN);
1299		bp = fbuf;
1300		while (*bp != '\0') {
1301			printf("\tenabled=%s\n", bp);
1302			bp += strlen(bp) + 1;
1303		}
1304	}
1305
1306	SIMPLEQ_FOREACH(status_f, &status_funcs, f_next)
1307		(*status_f->f_func)(env, oenv);
1308
1309	estrlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1310	ifr.ifr_buf = &ifdescr;
1311	ifr.ifr_buflen = sizeof(ifdescr);
1312	if (prog_ioctl(s, SIOCGIFDESCR, &ifr) == 0)
1313		printf("\tdescription: \"%s\"\n", (char *)ifr.ifr_buf);
1314
1315	print_link_addresses(env, true);
1316
1317	estrlcpy(ifdrv.ifd_name, ifname, sizeof(ifdrv.ifd_name));
1318	ifdrv.ifd_cmd = IFLINKSTR_QUERYLEN;
1319	ifdrv.ifd_len = 0;
1320	ifdrv.ifd_data = NULL;
1321	/* interface supports linkstr? */
1322	if (prog_ioctl(s, SIOCGLINKSTR, &ifdrv) != -1) {
1323		char *p;
1324
1325		p = malloc(ifdrv.ifd_len);
1326		if (p == NULL)
1327			err(EXIT_FAILURE, "malloc linkstr buf failed");
1328		ifdrv.ifd_data = p;
1329		ifdrv.ifd_cmd = 0;
1330		if (prog_ioctl(s, SIOCGLINKSTR, &ifdrv) == -1)
1331			err(EXIT_FAILURE, "failed to query linkstr");
1332		printf("\tlinkstr: %s\n", (char *)ifdrv.ifd_data);
1333		free(p);
1334	}
1335
1336	media_status(env, oenv);
1337
1338	if (!vflag && !zflag)
1339		goto proto_status;
1340
1341	/* We already have if_data from SIOCGIFDATA in ifa_data. */
1342	estrlcpy(ifdr.ifdr_name, ifname, sizeof(ifdr.ifdr_name));
1343	if (prog_ioctl(s, zflag ? SIOCZIFDATA : SIOCGIFDATA, &ifdr) == -1)
1344		err(EXIT_FAILURE, zflag ? "SIOCZIFDATA" : "SIOCGIFDATA");
1345	ifi = &ifdr.ifdr_data;
1346
1347	print_plural("\tinput: ", ifi->ifi_ipackets, "packet");
1348	print_human_bytes(hflag, ifi->ifi_ibytes);
1349	if (ifi->ifi_imcasts)
1350		print_plural(", ", ifi->ifi_imcasts, "multicast");
1351	if (ifi->ifi_ierrors)
1352		print_plural(", ", ifi->ifi_ierrors, "error");
1353	if (ifi->ifi_iqdrops)
1354		print_plural(", ", ifi->ifi_iqdrops, "queue drop");
1355	if (ifi->ifi_noproto)
1356		printf(", %" PRIu64 " unknown protocol", ifi->ifi_noproto);
1357	print_plural("\n\toutput: ", ifi->ifi_opackets, "packet");
1358	print_human_bytes(hflag, ifi->ifi_obytes);
1359	if (ifi->ifi_omcasts)
1360		print_plural(", ", ifi->ifi_omcasts, "multicast");
1361	if (ifi->ifi_oerrors)
1362		print_plural(", ", ifi->ifi_oerrors, "error");
1363	if (ifi->ifi_collisions)
1364		print_plural(", ", ifi->ifi_collisions, "collision");
1365	printf("\n");
1366
1367	SIMPLEQ_FOREACH(statistics_f, &statistics_funcs, f_next)
1368		(*statistics_f->f_func)(env);
1369
1370 proto_status:
1371
1372	if (afp != NULL)
1373		(*afp->af_status)(env, oenv, true);
1374	else SIMPLEQ_FOREACH(afp, &aflist, af_next)
1375		(*afp->af_status)(env, oenv, false);
1376}
1377
1378static int
1379setifprefixlen(prop_dictionary_t env, prop_dictionary_t oenv)
1380{
1381	bool rc;
1382	int64_t plen;
1383	int af;
1384	struct paddr_prefix *pfx;
1385	prop_data_t d;
1386
1387	if ((af = getaf(env)) == -1)
1388		af = AF_INET;
1389
1390	rc = prop_dictionary_get_int64(env, "prefixlen", &plen);
1391	assert(rc);
1392
1393	pfx = prefixlen_to_mask(af, plen);
1394	if (pfx == NULL)
1395		err(EXIT_FAILURE, "prefixlen_to_mask");
1396
1397	d = prop_data_create_copy(pfx, paddr_prefix_size(pfx));
1398	if (d == NULL)
1399		err(EXIT_FAILURE, "%s: prop_data_create_copy", __func__);
1400
1401	if (!prop_dictionary_set(oenv, "netmask", (prop_object_t)d))
1402		err(EXIT_FAILURE, "%s: prop_dictionary_set", __func__);
1403
1404	free(pfx);
1405	return 0;
1406}
1407
1408static int
1409setlinkstr(prop_dictionary_t env, prop_dictionary_t oenv)
1410{
1411	struct ifdrv ifdrv;
1412	size_t linkstrlen;
1413	prop_data_t data;
1414	char *linkstr;
1415
1416	data = (prop_data_t)prop_dictionary_get(env, "linkstr");
1417	if (data == NULL) {
1418		errno = ENOENT;
1419		return -1;
1420	}
1421	linkstrlen = prop_data_size(data)+1;
1422
1423	linkstr = malloc(linkstrlen);
1424	if (linkstr == NULL)
1425		err(EXIT_FAILURE, "malloc linkstr space");
1426	if (getargstr(env, "linkstr", linkstr, linkstrlen) == -1)
1427		errx(EXIT_FAILURE, "getargstr linkstr failed");
1428
1429	ifdrv.ifd_cmd = 0;
1430	ifdrv.ifd_len = linkstrlen;
1431	ifdrv.ifd_data = __UNCONST(linkstr);
1432
1433	if (direct_ioctl(env, SIOCSLINKSTR, &ifdrv) == -1)
1434		err(EXIT_FAILURE, "SIOCSLINKSTR");
1435	free(linkstr);
1436
1437	return 0;
1438}
1439
1440static int
1441unsetlinkstr(prop_dictionary_t env, prop_dictionary_t oenv)
1442{
1443	struct ifdrv ifdrv;
1444
1445	memset(&ifdrv, 0, sizeof(ifdrv));
1446	ifdrv.ifd_cmd = IFLINKSTR_UNSET;
1447
1448	if (direct_ioctl(env, SIOCSLINKSTR, &ifdrv) == -1)
1449		err(EXIT_FAILURE, "SIOCSLINKSTR");
1450
1451	return 0;
1452}
1453
1454static int
1455setifdescr(prop_dictionary_t env, prop_dictionary_t oenv)
1456{
1457	struct ifreq ifr;
1458	size_t len;
1459	prop_data_t data;
1460	char *descr;
1461
1462	data = (prop_data_t)prop_dictionary_get(env, "descr");
1463	if (data == NULL) {
1464		errno = ENOENT;
1465		return -1;
1466	}
1467	len = prop_data_size(data) + 1;
1468
1469	if (len > IFDESCRSIZE)
1470		err(EXIT_FAILURE, "description too long");
1471
1472	descr = malloc(len);
1473	if (descr == NULL)
1474		err(EXIT_FAILURE, "malloc description space");
1475	if (getargstr(env, "descr", descr, len) == -1)
1476		errx(EXIT_FAILURE, "getargstr descr failed");
1477
1478
1479	ifr.ifr_buf = descr;
1480	ifr.ifr_buflen = len;
1481	if (direct_ioctl(env, SIOCSIFDESCR, &ifr) != 0)
1482		err(EXIT_FAILURE, "SIOCSIFDESCR");
1483
1484	free(descr);
1485
1486	return 0;
1487}
1488
1489static int
1490unsetifdescr(prop_dictionary_t env, prop_dictionary_t oenv)
1491{
1492	struct ifreq ifr;
1493	ifr.ifr_buf = NULL;
1494	ifr.ifr_buflen = 0;
1495
1496	if (direct_ioctl(env, SIOCSIFDESCR, &ifr) != 0)
1497		err(EXIT_FAILURE, "SIOCSIFDESCR");
1498
1499	return 0;
1500}
1501
1502
1503static void
1504usage(void)
1505{
1506	const char *progname = getprogname();
1507	usage_func_t *usage_f;
1508	prop_dictionary_t env;
1509
1510	if ((env = prop_dictionary_create()) == NULL)
1511		err(EXIT_FAILURE, "%s: prop_dictionary_create", __func__);
1512
1513	fprintf(stderr, "usage: %s [-h] %s[-v] [-z] %sinterface\n"
1514		"\t[ af [ address [ dest_addr ] ] [ netmask mask ] [ prefixlen n ]\n"
1515		"\t\t[ alias | -alias ] ]\n"
1516		"\t[ up ] [ down ] [ metric n ] [ mtu n ]\n", progname,
1517		flag_is_registered(gflags, 'm') ? "[-m] " : "",
1518		flag_is_registered(gflags, 'L') ? "[-L] " : "");
1519
1520	SIMPLEQ_FOREACH(usage_f, &usage_funcs, f_next)
1521		(*usage_f->f_func)(env);
1522
1523	fprintf(stderr,
1524		"\t[ arp | -arp ]\n"
1525		"\t[ preference n ]\n"
1526		"\t[ link0 | -link0 ] [ link1 | -link1 ] [ link2 | -link2 ]\n"
1527		"\t[ linkstr str | -linkstr ]\n"
1528		"\t[ unnumbered | -unnumbered ]\n"
1529		"\t[ description str | descr str | -description | -descr ]\n"
1530		"       %s -a [-b] [-d] [-h] %s[-u] [-v] [-z] [ af ]\n"
1531		"       %s -l [-b] [-d] [-s] [-u]\n"
1532		"       %s -C\n"
1533		"       %s -w n\n"
1534		"       %s interface create\n"
1535		"       %s interface destroy\n",
1536		progname, flag_is_registered(gflags, 'm') ? "[-m] " : "",
1537		progname, progname, progname, progname, progname);
1538
1539	prop_object_release((prop_object_t)env);
1540	exit(EXIT_FAILURE);
1541}
1542