nfs_boot.c revision 1.6.2.2 1 /* $NetBSD: nfs_boot.c,v 1.6.2.2 1994/07/19 16:27:07 cgd Exp $ */
2
3 /*
4 * Copyright (c) 1994 Adam Glass, Gordon Ross
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the authors may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/conf.h>
34 #include <sys/ioctl.h>
35 #include <sys/proc.h>
36 #include <sys/mount.h>
37 #include <sys/mbuf.h>
38 #include <sys/socket.h>
39 #include <sys/reboot.h>
40
41 #include <net/if.h>
42 #include <net/route.h>
43
44 #include <netinet/in.h>
45 #include <netinet/if_ether.h>
46
47 #include <nfs/rpcv2.h>
48 #include <nfs/nfsv2.h>
49 #include <nfs/nfs.h>
50 #include <nfs/nfsdiskless.h>
51
52 #include "ether.h"
53 #if NETHER > 0
54
55 /*
56 * Support for NFS diskless booting, specifically getting information
57 * about where to boot from, what pathnames, etc.
58 *
59 * This implememtation uses RARP and the bootparam RPC.
60 * We are forced to implement RPC anyway (to get file handles)
61 * so we might as well take advantage of it for bootparam too.
62 *
63 * The diskless boot sequence goes as follows:
64 * (1) Get our interface address using RARP
65 * (also save the address of the RARP server)
66 * (2) Get our hostname using RPC/bootparam/whoami
67 * (all boopararms RPCs to the RARP server)
68 * (3) Get the root path using RPC/bootparam/getfile
69 * (4) Get the root file handle using RPC/mountd
70 * (5) Get the swap path using RPC/bootparam/getfile
71 * (6) Get the swap file handle using RPC/mountd
72 *
73 * (This happens to be the way Sun does it too.)
74 */
75
76 /* bootparam RPC */
77 static int bp_whoami __P((struct sockaddr_in *bpsin,
78 struct in_addr *my_ip, struct in_addr *gw_ip));
79 static int bp_getfile __P((struct sockaddr_in *bpsin, char *key,
80 struct sockaddr_in *mdsin, char *servname, char *path));
81
82 /* mountd RPC */
83 static int md_mount __P((struct sockaddr_in *mdsin, char *path,
84 u_char *fh));
85
86 /* other helpers */
87 static void get_path_and_handle __P((struct sockaddr_in *bpsin,
88 char *key, struct nfs_dlmount *ndmntp));
89
90 /*
91 * Called with an empty nfs_diskless struct to be filled in.
92 */
93 int
94 nfs_boot_init(nd, procp)
95 struct nfs_diskless *nd;
96 struct proc *procp;
97 {
98 struct ifreq ireq;
99 struct in_addr my_ip, srv_ip, gw_ip;
100 struct sockaddr_in bp_sin;
101 struct sockaddr_in *sin;
102 struct ifnet *ifp;
103 struct socket *so;
104 int error, len;
105 u_short port;
106
107 #if 0
108 /*
109 * XXX time must be non-zero when we init the interface or else
110 * the arp code will wedge... (Fixed in if_ether.c -gwr)
111 */
112 if (time.tv_sec == 0)
113 time.tv_sec = 1;
114 #endif
115
116 /*
117 * Find an interface, rarp for its ip address, stuff it, the
118 * implied broadcast addr, and netmask into a nfs_diskless struct.
119 *
120 * This was moved here from nfs_vfsops.c because this procedure
121 * would be quite different if someone decides to write (i.e.) a
122 * BOOTP version of this file (might not use RARP, etc.) -gwr
123 */
124
125 /*
126 * Find a network interface.
127 * XXX - This should use the specified boot device.
128 */
129 for (ifp = ifnet; ifp; ifp = ifp->if_next)
130 if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
131 break;
132 if (ifp == NULL)
133 panic("nfs_boot: no suitable interface");
134 sprintf(ireq.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
135 printf("nfs_boot: using network interface '%s'\n",
136 ireq.ifr_name);
137
138 /*
139 * Bring up the interface.
140 */
141 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0)
142 panic("nfs_boot: socreate, error=%d", error);
143 ireq.ifr_flags = IFF_UP;
144 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp);
145 if (error)
146 panic("nfs_boot: SIFFLAGS, error=%d", error);
147
148 /*
149 * Do RARP for the interface address. Also
150 * save the server address for bootparam RPC.
151 */
152 if ((error = revarpwhoarewe(ifp, &srv_ip, &my_ip)) != 0)
153 panic("revarp failed, error=%d", error);
154 printf("nfs_boot: client=0x%x, server=0x%x\n",
155 ntohl(my_ip.s_addr), ntohl(srv_ip.s_addr));
156
157 /*
158 * Do enough of ifconfig(8) so that the chosen interface can
159 * talk to the server(s). (also get brcast addr and netmask)
160 */
161 /* Set interface address. */
162 sin = (struct sockaddr_in *)&ireq.ifr_addr;
163 sin->sin_len = sizeof(*sin);
164 sin->sin_family = AF_INET;
165 sin->sin_addr.s_addr = my_ip.s_addr;
166 error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp);
167 if (error)
168 panic("nfs_boot: set if addr, error=%d", error);
169
170 soclose(so);
171
172 /*
173 * Get client name and gateway address.
174 * RPC: bootparam/whoami
175 */
176 bp_sin.sin_len = sizeof(bp_sin);
177 bp_sin.sin_family = AF_INET;
178 bp_sin.sin_addr.s_addr = srv_ip.s_addr;
179 hostnamelen = MAXHOSTNAMELEN;
180
181 /* this returns gateway IP address */
182 error = bp_whoami(&bp_sin, &my_ip, &gw_ip);
183 if (error)
184 panic("nfs_boot: bootparam whoami, error=%d", error);
185 printf("nfs_boot: hostname=%s\n", hostname);
186
187 #ifdef NFS_BOOT_GATEWAY
188 /*
189 * XXX - This code is conditionally compiled only because
190 * many bootparam servers (in particular, SunOS 4.1.3)
191 * always set the gateway address to their own address.
192 * The bootparam server is not necessarily the gateway.
193 * We could just believe the server, and at worst you would
194 * need to delete the incorrect default route before adding
195 * the correct one, but for simplicity, ignore the gateway.
196 * If your server is OK, you can turn on this option.
197 *
198 * If the gateway address is set, add a default route.
199 * (The mountd RPCs may go across a gateway.)
200 */
201 if (gw_ip.s_addr) {
202 struct sockaddr dst, gw, mask;
203 /* Destination: (default) */
204 bzero(&dst, sizeof(dst));
205 dst.sa_len = sizeof(dst);
206 dst.sa_family = AF_INET;
207 /* Gateway: */
208 bzero(&gw, sizeof(gw));
209 sin = (struct sockaddr_in *)&gw;
210 sin->sin_len = sizeof(gw);
211 sin->sin_family = AF_INET;
212 sin->sin_addr.s_addr = gw_ip.s_addr;
213 /* Mask: (zero length) */
214 bzero(&mask, sizeof(mask));
215
216 printf("nfs_boot: gateway=0x%x\n", ntohl(gw_ip.s_addr));
217 /* add, dest, gw, mask, flags, 0 */
218 error = rtrequest(RTM_ADD, &dst, (struct sockaddr *)&gw,
219 &mask, (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL);
220 if (error)
221 printf("nfs_boot: add route, error=%d\n", error);
222 }
223 #endif
224
225 get_path_and_handle(&bp_sin, "root", &nd->nd_root);
226 get_path_and_handle(&bp_sin, "swap", &nd->nd_swap);
227
228 return (0);
229 }
230
231 static void
232 get_path_and_handle(bpsin, key, ndmntp)
233 struct sockaddr_in *bpsin; /* bootparam server */
234 char *key; /* root or swap */
235 struct nfs_dlmount *ndmntp; /* output */
236 {
237 char pathname[MAXPATHLEN];
238 char *sp, *dp, *endp;
239 int error;
240
241 /*
242 * Get server:pathname for "key" (root or swap)
243 * using RPC to bootparam/getfile
244 */
245 error = bp_getfile(bpsin, key, &ndmntp->ndm_saddr,
246 ndmntp->ndm_host, pathname);
247 if (error)
248 panic("nfs_boot: bootparam get %s: %d", key, error);
249 printf("%s on %s:%s\n", key, ndmntp->ndm_host, pathname);
250
251 /*
252 * Get file handle for "key" (root or swap)
253 * using RPC to mountd/mount
254 */
255 error = md_mount(&ndmntp->ndm_saddr, pathname, ndmntp->ndm_fh);
256 if (error)
257 panic("nfs_boot: mountd %s, error=%d", key, error);
258
259 /* Construct remote path (for getmntinfo(3)) */
260 dp = ndmntp->ndm_host;
261 endp = dp + MNAMELEN - 1;
262 dp += strlen(dp);
263 *dp++ = ':';
264 for (sp = pathname; *sp && dp < endp;)
265 *dp++ = *sp++;
266 *dp = '\0';
267
268 }
269
270
271 /*
272 * Get an mbuf with the given length, and
273 * initialize the pkthdr length field.
274 */
275 static struct mbuf *
276 m_get_len(int msg_len)
277 {
278 struct mbuf *m;
279 m = m_gethdr(M_WAIT, MT_DATA);
280 if (m == NULL)
281 return NULL;
282 if (msg_len > MHLEN) {
283 if (msg_len > MCLBYTES)
284 panic("nfs_boot: msg_len > MCLBYTES");
285 MCLGET(m, M_WAIT);
286 if (m == NULL)
287 return NULL;
288 }
289 m->m_len = msg_len;
290 m->m_pkthdr.len = m->m_len;
291 return (m);
292 }
293
294
295 /*
296 * String representation for RPC.
297 */
298 struct rpc_string {
299 u_long len; /* length without null or padding */
300 u_char data[4]; /* data (longer, of course) */
301 /* data is padded to a long-word boundary */
302 };
303 /* Compute space used given string length. */
304 #define RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3))
305
306 /*
307 * Inet address in RPC messages
308 * (Note, really four longs, NOT chars. Blech.)
309 */
310 struct bp_inaddr {
311 u_long atype;
312 long addr[4];
313 };
314
315
316 /*
317 * RPC definitions for bootparamd
318 * (XXX - move to a header file?)
319 */
320 #define BOOTPARAM_PROG 100026
321 #define BOOTPARAM_VERS 1
322 #define BOOTPARAM_WHOAMI 1
323 #define BOOTPARAM_GETFILE 2
324
325
326 /*
327 * RPC: bootparam/whoami
328 * Given client IP address, get:
329 * client name (hostname)
330 * domain name (domainname)
331 * gateway address
332 *
333 * Setting the hostname and domainname here may be somewhat
334 * controvercial, but it is so easy to do it here. -gwr
335 */
336 static int
337 bp_whoami(bpsin, my_ip, gw_ip)
338 struct sockaddr_in *bpsin;
339 struct in_addr *my_ip;
340 struct in_addr *gw_ip;
341 {
342 /* The RPC structures */
343 struct bp_inaddr *bia;
344 struct rpc_string *str;
345 struct mbuf *m;
346 struct sockaddr_in *sin;
347 int error, msg_len;
348 int cn_len, dn_len;
349 u_char *p;
350
351 /*
352 * Get message buffer of sufficient size.
353 */
354 msg_len = sizeof(*bia);
355 m = m_get_len(msg_len);
356 if (m == NULL)
357 return ENOBUFS;
358
359 /*
360 * Build request message.
361 */
362 /* client IP address */
363 bia = mtod(m, struct bp_inaddr *);
364 bia->atype = htonl(1);
365 p = (u_char*)my_ip; /* ugh! */
366 bia->addr[0] = htonl(*p);
367 p++;
368 bia->addr[1] = htonl(*p);
369 p++;
370 bia->addr[2] = htonl(*p);
371 p++;
372 bia->addr[3] = htonl(*p);
373 p++;
374
375 /* RPC: bootparam/whoami */
376 error = krpc_call((struct sockaddr *)bpsin,
377 BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_WHOAMI, &m);
378 if (error)
379 return error;
380
381 /*
382 * Parse result message.
383 */
384 msg_len = m->m_len;
385 p = mtod(m, char *);
386
387 /* client name */
388 if (msg_len < sizeof(*str))
389 goto bad;
390 str = (struct rpc_string *)p;
391 cn_len = ntohl(str->len);
392 if (msg_len < cn_len)
393 goto bad;
394 if (cn_len >= MAXHOSTNAMELEN)
395 goto bad;
396 bcopy(str->data, hostname, cn_len);
397 hostname[cn_len] = '\0';
398 hostnamelen = cn_len;
399 p += RPC_STR_SIZE(cn_len);
400 msg_len -= RPC_STR_SIZE(cn_len);
401
402 /* domain name */
403 if (msg_len < sizeof(*str))
404 goto bad;
405 str = (struct rpc_string *)p;
406 dn_len = ntohl(str->len);
407 if (msg_len < dn_len)
408 goto bad;
409 if (dn_len >= MAXHOSTNAMELEN)
410 goto bad;
411 bcopy(str->data, domainname, dn_len);
412 domainname[dn_len] = '\0';
413 domainnamelen = dn_len;
414 p += RPC_STR_SIZE(dn_len);
415 msg_len -= RPC_STR_SIZE(dn_len);
416
417 /* gateway address */
418 if (msg_len < sizeof(*bia))
419 goto bad;
420 bia = (struct bp_inaddr *)p;
421 if (bia->atype != htonl(1))
422 goto bad;
423 p = (u_char*)gw_ip;
424 *p++ = ntohl(bia->addr[0]);
425 *p++ = ntohl(bia->addr[1]);
426 *p++ = ntohl(bia->addr[2]);
427 *p++ = ntohl(bia->addr[3]);
428 goto out;
429
430 bad:
431 printf("nfs_boot: bootparam_whoami: bad reply\n");
432 error = EBADRPC;
433
434 out:
435 m_freem(m);
436 return(error);
437 }
438
439
440 /*
441 * RPC: bootparam/getfile
442 * Given client name and file "key", get:
443 * server name
444 * server IP address
445 * server pathname
446 */
447 static int
448 bp_getfile(bpsin, key, md_sin, serv_name, pathname)
449 struct sockaddr_in *bpsin;
450 char *key;
451 struct sockaddr_in *md_sin;
452 char *serv_name;
453 char *pathname;
454 {
455 struct rpc_string *str;
456 struct mbuf *m;
457 struct bp_inaddr *bia;
458 struct sockaddr_in *sin;
459 u_char *p, *q;
460 int error, msg_len;
461 int cn_len, key_len, sn_len, path_len;
462
463 /*
464 * Get message buffer of sufficient size.
465 */
466 cn_len = hostnamelen;
467 key_len = strlen(key);
468 msg_len = 0;
469 msg_len += RPC_STR_SIZE(cn_len);
470 msg_len += RPC_STR_SIZE(key_len);
471 m = m_get_len(msg_len);
472 if (m == NULL)
473 return ENOBUFS;
474
475 /*
476 * Build request message.
477 */
478 p = mtod(m, u_char *);
479 bzero(p, msg_len);
480 /* client name (hostname) */
481 str = (struct rpc_string *)p;
482 str->len = htonl(cn_len);
483 bcopy(hostname, str->data, cn_len);
484 p += RPC_STR_SIZE(cn_len);
485 /* key name (root or swap) */
486 str = (struct rpc_string *)p;
487 str->len = htonl(key_len);
488 bcopy(key, str->data, key_len);
489
490 /* RPC: bootparam/getfile */
491 error = krpc_call((struct sockaddr *)bpsin,
492 BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_GETFILE, &m);
493 if (error)
494 return error;
495
496 /*
497 * Parse result message.
498 */
499 p = mtod(m, u_char *);
500 msg_len = m->m_len;
501
502 /* server name */
503 if (msg_len < sizeof(*str))
504 goto bad;
505 str = (struct rpc_string *)p;
506 sn_len = ntohl(str->len);
507 if (msg_len < sn_len)
508 goto bad;
509 if (sn_len >= MNAMELEN)
510 goto bad;
511 bcopy(str->data, serv_name, sn_len);
512 serv_name[sn_len] = '\0';
513 p += RPC_STR_SIZE(sn_len);
514 msg_len -= RPC_STR_SIZE(sn_len);
515
516 /* server IP address (mountd) */
517 if (msg_len < sizeof(*bia))
518 goto bad;
519 bia = (struct bp_inaddr *)p;
520 if (bia->atype != htonl(1))
521 goto bad;
522 sin = md_sin;
523 sin->sin_len = sizeof(*sin);
524 sin->sin_family = AF_INET;
525 q = (u_char*) &sin->sin_addr;
526 *q++ = ntohl(bia->addr[0]);
527 *q++ = ntohl(bia->addr[1]);
528 *q++ = ntohl(bia->addr[2]);
529 *q++ = ntohl(bia->addr[3]);
530 p += sizeof(*bia);
531 msg_len -= sizeof(*bia);
532
533 /* server pathname */
534 if (msg_len < sizeof(*str))
535 goto bad;
536 str = (struct rpc_string *)p;
537 path_len = ntohl(str->len);
538 if (msg_len < path_len)
539 goto bad;
540 if (path_len >= MAXPATHLEN)
541 goto bad;
542 bcopy(str->data, pathname, path_len);
543 pathname[path_len] = '\0';
544 goto out;
545
546 bad:
547 printf("nfs_boot: bootparam_getfile: bad reply\n");
548 error = EBADRPC;
549
550 out:
551 m_freem(m);
552 return(0);
553 }
554
555
556 /*
557 * RPC: mountd/mount
558 * Given a server pathname, get an NFS file handle.
559 * Also, sets sin->sin_port to the NFS service port.
560 */
561 static int
562 md_mount(mdsin, path, fhp)
563 struct sockaddr_in *mdsin; /* mountd server address */
564 char *path;
565 u_char *fhp;
566 {
567 /* The RPC structures */
568 struct rpc_string *str;
569 struct rdata {
570 u_long errno;
571 u_char fh[NFS_FHSIZE];
572 } *rdata;
573 struct mbuf *m;
574 int error, mlen, slen;
575
576 slen = strlen(path);
577 mlen = RPC_STR_SIZE(slen);
578
579 m = m_get_len(mlen);
580 if (m == NULL)
581 return ENOBUFS;
582 str = mtod(m, struct rpc_string *);
583 str->len = htonl(slen);
584 bcopy(path, str->data, slen);
585
586 /* Do RPC to mountd. */
587 error = krpc_call((struct sockaddr *)mdsin,
588 RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT, &m);
589 if (error)
590 return error; /* message already freed */
591
592 mlen = m->m_len;
593 if (mlen < sizeof(*rdata))
594 goto bad;
595 rdata = mtod(m, struct rdata *);
596 error = ntohl(rdata->errno);
597 if (error)
598 goto bad;
599 bcopy(rdata->fh, fhp, NFS_FHSIZE);
600
601 /* Set port number for NFS use. */
602 error = krpc_portmap((struct sockaddr *)mdsin,
603 NFS_PROG, NFS_VER2, &mdsin->sin_port);
604 goto out;
605
606 bad:
607 error = EBADRPC;
608
609 out:
610 m_freem(m);
611 return error;
612 }
613
614 #else /* NETHER */
615
616 int nfs_boot_init(nd, procp)
617 struct nfs_diskless *nd;
618 struct proc *procp;
619 {
620 panic("nfs_boot_init: no ether");
621 }
622
623 #endif /* NETHER */
624