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