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