1 /* $NetBSD: nfs_boot.c,v 1.44 1998/03/01 02:24:27 fvdl 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, specifically getting information 41 * about where to mount root from, what pathnames, etc. 42 */ 43 44 #include "opt_nfs_boot.h" 45 46 #include <sys/param.h> 47 #include <sys/systm.h> 48 #include <sys/kernel.h> 49 #include <sys/conf.h> 50 #include <sys/device.h> 51 #include <sys/ioctl.h> 52 #include <sys/proc.h> 53 #include <sys/mount.h> 54 #include <sys/mbuf.h> 55 #include <sys/reboot.h> 56 #include <sys/socket.h> 57 #include <sys/socketvar.h> 58 59 #include <net/if.h> 60 #include <net/route.h> 61 #include <net/if_ether.h> 62 #include <net/if_types.h> 63 64 #include <netinet/in.h> 65 #include <netinet/if_inarp.h> 66 67 #include <nfs/rpcv2.h> 68 #include <nfs/krpc.h> 69 #include <nfs/xdr_subs.h> 70 71 #include <nfs/nfsproto.h> 72 #include <nfs/nfs.h> 73 #include <nfs/nfsmount.h> 74 #include <nfs/nfsdiskless.h> 75 76 /* 77 * There are two implementations of NFS diskless boot. 78 * One implementation uses BOOTP (RFC951, RFC1048), 79 * the other uses Sun RPC/bootparams. See the files: 80 * nfs_bootp.c: BOOTP (RFC951, RFC1048) 81 * nfs_bootsun.c: Sun RPC/bootparams 82 */ 83 #if defined(NFS_BOOT_BOOTP) || defined(NFS_BOOT_DHCP) 84 int nfs_boot_rfc951 = 1; /* BOOTP enabled (default) */ 85 #endif 86 #ifdef NFS_BOOT_BOOTPARAM 87 int nfs_boot_bootparam = 1; /* BOOTPARAM enabled (default) */ 88 #endif 89 90 /* mountd RPC */ 91 static int md_mount __P((struct sockaddr_in *mdsin, char *path, 92 struct nfs_args *argp)); 93 94 static void nfs_boot_defrt __P((struct in_addr *)); 95 static int nfs_boot_getfh __P((struct nfs_dlmount *ndm)); 96 97 98 /* 99 * Called with an empty nfs_diskless struct to be filled in. 100 * Find an interface, determine its ip address (etc.) and 101 * save all the boot parameters in the nfs_diskless struct. 102 */ 103 int 104 nfs_boot_init(nd, procp) 105 struct nfs_diskless *nd; 106 struct proc *procp; 107 { 108 struct ifnet *ifp; 109 int error; 110 111 /* 112 * Find the network interface. 113 */ 114 ifp = ifunit(root_device->dv_xname); 115 if (ifp == NULL) { 116 printf("nfs_boot: '%s' not found\n", 117 root_device->dv_xname); 118 return (ENXIO); 119 } 120 121 error = EADDRNOTAVAIL; /* ??? */ 122 #if defined(NFS_BOOT_BOOTP) || defined(NFS_BOOT_DHCP) 123 if (nfs_boot_rfc951) { 124 #if defined(NFS_BOOT_DHCP) 125 printf("nfs_boot: trying DHCP/BOOTP\n"); 126 #else 127 printf("nfs_boot: trying BOOTP\n"); 128 #endif 129 error = nfs_bootdhcp(ifp, nd, procp); 130 if (!error) 131 goto ok; 132 } 133 #endif 134 #ifdef NFS_BOOT_BOOTPARAM 135 if (nfs_boot_bootparam) { 136 printf("nfs_boot: trying RARP (and RPC/bootparam)\n"); 137 error = nfs_bootparam(ifp, nd, procp); 138 if (!error) 139 goto ok; 140 } 141 #endif 142 if (0) goto ok; /* XXX stupid gcc */ 143 return (error); 144 145 ok: 146 /* 147 * If the gateway address is set, add a default route. 148 * (The mountd RPCs may go across a gateway.) 149 */ 150 if (nd->nd_gwip.s_addr) 151 nfs_boot_defrt(&nd->nd_gwip); 152 153 /* 154 * Now fetch the NFS file handles as appropriate. 155 */ 156 error = nfs_boot_getfh(&nd->nd_root); 157 if (error) 158 return (error); 159 160 return (error); 161 } 162 163 int nfs_boot_setrecvtimo(so) 164 struct socket *so; 165 { 166 struct mbuf *m; 167 struct timeval *tv; 168 169 m = m_get(M_WAIT, MT_SOOPTS); 170 tv = mtod(m, struct timeval *); 171 m->m_len = sizeof(*tv); 172 tv->tv_sec = 1; 173 tv->tv_usec = 0; 174 return(sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m)); 175 } 176 177 int nfs_boot_enbroadcast(so) 178 struct socket *so; 179 { 180 struct mbuf *m; 181 int32_t *on; 182 183 m = m_get(M_WAIT, MT_SOOPTS); 184 on = mtod(m, int32_t *); 185 m->m_len = sizeof(*on); 186 *on = 1; 187 return(sosetopt(so, SOL_SOCKET, SO_BROADCAST, m)); 188 } 189 190 int nfs_boot_sobind_ipport(so, port) 191 struct socket *so; 192 u_int16_t port; 193 { 194 struct mbuf *m; 195 struct sockaddr_in *sin; 196 int error; 197 198 m = m_getclr(M_WAIT, MT_SONAME); 199 sin = mtod(m, struct sockaddr_in *); 200 sin->sin_len = m->m_len = sizeof(*sin); 201 sin->sin_family = AF_INET; 202 sin->sin_addr.s_addr = INADDR_ANY; 203 sin->sin_port = htons(port); 204 error = sobind(so, m); 205 m_freem(m); 206 return(error); 207 } 208 209 /* 210 * What is the longest we will wait before re-sending a request? 211 * Note this is also the frequency of "timeout" messages. 212 * The re-send loop counts up linearly to this maximum, so the 213 * first complaint will happen after (1+2+3+4+5)=15 seconds. 214 */ 215 #define MAX_RESEND_DELAY 5 /* seconds */ 216 #define TOTAL_TIMEOUT 30 /* seconds */ 217 218 int nfs_boot_sendrecv(so, nam, sndproc, snd, rcvproc, rcv, 219 from_p, context) 220 struct socket *so; 221 struct mbuf *nam; 222 int (*sndproc) __P((struct mbuf*, void*, int)); 223 struct mbuf *snd; 224 int (*rcvproc) __P((struct mbuf*, void*)); 225 struct mbuf **rcv, **from_p; 226 void *context; 227 { 228 int error, rcvflg, timo, secs, waited; 229 struct mbuf *m, *from; 230 struct uio uio; 231 232 /* Free at end if not null. */ 233 from = NULL; 234 235 /* 236 * Send it, repeatedly, until a reply is received, 237 * but delay each re-send by an increasing amount. 238 * If the delay hits the maximum, start complaining. 239 */ 240 waited = timo = 0; 241 send_again: 242 waited += timo; 243 if (waited >= TOTAL_TIMEOUT) 244 return(ETIMEDOUT); 245 246 /* Determine new timeout. */ 247 if (timo < MAX_RESEND_DELAY) 248 timo++; 249 else 250 printf("nfs_boot: timeout...\n"); 251 252 if (sndproc) { 253 error = (*sndproc)(snd, context, waited); 254 if (error) 255 goto out; 256 } 257 258 /* Send request (or re-send). */ 259 m = m_copypacket(snd, M_WAIT); 260 if (m == NULL) { 261 error = ENOBUFS; 262 goto out; 263 } 264 error = sosend(so, nam, NULL, m, NULL, 0); 265 if (error) { 266 printf("nfs_boot: sosend: %d\n", error); 267 goto out; 268 } 269 m = NULL; 270 271 /* 272 * Wait for up to timo seconds for a reply. 273 * The socket receive timeout was set to 1 second. 274 */ 275 276 secs = timo; 277 for (;;) { 278 if (from) { 279 m_freem(from); 280 from = NULL; 281 } 282 if (m) { 283 m_freem(m); 284 m = NULL; 285 } 286 uio.uio_resid = 1 << 16; /* ??? */ 287 rcvflg = 0; 288 error = soreceive(so, &from, &uio, &m, NULL, &rcvflg); 289 if (error == EWOULDBLOCK) { 290 if (--secs <= 0) 291 goto send_again; 292 continue; 293 } 294 if (error) 295 goto out; 296 #ifdef DIAGNOSTIC 297 if (!m || !(m->m_flags & M_PKTHDR) 298 || (1 << 16) - uio.uio_resid != m->m_pkthdr.len) 299 panic("nfs_boot_sendrecv: return size"); 300 #endif 301 302 if ((*rcvproc)(m, context)) 303 continue; 304 305 if (rcv) 306 *rcv = m; 307 else 308 m_freem(m); 309 if (from_p) { 310 *from_p = from; 311 from = NULL; 312 } 313 break; 314 } 315 out: 316 if (from) m_freem(from); 317 return(error); 318 } 319 320 /* 321 * Install a default route to the passed IP address. 322 */ 323 static void 324 nfs_boot_defrt(gw_ip) 325 struct in_addr *gw_ip; 326 { 327 struct sockaddr dst, gw, mask; 328 struct sockaddr_in *sin; 329 int error; 330 331 /* Destination: (default) */ 332 bzero((caddr_t)&dst, sizeof(dst)); 333 dst.sa_len = sizeof(dst); 334 dst.sa_family = AF_INET; 335 /* Gateway: */ 336 bzero((caddr_t)&gw, sizeof(gw)); 337 sin = (struct sockaddr_in *)&gw; 338 sin->sin_len = sizeof(*sin); 339 sin->sin_family = AF_INET; 340 sin->sin_addr.s_addr = gw_ip->s_addr; 341 /* Mask: (zero length) */ 342 /* XXX - Just pass a null pointer? */ 343 bzero(&mask, sizeof(mask)); 344 345 /* add, dest, gw, mask, flags, 0 */ 346 error = rtrequest(RTM_ADD, &dst, &gw, &mask, 347 (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL); 348 if (error) { 349 printf("nfs_boot: add route, error=%d\n", error); 350 error = 0; 351 } 352 } 353 354 /* 355 * Get an initial NFS file handle using Sun RPC/mountd. 356 * Separate function because we used to call it twice. 357 * (once for root and once for swap) 358 */ 359 static int 360 nfs_boot_getfh(ndm) 361 struct nfs_dlmount *ndm; /* output */ 362 { 363 struct nfs_args *args; 364 struct sockaddr_in *sin; 365 char *pathname; 366 int error; 367 u_int16_t port; 368 369 args = &ndm->ndm_args; 370 371 /* Initialize mount args. */ 372 bzero((caddr_t) args, sizeof(*args)); 373 args->addr = &ndm->ndm_saddr; 374 args->addrlen = args->addr->sa_len; 375 #ifdef NFS_BOOT_TCP 376 args->sotype = SOCK_STREAM; 377 #else 378 args->sotype = SOCK_DGRAM; 379 #endif 380 args->fh = ndm->ndm_fh; 381 args->hostname = ndm->ndm_host; 382 args->flags = NFSMNT_RESVPORT | NFSMNT_NFSV3; 383 384 #ifdef NFS_BOOT_OPTIONS 385 args->flags |= NFS_BOOT_OPTIONS; 386 #endif 387 #ifdef NFS_BOOT_RWSIZE 388 /* 389 * Reduce rsize,wsize for interfaces that consistently 390 * drop fragments of long UDP messages. (i.e. wd8003). 391 * You can always change these later via remount. 392 */ 393 args->flags |= NFSMNT_WSIZE | NFSMNT_RSIZE; 394 args->wsize = NFS_BOOT_RWSIZE; 395 args->rsize = NFS_BOOT_RWSIZE; 396 #endif 397 398 /* 399 * Find the pathname part of the "server:pathname" 400 * string left in ndm->ndm_host by nfs_boot_init. 401 */ 402 pathname = strchr(ndm->ndm_host, ':'); 403 if (pathname == 0) { 404 printf("nfs_boot: getfh - no pathname\n"); 405 return (EIO); 406 } 407 pathname++; 408 409 /* 410 * Get file handle using RPC to mountd/mount 411 */ 412 sin = (struct sockaddr_in *)&ndm->ndm_saddr; 413 error = md_mount(sin, pathname, args); 414 if (error) { 415 printf("nfs_boot: mountd `%s', error=%d\n", 416 ndm->ndm_host, error); 417 return (error); 418 } 419 420 /* Set port number for NFS use. */ 421 /* XXX: NFS port is always 2049, right? */ 422 #ifdef NFS_BOOT_TCP 423 retry: 424 #endif 425 error = krpc_portmap(sin, NFS_PROG, 426 (args->flags & NFSMNT_NFSV3) ? NFS_VER3 : NFS_VER2, 427 (args->sotype == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP, 428 &port); 429 if (port == htons(0)) 430 error = EIO; 431 if (error) { 432 #ifdef NFS_BOOT_TCP 433 if (args->sotype == SOCK_STREAM) { 434 args->sotype = SOCK_DGRAM; 435 goto retry; 436 } 437 #endif 438 printf("nfs_boot: portmap NFS, error=%d\n", error); 439 return (error); 440 } 441 sin->sin_port = port; 442 return (0); 443 } 444 445 446 /* 447 * RPC: mountd/mount 448 * Given a server pathname, get an NFS file handle. 449 * Also, sets sin->sin_port to the NFS service port. 450 */ 451 static int 452 md_mount(mdsin, path, argp) 453 struct sockaddr_in *mdsin; /* mountd server address */ 454 char *path; 455 struct nfs_args *argp; 456 { 457 /* The RPC structures */ 458 struct rdata { 459 u_int32_t errno; 460 union { 461 u_int8_t v2fh[NFSX_V2FH]; 462 struct { 463 u_int32_t fhlen; 464 u_int8_t fh[1]; 465 } v3fh; 466 } fh; 467 } *rdata; 468 struct mbuf *m; 469 u_int8_t *fh; 470 int minlen, error; 471 int mntver; 472 473 mntver = (argp->flags & NFSMNT_NFSV3) ? 3 : 2; 474 do { 475 /* 476 * Get port number for MOUNTD. 477 */ 478 error = krpc_portmap(mdsin, RPCPROG_MNT, mntver, 479 IPPROTO_UDP, &mdsin->sin_port); 480 if (error) 481 continue; 482 483 /* This mbuf is consumed by krpc_call. */ 484 m = xdr_string_encode(path, strlen(path)); 485 if (m == NULL) 486 return ENOMEM; 487 488 /* Do RPC to mountd. */ 489 error = krpc_call(mdsin, RPCPROG_MNT, mntver, 490 RPCMNT_MOUNT, &m, NULL); 491 if (error != EPROGMISMATCH) 492 break; 493 /* Try lower version of mountd. */ 494 } while (--mntver >= 1); 495 if (error) { 496 printf("nfs_boot: mountd error=%d\n", error); 497 return error; 498 } 499 if (mntver != 3) 500 argp->flags &= ~NFSMNT_NFSV3; 501 502 /* The reply might have only the errno. */ 503 if (m->m_len < 4) 504 goto bad; 505 /* Have at least errno, so check that. */ 506 rdata = mtod(m, struct rdata *); 507 error = fxdr_unsigned(u_int32_t, rdata->errno); 508 if (error) 509 goto out; 510 511 /* Have errno==0, so the fh must be there. */ 512 if (mntver == 3) { 513 argp->fhsize = fxdr_unsigned(u_int32_t, rdata->fh.v3fh.fhlen); 514 if (argp->fhsize > NFSX_V3FHMAX) 515 goto bad; 516 minlen = 2 * sizeof(u_int32_t) + argp->fhsize; 517 } else { 518 argp->fhsize = NFSX_V2FH; 519 minlen = sizeof(u_int32_t) + argp->fhsize; 520 } 521 522 if (m->m_len < minlen) { 523 m = m_pullup(m, minlen); 524 if (m == NULL) 525 return(EBADRPC); 526 rdata = mtod(m, struct rdata *); 527 } 528 529 fh = (mntver == 3) ? 530 rdata->fh.v3fh.fh : rdata->fh.v2fh; 531 bcopy(fh, argp->fh, argp->fhsize); 532 533 goto out; 534 535 bad: 536 error = EBADRPC; 537 538 out: 539 m_freem(m); 540 return error; 541 } 542