1 /** $MirOS: src/sys/nfs/nfs_boot.c,v 1.3 2005/04/29 15:07:08 tg Exp $ */
2 /* $OpenBSD: nfs_boot.c,v 1.16 2004/11/04 13:14:29 pedro Exp $ */
3 /* $NetBSD: nfs_boot.c,v 1.26 1996/05/07 02:51:25 thorpej Exp $ */
4
5 /*
6 * Copyright (c) 1995 Adam Glass, Gordon Ross
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of the authors may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/conf.h>
36 #include <sys/ioctl.h>
37 #include <sys/proc.h>
38 #include <sys/mount.h>
39 #include <sys/mbuf.h>
40 #include <sys/reboot.h>
41 #include <sys/socket.h>
42 #include <sys/socketvar.h>
43
44 #include <net/if.h>
45 #include <net/route.h>
46
47 #include <netinet/in.h>
48 #include <netinet/if_ether.h>
49
50 #include <nfs/rpcv2.h>
51 #include <nfs/nfsproto.h>
52 #include <nfs/nfs.h>
53 #include <nfs/nfsdiskless.h>
54 #include <nfs/krpc.h>
55 #include <nfs/xdr_subs.h>
56 #include <nfs/nfs_var.h>
57
58 #include "ether.h"
59
60 #if !defined(NFSCLIENT) || (NETHER == 0 && NFDDI == 0)
61
62 int
nfs_boot_init(nd,procp)63 nfs_boot_init(nd, procp)
64 struct nfs_diskless *nd;
65 struct proc *procp;
66 {
67 panic("nfs_boot_init: NFSCLIENT not enabled in kernel");
68 }
69
70 int
nfs_boot_getfh(bpsin,key,ndmntp,retries)71 nfs_boot_getfh(bpsin, key, ndmntp, retries)
72 struct sockaddr_in *bpsin;
73 char *key;
74 struct nfs_dlmount *ndmntp;
75 int retries;
76 {
77 /* can not get here */
78 return (EOPNOTSUPP);
79 }
80
81 #else
82
83 /*
84 * Support for NFS diskless booting, specifically getting information
85 * about where to boot from, what pathnames, etc.
86 *
87 * This implementation uses RARP and the bootparam RPC.
88 * We are forced to implement RPC anyway (to get file handles)
89 * so we might as well take advantage of it for bootparam too.
90 *
91 * The diskless boot sequence goes as follows:
92 * (1) Use RARP to get our interface address
93 * (2) Use RPC/bootparam/whoami to get our hostname,
94 * our IP address, and the server's IP address.
95 * (3) Use RPC/bootparam/getfile to get the root path
96 * (4) Use RPC/mountd to get the root file handle
97 * (5) Use RPC/bootparam/getfile to get the swap path
98 * (6) Use RPC/mountd to get the swap file handle
99 *
100 * (This happens to be the way Sun does it too.)
101 */
102
103 /* bootparam RPC */
104 static int bp_whoami(struct sockaddr_in *bpsin,
105 struct in_addr *my_ip, struct in_addr *gw_ip);
106 static int bp_getfile(struct sockaddr_in *bpsin, char *key,
107 struct sockaddr_in *mdsin, char *servname, char *path, int retries);
108
109 /* mountd RPC */
110 static int md_mount(struct sockaddr_in *mdsin, char *path,
111 u_char *fh);
112
113 char *nfsbootdevname;
114
115 /*
116 * Called with an empty nfs_diskless struct to be filled in.
117 */
118 int
nfs_boot_init(nd,procp)119 nfs_boot_init(nd, procp)
120 struct nfs_diskless *nd;
121 struct proc *procp;
122 {
123 struct ifreq ireq;
124 struct in_addr my_ip, gw_ip;
125 struct sockaddr_in bp_sin;
126 struct sockaddr_in *sin;
127 struct ifnet *ifp;
128 struct socket *so;
129 int error;
130
131 /*
132 * Find an interface, rarp for its ip address, stuff it, the
133 * implied broadcast addr, and netmask into a nfs_diskless struct.
134 *
135 * This was moved here from nfs_vfsops.c because this procedure
136 * would be quite different if someone decides to write (i.e.) a
137 * BOOTP version of this file (might not use RARP, etc.)
138 */
139
140 /*
141 * Find a network interface.
142 */
143 if (nfsbootdevname)
144 ifp = ifunit(nfsbootdevname);
145 else {
146 for (ifp = TAILQ_FIRST(&ifnet); ifp != NULL;
147 ifp = TAILQ_NEXT(ifp, if_list)) {
148 if ((ifp->if_flags &
149 (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
150 break;
151 }
152 }
153 if (ifp == NULL)
154 panic("nfs_boot: no suitable interface");
155 bcopy(ifp->if_xname, ireq.ifr_name, IFNAMSIZ);
156 printf("nfs_boot: using network interface '%s'\n", 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);
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
nfs_boot_getfh(bpsin,key,ndmntp,retries)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 * gateway address
321 *
322 * The hostname is set here for convenience.
323 *
324 * Note - bpsin is initialized to the broadcast address,
325 * and will be replaced with the bootparam server address
326 * after this call is complete. Have to use PMAP_PROC_CALL
327 * to make sure we get responses only from a servers that
328 * know about us (don't want to broadcast a getport call).
329 */
330 static int
bp_whoami(bpsin,my_ip,gw_ip)331 bp_whoami(bpsin, my_ip, gw_ip)
332 struct sockaddr_in *bpsin;
333 struct in_addr *my_ip;
334 struct in_addr *gw_ip;
335 {
336 int ldomainnamelen;
337 char ldomainname[MAXHOSTNAMELEN];
338
339 /* RPC structures for PMAPPROC_CALLIT */
340 struct whoami_call {
341 u_int32_t call_prog;
342 u_int32_t call_vers;
343 u_int32_t call_proc;
344 u_int32_t call_arglen;
345 } *call;
346 struct callit_reply {
347 u_int32_t port;
348 u_int32_t encap_len;
349 /* encapsulated data here */
350 } *reply;
351
352 struct mbuf *m, *from;
353 struct sockaddr_in *sin;
354 int error, msg_len;
355 int16_t port;
356
357 /*
358 * Build request message for PMAPPROC_CALLIT.
359 */
360 m = m_get(M_WAIT, MT_DATA);
361 call = mtod(m, struct whoami_call *);
362 m->m_len = sizeof(*call);
363 call->call_prog = txdr_unsigned(BOOTPARAM_PROG);
364 call->call_vers = txdr_unsigned(BOOTPARAM_VERS);
365 call->call_proc = txdr_unsigned(BOOTPARAM_WHOAMI);
366
367 /*
368 * append encapsulated data (client IP address)
369 */
370 m->m_next = xdr_inaddr_encode(my_ip);
371 call->call_arglen = txdr_unsigned(m->m_next->m_len);
372
373 /* RPC: portmap/callit */
374 bpsin->sin_port = htons(PMAPPORT);
375 from = NULL;
376 error = krpc_call(bpsin, PMAPPROG, PMAPVERS,
377 PMAPPROC_CALLIT, &m, &from, -1);
378 if (error)
379 return error;
380
381 /*
382 * Parse result message.
383 */
384 if (m->m_len < sizeof(*reply)) {
385 m = m_pullup(m, sizeof(*reply));
386 if (m == NULL)
387 goto bad;
388 }
389 reply = mtod(m, struct callit_reply *);
390 port = fxdr_unsigned(u_int32_t, reply->port);
391 msg_len = fxdr_unsigned(u_int32_t, reply->encap_len);
392 m_adj(m, sizeof(*reply));
393
394 /*
395 * Save bootparam server address
396 */
397 sin = mtod(from, struct sockaddr_in *);
398 bpsin->sin_port = htons(port);
399 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr;
400
401 /* client name */
402 hostnamelen = MAXHOSTNAMELEN-1;
403 m = xdr_string_decode(m, hostname, &hostnamelen);
404 if (m == NULL)
405 goto bad;
406
407 /* domain name */
408 domainnamelen = 0;
409 m = xdr_string_decode(m, ldomainname, &ldomainnamelen);
410 if (m == NULL)
411 goto bad;
412
413 /* gateway address */
414 m = xdr_inaddr_decode(m, gw_ip);
415 if (m == NULL)
416 goto bad;
417
418 /* success */
419 goto out;
420
421 bad:
422 printf("nfs_boot: bootparam_whoami: bad reply\n");
423 error = EBADRPC;
424
425 out:
426 if (from)
427 m_freem(from);
428 if (m)
429 m_freem(m);
430 return(error);
431 }
432
433
434 /*
435 * RPC: bootparam/getfile
436 * Given client name and file "key", get:
437 * server name
438 * server IP address
439 * server pathname
440 */
441 static int
bp_getfile(bpsin,key,md_sin,serv_name,pathname,retries)442 bp_getfile(bpsin, key, md_sin, serv_name, pathname, retries)
443 struct sockaddr_in *bpsin;
444 char *key;
445 struct sockaddr_in *md_sin;
446 char *serv_name;
447 char *pathname;
448 int retries;
449 {
450 struct mbuf *m;
451 struct sockaddr_in *sin;
452 struct in_addr inaddr;
453 int error, sn_len, path_len;
454
455 /*
456 * Build request message.
457 */
458
459 /* client name (hostname) */
460 m = xdr_string_encode(hostname, hostnamelen);
461 if (m == NULL)
462 return (ENOMEM);
463
464 /* key name (root or swap) */
465 m->m_next = xdr_string_encode(key, strlen(key));
466 if (m->m_next == NULL)
467 return (ENOMEM);
468
469 /* RPC: bootparam/getfile */
470 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS,
471 BOOTPARAM_GETFILE, &m, NULL, retries);
472 if (error)
473 return error;
474
475 /*
476 * Parse result message.
477 */
478
479 /* server name */
480 sn_len = MNAMELEN-1;
481 m = xdr_string_decode(m, serv_name, &sn_len);
482 if (m == NULL)
483 goto bad;
484
485 /* server IP address (mountd/NFS) */
486 m = xdr_inaddr_decode(m, &inaddr);
487 if (m == NULL)
488 goto bad;
489
490 /* server pathname */
491 path_len = MAXPATHLEN-1;
492 m = xdr_string_decode(m, pathname, &path_len);
493 if (m == NULL)
494 goto bad;
495
496 /* setup server socket address */
497 sin = md_sin;
498 bzero((caddr_t)sin, sizeof(*sin));
499 sin->sin_len = sizeof(*sin);
500 sin->sin_family = AF_INET;
501 sin->sin_addr = inaddr;
502
503 /* success */
504 goto out;
505
506 bad:
507 printf("nfs_boot: bootparam_getfile: bad reply\n");
508 error = EBADRPC;
509
510 out:
511 m_freem(m);
512 return(0);
513 }
514
515
516 /*
517 * RPC: mountd/mount
518 * Given a server pathname, get an NFS file handle.
519 * Also, sets sin->sin_port to the NFS service port.
520 */
521 static int
md_mount(mdsin,path,fhp)522 md_mount(mdsin, path, fhp)
523 struct sockaddr_in *mdsin; /* mountd server address */
524 char *path;
525 u_char *fhp;
526 {
527 /* The RPC structures */
528 struct rdata {
529 u_int32_t errno;
530 u_int8_t fh[NFSX_V2FH];
531 } *rdata;
532 struct mbuf *m;
533 int error;
534
535 /* Get port number for MOUNTD. */
536 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1,
537 &mdsin->sin_port);
538 if (error) return error;
539
540 m = xdr_string_encode(path, strlen(path));
541 if (m == NULL)
542 return ENOMEM;
543
544 /* Do RPC to mountd. */
545 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
546 RPCMNT_MOUNT, &m, NULL, -1);
547 if (error)
548 return error; /* message already freed */
549
550 /* The reply might have only the errno. */
551 if (m->m_len < 4)
552 goto bad;
553 /* Have at least errno, so check that. */
554 rdata = mtod(m, struct rdata *);
555 error = fxdr_unsigned(u_int32_t, rdata->errno);
556 if (error)
557 goto out;
558
559 /* Have errno==0, so the fh must be there. */
560 if (m->m_len < sizeof(*rdata)) {
561 m = m_pullup(m, sizeof(*rdata));
562 if (m == NULL)
563 goto bad;
564 rdata = mtod(m, struct rdata *);
565 }
566 bcopy(rdata->fh, fhp, NFSX_V2FH);
567 goto out;
568
569 bad:
570 error = EBADRPC;
571
572 out:
573 m_freem(m);
574 return error;
575 }
576
577 #endif /* ifdef NFSCLIENT */
578