]>
git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_boot.c
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
22 /* Copyright (c) 1995, 1997 NeXT Computer, Inc. All Rights Reserved */
24 * Copyright (c) 1994 Adam Glass, Gordon Ross
25 * All rights reserved.
27 * This software was developed by the Computer Systems Engineering group
28 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
29 * contributed to Berkeley.
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 3. All advertising materials mentioning features or use of this software
40 * must display the following acknowledgement:
41 * This product includes software developed by the University of
42 * California, Lawrence Berkeley Laboratory and its contributors.
43 * 4. Neither the name of the University nor the names of its contributors
44 * may be used to endorse or promote products derived from this software
45 * without specific prior written permission.
47 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * 14-March-97 Dieter Siegmund (dieter@next.com)
61 * - Use BOOTP instead of RARP to get the IP address at boot time
63 * 23-May-97 Umesh Vaishampayan (umeshv@apple.com)
64 * - Added the ability to mount "/private" separately.
66 * 30-May-97 Dieter Siegmund (dieter@next.com)
67 * - Clear out the ireq structure before using it to prevent
68 * our sending using a bogus source IP address, we should use
69 * an IP address of all zeroes
70 * - Right after BOOTP, get the correct netmask using AUTONETMASK
71 * 18-Jul-97 Dieter Siegmund (dieter@apple.com)
72 * - we can't restrict the netmask until we have a default route,
73 * removed AUTONETMASK call (ifdef'd out)
74 * 5-Aug-97 Dieter Siegmund (dieter@apple.com)
75 * - use the default route from the bpwhoami call, enabled autonetmask
77 * 19-Feb-1999 Dieter Siegmund (dieter@apple.com)
78 * - use new BOOTP routine to get the subnet mask and router
79 * and stop using SIOCAUTOADDR
80 * - don't bother mounting private separately if it's not
81 * specified or not required because they are substrings of
82 * one another ie. root=host:/A and private=host:/A/private
83 * - allow the root path to be specified in the boot variable
84 * "rp" (AKA "rootpath")
85 * 19-Jul-1999 Dieter Siegmund (dieter@apple.com)
86 * - replaced big automatic arrays with MALLOC'd data
89 #include <sys/param.h>
90 #include <sys/systm.h>
91 #include <sys/kernel.h>
93 #include <sys/ioctl.h>
95 #include <sys/mount.h>
98 #include <sys/malloc.h>
99 #include <sys/socket.h>
100 #include <sys/reboot.h>
103 #include <net/if_dl.h>
104 #include <net/if_types.h>
105 #include <net/route.h>
107 #include <netinet/in.h>
108 #include <netinet/if_ether.h>
110 #include <nfs/rpcv2.h>
111 #include <nfs/nfsproto.h>
113 #include <nfs/nfsdiskless.h>
114 #include <nfs/krpc.h>
116 #include <pexpert/pexpert.h>
120 #include <libkern/libkern.h>
122 extern char *strchr(const char *str
, int ch
);
126 int nfs_boot_init(nd
, procp
)
127 struct nfs_diskless
*nd
;
130 panic("nfs_boot_init: no ether");
133 int nfs_boot_getfh(nd
, procp
, v3
)
134 struct nfs_diskless
*nd
;
138 panic("nfs_boot_getfh: no ether");
144 * Support for NFS diskless booting, specifically getting information
145 * about where to boot from, what pathnames, etc.
147 * This implememtation uses RARP and the bootparam RPC.
148 * We are forced to implement RPC anyway (to get file handles)
149 * so we might as well take advantage of it for bootparam too.
151 * The diskless boot sequence goes as follows:
152 * (1) Use RARP to get our interface address
153 * (2) Use RPC/bootparam/whoami to get our hostname,
154 * our IP address, and the server's IP address.
155 * (3) Use RPC/bootparam/getfile to get the root path
156 * (4) Use RPC/mountd to get the root file handle
157 * (5) Use RPC/bootparam/getfile to get the swap path
158 * (6) Use RPC/mountd to get the swap file handle
160 * (This happens to be the way Sun does it too.)
164 static int bp_whoami
__P((struct sockaddr_in
*bpsin
,
165 struct in_addr
*my_ip
, struct in_addr
*gw_ip
));
166 static int bp_getfile
__P((struct sockaddr_in
*bpsin
, char *key
,
167 struct sockaddr_in
*mdsin
, char *servname
, char *path
));
170 static int md_mount
__P((struct sockaddr_in
*mdsin
, char *path
, int v3
,
171 u_char
*fhp
, u_long
*fhlenp
));
174 static int get_file_handle
__P((struct nfs_dlmount
*ndmntp
));
177 #define IP_FORMAT "%d.%d.%d.%d"
178 #define IP_CH(ip) ((u_char *)ip)
179 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
182 netboot_iaddr(struct in_addr
* iaddr_p
);
185 netboot_rootpath(struct in_addr
* server_ip
,
186 char * name
, int name_len
,
187 char * path
, int path_len
);
190 * Called with an empty nfs_diskless struct to be filled in.
193 nfs_boot_init(nd
, procp
)
194 struct nfs_diskless
*nd
;
197 struct sockaddr_in bp_sin
;
198 boolean_t do_bpwhoami
= TRUE
;
199 boolean_t do_bpgetfile
= TRUE
;
201 struct in_addr my_ip
;
202 struct sockaddr_in
* sin_p
;
204 /* by this point, networking must already have been configured */
205 if (netboot_iaddr(&my_ip
) == FALSE
) {
206 printf("nfs_boot: networking is not initialized\n");
208 goto failed_noswitch
;
211 /* get the root path information */
212 MALLOC_ZONE(nd
->nd_root
.ndm_path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
213 sin_p
= &nd
->nd_root
.ndm_saddr
;
214 bzero((caddr_t
)sin_p
, sizeof(*sin_p
));
215 sin_p
->sin_len
= sizeof(*sin_p
);
216 sin_p
->sin_family
= AF_INET
;
217 if (netboot_rootpath(&sin_p
->sin_addr
, nd
->nd_root
.ndm_host
,
218 sizeof(nd
->nd_root
.ndm_host
),
219 nd
->nd_root
.ndm_path
, MAXPATHLEN
) == TRUE
) {
220 do_bpgetfile
= FALSE
;
223 nd
->nd_private
.ndm_saddr
.sin_addr
.s_addr
= 0;
225 thread_funnel_switch(KERNEL_FUNNEL
, NETWORK_FUNNEL
);
228 struct in_addr router
;
230 * Get client name and gateway address.
231 * RPC: bootparam/whoami
232 * Use the old broadcast address for the WHOAMI
233 * call because we do not yet know our netmask.
234 * The server address returned by the WHOAMI call
235 * is used for all subsequent booptaram RPCs.
237 bzero((caddr_t
)&bp_sin
, sizeof(bp_sin
));
238 bp_sin
.sin_len
= sizeof(bp_sin
);
239 bp_sin
.sin_family
= AF_INET
;
240 bp_sin
.sin_addr
.s_addr
= INADDR_BROADCAST
;
241 hostnamelen
= MAXHOSTNAMELEN
;
243 error
= bp_whoami(&bp_sin
, &my_ip
, &router
);
245 printf("nfs_boot: bootparam whoami, error=%d", error
);
248 printf("nfs_boot: BOOTPARAMS server " IP_FORMAT
"\n",
249 IP_LIST(&bp_sin
.sin_addr
));
250 printf("nfs_boot: hostname %s\n", hostname
);
253 error
= bp_getfile(&bp_sin
, "root", &nd
->nd_root
.ndm_saddr
,
254 nd
->nd_root
.ndm_host
, nd
->nd_root
.ndm_path
);
256 printf("nfs_boot: bootparam get root: %d\n", error
);
261 #if !defined(NO_MOUNT_PRIVATE)
262 if (do_bpgetfile
) { /* get private path */
263 MALLOC_ZONE(nd
->nd_private
.ndm_path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
264 error
= bp_getfile(&bp_sin
, "private",
265 &nd
->nd_private
.ndm_saddr
,
266 nd
->nd_private
.ndm_host
,
267 nd
->nd_private
.ndm_path
);
269 char * check_path
= NULL
;
271 MALLOC_ZONE(check_path
, char *, MAXPATHLEN
, M_NAMEI
, M_WAITOK
);
272 snprintf(check_path
, MAXPATHLEN
, "%s/private", nd
->nd_root
.ndm_path
);
273 if ((nd
->nd_root
.ndm_saddr
.sin_addr
.s_addr
274 == nd
->nd_private
.ndm_saddr
.sin_addr
.s_addr
)
275 && (strcmp(check_path
, nd
->nd_private
.ndm_path
) == 0)) {
276 /* private path is prefix of root path, don't mount */
277 nd
->nd_private
.ndm_saddr
.sin_addr
.s_addr
= 0;
279 FREE_ZONE(check_path
, MAXPATHLEN
, M_NAMEI
);
282 /* private key not defined, don't mount */
283 nd
->nd_private
.ndm_saddr
.sin_addr
.s_addr
= 0;
289 #endif /* NO_MOUNT_PRIVATE */
291 thread_funnel_switch(NETWORK_FUNNEL
, KERNEL_FUNNEL
);
297 * Called with a partially initialized nfs_diskless struct
298 * with file handles to be filled in.
301 nfs_boot_getfh(nd
, procp
, v3
)
302 struct nfs_diskless
*nd
;
308 thread_funnel_switch(KERNEL_FUNNEL
, NETWORK_FUNNEL
);
310 nd
->nd_root
.ndm_nfsv3
= v3
;
311 error
= get_file_handle(&nd
->nd_root
);
313 printf("nfs_boot: get_file_handle(v%d) root failed, %d\n",
318 #if !defined(NO_MOUNT_PRIVATE)
319 if (nd
->nd_private
.ndm_saddr
.sin_addr
.s_addr
) {
320 /* get private file handle */
321 nd
->nd_private
.ndm_nfsv3
= v3
;
322 error
= get_file_handle(&nd
->nd_private
);
324 printf("nfs_boot: get_file_handle(v%d) private failed, %d\n",
329 #endif /* NO_MOUNT_PRIVATE */
331 thread_funnel_switch(NETWORK_FUNNEL
, KERNEL_FUNNEL
);
336 get_file_handle(ndmntp
)
337 struct nfs_dlmount
*ndmntp
;
339 char *sp
, *dp
, *endp
;
343 * Get file handle for "key" (root or swap)
344 * using RPC to mountd/mount
346 error
= md_mount(&ndmntp
->ndm_saddr
, ndmntp
->ndm_path
, ndmntp
->ndm_nfsv3
,
347 ndmntp
->ndm_fh
, &ndmntp
->ndm_fhlen
);
351 /* Construct remote path (for getmntinfo(3)) */
352 dp
= ndmntp
->ndm_host
;
353 endp
= dp
+ MNAMELEN
- 1;
356 for (sp
= ndmntp
->ndm_path
; *sp
&& dp
< endp
;)
365 * Get an mbuf with the given length, and
366 * initialize the pkthdr length field.
369 m_get_len(int msg_len
)
372 m
= m_gethdr(M_WAIT
, MT_DATA
);
375 if (msg_len
> MHLEN
) {
376 if (msg_len
> MCLBYTES
)
377 panic("nfs_boot: msg_len > MCLBYTES");
383 m
->m_pkthdr
.len
= m
->m_len
;
389 * String representation for RPC.
392 u_long len
; /* length without null or padding */
393 u_char data
[4]; /* data (longer, of course) */
394 /* data is padded to a long-word boundary */
396 /* Compute space used given string length. */
397 #define RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3))
400 * Inet address in RPC messages
401 * (Note, really four longs, NOT chars. Blech.)
410 * RPC: bootparam/whoami
411 * Given client IP address, get:
412 * client name (hostname)
413 * domain name (domainname)
416 * The hostname and domainname are set here for convenience.
418 * Note - bpsin is initialized to the broadcast address,
419 * and will be replaced with the bootparam server address
420 * after this call is complete. Have to use PMAP_PROC_CALL
421 * to make sure we get responses only from a servers that
422 * know about us (don't want to broadcast a getport call).
425 bp_whoami(bpsin
, my_ip
, gw_ip
)
426 struct sockaddr_in
*bpsin
;
427 struct in_addr
*my_ip
;
428 struct in_addr
*gw_ip
;
430 /* RPC structures for PMAPPROC_CALLIT */
436 struct bp_inaddr call_ia
;
439 struct rpc_string
*str
;
440 struct bp_inaddr
*bia
;
442 struct sockaddr_in
*sin
;
449 * Get message buffer of sufficient size.
451 msg_len
= sizeof(*call
);
452 m
= m_get_len(msg_len
);
457 * Build request message for PMAPPROC_CALLIT.
459 call
= mtod(m
, struct whoami_call
*);
460 call
->call_prog
= htonl(BOOTPARAM_PROG
);
461 call
->call_vers
= htonl(BOOTPARAM_VERS
);
462 call
->call_proc
= htonl(BOOTPARAM_WHOAMI
);
463 call
->call_arglen
= htonl(sizeof(struct bp_inaddr
));
465 /* client IP address */
466 call
->call_ia
.atype
= htonl(1);
468 lp
= call
->call_ia
.addr
;
469 *lp
++ = htonl(*p
); p
++;
470 *lp
++ = htonl(*p
); p
++;
471 *lp
++ = htonl(*p
); p
++;
472 *lp
++ = htonl(*p
); p
++;
474 /* RPC: portmap/callit */
475 bpsin
->sin_port
= htons(PMAPPORT
);
477 error
= krpc_call(bpsin
, PMAPPROG
, PMAPVERS
,
478 PMAPPROC_CALLIT
, &m
, &sin
);
483 * Parse result message.
486 lp
= mtod(m
, long *);
488 /* bootparam server port (also grab from address). */
489 if (msg_len
< sizeof(*lp
))
491 msg_len
-= sizeof(*lp
);
492 bpsin
->sin_port
= htons((short)ntohl(*lp
++));
493 bpsin
->sin_addr
.s_addr
= sin
->sin_addr
.s_addr
;
495 /* length of encapsulated results */
496 if (msg_len
< (ntohl(*lp
) + sizeof(*lp
)))
498 msg_len
= ntohl(*lp
++);
502 if (msg_len
< sizeof(*str
))
504 str
= (struct rpc_string
*)p
;
505 cn_len
= ntohl(str
->len
);
506 if (msg_len
< cn_len
)
508 if (cn_len
>= MAXHOSTNAMELEN
)
510 bcopy(str
->data
, hostname
, cn_len
);
511 hostname
[cn_len
] = '\0';
512 hostnamelen
= cn_len
;
513 p
+= RPC_STR_SIZE(cn_len
);
514 msg_len
-= RPC_STR_SIZE(cn_len
);
517 if (msg_len
< sizeof(*str
))
519 str
= (struct rpc_string
*)p
;
520 dn_len
= ntohl(str
->len
);
521 if (msg_len
< dn_len
)
523 if (dn_len
>= MAXHOSTNAMELEN
)
525 bcopy(str
->data
, domainname
, dn_len
);
526 domainname
[dn_len
] = '\0';
527 domainnamelen
= dn_len
;
528 p
+= RPC_STR_SIZE(dn_len
);
529 msg_len
-= RPC_STR_SIZE(dn_len
);
531 /* gateway address */
532 if (msg_len
< sizeof(*bia
))
534 bia
= (struct bp_inaddr
*)p
;
535 if (bia
->atype
!= htonl(1))
538 *p
++ = ntohl(bia
->addr
[0]);
539 *p
++ = ntohl(bia
->addr
[1]);
540 *p
++ = ntohl(bia
->addr
[2]);
541 *p
++ = ntohl(bia
->addr
[3]);
545 printf("nfs_boot: bootparam_whoami: bad reply\n");
558 * RPC: bootparam/getfile
559 * Given client name and file "key", get:
565 bp_getfile(bpsin
, key
, md_sin
, serv_name
, pathname
)
566 struct sockaddr_in
*bpsin
;
568 struct sockaddr_in
*md_sin
;
572 struct rpc_string
*str
;
574 struct bp_inaddr
*bia
;
575 struct sockaddr_in
*sin
;
578 int cn_len
, key_len
, sn_len
, path_len
;
581 * Get message buffer of sufficient size.
583 cn_len
= hostnamelen
;
584 key_len
= strlen(key
);
586 msg_len
+= RPC_STR_SIZE(cn_len
);
587 msg_len
+= RPC_STR_SIZE(key_len
);
588 m
= m_get_len(msg_len
);
593 * Build request message.
595 p
= mtod(m
, u_char
*);
597 /* client name (hostname) */
598 str
= (struct rpc_string
*)p
;
599 str
->len
= htonl(cn_len
);
600 bcopy(hostname
, str
->data
, cn_len
);
601 p
+= RPC_STR_SIZE(cn_len
);
602 /* key name (root or swap) */
603 str
= (struct rpc_string
*)p
;
604 str
->len
= htonl(key_len
);
605 bcopy(key
, str
->data
, key_len
);
607 /* RPC: bootparam/getfile */
608 error
= krpc_call(bpsin
, BOOTPARAM_PROG
, BOOTPARAM_VERS
,
609 BOOTPARAM_GETFILE
, &m
, NULL
);
614 * Parse result message.
616 p
= mtod(m
, u_char
*);
620 if (msg_len
< sizeof(*str
))
622 str
= (struct rpc_string
*)p
;
623 sn_len
= ntohl(str
->len
);
624 if (msg_len
< sn_len
)
626 if (sn_len
>= MNAMELEN
)
628 bcopy(str
->data
, serv_name
, sn_len
);
629 serv_name
[sn_len
] = '\0';
630 p
+= RPC_STR_SIZE(sn_len
);
631 msg_len
-= RPC_STR_SIZE(sn_len
);
633 /* server IP address (mountd) */
634 if (msg_len
< sizeof(*bia
))
636 bia
= (struct bp_inaddr
*)p
;
637 if (bia
->atype
!= htonl(1))
640 bzero((caddr_t
)sin
, sizeof(*sin
));
641 sin
->sin_len
= sizeof(*sin
);
642 sin
->sin_family
= AF_INET
;
643 q
= (u_char
*) &sin
->sin_addr
;
644 *q
++ = ntohl(bia
->addr
[0]);
645 *q
++ = ntohl(bia
->addr
[1]);
646 *q
++ = ntohl(bia
->addr
[2]);
647 *q
++ = ntohl(bia
->addr
[3]);
649 msg_len
-= sizeof(*bia
);
651 /* server pathname */
652 if (msg_len
< sizeof(*str
))
654 str
= (struct rpc_string
*)p
;
655 path_len
= ntohl(str
->len
);
656 if (msg_len
< path_len
)
658 if (path_len
>= MAXPATHLEN
)
660 bcopy(str
->data
, pathname
, path_len
);
661 pathname
[path_len
] = '\0';
665 printf("nfs_boot: bootparam_getfile: bad reply\n");
676 * Given a server pathname, get an NFS file handle.
677 * Also, sets sin->sin_port to the NFS service port.
680 md_mount(mdsin
, path
, v3
, fhp
, fhlenp
)
681 struct sockaddr_in
*mdsin
; /* mountd server address */
687 /* The RPC structures */
688 struct rpc_string
*str
;
691 u_char data
[NFSX_V3FHMAX
+ sizeof(u_long
)];
694 int error
, mlen
, slen
;
695 int mntversion
= v3
? RPCMNT_VER3
: RPCMNT_VER1
;
697 /* Get port number for MOUNTD. */
698 error
= krpc_portmap(mdsin
, RPCPROG_MNT
, mntversion
,
700 if (error
) return error
;
703 mlen
= RPC_STR_SIZE(slen
);
708 str
= mtod(m
, struct rpc_string
*);
709 str
->len
= htonl(slen
);
710 bcopy(path
, str
->data
, slen
);
712 /* Do RPC to mountd. */
713 error
= krpc_call(mdsin
, RPCPROG_MNT
, mntversion
,
714 RPCMNT_MOUNT
, &m
, NULL
);
716 return error
; /* message already freed */
719 * the reply must be long enough to hold the errno plus either of:
721 * + a v3 filehandle length + a v3 filehandle
724 if (mlen
< sizeof(u_long
))
726 rdata
= mtod(m
, struct rdata
*);
727 error
= ntohl(rdata
->errno
);
733 if (mlen
< sizeof(u_long
)*2)
735 fhlen
= ntohl(*(u_long
*)rdata
->data
);
736 fh
= rdata
->data
+ sizeof(u_long
);
737 if (mlen
< (sizeof(u_long
)*2 + fhlen
))
739 bcopy(fh
, fhp
, fhlen
);
742 if (mlen
< (sizeof(u_long
) + NFSX_V2FH
))
744 bcopy(rdata
->data
, fhp
, NFSX_V2FH
);
748 /* Set port number for NFS use. */
749 error
= krpc_portmap(mdsin
, NFS_PROG
, v3
? NFS_VER3
: NFS_VER2
,