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