1 /* $NetBSD: nfs_boot.c,v 1.12 1994/10/29 06:38:11 cgd 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 /* 101 * Called with an empty nfs_diskless struct to be filled in. 102 */ 103 int 104 nfs_boot_init(nd, procp) 105 struct nfs_diskless *nd; 106 struct proc *procp; 107 { 108 struct ifreq ireq; 109 struct in_addr my_ip, gw_ip; 110 struct sockaddr_in bp_sin; 111 struct sockaddr_in *sin; 112 struct ifnet *ifp; 113 struct socket *so; 114 int error, len; 115 u_short port; 116 117 #if 0 118 /* 119 * XXX time must be non-zero when we init the interface or else 120 * the arp code will wedge... (Fixed in if_ether.c -gwr) 121 */ 122 if (time.tv_sec == 0) 123 time.tv_sec = 1; 124 #endif 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.) -gwr 133 */ 134 135 /* 136 * Find a network interface. 137 * XXX - This should use the specified boot device. 138 */ 139 for (ifp = ifnet; ifp; ifp = ifp->if_next) 140 if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0) 141 break; 142 if (ifp == NULL) 143 panic("nfs_boot: no suitable interface"); 144 sprintf(ireq.ifr_name, "%s%d", ifp->if_name, ifp->if_unit); 145 printf("nfs_boot: using network interface '%s'\n", 146 ireq.ifr_name); 147 148 /* 149 * Bring up the interface. 150 */ 151 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0) 152 panic("nfs_boot: socreate, error=%d", error); 153 ireq.ifr_flags = IFF_UP; 154 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp); 155 if (error) 156 panic("nfs_boot: SIFFLAGS, error=%d", error); 157 158 /* 159 * Do RARP for the interface address. Also 160 * save the server address for bootparam RPC. 161 */ 162 if ((error = revarpwhoami(&my_ip, ifp)) != 0) 163 panic("revarp failed, error=%d", error); 164 printf("nfs_boot: client_addr=0x%x\n", ntohl(my_ip.s_addr)); 165 166 /* 167 * Do enough of ifconfig(8) so that the chosen interface can 168 * talk to the server(s). (also get brcast addr and netmask) 169 */ 170 /* Set interface address. */ 171 sin = (struct sockaddr_in *)&ireq.ifr_addr; 172 bzero((caddr_t)sin, sizeof(*sin)); 173 sin->sin_len = sizeof(*sin); 174 sin->sin_family = AF_INET; 175 sin->sin_addr.s_addr = my_ip.s_addr; 176 error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp); 177 if (error) 178 panic("nfs_boot: set if addr, error=%d", error); 179 180 soclose(so); 181 182 /* 183 * Get client name and gateway address. 184 * RPC: bootparam/whoami 185 * XXX - Using old broadcast addr. for WHOAMI, 186 * then it is replaced with the BP server addr. 187 */ 188 bzero((caddr_t)&bp_sin, sizeof(bp_sin)); 189 bp_sin.sin_len = sizeof(bp_sin); 190 bp_sin.sin_family = AF_INET; 191 bp_sin.sin_addr.s_addr = ~0; /* XXX */ 192 hostnamelen = MAXHOSTNAMELEN; 193 194 /* this returns gateway IP address */ 195 error = bp_whoami(&bp_sin, &my_ip, &gw_ip); 196 if (error) 197 panic("nfs_boot: bootparam whoami, error=%d", error); 198 printf("nfs_boot: server_addr=0x%x\n", 199 ntohl(bp_sin.sin_addr.s_addr)); 200 printf("nfs_boot: hostname=%s\n", hostname); 201 202 #ifdef NFS_BOOT_GATEWAY 203 /* 204 * XXX - This code is conditionally compiled only because 205 * many bootparam servers (in particular, SunOS 4.1.3) 206 * always set the gateway address to their own address. 207 * The bootparam server is not necessarily the gateway. 208 * We could just believe the server, and at worst you would 209 * need to delete the incorrect default route before adding 210 * the correct one, but for simplicity, ignore the gateway. 211 * If your server is OK, you can turn on this option. 212 * 213 * If the gateway address is set, add a default route. 214 * (The mountd RPCs may go across a gateway.) 215 */ 216 if (gw_ip.s_addr) { 217 struct sockaddr dst, gw, mask; 218 /* Destination: (default) */ 219 bzero((caddr_t)&dst, sizeof(dst)); 220 dst.sa_len = sizeof(dst); 221 dst.sa_family = AF_INET; 222 /* Gateway: */ 223 bzero((caddr_t)&gw, sizeof(gw)); 224 sin = (struct sockaddr_in *)&gw; 225 sin->sin_len = sizeof(gw); 226 sin->sin_family = AF_INET; 227 sin->sin_addr.s_addr = gw_ip.s_addr; 228 /* Mask: (zero length) */ 229 bzero(&mask, sizeof(mask)); 230 231 printf("nfs_boot: gateway=0x%x\n", ntohl(gw_ip.s_addr)); 232 /* add, dest, gw, mask, flags, 0 */ 233 error = rtrequest(RTM_ADD, &dst, (struct sockaddr *)&gw, 234 &mask, (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL); 235 if (error) 236 printf("nfs_boot: add route, error=%d\n", error); 237 } 238 #endif 239 240 get_path_and_handle(&bp_sin, "root", &nd->nd_root); 241 get_path_and_handle(&bp_sin, "swap", &nd->nd_swap); 242 243 return (0); 244 } 245 246 static void 247 get_path_and_handle(bpsin, key, ndmntp) 248 struct sockaddr_in *bpsin; /* bootparam server */ 249 char *key; /* root or swap */ 250 struct nfs_dlmount *ndmntp; /* output */ 251 { 252 char pathname[MAXPATHLEN]; 253 char *sp, *dp, *endp; 254 int error; 255 256 /* 257 * Get server:pathname for "key" (root or swap) 258 * using RPC to bootparam/getfile 259 */ 260 error = bp_getfile(bpsin, key, &ndmntp->ndm_saddr, 261 ndmntp->ndm_host, pathname); 262 if (error) 263 panic("nfs_boot: bootparam get %s: %d", key, error); 264 printf("%s on %s:%s\n", key, ndmntp->ndm_host, pathname); 265 266 /* 267 * Get file handle for "key" (root or swap) 268 * using RPC to mountd/mount 269 */ 270 error = md_mount(&ndmntp->ndm_saddr, pathname, ndmntp->ndm_fh); 271 if (error) 272 panic("nfs_boot: mountd %s, error=%d", key, error); 273 274 /* Construct remote path (for getmntinfo(3)) */ 275 dp = ndmntp->ndm_host; 276 endp = dp + MNAMELEN - 1; 277 dp += strlen(dp); 278 *dp++ = ':'; 279 for (sp = pathname; *sp && dp < endp;) 280 *dp++ = *sp++; 281 *dp = '\0'; 282 283 } 284 285 286 /* 287 * Get an mbuf with the given length, and 288 * initialize the pkthdr length field. 289 */ 290 static struct mbuf * 291 m_get_len(int msg_len) 292 { 293 struct mbuf *m; 294 m = m_gethdr(M_WAIT, MT_DATA); 295 if (m == NULL) 296 return NULL; 297 if (msg_len > MHLEN) { 298 if (msg_len > MCLBYTES) 299 panic("nfs_boot: msg_len > MCLBYTES"); 300 MCLGET(m, M_WAIT); 301 if (m == NULL) 302 return NULL; 303 } 304 m->m_len = msg_len; 305 m->m_pkthdr.len = m->m_len; 306 return (m); 307 } 308 309 310 /* 311 * String representation for RPC. 312 */ 313 struct rpc_string { 314 u_long len; /* length without null or padding */ 315 u_char data[4]; /* data (longer, of course) */ 316 /* data is padded to a long-word boundary */ 317 }; 318 /* Compute space used given string length. */ 319 #define RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3)) 320 321 /* 322 * Inet address in RPC messages 323 * (Note, really four longs, NOT chars. Blech.) 324 */ 325 struct bp_inaddr { 326 u_long atype; 327 long addr[4]; 328 }; 329 330 331 /* 332 * RPC: bootparam/whoami 333 * Given client IP address, get: 334 * client name (hostname) 335 * domain name (domainname) 336 * gateway address 337 * 338 * Setting the hostname and domainname here may be somewhat 339 * controvercial, but it is so easy to do it here. -gwr 340 * 341 * Note - bpsin is initialized to the broadcast address, 342 * and will be replaced with the bootparam server address 343 * after this call is complete. Have to use PMAP_PROC_CALL 344 * to make sure we get responses only from a servers that 345 * know about us (don't want to broadcast a getport call). 346 */ 347 static int 348 bp_whoami(bpsin, my_ip, gw_ip) 349 struct sockaddr_in *bpsin; 350 struct in_addr *my_ip; 351 struct in_addr *gw_ip; 352 { 353 /* RPC structures for PMAPPROC_CALLIT */ 354 struct whoami_call { 355 u_long call_prog; 356 u_long call_vers; 357 u_long call_proc; 358 u_long call_arglen; 359 struct bp_inaddr call_ia; 360 } *call; 361 362 struct rpc_string *str; 363 struct bp_inaddr *bia; 364 struct mbuf *m, *from; 365 struct sockaddr_in *sin; 366 int error, msg_len; 367 int cn_len, dn_len; 368 u_char *p; 369 long *lp; 370 371 /* 372 * Get message buffer of sufficient size. 373 */ 374 msg_len = sizeof(*call); 375 m = m_get_len(msg_len); 376 if (m == NULL) 377 return ENOBUFS; 378 379 /* 380 * Build request message for PMAPPROC_CALLIT. 381 */ 382 call = mtod(m, struct whoami_call *); 383 call->call_prog = htonl(BOOTPARAM_PROG); 384 call->call_vers = htonl(BOOTPARAM_VERS); 385 call->call_proc = htonl(BOOTPARAM_WHOAMI); 386 call->call_arglen = htonl(sizeof(struct bp_inaddr)); 387 388 /* client IP address */ 389 call->call_ia.atype = htonl(1); 390 p = (u_char*)my_ip; 391 lp = call->call_ia.addr; 392 *lp++ = htonl(*p); p++; 393 *lp++ = htonl(*p); p++; 394 *lp++ = htonl(*p); p++; 395 *lp++ = htonl(*p); p++; 396 397 /* RPC: portmap/callit */ 398 bpsin->sin_port = htons(PMAPPORT); 399 from = NULL; 400 error = krpc_call(bpsin, PMAPPROG, PMAPVERS, 401 PMAPPROC_CALLIT, &m, &from); 402 if (error) 403 return error; 404 405 /* 406 * Parse result message. 407 */ 408 msg_len = m->m_len; 409 lp = mtod(m, long *); 410 411 /* bootparam server port (also grab from address). */ 412 if (msg_len < sizeof(*lp)) 413 goto bad; 414 msg_len -= sizeof(*lp); 415 bpsin->sin_port = htons((short)ntohl(*lp++)); 416 sin = mtod(from, struct sockaddr_in *); 417 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr; 418 419 /* length of encapsulated results */ 420 if (msg_len < (ntohl(*lp) + sizeof(*lp))) 421 goto bad; 422 msg_len = ntohl(*lp++); 423 p = (char*)lp; 424 425 /* client name */ 426 if (msg_len < sizeof(*str)) 427 goto bad; 428 str = (struct rpc_string *)p; 429 cn_len = ntohl(str->len); 430 if (msg_len < cn_len) 431 goto bad; 432 if (cn_len >= MAXHOSTNAMELEN) 433 goto bad; 434 bcopy(str->data, hostname, cn_len); 435 hostname[cn_len] = '\0'; 436 hostnamelen = cn_len; 437 p += RPC_STR_SIZE(cn_len); 438 msg_len -= RPC_STR_SIZE(cn_len); 439 440 /* domain name */ 441 if (msg_len < sizeof(*str)) 442 goto bad; 443 str = (struct rpc_string *)p; 444 dn_len = ntohl(str->len); 445 if (msg_len < dn_len) 446 goto bad; 447 if (dn_len >= MAXHOSTNAMELEN) 448 goto bad; 449 bcopy(str->data, domainname, dn_len); 450 domainname[dn_len] = '\0'; 451 domainnamelen = dn_len; 452 p += RPC_STR_SIZE(dn_len); 453 msg_len -= RPC_STR_SIZE(dn_len); 454 455 /* gateway address */ 456 if (msg_len < sizeof(*bia)) 457 goto bad; 458 bia = (struct bp_inaddr *)p; 459 if (bia->atype != htonl(1)) 460 goto bad; 461 p = (u_char*)gw_ip; 462 *p++ = ntohl(bia->addr[0]); 463 *p++ = ntohl(bia->addr[1]); 464 *p++ = ntohl(bia->addr[2]); 465 *p++ = ntohl(bia->addr[3]); 466 goto out; 467 468 bad: 469 printf("nfs_boot: bootparam_whoami: bad reply\n"); 470 error = EBADRPC; 471 472 out: 473 if (from) 474 m_freem(from); 475 m_freem(m); 476 return(error); 477 } 478 479 480 /* 481 * RPC: bootparam/getfile 482 * Given client name and file "key", get: 483 * server name 484 * server IP address 485 * server pathname 486 */ 487 static int 488 bp_getfile(bpsin, key, md_sin, serv_name, pathname) 489 struct sockaddr_in *bpsin; 490 char *key; 491 struct sockaddr_in *md_sin; 492 char *serv_name; 493 char *pathname; 494 { 495 struct rpc_string *str; 496 struct mbuf *m; 497 struct bp_inaddr *bia; 498 struct sockaddr_in *sin; 499 u_char *p, *q; 500 int error, msg_len; 501 int cn_len, key_len, sn_len, path_len; 502 503 /* 504 * Get message buffer of sufficient size. 505 */ 506 cn_len = hostnamelen; 507 key_len = strlen(key); 508 msg_len = 0; 509 msg_len += RPC_STR_SIZE(cn_len); 510 msg_len += RPC_STR_SIZE(key_len); 511 m = m_get_len(msg_len); 512 if (m == NULL) 513 return ENOBUFS; 514 515 /* 516 * Build request message. 517 */ 518 p = mtod(m, u_char *); 519 bzero(p, msg_len); 520 /* client name (hostname) */ 521 str = (struct rpc_string *)p; 522 str->len = htonl(cn_len); 523 bcopy(hostname, str->data, cn_len); 524 p += RPC_STR_SIZE(cn_len); 525 /* key name (root or swap) */ 526 str = (struct rpc_string *)p; 527 str->len = htonl(key_len); 528 bcopy(key, str->data, key_len); 529 530 /* RPC: bootparam/getfile */ 531 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS, 532 BOOTPARAM_GETFILE, &m, NULL); 533 if (error) 534 return error; 535 536 /* 537 * Parse result message. 538 */ 539 p = mtod(m, u_char *); 540 msg_len = m->m_len; 541 542 /* server name */ 543 if (msg_len < sizeof(*str)) 544 goto bad; 545 str = (struct rpc_string *)p; 546 sn_len = ntohl(str->len); 547 if (msg_len < sn_len) 548 goto bad; 549 if (sn_len >= MNAMELEN) 550 goto bad; 551 bcopy(str->data, serv_name, sn_len); 552 serv_name[sn_len] = '\0'; 553 p += RPC_STR_SIZE(sn_len); 554 msg_len -= RPC_STR_SIZE(sn_len); 555 556 /* server IP address (mountd) */ 557 if (msg_len < sizeof(*bia)) 558 goto bad; 559 bia = (struct bp_inaddr *)p; 560 if (bia->atype != htonl(1)) 561 goto bad; 562 sin = md_sin; 563 bzero((caddr_t)sin, sizeof(*sin)); 564 sin->sin_len = sizeof(*sin); 565 sin->sin_family = AF_INET; 566 q = (u_char*) &sin->sin_addr; 567 *q++ = ntohl(bia->addr[0]); 568 *q++ = ntohl(bia->addr[1]); 569 *q++ = ntohl(bia->addr[2]); 570 *q++ = ntohl(bia->addr[3]); 571 p += sizeof(*bia); 572 msg_len -= sizeof(*bia); 573 574 /* server pathname */ 575 if (msg_len < sizeof(*str)) 576 goto bad; 577 str = (struct rpc_string *)p; 578 path_len = ntohl(str->len); 579 if (msg_len < path_len) 580 goto bad; 581 if (path_len >= MAXPATHLEN) 582 goto bad; 583 bcopy(str->data, pathname, path_len); 584 pathname[path_len] = '\0'; 585 goto out; 586 587 bad: 588 printf("nfs_boot: bootparam_getfile: bad reply\n"); 589 error = EBADRPC; 590 591 out: 592 m_freem(m); 593 return(0); 594 } 595 596 597 /* 598 * RPC: mountd/mount 599 * Given a server pathname, get an NFS file handle. 600 * Also, sets sin->sin_port to the NFS service port. 601 */ 602 static int 603 md_mount(mdsin, path, fhp) 604 struct sockaddr_in *mdsin; /* mountd server address */ 605 char *path; 606 u_char *fhp; 607 { 608 /* The RPC structures */ 609 struct rpc_string *str; 610 struct rdata { 611 u_long errno; 612 u_char fh[NFS_FHSIZE]; 613 } *rdata; 614 struct mbuf *m; 615 int error, mlen, slen; 616 617 /* Get port number for MOUNTD. */ 618 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1, 619 &mdsin->sin_port); 620 if (error) return error; 621 622 slen = strlen(path); 623 mlen = RPC_STR_SIZE(slen); 624 625 m = m_get_len(mlen); 626 if (m == NULL) 627 return ENOBUFS; 628 str = mtod(m, struct rpc_string *); 629 str->len = htonl(slen); 630 bcopy(path, str->data, slen); 631 632 /* Do RPC to mountd. */ 633 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1, 634 RPCMNT_MOUNT, &m, NULL); 635 if (error) 636 return error; /* message already freed */ 637 638 mlen = m->m_len; 639 if (mlen < sizeof(*rdata)) 640 goto bad; 641 rdata = mtod(m, struct rdata *); 642 error = ntohl(rdata->errno); 643 if (error) 644 goto bad; 645 bcopy(rdata->fh, fhp, NFS_FHSIZE); 646 647 /* Set port number for NFS use. */ 648 error = krpc_portmap(mdsin, NFS_PROG, NFS_VER2, 649 &mdsin->sin_port); 650 goto out; 651 652 bad: 653 error = EBADRPC; 654 655 out: 656 m_freem(m); 657 return error; 658 } 659 660 #endif /* NETHER */ 661