nfs_boot.c revision 1.3 1 /*
2 * Copyright (c) 1994 Adam Glass, Gordon Ross
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the authors may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * $Id: nfs_boot.c,v 1.3 1994/06/13 15:28:59 gwr Exp $
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 int error;
229
230 /*
231 * Get server:pathname for "key" (root or swap)
232 * using RPC to bootparam/getfile
233 */
234 error = bp_getfile(bpsin, key,
235 &ndmntp->ndm_saddr,
236 ndmntp->ndm_host,
237 pathname);
238 if (error)
239 panic("nfs_boot: bootparam get %s: %d", key, error);
240 printf("%s on %s:%s\n", key, ndmntp->ndm_host, pathname);
241
242 /*
243 * Get file handle for "key" (root or swap)
244 * using RPC to mountd/mount
245 */
246 error = md_mount(&ndmntp->ndm_saddr,
247 pathname,
248 ndmntp->ndm_fh);
249 if (error)
250 panic("nfs_boot: mountd %s, error=%d", key, error);
251 }
252
253
254 /*
255 * Get an mbuf with the given length, and
256 * initialize the pkthdr length field.
257 */
258 static struct mbuf *
259 m_get_len(int msg_len)
260 {
261 struct mbuf *m;
262 m = m_gethdr(M_WAIT, MT_DATA);
263 if (m == NULL)
264 return NULL;
265 if (msg_len > MHLEN) {
266 if (msg_len > MCLBYTES)
267 panic("nfs_boot: msg_len > MCLBYTES");
268 MCLGET(m, M_WAIT);
269 if (m == NULL)
270 return NULL;
271 }
272 m->m_len = msg_len;
273 m->m_pkthdr.len = m->m_len;
274 return (m);
275 }
276
277
278 /*
279 * String representation for RPC.
280 */
281 struct rpc_string {
282 u_long len; /* length without null or padding */
283 u_char data[4]; /* data (longer, of course) */
284 /* data is padded to a long-word boundary */
285 };
286 /* Compute space used given string length. */
287 #define RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3))
288
289 /*
290 * Inet address in RPC messages
291 * (Note, really four longs, NOT chars. Blech.)
292 */
293 struct bp_inaddr {
294 u_long atype;
295 long addr[4];
296 };
297
298
299 /*
300 * RPC definitions for bootparamd
301 * (XXX - move to a header file?)
302 */
303 #define BOOTPARAM_PROG 100026
304 #define BOOTPARAM_VERS 1
305 #define BOOTPARAM_WHOAMI 1
306 #define BOOTPARAM_GETFILE 2
307
308
309 /*
310 * RPC: bootparam/whoami
311 * Given client IP address, get:
312 * client name (hostname)
313 * domain name (domainname)
314 * gateway address
315 *
316 * Setting the hostname and domainname here may be somewhat
317 * controvercial, but it is so easy to do it here. -gwr
318 */
319 static int
320 bp_whoami(struct sockaddr_in *bpsin,
321 struct in_addr *my_ip,
322 struct in_addr *gw_ip)
323 {
324 /* The RPC structures */
325 struct bp_inaddr *bia;
326 struct rpc_string *str;
327 struct mbuf *m;
328 struct sockaddr_in *sin;
329 int error, msg_len;
330 int cn_len, dn_len;
331 u_char *p;
332
333 /*
334 * Get message buffer of sufficient size.
335 */
336 msg_len = sizeof(*bia);
337 m = m_get_len(msg_len);
338 if (m == NULL)
339 return ENOBUFS;
340
341 /*
342 * Build request message.
343 */
344 /* client IP address */
345 bia = mtod(m, struct bp_inaddr *);
346 bia->atype = htonl(1);
347 p = (u_char*)my_ip; /* ugh! */
348 bia->addr[0] = htonl(*p); p++;
349 bia->addr[1] = htonl(*p); p++;
350 bia->addr[2] = htonl(*p); p++;
351 bia->addr[3] = htonl(*p); p++;
352
353 /* RPC: bootparam/whoami */
354 error = krpc_call((struct sockaddr *)bpsin,
355 BOOTPARAM_PROG, BOOTPARAM_VERS,
356 BOOTPARAM_WHOAMI, &m);
357 if (error)
358 return error;
359
360 /*
361 * Parse result message.
362 */
363 msg_len = m->m_len;
364 p = mtod(m, char *);
365
366 /* client name */
367 if (msg_len < sizeof(*str))
368 goto bad;
369 str = (struct rpc_string *)p;
370 cn_len = ntohl(str->len);
371 if (msg_len < cn_len)
372 goto bad;
373 if (cn_len >= MAXHOSTNAMELEN)
374 goto bad;
375 bcopy(str->data, hostname, cn_len);
376 hostname[cn_len] = '\0';
377 hostnamelen = cn_len;
378 p += RPC_STR_SIZE(cn_len);
379 msg_len -= RPC_STR_SIZE(cn_len);
380
381 /* domain name */
382 if (msg_len < sizeof(*str))
383 goto bad;
384 str = (struct rpc_string *)p;
385 dn_len = ntohl(str->len);
386 if (msg_len < dn_len)
387 goto bad;
388 if (dn_len >= MAXHOSTNAMELEN)
389 goto bad;
390 bcopy(str->data, domainname, dn_len);
391 domainname[dn_len] = '\0';
392 domainnamelen = dn_len;
393 p += RPC_STR_SIZE(dn_len);
394 msg_len -= RPC_STR_SIZE(dn_len);
395
396 /* gateway address */
397 if (msg_len < sizeof(*bia))
398 goto bad;
399 bia = (struct bp_inaddr *)p;
400 if (bia->atype != htonl(1))
401 goto bad;
402 p = (u_char*)gw_ip;
403 *p++ = ntohl(bia->addr[0]);
404 *p++ = ntohl(bia->addr[1]);
405 *p++ = ntohl(bia->addr[2]);
406 *p++ = ntohl(bia->addr[3]);
407 goto out;
408
409 bad:
410 printf("nfs_boot: bootparam_whoami: bad reply\n");
411 error = EBADRPC;
412
413 out:
414 m_freem(m);
415 return(error);
416 }
417
418
419 /*
420 * RPC: bootparam/getfile
421 * Given client name and file "key", get:
422 * server name
423 * server IP address
424 * server pathname
425 */
426 static int
427 bp_getfile(struct sockaddr_in *bpsin, char *key,
428 struct sockaddr_in *md_sin, char *serv_name, char *pathname)
429 {
430 struct rpc_string *str;
431 struct mbuf *m;
432 struct bp_inaddr *bia;
433 struct sockaddr_in *sin;
434 u_char *p, *q;
435 int error, msg_len;
436 int cn_len, key_len, sn_len, path_len;
437
438 /*
439 * Get message buffer of sufficient size.
440 */
441 cn_len = hostnamelen;
442 key_len = strlen(key);
443 msg_len = 0;
444 msg_len += RPC_STR_SIZE(cn_len);
445 msg_len += RPC_STR_SIZE(key_len);
446 m = m_get_len(msg_len);
447 if (m == NULL)
448 return ENOBUFS;
449
450 /*
451 * Build request message.
452 */
453 p = mtod(m, u_char *);
454 bzero(p, msg_len);
455 /* client name (hostname) */
456 str = (struct rpc_string *)p;
457 str->len = htonl(cn_len);
458 bcopy(hostname, str->data, cn_len);
459 p += RPC_STR_SIZE(cn_len);
460 /* key name (root or swap) */
461 str = (struct rpc_string *)p;
462 str->len = htonl(key_len);
463 bcopy(key, str->data, key_len);
464
465 /* RPC: bootparam/getfile */
466 error = krpc_call((struct sockaddr *)bpsin,
467 BOOTPARAM_PROG, BOOTPARAM_VERS,
468 BOOTPARAM_GETFILE, &m);
469 if (error)
470 return error;
471
472 /*
473 * Parse result message.
474 */
475 p = mtod(m, u_char *);
476 msg_len = m->m_len;
477
478 /* server name */
479 if (msg_len < sizeof(*str))
480 goto bad;
481 str = (struct rpc_string *)p;
482 sn_len = ntohl(str->len);
483 if (msg_len < sn_len)
484 goto bad;
485 if (sn_len >= MNAMELEN)
486 goto bad;
487 bcopy(str->data, serv_name, sn_len);
488 serv_name[sn_len] = '\0';
489 p += RPC_STR_SIZE(sn_len);
490 msg_len -= RPC_STR_SIZE(sn_len);
491
492 /* server IP address (mountd) */
493 if (msg_len < sizeof(*bia))
494 goto bad;
495 bia = (struct bp_inaddr *)p;
496 if (bia->atype != htonl(1))
497 goto bad;
498 sin = md_sin;
499 sin->sin_len = sizeof(*sin);
500 sin->sin_family = AF_INET;
501 q = (u_char*) &sin->sin_addr;
502 *q++ = ntohl(bia->addr[0]);
503 *q++ = ntohl(bia->addr[1]);
504 *q++ = ntohl(bia->addr[2]);
505 *q++ = ntohl(bia->addr[3]);
506 p += sizeof(*bia);
507 msg_len -= sizeof(*bia);
508
509 /* server pathname */
510 if (msg_len < sizeof(*str))
511 goto bad;
512 str = (struct rpc_string *)p;
513 path_len = ntohl(str->len);
514 if (msg_len < path_len)
515 goto bad;
516 if (path_len >= MAXPATHLEN)
517 goto bad;
518 bcopy(str->data, pathname, path_len);
519 pathname[path_len] = '\0';
520 goto out;
521
522 bad:
523 printf("nfs_boot: bootparam_getfile: bad reply\n");
524 error = EBADRPC;
525
526 out:
527 m_freem(m);
528 return(0);
529 }
530
531
532 /*
533 * RPC: mountd/mount
534 * Given a server pathname, get an NFS file handle.
535 * Also, sets sin->sin_port to the NFS service port.
536 */
537 static int
538 md_mount(mdsin, path, fhp)
539 struct sockaddr_in *mdsin; /* mountd server address */
540 char *path;
541 u_char *fhp;
542 {
543 /* The RPC structures */
544 struct rpc_string *str;
545 struct rdata {
546 u_long errno;
547 u_char fh[NFS_FHSIZE];
548 } *rdata;
549 struct mbuf *m;
550 int error, mlen, slen;
551
552 slen = strlen(path);
553 mlen = RPC_STR_SIZE(slen);
554
555 m = m_get_len(mlen);
556 if (m == NULL)
557 return ENOBUFS;
558 str = mtod(m, struct rpc_string *);
559 str->len = htonl(slen);
560 bcopy(path, str->data, slen);
561
562 /* Do RPC to mountd. */
563 error = krpc_call((struct sockaddr *)mdsin,
564 RPCPROG_MNT, RPCMNT_VER1,
565 RPCMNT_MOUNT, &m);
566 if (error)
567 return error; /* message already freed */
568
569 mlen = m->m_len;
570 if (mlen < sizeof(*rdata))
571 goto bad;
572 rdata = mtod(m, struct rdata *);
573 error = ntohl(rdata->errno);
574 if (error)
575 goto bad;
576 bcopy(rdata->fh, fhp, NFS_FHSIZE);
577
578 /* Set port number for NFS use. */
579 error = krpc_portmap((struct sockaddr *)mdsin,
580 NFS_PROG, NFS_VER2,
581 &mdsin->sin_port);
582 goto out;
583
584 bad:
585 error = EBADRPC;
586
587 out:
588 m_freem(m);
589 return error;
590 }
591