1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/param.h> 29 #include <sys/types.h> 30 #include <sys/user.h> 31 #include <sys/vfs.h> 32 #include <sys/vnode.h> 33 #include <sys/file.h> 34 #include <sys/stream.h> 35 #include <sys/stropts.h> 36 #include <sys/strsubr.h> 37 #include <sys/dlpi.h> 38 #include <sys/vnode.h> 39 #include <sys/socket.h> 40 #include <sys/sockio.h> 41 #include <net/if.h> 42 43 #include <sys/cred.h> 44 #include <sys/sysmacros.h> 45 46 #include <sys/sad.h> 47 #include <sys/kstr.h> 48 #include <sys/bootconf.h> 49 #include <sys/bootprops.h> 50 51 #include <sys/errno.h> 52 #include <sys/modctl.h> 53 #include <sys/sunddi.h> 54 #include <sys/sunldi.h> 55 #include <sys/esunddi.h> 56 #include <sys/promif.h> 57 58 #include <netinet/in.h> 59 #include <netinet/ip6.h> 60 #include <netinet/icmp6.h> 61 #include <netinet/sctp.h> 62 #include <inet/common.h> 63 #include <inet/ip.h> 64 #include <inet/ip6.h> 65 #include <inet/tcp.h> 66 #include <inet/sctp_ip.h> 67 68 #include <sys/strlog.h> 69 #include <sys/log.h> 70 #include <sys/ethernet.h> 71 #include <sys/ddi_implfuncs.h> 72 73 #include <sys/dld.h> 74 #include <sys/mac.h> 75 76 /* 77 * Debug Macros 78 */ 79 int strplumbdebug = 0; 80 81 #define DBG0(_f) \ 82 if (strplumbdebug != 0) \ 83 printf("strplumb: " _f) 84 85 #define DBG1(_f, _a) \ 86 if (strplumbdebug != 0) \ 87 printf("strplumb: " _f, (_a)) 88 89 #define DBG2(_f, _a, _b) \ 90 if (strplumbdebug != 0) \ 91 printf("strplumb: " _f, (_a), (_b)) 92 93 #define DBG3(_f, _a, _b, _c) \ 94 if (strplumbdebug != 0) \ 95 printf("strplumb: " _f, (_a), (_b), (_c)) 96 97 /* 98 * Module linkage information for the kernel. 99 */ 100 #define STRPLUMB_IDENT "STREAMS Plumbing Module" 101 102 static struct modlmisc modlmisc = { 103 &mod_miscops, 104 STRPLUMB_IDENT 105 }; 106 107 static struct modlinkage modlinkage = { 108 MODREV_1, 109 &modlmisc, 110 NULL 111 }; 112 113 int 114 _init(void) 115 { 116 return (mod_install(&modlinkage)); 117 } 118 119 int 120 _fini(void) 121 { 122 return (mod_remove(&modlinkage)); 123 } 124 125 int 126 _info(struct modinfo *modinfop) 127 { 128 return (mod_info(&modlinkage, modinfop)); 129 } 130 131 #define ARP "arp" 132 #define TCP "tcp" 133 #define TCP6 "tcp6" 134 #define UDP "udp" 135 #define UDP6 "udp6" 136 #define SCTP "sctp" 137 #define SCTP6 "sctp6" 138 #define ICMP "icmp" 139 #define ICMP6 "icmp6" 140 #define IP "ip" 141 #define IP6 "ip6" 142 #define TIMOD "timod" 143 144 #define UDPDEV "/devices/pseudo/udp@0:udp" 145 #define TCP6DEV "/devices/pseudo/tcp6@0:tcp6" 146 #define SCTP6DEV "/devices/pseudo/sctp6@0:sctp6" 147 #define IP6DEV "/devices/pseudo/ip6@0:ip6" 148 149 typedef struct strplumb_modspec { 150 char *sm_type; 151 char *sm_name; 152 } strplumb_modspec_t; 153 154 static strplumb_modspec_t strplumb_modlist[] = { 155 { "drv", DLD_DRIVER_NAME }, 156 { "drv", IP }, 157 { "drv", IP6 }, 158 { "drv", TCP }, 159 { "drv", TCP6 }, 160 { "drv", UDP }, 161 { "drv", UDP6 }, 162 { "drv", SCTP }, 163 { "drv", SCTP6 }, 164 { "drv", ICMP }, 165 { "drv", ICMP6 }, 166 { "drv", ARP }, 167 { "strmod", TIMOD } 168 }; 169 170 /* 171 * Called from swapgeneric.c:loadrootmodules() in the network boot case. 172 */ 173 int 174 strplumb_load(void) 175 { 176 uint_t i; 177 strplumb_modspec_t *p; 178 179 DBG0("loading modules\n"); 180 181 for (i = 0, p = strplumb_modlist; 182 i < sizeof (strplumb_modlist) / sizeof (strplumb_modlist[0]); 183 i++, p++) { 184 if (modloadonly(p->sm_type, p->sm_name) < 0) { 185 printf("strplumb: failed to load %s/%s\n", 186 p->sm_type, p->sm_name); 187 return (EFAULT); 188 } 189 } 190 191 return (0); 192 } 193 194 static int 195 strplumb_init(void) 196 { 197 uint_t i; 198 strplumb_modspec_t *p; 199 int err; 200 201 DBG0("initializing modules\n"); 202 203 for (i = 0, p = strplumb_modlist; 204 i < sizeof (strplumb_modlist) / sizeof (strplumb_modlist[0]); 205 i++, p++) { 206 if (strcmp(p->sm_type, "drv") == 0) 207 err = (i_ddi_attach_pseudo_node(p->sm_name) != NULL) ? 208 0 : EFAULT; 209 else 210 err = (modload(p->sm_type, p->sm_name) < 0) ? 211 EFAULT : 0; 212 213 if (err != 0) { 214 printf("strplumb: failed to initialize %s/%s\n", 215 p->sm_type, p->sm_name); 216 return (err); 217 } 218 } 219 220 return (0); 221 } 222 223 static int 224 strplumb_autopush(void) 225 { 226 major_t maj; 227 minor_t min; 228 char *mods[5]; 229 uint_t anchor = 1; 230 int err; 231 232 min = (minor_t)-1; 233 mods[1] = NULL; 234 235 /* 236 * ARP 237 */ 238 DBG0("setting up arp autopush\n"); 239 240 mods[0] = ARP; 241 242 maj = ddi_name_to_major(ARP); 243 if ((err = kstr_autopush(SET_AUTOPUSH, &maj, &min, NULL, &anchor, 244 mods)) != 0) { 245 printf("strplumb: kstr_autopush(SET/ARP) failed: %d\n", err); 246 return (err); 247 } 248 249 return (0); 250 } 251 252 static int 253 strplumb_sctpq(ldi_ident_t li) 254 { 255 ldi_handle_t lh = NULL; 256 int err; 257 int rval; 258 259 DBG0("configuring SCTP default queue\n"); 260 261 if ((err = ldi_open_by_name(SCTP6DEV, FREAD|FWRITE, CRED(), &lh, 262 li)) != 0) { 263 printf("strplumb: open of SCTP6DEV failed: %d\n", err); 264 return (err); 265 } 266 267 if ((err = ldi_ioctl(lh, SCTP_IOC_DEFAULT_Q, (intptr_t)0, FKIOCTL, 268 CRED(), &rval)) != 0) { 269 printf("strplumb: failed to set SCTP default queue: %d\n", 270 err); 271 (void) ldi_close(lh, FREAD|FWRITE, CRED()); 272 return (err); 273 } 274 275 return (0); 276 } 277 278 static int 279 strplumb_tcpq(ldi_ident_t li) 280 { 281 ldi_handle_t lh = NULL; 282 ldi_handle_t ip_lh = NULL; 283 int err; 284 int rval; 285 286 DBG0("configuring TCP default queue\n"); 287 288 /* 289 * We open IP6DEV here because we need to have it open to in 290 * order to open TCP6DEV successfully. 291 */ 292 if ((err = ldi_open_by_name(IP6DEV, FREAD|FWRITE, CRED(), &ip_lh, 293 li)) != 0) { 294 printf("strplumb: open of IP6DEV failed: %d\n", err); 295 return (err); 296 } 297 298 /* 299 * We set the tcp default queue to IPv6 because IPv4 falls back to 300 * IPv6 when it can't find a client, but IPv6 does not fall back to 301 * IPv4. 302 */ 303 if ((err = ldi_open_by_name(TCP6DEV, FREAD|FWRITE, CRED(), &lh, 304 li)) != 0) { 305 printf("strplumb: open of TCP6DEV failed: %d\n", err); 306 goto done; 307 } 308 309 if ((err = ldi_ioctl(lh, TCP_IOC_DEFAULT_Q, (intptr_t)0, FKIOCTL, 310 CRED(), &rval)) != 0) { 311 printf("strplumb: failed to set TCP default queue: %d\n", 312 err); 313 goto done; 314 } 315 316 done: 317 (void) ldi_close(ip_lh, FREAD|FWRITE, CRED()); 318 return (err); 319 } 320 321 /* 322 * Can be set in /etc/system in the case of local booting. See comment below. 323 */ 324 char *ndev_name = 0; 325 int ndev_unit = 0; 326 327 /* 328 * If we booted diskless then strplumb() will have been called from 329 * swapgeneric.c:rootconf(). All we can do in that case is plumb the 330 * network device that we booted from. 331 * 332 * If we booted from a local disk, we will have been called from main(), 333 * and normally we defer the plumbing of interfaces until network/physical. 334 * This can be overridden by setting "ndev_name" in /etc/system. 335 */ 336 static int 337 resolve_boot_path(void) 338 { 339 char *devpath; 340 dev_info_t *dip; 341 const char *driver; 342 int instance; 343 #ifdef _OBP 344 char stripped_path[OBP_MAXPATHLEN]; 345 #endif 346 347 if (strncmp(rootfs.bo_fstype, "nfs", 3) == 0) 348 devpath = rootfs.bo_name; 349 else 350 devpath = strplumb_get_netdev_path(); 351 352 if (devpath != NULL) { 353 DBG1("resolving boot-path: %s\n", devpath); 354 #ifdef _OBP 355 /* 356 * OBP passes options e.g, "net:dhcp" 357 * remove them here 358 */ 359 prom_strip_options(devpath, stripped_path); 360 devpath = stripped_path; 361 #endif 362 /* 363 * Hold the devi since this is the root device. 364 */ 365 if ((dip = e_ddi_hold_devi_by_path(devpath, 0)) == NULL) { 366 printf("strplumb: unable to hold root device: %s\n", 367 devpath); 368 return (ENXIO); 369 } 370 371 driver = ddi_driver_name(dip); 372 instance = ddi_get_instance(dip); 373 } else { 374 if (ndev_name == NULL) 375 return (ENODEV); 376 377 DBG2("using ndev_name (%s) ndev_unit (%d)\n", ndev_name, 378 ndev_unit); 379 380 if (i_ddi_attach_hw_nodes(ndev_name) != DDI_SUCCESS) { 381 printf("strplumb: cannot load ndev_name '%s'\n", 382 ndev_name); 383 return (ENXIO); 384 } 385 386 driver = ndev_name; 387 instance = ndev_unit; 388 } 389 390 (void) snprintf(rootfs.bo_devname, BO_MAXOBJNAME, 391 "/devices/pseudo/clone@0:%s", driver); 392 (void) snprintf(rootfs.bo_ifname, BO_MAXOBJNAME, "%s%d", 393 driver, instance); 394 rootfs.bo_ppa = instance; 395 return (0); 396 } 397 398 static int 399 getifflags(ldi_handle_t lh, struct lifreq *lifrp) 400 { 401 struct strioctl iocb; 402 int rval; 403 404 iocb.ic_cmd = SIOCGLIFFLAGS; 405 iocb.ic_timout = 15; 406 iocb.ic_len = sizeof (struct lifreq); 407 iocb.ic_dp = (char *)lifrp; 408 409 return (ldi_ioctl(lh, I_STR, (intptr_t)&iocb, FKIOCTL, CRED(), &rval)); 410 411 } 412 413 static int 414 setifname(ldi_handle_t lh, struct lifreq *lifrp) 415 { 416 struct strioctl iocb; 417 int rval; 418 419 iocb.ic_cmd = SIOCSLIFNAME; 420 iocb.ic_timout = 15; 421 iocb.ic_len = sizeof (struct lifreq); 422 iocb.ic_dp = (char *)lifrp; 423 424 return (ldi_ioctl(lh, I_STR, (intptr_t)&iocb, FKIOCTL, CRED(), &rval)); 425 } 426 427 static int 428 strplumb_dev(ldi_ident_t li) 429 { 430 ldi_handle_t lh = NULL; 431 ldi_handle_t mux_lh = NULL; 432 int err; 433 struct lifreq lifr; 434 struct ifreq ifr; 435 int rval; 436 437 bzero(&lifr, sizeof (struct lifreq)); 438 bzero(&ifr, sizeof (ifr)); 439 440 /* 441 * Now set up the links. Ultimately, we should have two streams 442 * permanently linked underneath UDP (which is actually IP with UDP 443 * autopushed). One stream consists of the ARP-[ifname] combination, 444 * while the other consists of ARP-IP-[ifname]. The second combination 445 * seems a little weird, but is linked underneath UDP just to keep it 446 * around. 447 * 448 * We pin underneath UDP here to match what is done in ifconfig(1m); 449 * otherwise, ifconfig will be unable to unplumb the stream (the major 450 * number and mux id must both match for a successful I_PUNLINK). 451 * 452 * There are subtleties in the plumbing which make it essential to 453 * follow the logic used in ifconfig(1m) very closely. 454 */ 455 456 /* 457 * Plumb UDP-ARP-IP-<dev> 458 */ 459 460 if ((err = ldi_open_by_name(rootfs.bo_devname, FREAD|FWRITE, CRED(), 461 &lh, li)) != 0) { 462 printf("strplumb: open %s failed: %d\n", rootfs.bo_devname, 463 err); 464 goto done; 465 } 466 467 468 if ((err = ldi_ioctl(lh, I_PUSH, (intptr_t)IP, FKIOCTL, CRED(), 469 &rval)) != 0) { 470 printf("strplumb: push IP failed: %d\n", err); 471 goto done; 472 } 473 474 if ((err = getifflags(lh, &lifr)) != 0) 475 goto done; 476 477 lifr.lifr_flags |= IFF_IPV4; 478 lifr.lifr_flags &= ~IFF_IPV6; 479 480 if ((err = ldi_ioctl(lh, I_PUSH, (intptr_t)ARP, FKIOCTL, CRED(), 481 &rval)) != 0) { 482 printf("strplumb: push ARP failed: %d\n", err); 483 goto done; 484 } 485 486 (void) strlcpy(lifr.lifr_name, rootfs.bo_ifname, 487 sizeof (lifr.lifr_name)); 488 lifr.lifr_ppa = rootfs.bo_ppa; 489 490 if ((err = setifname(lh, &lifr)) != 0) 491 goto done; 492 493 /* Get the flags and check if ARP is needed */ 494 if ((err = getifflags(lh, &lifr)) != 0) { 495 printf("strplumb: getifflags %s IP failed, error %d\n", 496 lifr.lifr_name, err); 497 goto done; 498 } 499 500 /* Pop out ARP if not needed */ 501 if (lifr.lifr_flags & IFF_NOARP) { 502 err = ldi_ioctl(lh, I_POP, (intptr_t)0, FKIOCTL, CRED(), 503 &rval); 504 if (err != 0) { 505 printf("strplumb: pop ARP failed, error %d\n", err); 506 goto done; 507 } 508 } 509 510 if ((err = ldi_open_by_name(UDPDEV, FREAD|FWRITE, CRED(), &mux_lh, 511 li)) != 0) { 512 printf("strplumb: open of UDPDEV failed: %d\n", err); 513 goto done; 514 } 515 516 if ((err = ldi_ioctl(mux_lh, I_PLINK, (intptr_t)lh, 517 FREAD|FWRITE|FNOCTTY|FKIOCTL, CRED(), 518 &(ifr.ifr_ip_muxid))) != 0) { 519 printf("strplumb: plink UDP-ARP-IP-%s failed: %d\n", 520 rootfs.bo_ifname, err); 521 goto done; 522 } 523 524 DBG2("UDP-ARP-IP-%s muxid: %d\n", rootfs.bo_ifname, ifr.ifr_ip_muxid); 525 526 (void) ldi_close(lh, FREAD|FWRITE, CRED()); 527 lh = NULL; 528 529 /* 530 * Plumb UDP-ARP-<dev> 531 */ 532 533 if ((err = ldi_open_by_name(rootfs.bo_devname, FREAD|FWRITE, CRED(), 534 &lh, li)) != 0) { 535 printf("strplumb: open %s failed: %d\n", rootfs.bo_devname, 536 err); 537 goto done; 538 } 539 540 if ((err = ldi_ioctl(lh, I_PUSH, (intptr_t)ARP, FKIOCTL, CRED(), 541 &rval)) != 0) { 542 printf("strplumb: push ARP failed: %d\n", err); 543 goto done; 544 } 545 546 if ((err = setifname(lh, &lifr)) != 0) 547 goto done; 548 549 if ((err = ldi_ioctl(mux_lh, I_PLINK, (intptr_t)lh, 550 FREAD|FWRITE|FNOCTTY|FKIOCTL, CRED(), 551 &(ifr.ifr_arp_muxid))) != 0) { 552 printf("strplumb: plink UDP-ARP-%s failed: %d\n", 553 rootfs.bo_ifname, err); 554 goto done; 555 } 556 557 DBG2("UDP-ARP-%s muxid: %d\n", rootfs.bo_ifname, ifr.ifr_arp_muxid); 558 559 /* 560 * Cache the mux ids. 561 */ 562 (void) strlcpy(ifr.ifr_name, rootfs.bo_ifname, sizeof (ifr.ifr_name)); 563 564 if ((err = ldi_ioctl(mux_lh, SIOCSIFMUXID, (intptr_t)&ifr, FKIOCTL, 565 CRED(), &rval)) != 0) { 566 printf("strplumb: SIOCSIFMUXID failed: %d\n", err); 567 goto done; 568 } 569 570 done: 571 if (lh != NULL) 572 (void) ldi_close(lh, FREAD|FWRITE, CRED()); 573 574 if (mux_lh != NULL) 575 (void) ldi_close(mux_lh, FREAD|FWRITE, CRED()); 576 577 return (err); 578 } 579 580 /* 581 * Do streams plumbing for internet protocols. 582 */ 583 int 584 strplumb(void) 585 { 586 ldi_ident_t li; 587 int err; 588 589 if ((err = strplumb_init()) != 0) 590 return (err); 591 592 if ((err = strplumb_autopush()) != 0) 593 return (err); 594 595 if ((err = ldi_ident_from_mod(&modlinkage, &li)) != 0) 596 return (err); 597 598 /* 599 * Setup the TCP and SCTP default queues for the global stack. 600 * tcp/sctp_stack_init will do this for additional stack instances. 601 */ 602 if ((err = strplumb_sctpq(li)) != 0) 603 goto done; 604 605 if ((err = strplumb_tcpq(li)) != 0) 606 goto done; 607 608 if ((err = resolve_boot_path()) != 0) 609 goto done; 610 611 DBG1("rootfs.bo_devname: %s\n", rootfs.bo_devname); 612 DBG1("rootfs.bo_ifname: %s\n", rootfs.bo_ifname); 613 DBG1("rootfs.bo_ppa: %d\n", rootfs.bo_ppa); 614 615 if ((err = strplumb_dev(li)) != 0) 616 goto done; 617 618 done: 619 ldi_ident_release(li); 620 621 return (err); 622 } 623 624 /* multiboot: diskless boot interface discovery */ 625 626 #ifndef _OBP 627 628 static uchar_t boot_macaddr[16]; 629 static int boot_maclen; 630 static uchar_t *getmacaddr(dev_info_t *dip, size_t *maclenp); 631 static int matchmac(dev_info_t *dip, void *arg); 632 633 #endif /* !_OBP */ 634 635 char * 636 strplumb_get_netdev_path(void) 637 { 638 #ifdef _OBP 639 char fstype[OBP_MAXPROPNAME]; 640 641 if (bop_getprop("fstype", fstype) == -1) 642 return (NULL); 643 644 if (strncmp(fstype, "nfs", 3) == 0) 645 return (prom_bootpath()); 646 else 647 return (NULL); 648 #else 649 650 char *macstr, *devpath = NULL; 651 uchar_t *bootp; 652 uint_t bootp_len; 653 654 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 655 DDI_PROP_DONTPASS, BP_BOOT_MAC, &macstr) == DDI_SUCCESS) { 656 /* 657 * hard coded ether mac len for booting floppy on 658 * machines with old cards 659 */ 660 boot_maclen = ether_aton(macstr, boot_macaddr); 661 if (boot_maclen != 6) { 662 cmn_err(CE_WARN, 663 "malformed boot_mac property, %d bytes", 664 boot_maclen); 665 } 666 ddi_prop_free(macstr); 667 } else if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(), 668 DDI_PROP_DONTPASS, BP_BOOTP_RESPONSE, &bootp, &bootp_len) 669 == DDI_SUCCESS) { 670 671 /* 672 * These offsets are defined by dhcp standard 673 * Should use structure offsets 674 */ 675 boot_maclen = *(bootp + 2); 676 ASSERT(boot_maclen <= 16); 677 bcopy(bootp + 28, boot_macaddr, boot_maclen); 678 679 dhcack = kmem_alloc(bootp_len, KM_SLEEP); 680 bcopy(bootp, dhcack, bootp_len); 681 dhcacklen = bootp_len; 682 683 ddi_prop_free(bootp); 684 } else 685 return (NULL); 686 687 ddi_walk_devs(ddi_root_node(), matchmac, (void *)&devpath); 688 return (devpath); 689 690 #endif /* _OBP */ 691 } 692 693 #ifndef _OBP 694 695 /* 696 * Get boot path from the boot_mac address 697 */ 698 /*ARGSUSED*/ 699 static int 700 matchmac(dev_info_t *dip, void *arg) 701 { 702 char **devpathp = (char **)arg; 703 char *model_str; 704 uchar_t *macaddr; 705 size_t maclen; 706 707 /* XXX Should use "device-type" per IEEE 1275 */ 708 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0, 709 "model", &model_str) != DDI_SUCCESS) 710 return (DDI_WALK_CONTINUE); 711 712 if (strcmp(model_str, "Ethernet controller") != 0) { 713 ddi_prop_free(model_str); 714 return (DDI_WALK_CONTINUE); 715 } 716 ddi_prop_free(model_str); 717 718 /* We have a network device now */ 719 if (i_ddi_attach_node_hierarchy(dip) != DDI_SUCCESS) { 720 return (DDI_WALK_CONTINUE); 721 } 722 723 ASSERT(boot_maclen != 0); 724 macaddr = getmacaddr(dip, &maclen); 725 if (macaddr == NULL) 726 return (DDI_WALK_CONTINUE); 727 728 if (maclen != boot_maclen || 729 bcmp(macaddr, boot_macaddr, maclen) != 0) { 730 kmem_free(macaddr, maclen); 731 return (DDI_WALK_CONTINUE); 732 } 733 734 /* found hardware with the mac address */ 735 (void) localetheraddr((struct ether_addr *)macaddr, NULL); 736 kmem_free(macaddr, maclen); 737 738 *devpathp = kmem_alloc(MAXPATHLEN, KM_SLEEP); 739 (void) ddi_pathname(dip, *devpathp); 740 741 /* fill in dhcifname */ 742 if (dhcack) { 743 (void) snprintf(dhcifname, IFNAMSIZ, "%s%d", 744 ddi_driver_name(dip), i_ddi_devi_get_ppa(dip)); 745 } 746 return (DDI_WALK_TERMINATE); 747 } 748 749 static uchar_t * 750 getmacaddr(dev_info_t *dip, size_t *maclenp) 751 { 752 int rc, ppa; 753 ldi_ident_t li; 754 ldi_handle_t lh; 755 const char *drv_name = ddi_driver_name(dip); 756 char *clonepath; 757 uchar_t *macaddr = NULL; 758 759 if (rc = ldi_ident_from_mod(&modlinkage, &li)) { 760 cmn_err(CE_WARN, 761 "getmacaddr: ldi_ident_from_mod failed: %d\n", rc); 762 return (NULL); 763 } 764 765 clonepath = kmem_alloc(MAXPATHLEN, KM_SLEEP); 766 (void) snprintf(clonepath, MAXPATHLEN, 767 "/devices/pseudo/clone@0:%s", drv_name); 768 769 rc = ldi_open_by_name(clonepath, FREAD|FWRITE, CRED(), &lh, li); 770 ldi_ident_release(li); 771 if (rc) { 772 cmn_err(CE_WARN, 773 "getmacaddr: ldi_open_by_name(%s) failed: %d\n", 774 clonepath, rc); 775 kmem_free(clonepath, MAXPATHLEN); 776 return (NULL); 777 } 778 kmem_free(clonepath, MAXPATHLEN); 779 780 ppa = i_ddi_devi_get_ppa(dip); 781 if ((dl_attach(lh, ppa, NULL) != 0) || 782 (dl_bind(lh, ETHERTYPE_IP, NULL) != 0)) { 783 (void) ldi_close(lh, FREAD|FWRITE, CRED()); 784 cmn_err(CE_WARN, 785 "getmacaddr: dl_attach/bind(%s%d) failed: %d\n", 786 drv_name, ppa, rc); 787 return (NULL); 788 } 789 790 *maclenp = ETHERADDRL; 791 macaddr = kmem_alloc(ETHERADDRL, KM_SLEEP); 792 if (dl_phys_addr(lh, macaddr, maclenp, NULL) != 0 || 793 *maclenp != ETHERADDRL) { 794 kmem_free(macaddr, ETHERADDRL); 795 macaddr = NULL; 796 *maclenp = 0; 797 cmn_err(CE_WARN, 798 "getmacaddr: dl_phys_addr(%s%d) failed: %d\n", 799 drv_name, ppa, rc); 800 } 801 (void) ldi_close(lh, FREAD|FWRITE, CRED()); 802 return (macaddr); 803 } 804 #endif /* !_OBP */ 805