]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/nfs/nfs_boot.c
xnu-201.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_boot.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/* Copyright (c) 1995, 1997 NeXT Computer, Inc. All Rights Reserved */
23/*
24 * Copyright (c) 1994 Adam Glass, Gordon Ross
25 * All rights reserved.
26 *
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.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
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.
46 *
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
57 * SUCH DAMAGE.
58 *
59 * History:
60 * 14-March-97 Dieter Siegmund (dieter@next.com)
61 * - Use BOOTP instead of RARP to get the IP address at boot time
62 *
63 * 23-May-97 Umesh Vaishampayan (umeshv@apple.com)
64 * - Added the ability to mount "/private" separately.
65 *
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
76 * again
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
87 */
88
89#include <sys/param.h>
90#include <sys/systm.h>
91#include <sys/kernel.h>
92#include <sys/conf.h>
93#include <sys/ioctl.h>
94#include <sys/proc.h>
95#include <sys/mount.h>
96#include <sys/mbuf.h>
97
98#include <sys/malloc.h>
99#include <sys/socket.h>
100#include <sys/reboot.h>
101
102#include <net/if.h>
103#include <net/if_dl.h>
104#include <net/if_types.h>
105#include <net/route.h>
106
107#include <netinet/in.h>
108#include <netinet/if_ether.h>
109
110#include <nfs/rpcv2.h>
111#include <nfs/nfsproto.h>
112#include <nfs/nfs.h>
113#include <nfs/nfsdiskless.h>
114#include <nfs/krpc.h>
115
116#include <pexpert/pexpert.h>
117
118#include "ether.h"
119
120#include <libkern/libkern.h>
121
122extern char *strchr(const char *str, int ch);
123
124#if NETHER == 0
125
126int nfs_boot_init(nd, procp)
127 struct nfs_diskless *nd;
128 struct proc *procp;
129{
130 panic("nfs_boot_init: no ether");
131}
132
133#else /* NETHER */
134
135/*
136 * Support for NFS diskless booting, specifically getting information
137 * about where to boot from, what pathnames, etc.
138 *
139 * This implememtation uses RARP and the bootparam RPC.
140 * We are forced to implement RPC anyway (to get file handles)
141 * so we might as well take advantage of it for bootparam too.
142 *
143 * The diskless boot sequence goes as follows:
144 * (1) Use RARP to get our interface address
145 * (2) Use RPC/bootparam/whoami to get our hostname,
146 * our IP address, and the server's IP address.
147 * (3) Use RPC/bootparam/getfile to get the root path
148 * (4) Use RPC/mountd to get the root file handle
149 * (5) Use RPC/bootparam/getfile to get the swap path
150 * (6) Use RPC/mountd to get the swap file handle
151 *
152 * (This happens to be the way Sun does it too.)
153 */
154
155extern int bootp(struct ifnet * ifp, struct in_addr * iaddr_p, int max_retry,
156 struct in_addr * netmask_p, struct in_addr * router_p,
157 struct proc * procp);
158
159/* bootparam RPC */
160static int bp_whoami __P((struct sockaddr_in *bpsin,
161 struct in_addr *my_ip, struct in_addr *gw_ip));
162static int bp_getfile __P((struct sockaddr_in *bpsin, char *key,
163 struct sockaddr_in *mdsin, char *servname, char *path));
164
165static boolean_t path_getfile __P((char * image_path,
166 struct sockaddr_in * sin_p,
167 char * serv_name, char * pathname));
168
169static __inline__
170u_long iptohl(struct in_addr ip)
171{
172 return (ntohl(ip.s_addr));
173}
174
175static __inline__ boolean_t
176same_subnet(struct in_addr addr1, struct in_addr addr2, struct in_addr mask)
177{
178 u_long m = iptohl(mask);
179 if ((iptohl(addr1) & m) != (iptohl(addr2) & m))
180 return (FALSE);
181 return (TRUE);
182}
183
184/* mountd RPC */
185static int md_mount __P((struct sockaddr_in *mdsin, char *path,
186 u_char *fh));
187
188/* other helpers */
189static void get_file_handle __P((char *pathname, struct nfs_dlmount *ndmntp));
190
191#define IP_FORMAT "%d.%d.%d.%d"
192#define IP_CH(ip) ((u_char *)ip)
193#define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
194/*
195 * Called with an empty nfs_diskless struct to be filled in.
196 */
197int
198nfs_boot_init(nd, procp)
199 struct nfs_diskless *nd;
200 struct proc *procp;
201{
202 char * booter_path = NULL;
203 boolean_t do_bpwhoami = TRUE;
204 boolean_t do_bpgetfile = TRUE;
205 struct ifreq ireq;
206 struct in_addr my_ip;
207 struct sockaddr_in bp_sin;
208 struct sockaddr_in *sin;
209 struct ifnet *ifp;
210 struct in_addr gw_ip;
211 struct socket *so;
212 struct in_addr my_netmask;
213 int error;
214 char * root_path = NULL;
215
216 MALLOC(booter_path, char *, MAXPATHLEN, M_TEMP, M_WAITOK);
217 MALLOC(root_path, char *, MAXPATHLEN, M_TEMP, M_WAITOK);
218
219 /* booter-supplied path */
220 if (!PE_parse_boot_arg("rp", booter_path)
221 && !PE_parse_boot_arg("rootpath", booter_path)) {
222 booter_path[0] = 0;
223 }
224
225 root_path[0] = 0;
226
227 gw_ip.s_addr = 0;
228
229 /* clear out the request structure */
230 bzero(&ireq, sizeof(ireq));
231
232 /*
233 * Find an interface, rarp for its ip address, stuff it, the
234 * implied broadcast addr, and netmask into a nfs_diskless struct.
235 *
236 * This was moved here from nfs_vfsops.c because this procedure
237 * would be quite different if someone decides to write (i.e.) a
238 * BOOTP version of this file (might not use RARP, etc.)
239 */
240
241 thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
242
243 /*
244 * Find a network interface.
245 */
246 ifp = NULL;
247 { /* if the root device is set, use it */
248 extern char rootdevice[];
249 if (rootdevice[0])
250 ifp = ifunit(rootdevice);
251 }
252 if (ifp == NULL) { /* search for network device */
253 /* for (ifp = ifnet; ifp; ifp = ifp->if_next)*/
254 TAILQ_FOREACH(ifp, &ifnet, if_link)
255 if ((ifp->if_flags &
256 (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
257 break;
258 }
259 if (ifp == NULL)
260 panic("nfs_boot: no suitable interface");
261 sprintf(ireq.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
262 printf("nfs_boot: using network interface '%s'\n", ireq.ifr_name);
263
264 /*
265 * Bring up the interface.
266 */
267 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0)
268 panic("nfs_boot: socreate, error=%d", error);
269 ireq.ifr_flags = ifp->if_flags | IFF_UP;
270 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp);
271 if (error)
272 panic("nfs_boot: SIFFLAGS, error=%d", error);
273
274#define DO_BOOTP
275#ifdef DO_BOOTP
276 { /* use BOOTP to retrieve IP address, netmask and router */
277 struct sockaddr_in sockin;
278 struct in_addr router;
279 struct in_addr netmask;
280
281 my_ip.s_addr = 0;
282 netmask.s_addr = 0;
283 router.s_addr = 0;
284 sockin.sin_family = AF_INET;
285 sockin.sin_len = sizeof(sockin);
286 sockin.sin_addr.s_addr = 0;
287#define RETRY_COUNT 32
288 while ((error = bootp(ifp, &my_ip, RETRY_COUNT,
289 &netmask, &router, procp))) {
290 if (error == ETIMEDOUT)
291 printf("nfs_boot: BOOTP timed out, retrying...\n");
292
293 else {
294 printf("nfs_boot: bootp() failed, error = %d\n", error);
295 panic("nfs_boot");
296 }
297 }
298 /* clear the netmask */
299 ((struct sockaddr_in *)&ireq.ifr_addr)->sin_addr.s_addr = 0;
300 error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)&ireq, procp);
301 if (error)
302 printf("nfs_boot: SIOCSIFNETMASK failed: %d\n", error);
303
304 if (netmask.s_addr) {
305 /* set our new subnet mask */
306 sockin.sin_addr = netmask;
307 *((struct sockaddr_in *)&ireq.ifr_addr) = sockin;
308 error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)&ireq, procp);
309 if (error)
310 printf("nfs_boot: SIOCSIFNETMASK failed: %d\n", error);
311 }
312
313 /* set our address */
314 sockin.sin_addr = my_ip;
315 *((struct sockaddr_in *)&ireq.ifr_addr) = sockin;
316 error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp);
317 if (error) {
318 printf("SIOCSIFADDR failed: %d\n", error);
319 panic("nfs_boot.c");
320 }
321 printf("nfs_boot: IP address " IP_FORMAT, IP_LIST(&my_ip));
322 if (netmask.s_addr)
323 printf(" netmask " IP_FORMAT, IP_LIST(&netmask));
324 if (router.s_addr) {
325 gw_ip = router;
326 printf(" router " IP_FORMAT, IP_LIST(&router));
327 }
328 printf("\n");
329 }
330#else
331 /*
332 * Do RARP for the interface address.
333 */
334 if ((error = revarpwhoami(&my_ip, ifp)) != 0)
335 panic("revarp failed, error=%d", error);
336 printf("nfs_boot: client_addr=0x%x\n", ntohl(my_ip.s_addr));
337
338 /*
339 * Do enough of ifconfig(8) so that the chosen interface
340 * can talk to the servers. (just set the address)
341 */
342 sin = (struct sockaddr_in *)&ireq.ifr_addr;
343 bzero((caddr_t)sin, sizeof(*sin));
344 sin->sin_len = sizeof(*sin);
345 sin->sin_family = AF_INET;
346 sin->sin_addr.s_addr = my_ip.s_addr;
347 error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp);
348 if (error)
349 panic("nfs_boot: set if addr, error=%d", error);
350#endif DO_BOOTP
351
352 /* need netmask to determine whether NFS server local */
353 sin = (struct sockaddr_in *)&ireq.ifr_addr;
354 bzero((caddr_t)sin, sizeof(*sin));
355 sin->sin_len = sizeof(*sin);
356 sin->sin_family = AF_INET;
357 error = ifioctl(so, SIOCGIFNETMASK, (caddr_t)&ireq, procp);
358 if (error)
359 panic("nfs_boot: SIOCGIFNETMASK error=%d", error);
360 my_netmask = sin->sin_addr;
361
362 soclose(so);
363
364 /* check for a booter-specified path */
365 if (booter_path[0]) {
366 nd->nd_root.ndm_saddr.sin_addr.s_addr = 0;
367 nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
368 if (path_getfile(booter_path, &nd->nd_root.ndm_saddr,
369 nd->nd_root.ndm_host, root_path)) {
370 do_bpgetfile = FALSE;
371 printf("nfs_boot: using booter-supplied path '%s'\n",
372 booter_path);
373 if (same_subnet(nd->nd_root.ndm_saddr.sin_addr,
374 my_ip, my_netmask)
375 || gw_ip.s_addr) {
376 do_bpwhoami = FALSE;
377 }
378 else {
379 /* do bpwhoami to attempt to get the router */
380 }
381 }
382 else {
383 printf("nfs_boot: ignoring badly formed bootpath '%s'\n",
384 booter_path);
385 }
386 }
387
388 if (do_bpwhoami) {
389 /*
390 * Get client name and gateway address.
391 * RPC: bootparam/whoami
392 * Use the old broadcast address for the WHOAMI
393 * call because we do not yet know our netmask.
394 * The server address returned by the WHOAMI call
395 * is used for all subsequent booptaram RPCs.
396 */
397 bzero((caddr_t)&bp_sin, sizeof(bp_sin));
398 bp_sin.sin_len = sizeof(bp_sin);
399 bp_sin.sin_family = AF_INET;
400 bp_sin.sin_addr.s_addr = INADDR_BROADCAST;
401 hostnamelen = MAXHOSTNAMELEN;
402
403 { /* bpwhoami also returns gateway IP address */
404
405 struct in_addr router;
406
407 router.s_addr = 0;
408 error = bp_whoami(&bp_sin, &my_ip, &router);
409 if (error) {
410 printf("nfs_boot: bootparam whoami, error=%d", error);
411 panic("nfs_boot: bootparam whoami\n");
412 }
413 /* if not already set by BOOTP, use the one from BPWHOAMI */
414 if (gw_ip.s_addr == 0)
415 gw_ip = router;
416 }
417 printf("nfs_boot: BOOTPARAMS server " IP_FORMAT "\n",
418 IP_LIST(&bp_sin.sin_addr));
419 printf("nfs_boot: hostname %s\n", hostname);
420 }
421#define NFS_BOOT_GATEWAY 1
422#ifdef NFS_BOOT_GATEWAY
423 /*
424 * DWS 2/18/1999
425 * The comment below does not apply to gw_ip discovered
426 * via BOOTP (see DO_BOOTP loop above) since BOOTP servers
427 * are supposed to be more trustworthy.
428 */
429 /*
430 * XXX - This code is conditionally compiled only because
431 * many bootparam servers (in particular, SunOS 4.1.3)
432 * always set the gateway address to their own address.
433 * The bootparam server is not necessarily the gateway.
434 * We could just believe the server, and at worst you would
435 * need to delete the incorrect default route before adding
436 * the correct one, but for simplicity, ignore the gateway.
437 * If your server is OK, you can turn on this option.
438 *
439 * If the gateway address is set, add a default route.
440 * (The mountd RPCs may go across a gateway.)
441 */
442 if (gw_ip.s_addr) {
443 struct sockaddr dst, gw, mask;
444 /* Destination: (default) */
445 bzero((caddr_t)&dst, sizeof(dst));
446 dst.sa_len = sizeof(dst);
447 dst.sa_family = AF_INET;
448 /* Gateway: */
449 bzero((caddr_t)&gw, sizeof(gw));
450 sin = (struct sockaddr_in *)&gw;
451 sin->sin_len = sizeof(gw);
452 sin->sin_family = AF_INET;
453 sin->sin_addr.s_addr = gw_ip.s_addr;
454 /* Mask: (zero length) */
455 bzero(&mask, sizeof(mask));
456 printf("nfs_boot: adding default route " IP_FORMAT "\n",
457 IP_LIST(&gw_ip));
458 /* add, dest, gw, mask, flags, 0 */
459 error = rtrequest(RTM_ADD, &dst, (struct sockaddr *)&gw,
460 &mask, (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL);
461 if (error)
462 printf("nfs_boot: add route, error=%d\n", error);
463 }
464#endif
465 if (do_bpgetfile) {
466 error = bp_getfile(&bp_sin, "root", &nd->nd_root.ndm_saddr,
467 nd->nd_root.ndm_host, root_path);
468 if (error) {
469 printf("nfs_boot: bootparam get root: %d\n", error);
470 panic("nfs_boot: bootparam get root");
471 }
472 }
473
474 get_file_handle(root_path, &nd->nd_root);
475
476#if !defined(NO_MOUNT_PRIVATE)
477 if (do_bpgetfile) { /* get private path */
478 char * private_path = NULL;
479
480 MALLOC(private_path, char *, MAXPATHLEN, M_TEMP, M_WAITOK);
481 error = bp_getfile(&bp_sin, "private",
482 &nd->nd_private.ndm_saddr,
483 nd->nd_private.ndm_host, private_path);
484 if (!error) {
485 char * check_path = NULL;
486
487 MALLOC(check_path, char *, MAXPATHLEN, M_TEMP, M_WAITOK);
488 sprintf(check_path, "%s/private", root_path);
489 if ((nd->nd_root.ndm_saddr.sin_addr.s_addr
490 == nd->nd_private.ndm_saddr.sin_addr.s_addr)
491 && (strcmp(check_path, private_path) == 0)) {
492 /* private path is prefix of root path, don't mount */
493 nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
494 }
495 else {
496 get_file_handle(private_path, &nd->nd_private);
497 }
498 _FREE(check_path, M_TEMP);
499 }
500 else {
501 /* private key not defined, don't mount */
502 nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
503 }
504 _FREE(private_path, M_TEMP);
505 }
506#endif NO_MOUNT_PRIVATE
507 thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
508 _FREE(booter_path, M_TEMP);
509 _FREE(root_path, M_TEMP);
510 return (0);
511}
512
513int
514inet_aton(char * cp, struct in_addr * pin)
515{
516 u_char * b = (char *)pin;
517 int i;
518 char * p;
519
520 for (p = cp, i = 0; i < 4; i++) {
521 u_long l = strtoul(p, 0, 0);
522 if (l > 255)
523 return (FALSE);
524 b[i] = l;
525 p = strchr(p, '.');
526 if (i < 3 && p == NULL)
527 return (FALSE);
528 p++;
529 }
530 return (TRUE);
531}
532
533/*
534 * Function: parse_image_path
535 * Purpose:
536 * Parse a string of the form "<IP>:<host>:<path>" into
537 * the given ip address and host and pathnames.
538 * Example:
539 * "17.202.16.17:seaport:/release/.images/Image9/CurrentHera"
540 */
541static __inline__ boolean_t
542parse_image_path(char * c, struct in_addr * iaddr_p, char * hostname,
543 char * pathname)
544{
545 char * d;
546 char * p;
547#define TMP_SIZE 128
548 char tmp[TMP_SIZE];
549
550 p = strchr(c, ':');
551 if (p == NULL)
552 return (FALSE);
553 if ((p - c) >= TMP_SIZE)
554 return (FALSE);
555 strncpy(tmp, c, p - c);
556 tmp[p - c] = 0;
557 if (inet_aton(tmp, iaddr_p) != 1)
558 return (FALSE);
559 p++;
560 d = strchr(p, ':');
561 if (d == NULL)
562 return (FALSE);
563 strncpy(hostname, p, d - p);
564 hostname[d - p] = 0;
565 d++;
566 strcpy(pathname, d);
567 return (TRUE);
568}
569
570static boolean_t
571path_getfile(char * image_path, struct sockaddr_in * sin_p,
572 char * serv_name, char * pathname)
573{
574 bzero((caddr_t)sin_p, sizeof(*sin_p));
575 sin_p->sin_len = sizeof(*sin_p);
576 sin_p->sin_family = AF_INET;
577 if (parse_image_path(image_path, &sin_p->sin_addr, serv_name, pathname)
578 == FALSE)
579 return (FALSE);
580 return (TRUE);
581}
582
583static void
584get_file_handle(pathname, ndmntp)
585 char *pathname; /* path on server */
586 struct nfs_dlmount *ndmntp; /* output */
587{
588 char *sp, *dp, *endp;
589 int error;
590
591 /*
592 * Get file handle for "key" (root or swap)
593 * using RPC to mountd/mount
594 */
595 error = md_mount(&ndmntp->ndm_saddr, pathname, ndmntp->ndm_fh);
596 if (error)
597 panic("nfs_boot: mountd, error=%d", error);
598
599 /* Construct remote path (for getmntinfo(3)) */
600 dp = ndmntp->ndm_host;
601 endp = dp + MNAMELEN - 1;
602 dp += strlen(dp);
603 *dp++ = ':';
604 for (sp = pathname; *sp && dp < endp;)
605 *dp++ = *sp++;
606 *dp = '\0';
607
608}
609
610
611/*
612 * Get an mbuf with the given length, and
613 * initialize the pkthdr length field.
614 */
615static struct mbuf *
616m_get_len(int msg_len)
617{
618 struct mbuf *m;
619 m = m_gethdr(M_WAIT, MT_DATA);
620 if (m == NULL)
621 return NULL;
622 if (msg_len > MHLEN) {
623 if (msg_len > MCLBYTES)
624 panic("nfs_boot: msg_len > MCLBYTES");
625 MCLGET(m, M_WAIT);
626 if (m == NULL)
627 return NULL;
628 }
629 m->m_len = msg_len;
630 m->m_pkthdr.len = m->m_len;
631 return (m);
632}
633
634
635/*
636 * String representation for RPC.
637 */
638struct rpc_string {
639 u_long len; /* length without null or padding */
640 u_char data[4]; /* data (longer, of course) */
641 /* data is padded to a long-word boundary */
642};
643/* Compute space used given string length. */
644#define RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3))
645
646/*
647 * Inet address in RPC messages
648 * (Note, really four longs, NOT chars. Blech.)
649 */
650struct bp_inaddr {
651 u_long atype;
652 long addr[4];
653};
654
655
656/*
657 * RPC: bootparam/whoami
658 * Given client IP address, get:
659 * client name (hostname)
660 * domain name (domainname)
661 * gateway address
662 *
663 * The hostname and domainname are set here for convenience.
664 *
665 * Note - bpsin is initialized to the broadcast address,
666 * and will be replaced with the bootparam server address
667 * after this call is complete. Have to use PMAP_PROC_CALL
668 * to make sure we get responses only from a servers that
669 * know about us (don't want to broadcast a getport call).
670 */
671static int
672bp_whoami(bpsin, my_ip, gw_ip)
673 struct sockaddr_in *bpsin;
674 struct in_addr *my_ip;
675 struct in_addr *gw_ip;
676{
677 /* RPC structures for PMAPPROC_CALLIT */
678 struct whoami_call {
679 u_long call_prog;
680 u_long call_vers;
681 u_long call_proc;
682 u_long call_arglen;
683 struct bp_inaddr call_ia;
684 } *call;
685
686 struct rpc_string *str;
687 struct bp_inaddr *bia;
688 struct mbuf *m;
689 struct sockaddr_in *sin;
690 int error, msg_len;
691 int cn_len, dn_len;
692 u_char *p;
693 long *lp;
694
695 /*
696 * Get message buffer of sufficient size.
697 */
698 msg_len = sizeof(*call);
699 m = m_get_len(msg_len);
700 if (m == NULL)
701 return ENOBUFS;
702
703 /*
704 * Build request message for PMAPPROC_CALLIT.
705 */
706 call = mtod(m, struct whoami_call *);
707 call->call_prog = htonl(BOOTPARAM_PROG);
708 call->call_vers = htonl(BOOTPARAM_VERS);
709 call->call_proc = htonl(BOOTPARAM_WHOAMI);
710 call->call_arglen = htonl(sizeof(struct bp_inaddr));
711
712 /* client IP address */
713 call->call_ia.atype = htonl(1);
714 p = (u_char*)my_ip;
715 lp = call->call_ia.addr;
716 *lp++ = htonl(*p); p++;
717 *lp++ = htonl(*p); p++;
718 *lp++ = htonl(*p); p++;
719 *lp++ = htonl(*p); p++;
720
721 /* RPC: portmap/callit */
722 bpsin->sin_port = htons(PMAPPORT);
723
724 error = krpc_call(bpsin, PMAPPROG, PMAPVERS,
725 PMAPPROC_CALLIT, &m, &sin);
726 if (error)
727 return error;
728
729 /*
730 * Parse result message.
731 */
732 msg_len = m->m_len;
733 lp = mtod(m, long *);
734
735 /* bootparam server port (also grab from address). */
736 if (msg_len < sizeof(*lp))
737 goto bad;
738 msg_len -= sizeof(*lp);
739 bpsin->sin_port = htons((short)ntohl(*lp++));
740 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr;
741
742 /* length of encapsulated results */
743 if (msg_len < (ntohl(*lp) + sizeof(*lp)))
744 goto bad;
745 msg_len = ntohl(*lp++);
746 p = (char*)lp;
747
748 /* client name */
749 if (msg_len < sizeof(*str))
750 goto bad;
751 str = (struct rpc_string *)p;
752 cn_len = ntohl(str->len);
753 if (msg_len < cn_len)
754 goto bad;
755 if (cn_len >= MAXHOSTNAMELEN)
756 goto bad;
757 bcopy(str->data, hostname, cn_len);
758 hostname[cn_len] = '\0';
759 hostnamelen = cn_len;
760 p += RPC_STR_SIZE(cn_len);
761 msg_len -= RPC_STR_SIZE(cn_len);
762
763 /* domain name */
764 if (msg_len < sizeof(*str))
765 goto bad;
766 str = (struct rpc_string *)p;
767 dn_len = ntohl(str->len);
768 if (msg_len < dn_len)
769 goto bad;
770 if (dn_len >= MAXHOSTNAMELEN)
771 goto bad;
772 bcopy(str->data, domainname, dn_len);
773 domainname[dn_len] = '\0';
774 domainnamelen = dn_len;
775 p += RPC_STR_SIZE(dn_len);
776 msg_len -= RPC_STR_SIZE(dn_len);
777
778 /* gateway address */
779 if (msg_len < sizeof(*bia))
780 goto bad;
781 bia = (struct bp_inaddr *)p;
782 if (bia->atype != htonl(1))
783 goto bad;
784 p = (u_char*)gw_ip;
785 *p++ = ntohl(bia->addr[0]);
786 *p++ = ntohl(bia->addr[1]);
787 *p++ = ntohl(bia->addr[2]);
788 *p++ = ntohl(bia->addr[3]);
789 goto out;
790
791bad:
792 printf("nfs_boot: bootparam_whoami: bad reply\n");
793 error = EBADRPC;
794
795out:
796 if (sin)
797 FREE(sin, M_SONAME);
798
799 m_freem(m);
800 return(error);
801}
802
803
804/*
805 * RPC: bootparam/getfile
806 * Given client name and file "key", get:
807 * server name
808 * server IP address
809 * server pathname
810 */
811static int
812bp_getfile(bpsin, key, md_sin, serv_name, pathname)
813 struct sockaddr_in *bpsin;
814 char *key;
815 struct sockaddr_in *md_sin;
816 char *serv_name;
817 char *pathname;
818{
819 struct rpc_string *str;
820 struct mbuf *m;
821 struct bp_inaddr *bia;
822 struct sockaddr_in *sin;
823 u_char *p, *q;
824 int error, msg_len;
825 int cn_len, key_len, sn_len, path_len;
826
827 /*
828 * Get message buffer of sufficient size.
829 */
830 cn_len = hostnamelen;
831 key_len = strlen(key);
832 msg_len = 0;
833 msg_len += RPC_STR_SIZE(cn_len);
834 msg_len += RPC_STR_SIZE(key_len);
835 m = m_get_len(msg_len);
836 if (m == NULL)
837 return ENOBUFS;
838
839 /*
840 * Build request message.
841 */
842 p = mtod(m, u_char *);
843 bzero(p, msg_len);
844 /* client name (hostname) */
845 str = (struct rpc_string *)p;
846 str->len = htonl(cn_len);
847 bcopy(hostname, str->data, cn_len);
848 p += RPC_STR_SIZE(cn_len);
849 /* key name (root or swap) */
850 str = (struct rpc_string *)p;
851 str->len = htonl(key_len);
852 bcopy(key, str->data, key_len);
853
854 /* RPC: bootparam/getfile */
855 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS,
856 BOOTPARAM_GETFILE, &m, NULL);
857 if (error)
858 return error;
859
860 /*
861 * Parse result message.
862 */
863 p = mtod(m, u_char *);
864 msg_len = m->m_len;
865
866 /* server name */
867 if (msg_len < sizeof(*str))
868 goto bad;
869 str = (struct rpc_string *)p;
870 sn_len = ntohl(str->len);
871 if (msg_len < sn_len)
872 goto bad;
873 if (sn_len >= MNAMELEN)
874 goto bad;
875 bcopy(str->data, serv_name, sn_len);
876 serv_name[sn_len] = '\0';
877 p += RPC_STR_SIZE(sn_len);
878 msg_len -= RPC_STR_SIZE(sn_len);
879
880 /* server IP address (mountd) */
881 if (msg_len < sizeof(*bia))
882 goto bad;
883 bia = (struct bp_inaddr *)p;
884 if (bia->atype != htonl(1))
885 goto bad;
886 sin = md_sin;
887 bzero((caddr_t)sin, sizeof(*sin));
888 sin->sin_len = sizeof(*sin);
889 sin->sin_family = AF_INET;
890 q = (u_char*) &sin->sin_addr;
891 *q++ = ntohl(bia->addr[0]);
892 *q++ = ntohl(bia->addr[1]);
893 *q++ = ntohl(bia->addr[2]);
894 *q++ = ntohl(bia->addr[3]);
895 p += sizeof(*bia);
896 msg_len -= sizeof(*bia);
897
898 /* server pathname */
899 if (msg_len < sizeof(*str))
900 goto bad;
901 str = (struct rpc_string *)p;
902 path_len = ntohl(str->len);
903 if (msg_len < path_len)
904 goto bad;
905 if (path_len >= MAXPATHLEN)
906 goto bad;
907 bcopy(str->data, pathname, path_len);
908 pathname[path_len] = '\0';
909 goto out;
910
911bad:
912 printf("nfs_boot: bootparam_getfile: bad reply\n");
913 error = EBADRPC;
914
915out:
916 m_freem(m);
917 return(0);
918}
919
920
921/*
922 * RPC: mountd/mount
923 * Given a server pathname, get an NFS file handle.
924 * Also, sets sin->sin_port to the NFS service port.
925 */
926static int
927md_mount(mdsin, path, fhp)
928 struct sockaddr_in *mdsin; /* mountd server address */
929 char *path;
930 u_char *fhp;
931{
932 /* The RPC structures */
933 struct rpc_string *str;
934 struct rdata {
935 u_long errno;
936 u_char fh[NFSX_V2FH];
937 } *rdata;
938 struct mbuf *m;
939 int error, mlen, slen;
940
941 /* Get port number for MOUNTD. */
942 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1,
943 &mdsin->sin_port);
944 if (error) return error;
945
946 slen = strlen(path);
947 mlen = RPC_STR_SIZE(slen);
948
949 m = m_get_len(mlen);
950 if (m == NULL)
951 return ENOBUFS;
952 str = mtod(m, struct rpc_string *);
953 str->len = htonl(slen);
954 bcopy(path, str->data, slen);
955
956 /* Do RPC to mountd. */
957 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
958 RPCMNT_MOUNT, &m, NULL);
959 if (error)
960 return error; /* message already freed */
961
962 mlen = m->m_len;
963 if (mlen < sizeof(*rdata))
964 goto bad;
965 rdata = mtod(m, struct rdata *);
966 error = ntohl(rdata->errno);
967 if (error)
968 goto bad;
969 bcopy(rdata->fh, fhp, NFSX_V2FH);
970
971 /* Set port number for NFS use. */
972 error = krpc_portmap(mdsin, NFS_PROG, NFS_VER2,
973 &mdsin->sin_port);
974 goto out;
975
976bad:
977 error = EBADRPC;
978
979out:
980 m_freem(m);
981 return error;
982}
983
984#endif /* NETHER */