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