nfs_boot.c revision 1.28
1/* $OpenBSD: nfs_boot.c,v 1.28 2013/09/12 13:12:33 deraadt 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#include <sys/queue.h> 43 44#include <net/if.h> 45#include <net/route.h> 46 47#include <netinet/in.h> 48#include <netinet/in_var.h> 49#include <netinet/if_ether.h> 50 51#include <nfs/rpcv2.h> 52#include <nfs/nfsproto.h> 53#include <nfs/nfs.h> 54#include <nfs/nfsdiskless.h> 55#include <nfs/krpc.h> 56#include <nfs/xdr_subs.h> 57#include <nfs/nfs_var.h> 58 59#include "ether.h" 60 61#if !defined(NFSCLIENT) || (NETHER == 0 && NFDDI == 0) 62 63int 64nfs_boot_init(struct nfs_diskless *nd, struct proc *procp) 65{ 66 panic("nfs_boot_init: NFSCLIENT not enabled in kernel"); 67} 68 69int 70nfs_boot_getfh(struct sockaddr_in *bpsin, char *key, 71 struct nfs_dlmount *ndmntp, int retries) 72{ 73 /* can not get here */ 74 return (EOPNOTSUPP); 75} 76 77#else 78 79/* 80 * Support for NFS diskless booting, specifically getting information 81 * about where to boot from, what pathnames, etc. 82 * 83 * This implementation uses RARP and the bootparam RPC. 84 * We are forced to implement RPC anyway (to get file handles) 85 * so we might as well take advantage of it for bootparam too. 86 * 87 * The diskless boot sequence goes as follows: 88 * (1) Use RARP to get our interface address 89 * (2) Use RPC/bootparam/whoami to get our hostname, 90 * our IP address, and the server's IP address. 91 * (3) Use RPC/bootparam/getfile to get the root path 92 * (4) Use RPC/mountd to get the root file handle 93 * (5) Use RPC/bootparam/getfile to get the swap path 94 * (6) Use RPC/mountd to get the swap file handle 95 * 96 * (This happens to be the way Sun does it too.) 97 */ 98 99/* bootparam RPC */ 100static int bp_whoami(struct sockaddr_in *bpsin, 101 struct in_addr *my_ip, struct in_addr *gw_ip); 102static int bp_getfile(struct sockaddr_in *bpsin, char *key, 103 struct sockaddr_in *mdsin, char *servname, char *path, int retries); 104 105/* mountd RPC */ 106static int md_mount(struct sockaddr_in *mdsin, char *path, 107 u_char *fh); 108 109char *nfsbootdevname; 110 111/* 112 * Called with an empty nfs_diskless struct to be filled in. 113 */ 114int 115nfs_boot_init(struct nfs_diskless *nd, struct proc *procp) 116{ 117 struct ifreq ireq; 118 struct in_aliasreq ifra; 119 struct in_addr my_ip, gw_ip; 120 struct sockaddr_in bp_sin; 121 struct sockaddr_in *sin; 122 struct ifnet *ifp; 123 struct socket *so; 124 int error; 125 126 /* 127 * Find an interface, rarp for its ip address, stuff it, the 128 * implied broadcast addr, and netmask into a nfs_diskless struct. 129 * 130 * This was moved here from nfs_vfsops.c because this procedure 131 * would be quite different if someone decides to write (i.e.) a 132 * BOOTP version of this file (might not use RARP, etc.) 133 */ 134 135 /* 136 * Find a network interface. 137 */ 138 if (nfsbootdevname) 139 ifp = ifunit(nfsbootdevname); 140 else { 141 TAILQ_FOREACH(ifp, &ifnet, if_list) { 142 if ((ifp->if_flags & 143 (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0) 144 break; 145 } 146 } 147 if (ifp == NULL) 148 panic("nfs_boot: no suitable interface"); 149 bcopy(ifp->if_xname, ireq.ifr_name, IFNAMSIZ); 150 printf("nfs_boot: using interface %s, with revarp & bootparams\n", 151 ireq.ifr_name); 152 153 /* 154 * Bring up the interface. 155 * 156 * Get the old interface flags and or IFF_UP into them; if 157 * IFF_UP set blindly, interface selection can be clobbered. 158 */ 159 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0) 160 panic("nfs_boot: socreate, error=%d", error); 161 error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)&ireq, procp); 162 if (error) 163 panic("nfs_boot: GIFFLAGS, error=%d", error); 164 ireq.ifr_flags |= IFF_UP; 165 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp); 166 if (error) 167 panic("nfs_boot: SIFFLAGS, error=%d", error); 168 169 /* 170 * Do RARP for the interface address. 171 */ 172 if ((error = revarpwhoami(&my_ip, ifp)) != 0) 173 panic("reverse arp not answered by rarpd(8) or dhcpd(8)"); 174 printf("nfs_boot: client_addr=%s\n", inet_ntoa(my_ip)); 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 bzero(&ifra, sizeof(ifra)); 181 bcopy(ifp->if_xname, ifra.ifra_name, sizeof(ifra.ifra_name)); 182 183 sin = (struct sockaddr_in *)&ifra.ifra_addr; 184 sin->sin_len = sizeof(*sin); 185 sin->sin_family = AF_INET; 186 sin->sin_addr.s_addr = my_ip.s_addr; 187 error = ifioctl(so, SIOCAIFADDR, (caddr_t)&ifra, procp); 188 if (error) 189 panic("nfs_boot: set if addr, error=%d", error); 190 191 soclose(so); 192 193 /* 194 * Get client name and gateway address. 195 * RPC: bootparam/whoami 196 * Use the old broadcast address for the WHOAMI 197 * call because we do not yet know our netmask. 198 * The server address returned by the WHOAMI call 199 * is used for all subsequent bootparam RPCs. 200 */ 201 bzero((caddr_t)&bp_sin, sizeof(bp_sin)); 202 bp_sin.sin_len = sizeof(bp_sin); 203 bp_sin.sin_family = AF_INET; 204 bp_sin.sin_addr.s_addr = INADDR_BROADCAST; 205 hostnamelen = MAXHOSTNAMELEN; 206 207 /* this returns gateway IP address */ 208 error = bp_whoami(&bp_sin, &my_ip, &gw_ip); 209 if (error) 210 panic("nfs_boot: bootparam whoami, error=%d", error); 211 printf("nfs_boot: server_addr=%s hostname=%s\n", 212 inet_ntoa(bp_sin.sin_addr), hostname); 213 214#ifdef NFS_BOOT_GATEWAY 215 /* 216 * XXX - This code is conditionally compiled only because 217 * many bootparam servers (in particular, SunOS 4.1.3) 218 * always set the gateway address to their own address. 219 * The bootparam server is not necessarily the gateway. 220 * We could just believe the server, and at worst you would 221 * need to delete the incorrect default route before adding 222 * the correct one, but for simplicity, ignore the gateway. 223 * If your server is OK, you can turn on this option. 224 * 225 * If the gateway address is set, add a default route. 226 * (The mountd RPCs may go across a gateway.) 227 */ 228 if (gw_ip.s_addr) { 229 struct sockaddr dst, gw, mask; 230 struct rt_addrinfo info; 231 /* Destination: (default) */ 232 bzero((caddr_t)&dst, sizeof(dst)); 233 dst.sa_len = sizeof(dst); 234 dst.sa_family = AF_INET; 235 /* Gateway: */ 236 bzero((caddr_t)&gw, sizeof(gw)); 237 sin = (struct sockaddr_in *)&gw; 238 sin->sin_len = sizeof(gw); 239 sin->sin_family = AF_INET; 240 sin->sin_addr.s_addr = gw_ip.s_addr; 241 /* Mask: (zero length) */ 242 bzero(&mask, sizeof(mask)); 243 bzero(&info, sizeof(info)); 244 info.rti_info[RTAX_DST] = &dst; 245 info.rti_info[RTAX_GATEWAY] = &gw; 246 info.rti_info[RTAX_NETMASK] = &mask; 247 info.rti_flags = (RTF_UP | RTF_GATEWAY | RTF_STATIC); 248 249 printf("nfs_boot: gateway=%s\n", inet_ntoa(gw_ip)); 250 /* add, dest, gw, mask, flags, 0 */ 251 error = rtrequest1(RTM_ADD, &info, RTP_STATIC, NULL, 0); 252 if (error) 253 printf("nfs_boot: add route, error=%d\n", error); 254 } 255#endif 256 257 bcopy(&bp_sin, &nd->nd_boot, sizeof(bp_sin)); 258 259 return (0); 260} 261 262/* 263 * bpsin: bootparam server 264 * key: root or swap 265 * ndmntp: output 266 */ 267int 268nfs_boot_getfh(struct sockaddr_in *bpsin, char *key, 269 struct nfs_dlmount *ndmntp, int retries) 270{ 271 char pathname[MAXPATHLEN]; 272 char *sp, *dp, *endp; 273 struct sockaddr_in *sin; 274 int error; 275 276 sin = &ndmntp->ndm_saddr; 277 278 /* 279 * Get server:pathname for "key" (root or swap) 280 * using RPC to bootparam/getfile 281 */ 282 error = bp_getfile(bpsin, key, sin, ndmntp->ndm_host, pathname, 283 retries); 284 if (error) { 285 printf("nfs_boot: bootparam get %s: %d\n", key, error); 286 return (error); 287 } 288 289 /* 290 * Get file handle for "key" (root or swap) 291 * using RPC to mountd/mount 292 */ 293 error = md_mount(sin, pathname, ndmntp->ndm_fh); 294 if (error) { 295 printf("nfs_boot: mountd %s, error=%d\n", key, error); 296 return (error); 297 } 298 299 /* Set port number for NFS use. */ 300 /* XXX: NFS port is always 2049, right? */ 301 error = krpc_portmap(sin, NFS_PROG, NFS_VER2, &sin->sin_port); 302 if (error) { 303 printf("nfs_boot: portmap NFS/v2, error=%d\n", error); 304 return (error); 305 } 306 307 /* Construct remote path (for getmntinfo(3)) */ 308 dp = ndmntp->ndm_host; 309 endp = dp + MNAMELEN - 1; 310 dp += strlen(dp); 311 *dp++ = ':'; 312 for (sp = pathname; *sp && dp < endp;) 313 *dp++ = *sp++; 314 *dp = '\0'; 315 316 return (0); 317} 318 319 320/* 321 * RPC: bootparam/whoami 322 * Given client IP address, get: 323 * client name (hostname) 324 * domain name (domainname) 325 * gateway address 326 * 327 * The hostname and domainname are set here for convenience. 328 * 329 * Note - bpsin is initialized to the broadcast address, 330 * and will be replaced with the bootparam server address 331 * after this call is complete. Have to use PMAP_PROC_CALL 332 * to make sure we get responses only from a servers that 333 * know about us (don't want to broadcast a getport call). 334 */ 335static int 336bp_whoami(struct sockaddr_in *bpsin, struct in_addr *my_ip, 337 struct in_addr *gw_ip) 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 = MAXHOSTNAMELEN-1; 409 m = xdr_string_decode(m, domainname, &domainnamelen); 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 421bad: 422 printf("nfs_boot: bootparam_whoami: bad reply\n"); 423 error = EBADRPC; 424 425out: 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 */ 441static int 442bp_getfile(struct sockaddr_in *bpsin, char *key, struct sockaddr_in *md_sin, 443 char *serv_name, char *pathname, int retries) 444{ 445 struct mbuf *m; 446 struct sockaddr_in *sin; 447 struct in_addr inaddr; 448 int error, sn_len, path_len; 449 450 /* 451 * Build request message. 452 */ 453 454 /* client name (hostname) */ 455 m = xdr_string_encode(hostname, hostnamelen); 456 if (m == NULL) 457 return (ENOMEM); 458 459 /* key name (root or swap) */ 460 m->m_next = xdr_string_encode(key, strlen(key)); 461 if (m->m_next == NULL) 462 return (ENOMEM); 463 464 /* RPC: bootparam/getfile */ 465 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS, 466 BOOTPARAM_GETFILE, &m, NULL, retries); 467 if (error) 468 return error; 469 470 /* 471 * Parse result message. 472 */ 473 474 /* server name */ 475 sn_len = MNAMELEN-1; 476 m = xdr_string_decode(m, serv_name, &sn_len); 477 if (m == NULL) 478 goto bad; 479 480 /* server IP address (mountd/NFS) */ 481 m = xdr_inaddr_decode(m, &inaddr); 482 if (m == NULL) 483 goto bad; 484 485 /* server pathname */ 486 path_len = MAXPATHLEN-1; 487 m = xdr_string_decode(m, pathname, &path_len); 488 if (m == NULL) 489 goto bad; 490 491 /* setup server socket address */ 492 sin = md_sin; 493 bzero((caddr_t)sin, sizeof(*sin)); 494 sin->sin_len = sizeof(*sin); 495 sin->sin_family = AF_INET; 496 sin->sin_addr = inaddr; 497 498 /* success */ 499 goto out; 500 501bad: 502 printf("nfs_boot: bootparam_getfile: bad reply\n"); 503 error = EBADRPC; 504 505out: 506 m_freem(m); 507 return(error); 508} 509 510 511/* 512 * RPC: mountd/mount 513 * Given a server pathname, get an NFS file handle. 514 * Also, sets sin->sin_port to the NFS service port. 515 * mdsin: mountd server address 516 */ 517static int 518md_mount(struct sockaddr_in *mdsin, char *path, u_char *fhp) 519{ 520 /* The RPC structures */ 521 struct rdata { 522 u_int32_t errno; 523 u_int8_t fh[NFSX_V2FH]; 524 } *rdata; 525 struct mbuf *m; 526 int error; 527 528 /* Get port number for MOUNTD. */ 529 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1, 530 &mdsin->sin_port); 531 if (error) return error; 532 533 m = xdr_string_encode(path, strlen(path)); 534 if (m == NULL) 535 return ENOMEM; 536 537 /* Do RPC to mountd. */ 538 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1, 539 RPCMNT_MOUNT, &m, NULL, -1); 540 if (error) 541 return error; /* message already freed */ 542 543 /* The reply might have only the errno. */ 544 if (m->m_len < 4) 545 goto bad; 546 /* Have at least errno, so check that. */ 547 rdata = mtod(m, struct rdata *); 548 error = fxdr_unsigned(u_int32_t, rdata->errno); 549 if (error) 550 goto out; 551 552 /* Have errno==0, so the fh must be there. */ 553 if (m->m_len < sizeof(*rdata)) { 554 m = m_pullup(m, sizeof(*rdata)); 555 if (m == NULL) 556 goto bad; 557 rdata = mtod(m, struct rdata *); 558 } 559 bcopy(rdata->fh, fhp, NFSX_V2FH); 560 goto out; 561 562bad: 563 error = EBADRPC; 564 565out: 566 m_freem(m); 567 return error; 568} 569 570#endif /* ifdef NFSCLIENT */ 571