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