1 /* $NetBSD: nfs_boot.c,v 1.14 1995/03/18 05:51:22 gwr 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 270 /* 271 * Get file handle for "key" (root or swap) 272 * using RPC to mountd/mount 273 */ 274 error = md_mount(&ndmntp->ndm_saddr, pathname, ndmntp->ndm_fh); 275 if (error) 276 panic("nfs_boot: mountd %s, error=%d", key, error); 277 278 /* Construct remote path (for getmntinfo(3)) */ 279 dp = ndmntp->ndm_host; 280 endp = dp + MNAMELEN - 1; 281 dp += strlen(dp); 282 *dp++ = ':'; 283 for (sp = pathname; *sp && dp < endp;) 284 *dp++ = *sp++; 285 *dp = '\0'; 286 287 } 288 289 290 /* 291 * Get an mbuf with the given length, and 292 * initialize the pkthdr length field. 293 */ 294 static struct mbuf * 295 m_get_len(int msg_len) 296 { 297 struct mbuf *m; 298 m = m_gethdr(M_WAIT, MT_DATA); 299 if (m == NULL) 300 return NULL; 301 if (msg_len > MHLEN) { 302 if (msg_len > MCLBYTES) 303 panic("nfs_boot: msg_len > MCLBYTES"); 304 MCLGET(m, M_WAIT); 305 if (m == NULL) 306 return NULL; 307 } 308 m->m_len = msg_len; 309 m->m_pkthdr.len = m->m_len; 310 return (m); 311 } 312 313 314 /* 315 * String representation for RPC. 316 */ 317 struct rpc_string { 318 u_long len; /* length without null or padding */ 319 u_char data[4]; /* data (longer, of course) */ 320 /* data is padded to a long-word boundary */ 321 }; 322 /* Compute space used given string length. */ 323 #define RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3)) 324 325 /* 326 * Inet address in RPC messages 327 * (Note, really four longs, NOT chars. Blech.) 328 */ 329 struct bp_inaddr { 330 u_long atype; 331 long addr[4]; 332 }; 333 334 335 /* 336 * RPC: bootparam/whoami 337 * Given client IP address, get: 338 * client name (hostname) 339 * domain name (domainname) 340 * gateway address 341 * 342 * Setting the hostname and domainname here may be somewhat 343 * controvercial, but it is so easy to do it here. -gwr 344 * 345 * Note - bpsin is initialized to the broadcast address, 346 * and will be replaced with the bootparam server address 347 * after this call is complete. Have to use PMAP_PROC_CALL 348 * to make sure we get responses only from a servers that 349 * know about us (don't want to broadcast a getport call). 350 */ 351 static int 352 bp_whoami(bpsin, my_ip, gw_ip) 353 struct sockaddr_in *bpsin; 354 struct in_addr *my_ip; 355 struct in_addr *gw_ip; 356 { 357 /* RPC structures for PMAPPROC_CALLIT */ 358 struct whoami_call { 359 u_long call_prog; 360 u_long call_vers; 361 u_long call_proc; 362 u_long call_arglen; 363 struct bp_inaddr call_ia; 364 } *call; 365 366 struct rpc_string *str; 367 struct bp_inaddr *bia; 368 struct mbuf *m, *from; 369 struct sockaddr_in *sin; 370 int error, msg_len; 371 int cn_len, dn_len; 372 u_char *p; 373 long *lp; 374 375 /* 376 * Get message buffer of sufficient size. 377 */ 378 msg_len = sizeof(*call); 379 m = m_get_len(msg_len); 380 if (m == NULL) 381 return ENOBUFS; 382 383 /* 384 * Build request message for PMAPPROC_CALLIT. 385 */ 386 call = mtod(m, struct whoami_call *); 387 call->call_prog = htonl(BOOTPARAM_PROG); 388 call->call_vers = htonl(BOOTPARAM_VERS); 389 call->call_proc = htonl(BOOTPARAM_WHOAMI); 390 call->call_arglen = htonl(sizeof(struct bp_inaddr)); 391 392 /* client IP address */ 393 call->call_ia.atype = htonl(1); 394 p = (u_char*)my_ip; 395 lp = call->call_ia.addr; 396 *lp++ = htonl(*p); p++; 397 *lp++ = htonl(*p); p++; 398 *lp++ = htonl(*p); p++; 399 *lp++ = htonl(*p); p++; 400 401 /* RPC: portmap/callit */ 402 bpsin->sin_port = htons(PMAPPORT); 403 from = NULL; 404 error = krpc_call(bpsin, PMAPPROG, PMAPVERS, 405 PMAPPROC_CALLIT, &m, &from); 406 if (error) 407 return error; 408 409 /* 410 * Parse result message. 411 */ 412 msg_len = m->m_len; 413 lp = mtod(m, long *); 414 415 /* bootparam server port (also grab from address). */ 416 if (msg_len < sizeof(*lp)) 417 goto bad; 418 msg_len -= sizeof(*lp); 419 bpsin->sin_port = htons((short)ntohl(*lp++)); 420 sin = mtod(from, struct sockaddr_in *); 421 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr; 422 423 /* length of encapsulated results */ 424 if (msg_len < (ntohl(*lp) + sizeof(*lp))) 425 goto bad; 426 msg_len = ntohl(*lp++); 427 p = (char*)lp; 428 429 /* client name */ 430 if (msg_len < sizeof(*str)) 431 goto bad; 432 str = (struct rpc_string *)p; 433 cn_len = ntohl(str->len); 434 if (msg_len < cn_len) 435 goto bad; 436 if (cn_len >= MAXHOSTNAMELEN) 437 goto bad; 438 bcopy(str->data, hostname, cn_len); 439 hostname[cn_len] = '\0'; 440 hostnamelen = cn_len; 441 p += RPC_STR_SIZE(cn_len); 442 msg_len -= RPC_STR_SIZE(cn_len); 443 444 /* domain name */ 445 if (msg_len < sizeof(*str)) 446 goto bad; 447 str = (struct rpc_string *)p; 448 dn_len = ntohl(str->len); 449 if (msg_len < dn_len) 450 goto bad; 451 if (dn_len >= MAXHOSTNAMELEN) 452 goto bad; 453 bcopy(str->data, domainname, dn_len); 454 domainname[dn_len] = '\0'; 455 domainnamelen = dn_len; 456 p += RPC_STR_SIZE(dn_len); 457 msg_len -= RPC_STR_SIZE(dn_len); 458 459 /* gateway address */ 460 if (msg_len < sizeof(*bia)) 461 goto bad; 462 bia = (struct bp_inaddr *)p; 463 if (bia->atype != htonl(1)) 464 goto bad; 465 p = (u_char*)gw_ip; 466 *p++ = ntohl(bia->addr[0]); 467 *p++ = ntohl(bia->addr[1]); 468 *p++ = ntohl(bia->addr[2]); 469 *p++ = ntohl(bia->addr[3]); 470 goto out; 471 472 bad: 473 printf("nfs_boot: bootparam_whoami: bad reply\n"); 474 error = EBADRPC; 475 476 out: 477 if (from) 478 m_freem(from); 479 m_freem(m); 480 return(error); 481 } 482 483 484 /* 485 * RPC: bootparam/getfile 486 * Given client name and file "key", get: 487 * server name 488 * server IP address 489 * server pathname 490 */ 491 static int 492 bp_getfile(bpsin, key, md_sin, serv_name, pathname) 493 struct sockaddr_in *bpsin; 494 char *key; 495 struct sockaddr_in *md_sin; 496 char *serv_name; 497 char *pathname; 498 { 499 struct rpc_string *str; 500 struct mbuf *m; 501 struct bp_inaddr *bia; 502 struct sockaddr_in *sin; 503 u_char *p, *q; 504 int error, msg_len; 505 int cn_len, key_len, sn_len, path_len; 506 507 /* 508 * Get message buffer of sufficient size. 509 */ 510 cn_len = hostnamelen; 511 key_len = strlen(key); 512 msg_len = 0; 513 msg_len += RPC_STR_SIZE(cn_len); 514 msg_len += RPC_STR_SIZE(key_len); 515 m = m_get_len(msg_len); 516 if (m == NULL) 517 return ENOBUFS; 518 519 /* 520 * Build request message. 521 */ 522 p = mtod(m, u_char *); 523 bzero(p, msg_len); 524 /* client name (hostname) */ 525 str = (struct rpc_string *)p; 526 str->len = htonl(cn_len); 527 bcopy(hostname, str->data, cn_len); 528 p += RPC_STR_SIZE(cn_len); 529 /* key name (root or swap) */ 530 str = (struct rpc_string *)p; 531 str->len = htonl(key_len); 532 bcopy(key, str->data, key_len); 533 534 /* RPC: bootparam/getfile */ 535 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS, 536 BOOTPARAM_GETFILE, &m, NULL); 537 if (error) 538 return error; 539 540 /* 541 * Parse result message. 542 */ 543 p = mtod(m, u_char *); 544 msg_len = m->m_len; 545 546 /* server name */ 547 if (msg_len < sizeof(*str)) 548 goto bad; 549 str = (struct rpc_string *)p; 550 sn_len = ntohl(str->len); 551 if (msg_len < sn_len) 552 goto bad; 553 if (sn_len >= MNAMELEN) 554 goto bad; 555 bcopy(str->data, serv_name, sn_len); 556 serv_name[sn_len] = '\0'; 557 p += RPC_STR_SIZE(sn_len); 558 msg_len -= RPC_STR_SIZE(sn_len); 559 560 /* server IP address (mountd) */ 561 if (msg_len < sizeof(*bia)) 562 goto bad; 563 bia = (struct bp_inaddr *)p; 564 if (bia->atype != htonl(1)) 565 goto bad; 566 sin = md_sin; 567 bzero((caddr_t)sin, sizeof(*sin)); 568 sin->sin_len = sizeof(*sin); 569 sin->sin_family = AF_INET; 570 q = (u_char*) &sin->sin_addr; 571 *q++ = ntohl(bia->addr[0]); 572 *q++ = ntohl(bia->addr[1]); 573 *q++ = ntohl(bia->addr[2]); 574 *q++ = ntohl(bia->addr[3]); 575 p += sizeof(*bia); 576 msg_len -= sizeof(*bia); 577 578 /* server pathname */ 579 if (msg_len < sizeof(*str)) 580 goto bad; 581 str = (struct rpc_string *)p; 582 path_len = ntohl(str->len); 583 if (msg_len < path_len) 584 goto bad; 585 if (path_len >= MAXPATHLEN) 586 goto bad; 587 bcopy(str->data, pathname, path_len); 588 pathname[path_len] = '\0'; 589 goto out; 590 591 bad: 592 printf("nfs_boot: bootparam_getfile: bad reply\n"); 593 error = EBADRPC; 594 595 out: 596 m_freem(m); 597 return(0); 598 } 599 600 601 /* 602 * RPC: mountd/mount 603 * Given a server pathname, get an NFS file handle. 604 * Also, sets sin->sin_port to the NFS service port. 605 */ 606 static int 607 md_mount(mdsin, path, fhp) 608 struct sockaddr_in *mdsin; /* mountd server address */ 609 char *path; 610 u_char *fhp; 611 { 612 /* The RPC structures */ 613 struct rpc_string *str; 614 struct rdata { 615 u_long errno; 616 u_char fh[NFS_FHSIZE]; 617 } *rdata; 618 struct mbuf *m; 619 int error, mlen, slen; 620 621 /* Get port number for MOUNTD. */ 622 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1, 623 &mdsin->sin_port); 624 if (error) return error; 625 626 slen = strlen(path); 627 mlen = RPC_STR_SIZE(slen); 628 629 m = m_get_len(mlen); 630 if (m == NULL) 631 return ENOBUFS; 632 str = mtod(m, struct rpc_string *); 633 str->len = htonl(slen); 634 bcopy(path, str->data, slen); 635 636 /* Do RPC to mountd. */ 637 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1, 638 RPCMNT_MOUNT, &m, NULL); 639 if (error) 640 return error; /* message already freed */ 641 642 mlen = m->m_len; 643 if (mlen < sizeof(*rdata)) 644 goto bad; 645 rdata = mtod(m, struct rdata *); 646 error = ntohl(rdata->errno); 647 if (error) 648 goto bad; 649 bcopy(rdata->fh, fhp, NFS_FHSIZE); 650 651 /* Set port number for NFS use. */ 652 error = krpc_portmap(mdsin, NFS_PROG, NFS_VER2, 653 &mdsin->sin_port); 654 goto out; 655 656 bad: 657 error = EBADRPC; 658 659 out: 660 m_freem(m); 661 return error; 662 } 663 664 #endif /* NETHER */ 665