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