2 * Copyright (c) 2001-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
31 * 14 December, 2001 Dieter Siegmund (dieter@apple.com)
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
38 #include <sys/ioctl.h>
39 #include <sys/proc_internal.h>
40 #include <sys/mount_internal.h>
42 #include <sys/filedesc.h>
43 #include <sys/vnode_internal.h>
44 #include <sys/malloc.h>
45 #include <sys/socket.h>
46 #include <sys/socketvar.h>
47 #include <sys/reboot.h>
49 #include <net/if_dl.h>
50 #include <net/if_types.h>
51 #include <net/route.h>
52 #include <netinet/in.h>
53 #include <netinet/if_ether.h>
54 #include <netinet/dhcp_options.h>
55 #include <pexpert/pexpert.h>
57 #include <kern/kern_types.h>
58 #include <kern/kalloc.h>
60 //#include <libkern/libkern.h>
61 extern struct filedesc filedesc0
;
63 extern int strncmp(const char *,const char *, size_t);
64 extern unsigned long strtoul(const char *, char **, int);
65 extern char * strchr(const char *str
, int ch
);
67 extern int nfs_mountroot(void); /* nfs_vfsops.c */
68 extern int (*mountroot
)(void);
70 extern unsigned char rootdevice
[];
72 static int S_netboot
= 0;
73 static struct netboot_info
* S_netboot_info_p
;
76 IOBSDRegistryEntryForDeviceTree(const char * path
);
79 IOBSDRegistryEntryRelease(void * entry
);
82 IOBSDRegistryEntryGetData(void * entry
, const char * property_name
,
85 extern int vndevice_root_image(const char * path
, char devname
[],
87 extern int di_root_image(const char *path
, char devname
[], dev_t
*dev_p
);
89 #define BOOTP_RESPONSE "bootp-response"
90 #define BSDP_RESPONSE "bsdp-response"
91 #define DHCP_RESPONSE "dhcp-response"
94 bootp(struct ifnet
* ifp
, struct in_addr
* iaddr_p
, int max_retry
,
95 struct in_addr
* netmask_p
, struct in_addr
* router_p
,
99 /* forward declarations */
100 int inet_aton(char * cp
, struct in_addr
* pin
);
102 boolean_t
netboot_iaddr(struct in_addr
* iaddr_p
);
103 boolean_t
netboot_rootpath(struct in_addr
* server_ip
,
104 char * name
, int name_len
,
105 char * path
, int path_len
);
106 int netboot_setup(struct proc
* p
);
107 int netboot_mountroot(void);
108 int netboot_root(void);
112 #define IP_FORMAT "%d.%d.%d.%d"
113 #define IP_CH(ip) ((u_char *)ip)
114 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
116 #define kNetBootRootPathPrefixNFS "nfs:"
117 #define kNetBootRootPathPrefixHTTP "http:"
120 kNetBootImageTypeUnknown
= 0,
121 kNetBootImageTypeNFS
= 1,
122 kNetBootImageTypeHTTP
= 2,
125 struct netboot_info
{
126 struct in_addr client_ip
;
127 struct in_addr server_ip
;
129 int server_name_length
;
131 int mount_point_length
;
133 int image_path_length
;
134 NetBootImageType image_type
;
139 inet_aton(char * cp
, struct in_addr
* pin
)
141 u_char
* b
= (char *)pin
;
145 for (p
= cp
, i
= 0; i
< 4; i
++) {
146 u_long l
= strtoul(p
, 0, 0);
151 if (i
< 3 && p
== NULL
)
159 * Function: parse_booter_path
161 * Parse a string of the form:
162 * "<IP>:<host>:<mount>[:<image_path>]"
163 * into the given ip address, host, mount point, and optionally, image_path.
166 * The passed in string is modified i.e. ':' is replaced by '\0'.
168 * "17.202.16.17:seaport:/release/.images/Image9/CurrentHera"
170 static __inline__ boolean_t
171 parse_booter_path(char * path
, struct in_addr
* iaddr_p
, char * * host
,
172 char * * mount_dir
, char * * image_path
)
179 colon
= strchr(start
, ':');
184 if (inet_aton(start
, iaddr_p
) != 1) {
190 colon
= strchr(start
, ':');
199 colon
= strchr(start
, ':');
214 * Function: find_colon
216 * Find the next unescaped instance of the colon character.
217 * If a colon is escaped (preceded by a backslash '\' character),
218 * shift the string over by one character to overwrite the backslash.
220 static __inline__
char *
221 find_colon(char * str
)
226 while ((colon
= strchr(start
, ':')) != NULL
) {
230 if (colon
== start
) {
233 if (colon
[-1] != '\\')
235 for (dst
= colon
- 1, src
= colon
; *dst
!= '\0'; dst
++, src
++) {
244 * Function: parse_netboot_path
246 * Parse a string of the form:
247 * "nfs:<IP>:<mount>[:<image_path>]"
248 * into the given ip address, host, mount point, and optionally, image_path.
250 * - the passed in string is modified i.e. ':' is replaced by '\0'
251 * - literal colons must be escaped with a backslash
254 * nfs:17.202.42.112:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg
255 * nfs:17.202.42.112:/Volumes/Foo\:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg
257 static __inline__ boolean_t
258 parse_netboot_path(char * path
, struct in_addr
* iaddr_p
, char * * host
,
259 char * * mount_dir
, char * * image_path
)
261 static char tmp
[MAX_IPv4_STR_LEN
]; /* Danger - not thread safe */
265 if (strncmp(path
, kNetBootRootPathPrefixNFS
,
266 strlen(kNetBootRootPathPrefixNFS
)) != 0) {
271 start
= path
+ strlen(kNetBootRootPathPrefixNFS
);
272 colon
= strchr(start
, ':');
277 if (inet_aton(start
, iaddr_p
) != 1) {
283 colon
= find_colon(start
);
292 (void)find_colon(start
);
295 *host
= inet_ntop(AF_INET
, iaddr_p
, tmp
, sizeof(tmp
));
300 parse_image_path(char * path
, struct in_addr
* iaddr_p
, char * * host
,
301 char * * mount_dir
, char * * image_path
)
303 if (path
[0] >= '0' && path
[0] <= '9') {
304 return (parse_booter_path(path
, iaddr_p
, host
, mount_dir
,
307 return (parse_netboot_path(path
, iaddr_p
, host
, mount_dir
,
312 get_root_path(char * root_path
)
315 boolean_t found
= FALSE
;
319 entry
= IOBSDRegistryEntryForDeviceTree("/chosen");
323 pkt
= IOBSDRegistryEntryGetData(entry
, BSDP_RESPONSE
, &pkt_len
);
324 if (pkt
!= NULL
&& pkt_len
>= (int)sizeof(struct dhcp
)) {
325 printf("netboot: retrieving root path from BSDP response\n");
328 pkt
= IOBSDRegistryEntryGetData(entry
, BOOTP_RESPONSE
,
330 if (pkt
!= NULL
&& pkt_len
>= (int)sizeof(struct dhcp
)) {
331 printf("netboot: retrieving root path from BOOTP response\n");
340 reply
= (struct dhcp
*)pkt
;
341 (void)dhcpol_parse_packet(&options
, reply
, pkt_len
, NULL
);
343 path
= (char *)dhcpol_find(&options
,
344 dhcptag_root_path_e
, &len
, NULL
);
346 bcopy(path
, root_path
, len
);
347 root_path
[len
] = '\0';
351 IOBSDRegistryEntryRelease(entry
);
356 static struct netboot_info
*
357 netboot_info_init(struct in_addr iaddr
)
359 struct netboot_info
* info
;
360 char * root_path
= NULL
;
361 boolean_t use_hdix
= TRUE
;
362 char * vndevice
= NULL
;
364 MALLOC_ZONE(vndevice
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
365 if (vndevice
== NULL
)
366 panic("netboot_info_init: M_NAMEI zone exhausted");
367 if (PE_parse_boot_arg("vndevice", vndevice
) == TRUE
) {
370 FREE_ZONE(vndevice
, MAXPATHLEN
, M_NAMEI
);
372 info
= (struct netboot_info
*)kalloc(sizeof(*info
));
373 bzero(info
, sizeof(*info
));
374 info
->client_ip
= iaddr
;
375 info
->image_type
= kNetBootImageTypeUnknown
;
376 info
->use_hdix
= use_hdix
;
378 /* check for a booter-specified path then a NetBoot path */
379 MALLOC_ZONE(root_path
, caddr_t
, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
380 if (root_path
== NULL
)
381 panic("netboot_info_init: M_NAMEI zone exhausted");
382 if (PE_parse_boot_arg("rp", root_path
) == TRUE
383 || PE_parse_boot_arg("rootpath", root_path
) == TRUE
384 || get_root_path(root_path
) == TRUE
) {
385 char * server_name
= NULL
;
386 char * mount_point
= NULL
;
387 char * image_path
= NULL
;
388 struct in_addr server_ip
;
390 if (parse_image_path(root_path
, &server_ip
, &server_name
,
391 &mount_point
, &image_path
)) {
392 info
->image_type
= kNetBootImageTypeNFS
;
393 info
->server_ip
= server_ip
;
394 info
->server_name_length
= strlen(server_name
) + 1;
395 info
->server_name
= (char *)kalloc(info
->server_name_length
);
396 info
->mount_point_length
= strlen(mount_point
) + 1;
397 info
->mount_point
= (char *)kalloc(info
->mount_point_length
);
398 strcpy(info
->server_name
, server_name
);
399 strcpy(info
->mount_point
, mount_point
);
401 printf("Server %s Mount %s",
402 server_name
, info
->mount_point
);
403 if (image_path
!= NULL
) {
404 boolean_t needs_slash
= FALSE
;
406 info
->image_path_length
= strlen(image_path
) + 1;
407 if (image_path
[0] != '/') {
409 info
->image_path_length
++;
411 info
->image_path
= (char *)kalloc(info
->image_path_length
);
413 info
->image_path
[0] = '/';
414 strcpy(info
->image_path
+ 1, image_path
);
417 strcpy(info
->image_path
, image_path
);
419 printf(" Image %s", info
->image_path
);
423 else if (strncmp(root_path
, kNetBootRootPathPrefixHTTP
,
424 strlen(kNetBootRootPathPrefixHTTP
)) == 0) {
425 /* only HDIX supports HTTP */
426 info
->image_type
= kNetBootImageTypeHTTP
;
427 info
->use_hdix
= TRUE
;
428 info
->image_path_length
= strlen(root_path
) + 1;
429 info
->image_path
= (char *)kalloc(info
->image_path_length
);
430 strcpy(info
->image_path
, root_path
);
433 printf("netboot: root path uses unrecognized format\n");
436 FREE_ZONE(root_path
, MAXPATHLEN
, M_NAMEI
);
441 netboot_info_free(struct netboot_info
* * info_p
)
443 struct netboot_info
* info
= *info_p
;
446 if (info
->mount_point
) {
447 kfree(info
->mount_point
, info
->mount_point_length
);
449 if (info
->server_name
) {
450 kfree(info
->server_name
, info
->server_name_length
);
452 if (info
->image_path
) {
453 kfree(info
->image_path
, info
->image_path_length
);
455 kfree(info
, sizeof(*info
));
462 netboot_iaddr(struct in_addr
* iaddr_p
)
464 if (S_netboot_info_p
== NULL
)
467 *iaddr_p
= S_netboot_info_p
->client_ip
;
472 netboot_rootpath(struct in_addr
* server_ip
,
473 char * name
, int name_len
,
474 char * path
, int path_len
)
476 if (S_netboot_info_p
== NULL
)
482 if (S_netboot_info_p
->mount_point_length
== 0) {
485 if (path_len
< S_netboot_info_p
->mount_point_length
) {
486 printf("netboot: path too small %d < %d\n",
487 path_len
, S_netboot_info_p
->mount_point_length
);
490 strcpy(path
, S_netboot_info_p
->mount_point
);
491 strncpy(name
, S_netboot_info_p
->server_name
, name_len
);
492 *server_ip
= S_netboot_info_p
->server_ip
;
498 get_ip_parameters(struct in_addr
* iaddr_p
, struct in_addr
* netmask_p
,
499 struct in_addr
* router_p
)
506 entry
= IOBSDRegistryEntryForDeviceTree("/chosen");
510 pkt
= IOBSDRegistryEntryGetData(entry
, DHCP_RESPONSE
, &pkt_len
);
511 if (pkt
!= NULL
&& pkt_len
>= (int)sizeof(struct dhcp
)) {
512 printf("netboot: retrieving IP information from DHCP response\n");
515 pkt
= IOBSDRegistryEntryGetData(entry
, BOOTP_RESPONSE
, &pkt_len
);
516 if (pkt
!= NULL
&& pkt_len
>= (int)sizeof(struct dhcp
)) {
517 printf("netboot: retrieving IP information from BOOTP response\n");
526 reply
= (struct dhcp
*)pkt
;
527 (void)dhcpol_parse_packet(&options
, reply
, pkt_len
, NULL
);
528 *iaddr_p
= reply
->dp_yiaddr
;
529 ip
= (struct in_addr
*)
530 dhcpol_find(&options
,
531 dhcptag_subnet_mask_e
, &len
, NULL
);
535 ip
= (struct in_addr
*)
536 dhcpol_find(&options
, dhcptag_router_e
, &len
, NULL
);
541 IOBSDRegistryEntryRelease(entry
);
542 return (pkt
!= NULL
);
546 inet_aifaddr(struct socket
* so
, char * name
, const struct in_addr
* addr
,
547 const struct in_addr
* mask
,
548 const struct in_addr
* broadcast
)
550 struct sockaddr blank_sin
;
551 struct ifaliasreq ifra
;
553 bzero(&blank_sin
, sizeof(blank_sin
));
554 blank_sin
.sa_len
= sizeof(blank_sin
);
555 blank_sin
.sa_family
= AF_INET
;
557 bzero(&ifra
, sizeof(ifra
));
558 strncpy(ifra
.ifra_name
, name
, sizeof(ifra
.ifra_name
));
560 ifra
.ifra_addr
= blank_sin
;
561 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_addr
= *addr
;
564 ifra
.ifra_mask
= blank_sin
;
565 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_addr
= *mask
;
568 ifra
.ifra_broadaddr
= blank_sin
;
569 ((struct sockaddr_in
*)&ifra
.ifra_broadaddr
)->sin_addr
= *broadcast
;
571 return (ifioctl(so
, SIOCAIFADDR
, (caddr_t
)&ifra
, current_proc()));
575 route_cmd(int cmd
, struct in_addr d
, struct in_addr g
,
576 struct in_addr m
, u_long more_flags
)
578 struct sockaddr_in dst
;
579 u_long flags
= RTF_UP
| RTF_STATIC
;
580 struct sockaddr_in gw
;
581 struct sockaddr_in mask
;
586 bzero((caddr_t
)&dst
, sizeof(dst
));
587 dst
.sin_len
= sizeof(dst
);
588 dst
.sin_family
= AF_INET
;
592 bzero((caddr_t
)&gw
, sizeof(gw
));
593 gw
.sin_len
= sizeof(gw
);
594 gw
.sin_family
= AF_INET
;
598 bzero(&mask
, sizeof(mask
));
599 mask
.sin_len
= sizeof(mask
);
600 mask
.sin_family
= AF_INET
;
603 return (rtrequest(cmd
, (struct sockaddr
*)&dst
, (struct sockaddr
*)&gw
,
604 (struct sockaddr
*)&mask
, flags
, NULL
));
608 default_route_add(struct in_addr router
, boolean_t proxy_arp
)
611 struct in_addr zeroes
= { 0 };
613 if (proxy_arp
== FALSE
) {
614 flags
|= RTF_GATEWAY
;
616 return (route_cmd(RTM_ADD
, zeroes
, router
, zeroes
, flags
));
620 host_route_delete(struct in_addr host
)
622 struct in_addr zeroes
= { 0 };
624 return (route_cmd(RTM_DELETE
, host
, zeroes
, zeroes
, RTF_HOST
));
627 static struct ifnet
*
630 struct ifnet
* ifp
= NULL
;
633 ifp
= ifunit(rootdevice
);
636 ifnet_head_lock_shared();
637 TAILQ_FOREACH(ifp
, &ifnet_head
, if_link
)
638 if ((ifp
->if_flags
& (IFF_LOOPBACK
|IFF_POINTOPOINT
)) == 0)
646 netboot_mountroot(void)
649 struct in_addr iaddr
= { 0 };
652 struct in_addr netmask
= { 0 };
653 struct proc
* procp
= current_proc();
654 struct in_addr router
= { 0 };
655 struct socket
* so
= NULL
;
658 bzero(&ifr
, sizeof(ifr
));
661 /* find the interface */
662 ifp
= find_interface();
664 printf("netboot: no suitable interface\n");
668 sprintf(ifr
.ifr_name
, "%s%d", ifp
->if_name
, ifp
->if_unit
);
669 printf("netboot: using network interface '%s'\n", ifr
.ifr_name
);
672 if ((error
= socreate(AF_INET
, &so
, SOCK_DGRAM
, 0)) != 0) {
673 printf("netboot: socreate, error=%d\n", error
);
676 ifr
.ifr_flags
= ifp
->if_flags
| IFF_UP
;
677 error
= ifioctl(so
, SIOCSIFFLAGS
, (caddr_t
)&ifr
, procp
);
679 printf("netboot: SIFFLAGS, error=%d\n", error
);
683 /* grab information from the registry */
684 if (get_ip_parameters(&iaddr
, &netmask
, &router
) == FALSE
) {
685 /* use BOOTP to retrieve IP address, netmask and router */
686 error
= bootp(ifp
, &iaddr
, 32, &netmask
, &router
, procp
);
688 printf("netboot: BOOTP failed %d\n", error
);
692 printf("netboot: IP address " IP_FORMAT
, IP_LIST(&iaddr
));
693 if (netmask
.s_addr
) {
694 printf(" netmask " IP_FORMAT
, IP_LIST(&netmask
));
697 printf(" router " IP_FORMAT
, IP_LIST(&router
));
700 error
= inet_aifaddr(so
, ifr
.ifr_name
, &iaddr
, &netmask
, NULL
);
702 printf("netboot: inet_aifaddr failed, %d\n", error
);
705 if (router
.s_addr
== 0) {
706 /* enable proxy arp if we don't have a router */
707 router
.s_addr
= iaddr
.s_addr
;
709 printf("netboot: adding default route " IP_FORMAT
"\n",
711 error
= default_route_add(router
, router
.s_addr
== iaddr
.s_addr
);
713 printf("netboot: default_route_add failed %d\n", error
);
718 S_netboot_info_p
= netboot_info_init(iaddr
);
719 switch (S_netboot_info_p
->image_type
) {
721 case kNetBootImageTypeNFS
:
722 for (try = 1; TRUE
; try++) {
723 error
= nfs_mountroot();
727 printf("netboot: nfs_mountroot() attempt %u failed; "
728 "clearing ARP entry and trying again\n", try);
730 * error is either EHOSTDOWN or EHOSTUNREACH, which likely means
731 * that the port we're plugged into has spanning tree enabled,
732 * and either the router or the server can't answer our ARP
733 * requests. Clear the incomplete ARP entry by removing the
734 * appropriate route, depending on the error code:
735 * EHOSTDOWN NFS server's route
736 * EHOSTUNREACH router's route
742 /* remove the server's arp entry */
743 error
= host_route_delete(S_netboot_info_p
->server_ip
);
745 printf("netboot: host_route_delete(" IP_FORMAT
747 IP_LIST(&S_netboot_info_p
->server_ip
), error
);
751 error
= host_route_delete(router
);
753 printf("netboot: host_route_delete(" IP_FORMAT
754 ") failed %d\n", IP_LIST(&router
), error
);
760 case kNetBootImageTypeHTTP
:
761 error
= netboot_setup(procp
);
779 netboot_setup(struct proc
* p
)
784 if (S_netboot_info_p
== NULL
785 || S_netboot_info_p
->image_path
== NULL
) {
788 if (S_netboot_info_p
->use_hdix
) {
789 printf("netboot_setup: calling di_root_image\n");
790 error
= di_root_image(S_netboot_info_p
->image_path
,
793 printf("netboot_setup: di_root_image: failed %d\n", error
);
798 printf("netboot_setup: calling vndevice_root_image\n");
799 error
= vndevice_root_image(S_netboot_info_p
->image_path
,
802 printf("netboot_setup: vndevice_root_image: failed %d\n", error
);
808 printf("netboot: root device 0x%x\n", rootdev
);
809 error
= vfs_mountroot();
810 if (error
== 0 && rootvnode
!= NULL
) {
813 struct vfs_context context
;
816 context
.vc_ucred
= proc_ucred(p
); /* XXX kauth_cred_get() ??? proxy */
818 /* Get the vnode for '/'. Set fdp->fd_fd.fd_cdir to reference it. */
819 if (VFS_ROOT(mountlist
.tqh_last
, &newdp
, &context
))
820 panic("netboot_setup: cannot find root vnode");
825 filedesc0
.fd_cdir
= newdp
;
828 TAILQ_REMOVE(&mountlist
, TAILQ_FIRST(&mountlist
), mnt_list
);
830 mountlist
.tqh_first
->mnt_flag
|= MNT_ROOTFS
;
833 netboot_info_free(&S_netboot_info_p
);