nfs_boot.c revision 1.22 1 /* $NetBSD: nfs_boot.c,v 1.22 1996/02/10 22:55:16 pk 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 get_path_and_handle(&bp_sin, "root", &nd->nd_root);
244 get_path_and_handle(&bp_sin, "swap", &nd->nd_swap);
245
246 return (0);
247 }
248
249 static void
250 get_path_and_handle(bpsin, key, ndmntp)
251 struct sockaddr_in *bpsin; /* bootparam server */
252 char *key; /* root or swap */
253 struct nfs_dlmount *ndmntp; /* output */
254 {
255 char pathname[MAXPATHLEN];
256 char *sp, *dp, *endp;
257 int error;
258
259 /*
260 * Get server:pathname for "key" (root or swap)
261 * using RPC to bootparam/getfile
262 */
263 error = bp_getfile(bpsin, key, &ndmntp->ndm_saddr,
264 ndmntp->ndm_host, pathname);
265 if (error)
266 panic("nfs_boot: bootparam get %s: %d", key, error);
267
268 /*
269 * Get file handle for "key" (root or swap)
270 * using RPC to mountd/mount
271 */
272 error = md_mount(&ndmntp->ndm_saddr, pathname, ndmntp->ndm_fh);
273 if (error)
274 panic("nfs_boot: mountd %s, error=%d", key, error);
275
276 /* Construct remote path (for getmntinfo(3)) */
277 dp = ndmntp->ndm_host;
278 endp = dp + MNAMELEN - 1;
279 dp += strlen(dp);
280 *dp++ = ':';
281 for (sp = pathname; *sp && dp < endp;)
282 *dp++ = *sp++;
283 *dp = '\0';
284
285 }
286
287
288 /*
289 * RPC: bootparam/whoami
290 * Given client IP address, get:
291 * client name (hostname)
292 * domain name (domainname)
293 * gateway address
294 *
295 * The hostname and domainname are set here for convenience.
296 *
297 * Note - bpsin is initialized to the broadcast address,
298 * and will be replaced with the bootparam server address
299 * after this call is complete. Have to use PMAP_PROC_CALL
300 * to make sure we get responses only from a servers that
301 * know about us (don't want to broadcast a getport call).
302 */
303 static int
304 bp_whoami(bpsin, my_ip, gw_ip)
305 struct sockaddr_in *bpsin;
306 struct in_addr *my_ip;
307 struct in_addr *gw_ip;
308 {
309 /* RPC structures for PMAPPROC_CALLIT */
310 struct whoami_call {
311 u_int32_t call_prog;
312 u_int32_t call_vers;
313 u_int32_t call_proc;
314 u_int32_t call_arglen;
315 } *call;
316 struct callit_reply {
317 u_int32_t port;
318 u_int32_t encap_len;
319 /* encapsulated data here */
320 } *reply;
321
322 struct mbuf *m, *from;
323 struct sockaddr_in *sin;
324 int error, msg_len;
325 int16_t port;
326
327 /*
328 * Build request message for PMAPPROC_CALLIT.
329 */
330 m = m_get(M_WAIT, MT_DATA);
331 call = mtod(m, struct whoami_call *);
332 m->m_len = sizeof(*call);
333 call->call_prog = txdr_unsigned(BOOTPARAM_PROG);
334 call->call_vers = txdr_unsigned(BOOTPARAM_VERS);
335 call->call_proc = txdr_unsigned(BOOTPARAM_WHOAMI);
336
337 /*
338 * append encapsulated data (client IP address)
339 */
340 m->m_next = xdr_inaddr_encode(my_ip);
341 call->call_arglen = txdr_unsigned(m->m_next->m_len);
342
343 /* RPC: portmap/callit */
344 bpsin->sin_port = htons(PMAPPORT);
345 from = NULL;
346 error = krpc_call(bpsin, PMAPPROG, PMAPVERS,
347 PMAPPROC_CALLIT, &m, &from);
348 if (error)
349 return error;
350
351 /*
352 * Parse result message.
353 */
354 if (m->m_len < sizeof(*reply)) {
355 m = m_pullup(m, sizeof(*reply));
356 if (m == NULL)
357 goto bad;
358 }
359 reply = mtod(m, struct callit_reply *);
360 port = fxdr_unsigned(u_int32_t, reply->port);
361 msg_len = fxdr_unsigned(u_int32_t, reply->encap_len);
362 m_adj(m, sizeof(*reply));
363
364 /*
365 * Save bootparam server address
366 */
367 sin = mtod(from, struct sockaddr_in *);
368 bpsin->sin_port = htons(port);
369 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr;
370
371 /* client name */
372 hostnamelen = MAXHOSTNAMELEN-1;
373 m = xdr_string_decode(m, hostname, &hostnamelen);
374 if (m == NULL)
375 goto bad;
376
377 /* domain name */
378 domainnamelen = MAXHOSTNAMELEN-1;
379 m = xdr_string_decode(m, domainname, &domainnamelen);
380 if (m == NULL)
381 goto bad;
382
383 /* gateway address */
384 m = xdr_inaddr_decode(m, gw_ip);
385 if (m == NULL)
386 goto bad;
387
388 /* success */
389 goto out;
390
391 bad:
392 printf("nfs_boot: bootparam_whoami: bad reply\n");
393 error = EBADRPC;
394
395 out:
396 if (from)
397 m_freem(from);
398 if (m)
399 m_freem(m);
400 return(error);
401 }
402
403
404 /*
405 * RPC: bootparam/getfile
406 * Given client name and file "key", get:
407 * server name
408 * server IP address
409 * server pathname
410 */
411 static int
412 bp_getfile(bpsin, key, md_sin, serv_name, pathname)
413 struct sockaddr_in *bpsin;
414 char *key;
415 struct sockaddr_in *md_sin;
416 char *serv_name;
417 char *pathname;
418 {
419 struct mbuf *m;
420 struct sockaddr_in *sin;
421 struct in_addr inaddr;
422 int error, sn_len, path_len;
423
424 /*
425 * Build request message.
426 */
427
428 /* client name (hostname) */
429 m = xdr_string_encode(hostname, hostnamelen);
430 if (m == NULL)
431 return (ENOMEM);
432
433 /* key name (root or swap) */
434 m->m_next = xdr_string_encode(key, strlen(key));
435 if (m->m_next == NULL)
436 return (ENOMEM);
437
438 /* RPC: bootparam/getfile */
439 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS,
440 BOOTPARAM_GETFILE, &m, NULL);
441 if (error)
442 return error;
443
444 /*
445 * Parse result message.
446 */
447
448 /* server name */
449 sn_len = MNAMELEN-1;
450 m = xdr_string_decode(m, serv_name, &sn_len);
451 if (m == NULL)
452 goto bad;
453
454 /* server IP address (mountd/NFS) */
455 m = xdr_inaddr_decode(m, &inaddr);
456 if (m == NULL)
457 goto bad;
458
459 /* server pathname */
460 path_len = MAXPATHLEN-1;
461 m = xdr_string_decode(m, pathname, &path_len);
462 if (m == NULL)
463 goto bad;
464
465 /* setup server socket address */
466 sin = md_sin;
467 bzero((caddr_t)sin, sizeof(*sin));
468 sin->sin_len = sizeof(*sin);
469 sin->sin_family = AF_INET;
470 sin->sin_addr = inaddr;
471
472 /* success */
473 goto out;
474
475 bad:
476 printf("nfs_boot: bootparam_getfile: bad reply\n");
477 error = EBADRPC;
478
479 out:
480 m_freem(m);
481 return(0);
482 }
483
484
485 /*
486 * RPC: mountd/mount
487 * Given a server pathname, get an NFS file handle.
488 * Also, sets sin->sin_port to the NFS service port.
489 */
490 static int
491 md_mount(mdsin, path, fhp)
492 struct sockaddr_in *mdsin; /* mountd server address */
493 char *path;
494 u_char *fhp;
495 {
496 /* The RPC structures */
497 struct rdata {
498 u_int32_t errno;
499 u_int8_t fh[NFS_FHSIZE];
500 } *rdata;
501 struct mbuf *m;
502 int error;
503
504 /* Get port number for MOUNTD. */
505 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1,
506 &mdsin->sin_port);
507 if (error) return error;
508
509 m = xdr_string_encode(path, strlen(path));
510 if (m == NULL)
511 return (ENOMEM);
512
513 /* Do RPC to mountd. */
514 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
515 RPCMNT_MOUNT, &m, NULL);
516 if (error)
517 return error; /* message already freed */
518
519 if (m->m_len < sizeof(*rdata)) {
520 m = m_pullup(m, sizeof(*rdata));
521 if (m == NULL)
522 goto bad;
523 }
524 rdata = mtod(m, struct rdata *);
525 error = fxdr_unsigned(u_int32_t, rdata->errno);
526 if (error)
527 goto out;
528 bcopy(rdata->fh, fhp, NFS_FHSIZE);
529
530 /* Set port number for NFS use. */
531 error = krpc_portmap(mdsin, NFS_PROG, NFS_VER2,
532 &mdsin->sin_port);
533 goto out;
534
535 bad:
536 error = EBADRPC;
537
538 out:
539 m_freem(m);
540 return error;
541 }
542
543 #endif /* NETHER */
544