nfs_bootparam.c revision 1.9 1 /* $NetBSD: nfs_bootparam.c,v 1.9 1998/08/09 21:19:50 perry 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, Sun-style (RPC/bootparams)
41 */
42
43 #include "opt_nfs_boot.h"
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/conf.h>
49 #include <sys/device.h>
50 #include <sys/ioctl.h>
51 #include <sys/proc.h>
52 #include <sys/mount.h>
53 #include <sys/mbuf.h>
54 #include <sys/reboot.h>
55 #include <sys/socket.h>
56 #include <sys/socketvar.h>
57
58 #include <net/if.h>
59 #include <net/if_types.h>
60 #include <net/route.h>
61 #include <net/if_ether.h>
62
63 #include <netinet/in.h>
64 #include <netinet/if_inarp.h>
65
66 #include <nfs/rpcv2.h>
67 #include <nfs/krpc.h>
68 #include <nfs/xdr_subs.h>
69
70 #include <nfs/nfsproto.h>
71 #include <nfs/nfs.h>
72 #include <nfs/nfsmount.h>
73 #include <nfs/nfsdiskless.h>
74
75 #include "arp.h"
76
77 /*
78 * There are two implementations of NFS diskless boot.
79 * This implementation uses Sun RPC/bootparams, and the
80 * the other uses BOOTP (RFC951 - see nfs_bootdhcp.c).
81 *
82 * The Sun-style boot sequence goes as follows:
83 * (1) Use RARP to get our interface address
84 * (2) Use RPC/bootparam/whoami to get our hostname,
85 * our IP address, and the server's IP address.
86 * (3) Use RPC/bootparam/getfile to get the root path
87 * (4) Use RPC/mountd to get the root file handle
88 * (5) Use RPC/bootparam/getfile to get the swap path
89 * (6) Use RPC/mountd to get the swap file handle
90 */
91
92 /* bootparam RPC */
93 static int bp_whoami __P((struct sockaddr_in *bpsin,
94 struct in_addr *my_ip, struct in_addr *gw_ip));
95 static int bp_getfile __P((struct sockaddr_in *bpsin, char *key,
96 struct nfs_dlmount *ndm));
97
98
99 /*
100 * Get client name, gateway address, then
101 * get root and swap server:pathname info.
102 * RPCs: bootparam/whoami, bootparam/getfile
103 *
104 * Use the old broadcast address for the WHOAMI
105 * call because we do not yet know our netmask.
106 * The server address returned by the WHOAMI call
107 * is used for all subsequent booptaram RPCs.
108 */
109 int
110 nfs_bootparam(ifp, nd, procp)
111 struct ifnet *ifp;
112 struct nfs_diskless *nd;
113 struct proc *procp;
114 {
115 struct ifreq ireq;
116 struct in_addr my_ip, gw_ip;
117 struct sockaddr_in bp_sin;
118 struct sockaddr_in *sin;
119 struct socket *so;
120 struct nfs_dlmount *gw_ndm;
121 #if 0 /* XXX - not yet */
122 char *p;
123 u_int32_t mask;
124 #endif /* XXX */
125 int error;
126
127 gw_ndm = 0;
128 memset(&ireq, 0, sizeof(ireq));
129 memcpy(ireq.ifr_name, ifp->if_xname, IFNAMSIZ);
130
131 /*
132 * Get a socket to use for various things in here.
133 * After this, use "goto out" to cleanup and return.
134 */
135 error = socreate(AF_INET, &so, SOCK_DGRAM, 0);
136 if (error) {
137 printf("nfs_boot: socreate, error=%d\n", error);
138 return (error);
139 }
140
141 /*
142 * Bring up the interface. (just set the "up" flag)
143 * Get the old interface flags and or IFF_UP into them so
144 * things like media selection flags are not clobbered.
145 */
146 error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)&ireq, procp);
147 if (error) {
148 printf("nfs_boot: GIFFLAGS, error=%d\n", error);
149 goto out;
150 }
151 ireq.ifr_flags |= IFF_UP;
152 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp);
153 if (error) {
154 printf("nfs_boot: SIFFLAGS, error=%d\n", error);
155 goto out;
156 }
157
158 error = EADDRNOTAVAIL; /* ??? */
159 #if NARP > 0
160 if (ifp->if_type == IFT_ETHER || ifp->if_type == IFT_FDDI) {
161 /*
162 * Do RARP for the interface address.
163 */
164 error = revarpwhoami(&my_ip, ifp);
165 if (error) {
166 printf("revarp failed, error=%d\n", error);
167 goto out;
168 }
169 }
170 #endif
171
172 nd->nd_myip.s_addr = my_ip.s_addr;
173 printf("nfs_boot: client_addr=0x%x\n",
174 (u_int32_t)ntohl(my_ip.s_addr));
175
176 /*
177 * Do enough of ifconfig(8) so that the chosen interface
178 * can talk to the servers. (just set the address)
179 */
180 sin = (struct sockaddr_in *)&ireq.ifr_addr;
181 sin->sin_len = sizeof(*sin);
182 sin->sin_family = AF_INET;
183 sin->sin_addr = my_ip;
184 error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp);
185 if (error) {
186 printf("nfs_boot: set ifaddr, error=%d\n", error);
187 goto out;
188 }
189
190 /*
191 * Get client name and gateway address.
192 * RPC: bootparam/whoami
193 * Use the old broadcast address for the WHOAMI
194 * call because we do not yet know our netmask.
195 * The server address returned by the WHOAMI call
196 * is used for all subsequent booptaram RPCs.
197 */
198 sin = &bp_sin;
199 memset((caddr_t)sin, 0, sizeof(*sin));
200 sin->sin_len = sizeof(*sin);
201 sin->sin_family = AF_INET;
202 sin->sin_addr.s_addr = INADDR_BROADCAST;
203
204 /* Do the RPC/bootparam/whoami. */
205 error = bp_whoami(sin, &my_ip, &gw_ip);
206 if (error) {
207 printf("nfs_boot: bootparam whoami, error=%d\n", error);
208 goto out;
209 }
210 printf("nfs_boot: server_addr=0x%x\n",
211 (u_int32_t)ntohl(sin->sin_addr.s_addr));
212 printf("nfs_boot: hostname=%s\n", hostname);
213
214 /*
215 * Now fetch the server:pathname strings and server IP
216 * for root and swap. Missing swap is not fatal.
217 */
218 error = bp_getfile(sin, "root", &nd->nd_root);
219 if (error) {
220 printf("nfs_boot: bootparam get root: %d\n", error);
221 goto out;
222 }
223 #if 0
224 error = bp_getfile(sin, "swap", &nd->nd_swap);
225 if (error) {
226 printf("nfs_boot: bootparam get swap: %d\n", error);
227 error = 0;
228 }
229 #endif
230
231 #ifdef NFS_BOOT_GATEWAY
232 /*
233 * Note: we normally ignore the gateway address returned
234 * by the "bootparam/whoami" RPC above, because many old
235 * bootparam servers supply a bogus gateway value.
236 *
237 * These deficiencies in the bootparam RPC interface are
238 * circumvented by using the bootparam/getfile RPC. The
239 * parameter "gateway" is requested, and if its returned,
240 * we use the "server" part of the reply as the gateway,
241 * and use the "pathname" part of the reply as the mask.
242 * (The mask comes to us as a string.)
243 */
244 if (gw_ip.s_addr) {
245 /* Our caller will add the route. */
246 nd->nd_gwip = gw_ip;
247 }
248 #endif
249 #if 0 /* XXX - not yet */
250 gw_ndm = malloc(sizeof(*gw_ndm), M_NFSMNT, M_WAITOK);
251 memset((caddr_t)gw_ndm, 0, sizeof(*gw_ndm));
252 error = bp_getfile(sin, "gateway", gw_ndm);
253 if (error) {
254 /* Ignore the error. No gateway supplied. */
255 error = 0;
256 goto out;
257 }
258 printf("nfs_boot: gateway=%s\n", gw_ndm->ndm_host);
259 sin = (struct sockaddr_in *) &gw_ndm->ndm_saddr;
260 nd->nd_gwip = sin->sin_addr;
261 /* Find the pathname part of the "mounted-on" string. */
262 p = strchr(gw_ndm->ndm_host, ':');
263 if (p == 0)
264 goto out;
265 /* have pathname */
266 p++; /* skip ':' */
267 mask = inet_addr(p); /* libkern */
268 if (mask == 0) {
269 printf("nfs_boot: gateway netmask missing\n");
270 goto out;
271 }
272
273 /* Save our netmask and update the network interface. */
274 nd->nd_mask.s_addr = mask;
275 sin = (struct sockaddr_in *)&ireq.ifr_addr;
276 sin->sin_len = sizeof(*sin);
277 sin->sin_family = AF_INET;
278 sin->sin_addr = nd->nd_mask;
279 error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)&ireq, procp);
280 if (error) {
281 printf("nfs_boot: set ifmask, error=%d\n", error);
282 error = 0; /* ignore it */
283 }
284 #endif /* XXX */
285
286 out:
287 if (gw_ndm)
288 free(gw_ndm, M_NFSMNT);
289 soclose(so);
290 return (error);
291 }
292
293
294 /*
295 * RPC: bootparam/whoami
296 * Given client IP address, get:
297 * client name (hostname)
298 * domain name (domainname)
299 * gateway address
300 *
301 * The hostname and domainname are set here for convenience.
302 *
303 * Note - bpsin is initialized to the broadcast address,
304 * and will be replaced with the bootparam server address
305 * after this call is complete. Have to use PMAP_PROC_CALL
306 * to make sure we get responses only from a servers that
307 * know about us (don't want to broadcast a getport call).
308 */
309 static int
310 bp_whoami(bpsin, my_ip, gw_ip)
311 struct sockaddr_in *bpsin;
312 struct in_addr *my_ip;
313 struct in_addr *gw_ip;
314 {
315 /* RPC structures for PMAPPROC_CALLIT */
316 struct whoami_call {
317 u_int32_t call_prog;
318 u_int32_t call_vers;
319 u_int32_t call_proc;
320 u_int32_t call_arglen;
321 } *call;
322 struct callit_reply {
323 u_int32_t port;
324 u_int32_t encap_len;
325 /* encapsulated data here */
326 } *reply;
327
328 struct mbuf *m, *from;
329 struct sockaddr_in *sin;
330 int error, msg_len;
331 int16_t port;
332
333 /*
334 * Build request message for PMAPPROC_CALLIT.
335 */
336 m = m_get(M_WAIT, MT_DATA);
337 call = mtod(m, struct whoami_call *);
338 m->m_len = sizeof(*call);
339 call->call_prog = txdr_unsigned(BOOTPARAM_PROG);
340 call->call_vers = txdr_unsigned(BOOTPARAM_VERS);
341 call->call_proc = txdr_unsigned(BOOTPARAM_WHOAMI);
342
343 /*
344 * append encapsulated data (client IP address)
345 */
346 m->m_next = xdr_inaddr_encode(my_ip);
347 call->call_arglen = txdr_unsigned(m->m_next->m_len);
348
349 /* RPC: portmap/callit */
350 bpsin->sin_port = htons(PMAPPORT);
351 from = NULL;
352 error = krpc_call(bpsin, PMAPPROG, PMAPVERS,
353 PMAPPROC_CALLIT, &m, &from);
354 if (error)
355 return error;
356
357 /*
358 * Parse result message.
359 */
360 if (m->m_len < sizeof(*reply)) {
361 m = m_pullup(m, sizeof(*reply));
362 if (m == NULL)
363 goto bad;
364 }
365 reply = mtod(m, struct callit_reply *);
366 port = fxdr_unsigned(u_int32_t, reply->port);
367 msg_len = fxdr_unsigned(u_int32_t, reply->encap_len);
368 m_adj(m, sizeof(*reply));
369
370 /*
371 * Save bootparam server address
372 */
373 sin = mtod(from, struct sockaddr_in *);
374 bpsin->sin_port = htons(port);
375 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr;
376
377 /* client name */
378 hostnamelen = MAXHOSTNAMELEN-1;
379 m = xdr_string_decode(m, hostname, &hostnamelen);
380 if (m == NULL)
381 goto bad;
382
383 /* domain name */
384 domainnamelen = MAXHOSTNAMELEN-1;
385 m = xdr_string_decode(m, domainname, &domainnamelen);
386 if (m == NULL)
387 goto bad;
388
389 /* gateway address */
390 m = xdr_inaddr_decode(m, gw_ip);
391 if (m == NULL)
392 goto bad;
393
394 /* success */
395 goto out;
396
397 bad:
398 printf("nfs_boot: bootparam_whoami: bad reply\n");
399 error = EBADRPC;
400
401 out:
402 if (from)
403 m_freem(from);
404 if (m)
405 m_freem(m);
406 return(error);
407 }
408
409
410 /*
411 * RPC: bootparam/getfile
412 * Given client name and file "key", get:
413 * server name
414 * server IP address
415 * server pathname
416 */
417 static int
418 bp_getfile(bpsin, key, ndm)
419 struct sockaddr_in *bpsin;
420 char *key;
421 struct nfs_dlmount *ndm;
422 {
423 char pathname[MNAMELEN];
424 struct in_addr inaddr;
425 struct sockaddr_in *sin;
426 struct mbuf *m;
427 char *serv_name;
428 int error, sn_len, path_len;
429
430 /*
431 * Build request message.
432 */
433
434 /* client name (hostname) */
435 m = xdr_string_encode(hostname, hostnamelen);
436 if (m == NULL)
437 return (ENOMEM);
438
439 /* key name (root or swap) */
440 m->m_next = xdr_string_encode(key, strlen(key));
441 if (m->m_next == NULL)
442 return (ENOMEM);
443
444 /* RPC: bootparam/getfile */
445 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS,
446 BOOTPARAM_GETFILE, &m, NULL);
447 if (error)
448 return error;
449
450 /*
451 * Parse result message.
452 */
453
454 /* server name */
455 serv_name = &ndm->ndm_host[0];
456 sn_len = sizeof(ndm->ndm_host) - 1;
457 m = xdr_string_decode(m, serv_name, &sn_len);
458 if (m == NULL)
459 goto bad;
460
461 /* server IP address (mountd/NFS) */
462 m = xdr_inaddr_decode(m, &inaddr);
463 if (m == NULL)
464 goto bad;
465
466 /* server pathname */
467 path_len = sizeof(pathname) - 1;
468 m = xdr_string_decode(m, pathname, &path_len);
469 if (m == NULL)
470 goto bad;
471
472 /*
473 * Store the results in the nfs_dlmount.
474 * The strings become "server:pathname"
475 */
476 sin = (struct sockaddr_in *) &ndm->ndm_saddr;
477 memset((caddr_t)sin, 0, sizeof(*sin));
478 sin->sin_len = sizeof(*sin);
479 sin->sin_family = AF_INET;
480 sin->sin_addr = inaddr;
481 if ((sn_len + 1 + path_len + 1) > sizeof(ndm->ndm_host)) {
482 printf("nfs_boot: getfile name too long\n");
483 error = EIO;
484 goto out;
485 }
486 ndm->ndm_host[sn_len] = ':';
487 memcpy(ndm->ndm_host + sn_len + 1, pathname, path_len + 1);
488
489 /* success */
490 goto out;
491
492 bad:
493 printf("nfs_boot: bootparam_getfile: bad reply\n");
494 error = EBADRPC;
495
496 out:
497 m_freem(m);
498 return(0);
499 }
500
501
502