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