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