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