1 /* $NetBSD: nfs_boot.c,v 1.13 1995/02/16 21:43:15 pk Exp $ */ 2 3 /* 4 * Copyright (c) 1994 Adam Glass, Gordon Ross 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the authors may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/kernel.h> 33 #include <sys/conf.h> 34 #include <sys/ioctl.h> 35 #include <sys/proc.h> 36 #include <sys/mount.h> 37 #include <sys/mbuf.h> 38 #include <sys/socket.h> 39 #include <sys/reboot.h> 40 41 #include <net/if.h> 42 #include <net/route.h> 43 44 #include <netinet/in.h> 45 #include <netinet/if_ether.h> 46 47 #include <nfs/rpcv2.h> 48 #include <nfs/nfsv2.h> 49 #include <nfs/nfs.h> 50 #include <nfs/nfsdiskless.h> 51 #include <nfs/krpc.h> 52 53 #include "ether.h" 54 #if NETHER == 0 55 56 int nfs_boot_init(nd, procp) 57 struct nfs_diskless *nd; 58 struct proc *procp; 59 { 60 panic("nfs_boot_init: no ether"); 61 } 62 63 #else /* NETHER */ 64 65 /* 66 * Support for NFS diskless booting, specifically getting information 67 * about where to boot from, what pathnames, etc. 68 * 69 * This implememtation uses RARP and the bootparam RPC. 70 * We are forced to implement RPC anyway (to get file handles) 71 * so we might as well take advantage of it for bootparam too. 72 * 73 * The diskless boot sequence goes as follows: 74 * (1) Get our interface address using RARP 75 * (also save the address of the RARP server) 76 * (2) Get our hostname using RPC/bootparam/whoami 77 * (all boopararms RPCs to the RARP server) 78 * (3) Get the root path using RPC/bootparam/getfile 79 * (4) Get the root file handle using RPC/mountd 80 * (5) Get the swap path using RPC/bootparam/getfile 81 * (6) Get the swap file handle using RPC/mountd 82 * 83 * (This happens to be the way Sun does it too.) 84 */ 85 86 /* bootparam RPC */ 87 static int bp_whoami __P((struct sockaddr_in *bpsin, 88 struct in_addr *my_ip, struct in_addr *gw_ip)); 89 static int bp_getfile __P((struct sockaddr_in *bpsin, char *key, 90 struct sockaddr_in *mdsin, char *servname, char *path)); 91 92 /* mountd RPC */ 93 static int md_mount __P((struct sockaddr_in *mdsin, char *path, 94 u_char *fh)); 95 96 /* other helpers */ 97 static void get_path_and_handle __P((struct sockaddr_in *bpsin, 98 char *key, struct nfs_dlmount *ndmntp)); 99 100 char *nfsbootdevname; 101 102 /* 103 * Called with an empty nfs_diskless struct to be filled in. 104 */ 105 int 106 nfs_boot_init(nd, procp) 107 struct nfs_diskless *nd; 108 struct proc *procp; 109 { 110 struct ifreq ireq; 111 struct in_addr my_ip, gw_ip; 112 struct sockaddr_in bp_sin; 113 struct sockaddr_in *sin; 114 struct ifnet *ifp; 115 struct socket *so; 116 int error, len; 117 u_short port; 118 119 #if 0 120 /* 121 * XXX time must be non-zero when we init the interface or else 122 * the arp code will wedge... (Fixed in if_ether.c -gwr) 123 */ 124 if (time.tv_sec == 0) 125 time.tv_sec = 1; 126 #endif 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.) -gwr 135 */ 136 137 /* 138 * Find a network interface. 139 */ 140 if (nfsbootdevname) 141 ifp = ifunit(nfsbootdevname); 142 else 143 for (ifp = ifnet; ifp; ifp = ifp->if_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 sprintf(ireq.ifr_name, "%s%d", ifp->if_name, ifp->if_unit); 150 printf("nfs_boot: using network interface '%s'\n", 151 ireq.ifr_name); 152 153 /* 154 * Bring up the interface. 155 */ 156 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0) 157 panic("nfs_boot: socreate, error=%d", error); 158 ireq.ifr_flags = IFF_UP; 159 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp); 160 if (error) 161 panic("nfs_boot: SIFFLAGS, error=%d", error); 162 163 /* 164 * Do RARP for the interface address. Also 165 * save the server address for bootparam RPC. 166 */ 167 if ((error = revarpwhoami(&my_ip, ifp)) != 0) 168 panic("revarp failed, error=%d", error); 169 printf("nfs_boot: client_addr=0x%x\n", ntohl(my_ip.s_addr)); 170 171 /* 172 * Do enough of ifconfig(8) so that the chosen interface can 173 * talk to the server(s). (also get brcast addr and netmask) 174 */ 175 /* Set interface address. */ 176 sin = (struct sockaddr_in *)&ireq.ifr_addr; 177 bzero((caddr_t)sin, sizeof(*sin)); 178 sin->sin_len = sizeof(*sin); 179 sin->sin_family = AF_INET; 180 sin->sin_addr.s_addr = my_ip.s_addr; 181 error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp); 182 if (error) 183 panic("nfs_boot: set if addr, error=%d", error); 184 185 soclose(so); 186 187 /* 188 * Get client name and gateway address. 189 * RPC: bootparam/whoami 190 * XXX - Using old broadcast addr. for WHOAMI, 191 * then it is replaced with the BP server addr. 192 */ 193 bzero((caddr_t)&bp_sin, sizeof(bp_sin)); 194 bp_sin.sin_len = sizeof(bp_sin); 195 bp_sin.sin_family = AF_INET; 196 bp_sin.sin_addr.s_addr = ~0; /* XXX */ 197 hostnamelen = MAXHOSTNAMELEN; 198 199 /* this returns gateway IP address */ 200 error = bp_whoami(&bp_sin, &my_ip, &gw_ip); 201 if (error) 202 panic("nfs_boot: bootparam whoami, error=%d", error); 203 printf("nfs_boot: server_addr=0x%x\n", 204 ntohl(bp_sin.sin_addr.s_addr)); 205 printf("nfs_boot: hostname=%s\n", hostname); 206 207 #ifdef NFS_BOOT_GATEWAY 208 /* 209 * XXX - This code is conditionally compiled only because 210 * many bootparam servers (in particular, SunOS 4.1.3) 211 * always set the gateway address to their own address. 212 * The bootparam server is not necessarily the gateway. 213 * We could just believe the server, and at worst you would 214 * need to delete the incorrect default route before adding 215 * the correct one, but for simplicity, ignore the gateway. 216 * If your server is OK, you can turn on this option. 217 * 218 * If the gateway address is set, add a default route. 219 * (The mountd RPCs may go across a gateway.) 220 */ 221 if (gw_ip.s_addr) { 222 struct sockaddr dst, gw, mask; 223 /* Destination: (default) */ 224 bzero((caddr_t)&dst, sizeof(dst)); 225 dst.sa_len = sizeof(dst); 226 dst.sa_family = AF_INET; 227 /* Gateway: */ 228 bzero((caddr_t)&gw, sizeof(gw)); 229 sin = (struct sockaddr_in *)&gw; 230 sin->sin_len = sizeof(gw); 231 sin->sin_family = AF_INET; 232 sin->sin_addr.s_addr = gw_ip.s_addr; 233 /* Mask: (zero length) */ 234 bzero(&mask, sizeof(mask)); 235 236 printf("nfs_boot: gateway=0x%x\n", ntohl(gw_ip.s_addr)); 237 /* add, dest, gw, mask, flags, 0 */ 238 error = rtrequest(RTM_ADD, &dst, (struct sockaddr *)&gw, 239 &mask, (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL); 240 if (error) 241 printf("nfs_boot: add route, error=%d\n", error); 242 } 243 #endif 244 245 get_path_and_handle(&bp_sin, "root", &nd->nd_root); 246 get_path_and_handle(&bp_sin, "swap", &nd->nd_swap); 247 248 return (0); 249 } 250 251 static void 252 get_path_and_handle(bpsin, key, ndmntp) 253 struct sockaddr_in *bpsin; /* bootparam server */ 254 char *key; /* root or swap */ 255 struct nfs_dlmount *ndmntp; /* output */ 256 { 257 char pathname[MAXPATHLEN]; 258 char *sp, *dp, *endp; 259 int error; 260 261 /* 262 * Get server:pathname for "key" (root or swap) 263 * using RPC to bootparam/getfile 264 */ 265 error = bp_getfile(bpsin, key, &ndmntp->ndm_saddr, 266 ndmntp->ndm_host, pathname); 267 if (error) 268 panic("nfs_boot: bootparam get %s: %d", key, error); 269 printf("%s on %s:%s\n", key, ndmntp->ndm_host, pathname); 270 271 /* 272 * Get file handle for "key" (root or swap) 273 * using RPC to mountd/mount 274 */ 275 error = md_mount(&ndmntp->ndm_saddr, pathname, ndmntp->ndm_fh); 276 if (error) 277 panic("nfs_boot: mountd %s, error=%d", key, error); 278 279 /* Construct remote path (for getmntinfo(3)) */ 280 dp = ndmntp->ndm_host; 281 endp = dp + MNAMELEN - 1; 282 dp += strlen(dp); 283 *dp++ = ':'; 284 for (sp = pathname; *sp && dp < endp;) 285 *dp++ = *sp++; 286 *dp = '\0'; 287 288 } 289 290 291 /* 292 * Get an mbuf with the given length, and 293 * initialize the pkthdr length field. 294 */ 295 static struct mbuf * 296 m_get_len(int msg_len) 297 { 298 struct mbuf *m; 299 m = m_gethdr(M_WAIT, MT_DATA); 300 if (m == NULL) 301 return NULL; 302 if (msg_len > MHLEN) { 303 if (msg_len > MCLBYTES) 304 panic("nfs_boot: msg_len > MCLBYTES"); 305 MCLGET(m, M_WAIT); 306 if (m == NULL) 307 return NULL; 308 } 309 m->m_len = msg_len; 310 m->m_pkthdr.len = m->m_len; 311 return (m); 312 } 313 314 315 /* 316 * String representation for RPC. 317 */ 318 struct rpc_string { 319 u_long len; /* length without null or padding */ 320 u_char data[4]; /* data (longer, of course) */ 321 /* data is padded to a long-word boundary */ 322 }; 323 /* Compute space used given string length. */ 324 #define RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3)) 325 326 /* 327 * Inet address in RPC messages 328 * (Note, really four longs, NOT chars. Blech.) 329 */ 330 struct bp_inaddr { 331 u_long atype; 332 long addr[4]; 333 }; 334 335 336 /* 337 * RPC: bootparam/whoami 338 * Given client IP address, get: 339 * client name (hostname) 340 * domain name (domainname) 341 * gateway address 342 * 343 * Setting the hostname and domainname here may be somewhat 344 * controvercial, but it is so easy to do it here. -gwr 345 * 346 * Note - bpsin is initialized to the broadcast address, 347 * and will be replaced with the bootparam server address 348 * after this call is complete. Have to use PMAP_PROC_CALL 349 * to make sure we get responses only from a servers that 350 * know about us (don't want to broadcast a getport call). 351 */ 352 static int 353 bp_whoami(bpsin, my_ip, gw_ip) 354 struct sockaddr_in *bpsin; 355 struct in_addr *my_ip; 356 struct in_addr *gw_ip; 357 { 358 /* RPC structures for PMAPPROC_CALLIT */ 359 struct whoami_call { 360 u_long call_prog; 361 u_long call_vers; 362 u_long call_proc; 363 u_long call_arglen; 364 struct bp_inaddr call_ia; 365 } *call; 366 367 struct rpc_string *str; 368 struct bp_inaddr *bia; 369 struct mbuf *m, *from; 370 struct sockaddr_in *sin; 371 int error, msg_len; 372 int cn_len, dn_len; 373 u_char *p; 374 long *lp; 375 376 /* 377 * Get message buffer of sufficient size. 378 */ 379 msg_len = sizeof(*call); 380 m = m_get_len(msg_len); 381 if (m == NULL) 382 return ENOBUFS; 383 384 /* 385 * Build request message for PMAPPROC_CALLIT. 386 */ 387 call = mtod(m, struct whoami_call *); 388 call->call_prog = htonl(BOOTPARAM_PROG); 389 call->call_vers = htonl(BOOTPARAM_VERS); 390 call->call_proc = htonl(BOOTPARAM_WHOAMI); 391 call->call_arglen = htonl(sizeof(struct bp_inaddr)); 392 393 /* client IP address */ 394 call->call_ia.atype = htonl(1); 395 p = (u_char*)my_ip; 396 lp = call->call_ia.addr; 397 *lp++ = htonl(*p); p++; 398 *lp++ = htonl(*p); p++; 399 *lp++ = htonl(*p); p++; 400 *lp++ = htonl(*p); p++; 401 402 /* RPC: portmap/callit */ 403 bpsin->sin_port = htons(PMAPPORT); 404 from = NULL; 405 error = krpc_call(bpsin, PMAPPROG, PMAPVERS, 406 PMAPPROC_CALLIT, &m, &from); 407 if (error) 408 return error; 409 410 /* 411 * Parse result message. 412 */ 413 msg_len = m->m_len; 414 lp = mtod(m, long *); 415 416 /* bootparam server port (also grab from address). */ 417 if (msg_len < sizeof(*lp)) 418 goto bad; 419 msg_len -= sizeof(*lp); 420 bpsin->sin_port = htons((short)ntohl(*lp++)); 421 sin = mtod(from, struct sockaddr_in *); 422 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr; 423 424 /* length of encapsulated results */ 425 if (msg_len < (ntohl(*lp) + sizeof(*lp))) 426 goto bad; 427 msg_len = ntohl(*lp++); 428 p = (char*)lp; 429 430 /* client name */ 431 if (msg_len < sizeof(*str)) 432 goto bad; 433 str = (struct rpc_string *)p; 434 cn_len = ntohl(str->len); 435 if (msg_len < cn_len) 436 goto bad; 437 if (cn_len >= MAXHOSTNAMELEN) 438 goto bad; 439 bcopy(str->data, hostname, cn_len); 440 hostname[cn_len] = '\0'; 441 hostnamelen = cn_len; 442 p += RPC_STR_SIZE(cn_len); 443 msg_len -= RPC_STR_SIZE(cn_len); 444 445 /* domain name */ 446 if (msg_len < sizeof(*str)) 447 goto bad; 448 str = (struct rpc_string *)p; 449 dn_len = ntohl(str->len); 450 if (msg_len < dn_len) 451 goto bad; 452 if (dn_len >= MAXHOSTNAMELEN) 453 goto bad; 454 bcopy(str->data, domainname, dn_len); 455 domainname[dn_len] = '\0'; 456 domainnamelen = dn_len; 457 p += RPC_STR_SIZE(dn_len); 458 msg_len -= RPC_STR_SIZE(dn_len); 459 460 /* gateway address */ 461 if (msg_len < sizeof(*bia)) 462 goto bad; 463 bia = (struct bp_inaddr *)p; 464 if (bia->atype != htonl(1)) 465 goto bad; 466 p = (u_char*)gw_ip; 467 *p++ = ntohl(bia->addr[0]); 468 *p++ = ntohl(bia->addr[1]); 469 *p++ = ntohl(bia->addr[2]); 470 *p++ = ntohl(bia->addr[3]); 471 goto out; 472 473 bad: 474 printf("nfs_boot: bootparam_whoami: bad reply\n"); 475 error = EBADRPC; 476 477 out: 478 if (from) 479 m_freem(from); 480 m_freem(m); 481 return(error); 482 } 483 484 485 /* 486 * RPC: bootparam/getfile 487 * Given client name and file "key", get: 488 * server name 489 * server IP address 490 * server pathname 491 */ 492 static int 493 bp_getfile(bpsin, key, md_sin, serv_name, pathname) 494 struct sockaddr_in *bpsin; 495 char *key; 496 struct sockaddr_in *md_sin; 497 char *serv_name; 498 char *pathname; 499 { 500 struct rpc_string *str; 501 struct mbuf *m; 502 struct bp_inaddr *bia; 503 struct sockaddr_in *sin; 504 u_char *p, *q; 505 int error, msg_len; 506 int cn_len, key_len, sn_len, path_len; 507 508 /* 509 * Get message buffer of sufficient size. 510 */ 511 cn_len = hostnamelen; 512 key_len = strlen(key); 513 msg_len = 0; 514 msg_len += RPC_STR_SIZE(cn_len); 515 msg_len += RPC_STR_SIZE(key_len); 516 m = m_get_len(msg_len); 517 if (m == NULL) 518 return ENOBUFS; 519 520 /* 521 * Build request message. 522 */ 523 p = mtod(m, u_char *); 524 bzero(p, msg_len); 525 /* client name (hostname) */ 526 str = (struct rpc_string *)p; 527 str->len = htonl(cn_len); 528 bcopy(hostname, str->data, cn_len); 529 p += RPC_STR_SIZE(cn_len); 530 /* key name (root or swap) */ 531 str = (struct rpc_string *)p; 532 str->len = htonl(key_len); 533 bcopy(key, str->data, key_len); 534 535 /* RPC: bootparam/getfile */ 536 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS, 537 BOOTPARAM_GETFILE, &m, NULL); 538 if (error) 539 return error; 540 541 /* 542 * Parse result message. 543 */ 544 p = mtod(m, u_char *); 545 msg_len = m->m_len; 546 547 /* server name */ 548 if (msg_len < sizeof(*str)) 549 goto bad; 550 str = (struct rpc_string *)p; 551 sn_len = ntohl(str->len); 552 if (msg_len < sn_len) 553 goto bad; 554 if (sn_len >= MNAMELEN) 555 goto bad; 556 bcopy(str->data, serv_name, sn_len); 557 serv_name[sn_len] = '\0'; 558 p += RPC_STR_SIZE(sn_len); 559 msg_len -= RPC_STR_SIZE(sn_len); 560 561 /* server IP address (mountd) */ 562 if (msg_len < sizeof(*bia)) 563 goto bad; 564 bia = (struct bp_inaddr *)p; 565 if (bia->atype != htonl(1)) 566 goto bad; 567 sin = md_sin; 568 bzero((caddr_t)sin, sizeof(*sin)); 569 sin->sin_len = sizeof(*sin); 570 sin->sin_family = AF_INET; 571 q = (u_char*) &sin->sin_addr; 572 *q++ = ntohl(bia->addr[0]); 573 *q++ = ntohl(bia->addr[1]); 574 *q++ = ntohl(bia->addr[2]); 575 *q++ = ntohl(bia->addr[3]); 576 p += sizeof(*bia); 577 msg_len -= sizeof(*bia); 578 579 /* server pathname */ 580 if (msg_len < sizeof(*str)) 581 goto bad; 582 str = (struct rpc_string *)p; 583 path_len = ntohl(str->len); 584 if (msg_len < path_len) 585 goto bad; 586 if (path_len >= MAXPATHLEN) 587 goto bad; 588 bcopy(str->data, pathname, path_len); 589 pathname[path_len] = '\0'; 590 goto out; 591 592 bad: 593 printf("nfs_boot: bootparam_getfile: bad reply\n"); 594 error = EBADRPC; 595 596 out: 597 m_freem(m); 598 return(0); 599 } 600 601 602 /* 603 * RPC: mountd/mount 604 * Given a server pathname, get an NFS file handle. 605 * Also, sets sin->sin_port to the NFS service port. 606 */ 607 static int 608 md_mount(mdsin, path, fhp) 609 struct sockaddr_in *mdsin; /* mountd server address */ 610 char *path; 611 u_char *fhp; 612 { 613 /* The RPC structures */ 614 struct rpc_string *str; 615 struct rdata { 616 u_long errno; 617 u_char fh[NFS_FHSIZE]; 618 } *rdata; 619 struct mbuf *m; 620 int error, mlen, slen; 621 622 /* Get port number for MOUNTD. */ 623 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1, 624 &mdsin->sin_port); 625 if (error) return error; 626 627 slen = strlen(path); 628 mlen = RPC_STR_SIZE(slen); 629 630 m = m_get_len(mlen); 631 if (m == NULL) 632 return ENOBUFS; 633 str = mtod(m, struct rpc_string *); 634 str->len = htonl(slen); 635 bcopy(path, str->data, slen); 636 637 /* Do RPC to mountd. */ 638 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1, 639 RPCMNT_MOUNT, &m, NULL); 640 if (error) 641 return error; /* message already freed */ 642 643 mlen = m->m_len; 644 if (mlen < sizeof(*rdata)) 645 goto bad; 646 rdata = mtod(m, struct rdata *); 647 error = ntohl(rdata->errno); 648 if (error) 649 goto bad; 650 bcopy(rdata->fh, fhp, NFS_FHSIZE); 651 652 /* Set port number for NFS use. */ 653 error = krpc_portmap(mdsin, NFS_PROG, NFS_VER2, 654 &mdsin->sin_port); 655 goto out; 656 657 bad: 658 error = EBADRPC; 659 660 out: 661 m_freem(m); 662 return error; 663 } 664 665 #endif /* NETHER */ 666