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