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