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