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