1 /* $NetBSD: nfs_bootparam.c,v 1.10 1998/09/13 13:49:29 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1995, 1997 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Adam Glass and Gordon W. Ross. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Support for NFS diskless booting, Sun-style (RPC/bootparams) 41 */ 42 43 #include "opt_nfs_boot.h" 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/kernel.h> 48 #include <sys/conf.h> 49 #include <sys/device.h> 50 #include <sys/ioctl.h> 51 #include <sys/proc.h> 52 #include <sys/mount.h> 53 #include <sys/mbuf.h> 54 #include <sys/reboot.h> 55 #include <sys/socket.h> 56 #include <sys/socketvar.h> 57 58 #include <net/if.h> 59 #include <net/if_types.h> 60 #include <net/route.h> 61 #include <net/if_ether.h> 62 63 #include <netinet/in.h> 64 #include <netinet/if_inarp.h> 65 66 #include <nfs/rpcv2.h> 67 #include <nfs/krpc.h> 68 #include <nfs/xdr_subs.h> 69 70 #include <nfs/nfsproto.h> 71 #include <nfs/nfs.h> 72 #include <nfs/nfsmount.h> 73 #include <nfs/nfsdiskless.h> 74 75 #include "arp.h" 76 77 /* 78 * There are two implementations of NFS diskless boot. 79 * This implementation uses Sun RPC/bootparams, and the 80 * the other uses BOOTP (RFC951 - see nfs_bootdhcp.c). 81 * 82 * The Sun-style boot sequence goes as follows: 83 * (1) Use RARP to get our interface address 84 * (2) Use RPC/bootparam/whoami to get our hostname, 85 * our IP address, and the server's IP address. 86 * (3) Use RPC/bootparam/getfile to get the root path 87 * (4) Use RPC/mountd to get the root file handle 88 * (5) Use RPC/bootparam/getfile to get the swap path 89 * (6) Use RPC/mountd to get the swap file handle 90 */ 91 92 /* bootparam RPC */ 93 static int bp_whoami __P((struct sockaddr_in *bpsin, 94 struct in_addr *my_ip, struct in_addr *gw_ip)); 95 static int bp_getfile __P((struct sockaddr_in *bpsin, char *key, 96 struct nfs_dlmount *ndm)); 97 98 99 /* 100 * Get client name, gateway address, then 101 * get root and swap server:pathname info. 102 * RPCs: bootparam/whoami, bootparam/getfile 103 * 104 * Use the old broadcast address for the WHOAMI 105 * call because we do not yet know our netmask. 106 * The server address returned by the WHOAMI call 107 * is used for all subsequent booptaram RPCs. 108 */ 109 int 110 nfs_bootparam(ifp, nd, procp) 111 struct ifnet *ifp; 112 struct nfs_diskless *nd; 113 struct proc *procp; 114 { 115 struct ifreq ireq; 116 struct in_addr my_ip, gw_ip; 117 struct sockaddr_in bp_sin; 118 struct sockaddr_in *sin; 119 struct socket *so; 120 struct nfs_dlmount *gw_ndm; 121 #if 0 /* XXX - not yet */ 122 char *p; 123 u_int32_t mask; 124 #endif /* XXX */ 125 int error; 126 127 gw_ndm = 0; 128 memset(&ireq, 0, sizeof(ireq)); 129 memcpy(ireq.ifr_name, ifp->if_xname, IFNAMSIZ); 130 131 /* 132 * Get a socket to use for various things in here. 133 * After this, use "goto out" to cleanup and return. 134 */ 135 error = socreate(AF_INET, &so, SOCK_DGRAM, 0); 136 if (error) { 137 printf("nfs_boot: socreate, error=%d\n", error); 138 return (error); 139 } 140 141 /* 142 * Bring up the interface. (just set the "up" flag) 143 * Get the old interface flags and or IFF_UP into them so 144 * things like media selection flags are not clobbered. 145 */ 146 error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)&ireq, procp); 147 if (error) { 148 printf("nfs_boot: GIFFLAGS, error=%d\n", error); 149 goto out; 150 } 151 ireq.ifr_flags |= IFF_UP; 152 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp); 153 if (error) { 154 printf("nfs_boot: SIFFLAGS, error=%d\n", error); 155 goto out; 156 } 157 158 error = EADDRNOTAVAIL; /* ??? */ 159 #if NARP > 0 160 if (ifp->if_type == IFT_ETHER || ifp->if_type == IFT_FDDI) { 161 /* 162 * Do RARP for the interface address. 163 */ 164 error = revarpwhoami(&my_ip, ifp); 165 if (error) { 166 printf("revarp failed, error=%d\n", error); 167 goto out; 168 } 169 } 170 #endif 171 172 nd->nd_myip.s_addr = my_ip.s_addr; 173 printf("nfs_boot: client_addr=0x%x\n", 174 (u_int32_t)ntohl(my_ip.s_addr)); 175 176 /* 177 * Do enough of ifconfig(8) so that the chosen interface 178 * can talk to the servers. (just set the address) 179 */ 180 sin = (struct sockaddr_in *)&ireq.ifr_addr; 181 sin->sin_len = sizeof(*sin); 182 sin->sin_family = AF_INET; 183 sin->sin_addr = my_ip; 184 error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp); 185 if (error) { 186 printf("nfs_boot: set ifaddr, error=%d\n", error); 187 goto out; 188 } 189 190 /* 191 * Get client name and gateway address. 192 * RPC: bootparam/whoami 193 * Use the old broadcast address for the WHOAMI 194 * call because we do not yet know our netmask. 195 * The server address returned by the WHOAMI call 196 * is used for all subsequent booptaram RPCs. 197 */ 198 sin = &bp_sin; 199 memset((caddr_t)sin, 0, sizeof(*sin)); 200 sin->sin_len = sizeof(*sin); 201 sin->sin_family = AF_INET; 202 sin->sin_addr.s_addr = INADDR_BROADCAST; 203 204 /* Do the RPC/bootparam/whoami. */ 205 error = bp_whoami(sin, &my_ip, &gw_ip); 206 if (error) { 207 printf("nfs_boot: bootparam whoami, error=%d\n", error); 208 goto out; 209 } 210 printf("nfs_boot: server_addr=0x%x\n", 211 (u_int32_t)ntohl(sin->sin_addr.s_addr)); 212 printf("nfs_boot: hostname=%s\n", hostname); 213 214 /* 215 * Now fetch the server:pathname strings and server IP 216 * for root and swap. Missing swap is not fatal. 217 */ 218 error = bp_getfile(sin, "root", &nd->nd_root); 219 if (error) { 220 printf("nfs_boot: bootparam get root: %d\n", error); 221 goto out; 222 } 223 #if 0 224 error = bp_getfile(sin, "swap", &nd->nd_swap); 225 if (error) { 226 printf("nfs_boot: bootparam get swap: %d\n", error); 227 error = 0; 228 } 229 #endif 230 231 #ifdef NFS_BOOT_GATEWAY 232 /* 233 * Note: we normally ignore the gateway address returned 234 * by the "bootparam/whoami" RPC above, because many old 235 * bootparam servers supply a bogus gateway value. 236 * 237 * These deficiencies in the bootparam RPC interface are 238 * circumvented by using the bootparam/getfile RPC. The 239 * parameter "gateway" is requested, and if its returned, 240 * we use the "server" part of the reply as the gateway, 241 * and use the "pathname" part of the reply as the mask. 242 * (The mask comes to us as a string.) 243 */ 244 if (gw_ip.s_addr) { 245 /* Our caller will add the route. */ 246 nd->nd_gwip = gw_ip; 247 } 248 #endif 249 #if 0 /* XXX - not yet */ 250 gw_ndm = malloc(sizeof(*gw_ndm), M_NFSMNT, M_WAITOK); 251 memset((caddr_t)gw_ndm, 0, sizeof(*gw_ndm)); 252 error = bp_getfile(sin, "gateway", gw_ndm); 253 if (error) { 254 /* Ignore the error. No gateway supplied. */ 255 error = 0; 256 goto out; 257 } 258 printf("nfs_boot: gateway=%s\n", gw_ndm->ndm_host); 259 sin = (struct sockaddr_in *) &gw_ndm->ndm_saddr; 260 nd->nd_gwip = sin->sin_addr; 261 /* Find the pathname part of the "mounted-on" string. */ 262 p = strchr(gw_ndm->ndm_host, ':'); 263 if (p == 0) 264 goto out; 265 /* have pathname */ 266 p++; /* skip ':' */ 267 mask = inet_addr(p); /* libkern */ 268 if (mask == 0) { 269 printf("nfs_boot: gateway netmask missing\n"); 270 goto out; 271 } 272 273 /* Save our netmask and update the network interface. */ 274 nd->nd_mask.s_addr = mask; 275 sin = (struct sockaddr_in *)&ireq.ifr_addr; 276 sin->sin_len = sizeof(*sin); 277 sin->sin_family = AF_INET; 278 sin->sin_addr = nd->nd_mask; 279 error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)&ireq, procp); 280 if (error) { 281 printf("nfs_boot: set ifmask, error=%d\n", error); 282 error = 0; /* ignore it */ 283 } 284 #endif /* XXX */ 285 286 out: 287 if (gw_ndm) 288 free(gw_ndm, M_NFSMNT); 289 soclose(so); 290 return (error); 291 } 292 293 294 /* 295 * RPC: bootparam/whoami 296 * Given client IP address, get: 297 * client name (hostname) 298 * domain name (domainname) 299 * gateway address 300 * 301 * The hostname and domainname are set here for convenience. 302 * 303 * Note - bpsin is initialized to the broadcast address, 304 * and will be replaced with the bootparam server address 305 * after this call is complete. Have to use PMAP_PROC_CALL 306 * to make sure we get responses only from a servers that 307 * know about us (don't want to broadcast a getport call). 308 */ 309 static int 310 bp_whoami(bpsin, my_ip, gw_ip) 311 struct sockaddr_in *bpsin; 312 struct in_addr *my_ip; 313 struct in_addr *gw_ip; 314 { 315 /* RPC structures for PMAPPROC_CALLIT */ 316 struct whoami_call { 317 u_int32_t call_prog; 318 u_int32_t call_vers; 319 u_int32_t call_proc; 320 u_int32_t call_arglen; 321 } *call; 322 struct callit_reply { 323 u_int32_t port; 324 u_int32_t encap_len; 325 /* encapsulated data here */ 326 } *reply; 327 328 struct mbuf *m, *from; 329 struct sockaddr_in *sin; 330 int error, msg_len; 331 int16_t port; 332 333 /* 334 * Build request message for PMAPPROC_CALLIT. 335 */ 336 m = m_get(M_WAIT, MT_DATA); 337 call = mtod(m, struct whoami_call *); 338 m->m_len = sizeof(*call); 339 call->call_prog = txdr_unsigned(BOOTPARAM_PROG); 340 call->call_vers = txdr_unsigned(BOOTPARAM_VERS); 341 call->call_proc = txdr_unsigned(BOOTPARAM_WHOAMI); 342 343 /* 344 * append encapsulated data (client IP address) 345 */ 346 m->m_next = xdr_inaddr_encode(my_ip); 347 call->call_arglen = txdr_unsigned(m->m_next->m_len); 348 349 /* RPC: portmap/callit */ 350 bpsin->sin_port = htons(PMAPPORT); 351 from = NULL; 352 error = krpc_call(bpsin, PMAPPROG, PMAPVERS, 353 PMAPPROC_CALLIT, &m, &from); 354 if (error) 355 return error; 356 357 /* 358 * Parse result message. 359 */ 360 if (m->m_len < sizeof(*reply)) { 361 m = m_pullup(m, sizeof(*reply)); 362 if (m == NULL) 363 goto bad; 364 } 365 reply = mtod(m, struct callit_reply *); 366 port = fxdr_unsigned(u_int32_t, reply->port); 367 msg_len = fxdr_unsigned(u_int32_t, reply->encap_len); 368 m_adj(m, sizeof(*reply)); 369 370 /* 371 * Save bootparam server address 372 */ 373 sin = mtod(from, struct sockaddr_in *); 374 bpsin->sin_port = htons(port); 375 bpsin->sin_addr.s_addr = sin->sin_addr.s_addr; 376 377 /* client name */ 378 hostnamelen = MAXHOSTNAMELEN-1; 379 m = xdr_string_decode(m, hostname, &hostnamelen); 380 if (m == NULL) 381 goto bad; 382 383 /* domain name */ 384 domainnamelen = MAXHOSTNAMELEN-1; 385 m = xdr_string_decode(m, domainname, &domainnamelen); 386 if (m == NULL) 387 goto bad; 388 389 /* gateway address */ 390 m = xdr_inaddr_decode(m, gw_ip); 391 if (m == NULL) 392 goto bad; 393 394 /* success */ 395 goto out; 396 397 bad: 398 printf("nfs_boot: bootparam_whoami: bad reply\n"); 399 error = EBADRPC; 400 401 out: 402 if (from) 403 m_freem(from); 404 if (m) 405 m_freem(m); 406 return(error); 407 } 408 409 410 /* 411 * RPC: bootparam/getfile 412 * Given client name and file "key", get: 413 * server name 414 * server IP address 415 * server pathname 416 */ 417 static int 418 bp_getfile(bpsin, key, ndm) 419 struct sockaddr_in *bpsin; 420 char *key; 421 struct nfs_dlmount *ndm; 422 { 423 char pathname[MNAMELEN]; 424 struct in_addr inaddr; 425 struct sockaddr_in *sin; 426 struct mbuf *m; 427 char *serv_name; 428 int error, sn_len, path_len; 429 430 /* 431 * Build request message. 432 */ 433 434 /* client name (hostname) */ 435 m = xdr_string_encode(hostname, hostnamelen); 436 if (m == NULL) 437 return (ENOMEM); 438 439 /* key name (root or swap) */ 440 m->m_next = xdr_string_encode(key, strlen(key)); 441 if (m->m_next == NULL) 442 return (ENOMEM); 443 444 /* RPC: bootparam/getfile */ 445 error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS, 446 BOOTPARAM_GETFILE, &m, NULL); 447 if (error) 448 return error; 449 450 /* 451 * Parse result message. 452 */ 453 454 /* server name */ 455 serv_name = &ndm->ndm_host[0]; 456 sn_len = sizeof(ndm->ndm_host) - 1; 457 m = xdr_string_decode(m, serv_name, &sn_len); 458 if (m == NULL) 459 goto bad; 460 461 /* server IP address (mountd/NFS) */ 462 m = xdr_inaddr_decode(m, &inaddr); 463 if (m == NULL) 464 goto bad; 465 466 /* server pathname */ 467 path_len = sizeof(pathname) - 1; 468 m = xdr_string_decode(m, pathname, &path_len); 469 if (m == NULL) 470 goto bad; 471 472 /* 473 * Store the results in the nfs_dlmount. 474 * The strings become "server:pathname" 475 */ 476 sin = (struct sockaddr_in *) &ndm->ndm_saddr; 477 memset((caddr_t)sin, 0, sizeof(*sin)); 478 sin->sin_len = sizeof(*sin); 479 sin->sin_family = AF_INET; 480 sin->sin_addr = inaddr; 481 if ((sn_len + 1 + path_len + 1) > sizeof(ndm->ndm_host)) { 482 printf("nfs_boot: getfile name too long\n"); 483 error = EIO; 484 goto out; 485 } 486 ndm->ndm_host[sn_len] = ':'; 487 memcpy(ndm->ndm_host + sn_len + 1, pathname, path_len + 1); 488 489 /* success */ 490 goto out; 491 492 bad: 493 printf("nfs_boot: bootparam_getfile: bad reply\n"); 494 error = EBADRPC; 495 496 out: 497 m_freem(m); 498 return(0); 499 } 500 501 502