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.
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.
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]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/param.h>
27 #include <sys/types.h>
30 #include <sys/vnode.h>
32 #include <sys/stream.h>
33 #include <sys/stropts.h>
34 #include <sys/strsubr.h>
36 #include <sys/vnode.h>
37 #include <sys/socket.h>
38 #include <sys/sockio.h>
42 #include <sys/sysmacros.h>
46 #include <sys/bootconf.h>
47 #include <sys/bootprops.h>
49 #include <sys/errno.h>
50 #include <sys/modctl.h>
51 #include <sys/sunddi.h>
52 #include <sys/sunldi.h>
53 #include <sys/esunddi.h>
54 #include <sys/promif.h>
56 #include <sys/strlog.h>
58 #include <sys/ethernet.h>
59 #include <sys/ddi_implfuncs.h>
62 #include <sys/mac_client.h>
67 int strplumbdebug
= 0;
69 extern ib_boot_prop_t
*iscsiboot_prop
;
72 if (strplumbdebug != 0) \
73 printf("strplumb: " _f)
75 #define DBG1(_f, _a) \
76 if (strplumbdebug != 0) \
77 printf("strplumb: " _f, (_a))
79 #define DBG2(_f, _a, _b) \
80 if (strplumbdebug != 0) \
81 printf("strplumb: " _f, (_a), (_b))
83 #define DBG3(_f, _a, _b, _c) \
84 if (strplumbdebug != 0) \
85 printf("strplumb: " _f, (_a), (_b), (_c))
88 * Module linkage information for the kernel.
90 #define STRPLUMB_IDENT "STREAMS Plumbing Module"
92 static struct modlmisc modlmisc
= {
97 static struct modlinkage modlinkage
= {
106 return (mod_install(&modlinkage
));
112 return (mod_remove(&modlinkage
));
116 _info(struct modinfo
*modinfop
)
118 return (mod_info(&modlinkage
, modinfop
));
127 #define ICMP6 "icmp6"
130 #define TIMOD "timod"
132 #define UDPDEV "/devices/pseudo/udp@0:udp"
133 #define TCP6DEV "/devices/pseudo/tcp6@0:tcp6"
134 #define UDP6DEV "/devices/pseudo/udp6@0:udp6"
135 #define IP6DEV "/devices/pseudo/ip6@0:ip6"
137 typedef struct strplumb_modspec
{
140 } strplumb_modspec_t
;
142 static strplumb_modspec_t strplumb_modlist
[] = {
143 { "drv", DLD_DRIVER_NAME
},
157 * Called from swapgeneric.c:loadrootmodules() in the network boot case.
163 strplumb_modspec_t
*p
;
165 DBG0("loading modules\n");
167 for (i
= 0, p
= strplumb_modlist
;
168 i
< sizeof (strplumb_modlist
) / sizeof (strplumb_modlist
[0]);
170 if (modloadonly(p
->sm_type
, p
->sm_name
) < 0) {
171 printf("strplumb: failed to load %s/%s\n",
172 p
->sm_type
, p
->sm_name
);
184 strplumb_modspec_t
*p
;
187 DBG0("initializing modules\n");
189 for (i
= 0, p
= strplumb_modlist
;
190 i
< sizeof (strplumb_modlist
) / sizeof (strplumb_modlist
[0]);
192 if (strcmp(p
->sm_type
, "drv") == 0)
193 err
= (i_ddi_attach_pseudo_node(p
->sm_name
) != NULL
) ?
196 err
= (modload(p
->sm_type
, p
->sm_name
) < 0) ?
200 printf("strplumb: failed to initialize %s/%s\n",
201 p
->sm_type
, p
->sm_name
);
210 * Can be set in /etc/system in the case of local booting. See comment below.
216 * If we booted diskless then strplumb() will have been called from
218 * in case of x86 NEWBOOT: vfs.c:rootconf()
219 * in case of nfs root, the rootfs.bo_name is reset from /ramdisk:a
220 * to empty string and we will copy netdev_path there.
222 * in case of sparc: swapgeneric.c:rootconf().
223 * All we can do in that case is plumb the network device that we booted from.
225 * If we booted from a local disk, we will have been called from main(),
226 * and normally we defer the plumbing of interfaces until network/physical.
227 * This can be overridden by setting "ndev_name" in /etc/system.
230 resolve_boot_path(void)
237 char stripped_path
[OBP_MAXPATHLEN
];
240 if (strncmp(rootfs
.bo_fstype
, "nfs", 3) == 0 &&
241 rootfs
.bo_name
[0] != '\0') {
242 devpath
= rootfs
.bo_name
;
244 devpath
= strplumb_get_netdev_path();
245 netdev_path
= devpath
;
246 if (netdev_path
!= NULL
) {
247 (void) strncpy(rootfs
.bo_name
, netdev_path
,
252 if (devpath
!= NULL
) {
253 DBG1("resolving boot-path: %s\n", devpath
);
256 * OBP passes options e.g, "net:dhcp"
259 prom_strip_options(devpath
, stripped_path
);
260 devpath
= stripped_path
;
263 * Hold the devi since this is the root device.
265 if ((dip
= e_ddi_hold_devi_by_path(devpath
, 0)) == NULL
) {
266 printf("strplumb: unable to hold root device: %s\n",
271 driver
= ddi_driver_name(dip
);
272 instance
= ddi_get_instance(dip
);
274 if (ndev_name
== NULL
)
277 DBG2("using ndev_name (%s) ndev_unit (%d)\n", ndev_name
,
280 if (i_ddi_attach_hw_nodes(ndev_name
) != DDI_SUCCESS
) {
281 printf("strplumb: cannot load ndev_name '%s'\n",
287 instance
= ndev_unit
;
290 (void) snprintf(rootfs
.bo_devname
, BO_MAXOBJNAME
,
291 "/devices/pseudo/clone@0:%s", driver
);
292 (void) snprintf(rootfs
.bo_ifname
, BO_MAXOBJNAME
, "%s%d",
294 rootfs
.bo_ppa
= instance
;
299 getifflags(ldi_handle_t lh
, struct lifreq
*lifrp
)
301 struct strioctl iocb
;
304 iocb
.ic_cmd
= SIOCGLIFFLAGS
;
306 iocb
.ic_len
= sizeof (struct lifreq
);
307 iocb
.ic_dp
= (char *)lifrp
;
309 return (ldi_ioctl(lh
, I_STR
, (intptr_t)&iocb
, FKIOCTL
, CRED(), &rval
));
314 setifname(ldi_handle_t lh
, struct lifreq
*lifrp
)
316 struct strioctl iocb
;
319 iocb
.ic_cmd
= SIOCSLIFNAME
;
321 iocb
.ic_len
= sizeof (struct lifreq
);
322 iocb
.ic_dp
= (char *)lifrp
;
324 return (ldi_ioctl(lh
, I_STR
, (intptr_t)&iocb
, FKIOCTL
, CRED(), &rval
));
328 strplumb_dev(ldi_ident_t li
)
330 ldi_handle_t lh
= NULL
;
331 ldi_handle_t mux_lh
= NULL
;
339 bzero(&lifr
, sizeof (struct lifreq
));
340 bzero(&ifr
, sizeof (ifr
));
342 if (iscsiboot_prop
!= NULL
) {
343 af
= iscsiboot_prop
->boot_nic
.sin_family
;
347 * Now set up the links. Ultimately, we should have two streams
348 * permanently linked under UDP. One stream consists of the
349 * ARP-[ifname] combination, while the other consists of IP-[ifname].
351 * We pin underneath UDP here to match what is done in ifconfig(1m);
352 * otherwise, ifconfig will be unable to unplumb the stream (the major
353 * number and mux id must both match for a successful I_PUNLINK).
355 * There are subtleties in the plumbing which make it essential to
356 * follow the logic used in ifconfig(1m) very closely.
363 if ((err
= ldi_open_by_name(rootfs
.bo_devname
, FREAD
|FWRITE
, CRED(),
365 printf("strplumb: open %s failed: %d\n", rootfs
.bo_devname
,
371 if ((err
= ldi_ioctl(lh
, I_PUSH
, (intptr_t)IP
, FKIOCTL
, CRED(),
373 printf("strplumb: push IP failed: %d\n", err
);
377 if ((err
= getifflags(lh
, &lifr
)) != 0)
380 if (af
== 0 || af
== AF_INET
) {
381 lifr
.lifr_flags
|= IFF_IPV4
;
382 lifr
.lifr_flags
&= ~IFF_IPV6
;
386 * iscsi boot is used with ipv6 enabled
388 lifr
.lifr_flags
|= IFF_IPV6
;
389 lifr
.lifr_flags
&= ~IFF_IPV4
;
392 (void) strlcpy(lifr
.lifr_name
, rootfs
.bo_ifname
,
393 sizeof (lifr
.lifr_name
));
394 lifr
.lifr_ppa
= rootfs
.bo_ppa
;
396 if ((err
= setifname(lh
, &lifr
)) != 0)
399 /* get the flags and check if ARP is needed */
400 if ((err
= getifflags(lh
, &lifr
)) != 0) {
401 printf("strplumb: getifflags %s IP failed, error %d\n",
402 lifr
.lifr_name
, err
);
405 if ((err
= ldi_open_by_name(name
, FREAD
|FWRITE
, CRED(), &mux_lh
,
407 printf("strplumb: open of %s failed: %d\n", name
, err
);
410 if ((err
= ldi_ioctl(mux_lh
, I_PLINK
, (intptr_t)lh
,
411 FREAD
|FWRITE
|FNOCTTY
|FKIOCTL
, CRED(),
412 &(ifr
.ifr_ip_muxid
))) != 0) {
413 printf("strplumb: plink UDP-ARP-IP-%s failed: %d\n",
414 rootfs
.bo_ifname
, err
);
418 /* if ARP is not needed, we are done */
419 if (lifr
.lifr_flags
& (IFF_NOARP
| IFF_IPV6
))
422 DBG2("UDP-ARP-IP-%s muxid: %d\n", rootfs
.bo_ifname
, ifr
.ifr_ip_muxid
);
424 (void) ldi_close(lh
, FREAD
|FWRITE
, CRED());
428 * Plumb UDP-ARP-<dev>
431 if ((err
= ldi_open_by_name(rootfs
.bo_devname
, FREAD
|FWRITE
, CRED(),
433 printf("strplumb: open %s failed: %d\n", rootfs
.bo_devname
,
438 if ((err
= ldi_ioctl(lh
, I_PUSH
, (intptr_t)ARP
, FKIOCTL
, CRED(),
440 printf("strplumb: push ARP failed: %d\n", err
);
444 if ((err
= setifname(lh
, &lifr
)) != 0)
447 if ((err
= ldi_ioctl(mux_lh
, I_PLINK
, (intptr_t)lh
,
448 FREAD
|FWRITE
|FNOCTTY
|FKIOCTL
, CRED(),
449 &(ifr
.ifr_arp_muxid
))) != 0) {
450 printf("strplumb: plink UDP-ARP-%s failed: %d\n",
451 rootfs
.bo_ifname
, err
);
455 DBG2("UDP-ARP-%s muxid: %d\n", rootfs
.bo_ifname
, ifr
.ifr_arp_muxid
);
460 (void) strlcpy(ifr
.ifr_name
, rootfs
.bo_ifname
, sizeof (ifr
.ifr_name
));
462 if ((err
= ldi_ioctl(mux_lh
, SIOCSIFMUXID
, (intptr_t)&ifr
, FKIOCTL
,
463 CRED(), &rval
)) != 0) {
464 printf("strplumb: SIOCSIFMUXID failed: %d\n", err
);
470 (void) ldi_close(lh
, FREAD
|FWRITE
, CRED());
473 (void) ldi_close(mux_lh
, FREAD
|FWRITE
, CRED());
479 * Do streams plumbing for internet protocols.
487 if ((err
= strplumb_init()) != 0)
490 if ((err
= ldi_ident_from_mod(&modlinkage
, &li
)) != 0)
493 if ((err
= resolve_boot_path()) != 0)
496 DBG1("rootfs.bo_devname: %s\n", rootfs
.bo_devname
);
497 DBG1("rootfs.bo_ifname: %s\n", rootfs
.bo_ifname
);
498 DBG1("rootfs.bo_ppa: %d\n", rootfs
.bo_ppa
);
500 if ((err
= strplumb_dev(li
)) != 0)
504 ldi_ident_release(li
);
509 /* multiboot: diskless boot interface discovery */
513 static uchar_t boot_macaddr
[16];
514 static int boot_maclen
;
515 static uchar_t
*getmacaddr(dev_info_t
*dip
, size_t *maclenp
);
516 static int matchmac(dev_info_t
*dip
, void *arg
);
521 strplumb_get_netdev_path(void)
524 char fstype
[OBP_MAXPROPNAME
];
525 static char iscsi_network_path
[BO_MAXOBJNAME
] = {0};
529 if (bop_getprop("fstype", fstype
) == -1)
532 if (strncmp(fstype
, "nfs", 3) == 0)
533 return (prom_bootpath());
534 else if (iscsiboot_prop
!= NULL
) {
535 proplen
= BOP_GETPROPLEN(bootops
,
536 BP_ISCSI_NETWORK_BOOTPATH
);
538 if (BOP_GETPROP(bootops
,
539 BP_ISCSI_NETWORK_BOOTPATH
,
540 iscsi_network_path
) > 0) {
541 p
= strchr(iscsi_network_path
, ':');
545 return (iscsi_network_path
);
552 char *macstr
, *devpath
= NULL
;
556 if (ddi_prop_lookup_string(DDI_DEV_T_ANY
, ddi_root_node(),
557 DDI_PROP_DONTPASS
, BP_BOOT_MAC
, &macstr
) == DDI_SUCCESS
) {
559 * hard coded ether mac len for booting floppy on
560 * machines with old cards
562 boot_maclen
= ether_aton(macstr
, boot_macaddr
);
563 if (boot_maclen
!= 6) {
565 "malformed boot_mac property, %d bytes",
568 ddi_prop_free(macstr
);
569 } else if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY
, ddi_root_node(),
570 DDI_PROP_DONTPASS
, BP_BOOTP_RESPONSE
, &bootp
, &bootp_len
)
574 * These offsets are defined by dhcp standard
575 * Should use structure offsets
577 boot_maclen
= *(bootp
+ 2);
578 ASSERT(boot_maclen
<= 16);
579 bcopy(bootp
+ 28, boot_macaddr
, boot_maclen
);
581 dhcack
= kmem_alloc(bootp_len
, KM_SLEEP
);
582 bcopy(bootp
, dhcack
, bootp_len
);
583 dhcacklen
= bootp_len
;
585 ddi_prop_free(bootp
);
586 } else if (iscsiboot_prop
!= NULL
) {
587 bcopy(iscsiboot_prop
->boot_nic
.nic_mac
,
588 boot_macaddr
, IB_BOOT_MACLEN
);
589 boot_maclen
= IB_BOOT_MACLEN
;
594 ddi_walk_devs(ddi_root_node(), matchmac
, (void *)&devpath
);
603 * Get boot path from the boot_mac address
607 matchmac(dev_info_t
*dip
, void *arg
)
609 char **devpathp
= (char **)arg
;
614 /* XXX Should use "device-type" per IEEE 1275 */
615 if (ddi_prop_lookup_string(DDI_DEV_T_ANY
, dip
, 0,
616 "model", &model_str
) != DDI_SUCCESS
)
617 return (DDI_WALK_CONTINUE
);
619 if (strcmp(model_str
, "Ethernet controller") != 0) {
620 ddi_prop_free(model_str
);
621 return (DDI_WALK_CONTINUE
);
623 ddi_prop_free(model_str
);
625 /* We have a network device now */
626 if (i_ddi_attach_node_hierarchy(dip
) != DDI_SUCCESS
) {
627 return (DDI_WALK_CONTINUE
);
630 ASSERT(boot_maclen
!= 0);
631 macaddr
= getmacaddr(dip
, &maclen
);
633 return (DDI_WALK_CONTINUE
);
635 if (maclen
!= boot_maclen
||
636 bcmp(macaddr
, boot_macaddr
, maclen
) != 0) {
637 kmem_free(macaddr
, maclen
);
638 return (DDI_WALK_CONTINUE
);
641 /* found hardware with the mac address */
642 (void) localetheraddr((struct ether_addr
*)macaddr
, NULL
);
643 kmem_free(macaddr
, maclen
);
645 *devpathp
= kmem_alloc(MAXPATHLEN
, KM_SLEEP
);
646 (void) ddi_pathname(dip
, *devpathp
);
648 /* fill in dhcifname */
650 (void) snprintf(dhcifname
, IFNAMSIZ
, "%s%d",
651 ddi_driver_name(dip
), i_ddi_devi_get_ppa(dip
));
653 return (DDI_WALK_TERMINATE
);
657 getmacaddr(dev_info_t
*dip
, size_t *maclenp
)
662 const char *drv_name
= ddi_driver_name(dip
);
664 uchar_t
*macaddr
= NULL
;
666 if (rc
= ldi_ident_from_mod(&modlinkage
, &li
)) {
668 "getmacaddr: ldi_ident_from_mod failed: %d\n", rc
);
672 clonepath
= kmem_alloc(MAXPATHLEN
, KM_SLEEP
);
673 (void) snprintf(clonepath
, MAXPATHLEN
,
674 "/devices/pseudo/clone@0:%s", drv_name
);
676 rc
= ldi_open_by_name(clonepath
, FREAD
|FWRITE
, CRED(), &lh
, li
);
677 ldi_ident_release(li
);
680 "getmacaddr: ldi_open_by_name(%s) failed: %d\n",
682 kmem_free(clonepath
, MAXPATHLEN
);
685 kmem_free(clonepath
, MAXPATHLEN
);
687 ppa
= i_ddi_devi_get_ppa(dip
);
688 if ((dl_attach(lh
, ppa
, NULL
) != 0) ||
689 (dl_bind(lh
, ETHERTYPE_IP
, NULL
) != 0)) {
690 (void) ldi_close(lh
, FREAD
|FWRITE
, CRED());
692 "getmacaddr: dl_attach/bind(%s%d) failed: %d\n",
697 *maclenp
= ETHERADDRL
;
698 macaddr
= kmem_alloc(ETHERADDRL
, KM_SLEEP
);
699 if (dl_phys_addr(lh
, macaddr
, maclenp
, NULL
) != 0 ||
700 *maclenp
!= ETHERADDRL
) {
701 kmem_free(macaddr
, ETHERADDRL
);
705 "getmacaddr: dl_phys_addr(%s%d) failed: %d\n",
708 (void) ldi_close(lh
, FREAD
|FWRITE
, CRED());