1 /* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #ifndef lint 31 static const char copyright[] = 32 "@(#) Copyright (c) 1983, 1993\n\ 33 The Regents of the University of California. All rights reserved.\n"; 34 #endif /* not lint */ 35 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94"; 39 #endif 40 static const char rcsid[] = 41 "$FreeBSD$"; 42 #endif /* not lint */ 43 44 #include <sys/param.h> 45 #include <sys/ioctl.h> 46 #include <sys/module.h> 47 #include <sys/linker.h> 48 #include <sys/queue.h> 49 #include <sys/socket.h> 50 #include <sys/time.h> 51 52 #include <net/ethernet.h> 53 #include <net/if.h> 54 #include <net/if_dl.h> 55 #include <net/if_types.h> 56 #include <net/route.h> 57 58 /* IP */ 59 #include <netinet/in.h> 60 #include <netinet/in_var.h> 61 #include <arpa/inet.h> 62 #include <netdb.h> 63 64 #include <ifaddrs.h> 65 #include <ctype.h> 66 #include <err.h> 67 #include <errno.h> 68 #include <fcntl.h> 69 #ifdef JAIL 70 #include <jail.h> 71 #endif 72 #include <stdio.h> 73 #include <stdlib.h> 74 #include <string.h> 75 #include <unistd.h> 76 77 #include "ifconfig.h" 78 79 /* 80 * Since "struct ifreq" is composed of various union members, callers 81 * should pay special attention to interpret the value. 82 * (.e.g. little/big endian difference in the structure.) 83 */ 84 struct ifreq ifr; 85 86 char name[IFNAMSIZ]; 87 char *descr = NULL; 88 size_t descrlen = 64; 89 int setaddr; 90 int setmask; 91 int doalias; 92 int clearaddr; 93 int newaddr = 1; 94 int verbose; 95 int noload; 96 97 int supmedia = 0; 98 int printkeys = 0; /* Print keying material for interfaces. */ 99 100 static int ifconfig(int argc, char *const *argv, int iscreate, 101 const struct afswtch *afp); 102 static void status(const struct afswtch *afp, const struct sockaddr_dl *sdl, 103 struct ifaddrs *ifa); 104 static void tunnel_status(int s); 105 static void usage(void); 106 107 static struct afswtch *af_getbyname(const char *name); 108 static struct afswtch *af_getbyfamily(int af); 109 static void af_other_status(int); 110 111 static struct option *opts = NULL; 112 113 struct ifa_order_elt { 114 int if_order; 115 int af_orders[255]; 116 struct ifaddrs *ifa; 117 TAILQ_ENTRY(ifa_order_elt) link; 118 }; 119 120 TAILQ_HEAD(ifa_queue, ifa_order_elt); 121 122 void 123 opt_register(struct option *p) 124 { 125 p->next = opts; 126 opts = p; 127 } 128 129 static void 130 usage(void) 131 { 132 char options[1024]; 133 struct option *p; 134 135 /* XXX not right but close enough for now */ 136 options[0] = '\0'; 137 for (p = opts; p != NULL; p = p->next) { 138 strlcat(options, p->opt_usage, sizeof(options)); 139 strlcat(options, " ", sizeof(options)); 140 } 141 142 fprintf(stderr, 143 "usage: ifconfig %sinterface address_family [address [dest_address]]\n" 144 " [parameters]\n" 145 " ifconfig interface create\n" 146 " ifconfig -a %s[-d] [-m] [-u] [-v] [address_family]\n" 147 " ifconfig -l [-d] [-u] [address_family]\n" 148 " ifconfig %s[-d] [-m] [-u] [-v]\n", 149 options, options, options); 150 exit(1); 151 } 152 153 #define ORDERS_SIZE(x) sizeof(x) / sizeof(x[0]) 154 155 static int 156 calcorders(struct ifaddrs *ifa, struct ifa_queue *q) 157 { 158 struct ifaddrs *prev; 159 struct ifa_order_elt *cur; 160 unsigned int ord, af, ifa_ord; 161 162 prev = NULL; 163 cur = NULL; 164 ord = 0; 165 ifa_ord = 0; 166 167 while (ifa != NULL) { 168 if (prev == NULL || 169 strcmp(ifa->ifa_name, prev->ifa_name) != 0) { 170 cur = calloc(1, sizeof(*cur)); 171 172 if (cur == NULL) 173 return (-1); 174 175 TAILQ_INSERT_TAIL(q, cur, link); 176 cur->if_order = ifa_ord ++; 177 cur->ifa = ifa; 178 ord = 0; 179 } 180 181 if (ifa->ifa_addr) { 182 af = ifa->ifa_addr->sa_family; 183 184 if (af < ORDERS_SIZE(cur->af_orders) && 185 cur->af_orders[af] == 0) 186 cur->af_orders[af] = ++ord; 187 } 188 prev = ifa; 189 ifa = ifa->ifa_next; 190 } 191 192 return (0); 193 } 194 195 static int 196 cmpifaddrs(struct ifaddrs *a, struct ifaddrs *b, struct ifa_queue *q) 197 { 198 struct ifa_order_elt *cur, *e1, *e2; 199 unsigned int af1, af2; 200 int ret; 201 202 e1 = e2 = NULL; 203 204 ret = strcmp(a->ifa_name, b->ifa_name); 205 if (ret != 0) { 206 TAILQ_FOREACH(cur, q, link) { 207 if (e1 && e2) 208 break; 209 210 if (strcmp(cur->ifa->ifa_name, a->ifa_name) == 0) 211 e1 = cur; 212 else if (strcmp(cur->ifa->ifa_name, b->ifa_name) == 0) 213 e2 = cur; 214 } 215 216 if (!e1 || !e2) 217 return (0); 218 else 219 return (e1->if_order - e2->if_order); 220 221 } else if (a->ifa_addr != NULL && b->ifa_addr != NULL) { 222 TAILQ_FOREACH(cur, q, link) { 223 if (strcmp(cur->ifa->ifa_name, a->ifa_name) == 0) { 224 e1 = cur; 225 break; 226 } 227 } 228 229 if (!e1) 230 return (0); 231 232 af1 = a->ifa_addr->sa_family; 233 af2 = b->ifa_addr->sa_family; 234 235 if (af1 < ORDERS_SIZE(e1->af_orders) && 236 af2 < ORDERS_SIZE(e1->af_orders)) 237 return (e1->af_orders[af1] - e1->af_orders[af2]); 238 } 239 240 return (0); 241 } 242 243 #undef ORDERS_SIZE 244 245 static struct ifaddrs * 246 sortifaddrs(struct ifaddrs *list, 247 int (*compare)(struct ifaddrs *, struct ifaddrs *, struct ifa_queue *), 248 struct ifa_queue *q) 249 { 250 struct ifaddrs *right, *temp, *last, *result, *next, *tail; 251 252 right = list; 253 temp = list; 254 last = list; 255 result = NULL; 256 next = NULL; 257 tail = NULL; 258 259 if (!list || !list->ifa_next) 260 return (list); 261 262 while (temp && temp->ifa_next) { 263 last = right; 264 right = right->ifa_next; 265 temp = temp->ifa_next->ifa_next; 266 } 267 268 last->ifa_next = NULL; 269 270 list = sortifaddrs(list, compare, q); 271 right = sortifaddrs(right, compare, q); 272 273 while (list || right) { 274 275 if (!right) { 276 next = list; 277 list = list->ifa_next; 278 } else if (!list) { 279 next = right; 280 right = right->ifa_next; 281 } else if (compare(list, right, q) <= 0) { 282 next = list; 283 list = list->ifa_next; 284 } else { 285 next = right; 286 right = right->ifa_next; 287 } 288 289 if (!result) 290 result = next; 291 else 292 tail->ifa_next = next; 293 294 tail = next; 295 } 296 297 return (result); 298 } 299 300 int 301 main(int argc, char *argv[]) 302 { 303 int c, all, namesonly, downonly, uponly; 304 const struct afswtch *afp = NULL; 305 int ifindex; 306 struct ifaddrs *ifap, *sifap, *ifa; 307 struct ifreq paifr; 308 const struct sockaddr_dl *sdl; 309 char options[1024], *cp, *namecp = NULL; 310 struct ifa_queue q = TAILQ_HEAD_INITIALIZER(q); 311 struct ifa_order_elt *cur, *tmp; 312 const char *ifname; 313 struct option *p; 314 size_t iflen; 315 316 all = downonly = uponly = namesonly = noload = verbose = 0; 317 318 /* Parse leading line options */ 319 strlcpy(options, "adklmnuv", sizeof(options)); 320 for (p = opts; p != NULL; p = p->next) 321 strlcat(options, p->opt, sizeof(options)); 322 while ((c = getopt(argc, argv, options)) != -1) { 323 switch (c) { 324 case 'a': /* scan all interfaces */ 325 all++; 326 break; 327 case 'd': /* restrict scan to "down" interfaces */ 328 downonly++; 329 break; 330 case 'k': 331 printkeys++; 332 break; 333 case 'l': /* scan interface names only */ 334 namesonly++; 335 break; 336 case 'm': /* show media choices in status */ 337 supmedia = 1; 338 break; 339 case 'n': /* suppress module loading */ 340 noload++; 341 break; 342 case 'u': /* restrict scan to "up" interfaces */ 343 uponly++; 344 break; 345 case 'v': 346 verbose++; 347 break; 348 default: 349 for (p = opts; p != NULL; p = p->next) 350 if (p->opt[0] == c) { 351 p->cb(optarg); 352 break; 353 } 354 if (p == NULL) 355 usage(); 356 break; 357 } 358 } 359 argc -= optind; 360 argv += optind; 361 362 /* -l cannot be used with -a or -m */ 363 if (namesonly && (all || supmedia)) 364 usage(); 365 366 /* nonsense.. */ 367 if (uponly && downonly) 368 usage(); 369 370 /* no arguments is equivalent to '-a' */ 371 if (!namesonly && argc < 1) 372 all = 1; 373 374 /* -a and -l allow an address family arg to limit the output */ 375 if (all || namesonly) { 376 if (argc > 1) 377 usage(); 378 379 ifname = NULL; 380 ifindex = 0; 381 if (argc == 1) { 382 afp = af_getbyname(*argv); 383 if (afp == NULL) { 384 warnx("Address family '%s' unknown.", *argv); 385 usage(); 386 } 387 if (afp->af_name != NULL) 388 argc--, argv++; 389 /* leave with afp non-zero */ 390 } 391 } else { 392 /* not listing, need an argument */ 393 if (argc < 1) 394 usage(); 395 396 ifname = *argv; 397 argc--, argv++; 398 399 /* check and maybe load support for this interface */ 400 ifmaybeload(ifname); 401 402 ifindex = if_nametoindex(ifname); 403 if (ifindex == 0) { 404 /* 405 * NOTE: We must special-case the `create' command 406 * right here as we would otherwise fail when trying 407 * to find the interface. 408 */ 409 if (argc > 0 && (strcmp(argv[0], "create") == 0 || 410 strcmp(argv[0], "plumb") == 0)) { 411 iflen = strlcpy(name, ifname, sizeof(name)); 412 if (iflen >= sizeof(name)) 413 errx(1, "%s: cloning name too long", 414 ifname); 415 ifconfig(argc, argv, 1, NULL); 416 exit(0); 417 } 418 #ifdef JAIL 419 /* 420 * NOTE: We have to special-case the `-vnet' command 421 * right here as we would otherwise fail when trying 422 * to find the interface as it lives in another vnet. 423 */ 424 if (argc > 0 && (strcmp(argv[0], "-vnet") == 0)) { 425 iflen = strlcpy(name, ifname, sizeof(name)); 426 if (iflen >= sizeof(name)) 427 errx(1, "%s: interface name too long", 428 ifname); 429 ifconfig(argc, argv, 0, NULL); 430 exit(0); 431 } 432 #endif 433 errx(1, "interface %s does not exist", ifname); 434 } 435 } 436 437 /* Check for address family */ 438 if (argc > 0) { 439 afp = af_getbyname(*argv); 440 if (afp != NULL) 441 argc--, argv++; 442 } 443 444 if (getifaddrs(&ifap) != 0) 445 err(EXIT_FAILURE, "getifaddrs"); 446 447 cp = NULL; 448 449 if (calcorders(ifap, &q) != 0) 450 err(EXIT_FAILURE, "calcorders"); 451 452 sifap = sortifaddrs(ifap, cmpifaddrs, &q); 453 454 TAILQ_FOREACH_SAFE(cur, &q, link, tmp) 455 free(cur); 456 457 ifindex = 0; 458 for (ifa = sifap; ifa; ifa = ifa->ifa_next) { 459 memset(&paifr, 0, sizeof(paifr)); 460 strncpy(paifr.ifr_name, ifa->ifa_name, sizeof(paifr.ifr_name)); 461 if (sizeof(paifr.ifr_addr) >= ifa->ifa_addr->sa_len) { 462 memcpy(&paifr.ifr_addr, ifa->ifa_addr, 463 ifa->ifa_addr->sa_len); 464 } 465 466 if (ifname != NULL && strcmp(ifname, ifa->ifa_name) != 0) 467 continue; 468 if (ifa->ifa_addr->sa_family == AF_LINK) 469 sdl = (const struct sockaddr_dl *) ifa->ifa_addr; 470 else 471 sdl = NULL; 472 if (cp != NULL && strcmp(cp, ifa->ifa_name) == 0 && !namesonly) 473 continue; 474 iflen = strlcpy(name, ifa->ifa_name, sizeof(name)); 475 if (iflen >= sizeof(name)) { 476 warnx("%s: interface name too long, skipping", 477 ifa->ifa_name); 478 continue; 479 } 480 cp = ifa->ifa_name; 481 482 if ((ifa->ifa_flags & IFF_CANTCONFIG) != 0) 483 continue; 484 if (downonly && (ifa->ifa_flags & IFF_UP) != 0) 485 continue; 486 if (uponly && (ifa->ifa_flags & IFF_UP) == 0) 487 continue; 488 /* 489 * Are we just listing the interfaces? 490 */ 491 if (namesonly) { 492 if (namecp == cp) 493 continue; 494 if (afp != NULL) { 495 /* special case for "ether" address family */ 496 if (!strcmp(afp->af_name, "ether")) { 497 if (sdl == NULL || 498 (sdl->sdl_type != IFT_ETHER && 499 sdl->sdl_type != IFT_L2VLAN && 500 sdl->sdl_type != IFT_BRIDGE) || 501 sdl->sdl_alen != ETHER_ADDR_LEN) 502 continue; 503 } else { 504 if (ifa->ifa_addr->sa_family 505 != afp->af_af) 506 continue; 507 } 508 } 509 namecp = cp; 510 ifindex++; 511 if (ifindex > 1) 512 printf(" "); 513 fputs(name, stdout); 514 continue; 515 } 516 ifindex++; 517 518 if (argc > 0) 519 ifconfig(argc, argv, 0, afp); 520 else 521 status(afp, sdl, ifa); 522 } 523 if (namesonly) 524 printf("\n"); 525 freeifaddrs(ifap); 526 527 exit(0); 528 } 529 530 static struct afswtch *afs = NULL; 531 532 void 533 af_register(struct afswtch *p) 534 { 535 p->af_next = afs; 536 afs = p; 537 } 538 539 static struct afswtch * 540 af_getbyname(const char *name) 541 { 542 struct afswtch *afp; 543 544 for (afp = afs; afp != NULL; afp = afp->af_next) 545 if (strcmp(afp->af_name, name) == 0) 546 return afp; 547 return NULL; 548 } 549 550 static struct afswtch * 551 af_getbyfamily(int af) 552 { 553 struct afswtch *afp; 554 555 for (afp = afs; afp != NULL; afp = afp->af_next) 556 if (afp->af_af == af) 557 return afp; 558 return NULL; 559 } 560 561 static void 562 af_other_status(int s) 563 { 564 struct afswtch *afp; 565 uint8_t afmask[howmany(AF_MAX, NBBY)]; 566 567 memset(afmask, 0, sizeof(afmask)); 568 for (afp = afs; afp != NULL; afp = afp->af_next) { 569 if (afp->af_other_status == NULL) 570 continue; 571 if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) 572 continue; 573 afp->af_other_status(s); 574 setbit(afmask, afp->af_af); 575 } 576 } 577 578 static void 579 af_all_tunnel_status(int s) 580 { 581 struct afswtch *afp; 582 uint8_t afmask[howmany(AF_MAX, NBBY)]; 583 584 memset(afmask, 0, sizeof(afmask)); 585 for (afp = afs; afp != NULL; afp = afp->af_next) { 586 if (afp->af_status_tunnel == NULL) 587 continue; 588 if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af)) 589 continue; 590 afp->af_status_tunnel(s); 591 setbit(afmask, afp->af_af); 592 } 593 } 594 595 static struct cmd *cmds = NULL; 596 597 void 598 cmd_register(struct cmd *p) 599 { 600 p->c_next = cmds; 601 cmds = p; 602 } 603 604 static const struct cmd * 605 cmd_lookup(const char *name, int iscreate) 606 { 607 const struct cmd *p; 608 609 for (p = cmds; p != NULL; p = p->c_next) 610 if (strcmp(name, p->c_name) == 0) { 611 if (iscreate) { 612 if (p->c_iscloneop) 613 return p; 614 } else { 615 if (!p->c_iscloneop) 616 return p; 617 } 618 } 619 return NULL; 620 } 621 622 struct callback { 623 callback_func *cb_func; 624 void *cb_arg; 625 struct callback *cb_next; 626 }; 627 static struct callback *callbacks = NULL; 628 629 void 630 callback_register(callback_func *func, void *arg) 631 { 632 struct callback *cb; 633 634 cb = malloc(sizeof(struct callback)); 635 if (cb == NULL) 636 errx(1, "unable to allocate memory for callback"); 637 cb->cb_func = func; 638 cb->cb_arg = arg; 639 cb->cb_next = callbacks; 640 callbacks = cb; 641 } 642 643 /* specially-handled commands */ 644 static void setifaddr(const char *, int, int, const struct afswtch *); 645 static const struct cmd setifaddr_cmd = DEF_CMD("ifaddr", 0, setifaddr); 646 647 static void setifdstaddr(const char *, int, int, const struct afswtch *); 648 static const struct cmd setifdstaddr_cmd = 649 DEF_CMD("ifdstaddr", 0, setifdstaddr); 650 651 static int 652 ifconfig(int argc, char *const *argv, int iscreate, const struct afswtch *uafp) 653 { 654 const struct afswtch *afp, *nafp; 655 const struct cmd *p; 656 struct callback *cb; 657 int s; 658 659 strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); 660 afp = NULL; 661 if (uafp != NULL) 662 afp = uafp; 663 /* 664 * This is the historical "accident" allowing users to configure IPv4 665 * addresses without the "inet" keyword which while a nice feature has 666 * proven to complicate other things. We cannot remove this but only 667 * make sure we will never have a similar implicit default for IPv6 or 668 * any other address familiy. We need a fallback though for 669 * ifconfig IF up/down etc. to work without INET support as people 670 * never used ifconfig IF link up/down, etc. either. 671 */ 672 #ifndef RESCUE 673 #ifdef INET 674 if (afp == NULL && feature_present("inet")) 675 afp = af_getbyname("inet"); 676 #endif 677 #endif 678 if (afp == NULL) 679 afp = af_getbyname("link"); 680 if (afp == NULL) { 681 warnx("Please specify an address_family."); 682 usage(); 683 } 684 top: 685 ifr.ifr_addr.sa_family = 686 afp->af_af == AF_LINK || afp->af_af == AF_UNSPEC ? 687 AF_LOCAL : afp->af_af; 688 689 if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0 && 690 (uafp != NULL || errno != EAFNOSUPPORT || 691 (s = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0)) 692 err(1, "socket(family %u,SOCK_DGRAM", ifr.ifr_addr.sa_family); 693 694 while (argc > 0) { 695 p = cmd_lookup(*argv, iscreate); 696 if (iscreate && p == NULL) { 697 /* 698 * Push the clone create callback so the new 699 * device is created and can be used for any 700 * remaining arguments. 701 */ 702 cb = callbacks; 703 if (cb == NULL) 704 errx(1, "internal error, no callback"); 705 callbacks = cb->cb_next; 706 cb->cb_func(s, cb->cb_arg); 707 iscreate = 0; 708 /* 709 * Handle any address family spec that 710 * immediately follows and potentially 711 * recreate the socket. 712 */ 713 nafp = af_getbyname(*argv); 714 if (nafp != NULL) { 715 argc--, argv++; 716 if (nafp != afp) { 717 close(s); 718 afp = nafp; 719 goto top; 720 } 721 } 722 /* 723 * Look for a normal parameter. 724 */ 725 continue; 726 } 727 if (p == NULL) { 728 /* 729 * Not a recognized command, choose between setting 730 * the interface address and the dst address. 731 */ 732 p = (setaddr ? &setifdstaddr_cmd : &setifaddr_cmd); 733 } 734 if (p->c_u.c_func || p->c_u.c_func2) { 735 if (p->c_parameter == NEXTARG) { 736 if (argv[1] == NULL) 737 errx(1, "'%s' requires argument", 738 p->c_name); 739 p->c_u.c_func(argv[1], 0, s, afp); 740 argc--, argv++; 741 } else if (p->c_parameter == OPTARG) { 742 p->c_u.c_func(argv[1], 0, s, afp); 743 if (argv[1] != NULL) 744 argc--, argv++; 745 } else if (p->c_parameter == NEXTARG2) { 746 if (argc < 3) 747 errx(1, "'%s' requires 2 arguments", 748 p->c_name); 749 p->c_u.c_func2(argv[1], argv[2], s, afp); 750 argc -= 2, argv += 2; 751 } else 752 p->c_u.c_func(*argv, p->c_parameter, s, afp); 753 } 754 argc--, argv++; 755 } 756 757 /* 758 * Do any post argument processing required by the address family. 759 */ 760 if (afp->af_postproc != NULL) 761 afp->af_postproc(s, afp); 762 /* 763 * Do deferred callbacks registered while processing 764 * command-line arguments. 765 */ 766 for (cb = callbacks; cb != NULL; cb = cb->cb_next) 767 cb->cb_func(s, cb->cb_arg); 768 /* 769 * Do deferred operations. 770 */ 771 if (clearaddr) { 772 if (afp->af_ridreq == NULL || afp->af_difaddr == 0) { 773 warnx("interface %s cannot change %s addresses!", 774 name, afp->af_name); 775 clearaddr = 0; 776 } 777 } 778 if (clearaddr) { 779 int ret; 780 strncpy(afp->af_ridreq, name, sizeof ifr.ifr_name); 781 ret = ioctl(s, afp->af_difaddr, afp->af_ridreq); 782 if (ret < 0) { 783 if (errno == EADDRNOTAVAIL && (doalias >= 0)) { 784 /* means no previous address for interface */ 785 } else 786 Perror("ioctl (SIOCDIFADDR)"); 787 } 788 } 789 if (newaddr) { 790 if (afp->af_addreq == NULL || afp->af_aifaddr == 0) { 791 warnx("interface %s cannot change %s addresses!", 792 name, afp->af_name); 793 newaddr = 0; 794 } 795 } 796 if (newaddr && (setaddr || setmask)) { 797 strncpy(afp->af_addreq, name, sizeof ifr.ifr_name); 798 if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0) 799 Perror("ioctl (SIOCAIFADDR)"); 800 } 801 802 close(s); 803 return(0); 804 } 805 806 /*ARGSUSED*/ 807 static void 808 setifaddr(const char *addr, int param, int s, const struct afswtch *afp) 809 { 810 if (afp->af_getaddr == NULL) 811 return; 812 /* 813 * Delay the ioctl to set the interface addr until flags are all set. 814 * The address interpretation may depend on the flags, 815 * and the flags may change when the address is set. 816 */ 817 setaddr++; 818 if (doalias == 0 && afp->af_af != AF_LINK) 819 clearaddr = 1; 820 afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR)); 821 } 822 823 static void 824 settunnel(const char *src, const char *dst, int s, const struct afswtch *afp) 825 { 826 struct addrinfo *srcres, *dstres; 827 int ecode; 828 829 if (afp->af_settunnel == NULL) { 830 warn("address family %s does not support tunnel setup", 831 afp->af_name); 832 return; 833 } 834 835 if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0) 836 errx(1, "error in parsing address string: %s", 837 gai_strerror(ecode)); 838 839 if ((ecode = getaddrinfo(dst, NULL, NULL, &dstres)) != 0) 840 errx(1, "error in parsing address string: %s", 841 gai_strerror(ecode)); 842 843 if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family) 844 errx(1, 845 "source and destination address families do not match"); 846 847 afp->af_settunnel(s, srcres, dstres); 848 849 freeaddrinfo(srcres); 850 freeaddrinfo(dstres); 851 } 852 853 /* ARGSUSED */ 854 static void 855 deletetunnel(const char *vname, int param, int s, const struct afswtch *afp) 856 { 857 858 if (ioctl(s, SIOCDIFPHYADDR, &ifr) < 0) 859 err(1, "SIOCDIFPHYADDR"); 860 } 861 862 #ifdef JAIL 863 static void 864 setifvnet(const char *jname, int dummy __unused, int s, 865 const struct afswtch *afp) 866 { 867 struct ifreq my_ifr; 868 869 memcpy(&my_ifr, &ifr, sizeof(my_ifr)); 870 my_ifr.ifr_jid = jail_getid(jname); 871 if (my_ifr.ifr_jid < 0) 872 errx(1, "%s", jail_errmsg); 873 if (ioctl(s, SIOCSIFVNET, &my_ifr) < 0) 874 err(1, "SIOCSIFVNET"); 875 } 876 877 static void 878 setifrvnet(const char *jname, int dummy __unused, int s, 879 const struct afswtch *afp) 880 { 881 struct ifreq my_ifr; 882 883 memcpy(&my_ifr, &ifr, sizeof(my_ifr)); 884 my_ifr.ifr_jid = jail_getid(jname); 885 if (my_ifr.ifr_jid < 0) 886 errx(1, "%s", jail_errmsg); 887 if (ioctl(s, SIOCSIFRVNET, &my_ifr) < 0) 888 err(1, "SIOCSIFRVNET(%d, %s)", my_ifr.ifr_jid, my_ifr.ifr_name); 889 } 890 #endif 891 892 static void 893 setifnetmask(const char *addr, int dummy __unused, int s, 894 const struct afswtch *afp) 895 { 896 if (afp->af_getaddr != NULL) { 897 setmask++; 898 afp->af_getaddr(addr, MASK); 899 } 900 } 901 902 static void 903 setifbroadaddr(const char *addr, int dummy __unused, int s, 904 const struct afswtch *afp) 905 { 906 if (afp->af_getaddr != NULL) 907 afp->af_getaddr(addr, DSTADDR); 908 } 909 910 static void 911 notealias(const char *addr, int param, int s, const struct afswtch *afp) 912 { 913 #define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr)) 914 if (setaddr && doalias == 0 && param < 0) 915 if (afp->af_addreq != NULL && afp->af_ridreq != NULL) 916 bcopy((caddr_t)rqtosa(af_addreq), 917 (caddr_t)rqtosa(af_ridreq), 918 rqtosa(af_addreq)->sa_len); 919 doalias = param; 920 if (param < 0) { 921 clearaddr = 1; 922 newaddr = 0; 923 } else 924 clearaddr = 0; 925 #undef rqtosa 926 } 927 928 /*ARGSUSED*/ 929 static void 930 setifdstaddr(const char *addr, int param __unused, int s, 931 const struct afswtch *afp) 932 { 933 if (afp->af_getaddr != NULL) 934 afp->af_getaddr(addr, DSTADDR); 935 } 936 937 /* 938 * Note: doing an SIOCIGIFFLAGS scribbles on the union portion 939 * of the ifreq structure, which may confuse other parts of ifconfig. 940 * Make a private copy so we can avoid that. 941 */ 942 static void 943 setifflags(const char *vname, int value, int s, const struct afswtch *afp) 944 { 945 struct ifreq my_ifr; 946 int flags; 947 948 memset(&my_ifr, 0, sizeof(my_ifr)); 949 (void) strlcpy(my_ifr.ifr_name, name, sizeof(my_ifr.ifr_name)); 950 951 if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) { 952 Perror("ioctl (SIOCGIFFLAGS)"); 953 exit(1); 954 } 955 flags = (my_ifr.ifr_flags & 0xffff) | (my_ifr.ifr_flagshigh << 16); 956 957 if (value < 0) { 958 value = -value; 959 flags &= ~value; 960 } else 961 flags |= value; 962 my_ifr.ifr_flags = flags & 0xffff; 963 my_ifr.ifr_flagshigh = flags >> 16; 964 if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) 965 Perror(vname); 966 } 967 968 void 969 setifcap(const char *vname, int value, int s, const struct afswtch *afp) 970 { 971 int flags; 972 973 if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) < 0) { 974 Perror("ioctl (SIOCGIFCAP)"); 975 exit(1); 976 } 977 flags = ifr.ifr_curcap; 978 if (value < 0) { 979 value = -value; 980 flags &= ~value; 981 } else 982 flags |= value; 983 flags &= ifr.ifr_reqcap; 984 ifr.ifr_reqcap = flags; 985 if (ioctl(s, SIOCSIFCAP, (caddr_t)&ifr) < 0) 986 Perror(vname); 987 } 988 989 static void 990 setifmetric(const char *val, int dummy __unused, int s, 991 const struct afswtch *afp) 992 { 993 strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); 994 ifr.ifr_metric = atoi(val); 995 if (ioctl(s, SIOCSIFMETRIC, (caddr_t)&ifr) < 0) 996 err(1, "ioctl SIOCSIFMETRIC (set metric)"); 997 } 998 999 static void 1000 setifmtu(const char *val, int dummy __unused, int s, 1001 const struct afswtch *afp) 1002 { 1003 strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); 1004 ifr.ifr_mtu = atoi(val); 1005 if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) < 0) 1006 err(1, "ioctl SIOCSIFMTU (set mtu)"); 1007 } 1008 1009 static void 1010 setifname(const char *val, int dummy __unused, int s, 1011 const struct afswtch *afp) 1012 { 1013 char *newname; 1014 1015 newname = strdup(val); 1016 if (newname == NULL) 1017 err(1, "no memory to set ifname"); 1018 ifr.ifr_data = newname; 1019 if (ioctl(s, SIOCSIFNAME, (caddr_t)&ifr) < 0) { 1020 free(newname); 1021 err(1, "ioctl SIOCSIFNAME (set name)"); 1022 } 1023 strlcpy(name, newname, sizeof(name)); 1024 free(newname); 1025 } 1026 1027 /* ARGSUSED */ 1028 static void 1029 setifdescr(const char *val, int dummy __unused, int s, 1030 const struct afswtch *afp) 1031 { 1032 char *newdescr; 1033 1034 ifr.ifr_buffer.length = strlen(val) + 1; 1035 if (ifr.ifr_buffer.length == 1) { 1036 ifr.ifr_buffer.buffer = newdescr = NULL; 1037 ifr.ifr_buffer.length = 0; 1038 } else { 1039 newdescr = strdup(val); 1040 ifr.ifr_buffer.buffer = newdescr; 1041 if (newdescr == NULL) { 1042 warn("no memory to set ifdescr"); 1043 return; 1044 } 1045 } 1046 1047 if (ioctl(s, SIOCSIFDESCR, (caddr_t)&ifr) < 0) 1048 err(1, "ioctl SIOCSIFDESCR (set descr)"); 1049 1050 free(newdescr); 1051 } 1052 1053 /* ARGSUSED */ 1054 static void 1055 unsetifdescr(const char *val, int value, int s, const struct afswtch *afp) 1056 { 1057 1058 setifdescr("", 0, s, 0); 1059 } 1060 1061 #define IFFBITS \ 1062 "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\7RUNNING" \ 1063 "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ 1064 "\20MULTICAST\22PPROMISC\23MONITOR\24STATICARP" 1065 1066 #define IFCAPBITS \ 1067 "\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7POLLING" \ 1068 "\10VLAN_HWCSUM\11TSO4\12TSO6\13LRO\14WOL_UCAST\15WOL_MCAST\16WOL_MAGIC" \ 1069 "\17TOE4\20TOE6\21VLAN_HWFILTER\23VLAN_HWTSO\24LINKSTATE\25NETMAP" \ 1070 "\26RXCSUM_IPV6\27TXCSUM_IPV6" 1071 1072 /* 1073 * Print the status of the interface. If an address family was 1074 * specified, show only it; otherwise, show them all. 1075 */ 1076 static void 1077 status(const struct afswtch *afp, const struct sockaddr_dl *sdl, 1078 struct ifaddrs *ifa) 1079 { 1080 struct ifaddrs *ift; 1081 int allfamilies, s; 1082 struct ifstat ifs; 1083 1084 if (afp == NULL) { 1085 allfamilies = 1; 1086 ifr.ifr_addr.sa_family = AF_LOCAL; 1087 } else { 1088 allfamilies = 0; 1089 ifr.ifr_addr.sa_family = 1090 afp->af_af == AF_LINK ? AF_LOCAL : afp->af_af; 1091 } 1092 strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); 1093 1094 s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0); 1095 if (s < 0) 1096 err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family); 1097 1098 printf("%s: ", name); 1099 printb("flags", ifa->ifa_flags, IFFBITS); 1100 if (ioctl(s, SIOCGIFMETRIC, &ifr) != -1) 1101 printf(" metric %d", ifr.ifr_metric); 1102 if (ioctl(s, SIOCGIFMTU, &ifr) != -1) 1103 printf(" mtu %d", ifr.ifr_mtu); 1104 putchar('\n'); 1105 1106 for (;;) { 1107 if ((descr = reallocf(descr, descrlen)) != NULL) { 1108 ifr.ifr_buffer.buffer = descr; 1109 ifr.ifr_buffer.length = descrlen; 1110 if (ioctl(s, SIOCGIFDESCR, &ifr) == 0) { 1111 if (ifr.ifr_buffer.buffer == descr) { 1112 if (strlen(descr) > 0) 1113 printf("\tdescription: %s\n", 1114 descr); 1115 } else if (ifr.ifr_buffer.length > descrlen) { 1116 descrlen = ifr.ifr_buffer.length; 1117 continue; 1118 } 1119 } 1120 } else 1121 warn("unable to allocate memory for interface" 1122 "description"); 1123 break; 1124 } 1125 1126 if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) == 0) { 1127 if (ifr.ifr_curcap != 0) { 1128 printb("\toptions", ifr.ifr_curcap, IFCAPBITS); 1129 putchar('\n'); 1130 } 1131 if (supmedia && ifr.ifr_reqcap != 0) { 1132 printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS); 1133 putchar('\n'); 1134 } 1135 } 1136 1137 tunnel_status(s); 1138 1139 for (ift = ifa; ift != NULL; ift = ift->ifa_next) { 1140 if (ift->ifa_addr == NULL) 1141 continue; 1142 if (strcmp(ifa->ifa_name, ift->ifa_name) != 0) 1143 continue; 1144 if (allfamilies) { 1145 const struct afswtch *p; 1146 p = af_getbyfamily(ift->ifa_addr->sa_family); 1147 if (p != NULL && p->af_status != NULL) 1148 p->af_status(s, ift); 1149 } else if (afp->af_af == ift->ifa_addr->sa_family) 1150 afp->af_status(s, ift); 1151 } 1152 #if 0 1153 if (allfamilies || afp->af_af == AF_LINK) { 1154 const struct afswtch *lafp; 1155 1156 /* 1157 * Hack; the link level address is received separately 1158 * from the routing information so any address is not 1159 * handled above. Cobble together an entry and invoke 1160 * the status method specially. 1161 */ 1162 lafp = af_getbyname("lladdr"); 1163 if (lafp != NULL) { 1164 info.rti_info[RTAX_IFA] = (struct sockaddr *)sdl; 1165 lafp->af_status(s, &info); 1166 } 1167 } 1168 #endif 1169 if (allfamilies) 1170 af_other_status(s); 1171 else if (afp->af_other_status != NULL) 1172 afp->af_other_status(s); 1173 1174 strncpy(ifs.ifs_name, name, sizeof ifs.ifs_name); 1175 if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) 1176 printf("%s", ifs.ascii); 1177 1178 if (verbose > 0) 1179 sfp_status(s, &ifr, verbose); 1180 1181 close(s); 1182 return; 1183 } 1184 1185 static void 1186 tunnel_status(int s) 1187 { 1188 af_all_tunnel_status(s); 1189 } 1190 1191 void 1192 Perror(const char *cmd) 1193 { 1194 switch (errno) { 1195 1196 case ENXIO: 1197 errx(1, "%s: no such interface", cmd); 1198 break; 1199 1200 case EPERM: 1201 errx(1, "%s: permission denied", cmd); 1202 break; 1203 1204 default: 1205 err(1, "%s", cmd); 1206 } 1207 } 1208 1209 /* 1210 * Print a value a la the %b format of the kernel's printf 1211 */ 1212 void 1213 printb(const char *s, unsigned v, const char *bits) 1214 { 1215 int i, any = 0; 1216 char c; 1217 1218 if (bits && *bits == 8) 1219 printf("%s=%o", s, v); 1220 else 1221 printf("%s=%x", s, v); 1222 bits++; 1223 if (bits) { 1224 putchar('<'); 1225 while ((i = *bits++) != '\0') { 1226 if (v & (1 << (i-1))) { 1227 if (any) 1228 putchar(','); 1229 any = 1; 1230 for (; (c = *bits) > 32; bits++) 1231 putchar(c); 1232 } else 1233 for (; *bits > 32; bits++) 1234 ; 1235 } 1236 putchar('>'); 1237 } 1238 } 1239 1240 void 1241 print_vhid(const struct ifaddrs *ifa, const char *s) 1242 { 1243 struct if_data *ifd; 1244 1245 if (ifa->ifa_data == NULL) 1246 return; 1247 1248 ifd = ifa->ifa_data; 1249 if (ifd->ifi_vhid == 0) 1250 return; 1251 1252 printf("vhid %d ", ifd->ifi_vhid); 1253 } 1254 1255 void 1256 ifmaybeload(const char *name) 1257 { 1258 #define MOD_PREFIX_LEN 3 /* "if_" */ 1259 struct module_stat mstat; 1260 int fileid, modid; 1261 char ifkind[IFNAMSIZ + MOD_PREFIX_LEN], ifname[IFNAMSIZ], *dp; 1262 const char *cp; 1263 1264 /* loading suppressed by the user */ 1265 if (noload) 1266 return; 1267 1268 /* trim the interface number off the end */ 1269 strlcpy(ifname, name, sizeof(ifname)); 1270 for (dp = ifname; *dp != 0; dp++) 1271 if (isdigit(*dp)) { 1272 *dp = 0; 1273 break; 1274 } 1275 1276 /* turn interface and unit into module name */ 1277 strlcpy(ifkind, "if_", sizeof(ifkind)); 1278 strlcat(ifkind, ifname, sizeof(ifkind)); 1279 1280 /* scan files in kernel */ 1281 mstat.version = sizeof(struct module_stat); 1282 for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { 1283 /* scan modules in file */ 1284 for (modid = kldfirstmod(fileid); modid > 0; 1285 modid = modfnext(modid)) { 1286 if (modstat(modid, &mstat) < 0) 1287 continue; 1288 /* strip bus name if present */ 1289 if ((cp = strchr(mstat.name, '/')) != NULL) { 1290 cp++; 1291 } else { 1292 cp = mstat.name; 1293 } 1294 /* already loaded? */ 1295 if (strcmp(ifname, cp) == 0 || 1296 strcmp(ifkind, cp) == 0) 1297 return; 1298 } 1299 } 1300 1301 /* not present, we should try to load it */ 1302 kldload(ifkind); 1303 } 1304 1305 static struct cmd basic_cmds[] = { 1306 DEF_CMD("up", IFF_UP, setifflags), 1307 DEF_CMD("down", -IFF_UP, setifflags), 1308 DEF_CMD("arp", -IFF_NOARP, setifflags), 1309 DEF_CMD("-arp", IFF_NOARP, setifflags), 1310 DEF_CMD("debug", IFF_DEBUG, setifflags), 1311 DEF_CMD("-debug", -IFF_DEBUG, setifflags), 1312 DEF_CMD_ARG("description", setifdescr), 1313 DEF_CMD_ARG("descr", setifdescr), 1314 DEF_CMD("-description", 0, unsetifdescr), 1315 DEF_CMD("-descr", 0, unsetifdescr), 1316 DEF_CMD("promisc", IFF_PPROMISC, setifflags), 1317 DEF_CMD("-promisc", -IFF_PPROMISC, setifflags), 1318 DEF_CMD("add", IFF_UP, notealias), 1319 DEF_CMD("alias", IFF_UP, notealias), 1320 DEF_CMD("-alias", -IFF_UP, notealias), 1321 DEF_CMD("delete", -IFF_UP, notealias), 1322 DEF_CMD("remove", -IFF_UP, notealias), 1323 #ifdef notdef 1324 #define EN_SWABIPS 0x1000 1325 DEF_CMD("swabips", EN_SWABIPS, setifflags), 1326 DEF_CMD("-swabips", -EN_SWABIPS, setifflags), 1327 #endif 1328 DEF_CMD_ARG("netmask", setifnetmask), 1329 DEF_CMD_ARG("metric", setifmetric), 1330 DEF_CMD_ARG("broadcast", setifbroadaddr), 1331 DEF_CMD_ARG2("tunnel", settunnel), 1332 DEF_CMD("-tunnel", 0, deletetunnel), 1333 DEF_CMD("deletetunnel", 0, deletetunnel), 1334 #ifdef JAIL 1335 DEF_CMD_ARG("vnet", setifvnet), 1336 DEF_CMD_ARG("-vnet", setifrvnet), 1337 #endif 1338 DEF_CMD("link0", IFF_LINK0, setifflags), 1339 DEF_CMD("-link0", -IFF_LINK0, setifflags), 1340 DEF_CMD("link1", IFF_LINK1, setifflags), 1341 DEF_CMD("-link1", -IFF_LINK1, setifflags), 1342 DEF_CMD("link2", IFF_LINK2, setifflags), 1343 DEF_CMD("-link2", -IFF_LINK2, setifflags), 1344 DEF_CMD("monitor", IFF_MONITOR, setifflags), 1345 DEF_CMD("-monitor", -IFF_MONITOR, setifflags), 1346 DEF_CMD("staticarp", IFF_STATICARP, setifflags), 1347 DEF_CMD("-staticarp", -IFF_STATICARP, setifflags), 1348 DEF_CMD("rxcsum6", IFCAP_RXCSUM_IPV6, setifcap), 1349 DEF_CMD("-rxcsum6", -IFCAP_RXCSUM_IPV6, setifcap), 1350 DEF_CMD("txcsum6", IFCAP_TXCSUM_IPV6, setifcap), 1351 DEF_CMD("-txcsum6", -IFCAP_TXCSUM_IPV6, setifcap), 1352 DEF_CMD("rxcsum", IFCAP_RXCSUM, setifcap), 1353 DEF_CMD("-rxcsum", -IFCAP_RXCSUM, setifcap), 1354 DEF_CMD("txcsum", IFCAP_TXCSUM, setifcap), 1355 DEF_CMD("-txcsum", -IFCAP_TXCSUM, setifcap), 1356 DEF_CMD("netcons", IFCAP_NETCONS, setifcap), 1357 DEF_CMD("-netcons", -IFCAP_NETCONS, setifcap), 1358 DEF_CMD("polling", IFCAP_POLLING, setifcap), 1359 DEF_CMD("-polling", -IFCAP_POLLING, setifcap), 1360 DEF_CMD("tso6", IFCAP_TSO6, setifcap), 1361 DEF_CMD("-tso6", -IFCAP_TSO6, setifcap), 1362 DEF_CMD("tso4", IFCAP_TSO4, setifcap), 1363 DEF_CMD("-tso4", -IFCAP_TSO4, setifcap), 1364 DEF_CMD("tso", IFCAP_TSO, setifcap), 1365 DEF_CMD("-tso", -IFCAP_TSO, setifcap), 1366 DEF_CMD("toe", IFCAP_TOE, setifcap), 1367 DEF_CMD("-toe", -IFCAP_TOE, setifcap), 1368 DEF_CMD("lro", IFCAP_LRO, setifcap), 1369 DEF_CMD("-lro", -IFCAP_LRO, setifcap), 1370 DEF_CMD("wol", IFCAP_WOL, setifcap), 1371 DEF_CMD("-wol", -IFCAP_WOL, setifcap), 1372 DEF_CMD("wol_ucast", IFCAP_WOL_UCAST, setifcap), 1373 DEF_CMD("-wol_ucast", -IFCAP_WOL_UCAST, setifcap), 1374 DEF_CMD("wol_mcast", IFCAP_WOL_MCAST, setifcap), 1375 DEF_CMD("-wol_mcast", -IFCAP_WOL_MCAST, setifcap), 1376 DEF_CMD("wol_magic", IFCAP_WOL_MAGIC, setifcap), 1377 DEF_CMD("-wol_magic", -IFCAP_WOL_MAGIC, setifcap), 1378 DEF_CMD("normal", -IFF_LINK0, setifflags), 1379 DEF_CMD("compress", IFF_LINK0, setifflags), 1380 DEF_CMD("noicmp", IFF_LINK1, setifflags), 1381 DEF_CMD_ARG("mtu", setifmtu), 1382 DEF_CMD_ARG("name", setifname), 1383 }; 1384 1385 static __constructor void 1386 ifconfig_ctor(void) 1387 { 1388 size_t i; 1389 1390 for (i = 0; i < nitems(basic_cmds); i++) 1391 cmd_register(&basic_cmds[i]); 1392 } 1393