nfs_boot.c revision 1.23 1 /* $NetBSD: nfs_boot.c,v 1.23 1996/02/13 17:53:33 gwr Exp $ */
2
3 /*
4 * Copyright (c) 1995 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/reboot.h>
39 #include <sys/socket.h>
40 #include <sys/socketvar.h>
41
42 #include <net/if.h>
43 #include <net/route.h>
44
45 #include <netinet/in.h>
46 #include <netinet/if_ether.h>
47
48 #include <nfs/rpcv2.h>
49 #include <nfs/nfsv2.h>
50 #include <nfs/nfs.h>
51 #include <nfs/nfsdiskless.h>
52 #include <nfs/krpc.h>
53 #include <nfs/xdr_subs.h>
54 #include <nfs/nfs_var.h>
55
56 #include "ether.h"
57 #if NETHER == 0
58
59 int nfs_boot_init(nd, procp)
60 struct nfs_diskless *nd;
61 struct proc *procp;
62 {
63 panic("nfs_boot_init: no ether");
64 }
65
66 #else /* NETHER */
67
68 /*
69 * Support for NFS diskless booting, specifically getting information
70 * about where to boot from, what pathnames, etc.
71 *
72 * This implememtation uses RARP and the bootparam RPC.
73 * We are forced to implement RPC anyway (to get file handles)
74 * so we might as well take advantage of it for bootparam too.
75 *
76 * The diskless boot sequence goes as follows:
77 * (1) Use RARP to get our interface address
78 * (2) Use RPC/bootparam/whoami to get our hostname,
79 * our IP address, and the server's IP address.
80 * (3) Use RPC/bootparam/getfile to get the root path
81 * (4) Use RPC/mountd to get the root file handle
82 * (5) Use RPC/bootparam/getfile to get the swap path
83 * (6) Use RPC/mountd to get the swap file handle
84 *
85 * (This happens to be the way Sun does it too.)
86 */
87
88 /* bootparam RPC */
89 static int bp_whoami __P((struct sockaddr_in *bpsin,
90 struct in_addr *my_ip, struct in_addr *gw_ip));
91 static int bp_getfile __P((struct sockaddr_in *bpsin, char *key,
92 struct sockaddr_in *mdsin, char *servname, char *path));
93
94 /* mountd RPC */
95 static int md_mount __P((struct sockaddr_in *mdsin, char *path,
96 u_char *fh));
97
98 /* other helpers */
99 static void get_path_and_handle __P((struct sockaddr_in *bpsin,
100 char *key, struct nfs_dlmount *ndmntp));
101
102 char *nfsbootdevname;
103
104 /*
105 * Called with an empty nfs_diskless struct to be filled in.
106 */
107 int
108 nfs_boot_init(nd, procp)
109 struct nfs_diskless *nd;
110 struct proc *procp;
111 {
112 struct ifreq ireq;
113 struct in_addr my_ip, gw_ip;
114 struct sockaddr_in bp_sin;
115 struct sockaddr_in *sin;
116 struct ifnet *ifp;
117 struct socket *so;
118 int error;
119
120 /*
121 * Find an interface, rarp for its ip address, stuff it, the
122 * implied broadcast addr, and netmask into a nfs_diskless struct.
123 *
124 * This was moved here from nfs_vfsops.c because this procedure
125 * would be quite different if someone decides to write (i.e.) a
126 * BOOTP version of this file (might not use RARP, etc.)
127 */
128
129 /*
130 * Find a network interface.
131 */
132 if (nfsbootdevname)
133 ifp = ifunit(nfsbootdevname);
134 else
135 for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next)
136 if ((ifp->if_flags &
137 (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
138 break;
139 if (ifp == NULL)
140 panic("nfs_boot: no suitable interface");
141 sprintf(ireq.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
142 printf("nfs_boot: using network interface '%s'\n",
143 ireq.ifr_name);
144
145 /*
146 * Bring up the interface.
147 *
148 * Get the old interface flags and or IFF_UP into them; if
149 * IFF_UP set blindly, interface selection can be clobbered.
150 */
151 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0)
152 panic("nfs_boot: socreate, error=%d", error);
153 error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)&ireq, procp);
154 if (error)
155 panic("nfs_boot: GIFFLAGS, error=%d", error);
156 ireq.ifr_flags |= IFF_UP;
157 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp);
158 if (error)
159 panic("nfs_boot: SIFFLAGS, error=%d", error);
160
161 /*
162 * Do RARP for the interface address.
163 */
164 if ((error = revarpwhoami(&my_ip, ifp)) != 0)
165 panic("revarp failed, error=%d", error);
166 printf("nfs_boot: client_addr=0x%x\n", ntohl(my_ip.s_addr));
167
168 /*
169 * Do enough of ifconfig(8) so that the chosen interface
170 * can talk to the servers. (just set the address)
171 */
172 sin = (struct sockaddr_in *)&ireq.ifr_addr;
173 bzero((caddr_t)sin, sizeof(*sin));
174 sin->sin_len = sizeof(*sin);
175 sin->sin_family = AF_INET;
176 sin->sin_addr.s_addr = my_ip.s_addr;
177 error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp);
178 if (error)
179 panic("nfs_boot: set if addr, error=%d", error);
180
181 soclose(so);
182
183 /*
184 * Get client name and gateway address.
185 * RPC: bootparam/whoami
186 * Use the old broadcast address for the WHOAMI
187 * call because we do not yet know our netmask.
188 * The server address returned by the WHOAMI call
189 * is used for all subsequent booptaram RPCs.
190 */
191 bzero((caddr_t)&bp_sin, sizeof(bp_sin));
192 bp_sin.sin_len = sizeof(bp_sin);
193 bp_sin.sin_family = AF_INET;
194 bp_sin.sin_addr.s_addr = INADDR_BROADCAST;
195 hostnamelen = MAXHOSTNAMELEN;
196
197 /* this returns gateway IP address */
198 error = bp_whoami(&bp_sin, &my_ip, &gw_ip);
199 if (error)
200 panic("nfs_boot: bootparam whoami, error=%d", error);
201 printf("nfs_boot: server_addr=0x%x\n",
202 ntohl(bp_sin.sin_addr.s_addr));
203 printf("nfs_boot: hostname=%s\n", hostname);
204
205 #ifdef NFS_BOOT_GATEWAY
206 /*
207 * XXX - This code is conditionally compiled only because
208 * many bootparam servers (in particular, SunOS 4.1.3)
209 * always set the gateway address to their own address.
210 * The bootparam server is not necessarily the gateway.
211 * We could just believe the server, and at worst you would
212 * need to delete the incorrect default route before adding
213 * the correct one, but for simplicity, ignore the gateway.
214 * If your server is OK, you can turn on this option.
215 *
216 * If the gateway address is set, add a default route.
217 * (The mountd RPCs may go across a gateway.)
218 */
219 if (gw_ip.s_addr) {
220 struct sockaddr dst, gw, mask;
221 /* Destination: (default) */
222 bzero((caddr_t)&dst, sizeof(dst));
223 dst.sa_len = sizeof(dst);
224 dst.sa_family = AF_INET;
225 /* Gateway: */
226 bzero((caddr_t)&gw, sizeof(gw));
227 sin = (struct sockaddr_in *)&gw;
228 sin->sin_len = sizeof(gw);
229 sin->sin_family = AF_INET;
230 sin->sin_addr.s_addr = gw_ip.s_addr;
231 /* Mask: (zero length) */
232 bzero(&mask, sizeof(mask));
233
234 printf("nfs_boot: gateway=0x%x\n", ntohl(gw_ip.s_addr));
235 /* add, dest, gw, mask, flags, 0 */
236 error = rtrequest(RTM_ADD, &dst, (struct sockaddr *)&gw,
237 &mask, (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL);
238 if (error)
239 printf("nfs_boot: add route, error=%d\n", error);
240 }
241 #endif
242
243 bcopy(&bp_sin, &nd->nd_boot, sizeof(bp_sin));
244
245 return (0);
246 }
247
248 void
249 nfs_boot_getfh(bpsin, key, ndmntp)
250 struct sockaddr_in *bpsin; /* bootparam server */
251 char *key; /* root or swap */
252 struct nfs_dlmount *ndmntp; /* output */
253 {
254 char pathname[MAXPATHLEN];
255 char *sp, *dp, *endp;
256 struct sockaddr_in *sin;
257 int error;
258
259 sin = &ndmntp->ndm_saddr;
260
261 /*
262 * Get server:pathname for "key" (root or swap)
263 * using RPC to bootparam/getfile
264 */
265 error = bp_getfile(bpsin, key, sin,
266 ndmntp->ndm_host, pathname);
267 if (error)
268 panic("nfs_boot: bootparam get %s: %d", key, error);
269
270 /*
271 * Get file handle for "key" (root or swap)
272 * using RPC to mountd/mount
273 */
274 error = md_mount(sin, pathname, ndmntp->ndm_fh);
275 if (error)
276 panic("nfs_boot: mountd %s, error=%d", key, error);
277
278 /* Set port number for NFS use. */
279 /* XXX: NFS port is always 2049, right? */
280 error = krpc_portmap(sin, NFS_PROG, NFS_VER2, &sin->sin_port);
281 if (error)
282 panic("nfs_boot: portmap NFS/v2, error=%d", error);
283
284 /* Construct remote path (for getmntinfo(3)) */
285 dp = ndmntp->ndm_host;
286 endp = dp + MNAMELEN - 1;
287 dp += strlen(dp);
288 *dp++ = ':';
289 for (sp = pathname; *sp && dp < endp;)
290 *dp++ = *sp++;
291 *dp = '\0';
292
293 }
294
295
296 /*
297 * RPC: bootparam/whoami
298 * Given client IP address, get:
299 * client name (hostname)
300 * domain name (domainname)
301 * gateway address
302 *
303 * The hostname and domainname are set here for convenience.
304 *
305 * Note - bpsin is initialized to the broadcast address,
306 * and will be replaced with the bootparam server address
307 * after this call is complete. Have to use PMAP_PROC_CALL
308 * to make sure we get responses only from a servers that
309 * know about us (don't want to broadcast a getport call).
310 */
311 static int
312 bp_whoami(bpsin, my_ip, gw_ip)
313 struct sockaddr_in *bpsin;
314 struct in_addr *my_ip;
315 struct in_addr *gw_ip;
316 {
317 /* RPC structures for PMAPPROC_CALLIT */
318 struct whoami_call {
319 u_int32_t call_prog;
320 u_int32_t call_vers;
321 u_int32_t call_proc;
322 u_int32_t call_arglen;
323 } *call;
324 struct callit_reply {
325 u_int32_t port;
326 u_int32_t encap_len;
327 /* encapsulated data here */
328 } *reply;
329
330 struct mbuf *m, *from;
331 struct sockaddr_in *sin;
332 int error, msg_len;
333 int16_t port;
334
335 /*
336 * Build request message for PMAPPROC_CALLIT.
337 */
338 m = m_get(M_WAIT, MT_DATA);
339 call = mtod(m, struct whoami_call *);
340 m->m_len = sizeof(*call);
341 call->call_prog = txdr_unsigned(BOOTPARAM_PROG);
342 call->call_vers = txdr_unsigned(BOOTPARAM_VERS);
343 call->call_proc = txdr_unsigned(BOOTPARAM_WHOAMI);
344
345 /*
346 * append encapsulated data (client IP address)
347 */
348 m->m_next = xdr_inaddr_encode(my_ip);
349 call->call_arglen = txdr_unsigned(m->m_next->m_len);
350
351 /* RPC: portmap/callit */
352 bpsin->sin_port = htons(PMAPPORT);
353 from = NULL;
354 error = krpc_call(bpsin, PMAPPROG, PMAPVERS,
355 PMAPPROC_CALLIT, &m, &from);
356 if (error)
357 return error;
358
359 /*
360 * Parse result message.
361 */
362 if (m->m_len < sizeof(*reply)) {
363 m = m_pullup(m, sizeof(*reply));
364 if (m == NULL)
365 goto bad;
366 }
367 reply = mtod(m, struct callit_reply *);
368 port = fxdr_unsigned(u_int32_t, reply->port);
369 msg_len = fxdr_unsigned(u_int32_t, reply->encap_len);
370 m_adj(m, sizeof(*reply));
371
372 /*
373 * Save bootparam server address
374 */
375 sin = mtod(from, struct sockaddr_in *);
376 bpsin->sin_port = htons(port);
377 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr;
378
379 /* client name */
380 hostnamelen = MAXHOSTNAMELEN-1;
381 m = xdr_string_decode(m, hostname, &hostnamelen);
382 if (m == NULL)
383 goto bad;
384
385 /* domain name */
386 domainnamelen = MAXHOSTNAMELEN-1;
387 m = xdr_string_decode(m, domainname, &domainnamelen);
388 if (m == NULL)
389 goto bad;
390
391 /* gateway address */
392 m = xdr_inaddr_decode(m, gw_ip);
393 if (m == NULL)
394 goto bad;
395
396 /* success */
397 goto out;
398
399 bad:
400 printf("nfs_boot: bootparam_whoami: bad reply\n");
401 error = EBADRPC;
402
403 out:
404 if (from)
405 m_freem(from);
406 if (m)
407 m_freem(m);
408 return(error);
409 }
410
411
412 /*
413 * RPC: bootparam/getfile
414 * Given client name and file "key", get:
415 * server name
416 * server IP address
417 * server pathname
418 */
419 static int
420 bp_getfile(bpsin, key, md_sin, serv_name, pathname)
421 struct sockaddr_in *bpsin;
422 char *key;
423 struct sockaddr_in *md_sin;
424 char *serv_name;
425 char *pathname;
426 {
427 struct mbuf *m;
428 struct sockaddr_in *sin;
429 struct in_addr inaddr;
430 int error, sn_len, path_len;
431
432 /*
433 * Build request message.
434 */
435
436 /* client name (hostname) */
437 m = xdr_string_encode(hostname, hostnamelen);
438 if (m == NULL)
439 return (ENOMEM);
440
441 /* key name (root or swap) */
442 m->m_next = xdr_string_encode(key, strlen(key));
443 if (m->m_next == NULL)
444 return (ENOMEM);
445
446 /* RPC: bootparam/getfile */
447 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS,
448 BOOTPARAM_GETFILE, &m, NULL);
449 if (error)
450 return error;
451
452 /*
453 * Parse result message.
454 */
455
456 /* server name */
457 sn_len = MNAMELEN-1;
458 m = xdr_string_decode(m, serv_name, &sn_len);
459 if (m == NULL)
460 goto bad;
461
462 /* server IP address (mountd/NFS) */
463 m = xdr_inaddr_decode(m, &inaddr);
464 if (m == NULL)
465 goto bad;
466
467 /* server pathname */
468 path_len = MAXPATHLEN-1;
469 m = xdr_string_decode(m, pathname, &path_len);
470 if (m == NULL)
471 goto bad;
472
473 /* setup server socket address */
474 sin = md_sin;
475 bzero((caddr_t)sin, sizeof(*sin));
476 sin->sin_len = sizeof(*sin);
477 sin->sin_family = AF_INET;
478 sin->sin_addr = inaddr;
479
480 /* success */
481 goto out;
482
483 bad:
484 printf("nfs_boot: bootparam_getfile: bad reply\n");
485 error = EBADRPC;
486
487 out:
488 m_freem(m);
489 return(0);
490 }
491
492
493 /*
494 * RPC: mountd/mount
495 * Given a server pathname, get an NFS file handle.
496 * Also, sets sin->sin_port to the NFS service port.
497 */
498 static int
499 md_mount(mdsin, path, fhp)
500 struct sockaddr_in *mdsin; /* mountd server address */
501 char *path;
502 u_char *fhp;
503 {
504 /* The RPC structures */
505 struct rdata {
506 u_int32_t errno;
507 u_int8_t fh[NFS_FHSIZE];
508 } *rdata;
509 struct mbuf *m;
510 int error;
511
512 /* Get port number for MOUNTD. */
513 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1,
514 &mdsin->sin_port);
515 if (error) return error;
516
517 m = xdr_string_encode(path, strlen(path));
518 if (m == NULL)
519 return (ENOMEM);
520
521 /* Do RPC to mountd. */
522 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
523 RPCMNT_MOUNT, &m, NULL);
524 if (error)
525 return error; /* message already freed */
526
527 /* The reply might have only the errno. */
528 if (m->m_len < 4)
529 goto bad;
530 /* Have at least errno, so check that. */
531 rdata = mtod(m, struct rdata *);
532 error = fxdr_unsigned(u_int32_t, rdata->errno);
533 if (error)
534 goto out;
535
536 /* Have errno==0, so the fh must be there. */
537 if (m->m_len < sizeof(*rdata)) {
538 m = m_pullup(m, sizeof(*rdata));
539 if (m == NULL)
540 goto bad;
541 rdata = mtod(m, struct rdata *);
542 }
543 bcopy(rdata->fh, fhp, NFS_FHSIZE);
544 goto out;
545
546 bad:
547 error = EBADRPC;
548
549 out:
550 m_freem(m);
551 return error;
552 }
553
554 #endif /* NETHER */
555