1 /* $NetBSD: nfs_boot.c,v 1.16 1995/04/24 21:55:08 gwr Exp $ */ 2 3 /* 4 * Copyright (c) 1995 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/reboot.h> 39 #include <sys/socket.h> 40 #include <sys/socketvar.h> 41 42 #include <net/if.h> 43 #include <net/route.h> 44 45 #include <netinet/in.h> 46 #include <netinet/if_ether.h> 47 48 #include <nfs/rpcv2.h> 49 #include <nfs/nfsv2.h> 50 #include <nfs/nfs.h> 51 #include <nfs/nfsdiskless.h> 52 #include <nfs/krpc.h> 53 54 #include "ether.h" 55 #if NETHER == 0 56 57 int nfs_boot_init(nd, procp) 58 struct nfs_diskless *nd; 59 struct proc *procp; 60 { 61 panic("nfs_boot_init: no ether"); 62 } 63 64 #else /* NETHER */ 65 66 /* 67 * Support for NFS diskless booting, specifically getting information 68 * about where to boot from, what pathnames, etc. 69 * 70 * This implememtation uses RARP and the bootparam RPC. 71 * We are forced to implement RPC anyway (to get file handles) 72 * so we might as well take advantage of it for bootparam too. 73 * 74 * The diskless boot sequence goes as follows: 75 * (1) Use RARP to get our interface address 76 * (2) Use RPC/bootparam/whoami to get our hostname, 77 * our IP address, and the server's IP address. 78 * (3) Use RPC/bootparam/getfile to get the root path 79 * (4) Use RPC/mountd to get the root file handle 80 * (5) Use RPC/bootparam/getfile to get the swap path 81 * (6) Use RPC/mountd to get the swap file handle 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 char *nfsbootdevname; 101 102 /* 103 * Called with an empty nfs_diskless struct to be filled in. 104 */ 105 int 106 nfs_boot_init(nd, procp) 107 struct nfs_diskless *nd; 108 struct proc *procp; 109 { 110 struct ifreq ireq; 111 struct in_addr my_ip, gw_ip; 112 struct sockaddr_in bp_sin; 113 struct sockaddr_in *sin; 114 struct ifnet *ifp; 115 struct socket *so; 116 int error; 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 * RPC: bootparam/whoami 282 * Given client IP address, get: 283 * client name (hostname) 284 * domain name (domainname) 285 * gateway address 286 * 287 * The hostname and domainname are set here for convenience. 288 * 289 * Note - bpsin is initialized to the broadcast address, 290 * and will be replaced with the bootparam server address 291 * after this call is complete. Have to use PMAP_PROC_CALL 292 * to make sure we get responses only from a servers that 293 * know about us (don't want to broadcast a getport call). 294 */ 295 static int 296 bp_whoami(bpsin, my_ip, gw_ip) 297 struct sockaddr_in *bpsin; 298 struct in_addr *my_ip; 299 struct in_addr *gw_ip; 300 { 301 /* RPC structures for PMAPPROC_CALLIT */ 302 struct whoami_call { 303 u_int32_t call_prog; 304 u_int32_t call_vers; 305 u_int32_t call_proc; 306 u_int32_t call_arglen; 307 } *call; 308 struct callit_reply { 309 u_int32_t port; 310 u_int32_t encap_len; 311 /* encapsulated data here */ 312 } *reply; 313 314 struct mbuf *m, *from; 315 struct sockaddr_in *sin; 316 int error, msg_len; 317 int16_t port; 318 319 /* 320 * Build request message for PMAPPROC_CALLIT. 321 */ 322 m = m_get(M_WAIT, MT_DATA); 323 call = mtod(m, struct whoami_call *); 324 m->m_len = sizeof(*call); 325 call->call_prog = htonl(BOOTPARAM_PROG); 326 call->call_vers = htonl(BOOTPARAM_VERS); 327 call->call_proc = htonl(BOOTPARAM_WHOAMI); 328 329 /* 330 * append encapsulated data (client IP address) 331 */ 332 m->m_next = xdr_inaddr_encode(my_ip); 333 call->call_arglen = m->m_next->m_len; 334 335 /* RPC: portmap/callit */ 336 bpsin->sin_port = htons(PMAPPORT); 337 from = NULL; 338 error = krpc_call(bpsin, PMAPPROG, PMAPVERS, 339 PMAPPROC_CALLIT, &m, &from); 340 if (error) 341 return error; 342 343 /* 344 * Parse result message. 345 */ 346 if (m->m_len < sizeof(*reply)) { 347 m = m_pullup(m, sizeof(*reply)); 348 if (m == NULL) 349 goto bad; 350 } 351 reply = mtod(m, struct callit_reply *); 352 port = ntohl(reply->port); 353 msg_len = ntohl(reply->encap_len); 354 m_adj(m, sizeof(*reply)); 355 356 /* 357 * Save bootparam server address 358 */ 359 sin = mtod(from, struct sockaddr_in *); 360 bpsin->sin_port = htons(port); 361 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr; 362 363 /* client name */ 364 hostnamelen = MAXHOSTNAMELEN-1; 365 m = xdr_string_decode(m, hostname, &hostnamelen); 366 if (m == NULL) 367 goto bad; 368 369 /* domain name */ 370 domainnamelen = MAXHOSTNAMELEN-1; 371 m = xdr_string_decode(m, domainname, &domainnamelen); 372 if (m == NULL) 373 goto bad; 374 375 /* gateway address */ 376 m = xdr_inaddr_decode(m, gw_ip); 377 if (m == NULL) 378 goto bad; 379 380 /* success */ 381 goto out; 382 383 bad: 384 printf("nfs_boot: bootparam_whoami: bad reply\n"); 385 error = EBADRPC; 386 387 out: 388 if (from) 389 m_freem(from); 390 if (m) 391 m_freem(m); 392 return(error); 393 } 394 395 396 /* 397 * RPC: bootparam/getfile 398 * Given client name and file "key", get: 399 * server name 400 * server IP address 401 * server pathname 402 */ 403 static int 404 bp_getfile(bpsin, key, md_sin, serv_name, pathname) 405 struct sockaddr_in *bpsin; 406 char *key; 407 struct sockaddr_in *md_sin; 408 char *serv_name; 409 char *pathname; 410 { 411 struct mbuf *m; 412 struct sockaddr_in *sin; 413 struct in_addr inaddr; 414 int error, sn_len, path_len; 415 416 /* 417 * Build request message. 418 */ 419 420 /* client name (hostname) */ 421 m = xdr_string_encode(hostname, hostnamelen); 422 423 /* key name (root or swap) */ 424 m->m_next = xdr_string_encode(key, strlen(key)); 425 426 /* RPC: bootparam/getfile */ 427 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS, 428 BOOTPARAM_GETFILE, &m, NULL); 429 if (error) 430 return error; 431 432 /* 433 * Parse result message. 434 */ 435 436 /* server name */ 437 sn_len = MNAMELEN-1; 438 m = xdr_string_decode(m, serv_name, &sn_len); 439 if (m == NULL) 440 goto bad; 441 442 /* server IP address (mountd/NFS) */ 443 m = xdr_inaddr_decode(m, &inaddr); 444 if (m == NULL) 445 goto bad; 446 447 /* server pathname */ 448 path_len = MAXPATHLEN-1; 449 m = xdr_string_decode(m, pathname, &path_len); 450 if (m == NULL) 451 goto bad; 452 453 /* setup server socket address */ 454 sin = md_sin; 455 bzero((caddr_t)sin, sizeof(*sin)); 456 sin->sin_len = sizeof(*sin); 457 sin->sin_family = AF_INET; 458 sin->sin_addr = inaddr; 459 460 /* success */ 461 goto out; 462 463 bad: 464 printf("nfs_boot: bootparam_getfile: bad reply\n"); 465 error = EBADRPC; 466 467 out: 468 m_freem(m); 469 return(0); 470 } 471 472 473 /* 474 * RPC: mountd/mount 475 * Given a server pathname, get an NFS file handle. 476 * Also, sets sin->sin_port to the NFS service port. 477 */ 478 static int 479 md_mount(mdsin, path, fhp) 480 struct sockaddr_in *mdsin; /* mountd server address */ 481 char *path; 482 u_char *fhp; 483 { 484 /* The RPC structures */ 485 struct rdata { 486 u_int32_t errno; 487 u_char fh[NFS_FHSIZE]; 488 } *rdata; 489 struct mbuf *m; 490 int error; 491 492 /* Get port number for MOUNTD. */ 493 error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1, 494 &mdsin->sin_port); 495 if (error) return error; 496 497 m = xdr_string_encode(path, strlen(path)); 498 499 /* Do RPC to mountd. */ 500 error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1, 501 RPCMNT_MOUNT, &m, NULL); 502 if (error) 503 return error; /* message already freed */ 504 505 if (m->m_len < sizeof(*rdata)) { 506 m = m_pullup(m, sizeof(*rdata)); 507 if (m == NULL) 508 goto bad; 509 } 510 rdata = mtod(m, struct rdata *); 511 error = ntohl(rdata->errno); 512 if (error) 513 goto bad; 514 bcopy(rdata->fh, fhp, NFS_FHSIZE); 515 516 /* Set port number for NFS use. */ 517 error = krpc_portmap(mdsin, NFS_PROG, NFS_VER2, 518 &mdsin->sin_port); 519 goto out; 520 521 bad: 522 error = EBADRPC; 523 524 out: 525 m_freem(m); 526 return error; 527 } 528 529 #endif /* NETHER */ 530