1 /* LWIP service - ifconf.c - interface configuration */ 2 3 #include "lwip.h" 4 #include "ifaddr.h" 5 #include "lldata.h" 6 7 #include <net/if_media.h> 8 #include <minix/if.h> 9 10 #define LOOPBACK_IFNAME "lo0" /* name of the loopback interface */ 11 12 /* 13 * Initialize the first loopback device, which is present by default. 14 */ 15 void 16 ifconf_init(void) 17 { 18 const struct sockaddr_in addr = { 19 .sin_family = AF_INET, 20 .sin_addr = { htonl(INADDR_LOOPBACK) } 21 }; 22 struct sockaddr_in6 ll_addr6 = { 23 .sin6_family = AF_INET6, 24 }; 25 const struct sockaddr_in6 lo_addr6 = { 26 .sin6_family = AF_INET6, 27 .sin6_addr = IN6ADDR_LOOPBACK_INIT 28 }; 29 const struct in6_addrlifetime lifetime = { 30 .ia6t_vltime = ND6_INFINITE_LIFETIME, 31 .ia6t_pltime = ND6_INFINITE_LIFETIME 32 }; 33 struct sockaddr_in6 mask6; 34 struct ifdev *ifdev; 35 socklen_t addr_len; 36 int r; 37 38 if ((r = ifdev_create(LOOPBACK_IFNAME)) != OK) 39 panic("unable to create loopback interface: %d", r); 40 41 if ((ifdev = ifdev_find_by_name(LOOPBACK_IFNAME)) == NULL) 42 panic("unable to find loopback interface"); 43 44 if ((r = ifaddr_v4_add(ifdev, &addr, NULL, NULL, NULL, 0)) != OK) 45 panic("unable to set IPv4 address on loopback interface: %d", 46 r); 47 48 addr_len = sizeof(mask6); 49 addr_put_netmask((struct sockaddr *)&mask6, &addr_len, IPADDR_TYPE_V6, 50 64 /*prefix*/); 51 52 ll_addr6.sin6_addr.s6_addr[0] = 0xfe; 53 ll_addr6.sin6_addr.s6_addr[1] = 0x80; 54 ll_addr6.sin6_addr.s6_addr[15] = ifdev_get_index(ifdev); 55 56 if ((r = ifaddr_v6_add(ifdev, &ll_addr6, &mask6, NULL, 0, 57 &lifetime)) != OK) 58 panic("unable to set IPv6 address on loopback interface: %d", 59 r); 60 61 addr_len = sizeof(mask6); 62 addr_put_netmask((struct sockaddr *)&mask6, &addr_len, IPADDR_TYPE_V6, 63 128 /*prefix*/); 64 65 if ((r = ifaddr_v6_add(ifdev, &lo_addr6, &mask6, NULL, 0, 66 &lifetime)) != OK) 67 panic("unable to set IPv6 address on loopback interface: %d", 68 r); 69 70 if ((r = ifdev_set_ifflags(ifdev, IFF_UP)) != OK) 71 panic("unable to bring up loopback interface"); 72 } 73 74 /* 75 * Process an address family independent IOCTL request with an "ifreq" 76 * structure. 77 */ 78 static int 79 ifconf_ioctl_ifreq(unsigned long request, const struct sockdriver_data * data) 80 { 81 struct ifdev *ifdev; 82 struct ifreq ifr; 83 int r; 84 85 if ((r = sockdriver_copyin(data, 0, &ifr, sizeof(ifr))) != OK) 86 return r; 87 88 if (request != SIOCIFCREATE) { 89 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0'; 90 91 if ((ifdev = ifdev_find_by_name(ifr.ifr_name)) == NULL) 92 return ENXIO; 93 } else 94 ifdev = NULL; 95 96 switch (request) { 97 case SIOCGIFFLAGS: 98 ifr.ifr_flags = ifdev_get_ifflags(ifdev); 99 100 return sockdriver_copyout(data, 0, &ifr, sizeof(ifr)); 101 102 case SIOCSIFFLAGS: 103 /* 104 * Unfortunately, ifr_flags is a signed integer and the sign 105 * bit is in fact used as a flag, so without explicit casting 106 * we end up setting all upper bits of the (full) integer. If 107 * NetBSD ever extends the field, this assert should trigger.. 108 */ 109 assert(sizeof(ifr.ifr_flags) == sizeof(short)); 110 111 return ifdev_set_ifflags(ifdev, (unsigned short)ifr.ifr_flags); 112 113 case SIOCGIFMETRIC: 114 ifr.ifr_metric = ifdev_get_metric(ifdev); 115 116 return sockdriver_copyout(data, 0, &ifr, sizeof(ifr)); 117 118 case SIOCSIFMETRIC: 119 /* The metric is not used within the operating system. */ 120 ifdev_set_metric(ifdev, ifr.ifr_metric); 121 122 return OK; 123 124 case SIOCSIFMEDIA: 125 return ifdev_set_ifmedia(ifdev, ifr.ifr_media); 126 127 case SIOCGIFMTU: 128 ifr.ifr_mtu = ifdev_get_mtu(ifdev); 129 130 return sockdriver_copyout(data, 0, &ifr, sizeof(ifr)); 131 132 case SIOCSIFMTU: 133 return ifdev_set_mtu(ifdev, ifr.ifr_mtu); 134 135 case SIOCIFCREATE: 136 if (memchr(ifr.ifr_name, '\0', sizeof(ifr.ifr_name)) == NULL) 137 return EINVAL; 138 139 return ifdev_create(ifr.ifr_name); 140 141 case SIOCIFDESTROY: 142 return ifdev_destroy(ifdev); 143 144 case SIOCGIFDLT: 145 ifr.ifr_dlt = ifdev_get_dlt(ifdev); 146 147 return sockdriver_copyout(data, 0, &ifr, sizeof(ifr)); 148 149 case SIOCGIFINDEX: 150 ifr.ifr_index = ifdev_get_index(ifdev); 151 152 return sockdriver_copyout(data, 0, &ifr, sizeof(ifr)); 153 154 default: 155 return ENOTTY; 156 } 157 } 158 159 /* 160 * Process an address family independent IOCTL request with an "ifcapreq" 161 * structure. 162 */ 163 static int 164 ifconf_ioctl_ifcap(unsigned long request, 165 const struct sockdriver_data * data) 166 { 167 struct ifdev *ifdev; 168 struct ifcapreq ifcr; 169 int r; 170 171 if ((r = sockdriver_copyin(data, 0, &ifcr, sizeof(ifcr))) != OK) 172 return r; 173 174 ifcr.ifcr_name[sizeof(ifcr.ifcr_name) - 1] = '\0'; 175 176 if ((ifdev = ifdev_find_by_name(ifcr.ifcr_name)) == NULL) 177 return ENXIO; 178 179 switch (request) { 180 case SIOCSIFCAP: 181 return ifdev_set_ifcap(ifdev, ifcr.ifcr_capenable); 182 183 case SIOCGIFCAP: 184 ifdev_get_ifcap(ifdev, &ifcr.ifcr_capabilities, 185 &ifcr.ifcr_capenable); 186 187 return sockdriver_copyout(data, 0, &ifcr, sizeof(ifcr)); 188 189 default: 190 return ENOTTY; 191 } 192 } 193 194 /* 195 * Process an address family independent IOCTL request with an "ifmediareq" 196 * structure. 197 */ 198 static int 199 ifconf_ioctl_ifmedia(unsigned long request, 200 const struct sockdriver_data * data) 201 { 202 struct ifdev *ifdev; 203 struct ifmediareq ifm; 204 int r; 205 206 if ((r = sockdriver_copyin(data, 0, &ifm, sizeof(ifm))) != OK) 207 return r; 208 209 ifm.ifm_name[sizeof(ifm.ifm_name) - 1] = '\0'; 210 211 if ((ifdev = ifdev_find_by_name(ifm.ifm_name)) == NULL) 212 return ENXIO; 213 214 switch (request) { 215 case MINIX_SIOCGIFMEDIA: 216 if ((r = ifdev_get_ifmedia(ifdev, &ifm.ifm_current, 217 &ifm.ifm_active)) != OK) 218 return r; 219 ifm.ifm_mask = 0; 220 221 switch (ifdev_get_link(ifdev)) { 222 case LINK_STATE_UP: 223 ifm.ifm_status = IFM_AVALID | IFM_ACTIVE; 224 break; 225 case LINK_STATE_DOWN: 226 ifm.ifm_status = IFM_AVALID; 227 break; 228 default: 229 ifm.ifm_status = 0; 230 break; 231 } 232 233 /* 234 * TODO: support for the list of supported media types. This 235 * one is not easy, because we cannot simply suspend the IOCTL 236 * and query the driver. For now, return only entry (which is 237 * the minimum for ifconfig(8) not to complain), namely the 238 * currently selected one. 239 */ 240 if (ifm.ifm_ulist != NULL) { 241 if (ifm.ifm_count < 1) 242 return ENOMEM; 243 244 /* 245 * Copy out the 'list', which consists of one entry. 246 * If we were to produce multiple entries, we would 247 * have to check against the MINIX_IF_MAXMEDIA limit. 248 */ 249 if ((r = sockdriver_copyout(data, 250 offsetof(struct minix_ifmediareq, mifm_list), 251 &ifm.ifm_current, sizeof(ifm.ifm_current))) != OK) 252 return r; 253 } 254 ifm.ifm_count = 1; 255 256 return sockdriver_copyout(data, 0, &ifm, sizeof(ifm)); 257 258 default: 259 return ENOTTY; 260 } 261 } 262 263 /* 264 * Process an address family independent IOCTL request with an "if_clonereq" 265 * structure. 266 */ 267 static int 268 ifconf_ioctl_ifclone(unsigned long request, 269 const struct sockdriver_data * data) 270 { 271 struct if_clonereq ifcr; 272 const char *ptr; 273 char name[IFNAMSIZ]; 274 size_t off; 275 unsigned int num; 276 int r; 277 278 if ((r = sockdriver_copyin(data, 0, &ifcr, sizeof(ifcr))) != OK) 279 return r; 280 281 if (ifcr.ifcr_count < 0) 282 return EINVAL; 283 284 off = offsetof(struct minix_if_clonereq, mifcr_buffer); 285 286 for (num = 0; (ptr = ifdev_enum_vtypes(num)) != NULL; num++) { 287 /* Prevent overflow in case we ever have over 128 vtypes.. */ 288 if (num == MINIX_IF_MAXCLONERS) 289 break; 290 291 if (ifcr.ifcr_buffer == NULL || 292 num >= (unsigned int)ifcr.ifcr_count) 293 continue; 294 295 memset(name, 0, sizeof(name)); 296 strlcpy(name, ptr, sizeof(name)); 297 298 if ((r = sockdriver_copyout(data, off, name, 299 sizeof(name))) != OK) 300 return r; 301 302 off += sizeof(name); 303 } 304 305 ifcr.ifcr_total = num; 306 307 return sockdriver_copyout(data, 0, &ifcr, sizeof(ifcr)); 308 } 309 310 /* 311 * Process an address family independent IOCTL request with an "if_addrprefreq" 312 * structure. 313 */ 314 static int 315 ifconf_ioctl_ifaddrpref(unsigned long request, 316 const struct sockdriver_data * data) 317 { 318 struct ifdev *ifdev; 319 struct if_addrprefreq ifap; 320 int r; 321 322 if ((r = sockdriver_copyin(data, 0, &ifap, sizeof(ifap))) != OK) 323 return r; 324 325 ifap.ifap_name[sizeof(ifap.ifap_name) - 1] = '\0'; 326 327 if ((ifdev = ifdev_find_by_name(ifap.ifap_name)) == NULL) 328 return ENXIO; 329 330 /* 331 * For now, we simply support only a preference of 0. We do not try to 332 * look up the given address, nor do we return the looked up address. 333 */ 334 switch (request) { 335 case SIOCSIFADDRPREF: 336 if (ifap.ifap_preference != 0) 337 return EINVAL; 338 339 return OK; 340 341 case SIOCGIFADDRPREF: 342 ifap.ifap_preference = 0; 343 344 return sockdriver_copyout(data, 0, &ifap, sizeof(ifap)); 345 346 default: 347 return ENOTTY; 348 } 349 } 350 351 /* 352 * Process an IOCTL request for AF_INET with an "ifreq" structure. 353 */ 354 static int 355 ifconf_ioctl_v4_ifreq(unsigned long request, 356 const struct sockdriver_data * data) 357 { 358 struct sockaddr_in addr, mask, bcast, dest, *sin = NULL /*gcc*/; 359 struct ifdev *ifdev; 360 struct ifreq ifr; 361 ifaddr_v4_num_t num; 362 int r, flags; 363 364 if ((r = sockdriver_copyin(data, 0, &ifr, sizeof(ifr))) != OK) 365 return r; 366 367 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0'; 368 369 if ((ifdev = ifdev_find_by_name(ifr.ifr_name)) == NULL) 370 return ENXIO; 371 372 switch (request) { 373 case SIOCGIFADDR: 374 case SIOCGIFNETMASK: 375 case SIOCGIFBRDADDR: 376 case SIOCGIFDSTADDR: 377 /* Retrieve all addresses, then copy out the desired one. */ 378 switch (request) { 379 case SIOCGIFADDR: sin = &addr; break; 380 case SIOCGIFNETMASK: sin = &mask; break; 381 case SIOCGIFBRDADDR: sin = &bcast; break; 382 case SIOCGIFDSTADDR: sin = &dest; break; 383 } 384 385 sin->sin_len = 0; 386 387 if ((r = ifaddr_v4_get(ifdev, (ifaddr_v4_num_t)0, &addr, &mask, 388 &bcast, &dest)) != OK) 389 return r; 390 391 if (sin->sin_len == 0) /* not filled in */ 392 return EADDRNOTAVAIL; 393 394 memcpy(&ifr.ifr_addr, sin, sizeof(*sin)); 395 396 return sockdriver_copyout(data, 0, &ifr, sizeof(ifr)); 397 398 case SIOCGIFAFLAG_IN: 399 if ((r = ifaddr_v4_find(ifdev, 400 (struct sockaddr_in *)&ifr.ifr_addr, &num)) != OK) 401 return r; 402 403 ifr.ifr_addrflags = ifaddr_v4_get_flags(ifdev, num); 404 405 return sockdriver_copyout(data, 0, &ifr, sizeof(ifr)); 406 407 case SIOCSIFADDR: 408 /* 409 * This one is slightly different from the rest, in that we 410 * either set or update the primary address: if we set it, we 411 * must let _add() generate a matching netmask automatically, 412 * while if we update it, _add() would fail unless we first 413 * delete the old entry. 414 */ 415 sin = (struct sockaddr_in *)&ifr.ifr_addr; 416 417 if ((r = ifaddr_v4_get(ifdev, (ifaddr_v4_num_t)0, &addr, &mask, 418 &bcast, &dest)) == OK) { 419 flags = ifaddr_v4_get_flags(ifdev, (ifaddr_v4_num_t)0); 420 421 ifaddr_v4_del(ifdev, (ifaddr_v4_num_t)0); 422 423 /* 424 * If setting the new address fails, reinstating the 425 * old address should always work. This is really ugly 426 * as it generates routing socket noise, but this call 427 * is deprecated anyway. 428 */ 429 if ((r = ifaddr_v4_add(ifdev, sin, &mask, &bcast, 430 &dest, 0 /*flags*/)) != OK) 431 (void)ifaddr_v4_add(ifdev, &addr, &mask, 432 &bcast, &dest, flags); 433 434 return r; 435 } else 436 return ifaddr_v4_add(ifdev, sin, NULL /*mask*/, 437 NULL /*bcast*/, NULL /*dest*/, 0 /*flags*/); 438 439 case SIOCSIFNETMASK: 440 case SIOCSIFBRDADDR: 441 case SIOCSIFDSTADDR: 442 /* These calls only update the existing primary address. */ 443 if ((r = ifaddr_v4_get(ifdev, (ifaddr_v4_num_t)0, &addr, &mask, 444 &bcast, &dest)) != OK) 445 return r; 446 447 sin = (struct sockaddr_in *)&ifr.ifr_addr; 448 449 switch (request) { 450 case SIOCSIFNETMASK: memcpy(&mask, sin, sizeof(mask)); break; 451 case SIOCSIFBRDADDR: memcpy(&bcast, sin, sizeof(bcast)); break; 452 case SIOCSIFDSTADDR: memcpy(&dest, sin, sizeof(dest)); break; 453 } 454 455 return ifaddr_v4_add(ifdev, &addr, &mask, &bcast, &dest, 456 ifaddr_v4_get_flags(ifdev, (ifaddr_v4_num_t)0)); 457 458 case SIOCDIFADDR: 459 if ((r = ifaddr_v4_find(ifdev, 460 (struct sockaddr_in *)&ifr.ifr_addr, &num)) != OK) 461 return r; 462 463 ifaddr_v4_del(ifdev, num); 464 465 return OK; 466 467 default: 468 return ENOTTY; 469 } 470 } 471 472 /* 473 * Process an IOCTL request for AF_INET with an "ifaliasreq" structure. 474 */ 475 static int 476 ifconf_ioctl_v4_ifalias(unsigned long request, 477 const struct sockdriver_data * data) 478 { 479 struct ifdev *ifdev; 480 struct ifaliasreq ifra; 481 struct sockaddr_in dest; 482 ifaddr_v4_num_t num; 483 int r; 484 485 if ((r = sockdriver_copyin(data, 0, &ifra, sizeof(ifra))) != OK) 486 return r; 487 488 ifra.ifra_name[sizeof(ifra.ifra_name) - 1] = '\0'; 489 490 if ((ifdev = ifdev_find_by_name(ifra.ifra_name)) == NULL) 491 return ENXIO; 492 493 switch (request) { 494 case SIOCAIFADDR: 495 return ifaddr_v4_add(ifdev, 496 (struct sockaddr_in *)&ifra.ifra_addr, 497 (struct sockaddr_in *)&ifra.ifra_mask, 498 (struct sockaddr_in *)&ifra.ifra_broadaddr, 499 (struct sockaddr_in *)&ifra.ifra_dstaddr, 0 /*flags*/); 500 501 case SIOCGIFALIAS: 502 if ((r = ifaddr_v4_find(ifdev, 503 (struct sockaddr_in *)&ifra.ifra_addr, &num)) != OK) 504 return r; 505 506 /* 507 * The broadcast and destination address are stored in the same 508 * ifaliasreq field. We cannot pass a pointer to the same 509 * field to ifaddr_v4_get(). So, use a temporary variable. 510 */ 511 (void)ifaddr_v4_get(ifdev, num, 512 (struct sockaddr_in *)&ifra.ifra_addr, 513 (struct sockaddr_in *)&ifra.ifra_mask, 514 (struct sockaddr_in *)&ifra.ifra_broadaddr, &dest); 515 516 if (ifra.ifra_broadaddr.sa_len == 0) 517 memcpy(&ifra.ifra_dstaddr, &dest, sizeof(dest)); 518 519 return sockdriver_copyout(data, 0, &ifra, sizeof(ifra)); 520 521 default: 522 return ENOTTY; 523 } 524 } 525 526 /* 527 * Process an IOCTL request for AF_INET. 528 */ 529 static int 530 ifconf_ioctl_v4(unsigned long request, const struct sockdriver_data * data, 531 endpoint_t user_endpt) 532 { 533 534 switch (request) { 535 case SIOCSIFADDR: 536 case SIOCSIFDSTADDR: 537 case SIOCSIFBRDADDR: 538 case SIOCSIFNETMASK: 539 case SIOCDIFADDR: 540 if (!util_is_root(user_endpt)) 541 return EPERM; 542 543 /* FALLTHROUGH */ 544 case SIOCGIFADDR: 545 case SIOCGIFDSTADDR: 546 case SIOCGIFBRDADDR: 547 case SIOCGIFNETMASK: 548 case SIOCGIFAFLAG_IN: 549 return ifconf_ioctl_v4_ifreq(request, data); 550 551 case SIOCAIFADDR: 552 if (!util_is_root(user_endpt)) 553 return EPERM; 554 555 /* FALLTHROUGH */ 556 case SIOCGIFALIAS: 557 return ifconf_ioctl_v4_ifalias(request, data); 558 559 default: 560 return ENOTTY; 561 } 562 } 563 564 #ifdef INET6 565 /* 566 * Process an IOCTL request for AF_INET6 with an "in6_ifreq" structure. 567 */ 568 static int 569 ifconf_ioctl_v6_ifreq(unsigned long request, 570 const struct sockdriver_data * data) 571 { 572 struct ifdev *ifdev; 573 struct in6_ifreq ifr; 574 ifaddr_v6_num_t num; 575 int r; 576 577 if ((r = sockdriver_copyin(data, 0, &ifr, sizeof(ifr))) != OK) 578 return r; 579 580 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0'; 581 582 if ((ifdev = ifdev_find_by_name(ifr.ifr_name)) == NULL) 583 return ENXIO; 584 585 if ((r = ifaddr_v6_find(ifdev, &ifr.ifr_addr, &num)) != OK) 586 return r; 587 588 switch (request) { 589 case SIOCGIFADDR_IN6: 590 /* This IOCTL basically checks if the given address exists. */ 591 ifaddr_v6_get(ifdev, num, &ifr.ifr_addr, NULL, NULL); 592 593 return sockdriver_copyout(data, 0, &ifr, sizeof(ifr)); 594 595 case SIOCDIFADDR_IN6: 596 ifaddr_v6_del(ifdev, num); 597 598 return OK; 599 600 case SIOCGIFNETMASK_IN6: 601 ifaddr_v6_get(ifdev, num, NULL, &ifr.ifr_addr, NULL); 602 603 return sockdriver_copyout(data, 0, &ifr, sizeof(ifr)); 604 605 case SIOCGIFAFLAG_IN6: 606 ifr.ifr_ifru.ifru_flags6 = ifaddr_v6_get_flags(ifdev, num); 607 608 return sockdriver_copyout(data, 0, &ifr, sizeof(ifr)); 609 610 case SIOCGIFALIFETIME_IN6: 611 ifaddr_v6_get_lifetime(ifdev, num, 612 &ifr.ifr_ifru.ifru_lifetime); 613 614 return sockdriver_copyout(data, 0, &ifr, sizeof(ifr)); 615 616 default: 617 return ENOTTY; 618 } 619 } 620 621 /* 622 * Process an IOCTL request for AF_INET6 with an "in6_aliasreq" structure. 623 */ 624 static int 625 ifconf_ioctl_v6_ifalias(unsigned long request, 626 const struct sockdriver_data * data) 627 { 628 struct ifdev *ifdev; 629 struct in6_aliasreq ifra; 630 int r; 631 632 if ((r = sockdriver_copyin(data, 0, &ifra, sizeof(ifra))) != OK) 633 return r; 634 635 ifra.ifra_name[sizeof(ifra.ifra_name) - 1] = '\0'; 636 637 if ((ifdev = ifdev_find_by_name(ifra.ifra_name)) == NULL) 638 return ENXIO; 639 640 switch (request) { 641 case SIOCAIFADDR_IN6: 642 return ifaddr_v6_add(ifdev, &ifra.ifra_addr, 643 &ifra.ifra_prefixmask, &ifra.ifra_dstaddr, 644 ifra.ifra_flags, &ifra.ifra_lifetime); 645 646 default: 647 return ENOTTY; 648 } 649 } 650 651 /* 652 * Process an IOCTL request for AF_INET6 with an "in6_ndireq" structure. 653 */ 654 static int 655 ifconf_ioctl_v6_ndireq(unsigned long request, 656 const struct sockdriver_data * data) 657 { 658 struct ifdev *ifdev; 659 struct in6_ndireq ndi; 660 int r; 661 662 if ((r = sockdriver_copyin(data, 0, &ndi, sizeof(ndi))) != OK) 663 return r; 664 665 ndi.ifname[sizeof(ndi.ifname) - 1] = '\0'; 666 667 if ((ifdev = ifdev_find_by_name(ndi.ifname)) == NULL) 668 return ENXIO; 669 670 switch (request) { 671 case SIOCGIFINFO_IN6: 672 memset(&ndi.ndi, 0, sizeof(ndi.ndi)); 673 674 ndi.ndi.linkmtu = ifdev_get_mtu(ifdev); 675 ndi.ndi.flags = ifdev_get_nd6flags(ifdev); 676 ndi.ndi.initialized = 1; 677 /* TODO: all the other fields.. */ 678 679 return sockdriver_copyout(data, 0, &ndi, sizeof(ndi)); 680 681 case SIOCSIFINFO_IN6: 682 /* TODO: all the other fields.. */ 683 684 /* FALLTHROUGH */ 685 case SIOCSIFINFO_FLAGS: 686 return ifdev_set_nd6flags(ifdev, ndi.ndi.flags); 687 688 default: 689 return ENOTTY; 690 } 691 } 692 693 /* 694 * Process an IOCTL request for AF_INET6 with an "in6_nbrinfo" structure. 695 */ 696 static int 697 ifconf_ioctl_v6_nbrinfo(unsigned long request, 698 const struct sockdriver_data * data) 699 { 700 struct ifdev *ifdev; 701 struct sockaddr_in6 addr; 702 struct in6_nbrinfo nbri; 703 lldata_ndp_num_t num; 704 int r; 705 706 if ((r = sockdriver_copyin(data, 0, &nbri, sizeof(nbri))) != OK) 707 return r; 708 709 nbri.ifname[sizeof(nbri.ifname) - 1] = '\0'; 710 711 if ((ifdev = ifdev_find_by_name(nbri.ifname)) == NULL) 712 return ENXIO; 713 714 switch (request) { 715 case SIOCGNBRINFO_IN6: 716 /* 717 * Convert the given in6_addr to a full sockaddr_in6, mainly 718 * for internal consistency. It would have been nice if the 719 * KAME management API had had any sort of consistency itself. 720 */ 721 memset(&addr, 0, sizeof(addr)); 722 addr.sin6_family = AF_INET6; 723 memcpy(&addr.sin6_addr.s6_addr, &nbri.addr, 724 sizeof(addr.sin6_addr.s6_addr)); 725 726 if ((r = lldata_ndp_find(ifdev, &addr, &num)) != OK) 727 return r; 728 729 lldata_ndp_get_info(num, &nbri.asked, &nbri.isrouter, 730 &nbri.state, &nbri.expire); 731 732 return sockdriver_copyout(data, 0, &nbri, sizeof(nbri)); 733 734 default: 735 return ENOTTY; 736 } 737 } 738 739 /* 740 * Process an IOCTL request for AF_INET6. 741 */ 742 static int 743 ifconf_ioctl_v6(unsigned long request, const struct sockdriver_data * data, 744 endpoint_t user_endpt) 745 { 746 747 switch (request) { 748 case SIOCDIFADDR_IN6: 749 if (!util_is_root(user_endpt)) 750 return EPERM; 751 752 /* FALLTHROUGH */ 753 case SIOCGIFADDR_IN6: 754 case SIOCGIFNETMASK_IN6: 755 case SIOCGIFAFLAG_IN6: 756 case SIOCGIFALIFETIME_IN6: 757 return ifconf_ioctl_v6_ifreq(request, data); 758 759 case SIOCAIFADDR_IN6: 760 if (!util_is_root(user_endpt)) 761 return EPERM; 762 763 return ifconf_ioctl_v6_ifalias(request, data); 764 765 case SIOCSIFINFO_IN6: 766 case SIOCSIFINFO_FLAGS: 767 if (!util_is_root(user_endpt)) 768 return EPERM; 769 770 /* FALLTHROUGH */ 771 case SIOCGIFINFO_IN6: 772 return ifconf_ioctl_v6_ndireq(request, data); 773 774 case SIOCGNBRINFO_IN6: 775 return ifconf_ioctl_v6_nbrinfo(request, data); 776 777 default: 778 return ENOTTY; 779 } 780 } 781 #endif /* INET6 */ 782 783 /* 784 * Process an IOCTL request for AF_LINK with an "if_laddrreq" structure. 785 */ 786 static int 787 ifconf_ioctl_dl_lifaddr(unsigned long request, 788 const struct sockdriver_data * data) 789 { 790 struct ifdev *ifdev; 791 struct if_laddrreq iflr; 792 ifaddr_dl_num_t num; 793 int r; 794 795 if ((r = sockdriver_copyin(data, 0, &iflr, sizeof(iflr))) != OK) 796 return r; 797 798 iflr.iflr_name[sizeof(iflr.iflr_name) - 1] = '\0'; 799 800 if ((ifdev = ifdev_find_by_name(iflr.iflr_name)) == NULL) 801 return ENXIO; 802 803 switch (request) { 804 case SIOCGLIFADDR: 805 if (iflr.flags & IFLR_PREFIX) { 806 /* We ignore the prefix length, like NetBSD does. */ 807 if ((r = ifaddr_dl_find(ifdev, 808 (struct sockaddr_dlx *)&iflr.addr, 809 sizeof(iflr.addr), &num)) != OK) 810 return r; 811 } else 812 num = (ifaddr_dl_num_t)0; /* this always works */ 813 814 ifaddr_dl_get(ifdev, num, (struct sockaddr_dlx *)&iflr.addr); 815 iflr.flags = ifaddr_dl_get_flags(ifdev, num); 816 memset(&iflr.dstaddr, 0, sizeof(iflr.dstaddr)); 817 818 return sockdriver_copyout(data, 0, &iflr, sizeof(iflr)); 819 820 case SIOCALIFADDR: 821 return ifaddr_dl_add(ifdev, (struct sockaddr_dlx *)&iflr.addr, 822 sizeof(iflr.addr), iflr.flags); 823 824 case SIOCDLIFADDR: 825 if ((r = ifaddr_dl_find(ifdev, 826 (struct sockaddr_dlx *)&iflr.addr, sizeof(iflr.addr), 827 &num)) != OK) 828 return r; 829 830 return ifaddr_dl_del(ifdev, num); 831 832 default: 833 return ENOTTY; 834 } 835 } 836 837 /* 838 * Process an IOCTL request for AF_LINK. 839 */ 840 static int 841 ifconf_ioctl_dl(unsigned long request, const struct sockdriver_data * data, 842 endpoint_t user_endpt) 843 { 844 845 switch (request) { 846 case SIOCALIFADDR: 847 case SIOCDLIFADDR: 848 if (!util_is_root(user_endpt)) 849 return EPERM; 850 851 /* FALLTHROUGH */ 852 case SIOCGLIFADDR: 853 return ifconf_ioctl_dl_lifaddr(request, data); 854 855 default: 856 return ENOTTY; 857 } 858 } 859 860 /* 861 * Process an IOCTL request. This routine is shared between TCP, UDP, RAW, and 862 * link sockets. The given socket may be used to obtain the target domain: 863 * AF_INET, AF_INET6, or AF_LINK. 864 */ 865 int 866 ifconf_ioctl(struct sock * sock, unsigned long request, 867 const struct sockdriver_data * data, endpoint_t user_endpt) 868 { 869 int domain; 870 871 domain = sockevent_get_domain(sock); 872 873 switch (request) { 874 case SIOCSIFFLAGS: 875 case SIOCSIFMETRIC: 876 case SIOCSIFMEDIA: 877 case SIOCSIFMTU: 878 case SIOCIFCREATE: 879 case SIOCIFDESTROY: 880 if (!util_is_root(user_endpt)) 881 return EPERM; 882 883 /* FALLTHROUGH */ 884 case SIOCGIFFLAGS: 885 case SIOCGIFMETRIC: 886 case SIOCGIFMTU: 887 case SIOCGIFDLT: 888 case SIOCGIFINDEX: 889 return ifconf_ioctl_ifreq(request, data); 890 891 case SIOCSIFCAP: 892 if (!util_is_root(user_endpt)) 893 return EPERM; 894 895 /* FALLTHROUGH */ 896 case SIOCGIFCAP: 897 return ifconf_ioctl_ifcap(request, data); 898 899 case MINIX_SIOCGIFMEDIA: 900 return ifconf_ioctl_ifmedia(request, data); 901 902 case MINIX_SIOCIFGCLONERS: 903 return ifconf_ioctl_ifclone(request, data); 904 905 case SIOCSIFADDRPREF: 906 if (!util_is_root(user_endpt)) 907 return EPERM; 908 909 /* FALLTHROUGH */ 910 case SIOCGIFADDRPREF: 911 return ifconf_ioctl_ifaddrpref(request, data); 912 913 default: 914 switch (domain) { 915 case AF_INET: 916 return ifconf_ioctl_v4(request, data, user_endpt); 917 918 #ifdef INET6 919 case AF_INET6: 920 return ifconf_ioctl_v6(request, data, user_endpt); 921 #endif /* INET6 */ 922 923 case AF_LINK: 924 return ifconf_ioctl_dl(request, data, user_endpt); 925 926 default: 927 return ENOTTY; 928 } 929 } 930 } 931