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