nfs_boot.c revision 1.33.4.2 1 /* $NetBSD: nfs_boot.c,v 1.33.4.2 1997/09/01 21:02:52 thorpej 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
60 #include <net/if_ether.h>
61 #include <net/if_types.h>
62
63 #include <netinet/in.h>
64 #include <netinet/if_inarp.h>
65
66 #include <nfs/rpcv2.h>
67 #include <nfs/nfsproto.h>
68 #include <nfs/nfs.h>
69 #include <nfs/nfsdiskless.h>
70 #include <nfs/krpc.h>
71 #include <nfs/xdr_subs.h>
72 #include <nfs/nfs_var.h>
73
74 #include "ether.h"
75 #if NETHER == 0
76 /* XXX - Do we still need this? How can I tell? -gwr */
77
78 int nfs_boot_init(nd, procp)
79 struct nfs_diskless *nd;
80 struct proc *procp;
81 {
82 printf("nfs_boot: NETHER == 0\n");
83 return (ENXIO);
84 }
85
86 int
87 nfs_boot_getfh(ndm)
88 struct nfs_dlmount *ndm;
89 {
90 return (ENXIO);
91 }
92
93 #else /* NETHER */
94
95 /*
96 * There are two implementations of NFS diskless boot.
97 * One implementation uses BOOTP (RFC951, RFC1048),
98 * the other uses Sun RPC/bootparams. See the files:
99 * nfs_bootp.c: BOOTP (RFC951, RFC1048)
100 * nfs_bootsun.c: Sun RPC/bootparams
101 *
102 * The variable nfs_boot_rfc951 selects which to use.
103 * This is defined as BSS so machine-dependent code
104 * may provide a data definition to override this.
105 */
106 int nfs_boot_rfc951; /* 0: BOOTP. 1: RARP/SUNRPC */
107
108 /* mountd RPC */
109 static int md_mount __P((struct sockaddr_in *mdsin, char *path,
110 struct nfs_args *argp));
111
112 static void nfs_boot_defrt __P((struct in_addr *));
113
114
115 /*
116 * Called with an empty nfs_diskless struct to be filled in.
117 * Find an interface, determine its ip address (etc.) and
118 * save all the boot parameters in the nfs_diskless struct.
119 */
120 int
121 nfs_boot_init(nd, procp)
122 struct nfs_diskless *nd;
123 struct proc *procp;
124 {
125 struct ifnet *ifp;
126 int error;
127
128 /*
129 * Find the network interface.
130 */
131 ifp = ifunit(root_device->dv_xname);
132 if (ifp == NULL) {
133 printf("nfs_boot: '%s' not found\n",
134 root_device->dv_xname);
135 return (ENXIO);
136 }
137 if (ifp->if_type != IFT_ETHER) {
138 printf("nfs_boot: unknown I/F type %d\n", ifp->if_type);
139 return (ENODEV); /* Op. not supported by device */
140 }
141
142 if (nfs_boot_rfc951) {
143 printf("nfs_boot: trying BOOTP/DHCP\n");
144 error = nfs_bootdhcp(ifp, nd, procp);
145 } else {
146 printf("nfs_boot: trying RARP (and RPC/bootparam)\n");
147 error = nfs_bootparam(ifp, nd, procp);
148 }
149 if (error)
150 return (error);
151
152 /*
153 * If the gateway address is set, add a default route.
154 * (The mountd RPCs may go across a gateway.)
155 */
156 if (nd->nd_gwip.s_addr)
157 nfs_boot_defrt(&nd->nd_gwip);
158
159 return (0);
160 }
161
162 /*
163 * Install a default route to the passed IP address.
164 */
165 static void
166 nfs_boot_defrt(gw_ip)
167 struct in_addr *gw_ip;
168 {
169 struct sockaddr dst, gw, mask;
170 struct sockaddr_in *sin;
171 int error;
172
173 /* Destination: (default) */
174 bzero((caddr_t)&dst, sizeof(dst));
175 dst.sa_len = sizeof(dst);
176 dst.sa_family = AF_INET;
177 /* Gateway: */
178 bzero((caddr_t)&gw, sizeof(gw));
179 sin = (struct sockaddr_in *)&gw;
180 sin->sin_len = sizeof(*sin);
181 sin->sin_family = AF_INET;
182 sin->sin_addr.s_addr = gw_ip->s_addr;
183 /* Mask: (zero length) */
184 /* XXX - Just pass a null pointer? */
185 bzero(&mask, sizeof(mask));
186
187 /* add, dest, gw, mask, flags, 0 */
188 error = rtrequest(RTM_ADD, &dst, &gw, &mask,
189 (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL);
190 if (error) {
191 printf("nfs_boot: add route, error=%d\n", error);
192 error = 0;
193 }
194 }
195
196 /*
197 * Get an initial NFS file handle using Sun RPC/mountd.
198 * Separate function because we used to call it twice.
199 * (once for root and once for swap)
200 */
201 int
202 nfs_boot_getfh(ndm)
203 struct nfs_dlmount *ndm; /* output */
204 {
205 struct nfs_args *args;
206 struct sockaddr_in *sin;
207 char *pathname;
208 int error;
209 u_int16_t port;
210
211 args = &ndm->ndm_args;
212
213 /* Initialize mount args. */
214 bzero((caddr_t) args, sizeof(*args));
215 args->addr = &ndm->ndm_saddr;
216 args->addrlen = args->addr->sa_len;
217 #ifdef NFS_BOOT_TCP
218 args->sotype = SOCK_STREAM;
219 #else
220 args->sotype = SOCK_DGRAM;
221 #endif
222 args->fh = ndm->ndm_fh;
223 args->hostname = ndm->ndm_host;
224 args->flags = NFSMNT_RESVPORT | NFSMNT_NFSV3;
225
226 #ifdef NFS_BOOT_OPTIONS
227 args->flags |= NFS_BOOT_OPTIONS;
228 #endif
229 #ifdef NFS_BOOT_RWSIZE
230 /*
231 * Reduce rsize,wsize for interfaces that consistently
232 * drop fragments of long UDP messages. (i.e. wd8003).
233 * You can always change these later via remount.
234 */
235 args->flags |= NFSMNT_WSIZE | NFSMNT_RSIZE;
236 args->wsize = NFS_BOOT_RWSIZE;
237 args->rsize = NFS_BOOT_RWSIZE;
238 #endif
239
240 /*
241 * Find the pathname part of the "server:pathname"
242 * string left in ndm->ndm_host by nfs_boot_init.
243 */
244 pathname = strchr(ndm->ndm_host, ':');
245 if (pathname == 0) {
246 printf("nfs_boot: getfh - no pathname\n");
247 return (EIO);
248 }
249 pathname++;
250
251 /*
252 * Get file handle using RPC to mountd/mount
253 */
254 sin = (struct sockaddr_in *)&ndm->ndm_saddr;
255 error = md_mount(sin, pathname, args);
256 if (error) {
257 printf("nfs_boot: mountd `%s', error=%d\n",
258 ndm->ndm_host, error);
259 return (error);
260 }
261
262 /* Set port number for NFS use. */
263 /* XXX: NFS port is always 2049, right? */
264 #ifdef NFS_BOOT_TCP
265 retry:
266 #endif
267 error = krpc_portmap(sin, NFS_PROG,
268 (args->flags & NFSMNT_NFSV3) ? NFS_VER3 : NFS_VER2,
269 (args->sotype == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP,
270 &port);
271 if (port == htons(0))
272 error = EIO;
273 if (error) {
274 #ifdef NFS_BOOT_TCP
275 if (args->sotype == SOCK_STREAM) {
276 args->sotype = SOCK_DGRAM;
277 goto retry;
278 }
279 #endif
280 printf("nfs_boot: portmap NFS, error=%d\n", error);
281 return (error);
282 }
283 sin->sin_port = port;
284 return (0);
285 }
286
287
288 /*
289 * RPC: mountd/mount
290 * Given a server pathname, get an NFS file handle.
291 * Also, sets sin->sin_port to the NFS service port.
292 */
293 static int
294 md_mount(mdsin, path, argp)
295 struct sockaddr_in *mdsin; /* mountd server address */
296 char *path;
297 struct nfs_args *argp;
298 {
299 /* The RPC structures */
300 struct rdata {
301 u_int32_t errno;
302 union {
303 u_int8_t v2fh[NFSX_V2FH];
304 struct {
305 u_int32_t fhlen;
306 u_int8_t fh[1];
307 } v3fh;
308 } fh;
309 } *rdata;
310 struct mbuf *m;
311 u_int8_t *fh;
312 int minlen, error;
313 int mntver;
314
315 mntver = (argp->flags & NFSMNT_NFSV3) ? 3 : 2;
316 do {
317 /*
318 * Get port number for MOUNTD.
319 */
320 error = krpc_portmap(mdsin, RPCPROG_MNT, mntver,
321 IPPROTO_UDP, &mdsin->sin_port);
322 if (error)
323 continue;
324
325 /* This mbuf is consumed by krpc_call. */
326 m = xdr_string_encode(path, strlen(path));
327 if (m == NULL)
328 return ENOMEM;
329
330 /* Do RPC to mountd. */
331 error = krpc_call(mdsin, RPCPROG_MNT, mntver,
332 RPCMNT_MOUNT, &m, NULL);
333 if (error != EPROGMISMATCH)
334 break;
335 /* Try lower version of mountd. */
336 } while (--mntver >= 1);
337 if (error) {
338 printf("nfs_boot: mountd error=%d\n", error);
339 return error;
340 }
341 if (mntver != 3)
342 argp->flags &= ~NFSMNT_NFSV3;
343
344 /* The reply might have only the errno. */
345 if (m->m_len < 4)
346 goto bad;
347 /* Have at least errno, so check that. */
348 rdata = mtod(m, struct rdata *);
349 error = fxdr_unsigned(u_int32_t, rdata->errno);
350 if (error)
351 goto out;
352
353 /* Have errno==0, so the fh must be there. */
354 if (mntver == 3) {
355 argp->fhsize = fxdr_unsigned(u_int32_t, rdata->fh.v3fh.fhlen);
356 if (argp->fhsize > NFSX_V3FHMAX)
357 goto bad;
358 minlen = 2 * sizeof(u_int32_t) + argp->fhsize;
359 } else {
360 argp->fhsize = NFSX_V2FH;
361 minlen = sizeof(u_int32_t) + argp->fhsize;
362 }
363
364 if (m->m_len < minlen) {
365 m = m_pullup(m, minlen);
366 if (m == NULL)
367 return(EBADRPC);
368 rdata = mtod(m, struct rdata *);
369 }
370
371 fh = (mntver == 3) ?
372 rdata->fh.v3fh.fh : rdata->fh.v2fh;
373 bcopy(fh, argp->fh, argp->fhsize);
374
375 goto out;
376
377 bad:
378 error = EBADRPC;
379
380 out:
381 m_freem(m);
382 return error;
383 }
384
385 #endif /* NETHER */
386