1 /* $OpenBSD: nfs_boot.c,v 1.24 2008/06/11 04:52:27 blambert 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/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 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 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 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 TAILQ_FOREACH(ifp, &ifnet, if_list) { 147 if ((ifp->if_flags & 148 (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0) 149 break; 150 } 151 } 152 if (ifp == NULL) 153 panic("nfs_boot: no suitable interface"); 154 bcopy(ifp->if_xname, ireq.ifr_name, IFNAMSIZ); 155 printf("nfs_boot: using interface %s, with revarp & bootparams\n", 156 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("reverse arp not answered by rarpd(8) or dhcpd(8)"); 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 struct rt_addrinfo info; 234 /* Destination: (default) */ 235 bzero((caddr_t)&dst, sizeof(dst)); 236 dst.sa_len = sizeof(dst); 237 dst.sa_family = AF_INET; 238 /* Gateway: */ 239 bzero((caddr_t)&gw, sizeof(gw)); 240 sin = (struct sockaddr_in *)&gw; 241 sin->sin_len = sizeof(gw); 242 sin->sin_family = AF_INET; 243 sin->sin_addr.s_addr = gw_ip.s_addr; 244 /* Mask: (zero length) */ 245 bzero(&mask, sizeof(mask)); 246 bzero(&info, sizeof(info)); 247 info.rti_info[RTAX_DST] = &dst; 248 info.rti_info[RTAX_GATEWAY] = &gw; 249 info.rti_info[RTAX_NETMASK] = &mask; 250 info.rti_flags = (RTF_UP | RTF_GATEWAY | RTF_STATIC); 251 252 printf("nfs_boot: gateway=%s\n", inet_ntoa(gw_ip)); 253 /* add, dest, gw, mask, flags, 0 */ 254 error = rtrequest1(RTM_ADD, &info, RTP_STATIC, NULL, 0); 255 if (error) 256 printf("nfs_boot: add route, error=%d\n", error); 257 } 258 #endif 259 260 bcopy(&bp_sin, &nd->nd_boot, sizeof(bp_sin)); 261 262 return (0); 263 } 264 265 int 266 nfs_boot_getfh(bpsin, key, ndmntp, retries) 267 struct sockaddr_in *bpsin; /* bootparam server */ 268 char *key; /* root or swap */ 269 struct nfs_dlmount *ndmntp; /* output */ 270 int retries; 271 { 272 char pathname[MAXPATHLEN]; 273 char *sp, *dp, *endp; 274 struct sockaddr_in *sin; 275 int error; 276 277 sin = &ndmntp->ndm_saddr; 278 279 /* 280 * Get server:pathname for "key" (root or swap) 281 * using RPC to bootparam/getfile 282 */ 283 error = bp_getfile(bpsin, key, sin, ndmntp->ndm_host, pathname, 284 retries); 285 if (error) { 286 printf("nfs_boot: bootparam get %s: %d\n", key, error); 287 return (error); 288 } 289 290 /* 291 * Get file handle for "key" (root or swap) 292 * using RPC to mountd/mount 293 */ 294 error = md_mount(sin, pathname, ndmntp->ndm_fh); 295 if (error) { 296 printf("nfs_boot: mountd %s, error=%d\n", key, error); 297 return (error); 298 } 299 300 /* Set port number for NFS use. */ 301 /* XXX: NFS port is always 2049, right? */ 302 error = krpc_portmap(sin, NFS_PROG, NFS_VER2, &sin->sin_port); 303 if (error) { 304 printf("nfs_boot: portmap NFS/v2, error=%d\n", error); 305 return (error); 306 } 307 308 /* Construct remote path (for getmntinfo(3)) */ 309 dp = ndmntp->ndm_host; 310 endp = dp + MNAMELEN - 1; 311 dp += strlen(dp); 312 *dp++ = ':'; 313 for (sp = pathname; *sp && dp < endp;) 314 *dp++ = *sp++; 315 *dp = '\0'; 316 317 return (0); 318 } 319 320 321 /* 322 * RPC: bootparam/whoami 323 * Given client IP address, get: 324 * client name (hostname) 325 * domain name (domainname) 326 * gateway address 327 * 328 * The hostname and domainname are set here for convenience. 329 * 330 * Note - bpsin is initialized to the broadcast address, 331 * and will be replaced with the bootparam server address 332 * after this call is complete. Have to use PMAP_PROC_CALL 333 * to make sure we get responses only from a servers that 334 * know about us (don't want to broadcast a getport call). 335 */ 336 static int 337 bp_whoami(bpsin, my_ip, gw_ip) 338 struct sockaddr_in *bpsin; 339 struct in_addr *my_ip; 340 struct in_addr *gw_ip; 341 { 342 /* RPC structures for PMAPPROC_CALLIT */ 343 struct whoami_call { 344 u_int32_t call_prog; 345 u_int32_t call_vers; 346 u_int32_t call_proc; 347 u_int32_t call_arglen; 348 } *call; 349 struct callit_reply { 350 u_int32_t port; 351 u_int32_t encap_len; 352 /* encapsulated data here */ 353 } *reply; 354 355 struct mbuf *m, *from; 356 struct sockaddr_in *sin; 357 int error, msg_len; 358 int16_t port; 359 360 /* 361 * Build request message for PMAPPROC_CALLIT. 362 */ 363 m = m_get(M_WAIT, MT_DATA); 364 call = mtod(m, struct whoami_call *); 365 m->m_len = sizeof(*call); 366 call->call_prog = txdr_unsigned(BOOTPARAM_PROG); 367 call->call_vers = txdr_unsigned(BOOTPARAM_VERS); 368 call->call_proc = txdr_unsigned(BOOTPARAM_WHOAMI); 369 370 /* 371 * append encapsulated data (client IP address) 372 */ 373 m->m_next = xdr_inaddr_encode(my_ip); 374 call->call_arglen = txdr_unsigned(m->m_next->m_len); 375 376 /* RPC: portmap/callit */ 377 bpsin->sin_port = htons(PMAPPORT); 378 from = NULL; 379 error = krpc_call(bpsin, PMAPPROG, PMAPVERS, 380 PMAPPROC_CALLIT, &m, &from, -1); 381 if (error) 382 return error; 383 384 /* 385 * Parse result message. 386 */ 387 if (m->m_len < sizeof(*reply)) { 388 m = m_pullup(m, sizeof(*reply)); 389 if (m == NULL) 390 goto bad; 391 } 392 reply = mtod(m, struct callit_reply *); 393 port = fxdr_unsigned(u_int32_t, reply->port); 394 msg_len = fxdr_unsigned(u_int32_t, reply->encap_len); 395 m_adj(m, sizeof(*reply)); 396 397 /* 398 * Save bootparam server address 399 */ 400 sin = mtod(from, struct sockaddr_in *); 401 bpsin->sin_port = htons(port); 402 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr; 403 404 /* client name */ 405 hostnamelen = MAXHOSTNAMELEN-1; 406 m = xdr_string_decode(m, hostname, &hostnamelen); 407 if (m == NULL) 408 goto bad; 409 410 /* domain name */ 411 domainnamelen = MAXHOSTNAMELEN-1; 412 m = xdr_string_decode(m, domainname, &domainnamelen); 413 if (m == NULL) 414 goto bad; 415 416 /* gateway address */ 417 m = xdr_inaddr_decode(m, gw_ip); 418 if (m == NULL) 419 goto bad; 420 421 /* success */ 422 goto out; 423 424 bad: 425 printf("nfs_boot: bootparam_whoami: bad reply\n"); 426 error = EBADRPC; 427 428 out: 429 if (from) 430 m_freem(from); 431 if (m) 432 m_freem(m); 433 return(error); 434 } 435 436 437 /* 438 * RPC: bootparam/getfile 439 * Given client name and file "key", get: 440 * server name 441 * server IP address 442 * server pathname 443 */ 444 static int 445 bp_getfile(bpsin, key, md_sin, serv_name, pathname, retries) 446 struct sockaddr_in *bpsin; 447 char *key; 448 struct sockaddr_in *md_sin; 449 char *serv_name; 450 char *pathname; 451 int retries; 452 { 453 struct mbuf *m; 454 struct sockaddr_in *sin; 455 struct in_addr inaddr; 456 int error, sn_len, path_len; 457 458 /* 459 * Build request message. 460 */ 461 462 /* client name (hostname) */ 463 m = xdr_string_encode(hostname, hostnamelen); 464 if (m == NULL) 465 return (ENOMEM); 466 467 /* key name (root or swap) */ 468 m->m_next = xdr_string_encode(key, strlen(key)); 469 if (m->m_next == NULL) 470 return (ENOMEM); 471 472 /* RPC: bootparam/getfile */ 473 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS, 474 BOOTPARAM_GETFILE, &m, NULL, retries); 475 if (error) 476 return error; 477 478 /* 479 * Parse result message. 480 */ 481 482 /* server name */ 483 sn_len = MNAMELEN-1; 484 m = xdr_string_decode(m, serv_name, &sn_len); 485 if (m == NULL) 486 goto bad; 487 488 /* server IP address (mountd/NFS) */ 489 m = xdr_inaddr_decode(m, &inaddr); 490 if (m == NULL) 491 goto bad; 492 493 /* server pathname */ 494 path_len = MAXPATHLEN-1; 495 m = xdr_string_decode(m, pathname, &path_len); 496 if (m == NULL) 497 goto bad; 498 499 /* setup server socket address */ 500 sin = md_sin; 501 bzero((caddr_t)sin, sizeof(*sin)); 502 sin->sin_len = sizeof(*sin); 503 sin->sin_family = AF_INET; 504 sin->sin_addr = inaddr; 505 506 /* success */ 507 goto out; 508 509 bad: 510 printf("nfs_boot: bootparam_getfile: bad reply\n"); 511 error = EBADRPC; 512 513 out: 514 m_freem(m); 515 return(0); 516 } 517 518 519 /* 520 * RPC: mountd/mount 521 * Given a server pathname, get an NFS file handle. 522 * Also, sets sin->sin_port to the NFS service port. 523 */ 524 static int 525 md_mount(mdsin, path, fhp) 526 struct sockaddr_in *mdsin; /* mountd server address */ 527 char *path; 528 u_char *fhp; 529 { 530 /* The RPC structures */ 531 struct rdata { 532 u_int32_t errno; 533 u_int8_t fh[NFSX_V2FH]; 534 } *rdata; 535 struct mbuf *m; 536 int error; 537 538 /* Get port number for MOUNTD. */ 539 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1, 540 &mdsin->sin_port); 541 if (error) return error; 542 543 m = xdr_string_encode(path, strlen(path)); 544 if (m == NULL) 545 return ENOMEM; 546 547 /* Do RPC to mountd. */ 548 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1, 549 RPCMNT_MOUNT, &m, NULL, -1); 550 if (error) 551 return error; /* message already freed */ 552 553 /* The reply might have only the errno. */ 554 if (m->m_len < 4) 555 goto bad; 556 /* Have at least errno, so check that. */ 557 rdata = mtod(m, struct rdata *); 558 error = fxdr_unsigned(u_int32_t, rdata->errno); 559 if (error) 560 goto out; 561 562 /* Have errno==0, so the fh must be there. */ 563 if (m->m_len < sizeof(*rdata)) { 564 m = m_pullup(m, sizeof(*rdata)); 565 if (m == NULL) 566 goto bad; 567 rdata = mtod(m, struct rdata *); 568 } 569 bcopy(rdata->fh, fhp, NFSX_V2FH); 570 goto out; 571 572 bad: 573 error = EBADRPC; 574 575 out: 576 m_freem(m); 577 return error; 578 } 579 580 #endif /* ifdef NFSCLIENT */ 581