1 /* $OpenBSD: nfs_boot.c,v 1.26 2010/04/03 20:03:38 krw Exp $ */ 2 /* $NetBSD: nfs_boot.c,v 1.26 1996/05/07 02:51:25 thorpej Exp $ */ 3 4 /* 5 * Copyright (c) 1995 Adam Glass, Gordon Ross 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the authors may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/kernel.h> 34 #include <sys/conf.h> 35 #include <sys/ioctl.h> 36 #include <sys/proc.h> 37 #include <sys/mount.h> 38 #include <sys/mbuf.h> 39 #include <sys/reboot.h> 40 #include <sys/socket.h> 41 #include <sys/socketvar.h> 42 #include <sys/queue.h> 43 44 #include <net/if.h> 45 #include <net/route.h> 46 47 #include <netinet/in.h> 48 #include <netinet/if_ether.h> 49 50 #include <nfs/rpcv2.h> 51 #include <nfs/nfsproto.h> 52 #include <nfs/nfs.h> 53 #include <nfs/nfsdiskless.h> 54 #include <nfs/krpc.h> 55 #include <nfs/xdr_subs.h> 56 #include <nfs/nfs_var.h> 57 58 #include "ether.h" 59 60 #if !defined(NFSCLIENT) || (NETHER == 0 && NFDDI == 0) 61 62 int 63 nfs_boot_init(struct nfs_diskless *nd, struct proc *procp) 64 { 65 panic("nfs_boot_init: NFSCLIENT not enabled in kernel"); 66 } 67 68 int 69 nfs_boot_getfh(struct sockaddr_in *bpsin, char *key, 70 struct nfs_dlmount *ndmntp, int retries) 71 { 72 /* can not get here */ 73 return (EOPNOTSUPP); 74 } 75 76 #else 77 78 /* 79 * Support for NFS diskless booting, specifically getting information 80 * about where to boot from, what pathnames, etc. 81 * 82 * This implementation uses RARP and the bootparam RPC. 83 * We are forced to implement RPC anyway (to get file handles) 84 * so we might as well take advantage of it for bootparam too. 85 * 86 * The diskless boot sequence goes as follows: 87 * (1) Use RARP to get our interface address 88 * (2) Use RPC/bootparam/whoami to get our hostname, 89 * our IP address, and the server's IP address. 90 * (3) Use RPC/bootparam/getfile to get the root path 91 * (4) Use RPC/mountd to get the root file handle 92 * (5) Use RPC/bootparam/getfile to get the swap path 93 * (6) Use RPC/mountd to get the swap file handle 94 * 95 * (This happens to be the way Sun does it too.) 96 */ 97 98 /* bootparam RPC */ 99 static int bp_whoami(struct sockaddr_in *bpsin, 100 struct in_addr *my_ip, struct in_addr *gw_ip); 101 static int bp_getfile(struct sockaddr_in *bpsin, char *key, 102 struct sockaddr_in *mdsin, char *servname, char *path, int retries); 103 104 /* mountd RPC */ 105 static int md_mount(struct sockaddr_in *mdsin, char *path, 106 u_char *fh); 107 108 char *nfsbootdevname; 109 110 /* 111 * Called with an empty nfs_diskless struct to be filled in. 112 */ 113 int 114 nfs_boot_init(struct nfs_diskless *nd, struct proc *procp) 115 { 116 struct ifreq ireq; 117 struct in_addr my_ip, gw_ip; 118 struct sockaddr_in bp_sin; 119 struct sockaddr_in *sin; 120 struct ifnet *ifp; 121 struct socket *so; 122 int error; 123 124 /* 125 * Find an interface, rarp for its ip address, stuff it, the 126 * implied broadcast addr, and netmask into a nfs_diskless struct. 127 * 128 * This was moved here from nfs_vfsops.c because this procedure 129 * would be quite different if someone decides to write (i.e.) a 130 * BOOTP version of this file (might not use RARP, etc.) 131 */ 132 133 /* 134 * Find a network interface. 135 */ 136 if (nfsbootdevname) 137 ifp = ifunit(nfsbootdevname); 138 else { 139 TAILQ_FOREACH(ifp, &ifnet, if_list) { 140 if ((ifp->if_flags & 141 (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0) 142 break; 143 } 144 } 145 if (ifp == NULL) 146 panic("nfs_boot: no suitable interface"); 147 bcopy(ifp->if_xname, ireq.ifr_name, IFNAMSIZ); 148 printf("nfs_boot: using interface %s, with revarp & bootparams\n", 149 ireq.ifr_name); 150 151 /* 152 * Bring up the interface. 153 * 154 * Get the old interface flags and or IFF_UP into them; if 155 * IFF_UP set blindly, interface selection can be clobbered. 156 */ 157 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0) 158 panic("nfs_boot: socreate, error=%d", error); 159 error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)&ireq, procp); 160 if (error) 161 panic("nfs_boot: GIFFLAGS, error=%d", error); 162 ireq.ifr_flags |= IFF_UP; 163 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp); 164 if (error) 165 panic("nfs_boot: SIFFLAGS, error=%d", error); 166 167 /* 168 * Do RARP for the interface address. 169 */ 170 if ((error = revarpwhoami(&my_ip, ifp)) != 0) 171 panic("reverse arp not answered by rarpd(8) or dhcpd(8)"); 172 printf("nfs_boot: client_addr=%s\n", inet_ntoa(my_ip)); 173 174 /* 175 * Do enough of ifconfig(8) so that the chosen interface 176 * can talk to the servers. (just set the address) 177 */ 178 sin = (struct sockaddr_in *)&ireq.ifr_addr; 179 bzero((caddr_t)sin, sizeof(*sin)); 180 sin->sin_len = sizeof(*sin); 181 sin->sin_family = AF_INET; 182 sin->sin_addr.s_addr = my_ip.s_addr; 183 error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp); 184 if (error) 185 panic("nfs_boot: set if addr, error=%d", error); 186 187 soclose(so); 188 189 /* 190 * Get client name and gateway address. 191 * RPC: bootparam/whoami 192 * Use the old broadcast address for the WHOAMI 193 * call because we do not yet know our netmask. 194 * The server address returned by the WHOAMI call 195 * is used for all subsequent booptaram RPCs. 196 */ 197 bzero((caddr_t)&bp_sin, sizeof(bp_sin)); 198 bp_sin.sin_len = sizeof(bp_sin); 199 bp_sin.sin_family = AF_INET; 200 bp_sin.sin_addr.s_addr = INADDR_BROADCAST; 201 hostnamelen = MAXHOSTNAMELEN; 202 203 /* this returns gateway IP address */ 204 error = bp_whoami(&bp_sin, &my_ip, &gw_ip); 205 if (error) 206 panic("nfs_boot: bootparam whoami, error=%d", error); 207 printf("nfs_boot: server_addr=%s hostname=%s\n", 208 inet_ntoa(bp_sin.sin_addr), hostname); 209 210 #ifdef NFS_BOOT_GATEWAY 211 /* 212 * XXX - This code is conditionally compiled only because 213 * many bootparam servers (in particular, SunOS 4.1.3) 214 * always set the gateway address to their own address. 215 * The bootparam server is not necessarily the gateway. 216 * We could just believe the server, and at worst you would 217 * need to delete the incorrect default route before adding 218 * the correct one, but for simplicity, ignore the gateway. 219 * If your server is OK, you can turn on this option. 220 * 221 * If the gateway address is set, add a default route. 222 * (The mountd RPCs may go across a gateway.) 223 */ 224 if (gw_ip.s_addr) { 225 struct sockaddr dst, gw, mask; 226 struct rt_addrinfo info; 227 /* Destination: (default) */ 228 bzero((caddr_t)&dst, sizeof(dst)); 229 dst.sa_len = sizeof(dst); 230 dst.sa_family = AF_INET; 231 /* Gateway: */ 232 bzero((caddr_t)&gw, sizeof(gw)); 233 sin = (struct sockaddr_in *)&gw; 234 sin->sin_len = sizeof(gw); 235 sin->sin_family = AF_INET; 236 sin->sin_addr.s_addr = gw_ip.s_addr; 237 /* Mask: (zero length) */ 238 bzero(&mask, sizeof(mask)); 239 bzero(&info, sizeof(info)); 240 info.rti_info[RTAX_DST] = &dst; 241 info.rti_info[RTAX_GATEWAY] = &gw; 242 info.rti_info[RTAX_NETMASK] = &mask; 243 info.rti_flags = (RTF_UP | RTF_GATEWAY | RTF_STATIC); 244 245 printf("nfs_boot: gateway=%s\n", inet_ntoa(gw_ip)); 246 /* add, dest, gw, mask, flags, 0 */ 247 error = rtrequest1(RTM_ADD, &info, RTP_STATIC, NULL, 0); 248 if (error) 249 printf("nfs_boot: add route, error=%d\n", error); 250 } 251 #endif 252 253 bcopy(&bp_sin, &nd->nd_boot, sizeof(bp_sin)); 254 255 return (0); 256 } 257 258 /* 259 * bpsin: bootparam server 260 * key: root or swap 261 * ndmntp: output 262 */ 263 int 264 nfs_boot_getfh(struct sockaddr_in *bpsin, char *key, 265 struct nfs_dlmount *ndmntp, int retries) 266 { 267 char pathname[MAXPATHLEN]; 268 char *sp, *dp, *endp; 269 struct sockaddr_in *sin; 270 int error; 271 272 sin = &ndmntp->ndm_saddr; 273 274 /* 275 * Get server:pathname for "key" (root or swap) 276 * using RPC to bootparam/getfile 277 */ 278 error = bp_getfile(bpsin, key, sin, ndmntp->ndm_host, pathname, 279 retries); 280 if (error) { 281 printf("nfs_boot: bootparam get %s: %d\n", key, error); 282 return (error); 283 } 284 285 /* 286 * Get file handle for "key" (root or swap) 287 * using RPC to mountd/mount 288 */ 289 error = md_mount(sin, pathname, ndmntp->ndm_fh); 290 if (error) { 291 printf("nfs_boot: mountd %s, error=%d\n", key, error); 292 return (error); 293 } 294 295 /* Set port number for NFS use. */ 296 /* XXX: NFS port is always 2049, right? */ 297 error = krpc_portmap(sin, NFS_PROG, NFS_VER2, &sin->sin_port); 298 if (error) { 299 printf("nfs_boot: portmap NFS/v2, error=%d\n", error); 300 return (error); 301 } 302 303 /* Construct remote path (for getmntinfo(3)) */ 304 dp = ndmntp->ndm_host; 305 endp = dp + MNAMELEN - 1; 306 dp += strlen(dp); 307 *dp++ = ':'; 308 for (sp = pathname; *sp && dp < endp;) 309 *dp++ = *sp++; 310 *dp = '\0'; 311 312 return (0); 313 } 314 315 316 /* 317 * RPC: bootparam/whoami 318 * Given client IP address, get: 319 * client name (hostname) 320 * domain name (domainname) 321 * gateway address 322 * 323 * The hostname and domainname are set here for convenience. 324 * 325 * Note - bpsin is initialized to the broadcast address, 326 * and will be replaced with the bootparam server address 327 * after this call is complete. Have to use PMAP_PROC_CALL 328 * to make sure we get responses only from a servers that 329 * know about us (don't want to broadcast a getport call). 330 */ 331 static int 332 bp_whoami(struct sockaddr_in *bpsin, struct in_addr *my_ip, 333 struct in_addr *gw_ip) 334 { 335 /* RPC structures for PMAPPROC_CALLIT */ 336 struct whoami_call { 337 u_int32_t call_prog; 338 u_int32_t call_vers; 339 u_int32_t call_proc; 340 u_int32_t call_arglen; 341 } *call; 342 struct callit_reply { 343 u_int32_t port; 344 u_int32_t encap_len; 345 /* encapsulated data here */ 346 } *reply; 347 348 struct mbuf *m, *from; 349 struct sockaddr_in *sin; 350 int error, msg_len; 351 int16_t port; 352 353 /* 354 * Build request message for PMAPPROC_CALLIT. 355 */ 356 m = m_get(M_WAIT, MT_DATA); 357 call = mtod(m, struct whoami_call *); 358 m->m_len = sizeof(*call); 359 call->call_prog = txdr_unsigned(BOOTPARAM_PROG); 360 call->call_vers = txdr_unsigned(BOOTPARAM_VERS); 361 call->call_proc = txdr_unsigned(BOOTPARAM_WHOAMI); 362 363 /* 364 * append encapsulated data (client IP address) 365 */ 366 m->m_next = xdr_inaddr_encode(my_ip); 367 call->call_arglen = txdr_unsigned(m->m_next->m_len); 368 369 /* RPC: portmap/callit */ 370 bpsin->sin_port = htons(PMAPPORT); 371 from = NULL; 372 error = krpc_call(bpsin, PMAPPROG, PMAPVERS, 373 PMAPPROC_CALLIT, &m, &from, -1); 374 if (error) 375 return error; 376 377 /* 378 * Parse result message. 379 */ 380 if (m->m_len < sizeof(*reply)) { 381 m = m_pullup(m, sizeof(*reply)); 382 if (m == NULL) 383 goto bad; 384 } 385 reply = mtod(m, struct callit_reply *); 386 port = fxdr_unsigned(u_int32_t, reply->port); 387 msg_len = fxdr_unsigned(u_int32_t, reply->encap_len); 388 m_adj(m, sizeof(*reply)); 389 390 /* 391 * Save bootparam server address 392 */ 393 sin = mtod(from, struct sockaddr_in *); 394 bpsin->sin_port = htons(port); 395 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr; 396 397 /* client name */ 398 hostnamelen = MAXHOSTNAMELEN-1; 399 m = xdr_string_decode(m, hostname, &hostnamelen); 400 if (m == NULL) 401 goto bad; 402 403 /* domain name */ 404 domainnamelen = MAXHOSTNAMELEN-1; 405 m = xdr_string_decode(m, domainname, &domainnamelen); 406 if (m == NULL) 407 goto bad; 408 409 /* gateway address */ 410 m = xdr_inaddr_decode(m, gw_ip); 411 if (m == NULL) 412 goto bad; 413 414 /* success */ 415 goto out; 416 417 bad: 418 printf("nfs_boot: bootparam_whoami: bad reply\n"); 419 error = EBADRPC; 420 421 out: 422 if (from) 423 m_freem(from); 424 if (m) 425 m_freem(m); 426 return(error); 427 } 428 429 430 /* 431 * RPC: bootparam/getfile 432 * Given client name and file "key", get: 433 * server name 434 * server IP address 435 * server pathname 436 */ 437 static int 438 bp_getfile(struct sockaddr_in *bpsin, char *key, struct sockaddr_in *md_sin, 439 char *serv_name, char *pathname, int retries) 440 { 441 struct mbuf *m; 442 struct sockaddr_in *sin; 443 struct in_addr inaddr; 444 int error, sn_len, path_len; 445 446 /* 447 * Build request message. 448 */ 449 450 /* client name (hostname) */ 451 m = xdr_string_encode(hostname, hostnamelen); 452 if (m == NULL) 453 return (ENOMEM); 454 455 /* key name (root or swap) */ 456 m->m_next = xdr_string_encode(key, strlen(key)); 457 if (m->m_next == NULL) 458 return (ENOMEM); 459 460 /* RPC: bootparam/getfile */ 461 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS, 462 BOOTPARAM_GETFILE, &m, NULL, retries); 463 if (error) 464 return error; 465 466 /* 467 * Parse result message. 468 */ 469 470 /* server name */ 471 sn_len = MNAMELEN-1; 472 m = xdr_string_decode(m, serv_name, &sn_len); 473 if (m == NULL) 474 goto bad; 475 476 /* server IP address (mountd/NFS) */ 477 m = xdr_inaddr_decode(m, &inaddr); 478 if (m == NULL) 479 goto bad; 480 481 /* server pathname */ 482 path_len = MAXPATHLEN-1; 483 m = xdr_string_decode(m, pathname, &path_len); 484 if (m == NULL) 485 goto bad; 486 487 /* setup server socket address */ 488 sin = md_sin; 489 bzero((caddr_t)sin, sizeof(*sin)); 490 sin->sin_len = sizeof(*sin); 491 sin->sin_family = AF_INET; 492 sin->sin_addr = inaddr; 493 494 /* success */ 495 goto out; 496 497 bad: 498 printf("nfs_boot: bootparam_getfile: bad reply\n"); 499 error = EBADRPC; 500 501 out: 502 m_freem(m); 503 return(error); 504 } 505 506 507 /* 508 * RPC: mountd/mount 509 * Given a server pathname, get an NFS file handle. 510 * Also, sets sin->sin_port to the NFS service port. 511 * mdsin: mountd server address 512 */ 513 static int 514 md_mount(struct sockaddr_in *mdsin, char *path, u_char *fhp) 515 { 516 /* The RPC structures */ 517 struct rdata { 518 u_int32_t errno; 519 u_int8_t fh[NFSX_V2FH]; 520 } *rdata; 521 struct mbuf *m; 522 int error; 523 524 /* Get port number for MOUNTD. */ 525 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1, 526 &mdsin->sin_port); 527 if (error) return error; 528 529 m = xdr_string_encode(path, strlen(path)); 530 if (m == NULL) 531 return ENOMEM; 532 533 /* Do RPC to mountd. */ 534 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1, 535 RPCMNT_MOUNT, &m, NULL, -1); 536 if (error) 537 return error; /* message already freed */ 538 539 /* The reply might have only the errno. */ 540 if (m->m_len < 4) 541 goto bad; 542 /* Have at least errno, so check that. */ 543 rdata = mtod(m, struct rdata *); 544 error = fxdr_unsigned(u_int32_t, rdata->errno); 545 if (error) 546 goto out; 547 548 /* Have errno==0, so the fh must be there. */ 549 if (m->m_len < sizeof(*rdata)) { 550 m = m_pullup(m, sizeof(*rdata)); 551 if (m == NULL) 552 goto bad; 553 rdata = mtod(m, struct rdata *); 554 } 555 bcopy(rdata->fh, fhp, NFSX_V2FH); 556 goto out; 557 558 bad: 559 error = EBADRPC; 560 561 out: 562 m_freem(m); 563 return error; 564 } 565 566 #endif /* ifdef NFSCLIENT */ 567