nfs_boot.c revision 1.39 1 /* $NetBSD: nfs_boot.c,v 1.39 1997/09/30 20:44:31 drochner Exp $ */
2
3 /*-
4 * Copyright (c) 1995, 1997 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Adam Glass and Gordon W. Ross.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Support for NFS diskless booting, specifically getting information
41 * about where to mount root from, what pathnames, etc.
42 */
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/conf.h>
48 #include <sys/device.h>
49 #include <sys/ioctl.h>
50 #include <sys/proc.h>
51 #include <sys/mount.h>
52 #include <sys/mbuf.h>
53 #include <sys/reboot.h>
54 #include <sys/socket.h>
55 #include <sys/socketvar.h>
56
57 #include <net/if.h>
58 #include <net/route.h>
59 #include <net/if_ether.h>
60 #include <net/if_types.h>
61
62 #include <netinet/in.h>
63 #include <netinet/if_inarp.h>
64
65 #include <nfs/rpcv2.h>
66 #include <nfs/krpc.h>
67 #include <nfs/xdr_subs.h>
68
69 #include <nfs/nfsproto.h>
70 #include <nfs/nfsdiskless.h>
71
72 #include "arp.h"
73 #if NARP == 0
74
75 int nfs_boot_init(nd, procp)
76 struct nfs_diskless *nd;
77 struct proc *procp;
78 {
79 printf("nfs_boot: NARP == 0\n");
80 return (ENXIO);
81 }
82
83 #else /* NARP */
84
85 /*
86 * There are two implementations of NFS diskless boot.
87 * One implementation uses BOOTP (RFC951, RFC1048),
88 * the other uses Sun RPC/bootparams. See the files:
89 * nfs_bootp.c: BOOTP (RFC951, RFC1048)
90 * nfs_bootsun.c: Sun RPC/bootparams
91 *
92 * The variable nfs_boot_rfc951 selects which to use.
93 * This is defined as BSS so machine-dependent code
94 * may provide a data definition to override this.
95 */
96 int nfs_boot_rfc951; /* 1: BOOTP. 0: RARP/SUNRPC */
97
98 /* mountd RPC */
99 static int md_mount __P((struct sockaddr_in *mdsin, char *path,
100 struct nfs_args *argp));
101
102 static void nfs_boot_defrt __P((struct in_addr *));
103 static int nfs_boot_getfh __P((struct nfs_dlmount *ndm));
104
105
106 /*
107 * Called with an empty nfs_diskless struct to be filled in.
108 * Find an interface, determine its ip address (etc.) and
109 * save all the boot parameters in the nfs_diskless struct.
110 */
111 int
112 nfs_boot_init(nd, procp)
113 struct nfs_diskless *nd;
114 struct proc *procp;
115 {
116 struct ifnet *ifp;
117 int error;
118
119 /*
120 * Find the network interface.
121 */
122 ifp = ifunit(root_device->dv_xname);
123 if (ifp == NULL) {
124 printf("nfs_boot: '%s' not found\n",
125 root_device->dv_xname);
126 return (ENXIO);
127 }
128 if (ifp->if_type != IFT_ETHER) {
129 printf("nfs_boot: unknown I/F type %d\n", ifp->if_type);
130 return (ENODEV); /* Op. not supported by device */
131 }
132
133 if (nfs_boot_rfc951) {
134 printf("nfs_boot: trying BOOTP/DHCP\n");
135 error = nfs_bootdhcp(ifp, nd, procp);
136 } else {
137 printf("nfs_boot: trying RARP (and RPC/bootparam)\n");
138 error = nfs_bootparam(ifp, nd, procp);
139 }
140 if (error)
141 return (error);
142
143 /*
144 * If the gateway address is set, add a default route.
145 * (The mountd RPCs may go across a gateway.)
146 */
147 if (nd->nd_gwip.s_addr)
148 nfs_boot_defrt(&nd->nd_gwip);
149
150 /*
151 * Now fetch the NFS file handles as appropriate.
152 */
153 error = nfs_boot_getfh(&nd->nd_root);
154 if (error)
155 return (error);
156
157 #if 0 /* swap now comes in from swapctl(2) */
158 if (nd->nd_swap.ndm_saddr.sa_len) {
159 error = nfs_boot_getfh(&nd->nd_swap);
160 if (error) {
161 printf("nfs_boot: warning: getfh(swap), error=%d\n", error);
162 /* Just ignore the error */
163 error = 0;
164 }
165 }
166 #endif
167
168 return (error);
169 }
170
171 int nfs_boot_setrecvtimo(so)
172 struct socket *so;
173 {
174 struct mbuf *m;
175 struct timeval *tv;
176
177 m = m_get(M_WAIT, MT_SOOPTS);
178 tv = mtod(m, struct timeval *);
179 m->m_len = sizeof(*tv);
180 tv->tv_sec = 1;
181 tv->tv_usec = 0;
182 return(sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m));
183 }
184
185 int nfs_boot_enbroadcast(so)
186 struct socket *so;
187 {
188 struct mbuf *m;
189 int32_t *on;
190
191 m = m_get(M_WAIT, MT_SOOPTS);
192 on = mtod(m, int32_t *);
193 m->m_len = sizeof(*on);
194 *on = 1;
195 return(sosetopt(so, SOL_SOCKET, SO_BROADCAST, m));
196 }
197
198 int nfs_boot_sobind_ipport(so, port)
199 struct socket *so;
200 u_int16_t port;
201 {
202 struct mbuf *m;
203 struct sockaddr_in *sin;
204 int error;
205
206 m = m_getclr(M_WAIT, MT_SONAME);
207 sin = mtod(m, struct sockaddr_in *);
208 sin->sin_len = m->m_len = sizeof(*sin);
209 sin->sin_family = AF_INET;
210 sin->sin_addr.s_addr = INADDR_ANY;
211 sin->sin_port = htons(port);
212 error = sobind(so, m);
213 m_freem(m);
214 return(error);
215 }
216
217 /*
218 * What is the longest we will wait before re-sending a request?
219 * Note this is also the frequency of "timeout" messages.
220 * The re-send loop counts up linearly to this maximum, so the
221 * first complaint will happen after (1+2+3+4+5)=15 seconds.
222 */
223 #define MAX_RESEND_DELAY 5 /* seconds */
224 #define TOTAL_TIMEOUT 30 /* seconds */
225
226 int nfs_boot_sendrecv(so, nam, sndproc, snd, rcvproc, rcv,
227 from_p, context)
228 struct socket *so;
229 struct mbuf *nam;
230 int (*sndproc) __P((struct mbuf*, void*, int));
231 struct mbuf *snd;
232 int (*rcvproc) __P((struct mbuf*, void*));
233 struct mbuf **rcv, **from_p;
234 void *context;
235 {
236 int error, rcvflg, timo, secs, waited;
237 struct mbuf *m, *from;
238 struct uio uio;
239
240 /* Free at end if not null. */
241 from = NULL;
242
243 /*
244 * Send it, repeatedly, until a reply is received,
245 * but delay each re-send by an increasing amount.
246 * If the delay hits the maximum, start complaining.
247 */
248 waited = timo = 0;
249 send_again:
250 waited += timo;
251 if (waited >= TOTAL_TIMEOUT)
252 return(ETIMEDOUT);
253
254 /* Determine new timeout. */
255 if (timo < MAX_RESEND_DELAY)
256 timo++;
257 else
258 printf("nfs_boot: timeout...\n");
259
260 if (sndproc) {
261 error = (*sndproc)(snd, context, waited);
262 if (error)
263 goto out;
264 }
265
266 /* Send request (or re-send). */
267 m = m_copypacket(snd, M_WAIT);
268 if (m == NULL) {
269 error = ENOBUFS;
270 goto out;
271 }
272 error = sosend(so, nam, NULL, m, NULL, 0);
273 if (error) {
274 printf("nfs_boot: sosend: %d\n", error);
275 goto out;
276 }
277 m = NULL;
278
279 /*
280 * Wait for up to timo seconds for a reply.
281 * The socket receive timeout was set to 1 second.
282 */
283
284 secs = timo;
285 for (;;) {
286 if (from) {
287 m_freem(from);
288 from = NULL;
289 }
290 if (m) {
291 m_freem(m);
292 m = NULL;
293 }
294 uio.uio_resid = 1 << 16; /* ??? */
295 rcvflg = 0;
296 error = soreceive(so, &from, &uio, &m, NULL, &rcvflg);
297 if (error == EWOULDBLOCK) {
298 if (--secs <= 0)
299 goto send_again;
300 continue;
301 }
302 if (error)
303 goto out;
304 #ifdef DIAGNOSTIC
305 if (!m || !(m->m_flags & M_PKTHDR)
306 || (1 << 16) - uio.uio_resid != m->m_pkthdr.len)
307 panic("nfs_boot_sendrecv: return size");
308 #endif
309
310 if ((*rcvproc)(m, context))
311 continue;
312
313 if (rcv)
314 *rcv = m;
315 else
316 m_freem(m);
317 if (from_p) {
318 *from_p = from;
319 from = NULL;
320 }
321 break;
322 }
323 out:
324 if (from) m_freem(from);
325 return(error);
326 }
327
328 /*
329 * Install a default route to the passed IP address.
330 */
331 static void
332 nfs_boot_defrt(gw_ip)
333 struct in_addr *gw_ip;
334 {
335 struct sockaddr dst, gw, mask;
336 struct sockaddr_in *sin;
337 int error;
338
339 /* Destination: (default) */
340 bzero((caddr_t)&dst, sizeof(dst));
341 dst.sa_len = sizeof(dst);
342 dst.sa_family = AF_INET;
343 /* Gateway: */
344 bzero((caddr_t)&gw, sizeof(gw));
345 sin = (struct sockaddr_in *)&gw;
346 sin->sin_len = sizeof(*sin);
347 sin->sin_family = AF_INET;
348 sin->sin_addr.s_addr = gw_ip->s_addr;
349 /* Mask: (zero length) */
350 /* XXX - Just pass a null pointer? */
351 bzero(&mask, sizeof(mask));
352
353 /* add, dest, gw, mask, flags, 0 */
354 error = rtrequest(RTM_ADD, &dst, &gw, &mask,
355 (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL);
356 if (error) {
357 printf("nfs_boot: add route, error=%d\n", error);
358 error = 0;
359 }
360 }
361
362 /*
363 * Get an initial NFS file handle using Sun RPC/mountd.
364 * Separate function because we used to call it twice.
365 * (once for root and once for swap)
366 */
367 static int
368 nfs_boot_getfh(ndm)
369 struct nfs_dlmount *ndm; /* output */
370 {
371 struct nfs_args *args;
372 struct sockaddr_in *sin;
373 char *pathname;
374 int error;
375 u_int16_t port;
376
377 args = &ndm->ndm_args;
378
379 /* Initialize mount args. */
380 bzero((caddr_t) args, sizeof(*args));
381 args->addr = &ndm->ndm_saddr;
382 args->addrlen = args->addr->sa_len;
383 #ifdef NFS_BOOT_TCP
384 args->sotype = SOCK_STREAM;
385 #else
386 args->sotype = SOCK_DGRAM;
387 #endif
388 args->fh = ndm->ndm_fh;
389 args->hostname = ndm->ndm_host;
390 args->flags = NFSMNT_RESVPORT | NFSMNT_NFSV3;
391
392 #ifdef NFS_BOOT_OPTIONS
393 args->flags |= NFS_BOOT_OPTIONS;
394 #endif
395 #ifdef NFS_BOOT_RWSIZE
396 /*
397 * Reduce rsize,wsize for interfaces that consistently
398 * drop fragments of long UDP messages. (i.e. wd8003).
399 * You can always change these later via remount.
400 */
401 args->flags |= NFSMNT_WSIZE | NFSMNT_RSIZE;
402 args->wsize = NFS_BOOT_RWSIZE;
403 args->rsize = NFS_BOOT_RWSIZE;
404 #endif
405
406 /*
407 * Find the pathname part of the "server:pathname"
408 * string left in ndm->ndm_host by nfs_boot_init.
409 */
410 pathname = strchr(ndm->ndm_host, ':');
411 if (pathname == 0) {
412 printf("nfs_boot: getfh - no pathname\n");
413 return (EIO);
414 }
415 pathname++;
416
417 /*
418 * Get file handle using RPC to mountd/mount
419 */
420 sin = (struct sockaddr_in *)&ndm->ndm_saddr;
421 error = md_mount(sin, pathname, args);
422 if (error) {
423 printf("nfs_boot: mountd `%s', error=%d\n",
424 ndm->ndm_host, error);
425 return (error);
426 }
427
428 /* Set port number for NFS use. */
429 /* XXX: NFS port is always 2049, right? */
430 #ifdef NFS_BOOT_TCP
431 retry:
432 #endif
433 error = krpc_portmap(sin, NFS_PROG,
434 (args->flags & NFSMNT_NFSV3) ? NFS_VER3 : NFS_VER2,
435 (args->sotype == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP,
436 &port);
437 if (port == htons(0))
438 error = EIO;
439 if (error) {
440 #ifdef NFS_BOOT_TCP
441 if (args->sotype == SOCK_STREAM) {
442 args->sotype = SOCK_DGRAM;
443 goto retry;
444 }
445 #endif
446 printf("nfs_boot: portmap NFS, error=%d\n", error);
447 return (error);
448 }
449 sin->sin_port = port;
450 return (0);
451 }
452
453
454 /*
455 * RPC: mountd/mount
456 * Given a server pathname, get an NFS file handle.
457 * Also, sets sin->sin_port to the NFS service port.
458 */
459 static int
460 md_mount(mdsin, path, argp)
461 struct sockaddr_in *mdsin; /* mountd server address */
462 char *path;
463 struct nfs_args *argp;
464 {
465 /* The RPC structures */
466 struct rdata {
467 u_int32_t errno;
468 union {
469 u_int8_t v2fh[NFSX_V2FH];
470 struct {
471 u_int32_t fhlen;
472 u_int8_t fh[1];
473 } v3fh;
474 } fh;
475 } *rdata;
476 struct mbuf *m;
477 u_int8_t *fh;
478 int minlen, error;
479 int mntver;
480
481 mntver = (argp->flags & NFSMNT_NFSV3) ? 3 : 2;
482 do {
483 /*
484 * Get port number for MOUNTD.
485 */
486 error = krpc_portmap(mdsin, RPCPROG_MNT, mntver,
487 IPPROTO_UDP, &mdsin->sin_port);
488 if (error)
489 continue;
490
491 /* This mbuf is consumed by krpc_call. */
492 m = xdr_string_encode(path, strlen(path));
493 if (m == NULL)
494 return ENOMEM;
495
496 /* Do RPC to mountd. */
497 error = krpc_call(mdsin, RPCPROG_MNT, mntver,
498 RPCMNT_MOUNT, &m, NULL);
499 if (error != EPROGMISMATCH)
500 break;
501 /* Try lower version of mountd. */
502 } while (--mntver >= 1);
503 if (error) {
504 printf("nfs_boot: mountd error=%d\n", error);
505 return error;
506 }
507 if (mntver != 3)
508 argp->flags &= ~NFSMNT_NFSV3;
509
510 /* The reply might have only the errno. */
511 if (m->m_len < 4)
512 goto bad;
513 /* Have at least errno, so check that. */
514 rdata = mtod(m, struct rdata *);
515 error = fxdr_unsigned(u_int32_t, rdata->errno);
516 if (error)
517 goto out;
518
519 /* Have errno==0, so the fh must be there. */
520 if (mntver == 3) {
521 argp->fhsize = fxdr_unsigned(u_int32_t, rdata->fh.v3fh.fhlen);
522 if (argp->fhsize > NFSX_V3FHMAX)
523 goto bad;
524 minlen = 2 * sizeof(u_int32_t) + argp->fhsize;
525 } else {
526 argp->fhsize = NFSX_V2FH;
527 minlen = sizeof(u_int32_t) + argp->fhsize;
528 }
529
530 if (m->m_len < minlen) {
531 m = m_pullup(m, minlen);
532 if (m == NULL)
533 return(EBADRPC);
534 rdata = mtod(m, struct rdata *);
535 }
536
537 fh = (mntver == 3) ?
538 rdata->fh.v3fh.fh : rdata->fh.v2fh;
539 bcopy(fh, argp->fh, argp->fhsize);
540
541 goto out;
542
543 bad:
544 error = EBADRPC;
545
546 out:
547 m_freem(m);
548 return error;
549 }
550
551 #endif /* NARP */
552