]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2009-2019 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
29 | /* | |
30 | * Copyright (c) 1983, 1993 | |
31 | * The Regents of the University of California. All rights reserved. | |
32 | * | |
33 | * Redistribution and use in source and binary forms, with or without | |
34 | * modification, are permitted provided that the following conditions | |
35 | * are met: | |
36 | * 1. Redistributions of source code must retain the above copyright | |
37 | * notice, this list of conditions and the following disclaimer. | |
38 | * 2. Redistributions in binary form must reproduce the above copyright | |
39 | * notice, this list of conditions and the following disclaimer in the | |
40 | * documentation and/or other materials provided with the distribution. | |
41 | * 4. Neither the name of the University nor the names of its contributors | |
42 | * may be used to endorse or promote products derived from this software | |
43 | * without specific prior written permission. | |
44 | * | |
45 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
46 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
47 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
48 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
49 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
50 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
51 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
52 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
53 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
54 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
55 | * SUCH DAMAGE. | |
56 | */ | |
57 | ||
58 | #include <sys/cdefs.h> | |
59 | ||
60 | #ifndef lint | |
61 | __unused static const char copyright[] = | |
62 | "@(#) Copyright (c) 1983, 1993\n\ | |
63 | The Regents of the University of California. All rights reserved.\n"; | |
64 | #endif /* not lint */ | |
65 | ||
66 | #include <sys/param.h> | |
67 | #include <sys/ioctl.h> | |
68 | #include <sys/socket.h> | |
69 | #include <sys/sysctl.h> | |
70 | #include <sys/time.h> | |
71 | #ifndef __APPLE__ | |
72 | #include <sys/module.h> | |
73 | #include <sys/linker.h> | |
74 | #endif | |
75 | ||
76 | #include <net/ethernet.h> | |
77 | #include <net/if.h> | |
78 | #include <net/if_var.h> | |
79 | #include <net/if_dl.h> | |
80 | #include <net/if_types.h> | |
81 | #include <net/if_mib.h> | |
82 | #include <net/route.h> | |
83 | #include <net/pktsched/pktsched.h> | |
84 | #include <net/network_agent.h> | |
85 | ||
86 | /* IP */ | |
87 | #include <netinet/in.h> | |
88 | #include <netinet/in_var.h> | |
89 | #include <arpa/inet.h> | |
90 | #include <netdb.h> | |
91 | ||
92 | #include <ifaddrs.h> | |
93 | #include <ctype.h> | |
94 | #include <err.h> | |
95 | #include <errno.h> | |
96 | #include <fcntl.h> | |
97 | #include <math.h> | |
98 | #include <stdio.h> | |
99 | #include <stdlib.h> | |
100 | #include <string.h> | |
101 | #include <strings.h> | |
102 | #include <unistd.h> | |
103 | #include <sysexits.h> | |
104 | #include <syslog.h> | |
105 | ||
106 | #include "ifconfig.h" | |
107 | ||
108 | #ifdef __APPLE__ | |
109 | #include <TargetConditionals.h> | |
110 | #endif | |
111 | ||
112 | /* | |
113 | * Since "struct ifreq" is composed of various union members, callers | |
114 | * should pay special attention to interprete the value. | |
115 | * (.e.g. little/big endian difference in the structure.) | |
116 | */ | |
117 | struct ifreq ifr; | |
118 | ||
119 | char name[IFNAMSIZ]; | |
120 | int setaddr; | |
121 | int setmask; | |
122 | int doalias; | |
123 | int clearaddr; | |
124 | int newaddr = 1; | |
125 | int noload; | |
126 | int all; | |
127 | ||
128 | int bond_details = 0; | |
129 | int supmedia = 0; | |
130 | #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) | |
131 | int verbose = 1; | |
132 | int showrtref = 1; | |
133 | #else /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */ | |
134 | int verbose = 0; | |
135 | int showrtref = 0; | |
136 | #endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */ | |
137 | int printkeys = 0; /* Print keying material for interfaces. */ | |
138 | ||
139 | static int ifconfig(int argc, char *const *argv, int iscreate, | |
140 | const struct afswtch *afp); | |
141 | static void status(const struct afswtch *afp, const struct sockaddr_dl *sdl, | |
142 | struct ifaddrs *ifa); | |
143 | static char *bytes_to_str(unsigned long long bytes); | |
144 | static char *bps_to_str(unsigned long long rate); | |
145 | static char *ns_to_str(unsigned long long nsec); | |
146 | static void tunnel_status(int s); | |
147 | static void clat46_addr(int s, char *name); | |
148 | static void nat64_status(int s, char *name); | |
149 | static void usage(void); | |
150 | static char *sched2str(unsigned int s); | |
151 | static char *tl2str(unsigned int s); | |
152 | static char *ift2str(unsigned int t, unsigned int f, unsigned int sf); | |
153 | static char *iffunct2str(u_int32_t functional_type); | |
154 | ||
155 | static struct afswtch *af_getbyname(const char *name); | |
156 | static struct afswtch *af_getbyfamily(int af); | |
157 | static void af_other_status(int); | |
158 | ||
159 | static struct option *opts = NULL; | |
160 | ||
161 | void | |
162 | opt_register(struct option *p) | |
163 | { | |
164 | p->next = opts; | |
165 | opts = p; | |
166 | } | |
167 | ||
168 | static void | |
169 | usage(void) | |
170 | { | |
171 | char options[1024]; | |
172 | struct option *p; | |
173 | ||
174 | /* XXX not right but close enough for now */ | |
175 | options[0] = '\0'; | |
176 | for (p = opts; p != NULL; p = p->next) { | |
177 | strlcat(options, p->opt_usage, sizeof(options)); | |
178 | strlcat(options, " ", sizeof(options)); | |
179 | } | |
180 | ||
181 | fprintf(stderr, | |
182 | "usage: ifconfig %sinterface address_family [address [dest_address]]\n" | |
183 | " [parameters]\n" | |
184 | " ifconfig interface create\n" | |
185 | " ifconfig -a %s[-d] [-m] [-u] [-v] [address_family]\n" | |
186 | " ifconfig -l [-d] [-u] [address_family]\n" | |
187 | " ifconfig %s[-d] [-m] [-u] [-v]\n", | |
188 | options, options, options); | |
189 | exit(1); | |
190 | } | |
191 | ||
192 | int | |
193 | main(int argc, char *argv[]) | |
194 | { | |
195 | int c, namesonly, downonly, uponly; | |
196 | const struct afswtch *afp = NULL; | |
197 | int ifindex; | |
198 | struct ifaddrs *ifap, *ifa; | |
199 | struct ifreq paifr; | |
200 | const struct sockaddr_dl *sdl; | |
201 | char options[1024], *cp; | |
202 | const char *ifname; | |
203 | struct option *p; | |
204 | size_t iflen; | |
205 | ||
206 | all = downonly = uponly = namesonly = noload = 0; | |
207 | ||
208 | /* Parse leading line options */ | |
209 | #ifndef __APPLE__ | |
210 | strlcpy(options, "adklmnuv", sizeof(options)); | |
211 | #else | |
212 | strlcpy(options, "abdlmruv", sizeof(options)); | |
213 | #endif | |
214 | for (p = opts; p != NULL; p = p->next) | |
215 | strlcat(options, p->opt, sizeof(options)); | |
216 | while ((c = getopt(argc, argv, options)) != -1) { | |
217 | switch (c) { | |
218 | case 'a': /* scan all interfaces */ | |
219 | all++; | |
220 | break; | |
221 | case 'b': /* bond detailed output */ | |
222 | bond_details++; | |
223 | break; | |
224 | case 'd': /* restrict scan to "down" interfaces */ | |
225 | downonly++; | |
226 | break; | |
227 | #ifndef __APPLE__ | |
228 | case 'k': | |
229 | printkeys++; | |
230 | break; | |
231 | #endif | |
232 | case 'l': /* scan interface names only */ | |
233 | namesonly++; | |
234 | break; | |
235 | case 'm': /* show media choices in status */ | |
236 | supmedia = 1; | |
237 | break; | |
238 | #ifndef __APPLE__ | |
239 | case 'n': /* suppress module loading */ | |
240 | noload++; | |
241 | break; | |
242 | #endif | |
243 | case 'r': | |
244 | showrtref++; | |
245 | break; | |
246 | case 'u': /* restrict scan to "up" interfaces */ | |
247 | uponly++; | |
248 | break; | |
249 | case 'v': | |
250 | verbose++; | |
251 | break; | |
252 | default: | |
253 | for (p = opts; p != NULL; p = p->next) | |
254 | if (p->opt[0] == c) { | |
255 | p->cb(optarg); | |
256 | break; | |
257 | } | |
258 | if (p == NULL) | |
259 | usage(); | |
260 | break; | |
261 | } | |
262 | } | |
263 | argc -= optind; | |
264 | argv += optind; | |
265 | ||
266 | /* -l cannot be used with -a or -q or -m or -b */ | |
267 | if (namesonly && | |
268 | (all || supmedia || bond_details)) | |
269 | usage(); | |
270 | ||
271 | /* nonsense.. */ | |
272 | if (uponly && downonly) | |
273 | usage(); | |
274 | ||
275 | /* no arguments is equivalent to '-a' */ | |
276 | if (!namesonly && argc < 1) | |
277 | all = 1; | |
278 | ||
279 | /* -a and -l allow an address family arg to limit the output */ | |
280 | if (all || namesonly) { | |
281 | if (argc > 1) | |
282 | usage(); | |
283 | ||
284 | ifname = NULL; | |
285 | if (argc == 1) { | |
286 | afp = af_getbyname(*argv); | |
287 | if (afp == NULL) | |
288 | usage(); | |
289 | if (afp->af_name != NULL) | |
290 | argc--, argv++; | |
291 | /* leave with afp non-zero */ | |
292 | } | |
293 | } else { | |
294 | /* not listing, need an argument */ | |
295 | if (argc < 1) | |
296 | usage(); | |
297 | ||
298 | ifname = *argv; | |
299 | argc--, argv++; | |
300 | ||
301 | #ifdef notdef | |
302 | /* check and maybe load support for this interface */ | |
303 | ifmaybeload(ifname); | |
304 | #endif | |
305 | ifindex = if_nametoindex(ifname); | |
306 | if (ifindex == 0) { | |
307 | /* | |
308 | * NOTE: We must special-case the `create' command | |
309 | * right here as we would otherwise fail when trying | |
310 | * to find the interface. | |
311 | */ | |
312 | if (argc > 0 && (strcmp(argv[0], "create") == 0 || | |
313 | strcmp(argv[0], "plumb") == 0)) { | |
314 | iflen = strlcpy(name, ifname, sizeof(name)); | |
315 | if (iflen >= sizeof(name)) | |
316 | errx(1, "%s: cloning name too long", | |
317 | ifname); | |
318 | ifconfig(argc, argv, 1, NULL); | |
319 | exit(0); | |
320 | } | |
321 | errx(1, "interface %s does not exist", ifname); | |
322 | } | |
323 | } | |
324 | ||
325 | /* Check for address family */ | |
326 | if (argc > 0) { | |
327 | afp = af_getbyname(*argv); | |
328 | if (afp != NULL) | |
329 | argc--, argv++; | |
330 | } | |
331 | ||
332 | if (getifaddrs(&ifap) != 0) | |
333 | err(EXIT_FAILURE, "getifaddrs"); | |
334 | cp = NULL; | |
335 | ifindex = 0; | |
336 | for (ifa = ifap; ifa; ifa = ifa->ifa_next) { | |
337 | memset(&paifr, 0, sizeof(paifr)); | |
338 | strlcpy(paifr.ifr_name, ifa->ifa_name, sizeof(paifr.ifr_name)); | |
339 | if (sizeof(paifr.ifr_addr) >= ifa->ifa_addr->sa_len) { | |
340 | memcpy(&paifr.ifr_addr, ifa->ifa_addr, | |
341 | ifa->ifa_addr->sa_len); | |
342 | } | |
343 | ||
344 | if (ifname != NULL && strcmp(ifname, ifa->ifa_name) != 0) | |
345 | continue; | |
346 | if (ifa->ifa_addr->sa_family == AF_LINK) | |
347 | sdl = (const struct sockaddr_dl *) ifa->ifa_addr; | |
348 | else | |
349 | sdl = NULL; | |
350 | if (cp != NULL && strcmp(cp, ifa->ifa_name) == 0) | |
351 | continue; | |
352 | iflen = strlcpy(name, ifa->ifa_name, sizeof(name)); | |
353 | if (iflen >= sizeof(name)) { | |
354 | warnx("%s: interface name too long, skipping", | |
355 | ifa->ifa_name); | |
356 | continue; | |
357 | } | |
358 | cp = ifa->ifa_name; | |
359 | ||
360 | if (downonly && (ifa->ifa_flags & IFF_UP) != 0) | |
361 | continue; | |
362 | if (uponly && (ifa->ifa_flags & IFF_UP) == 0) | |
363 | continue; | |
364 | ifindex++; | |
365 | /* | |
366 | * Are we just listing the interfaces? | |
367 | */ | |
368 | if (namesonly) { | |
369 | if (ifindex > 1) | |
370 | printf(" "); | |
371 | fputs(name, stdout); | |
372 | continue; | |
373 | } | |
374 | ||
375 | if (argc > 0) | |
376 | ifconfig(argc, argv, 0, afp); | |
377 | else | |
378 | status(afp, sdl, ifa); | |
379 | } | |
380 | if (namesonly) | |
381 | printf("\n"); | |
382 | freeifaddrs(ifap); | |
383 | ||
384 | exit(0); | |
385 | } | |
386 | ||
387 | static struct afswtch *afs = NULL; | |
388 | ||
389 | void | |
390 | af_register(struct afswtch *p) | |
391 | { | |
392 | p->af_next = afs; | |
393 | afs = p; | |
394 | } | |
395 | ||
396 | static struct afswtch * | |
397 | af_getbyname(const char *name) | |
398 | { | |
399 | struct afswtch *afp; | |
400 | ||
401 | for (afp = afs; afp != NULL; afp = afp->af_next) | |
402 | if (strcmp(afp->af_name, name) == 0) | |
403 | return afp; | |
404 | return NULL; | |
405 | } | |
406 | ||
407 | static struct afswtch * | |
408 | af_getbyfamily(int af) | |
409 | { | |
410 | struct afswtch *afp; | |
411 | ||
412 | for (afp = afs; afp != NULL; afp = afp->af_next) | |
413 | if (afp->af_af == af) | |
414 | return afp; | |
415 | return NULL; | |
416 | } | |
417 | ||
418 | static void | |
419 | af_other_status(int s) | |
420 | { | |
421 | struct afswtch *afp; | |
422 | uint8_t afmask[howmany(AF_MAX, NBBY)]; | |
423 | ||
424 | memset(afmask, 0, sizeof(afmask)); | |
425 | for (afp = afs; afp != NULL; afp = afp->af_next) { | |
426 | if (afp->af_other_status == NULL) | |
427 | continue; | |
428 | if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) | |
429 | continue; | |
430 | afp->af_other_status(s); | |
431 | setbit(afmask, afp->af_af); | |
432 | } | |
433 | } | |
434 | ||
435 | static void | |
436 | af_all_tunnel_status(int s) | |
437 | { | |
438 | struct afswtch *afp; | |
439 | uint8_t afmask[howmany(AF_MAX, NBBY)]; | |
440 | ||
441 | memset(afmask, 0, sizeof(afmask)); | |
442 | for (afp = afs; afp != NULL; afp = afp->af_next) { | |
443 | if (afp->af_status_tunnel == NULL) | |
444 | continue; | |
445 | if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) | |
446 | continue; | |
447 | afp->af_status_tunnel(s); | |
448 | setbit(afmask, afp->af_af); | |
449 | } | |
450 | } | |
451 | ||
452 | static struct cmd *cmds = NULL; | |
453 | ||
454 | void | |
455 | cmd_register(struct cmd *p) | |
456 | { | |
457 | p->c_next = cmds; | |
458 | cmds = p; | |
459 | } | |
460 | ||
461 | static const struct cmd * | |
462 | cmd_lookup(const char *name) | |
463 | { | |
464 | #define N(a) (sizeof(a)/sizeof(a[0])) | |
465 | const struct cmd *p; | |
466 | ||
467 | for (p = cmds; p != NULL; p = p->c_next) | |
468 | if (strcmp(name, p->c_name) == 0) | |
469 | return p; | |
470 | return NULL; | |
471 | #undef N | |
472 | } | |
473 | ||
474 | struct callback { | |
475 | callback_func *cb_func; | |
476 | void *cb_arg; | |
477 | struct callback *cb_next; | |
478 | }; | |
479 | static struct callback *callbacks = NULL; | |
480 | ||
481 | void | |
482 | callback_register(callback_func *func, void *arg) | |
483 | { | |
484 | struct callback *cb; | |
485 | ||
486 | cb = malloc(sizeof(struct callback)); | |
487 | if (cb == NULL) | |
488 | errx(1, "unable to allocate memory for callback"); | |
489 | cb->cb_func = func; | |
490 | cb->cb_arg = arg; | |
491 | cb->cb_next = callbacks; | |
492 | callbacks = cb; | |
493 | } | |
494 | ||
495 | /* specially-handled commands */ | |
496 | static void setifaddr(const char *, int, int, const struct afswtch *); | |
497 | static const struct cmd setifaddr_cmd = DEF_CMD("ifaddr", 0, setifaddr); | |
498 | ||
499 | static void setifdstaddr(const char *, int, int, const struct afswtch *); | |
500 | static const struct cmd setifdstaddr_cmd = | |
501 | DEF_CMD("ifdstaddr", 0, setifdstaddr); | |
502 | ||
503 | static int | |
504 | ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *afp) | |
505 | { | |
506 | const struct afswtch *nafp; | |
507 | struct callback *cb; | |
508 | int ret, s; | |
509 | ||
510 | strlcpy(ifr.ifr_name, name, sizeof ifr.ifr_name); | |
511 | top: | |
512 | if (afp == NULL) | |
513 | afp = af_getbyname("inet"); | |
514 | ifr.ifr_addr.sa_family = | |
515 | afp->af_af == AF_LINK || afp->af_af == AF_UNSPEC ? | |
516 | AF_INET : afp->af_af; | |
517 | ||
518 | if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0) | |
519 | err(1, "socket(family %u,SOCK_DGRAM", ifr.ifr_addr.sa_family); | |
520 | ||
521 | while (argc > 0) { | |
522 | const struct cmd *p; | |
523 | ||
524 | p = cmd_lookup(*argv); | |
525 | if (p == NULL) { | |
526 | /* | |
527 | * Not a recognized command, choose between setting | |
528 | * the interface address and the dst address. | |
529 | */ | |
530 | p = (setaddr ? &setifdstaddr_cmd : &setifaddr_cmd); | |
531 | } | |
532 | if (p->c_u.c_func || p->c_u.c_func2) { | |
533 | if (iscreate && !p->c_iscloneop) { | |
534 | /* | |
535 | * Push the clone create callback so the new | |
536 | * device is created and can be used for any | |
537 | * remaining arguments. | |
538 | */ | |
539 | cb = callbacks; | |
540 | if (cb == NULL) | |
541 | errx(1, "internal error, no callback"); | |
542 | callbacks = cb->cb_next; | |
543 | cb->cb_func(s, cb->cb_arg); | |
544 | iscreate = 0; | |
545 | /* | |
546 | * Handle any address family spec that | |
547 | * immediately follows and potentially | |
548 | * recreate the socket. | |
549 | */ | |
550 | nafp = af_getbyname(*argv); | |
551 | if (nafp != NULL) { | |
552 | argc--, argv++; | |
553 | if (nafp != afp) { | |
554 | close(s); | |
555 | afp = nafp; | |
556 | goto top; | |
557 | } | |
558 | } | |
559 | } | |
560 | if (p->c_parameter == NEXTARG) { | |
561 | if (argv[1] == NULL) | |
562 | errx(1, "'%s' requires argument", | |
563 | p->c_name); | |
564 | p->c_u.c_func(argv[1], 0, s, afp); | |
565 | argc--, argv++; | |
566 | } else if (p->c_parameter == OPTARG) { | |
567 | p->c_u.c_func(argv[1], 0, s, afp); | |
568 | if (argv[1] != NULL) | |
569 | argc--, argv++; | |
570 | } else if (p->c_parameter == NEXTARG2) { | |
571 | if (argc < 3) | |
572 | errx(1, "'%s' requires 2 arguments", | |
573 | p->c_name); | |
574 | p->c_u.c_func2(argv[1], argv[2], s, afp); | |
575 | argc -= 2, argv += 2; | |
576 | } else if (p->c_parameter == VAARGS) { | |
577 | ret = p->c_u.c_funcv(argc - 1, argv + 1, s, afp); | |
578 | if (ret < 0) | |
579 | errx(1, "'%s' command error", | |
580 | p->c_name); | |
581 | argc -= ret, argv += ret; | |
582 | } else { | |
583 | p->c_u.c_func(*argv, p->c_parameter, s, afp); | |
584 | } | |
585 | } | |
586 | argc--, argv++; | |
587 | } | |
588 | ||
589 | /* | |
590 | * Do any post argument processing required by the address family. | |
591 | */ | |
592 | if (afp->af_postproc != NULL) | |
593 | afp->af_postproc(s, afp); | |
594 | /* | |
595 | * Do deferred callbacks registered while processing | |
596 | * command-line arguments. | |
597 | */ | |
598 | for (cb = callbacks; cb != NULL; cb = cb->cb_next) | |
599 | cb->cb_func(s, cb->cb_arg); | |
600 | /* | |
601 | * Do deferred operations. | |
602 | */ | |
603 | if (clearaddr) { | |
604 | if (afp->af_ridreq == NULL || afp->af_difaddr == 0) { | |
605 | warnx("interface %s cannot change %s addresses!", | |
606 | name, afp->af_name); | |
607 | clearaddr = 0; | |
608 | } | |
609 | } | |
610 | if (clearaddr) { | |
611 | strlcpy(afp->af_ridreq, name, sizeof ifr.ifr_name); | |
612 | ret = ioctl(s, afp->af_difaddr, afp->af_ridreq); | |
613 | if (ret < 0) { | |
614 | if (errno == EADDRNOTAVAIL && (doalias >= 0)) { | |
615 | /* means no previous address for interface */ | |
616 | } else | |
617 | Perror("ioctl (SIOCDIFADDR)"); | |
618 | } | |
619 | } | |
620 | if (newaddr) { | |
621 | if (afp->af_addreq == NULL || afp->af_aifaddr == 0) { | |
622 | warnx("interface %s cannot change %s addresses!", | |
623 | name, afp->af_name); | |
624 | newaddr = 0; | |
625 | } | |
626 | } | |
627 | if (newaddr && (setaddr || setmask)) { | |
628 | strlcpy(afp->af_addreq, name, sizeof ifr.ifr_name); | |
629 | if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0) | |
630 | Perror("ioctl (SIOCAIFADDR)"); | |
631 | } | |
632 | ||
633 | close(s); | |
634 | return(0); | |
635 | } | |
636 | ||
637 | /*ARGSUSED*/ | |
638 | static void | |
639 | setifaddr(const char *addr, int param, int s, const struct afswtch *afp) | |
640 | { | |
641 | if (afp->af_getaddr == NULL) | |
642 | return; | |
643 | /* | |
644 | * Delay the ioctl to set the interface addr until flags are all set. | |
645 | * The address interpretation may depend on the flags, | |
646 | * and the flags may change when the address is set. | |
647 | */ | |
648 | setaddr++; | |
649 | if (doalias == 0 && afp->af_af != AF_LINK) | |
650 | clearaddr = 1; | |
651 | afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR)); | |
652 | } | |
653 | ||
654 | static void | |
655 | settunnel(const char *src, const char *dst, int s, const struct afswtch *afp) | |
656 | { | |
657 | struct addrinfo *srcres, *dstres; | |
658 | int ecode; | |
659 | ||
660 | if (afp->af_settunnel == NULL) { | |
661 | warn("address family %s does not support tunnel setup", | |
662 | afp->af_name); | |
663 | return; | |
664 | } | |
665 | ||
666 | if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0) | |
667 | errx(1, "error in parsing address string: %s", | |
668 | gai_strerror(ecode)); | |
669 | ||
670 | if ((ecode = getaddrinfo(dst, NULL, NULL, &dstres)) != 0) | |
671 | errx(1, "error in parsing address string: %s", | |
672 | gai_strerror(ecode)); | |
673 | ||
674 | if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family) | |
675 | errx(1, | |
676 | "source and destination address families do not match"); | |
677 | ||
678 | afp->af_settunnel(s, srcres, dstres); | |
679 | ||
680 | freeaddrinfo(srcres); | |
681 | freeaddrinfo(dstres); | |
682 | } | |
683 | ||
684 | /* ARGSUSED */ | |
685 | static void | |
686 | deletetunnel(const char *vname, int param, int s, const struct afswtch *afp) | |
687 | { | |
688 | ||
689 | if (ioctl(s, SIOCDIFPHYADDR, &ifr) < 0) | |
690 | err(1, "SIOCDIFPHYADDR"); | |
691 | } | |
692 | ||
693 | static void | |
694 | setifnetmask(const char *addr, int dummy __unused, int s, | |
695 | const struct afswtch *afp) | |
696 | { | |
697 | if (afp->af_getaddr != NULL) { | |
698 | setmask++; | |
699 | afp->af_getaddr(addr, MASK); | |
700 | } | |
701 | } | |
702 | ||
703 | static void | |
704 | setifbroadaddr(const char *addr, int dummy __unused, int s, | |
705 | const struct afswtch *afp) | |
706 | { | |
707 | if (afp->af_getaddr != NULL) | |
708 | afp->af_getaddr(addr, DSTADDR); | |
709 | } | |
710 | ||
711 | static void | |
712 | setifipdst(const char *addr, int dummy __unused, int s, | |
713 | const struct afswtch *afp) | |
714 | { | |
715 | const struct afswtch *inet; | |
716 | ||
717 | inet = af_getbyname("inet"); | |
718 | if (inet == NULL) | |
719 | return; | |
720 | inet->af_getaddr(addr, DSTADDR); | |
721 | clearaddr = 0; | |
722 | newaddr = 0; | |
723 | } | |
724 | ||
725 | static void | |
726 | notealias(const char *addr, int param, int s, const struct afswtch *afp) | |
727 | { | |
728 | #define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr)) | |
729 | if (setaddr && doalias == 0 && param < 0) | |
730 | if (afp->af_addreq != NULL && afp->af_ridreq != NULL) | |
731 | bcopy((caddr_t)rqtosa(af_addreq), | |
732 | (caddr_t)rqtosa(af_ridreq), | |
733 | rqtosa(af_addreq)->sa_len); | |
734 | doalias = param; | |
735 | if (param < 0) { | |
736 | clearaddr = 1; | |
737 | newaddr = 0; | |
738 | } else | |
739 | clearaddr = 0; | |
740 | #undef rqtosa | |
741 | } | |
742 | ||
743 | /*ARGSUSED*/ | |
744 | static void | |
745 | setifdstaddr(const char *addr, int param __unused, int s, | |
746 | const struct afswtch *afp) | |
747 | { | |
748 | if (afp->af_getaddr != NULL) | |
749 | afp->af_getaddr(addr, DSTADDR); | |
750 | } | |
751 | ||
752 | /* | |
753 | * Note: doing an SIOCIGIFFLAGS scribbles on the union portion | |
754 | * of the ifreq structure, which may confuse other parts of ifconfig. | |
755 | * Make a private copy so we can avoid that. | |
756 | */ | |
757 | static void | |
758 | setifflags(const char *vname, int value, int s, const struct afswtch *afp) | |
759 | { | |
760 | struct ifreq my_ifr; | |
761 | int flags; | |
762 | ||
763 | bcopy((char *)&ifr, (char *)&my_ifr, sizeof(struct ifreq)); | |
764 | ||
765 | if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) { | |
766 | Perror("ioctl (SIOCGIFFLAGS)"); | |
767 | exit(1); | |
768 | } | |
769 | strlcpy(my_ifr.ifr_name, name, sizeof (my_ifr.ifr_name)); | |
770 | flags = my_ifr.ifr_flags; | |
771 | ||
772 | if (value < 0) { | |
773 | value = -value; | |
774 | flags &= ~value; | |
775 | } else | |
776 | flags |= value; | |
777 | my_ifr.ifr_flags = flags & 0xffff; | |
778 | if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) | |
779 | Perror(vname); | |
780 | } | |
781 | ||
782 | void | |
783 | setifcap(const char *vname, int value, int s, const struct afswtch *afp) | |
784 | { | |
785 | int flags; | |
786 | ||
787 | if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) < 0) { | |
788 | Perror("ioctl (SIOCGIFCAP)"); | |
789 | exit(1); | |
790 | } | |
791 | flags = ifr.ifr_curcap; | |
792 | if (value < 0) { | |
793 | value = -value; | |
794 | flags &= ~value; | |
795 | } else | |
796 | flags |= value; | |
797 | flags &= ifr.ifr_reqcap; | |
798 | ifr.ifr_reqcap = flags; | |
799 | if (ioctl(s, SIOCSIFCAP, (caddr_t)&ifr) < 0) | |
800 | Perror(vname); | |
801 | } | |
802 | ||
803 | static void | |
804 | setifmetric(const char *val, int dummy __unused, int s, | |
805 | const struct afswtch *afp) | |
806 | { | |
807 | strlcpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); | |
808 | ifr.ifr_metric = atoi(val); | |
809 | if (ioctl(s, SIOCSIFMETRIC, (caddr_t)&ifr) < 0) | |
810 | warn("ioctl (set metric)"); | |
811 | } | |
812 | ||
813 | static void | |
814 | setifmtu(const char *val, int dummy __unused, int s, | |
815 | const struct afswtch *afp) | |
816 | { | |
817 | strlcpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); | |
818 | ifr.ifr_mtu = atoi(val); | |
819 | if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) < 0) | |
820 | warn("ioctl (set mtu)"); | |
821 | } | |
822 | ||
823 | #ifndef __APPLE__ | |
824 | static void | |
825 | setifname(const char *val, int dummy __unused, int s, | |
826 | const struct afswtch *afp) | |
827 | { | |
828 | char *newname; | |
829 | ||
830 | newname = strdup(val); | |
831 | if (newname == NULL) { | |
832 | warn("no memory to set ifname"); | |
833 | return; | |
834 | } | |
835 | ifr.ifr_data = newname; | |
836 | if (ioctl(s, SIOCSIFNAME, (caddr_t)&ifr) < 0) { | |
837 | warn("ioctl (set name)"); | |
838 | free(newname); | |
839 | return; | |
840 | } | |
841 | strlcpy(name, newname, sizeof(name)); | |
842 | free(newname); | |
843 | } | |
844 | #endif | |
845 | ||
846 | static void | |
847 | setrouter(const char *vname, int value, int s, const struct afswtch *afp) | |
848 | { | |
849 | if (afp->af_setrouter == NULL) { | |
850 | warn("address family %s does not support router mode", | |
851 | afp->af_name); | |
852 | return; | |
853 | } | |
854 | ||
855 | afp->af_setrouter(s, value); | |
856 | } | |
857 | ||
858 | static int | |
859 | routermode(int argc, char *const *argv, int s, const struct afswtch *afp) | |
860 | { | |
861 | return (*afp->af_routermode)(s, argc, argv); | |
862 | } | |
863 | ||
864 | ||
865 | static void | |
866 | setifdesc(const char *val, int dummy __unused, int s, const struct afswtch *afp) | |
867 | { | |
868 | struct if_descreq ifdr; | |
869 | ||
870 | bzero(&ifdr, sizeof (ifdr)); | |
871 | strlcpy(ifdr.ifdr_name, name, sizeof (ifdr.ifdr_name)); | |
872 | ifdr.ifdr_len = strlen(val); | |
873 | strlcpy((char *)ifdr.ifdr_desc, val, sizeof (ifdr.ifdr_desc)); | |
874 | ||
875 | if (ioctl(s, SIOCSIFDESC, (caddr_t)&ifdr) < 0) { | |
876 | warn("ioctl (set desc)"); | |
877 | } | |
878 | } | |
879 | ||
880 | static void | |
881 | settbr(const char *val, int dummy __unused, int s, const struct afswtch *afp) | |
882 | { | |
883 | struct if_linkparamsreq iflpr; | |
884 | long double bps; | |
885 | u_int64_t rate; | |
886 | u_int32_t percent = 0; | |
887 | char *cp; | |
888 | ||
889 | errno = 0; | |
890 | bzero(&iflpr, sizeof (iflpr)); | |
891 | strlcpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name)); | |
892 | ||
893 | bps = strtold(val, &cp); | |
894 | if (val == cp || errno != 0) { | |
895 | warn("Invalid value '%s'", val); | |
896 | return; | |
897 | } | |
898 | rate = (u_int64_t)bps; | |
899 | if (cp != NULL) { | |
900 | if (!strcmp(cp, "b") || !strcmp(cp, "bps")) { | |
901 | ; /* nothing */ | |
902 | } else if (!strcmp(cp, "Kb") || !strcmp(cp, "Kbps")) { | |
903 | rate *= 1000; | |
904 | } else if (!strcmp(cp, "Mb") || !strcmp(cp, "Mbps")) { | |
905 | rate *= 1000 * 1000; | |
906 | } else if (!strcmp(cp, "Gb") || !strcmp(cp, "Gbps")) { | |
907 | rate *= 1000 * 1000 * 1000; | |
908 | } else if (!strcmp(cp, "%")) { | |
909 | percent = rate; | |
910 | if (percent == 0 || percent > 100) { | |
911 | printf("Value out of range '%s'", val); | |
912 | return; | |
913 | } | |
914 | } else if (*cp != '\0') { | |
915 | printf("Unknown unit '%s'", cp); | |
916 | return; | |
917 | } | |
918 | } | |
919 | iflpr.iflpr_output_tbr_rate = rate; | |
920 | iflpr.iflpr_output_tbr_percent = percent; | |
921 | if (ioctl(s, SIOCSIFLINKPARAMS, &iflpr) < 0 && | |
922 | errno != ENOENT && errno != ENXIO && errno != ENODEV) { | |
923 | warn("ioctl (set link params)"); | |
924 | } else if (errno == ENXIO) { | |
925 | printf("TBR cannot be set on %s\n", name); | |
926 | } else if (errno == 0 && rate == 0) { | |
927 | printf("%s: TBR is now disabled\n", name); | |
928 | } else if (errno == ENODEV) { | |
929 | printf("%s: requires absolute TBR rate\n", name); | |
930 | } else if (percent != 0) { | |
931 | printf("%s: TBR rate set to %u%% of effective link rate\n", | |
932 | name, percent); | |
933 | } else { | |
934 | printf("%s: TBR rate set to %s\n", name, bps_to_str(rate)); | |
935 | } | |
936 | } | |
937 | ||
938 | static int | |
939 | get_int64(uint64_t *i, char const *s) | |
940 | { | |
941 | char *cp; | |
942 | *i = strtol(s, &cp, 10); | |
943 | if (cp == s || errno != 0) { | |
944 | return (-1); | |
945 | } | |
946 | return (0); | |
947 | } | |
948 | ||
949 | static int | |
950 | get_int32(uint32_t *i, char const *s) | |
951 | { | |
952 | char *cp; | |
953 | *i = strtol(s, &cp, 10); | |
954 | if (cp == s || errno != 0) { | |
955 | return (-1); | |
956 | } | |
957 | return (0); | |
958 | } | |
959 | ||
960 | static int | |
961 | get_percent(double *d, const char *s) | |
962 | { | |
963 | char *cp; | |
964 | *d = strtod(s, &cp) / (double)100; | |
965 | if (*d == HUGE_VALF || *d == HUGE_VALL) { | |
966 | return (-1); | |
967 | } | |
968 | if (*d == 0.0 || (*cp != '\0' && strcmp(cp, "%") != 0)) { | |
969 | return (-1); | |
970 | } | |
971 | return (0); | |
972 | } | |
973 | ||
974 | static int | |
975 | get_percent_fixed_point(uint32_t *i, const char *s) | |
976 | { | |
977 | double p; | |
978 | ||
979 | if (get_percent(&p, s) != 0){ | |
980 | return (-1); | |
981 | } | |
982 | ||
983 | *i = p * IF_NETEM_PARAMS_PSCALE; | |
984 | return (0); | |
985 | } | |
986 | ||
987 | static int | |
988 | netem_parse_args(struct if_netem_params *p, int argc, char *const *argv) | |
989 | { | |
990 | int argc_saved = argc; | |
991 | uint64_t bandwitdh = 0; | |
992 | uint32_t latency = 0, jitter = 0; | |
993 | uint32_t corruption = 0; | |
994 | uint32_t duplication = 0; | |
995 | uint32_t loss_p_gr_gl = 0, loss_p_gr_bl = 0, loss_p_bl_br = 0, | |
996 | loss_p_bl_gr = 0, loss_p_br_bl = 0; | |
997 | uint32_t reordering = 0; | |
998 | ||
999 | bzero(p, sizeof (*p)); | |
1000 | ||
1001 | /* take out "input"/"output" */ | |
1002 | argc--, argv++; | |
1003 | ||
1004 | for ( ; argc > 0; ) { | |
1005 | if (strcmp(*argv, "bandwidth") == 0) { | |
1006 | argc--, argv++; | |
1007 | if (argc <= 0 || get_int64(&bandwitdh, *argv) != 0) { | |
1008 | err(1, "Invalid value '%s'", *argv); | |
1009 | } | |
1010 | argc--, argv++; | |
1011 | } else if (strcmp(*argv, "corruption") == 0) { | |
1012 | argc--, argv++; | |
1013 | if (argc <= 0 || | |
1014 | get_percent_fixed_point(&corruption, *argv) != 0) { | |
1015 | err(1, "Invalid value '%s'", *argv); | |
1016 | } | |
1017 | argc--, argv++; | |
1018 | } else if (strcmp(*argv, "delay") == 0) { | |
1019 | argc--, argv++; | |
1020 | if (argc <= 0 || get_int32(&latency, *argv) != 0) { | |
1021 | err(1, "Invalid value '%s'", *argv); | |
1022 | } | |
1023 | argc--, argv++; | |
1024 | if (argc > 0 && get_int32(&jitter, *argv) == 0) { | |
1025 | argc--, argv++; | |
1026 | } | |
1027 | } else if (strcmp(*argv, "duplication") == 0) { | |
1028 | argc--, argv++; | |
1029 | if (argc <= 0 || | |
1030 | get_percent_fixed_point(&duplication, *argv) != 0) { | |
1031 | err(1, "Invalid value '%s'", *argv); | |
1032 | return (-1); | |
1033 | } | |
1034 | argc--, argv++; | |
1035 | } else if (strcmp(*argv, "loss") == 0) { | |
1036 | argc--, argv++; | |
1037 | if (argc <= 0 || get_percent_fixed_point(&loss_p_gr_gl, *argv) != 0) { | |
1038 | err(1, "Invalid value '%s'", *argv); | |
1039 | } | |
1040 | /* we may have all 5 probs, use naive model if not */ | |
1041 | argc--, argv++; | |
1042 | if (argc <= 0 || get_percent_fixed_point(&loss_p_gr_bl, *argv) != 0) { | |
1043 | continue; | |
1044 | } | |
1045 | /* if more than p_gr_gl, then should have all probs */ | |
1046 | argc--, argv++; | |
1047 | if (argc <= 0 || get_percent_fixed_point(&loss_p_bl_br, *argv) != 0) { | |
1048 | err(1, "Invalid value '%s' for p_bl_br", *argv); | |
1049 | } | |
1050 | argc--, argv++; | |
1051 | if (argc <= 0 || get_percent_fixed_point(&loss_p_bl_gr, *argv) != 0) { | |
1052 | err(1, "Invalid value '%s' for p_bl_gr", *argv); | |
1053 | } | |
1054 | argc--, argv++; | |
1055 | if (argc <= 0 || get_percent_fixed_point(&loss_p_br_bl, *argv) != 0) { | |
1056 | err(1, "Invalid value '%s' for p_br_bl", *argv); | |
1057 | } | |
1058 | argc--, argv++; | |
1059 | } else if (strcmp(*argv, "reordering") == 0) { | |
1060 | argc--, argv++; | |
1061 | if (argc <= 0 || get_percent_fixed_point(&reordering, *argv) != 0) { | |
1062 | err(1, "Invalid value '%s'", *argv); | |
1063 | } | |
1064 | argc--, argv++; | |
1065 | } else { | |
1066 | return (-1); | |
1067 | } | |
1068 | } | |
1069 | ||
1070 | if (corruption > IF_NETEM_PARAMS_PSCALE) { | |
1071 | err(1, "corruption percentage > 100%%"); | |
1072 | } | |
1073 | ||
1074 | if (duplication > IF_NETEM_PARAMS_PSCALE) { | |
1075 | err(1, "duplication percentage > 100%%"); | |
1076 | } | |
1077 | ||
1078 | if (duplication > 0 && latency == 0) { | |
1079 | /* we need to insert dup'ed packet with latency */ | |
1080 | err(1, "duplication needs latency param"); | |
1081 | } | |
1082 | ||
1083 | if (latency > 1000) { | |
1084 | err(1, "latency %dms too big (> 1 sec)", latency); | |
1085 | } | |
1086 | ||
1087 | if (jitter * 3 > latency) { | |
1088 | err(1, "jitter %dms too big (latency %dms)", jitter, latency); | |
1089 | } | |
1090 | ||
1091 | /* if gr_gl == 0 (no loss), other prob should all be zero */ | |
1092 | if (loss_p_gr_gl == 0 && | |
1093 | (loss_p_gr_bl != 0 || loss_p_bl_br != 0 || loss_p_bl_gr != 0 || | |
1094 | loss_p_br_bl != 0)) { | |
1095 | err(1, "loss params not all zero when gr_gl is zero"); | |
1096 | } | |
1097 | ||
1098 | /* check state machine transition prob integrity */ | |
1099 | if (loss_p_gr_gl > IF_NETEM_PARAMS_PSCALE || | |
1100 | /* gr_gl = IF_NETEM_PARAMS_PSCALE for total loss */ | |
1101 | loss_p_gr_bl > IF_NETEM_PARAMS_PSCALE || | |
1102 | loss_p_bl_br > IF_NETEM_PARAMS_PSCALE || | |
1103 | loss_p_bl_gr > IF_NETEM_PARAMS_PSCALE || | |
1104 | loss_p_br_bl > IF_NETEM_PARAMS_PSCALE || | |
1105 | loss_p_gr_gl + loss_p_gr_bl > IF_NETEM_PARAMS_PSCALE || | |
1106 | loss_p_bl_br + loss_p_bl_gr > IF_NETEM_PARAMS_PSCALE) { | |
1107 | err(1, "loss params too big"); | |
1108 | } | |
1109 | ||
1110 | if (reordering > IF_NETEM_PARAMS_PSCALE) { | |
1111 | err(1, "reordering percentage > 100%%"); | |
1112 | } | |
1113 | ||
1114 | p->ifnetem_bandwidth_bps = bandwitdh; | |
1115 | p->ifnetem_latency_ms = latency; | |
1116 | p->ifnetem_jitter_ms = jitter; | |
1117 | p->ifnetem_corruption_p = corruption; | |
1118 | p->ifnetem_duplication_p = duplication; | |
1119 | p->ifnetem_loss_p_gr_gl = loss_p_gr_gl; | |
1120 | p->ifnetem_loss_p_gr_bl = loss_p_gr_bl; | |
1121 | p->ifnetem_loss_p_bl_br = loss_p_bl_br; | |
1122 | p->ifnetem_loss_p_bl_gr = loss_p_bl_gr; | |
1123 | p->ifnetem_loss_p_br_bl = loss_p_br_bl; | |
1124 | p->ifnetem_reordering_p = reordering; | |
1125 | ||
1126 | return (argc_saved - argc); | |
1127 | } | |
1128 | ||
1129 | void | |
1130 | print_netem_params(struct if_netem_params *p, const char *desc) | |
1131 | { | |
1132 | struct if_netem_params zero_params; | |
1133 | double pscale = IF_NETEM_PARAMS_PSCALE / 100; | |
1134 | bzero(&zero_params, sizeof (zero_params)); | |
1135 | ||
1136 | if (memcmp(p, &zero_params, sizeof (zero_params)) == 0) { | |
1137 | printf("%s NetEm Disabled\n\n", desc); | |
1138 | } else { | |
1139 | printf( | |
1140 | "%s NetEm Parameters\n" | |
1141 | "\tbandwidth rate %llubps\n" | |
1142 | "\tdelay latency %dms\n" | |
1143 | "\t jitter %dms\n", | |
1144 | desc, p->ifnetem_bandwidth_bps, | |
1145 | p->ifnetem_latency_ms, p->ifnetem_jitter_ms); | |
1146 | if (p->ifnetem_loss_p_gr_bl == 0 && | |
1147 | p->ifnetem_loss_p_bl_br == 0 && | |
1148 | p->ifnetem_loss_p_bl_gr == 0 && | |
1149 | p->ifnetem_loss_p_br_bl == 0) { | |
1150 | printf( | |
1151 | "\tloss %.3f%%\n", | |
1152 | (double) p->ifnetem_loss_p_gr_gl / pscale); | |
1153 | } else { | |
1154 | printf( | |
1155 | "\tloss GAP_RECV -> GAP_LOSS %.3f%%\n" | |
1156 | "\t GAP_RECV -> BURST_LOSS %.3f%%\n" | |
1157 | "\t BURST_LOSS -> BURST_RECV %.3f%%\n" | |
1158 | "\t BURST_LOSS -> GAP_RECV %.3f%%\n" | |
1159 | "\t BURST_RECV -> BURST_LOSS %.3f%%\n", | |
1160 | (double) p->ifnetem_loss_p_gr_gl / pscale, | |
1161 | (double) p->ifnetem_loss_p_gr_bl / pscale, | |
1162 | (double) p->ifnetem_loss_p_bl_br / pscale, | |
1163 | (double) p->ifnetem_loss_p_bl_gr / pscale, | |
1164 | (double) p->ifnetem_loss_p_br_bl / pscale); | |
1165 | } | |
1166 | printf( | |
1167 | "\tcorruption %.3f%%\n" | |
1168 | "\treordering %.3f%%\n\n", | |
1169 | (double) p->ifnetem_corruption_p / pscale, | |
1170 | (double) p->ifnetem_reordering_p / pscale); | |
1171 | } | |
1172 | } | |
1173 | ||
1174 | static int | |
1175 | setnetem(int argc, char *const *argv, int s, const struct afswtch *afp) | |
1176 | { | |
1177 | struct if_linkparamsreq iflpr; | |
1178 | struct if_netem_params input_params, output_params; | |
1179 | int ret = 0, error = 0; | |
1180 | ||
1181 | bzero(&iflpr, sizeof (iflpr)); | |
1182 | bzero(&input_params, sizeof (input_params)); | |
1183 | bzero(&output_params, sizeof (output_params)); | |
1184 | ||
1185 | if (argc > 1) { | |
1186 | if (strcmp(argv[0], "input") == 0) { | |
1187 | ret = netem_parse_args(&input_params, argc, argv); | |
1188 | } else if (strcmp(argv[0], "output") == 0) { | |
1189 | ret = netem_parse_args(&output_params, argc, argv); | |
1190 | } else if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0) { | |
1191 | goto bad_args; | |
1192 | } else { | |
1193 | fprintf(stderr, "uknown option %s\n", argv[0]); | |
1194 | goto bad_args; | |
1195 | } | |
1196 | if (ret < 0) { | |
1197 | goto bad_args; | |
1198 | } | |
1199 | } | |
1200 | ||
1201 | errno = 0; | |
1202 | strlcpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name)); | |
1203 | error = ioctl(s, SIOCGIFLINKPARAMS, &iflpr); | |
1204 | if (error < 0) { | |
1205 | warn("ioctl (get link params)"); | |
1206 | } | |
1207 | ||
1208 | if (argc == 0) { | |
1209 | print_netem_params(&iflpr.iflpr_input_netem, "Input"); | |
1210 | print_netem_params(&iflpr.iflpr_output_netem, "Output"); | |
1211 | return (0); | |
1212 | } else if (argc == 1) { | |
1213 | if (strcmp(argv[0], "input") == 0) { | |
1214 | bzero(&iflpr.iflpr_input_netem, | |
1215 | sizeof (iflpr.iflpr_input_netem)); | |
1216 | } else if (strcmp(argv[0], "output") == 0) { | |
1217 | bzero(&iflpr.iflpr_output_netem, | |
1218 | sizeof (iflpr.iflpr_output_netem)); | |
1219 | } else { | |
1220 | fprintf(stderr, "uknown option %s\n", argv[0]); | |
1221 | goto bad_args; | |
1222 | } | |
1223 | printf("%s: netem is now disabled for %s\n", name, argv[0]); | |
1224 | ret = 1; | |
1225 | } else { | |
1226 | if (strcmp(argv[0], "input") == 0) { | |
1227 | iflpr.iflpr_input_netem = input_params; | |
1228 | } else if (strcmp(argv[0], "output") == 0) { | |
1229 | iflpr.iflpr_output_netem = output_params; | |
1230 | } | |
1231 | } | |
1232 | ||
1233 | error = ioctl(s, SIOCSIFLINKPARAMS, &iflpr); | |
1234 | if (error < 0 && errno != ENOENT && errno != ENXIO && errno != ENODEV) { | |
1235 | warn("ioctl (set link params)"); | |
1236 | } else if (errno == ENXIO) { | |
1237 | printf("netem cannot be set on %s\n", name); | |
1238 | } else { | |
1239 | printf("%s: netem configured\n", name); | |
1240 | } | |
1241 | ||
1242 | return (ret); | |
1243 | bad_args: | |
1244 | fprintf(stderr, "Usage:\n" | |
1245 | "\tTo enable/set netem params\n" | |
1246 | "\t\tnetem <input|output>\n" | |
1247 | "\t\t [ bandwidth BIT_PER_SEC ]\n" | |
1248 | "\t\t [ delay DELAY_MSEC [ JITTER_MSEC ] ]\n" | |
1249 | "\t\t [ loss PERCENTAGE ]\n" | |
1250 | "\t\t [ duplication PERCENTAGE ]\n" | |
1251 | "\t\t [ reordering PERCENTAGE ]\n\n" | |
1252 | "\tTo disable <input|output> netem\n" | |
1253 | "\t\tnetem <input|output>\n\n" | |
1254 | "\tTo show current settings\n" | |
1255 | "\t\tnetem\n\n"); | |
1256 | return (-1); | |
1257 | } | |
1258 | ||
1259 | static void | |
1260 | setthrottle(const char *val, int dummy __unused, int s, | |
1261 | const struct afswtch *afp) | |
1262 | { | |
1263 | struct if_throttlereq iftr; | |
1264 | char *cp; | |
1265 | ||
1266 | errno = 0; | |
1267 | bzero(&iftr, sizeof (iftr)); | |
1268 | strlcpy(iftr.ifthr_name, name, sizeof (iftr.ifthr_name)); | |
1269 | ||
1270 | iftr.ifthr_level = strtold(val, &cp); | |
1271 | if (val == cp || errno != 0) { | |
1272 | warn("Invalid value '%s'", val); | |
1273 | return; | |
1274 | } | |
1275 | ||
1276 | if (ioctl(s, SIOCSIFTHROTTLE, &iftr) < 0 && errno != ENXIO) { | |
1277 | warn("ioctl (set throttling level)"); | |
1278 | } else if (errno == ENXIO) { | |
1279 | printf("throttling level cannot be set on %s\n", name); | |
1280 | } else { | |
1281 | printf("%s: throttling level set to %d\n", name, | |
1282 | iftr.ifthr_level); | |
1283 | } | |
1284 | } | |
1285 | ||
1286 | static void | |
1287 | setdisableoutput(const char *val, int dummy __unused, int s, | |
1288 | const struct afswtch *afp) | |
1289 | { | |
1290 | struct ifreq ifr; | |
1291 | char *cp; | |
1292 | errno = 0; | |
1293 | bzero(&ifr, sizeof (ifr)); | |
1294 | strlcpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); | |
1295 | ||
1296 | ifr.ifr_ifru.ifru_disable_output = strtold(val, &cp); | |
1297 | if (val == cp || errno != 0) { | |
1298 | warn("Invalid value '%s'", val); | |
1299 | return; | |
1300 | } | |
1301 | ||
1302 | if (ioctl(s, SIOCSIFDISABLEOUTPUT, &ifr) < 0 && errno != ENXIO) { | |
1303 | warn("ioctl set disable output"); | |
1304 | } else if (errno == ENXIO) { | |
1305 | printf("output thread can not be disabled on %s\n", name); | |
1306 | } else { | |
1307 | printf("output %s on %s\n", | |
1308 | ((ifr.ifr_ifru.ifru_disable_output == 0) ? "enabled" : "disabled"), | |
1309 | name); | |
1310 | } | |
1311 | } | |
1312 | ||
1313 | static void | |
1314 | setlog(const char *val, int dummy __unused, int s, | |
1315 | const struct afswtch *afp) | |
1316 | { | |
1317 | char *cp; | |
1318 | ||
1319 | errno = 0; | |
1320 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1321 | ||
1322 | ifr.ifr_log.ifl_level = strtold(val, &cp); | |
1323 | if (val == cp || errno != 0) { | |
1324 | warn("Invalid value '%s'", val); | |
1325 | return; | |
1326 | } | |
1327 | ifr.ifr_log.ifl_flags = (IFRLOGF_DLIL|IFRLOGF_FAMILY|IFRLOGF_DRIVER| | |
1328 | IFRLOGF_FIRMWARE); | |
1329 | ||
1330 | if (ioctl(s, SIOCSIFLOG, &ifr) < 0) | |
1331 | warn("ioctl (set logging parameters)"); | |
1332 | } | |
1333 | ||
1334 | void | |
1335 | setcl2k(const char *vname, int value, int s, const struct afswtch *afp) | |
1336 | { | |
1337 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1338 | ifr.ifr_ifru.ifru_2kcl = value; | |
1339 | ||
1340 | if (ioctl(s, SIOCSIF2KCL, (caddr_t)&ifr) < 0) | |
1341 | Perror(vname); | |
1342 | } | |
1343 | ||
1344 | void | |
1345 | setexpensive(const char *vname, int value, int s, const struct afswtch *afp) | |
1346 | { | |
1347 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1348 | ifr.ifr_ifru.ifru_expensive = value; | |
1349 | ||
1350 | if (ioctl(s, SIOCSIFEXPENSIVE, (caddr_t)&ifr) < 0) | |
1351 | Perror(vname); | |
1352 | } | |
1353 | ||
1354 | #ifdef SIOCSIFCONSTRAINED | |
1355 | void | |
1356 | setconstrained(const char *vname, int value, int s, const struct afswtch *afp) | |
1357 | { | |
1358 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1359 | ifr.ifr_ifru.ifru_constrained = value; | |
1360 | ||
1361 | if (ioctl(s, SIOCSIFCONSTRAINED, (caddr_t)&ifr) < 0) | |
1362 | Perror(vname); | |
1363 | } | |
1364 | #endif | |
1365 | ||
1366 | static void | |
1367 | setifmpklog(const char *vname, int value, int s, const struct afswtch *afp) | |
1368 | { | |
1369 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1370 | ifr.ifr_ifru.ifru_mpk_log = value; | |
1371 | ||
1372 | if (ioctl(s, SIOCSIFMPKLOG, (caddr_t)&ifr) < 0) | |
1373 | Perror(vname); | |
1374 | } | |
1375 | ||
1376 | void | |
1377 | settimestamp(const char *vname, int value, int s, const struct afswtch *afp) | |
1378 | { | |
1379 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1380 | ||
1381 | if (value == 0) { | |
1382 | if (ioctl(s, SIOCSIFTIMESTAMPDISABLE, (caddr_t)&ifr) < 0) | |
1383 | Perror(vname); | |
1384 | } else { | |
1385 | if (ioctl(s, SIOCSIFTIMESTAMPENABLE, (caddr_t)&ifr) < 0) | |
1386 | Perror(vname); | |
1387 | } | |
1388 | } | |
1389 | ||
1390 | void | |
1391 | setecnmode(const char *val, int dummy __unused, int s, | |
1392 | const struct afswtch *afp) | |
1393 | { | |
1394 | char *cp; | |
1395 | ||
1396 | if (strcmp(val, "default") == 0) | |
1397 | ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_DEFAULT; | |
1398 | else if (strcmp(val, "enable") == 0) | |
1399 | ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_ENABLE; | |
1400 | else if (strcmp(val, "disable") == 0) | |
1401 | ifr.ifr_ifru.ifru_ecn_mode = IFRTYPE_ECN_DISABLE; | |
1402 | else { | |
1403 | ifr.ifr_ifru.ifru_ecn_mode = strtold(val, &cp); | |
1404 | if (val == cp || errno != 0) { | |
1405 | warn("Invalid ECN mode value '%s'", val); | |
1406 | return; | |
1407 | } | |
1408 | } | |
1409 | ||
1410 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1411 | ||
1412 | if (ioctl(s, SIOCSECNMODE, (caddr_t)&ifr) < 0) | |
1413 | Perror("ioctl(SIOCSECNMODE)"); | |
1414 | } | |
1415 | ||
1416 | void | |
1417 | setprobeconnectivity(const char *vname, int value, int s, const struct afswtch *afp) | |
1418 | { | |
1419 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1420 | ifr.ifr_ifru.ifru_probe_connectivity = value; | |
1421 | ||
1422 | if (ioctl(s, SIOCSIFPROBECONNECTIVITY, (caddr_t)&ifr) < 0) | |
1423 | Perror(vname); | |
1424 | } | |
1425 | ||
1426 | #if defined(SIOCSQOSMARKINGMODE) && defined(SIOCSQOSMARKINGENABLED) | |
1427 | ||
1428 | void | |
1429 | setqosmarking(const char *cmd, const char *arg, int s, const struct afswtch *afp) | |
1430 | { | |
1431 | u_long ioc; | |
1432 | ||
1433 | #if (DEBUG | DEVELOPMENT) | |
1434 | printf("%s(%s, %s)\n", __func__, cmd, arg); | |
1435 | #endif /* (DEBUG | DEVELOPMENT) */ | |
1436 | ||
1437 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1438 | ||
1439 | if (strcmp(cmd, "mode") == 0) { | |
1440 | ioc = SIOCSQOSMARKINGMODE; | |
1441 | ||
1442 | if (strcmp(arg, "fastlane") == 0) | |
1443 | ifr.ifr_qosmarking_mode = IFRTYPE_QOSMARKING_FASTLANE; | |
1444 | else if (strcmp(arg, "rfc4594") == 0) | |
1445 | ifr.ifr_qosmarking_mode = IFRTYPE_QOSMARKING_RFC4594; | |
1446 | else if (strcasecmp(arg, "none") == 0 || strcasecmp(arg, "off") == 0) | |
1447 | ifr.ifr_qosmarking_mode = IFRTYPE_QOSMARKING_MODE_NONE; | |
1448 | else | |
1449 | err(EX_USAGE, "bad value for qosmarking mode: %s", arg); | |
1450 | } else if (strcmp(cmd, "enabled") == 0) { | |
1451 | ioc = SIOCSQOSMARKINGENABLED; | |
1452 | if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| | |
1453 | strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) | |
1454 | ifr.ifr_qosmarking_enabled = 1; | |
1455 | else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| | |
1456 | strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) | |
1457 | ifr.ifr_qosmarking_enabled = 0; | |
1458 | else | |
1459 | err(EX_USAGE, "bad value for qosmarking enabled: %s", arg); | |
1460 | } else { | |
1461 | err(EX_USAGE, "qosmarking takes mode or enabled"); | |
1462 | } | |
1463 | ||
1464 | if (ioctl(s, ioc, (caddr_t)&ifr) < 0) | |
1465 | err(EX_OSERR, "ioctl(%s, %s)", cmd, arg); | |
1466 | } | |
1467 | ||
1468 | void | |
1469 | setfastlane(const char *cmd, const char *arg, int s, const struct afswtch *afp) | |
1470 | { | |
1471 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1472 | ||
1473 | warnx("### fastlane is obsolete, use qosmarking ###"); | |
1474 | ||
1475 | if (strcmp(cmd, "capable") == 0) { | |
1476 | if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| | |
1477 | strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) | |
1478 | setqosmarking("mode", "fastlane", s, afp); | |
1479 | else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| | |
1480 | strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) | |
1481 | setqosmarking("mode", "off", s, afp); | |
1482 | else | |
1483 | err(EX_USAGE, "bad value for fastlane %s", cmd); | |
1484 | } else if (strcmp(cmd, "enable") == 0) { | |
1485 | if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| | |
1486 | strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) | |
1487 | setqosmarking("enabled", "1", s, afp); | |
1488 | else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| | |
1489 | strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) | |
1490 | setqosmarking("enabled", "0", s, afp); | |
1491 | else | |
1492 | err(EX_USAGE, "bad value for fastlane %s", cmd); | |
1493 | } else { | |
1494 | err(EX_USAGE, "fastlane takes capable or enable"); | |
1495 | } | |
1496 | } | |
1497 | ||
1498 | #else /* defined(SIOCSQOSMARKINGMODE) && defined(SIOCSQOSMARKINGENABLED) */ | |
1499 | ||
1500 | void | |
1501 | setfastlane(const char *cmd, const char *arg, int s, const struct afswtch *afp) | |
1502 | { | |
1503 | int value; | |
1504 | u_long ioc; | |
1505 | ||
1506 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1507 | ||
1508 | if (strcmp(cmd, "capable") == 0) | |
1509 | ioc = SIOCSFASTLANECAPABLE; | |
1510 | else if (strcmp(cmd, "enable") == 0) | |
1511 | ioc = SIOCSFASTLEENABLED; | |
1512 | else | |
1513 | err(EX_USAGE, "fastlane takes capable or enabled"); | |
1514 | ||
1515 | if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| | |
1516 | strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) | |
1517 | value = 1; | |
1518 | else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| | |
1519 | strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) | |
1520 | value = 0; | |
1521 | else | |
1522 | err(EX_USAGE, "bad value for fastlane %s", cmd); | |
1523 | ||
1524 | if (ioc == SIOCSFASTLANECAPABLE) | |
1525 | ifr.ifr_fastlane_capable = value; | |
1526 | else | |
1527 | ifr.ifr_fastlane_enabled = value; | |
1528 | ||
1529 | if (ioctl(s, ioc, (caddr_t)&ifr) < 0) | |
1530 | err(EX_OSERR, "ioctl(%s, %s)", cmd, arg); | |
1531 | } | |
1532 | ||
1533 | ||
1534 | void | |
1535 | setqosmarking(const char *cmd, const char *arg, int s, const struct afswtch *afp) | |
1536 | { | |
1537 | if (strcmp(cmd, "mode") == 0) { | |
1538 | if (strcmp(arg, "fastlane") == 0) | |
1539 | setfastlane("capable", "on", s, afp); | |
1540 | else if (strcmp(arg, "none") == 0) | |
1541 | setfastlane("capable", "off", s, afp); | |
1542 | else | |
1543 | err(EX_USAGE, "bad value for qosmarking mode: %s", arg); | |
1544 | } else if (strcmp(cmd, "enabled") == 0) { | |
1545 | if (strcmp(arg, "1") == 0 || strcasecmp(arg, "on") == 0|| | |
1546 | strcasecmp(arg, "yes") == 0 || strcasecmp(arg, "true") == 0) | |
1547 | setfastlane("enable", "on", s, afp); | |
1548 | else if (strcmp(arg, "0") == 0 || strcasecmp(arg, "off") == 0|| | |
1549 | strcasecmp(arg, "no") == 0 || strcasecmp(arg, "false") == 0) | |
1550 | setfastlane("enable", "off", s, afp); | |
1551 | else | |
1552 | err(EX_USAGE, "bad value for qosmarking enabled: %s", arg); | |
1553 | } else { | |
1554 | err(EX_USAGE, "qosmarking takes mode or enabled"); | |
1555 | } | |
1556 | } | |
1557 | ||
1558 | #endif /* defined(SIOCSQOSMARKINGMODE) && defined(SIOCSQOSMARKINGENABLED) */ | |
1559 | ||
1560 | void | |
1561 | setlowpowermode(const char *vname, int value, int s, const struct afswtch *afp) | |
1562 | { | |
1563 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1564 | ifr.ifr_low_power_mode = !!value; | |
1565 | ||
1566 | if (ioctl(s, SIOCSIFLOWPOWER, (caddr_t)&ifr) < 0) | |
1567 | Perror(vname); | |
1568 | } | |
1569 | ||
1570 | struct str2num { | |
1571 | const char *str; | |
1572 | uint32_t num; | |
1573 | }; | |
1574 | ||
1575 | static struct str2num subfamily_str2num[] = { | |
1576 | { .str = "any", .num = IFRTYPE_SUBFAMILY_ANY }, | |
1577 | { .str = "USB", .num = IFRTYPE_SUBFAMILY_USB }, | |
1578 | { .str = "Bluetooth", .num = IFRTYPE_SUBFAMILY_BLUETOOTH }, | |
1579 | { .str = "Wi-Fi", .num = IFRTYPE_SUBFAMILY_WIFI }, | |
1580 | { .str = "wifi", .num = IFRTYPE_SUBFAMILY_WIFI }, | |
1581 | { .str = "Thunderbolt", .num = IFRTYPE_SUBFAMILY_THUNDERBOLT }, | |
1582 | { .str = "reserverd", .num = IFRTYPE_SUBFAMILY_RESERVED }, | |
1583 | { .str = "intcoproc", .num = IFRTYPE_SUBFAMILY_INTCOPROC }, | |
1584 | { .str = "QuickRelay", .num = IFRTYPE_SUBFAMILY_QUICKRELAY }, | |
1585 | { .str = "Default", .num = IFRTYPE_SUBFAMILY_DEFAULT }, | |
1586 | { .str = NULL, .num = 0 }, | |
1587 | }; | |
1588 | ||
1589 | static uint32_t | |
1590 | get_num_from_str(struct str2num* str2nums, const char *str) | |
1591 | { | |
1592 | struct str2num *str2num = str2nums; | |
1593 | ||
1594 | while (str2num != NULL && str2num->str != NULL) { | |
1595 | if (strcasecmp(str2num->str, str) == 0) { | |
1596 | return str2num->num; | |
1597 | } | |
1598 | str2num++; | |
1599 | } | |
1600 | return 0; | |
1601 | } | |
1602 | ||
1603 | static void | |
1604 | setifsubfamily(const char *val, int dummy __unused, int s, | |
1605 | const struct afswtch *afp) | |
1606 | { | |
1607 | strlcpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); | |
1608 | ||
1609 | char *endptr; | |
1610 | uint32_t subfamily = strtoul(val, &endptr, 0); | |
1611 | if (*endptr != 0) { | |
1612 | subfamily = get_num_from_str(subfamily_str2num, val); | |
1613 | if (subfamily == 0) { | |
1614 | return; | |
1615 | } | |
1616 | } | |
1617 | ||
1618 | ifr.ifr_type.ift_subfamily = subfamily; | |
1619 | if (ioctl(s, SIOCSIFSUBFAMILY, (caddr_t)&ifr) < 0) | |
1620 | warn("ioctl(SIOCSIFSUBFAMILY)"); | |
1621 | } | |
1622 | ||
1623 | void | |
1624 | setifavailability(const char *vname, int value, int s, const struct afswtch *afp) | |
1625 | { | |
1626 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1627 | ifr.ifr_interface_state.valid_bitmask = IF_INTERFACE_STATE_INTERFACE_AVAILABILITY_VALID; | |
1628 | if (value == 0) { | |
1629 | ifr.ifr_interface_state.interface_availability = IF_INTERFACE_STATE_INTERFACE_UNAVAILABLE; | |
1630 | } else { | |
1631 | ifr.ifr_interface_state.interface_availability = IF_INTERFACE_STATE_INTERFACE_AVAILABLE; | |
1632 | } | |
1633 | if (ioctl(s, SIOCSIFINTERFACESTATE, (caddr_t)&ifr) < 0) | |
1634 | warn("ioctl(SIOCSIFINTERFACESTATE)"); | |
1635 | } | |
1636 | ||
1637 | static void | |
1638 | show_routermode(int s) | |
1639 | { | |
1640 | struct afswtch *afp; | |
1641 | ||
1642 | afp = af_getbyname("inet"); | |
1643 | if (afp != NULL) { | |
1644 | (*afp->af_routermode)(s, 0, NULL); | |
1645 | } | |
1646 | } | |
1647 | ||
1648 | static void | |
1649 | show_routermode6(void) | |
1650 | { | |
1651 | struct afswtch *afp; | |
1652 | static int s = -1; | |
1653 | ||
1654 | afp = af_getbyname("inet6"); | |
1655 | if (afp != NULL) { | |
1656 | if (s < 0) { | |
1657 | s = socket(AF_INET6, SOCK_DGRAM, 0); | |
1658 | if (s < 0) { | |
1659 | perror("socket"); | |
1660 | return; | |
1661 | } | |
1662 | } | |
1663 | (*afp->af_routermode)(s, 0, NULL); | |
1664 | } | |
1665 | } | |
1666 | ||
1667 | #define IFFBITS \ | |
1668 | "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \ | |
1669 | "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ | |
1670 | "\20MULTICAST" | |
1671 | ||
1672 | #define IFEFBITS \ | |
1673 | "\020\1AUTOCONFIGURING\4PROBE_CONNECTIVITY\5FASTLN_CAP\6IPV6_DISABLED\7ACCEPT_RTADV\10TXSTART\11RXPOLL" \ | |
1674 | "\12VLAN\13BOND\14ARPLL\15CLAT46\16NOAUTOIPV6LL\17EXPENSIVE\20ROUTER4" \ | |
1675 | "\22LOCALNET_PRIVATE\23ND6ALT\24RESTRICTED_RECV\25AWDL\26NOACKPRI" \ | |
1676 | "\27AWDL_RESTRICTED\30CL2K\31ECN_ENABLE\32ECN_DISABLE\33CHANNEL_DRV\34CA" \ | |
1677 | "\35SENDLIST\36DIRECTLINK\37FASTLN_ON\40UPDOWNCHANGE" | |
1678 | ||
1679 | #define IFXFBITS \ | |
1680 | "\020\1WOL\2TIMESTAMP\3NOAUTONX\4LEGACY\5TXLOWINET\6RXLOWINET\7ALLOCKPI" \ | |
1681 | "\10LOWPOWER\11MPKLOG\12CONSTRAINED" | |
1682 | ||
1683 | #define IFCAPBITS \ | |
1684 | "\020\1RXCSUM\2TXCSUM\3VLAN_MTU\4VLAN_HWTAGGING\5JUMBO_MTU" \ | |
1685 | "\6TSO4\7TSO6\10LRO\11AV\12TXSTATUS\13CHANNEL_IO\14HW_TIMESTAMP\15SW_TIMESTAMP" \ | |
1686 | "\16PARTIAL_CSUM\17ZEROINVERT_CSUM" | |
1687 | ||
1688 | #define IFRLOGF_BITS \ | |
1689 | "\020\1DLIL\21FAMILY\31DRIVER\35FIRMWARE" | |
1690 | ||
1691 | /* | |
1692 | * Print the status of the interface. If an address family was | |
1693 | * specified, show only it; otherwise, show them all. | |
1694 | */ | |
1695 | static void | |
1696 | status(const struct afswtch *afp, const struct sockaddr_dl *sdl, | |
1697 | struct ifaddrs *ifa) | |
1698 | { | |
1699 | struct ifaddrs *ift; | |
1700 | int allfamilies, s; | |
1701 | struct ifstat ifs; | |
1702 | struct if_descreq ifdr; | |
1703 | struct if_linkparamsreq iflpr; | |
1704 | int mib[6]; | |
1705 | struct ifmibdata_supplemental ifmsupp; | |
1706 | size_t miblen = sizeof(struct ifmibdata_supplemental); | |
1707 | u_int64_t eflags = 0; | |
1708 | u_int64_t xflags = 0; | |
1709 | int curcap = 0; | |
1710 | ||
1711 | if (afp == NULL) { | |
1712 | allfamilies = 1; | |
1713 | afp = af_getbyname("inet"); | |
1714 | } else | |
1715 | allfamilies = 0; | |
1716 | ||
1717 | ifr.ifr_addr.sa_family = afp->af_af == AF_LINK ? AF_INET : afp->af_af; | |
1718 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
1719 | ||
1720 | s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0); | |
1721 | if (s < 0) | |
1722 | err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family); | |
1723 | ||
1724 | printf("%s: ", name); | |
1725 | printb("flags", ifa->ifa_flags, IFFBITS); | |
1726 | if (ioctl(s, SIOCGIFMETRIC, &ifr) != -1) | |
1727 | if (ifr.ifr_metric) | |
1728 | printf(" metric %d", ifr.ifr_metric); | |
1729 | if (ioctl(s, SIOCGIFMTU, &ifr) != -1) | |
1730 | printf(" mtu %d", ifr.ifr_mtu); | |
1731 | if (showrtref && ioctl(s, SIOCGIFGETRTREFCNT, &ifr) != -1) | |
1732 | printf(" rtref %d", ifr.ifr_route_refcnt); | |
1733 | if (verbose) { | |
1734 | unsigned int ifindex = if_nametoindex(ifa->ifa_name); | |
1735 | if (ifindex != 0) | |
1736 | printf(" index %u", ifindex); | |
1737 | } | |
1738 | #ifdef SIOCGIFCONSTRAINED | |
1739 | // Constrained is stored in if_xflags which isn't exposed directly | |
1740 | if (ioctl(s, SIOCGIFCONSTRAINED, (caddr_t)&ifr) == 0 && | |
1741 | ifr.ifr_constrained != 0) { | |
1742 | printf(" constrained"); | |
1743 | } | |
1744 | #endif | |
1745 | putchar('\n'); | |
1746 | ||
1747 | if (verbose && ioctl(s, SIOCGIFEFLAGS, (caddr_t)&ifr) != -1 && | |
1748 | (eflags = ifr.ifr_eflags) != 0) { | |
1749 | printb("\teflags", eflags, IFEFBITS); | |
1750 | putchar('\n'); | |
1751 | } | |
1752 | ||
1753 | if (verbose && ioctl(s, SIOCGIFXFLAGS, (caddr_t)&ifr) != -1 && | |
1754 | (xflags = ifr.ifr_xflags) != 0) { | |
1755 | printb("\txflags", xflags, IFXFBITS); | |
1756 | putchar('\n'); | |
1757 | } | |
1758 | ||
1759 | if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) == 0) { | |
1760 | if (ifr.ifr_curcap != 0) { | |
1761 | curcap = ifr.ifr_curcap; | |
1762 | printb("\toptions", ifr.ifr_curcap, IFCAPBITS); | |
1763 | putchar('\n'); | |
1764 | } | |
1765 | if (supmedia && ifr.ifr_reqcap != 0) { | |
1766 | printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS); | |
1767 | putchar('\n'); | |
1768 | } | |
1769 | } | |
1770 | ||
1771 | tunnel_status(s); | |
1772 | ||
1773 | for (ift = ifa; ift != NULL; ift = ift->ifa_next) { | |
1774 | if (ift->ifa_addr == NULL) | |
1775 | continue; | |
1776 | if (strcmp(ifa->ifa_name, ift->ifa_name) != 0) | |
1777 | continue; | |
1778 | if (allfamilies) { | |
1779 | const struct afswtch *p; | |
1780 | p = af_getbyfamily(ift->ifa_addr->sa_family); | |
1781 | if (p != NULL && p->af_status != NULL) | |
1782 | p->af_status(s, ift); | |
1783 | } else if (afp->af_af == ift->ifa_addr->sa_family) | |
1784 | afp->af_status(s, ift); | |
1785 | } | |
1786 | ||
1787 | /* Print CLAT46 address */ | |
1788 | clat46_addr(s, name); | |
1789 | ||
1790 | /* Print NAT64 prefix */ | |
1791 | nat64_status(s, name); | |
1792 | ||
1793 | #if 0 | |
1794 | if (allfamilies || afp->af_af == AF_LINK) { | |
1795 | const struct afswtch *lafp; | |
1796 | ||
1797 | /* | |
1798 | * Hack; the link level address is received separately | |
1799 | * from the routing information so any address is not | |
1800 | * handled above. Cobble together an entry and invoke | |
1801 | * the status method specially. | |
1802 | */ | |
1803 | lafp = af_getbyname("lladdr"); | |
1804 | if (lafp != NULL) { | |
1805 | info.rti_info[RTAX_IFA] = (struct sockaddr *)sdl; | |
1806 | lafp->af_status(s, &info); | |
1807 | } | |
1808 | } | |
1809 | #endif | |
1810 | if (allfamilies) | |
1811 | af_other_status(s); | |
1812 | else if (afp->af_other_status != NULL) | |
1813 | afp->af_other_status(s); | |
1814 | ||
1815 | strlcpy(ifs.ifs_name, name, sizeof ifs.ifs_name); | |
1816 | if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) | |
1817 | printf("%s", ifs.ascii); | |
1818 | ||
1819 | /* The rest is for when verbose is set; if not set, we're done */ | |
1820 | if (!verbose) | |
1821 | goto done; | |
1822 | ||
1823 | if (ioctl(s, SIOCGIFTYPE, &ifr) != -1) { | |
1824 | char *c = ift2str(ifr.ifr_type.ift_type, | |
1825 | ifr.ifr_type.ift_family, ifr.ifr_type.ift_subfamily); | |
1826 | if (c != NULL) | |
1827 | printf("\ttype: %s\n", c); | |
1828 | } | |
1829 | ||
1830 | if (verbose > 1) { | |
1831 | if (ioctl(s, SIOCGIFFUNCTIONALTYPE, &ifr) != -1) { | |
1832 | char *c = iffunct2str(ifr.ifr_functional_type); | |
1833 | if (c != NULL) | |
1834 | printf("\tfunctional type: %s\n", c); | |
1835 | } | |
1836 | } | |
1837 | { | |
1838 | struct if_agentidsreq ifar; | |
1839 | memset(&ifar, 0, sizeof(ifar)); | |
1840 | ||
1841 | strlcpy(ifar.ifar_name, name, sizeof(ifar.ifar_name)); | |
1842 | ||
1843 | if (ioctl(s, SIOCGIFAGENTIDS, &ifar) != -1) { | |
1844 | if (ifar.ifar_count != 0) { | |
1845 | ifar.ifar_uuids = calloc(ifar.ifar_count, sizeof(uuid_t)); | |
1846 | if (ifar.ifar_uuids != NULL) { | |
1847 | if (ioctl(s, SIOCGIFAGENTIDS, &ifar) != 1) { | |
1848 | for (int agent_i = 0; agent_i < ifar.ifar_count; agent_i++) { | |
1849 | struct netagent_req nar; | |
1850 | memset(&nar, 0, sizeof(nar)); | |
1851 | ||
1852 | uuid_copy(nar.netagent_uuid, ifar.ifar_uuids[agent_i]); | |
1853 | ||
1854 | if (ioctl(s, SIOCGIFAGENTDATA, &nar) != 1) { | |
1855 | printf("\tagent domain:%s type:%s flags:0x%x desc:\"%s\"\n", | |
1856 | nar.netagent_domain, nar.netagent_type, | |
1857 | nar.netagent_flags, nar.netagent_desc); | |
1858 | } | |
1859 | } | |
1860 | } | |
1861 | free(ifar.ifar_uuids); | |
1862 | } | |
1863 | } | |
1864 | } | |
1865 | } | |
1866 | ||
1867 | if (ioctl(s, SIOCGIFLINKQUALITYMETRIC, &ifr) != -1) { | |
1868 | int lqm = ifr.ifr_link_quality_metric; | |
1869 | if (verbose > 1) { | |
1870 | printf("\tlink quality: %d ", lqm); | |
1871 | if (lqm == IFNET_LQM_THRESH_OFF) | |
1872 | printf("(off)"); | |
1873 | else if (lqm == IFNET_LQM_THRESH_UNKNOWN) | |
1874 | printf("(unknown)"); | |
1875 | else if (lqm > IFNET_LQM_THRESH_UNKNOWN && | |
1876 | lqm <= IFNET_LQM_THRESH_BAD) | |
1877 | printf("(bad)"); | |
1878 | else if (lqm > IFNET_LQM_THRESH_UNKNOWN && | |
1879 | lqm <= IFNET_LQM_THRESH_POOR) | |
1880 | printf("(poor)"); | |
1881 | else if (lqm > IFNET_LQM_THRESH_POOR && | |
1882 | lqm <= IFNET_LQM_THRESH_GOOD) | |
1883 | printf("(good)"); | |
1884 | else | |
1885 | printf("(?)"); | |
1886 | printf("\n"); | |
1887 | } else if (lqm > IFNET_LQM_THRESH_UNKNOWN) { | |
1888 | printf("\tlink quality: %d ", lqm); | |
1889 | if (lqm <= IFNET_LQM_THRESH_BAD) | |
1890 | printf("(bad)"); | |
1891 | else if (lqm <= IFNET_LQM_THRESH_POOR) | |
1892 | printf("(poor)"); | |
1893 | else if (lqm <= IFNET_LQM_THRESH_GOOD) | |
1894 | printf("(good)"); | |
1895 | else | |
1896 | printf("(?)"); | |
1897 | printf("\n"); | |
1898 | } | |
1899 | } | |
1900 | ||
1901 | { | |
1902 | if (ioctl(s, SIOCGIFINTERFACESTATE, &ifr) != -1) { | |
1903 | printf("\tstate"); | |
1904 | if (ifr.ifr_interface_state.valid_bitmask & | |
1905 | IF_INTERFACE_STATE_RRC_STATE_VALID) { | |
1906 | uint8_t rrc_state = ifr.ifr_interface_state.rrc_state; | |
1907 | ||
1908 | printf(" rrc: %u ", rrc_state); | |
1909 | if (rrc_state == IF_INTERFACE_STATE_RRC_STATE_CONNECTED) | |
1910 | printf("(connected)"); | |
1911 | else if (rrc_state == IF_INTERFACE_STATE_RRC_STATE_IDLE) | |
1912 | printf("(idle)"); | |
1913 | else | |
1914 | printf("(?)"); | |
1915 | } | |
1916 | if (ifr.ifr_interface_state.valid_bitmask & | |
1917 | IF_INTERFACE_STATE_INTERFACE_AVAILABILITY_VALID) { | |
1918 | uint8_t ifavail = ifr.ifr_interface_state.interface_availability; | |
1919 | ||
1920 | printf(" availability: %u ", ifavail); | |
1921 | if (ifavail == IF_INTERFACE_STATE_INTERFACE_AVAILABLE) | |
1922 | printf("(true)"); | |
1923 | else if (ifavail == IF_INTERFACE_STATE_INTERFACE_UNAVAILABLE) | |
1924 | printf("(false)"); | |
1925 | else | |
1926 | printf("(?)"); | |
1927 | } else { | |
1928 | printf(" availability: (not valid)"); | |
1929 | } | |
1930 | if (verbose > 1 && | |
1931 | ifr.ifr_interface_state.valid_bitmask & | |
1932 | IF_INTERFACE_STATE_LQM_STATE_VALID) { | |
1933 | int8_t lqm = ifr.ifr_interface_state.lqm_state; | |
1934 | ||
1935 | printf(" lqm: %d", lqm); | |
1936 | ||
1937 | if (lqm == IFNET_LQM_THRESH_OFF) | |
1938 | printf("(off)"); | |
1939 | else if (lqm == IFNET_LQM_THRESH_UNKNOWN) | |
1940 | printf("(unknown)"); | |
1941 | else if (lqm == IFNET_LQM_THRESH_BAD) | |
1942 | printf("(bad)"); | |
1943 | else if (lqm == IFNET_LQM_THRESH_POOR) | |
1944 | printf("(poor)"); | |
1945 | else if (lqm == IFNET_LQM_THRESH_GOOD) | |
1946 | printf("(good)"); | |
1947 | else | |
1948 | printf("(?)"); | |
1949 | } | |
1950 | } | |
1951 | printf("\n"); | |
1952 | } | |
1953 | ||
1954 | bzero(&iflpr, sizeof (iflpr)); | |
1955 | strlcpy(iflpr.iflpr_name, name, sizeof (iflpr.iflpr_name)); | |
1956 | if (ioctl(s, SIOCGIFLINKPARAMS, &iflpr) != -1) { | |
1957 | u_int64_t ibw_max = iflpr.iflpr_input_bw.max_bw; | |
1958 | u_int64_t ibw_eff = iflpr.iflpr_input_bw.eff_bw; | |
1959 | u_int64_t obw_max = iflpr.iflpr_output_bw.max_bw; | |
1960 | u_int64_t obw_eff = iflpr.iflpr_output_bw.eff_bw; | |
1961 | u_int64_t obw_tbr = iflpr.iflpr_output_tbr_rate; | |
1962 | u_int32_t obw_pct = iflpr.iflpr_output_tbr_percent; | |
1963 | u_int64_t ilt_max = iflpr.iflpr_input_lt.max_lt; | |
1964 | u_int64_t ilt_eff = iflpr.iflpr_input_lt.eff_lt; | |
1965 | u_int64_t olt_max = iflpr.iflpr_output_lt.max_lt; | |
1966 | u_int64_t olt_eff = iflpr.iflpr_output_lt.eff_lt; | |
1967 | ||
1968 | ||
1969 | if (eflags & IFEF_TXSTART) { | |
1970 | u_int32_t flags = iflpr.iflpr_flags; | |
1971 | u_int32_t sched = iflpr.iflpr_output_sched; | |
1972 | struct if_throttlereq iftr; | |
1973 | ||
1974 | printf("\tscheduler: %s%s ", | |
1975 | (flags & IFLPRF_ALTQ) ? "ALTQ_" : "", | |
1976 | sched2str(sched)); | |
1977 | if (flags & IFLPRF_DRVMANAGED) | |
1978 | printf("(driver managed)"); | |
1979 | printf("\n"); | |
1980 | ||
1981 | bzero(&iftr, sizeof (iftr)); | |
1982 | strlcpy(iftr.ifthr_name, name, | |
1983 | sizeof (iftr.ifthr_name)); | |
1984 | if (ioctl(s, SIOCGIFTHROTTLE, &iftr) != -1 && | |
1985 | iftr.ifthr_level != IFNET_THROTTLE_OFF) | |
1986 | printf("\tthrottling: level %d (%s)\n", | |
1987 | iftr.ifthr_level, tl2str(iftr.ifthr_level)); | |
1988 | } | |
1989 | ||
1990 | if (obw_tbr != 0 && obw_eff > obw_tbr) | |
1991 | obw_eff = obw_tbr; | |
1992 | ||
1993 | if (ibw_max != 0 || obw_max != 0) { | |
1994 | if (ibw_max == obw_max && ibw_eff == obw_eff && | |
1995 | ibw_max == ibw_eff && obw_tbr == 0) { | |
1996 | printf("\tlink rate: %s\n", | |
1997 | bps_to_str(ibw_max)); | |
1998 | } else { | |
1999 | printf("\tuplink rate: %s [eff] / ", | |
2000 | bps_to_str(obw_eff)); | |
2001 | if (obw_tbr != 0) { | |
2002 | if (obw_pct == 0) | |
2003 | printf("%s [tbr] / ", | |
2004 | bps_to_str(obw_tbr)); | |
2005 | else | |
2006 | printf("%s [tbr %u%%] / ", | |
2007 | bps_to_str(obw_tbr), | |
2008 | obw_pct); | |
2009 | } | |
2010 | printf("%s", bps_to_str(obw_max)); | |
2011 | if (obw_tbr != 0) | |
2012 | printf(" [max]"); | |
2013 | printf("\n"); | |
2014 | if (ibw_eff == ibw_max) { | |
2015 | printf("\tdownlink rate: %s\n", | |
2016 | bps_to_str(ibw_max)); | |
2017 | } else { | |
2018 | printf("\tdownlink rate: " | |
2019 | "%s [eff] / ", bps_to_str(ibw_eff)); | |
2020 | printf("%s [max]\n", | |
2021 | bps_to_str(ibw_max)); | |
2022 | } | |
2023 | } | |
2024 | } else if (obw_tbr != 0) { | |
2025 | printf("\tuplink rate: %s [tbr]\n", | |
2026 | bps_to_str(obw_tbr)); | |
2027 | } | |
2028 | ||
2029 | if (ilt_max != 0 || olt_max != 0) { | |
2030 | if (ilt_max == olt_max && ilt_eff == olt_eff && | |
2031 | ilt_max == ilt_eff) { | |
2032 | printf("\tlink latency: %s\n", | |
2033 | ns_to_str(ilt_max)); | |
2034 | } else { | |
2035 | if (olt_max != 0 && olt_eff == olt_max) { | |
2036 | printf("\tuplink latency: %s\n", | |
2037 | ns_to_str(olt_max)); | |
2038 | } else if (olt_max != 0) { | |
2039 | printf("\tuplink latency: " | |
2040 | "%s [eff] / ", ns_to_str(olt_eff)); | |
2041 | printf("%s [max]\n", | |
2042 | ns_to_str(olt_max)); | |
2043 | } | |
2044 | if (ilt_max != 0 && ilt_eff == ilt_max) { | |
2045 | printf("\tdownlink latency: %s\n", | |
2046 | ns_to_str(ilt_max)); | |
2047 | } else if (ilt_max != 0) { | |
2048 | printf("\tdownlink latency: " | |
2049 | "%s [eff] / ", ns_to_str(ilt_eff)); | |
2050 | printf("%s [max]\n", | |
2051 | ns_to_str(ilt_max)); | |
2052 | } | |
2053 | } | |
2054 | } | |
2055 | } | |
2056 | ||
2057 | /* Common OID prefix */ | |
2058 | mib[0] = CTL_NET; | |
2059 | mib[1] = PF_LINK; | |
2060 | mib[2] = NETLINK_GENERIC; | |
2061 | mib[3] = IFMIB_IFDATA; | |
2062 | mib[4] = if_nametoindex(name); | |
2063 | mib[5] = IFDATA_SUPPLEMENTAL; | |
2064 | if (sysctl(mib, 6, &ifmsupp, &miblen, (void *)0, 0) == -1) | |
2065 | err(1, "sysctl IFDATA_SUPPLEMENTAL"); | |
2066 | ||
2067 | if (ifmsupp.ifmd_data_extended.ifi_alignerrs != 0) { | |
2068 | printf("\tunaligned pkts: %llu\n", | |
2069 | ifmsupp.ifmd_data_extended.ifi_alignerrs); | |
2070 | } | |
2071 | if (ifmsupp.ifmd_data_extended.ifi_dt_bytes != 0) { | |
2072 | printf("\tdata milestone interval: %s\n", | |
2073 | bytes_to_str(ifmsupp.ifmd_data_extended.ifi_dt_bytes)); | |
2074 | } | |
2075 | ||
2076 | bzero(&ifdr, sizeof (ifdr)); | |
2077 | strlcpy(ifdr.ifdr_name, name, sizeof (ifdr.ifdr_name)); | |
2078 | if (ioctl(s, SIOCGIFDESC, &ifdr) != -1 && ifdr.ifdr_len) { | |
2079 | printf("\tdesc: %s\n", ifdr.ifdr_desc); | |
2080 | } | |
2081 | ||
2082 | if (ioctl(s, SIOCGIFLOG, &ifr) != -1 && ifr.ifr_log.ifl_level) { | |
2083 | printf("\tlogging: level %d ", ifr.ifr_log.ifl_level); | |
2084 | printb("facilities", ifr.ifr_log.ifl_flags, IFRLOGF_BITS); | |
2085 | putchar('\n'); | |
2086 | } | |
2087 | ||
2088 | if (ioctl(s, SIOCGIFDELEGATE, &ifr) != -1 && ifr.ifr_delegated) { | |
2089 | char delegatedif[IFNAMSIZ+1]; | |
2090 | if (if_indextoname(ifr.ifr_delegated, delegatedif) != NULL) | |
2091 | printf("\teffective interface: %s\n", delegatedif); | |
2092 | } | |
2093 | ||
2094 | if (ioctl(s, SIOCGSTARTDELAY, &ifr) != -1) { | |
2095 | if (ifr.ifr_start_delay_qlen > 0 && | |
2096 | ifr.ifr_start_delay_timeout > 0) { | |
2097 | printf("\ttxstart qlen: %u packets " | |
2098 | "timeout: %u microseconds\n", | |
2099 | ifr.ifr_start_delay_qlen, | |
2100 | ifr.ifr_start_delay_timeout/1000); | |
2101 | } | |
2102 | } | |
2103 | #if defined(IFCAP_HW_TIMESTAMP) && defined(IFCAP_SW_TIMESTAMP) | |
2104 | if ((curcap & (IFCAP_HW_TIMESTAMP | IFCAP_SW_TIMESTAMP)) && | |
2105 | ioctl(s, SIOCGIFTIMESTAMPENABLED, &ifr) != -1) { | |
2106 | printf("\ttimestamp: %s\n", | |
2107 | (ifr.ifr_intval != 0) ? "enabled" : "disabled"); | |
2108 | } | |
2109 | #endif | |
2110 | #if defined(SIOCGQOSMARKINGENABLED) && defined(SIOCGQOSMARKINGMODE) | |
2111 | if (ioctl(s, SIOCGQOSMARKINGENABLED, &ifr) != -1) { | |
2112 | printf("\tqosmarking enabled: %s mode: ", | |
2113 | ifr.ifr_qosmarking_enabled ? "yes" : "no"); | |
2114 | if (ioctl(s, SIOCGQOSMARKINGMODE, &ifr) != -1) { | |
2115 | switch (ifr.ifr_qosmarking_mode) { | |
2116 | case IFRTYPE_QOSMARKING_FASTLANE: | |
2117 | printf("fastlane\n"); | |
2118 | break; | |
2119 | case IFRTYPE_QOSMARKING_RFC4594: | |
2120 | printf("RFC4594\n"); | |
2121 | break; | |
2122 | case IFRTYPE_QOSMARKING_MODE_NONE: | |
2123 | printf("none\n"); | |
2124 | break; | |
2125 | default: | |
2126 | printf("unknown (%u)\n", ifr.ifr_qosmarking_mode); | |
2127 | break; | |
2128 | } | |
2129 | } | |
2130 | } | |
2131 | #endif /* defined(SIOCGQOSMARKINGENABLED) && defined(SIOCGQOSMARKINGMODE) */ | |
2132 | ||
2133 | if (ioctl(s, SIOCGIFLOWPOWER, &ifr) != -1) { | |
2134 | printf("\tlow power mode: %s\n", | |
2135 | (ifr.ifr_low_power_mode != 0) ? "enabled" : "disabled"); | |
2136 | } | |
2137 | if (ioctl(s, SIOCGIFMPKLOG, &ifr) != -1) { | |
2138 | printf("\tmulti layer packet logging (mpklog): %s\n", | |
2139 | (ifr.ifr_mpk_log != 0) ? "enabled" : "disabled"); | |
2140 | } | |
2141 | show_routermode(s); | |
2142 | show_routermode6(); | |
2143 | ||
2144 | done: | |
2145 | close(s); | |
2146 | return; | |
2147 | } | |
2148 | ||
2149 | #define KILOBYTES 1024 | |
2150 | #define MEGABYTES (KILOBYTES * KILOBYTES) | |
2151 | #define GIGABYTES (KILOBYTES * KILOBYTES * KILOBYTES) | |
2152 | ||
2153 | static char * | |
2154 | bytes_to_str(unsigned long long bytes) | |
2155 | { | |
2156 | static char buf[32]; | |
2157 | const char *u; | |
2158 | long double n = bytes, t; | |
2159 | ||
2160 | if (bytes >= GIGABYTES) { | |
2161 | t = n / GIGABYTES; | |
2162 | u = "GB"; | |
2163 | } else if (n >= MEGABYTES) { | |
2164 | t = n / MEGABYTES; | |
2165 | u = "MB"; | |
2166 | } else if (n >= KILOBYTES) { | |
2167 | t = n / KILOBYTES; | |
2168 | u = "KB"; | |
2169 | } else { | |
2170 | t = n; | |
2171 | u = "bytes"; | |
2172 | } | |
2173 | ||
2174 | snprintf(buf, sizeof (buf), "%-4.2Lf %s", t, u); | |
2175 | return (buf); | |
2176 | } | |
2177 | ||
2178 | #define GIGABIT_PER_SEC 1000000000 /* gigabit per second */ | |
2179 | #define MEGABIT_PER_SEC 1000000 /* megabit per second */ | |
2180 | #define KILOBIT_PER_SEC 1000 /* kilobit per second */ | |
2181 | ||
2182 | static char * | |
2183 | bps_to_str(unsigned long long rate) | |
2184 | { | |
2185 | static char buf[32]; | |
2186 | const char *u; | |
2187 | long double n = rate, t; | |
2188 | ||
2189 | if (rate >= GIGABIT_PER_SEC) { | |
2190 | t = n / GIGABIT_PER_SEC; | |
2191 | u = "Gbps"; | |
2192 | } else if (n >= MEGABIT_PER_SEC) { | |
2193 | t = n / MEGABIT_PER_SEC; | |
2194 | u = "Mbps"; | |
2195 | } else if (n >= KILOBIT_PER_SEC) { | |
2196 | t = n / KILOBIT_PER_SEC; | |
2197 | u = "Kbps"; | |
2198 | } else { | |
2199 | t = n; | |
2200 | u = "bps "; | |
2201 | } | |
2202 | ||
2203 | snprintf(buf, sizeof (buf), "%-4.2Lf %4s", t, u); | |
2204 | return (buf); | |
2205 | } | |
2206 | ||
2207 | #define NSEC_PER_SEC 1000000000 /* nanosecond per second */ | |
2208 | #define USEC_PER_SEC 1000000 /* microsecond per second */ | |
2209 | #define MSEC_PER_SEC 1000 /* millisecond per second */ | |
2210 | ||
2211 | static char * | |
2212 | ns_to_str(unsigned long long nsec) | |
2213 | { | |
2214 | static char buf[32]; | |
2215 | const char *u; | |
2216 | long double n = nsec, t; | |
2217 | ||
2218 | if (nsec >= NSEC_PER_SEC) { | |
2219 | t = n / NSEC_PER_SEC; | |
2220 | u = "sec "; | |
2221 | } else if (n >= USEC_PER_SEC) { | |
2222 | t = n / USEC_PER_SEC; | |
2223 | u = "msec"; | |
2224 | } else if (n >= MSEC_PER_SEC) { | |
2225 | t = n / MSEC_PER_SEC; | |
2226 | u = "usec"; | |
2227 | } else { | |
2228 | t = n; | |
2229 | u = "nsec"; | |
2230 | } | |
2231 | ||
2232 | snprintf(buf, sizeof (buf), "%-4.2Lf %4s", t, u); | |
2233 | return (buf); | |
2234 | } | |
2235 | ||
2236 | static void | |
2237 | tunnel_status(int s) | |
2238 | { | |
2239 | af_all_tunnel_status(s); | |
2240 | } | |
2241 | ||
2242 | static void | |
2243 | clat46_addr(int s, char * if_name) | |
2244 | { | |
2245 | struct if_clat46req ifr; | |
2246 | char buf[MAXHOSTNAMELEN]; | |
2247 | ||
2248 | bzero(&ifr, sizeof (ifr)); | |
2249 | strlcpy(ifr.ifclat46_name, if_name, sizeof(ifr.ifclat46_name)); | |
2250 | ||
2251 | if (ioctl(s, SIOCGIFCLAT46ADDR, &ifr) < 0) { | |
2252 | if (errno != ENOENT) | |
2253 | syslog(LOG_WARNING, "ioctl (SIOCGIFCLAT46ADDR): %d", errno); | |
2254 | return; | |
2255 | } | |
2256 | ||
2257 | if (inet_ntop(AF_INET6, &ifr.ifclat46_addr.v6_address, buf, sizeof(buf)) != NULL) | |
2258 | printf("\tinet6 %s prefixlen %d clat46\n", | |
2259 | buf, ifr.ifclat46_addr.v6_prefixlen); | |
2260 | } | |
2261 | ||
2262 | static void | |
2263 | nat64_status(int s, char * if_name) | |
2264 | { | |
2265 | int i; | |
2266 | struct if_nat64req ifr; | |
2267 | char buf[MAXHOSTNAMELEN]; | |
2268 | ||
2269 | bzero(&ifr, sizeof(ifr)); | |
2270 | strlcpy(ifr.ifnat64_name, if_name, sizeof(ifr.ifnat64_name)); | |
2271 | ||
2272 | if (ioctl(s, SIOCGIFNAT64PREFIX, &ifr) < 0) { | |
2273 | if (errno != ENOENT) | |
2274 | syslog(LOG_WARNING, "ioctl(SIOCGIFNAT64PREFIX): %d", errno); | |
2275 | return; | |
2276 | } | |
2277 | ||
2278 | for (i = 0; i < NAT64_MAX_NUM_PREFIXES; i++) { | |
2279 | if (ifr.ifnat64_prefixes[i].prefix_len > 0) { | |
2280 | inet_ntop(AF_INET6, &ifr.ifnat64_prefixes[i].ipv6_prefix, buf, sizeof(buf)); | |
2281 | printf("\tnat64 prefix %s prefixlen %d\n", | |
2282 | buf, ifr.ifnat64_prefixes[i].prefix_len << 3); | |
2283 | } | |
2284 | } | |
2285 | } | |
2286 | ||
2287 | void | |
2288 | Perror(const char *cmd) | |
2289 | { | |
2290 | switch (errno) { | |
2291 | ||
2292 | case ENXIO: | |
2293 | errx(1, "%s: no such interface", cmd); | |
2294 | break; | |
2295 | ||
2296 | case EPERM: | |
2297 | errx(1, "%s: permission denied", cmd); | |
2298 | break; | |
2299 | ||
2300 | default: | |
2301 | err(1, "%s", cmd); | |
2302 | } | |
2303 | } | |
2304 | ||
2305 | /* | |
2306 | * Print a value a la the %b format of the kernel's printf | |
2307 | */ | |
2308 | void | |
2309 | printb(const char *s, unsigned v, const char *bits) | |
2310 | { | |
2311 | int i, any = 0; | |
2312 | char c; | |
2313 | ||
2314 | if (bits && *bits == 8) | |
2315 | printf("%s=%o", s, v); | |
2316 | else | |
2317 | printf("%s=%x", s, v); | |
2318 | bits++; | |
2319 | if (bits) { | |
2320 | putchar('<'); | |
2321 | while ((i = *bits++) != '\0') { | |
2322 | if (v & (1 << (i-1))) { | |
2323 | if (any) | |
2324 | putchar(','); | |
2325 | any = 1; | |
2326 | for (; (c = *bits) > 32; bits++) | |
2327 | putchar(c); | |
2328 | } else | |
2329 | for (; *bits > 32; bits++) | |
2330 | ; | |
2331 | } | |
2332 | putchar('>'); | |
2333 | } | |
2334 | } | |
2335 | ||
2336 | #ifndef __APPLE__ | |
2337 | void | |
2338 | ifmaybeload(const char *name) | |
2339 | { | |
2340 | #define MOD_PREFIX_LEN 3 /* "if_" */ | |
2341 | struct module_stat mstat; | |
2342 | int fileid, modid; | |
2343 | char ifkind[IFNAMSIZ + MOD_PREFIX_LEN], ifname[IFNAMSIZ], *dp; | |
2344 | const char *cp; | |
2345 | ||
2346 | /* loading suppressed by the user */ | |
2347 | if (noload) | |
2348 | return; | |
2349 | ||
2350 | /* trim the interface number off the end */ | |
2351 | strlcpy(ifname, name, sizeof(ifname)); | |
2352 | for (dp = ifname; *dp != 0; dp++) | |
2353 | if (isdigit(*dp)) { | |
2354 | *dp = 0; | |
2355 | break; | |
2356 | } | |
2357 | ||
2358 | /* turn interface and unit into module name */ | |
2359 | strlcpy(ifkind, "if_", sizeof(ifkind)); | |
2360 | strlcpy(ifkind + MOD_PREFIX_LEN, ifname, | |
2361 | sizeof(ifkind) - MOD_PREFIX_LEN); | |
2362 | ||
2363 | /* scan files in kernel */ | |
2364 | mstat.version = sizeof(struct module_stat); | |
2365 | for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { | |
2366 | /* scan modules in file */ | |
2367 | for (modid = kldfirstmod(fileid); modid > 0; | |
2368 | modid = modfnext(modid)) { | |
2369 | if (modstat(modid, &mstat) < 0) | |
2370 | continue; | |
2371 | /* strip bus name if present */ | |
2372 | if ((cp = strchr(mstat.name, '/')) != NULL) { | |
2373 | cp++; | |
2374 | } else { | |
2375 | cp = mstat.name; | |
2376 | } | |
2377 | /* already loaded? */ | |
2378 | if (strncmp(ifname, cp, strlen(ifname) + 1) == 0 || | |
2379 | strncmp(ifkind, cp, strlen(ifkind) + 1) == 0) | |
2380 | return; | |
2381 | } | |
2382 | } | |
2383 | ||
2384 | /* not present, we should try to load it */ | |
2385 | kldload(ifkind); | |
2386 | } | |
2387 | #endif | |
2388 | ||
2389 | static struct cmd basic_cmds[] = { | |
2390 | DEF_CMD("up", IFF_UP, setifflags), | |
2391 | DEF_CMD("down", -IFF_UP, setifflags), | |
2392 | DEF_CMD("arp", -IFF_NOARP, setifflags), | |
2393 | DEF_CMD("-arp", IFF_NOARP, setifflags), | |
2394 | DEF_CMD("debug", IFF_DEBUG, setifflags), | |
2395 | DEF_CMD("-debug", -IFF_DEBUG, setifflags), | |
2396 | #ifdef IFF_PPROMISC | |
2397 | DEF_CMD("promisc", IFF_PPROMISC, setifflags), | |
2398 | DEF_CMD("-promisc", -IFF_PPROMISC, setifflags), | |
2399 | #endif /* IFF_PPROMISC */ | |
2400 | DEF_CMD("add", IFF_UP, notealias), | |
2401 | DEF_CMD("alias", IFF_UP, notealias), | |
2402 | DEF_CMD("-alias", -IFF_UP, notealias), | |
2403 | DEF_CMD("delete", -IFF_UP, notealias), | |
2404 | DEF_CMD("remove", -IFF_UP, notealias), | |
2405 | #ifdef notdef | |
2406 | #define EN_SWABIPS 0x1000 | |
2407 | DEF_CMD("swabips", EN_SWABIPS, setifflags), | |
2408 | DEF_CMD("-swabips", -EN_SWABIPS, setifflags), | |
2409 | #endif | |
2410 | DEF_CMD_ARG("netmask", setifnetmask), | |
2411 | DEF_CMD_ARG("metric", setifmetric), | |
2412 | DEF_CMD_ARG("broadcast", setifbroadaddr), | |
2413 | DEF_CMD_ARG("ipdst", setifipdst), | |
2414 | DEF_CMD_ARG2("tunnel", settunnel), | |
2415 | DEF_CMD("-tunnel", 0, deletetunnel), | |
2416 | DEF_CMD("deletetunnel", 0, deletetunnel), | |
2417 | DEF_CMD("link0", IFF_LINK0, setifflags), | |
2418 | DEF_CMD("-link0", -IFF_LINK0, setifflags), | |
2419 | DEF_CMD("link1", IFF_LINK1, setifflags), | |
2420 | DEF_CMD("-link1", -IFF_LINK1, setifflags), | |
2421 | DEF_CMD("link2", IFF_LINK2, setifflags), | |
2422 | DEF_CMD("-link2", -IFF_LINK2, setifflags), | |
2423 | #ifdef IFF_MONITOR | |
2424 | DEF_CMD("monitor", IFF_MONITOR:, setifflags), | |
2425 | DEF_CMD("-monitor", -IFF_MONITOR, setifflags), | |
2426 | #endif /* IFF_MONITOR */ | |
2427 | DEF_CMD("mpklog", 1, setifmpklog), | |
2428 | DEF_CMD("-mpklog", 0, setifmpklog), | |
2429 | #ifdef IFF_STATICARP | |
2430 | DEF_CMD("staticarp", IFF_STATICARP, setifflags), | |
2431 | DEF_CMD("-staticarp", -IFF_STATICARP, setifflags), | |
2432 | #endif /* IFF_STATICARP */ | |
2433 | #ifdef IFCAP_RXCSUM | |
2434 | DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap), | |
2435 | DEF_CMD("-rxcsum", -IFCAP_RXCSUM, setifcap), | |
2436 | #endif /* IFCAP_RXCSUM */ | |
2437 | #ifdef IFCAP_TXCSUM | |
2438 | DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap), | |
2439 | DEF_CMD("-txcsum", -IFCAP_TXCSUM, setifcap), | |
2440 | #endif /* IFCAP_TXCSUM */ | |
2441 | #ifdef IFCAP_NETCONS | |
2442 | DEF_CMD("netcons", IFCAP_NETCONS, setifcap), | |
2443 | DEF_CMD("-netcons", -IFCAP_NETCONS, setifcap), | |
2444 | #endif /* IFCAP_NETCONS */ | |
2445 | #ifdef IFCAP_POLLING | |
2446 | DEF_CMD("polling", IFCAP_POLLING, setifcap), | |
2447 | DEF_CMD("-polling", -IFCAP_POLLING, setifcap), | |
2448 | #endif /* IFCAP_POLLING */ | |
2449 | #ifdef IFCAP_TSO | |
2450 | DEF_CMD("tso", IFCAP_TSO, setifcap), | |
2451 | DEF_CMD("-tso", -IFCAP_TSO, setifcap), | |
2452 | #endif /* IFCAP_TSO */ | |
2453 | #ifdef IFCAP_LRO | |
2454 | DEF_CMD("lro", IFCAP_LRO, setifcap), | |
2455 | DEF_CMD("-lro", -IFCAP_LRO, setifcap), | |
2456 | #endif /* IFCAP_LRO */ | |
2457 | #ifdef IFCAP_WOL | |
2458 | DEF_CMD("wol", IFCAP_WOL, setifcap), | |
2459 | DEF_CMD("-wol", -IFCAP_WOL, setifcap), | |
2460 | #endif /* IFCAP_WOL */ | |
2461 | #ifdef IFCAP_WOL_UCAST | |
2462 | DEF_CMD("wol_ucast", IFCAP_WOL_UCAST, setifcap), | |
2463 | DEF_CMD("-wol_ucast", -IFCAP_WOL_UCAST, setifcap), | |
2464 | #endif /* IFCAP_WOL_UCAST */ | |
2465 | #ifdef IFCAP_WOL_MCAST | |
2466 | DEF_CMD("wol_mcast", IFCAP_WOL_MCAST, setifcap), | |
2467 | DEF_CMD("-wol_mcast", -IFCAP_WOL_MCAST, setifcap), | |
2468 | #endif /* IFCAP_WOL_MCAST */ | |
2469 | #ifdef IFCAP_WOL_MAGIC | |
2470 | DEF_CMD("wol_magic", IFCAP_WOL_MAGIC, setifcap), | |
2471 | DEF_CMD("-wol_magic", -IFCAP_WOL_MAGIC, setifcap), | |
2472 | #endif /* IFCAP_WOL_MAGIC */ | |
2473 | DEF_CMD("normal", -IFF_LINK0, setifflags), | |
2474 | DEF_CMD("compress", IFF_LINK0, setifflags), | |
2475 | DEF_CMD("noicmp", IFF_LINK1, setifflags), | |
2476 | DEF_CMD_ARG("mtu", setifmtu), | |
2477 | #ifdef notdef | |
2478 | DEF_CMD_ARG("name", setifname), | |
2479 | #endif /* notdef */ | |
2480 | #ifdef IFCAP_AV | |
2481 | DEF_CMD("av", IFCAP_AV, setifcap), | |
2482 | DEF_CMD("-av", -IFCAP_AV, setifcap), | |
2483 | #endif /* IFCAP_AV */ | |
2484 | DEF_CMD("router", 1, setrouter), | |
2485 | DEF_CMD("-router", 0, setrouter), | |
2486 | DEF_CMD_VA("routermode", routermode), | |
2487 | DEF_CMD_ARG("desc", setifdesc), | |
2488 | DEF_CMD_ARG("tbr", settbr), | |
2489 | DEF_CMD_VA("netem", setnetem), | |
2490 | DEF_CMD_ARG("throttle", setthrottle), | |
2491 | DEF_CMD_ARG("log", setlog), | |
2492 | DEF_CMD("cl2k", 1, setcl2k), | |
2493 | DEF_CMD("-cl2k", 0, setcl2k), | |
2494 | DEF_CMD("expensive", 1, setexpensive), | |
2495 | DEF_CMD("-expensive", 0, setexpensive), | |
2496 | #ifdef SIOCSIFCONSTRAINED | |
2497 | DEF_CMD("constrained", 1, setconstrained), | |
2498 | DEF_CMD("-constrained", 0, setconstrained), | |
2499 | #endif | |
2500 | DEF_CMD("timestamp", 1, settimestamp), | |
2501 | DEF_CMD("-timestamp", 0, settimestamp), | |
2502 | DEF_CMD_ARG("ecn", setecnmode), | |
2503 | DEF_CMD_ARG2("fastlane", setfastlane), | |
2504 | DEF_CMD_ARG2("qosmarking", setqosmarking), | |
2505 | DEF_CMD_ARG("disable_output", setdisableoutput), | |
2506 | DEF_CMD("probe_connectivity", 1, setprobeconnectivity), | |
2507 | DEF_CMD("-probe_connectivity", 0, setprobeconnectivity), | |
2508 | DEF_CMD("lowpowermode", 1, setlowpowermode), | |
2509 | DEF_CMD("-lowpowermode", 0, setlowpowermode), | |
2510 | DEF_CMD_ARG("subfamily", setifsubfamily), | |
2511 | DEF_CMD("available", 1, setifavailability), | |
2512 | DEF_CMD("-available", 0, setifavailability), | |
2513 | DEF_CMD("unavailable", 0, setifavailability), | |
2514 | }; | |
2515 | ||
2516 | static __constructor void | |
2517 | ifconfig_ctor(void) | |
2518 | { | |
2519 | #define N(a) (sizeof(a) / sizeof(a[0])) | |
2520 | int i; | |
2521 | ||
2522 | for (i = 0; i < N(basic_cmds); i++) | |
2523 | cmd_register(&basic_cmds[i]); | |
2524 | #undef N | |
2525 | } | |
2526 | ||
2527 | static char * | |
2528 | sched2str(unsigned int s) | |
2529 | { | |
2530 | char *c; | |
2531 | ||
2532 | switch (s) { | |
2533 | case PKTSCHEDT_NONE: | |
2534 | c = "NONE"; | |
2535 | break; | |
2536 | case PKTSCHEDT_FQ_CODEL: | |
2537 | c = "FQ_CODEL"; | |
2538 | break; | |
2539 | default: | |
2540 | c = "UNKNOWN"; | |
2541 | break; | |
2542 | } | |
2543 | ||
2544 | return (c); | |
2545 | } | |
2546 | ||
2547 | static char * | |
2548 | tl2str(unsigned int s) | |
2549 | { | |
2550 | char *c; | |
2551 | ||
2552 | switch (s) { | |
2553 | case IFNET_THROTTLE_OFF: | |
2554 | c = "off"; | |
2555 | break; | |
2556 | case IFNET_THROTTLE_OPPORTUNISTIC: | |
2557 | c = "opportunistic"; | |
2558 | break; | |
2559 | default: | |
2560 | c = "unknown"; | |
2561 | break; | |
2562 | } | |
2563 | ||
2564 | return (c); | |
2565 | } | |
2566 | ||
2567 | static char * | |
2568 | ift2str(unsigned int t, unsigned int f, unsigned int sf) | |
2569 | { | |
2570 | static char buf[256]; | |
2571 | char *c = NULL; | |
2572 | ||
2573 | switch (t) { | |
2574 | case IFT_ETHER: | |
2575 | switch (sf) { | |
2576 | case IFRTYPE_SUBFAMILY_USB: | |
2577 | c = "USB Ethernet"; | |
2578 | break; | |
2579 | case IFRTYPE_SUBFAMILY_BLUETOOTH: | |
2580 | c = "Bluetooth PAN"; | |
2581 | break; | |
2582 | case IFRTYPE_SUBFAMILY_WIFI: | |
2583 | c = "Wi-Fi"; | |
2584 | break; | |
2585 | case IFRTYPE_SUBFAMILY_THUNDERBOLT: | |
2586 | c = "IP over Thunderbolt"; | |
2587 | break; | |
2588 | case IFRTYPE_SUBFAMILY_ANY: | |
2589 | default: | |
2590 | c = "Ethernet"; | |
2591 | break; | |
2592 | } | |
2593 | break; | |
2594 | ||
2595 | case IFT_IEEE1394: | |
2596 | c = "IP over FireWire"; | |
2597 | break; | |
2598 | ||
2599 | case IFT_PKTAP: | |
2600 | c = "Packet capture"; | |
2601 | break; | |
2602 | ||
2603 | case IFT_CELLULAR: | |
2604 | c = "Cellular"; | |
2605 | break; | |
2606 | ||
2607 | case IFT_OTHER: | |
2608 | if (ifr.ifr_type.ift_family == APPLE_IF_FAM_IPSEC) { | |
2609 | if (ifr.ifr_type.ift_subfamily == IFRTYPE_SUBFAMILY_BLUETOOTH) { | |
2610 | c = "Companion Link Bluetooth"; | |
2611 | } else if (ifr.ifr_type.ift_subfamily == IFRTYPE_SUBFAMILY_QUICKRELAY) { | |
2612 | c = "Companion Link QuickRelay"; | |
2613 | } else if (ifr.ifr_type.ift_subfamily == IFRTYPE_SUBFAMILY_WIFI) { | |
2614 | c = "Companion Link Wi-Fi"; | |
2615 | } else if (ifr.ifr_type.ift_subfamily == IFRTYPE_SUBFAMILY_DEFAULT) { | |
2616 | c = "Companion Link Default"; | |
2617 | } | |
2618 | } | |
2619 | break; | |
2620 | ||
2621 | case IFT_BRIDGE: | |
2622 | case IFT_PFLOG: | |
2623 | case IFT_PFSYNC: | |
2624 | case IFT_PPP: | |
2625 | case IFT_LOOP: | |
2626 | case IFT_GIF: | |
2627 | case IFT_STF: | |
2628 | case IFT_L2VLAN: | |
2629 | case IFT_IEEE8023ADLAG: | |
2630 | default: | |
2631 | break; | |
2632 | } | |
2633 | ||
2634 | if (verbose > 1) { | |
2635 | if (c == NULL) { | |
2636 | (void) snprintf(buf, sizeof (buf), | |
2637 | "0x%x family: %u subfamily: %u", | |
2638 | ifr.ifr_type.ift_type, ifr.ifr_type.ift_family, | |
2639 | ifr.ifr_type.ift_subfamily); | |
2640 | } else { | |
2641 | (void) snprintf(buf, sizeof (buf), | |
2642 | "%s (0x%x) family: %u subfamily: %u", c, | |
2643 | ifr.ifr_type.ift_type, ifr.ifr_type.ift_family, | |
2644 | ifr.ifr_type.ift_subfamily); | |
2645 | } | |
2646 | c = buf; | |
2647 | } | |
2648 | ||
2649 | return (c); | |
2650 | } | |
2651 | ||
2652 | static char * | |
2653 | iffunct2str(u_int32_t functional_type) | |
2654 | { | |
2655 | char *str = NULL; | |
2656 | ||
2657 | switch (functional_type) { | |
2658 | case IFRTYPE_FUNCTIONAL_UNKNOWN: | |
2659 | break; | |
2660 | ||
2661 | case IFRTYPE_FUNCTIONAL_LOOPBACK: | |
2662 | str = "loopback"; | |
2663 | break; | |
2664 | ||
2665 | case IFRTYPE_FUNCTIONAL_WIRED: | |
2666 | str = "wired"; | |
2667 | break; | |
2668 | ||
2669 | case IFRTYPE_FUNCTIONAL_WIFI_INFRA: | |
2670 | str = "wifi"; | |
2671 | break; | |
2672 | ||
2673 | case IFRTYPE_FUNCTIONAL_WIFI_AWDL: | |
2674 | str = "awdl"; | |
2675 | break; | |
2676 | ||
2677 | case IFRTYPE_FUNCTIONAL_CELLULAR: | |
2678 | str = "cellular"; | |
2679 | break; | |
2680 | ||
2681 | case IFRTYPE_FUNCTIONAL_INTCOPROC: | |
2682 | break; | |
2683 | ||
2684 | case IFRTYPE_FUNCTIONAL_COMPANIONLINK: | |
2685 | str = "companionlink"; | |
2686 | break; | |
2687 | ||
2688 | default: | |
2689 | break; | |
2690 | } | |
2691 | return str; | |
2692 | } |