1 1.90 rin /* $NetBSD: nfs_boot.c,v 1.90 2024/07/05 04:31:54 rin Exp $ */ 2 1.5 cgd 3 1.35 gwr /*- 4 1.35 gwr * Copyright (c) 1995, 1997 The NetBSD Foundation, Inc. 5 1.1 glass * All rights reserved. 6 1.1 glass * 7 1.35 gwr * This code is derived from software contributed to The NetBSD Foundation 8 1.35 gwr * by Adam Glass and Gordon W. Ross. 9 1.35 gwr * 10 1.1 glass * Redistribution and use in source and binary forms, with or without 11 1.1 glass * modification, are permitted provided that the following conditions 12 1.1 glass * are met: 13 1.1 glass * 1. Redistributions of source code must retain the above copyright 14 1.1 glass * notice, this list of conditions and the following disclaimer. 15 1.1 glass * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 glass * notice, this list of conditions and the following disclaimer in the 17 1.1 glass * documentation and/or other materials provided with the distribution. 18 1.1 glass * 19 1.35 gwr * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.35 gwr * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.35 gwr * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.35 gwr * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.35 gwr * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.35 gwr * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.35 gwr * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.35 gwr * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.35 gwr * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.35 gwr * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.35 gwr * POSSIBILITY OF SUCH DAMAGE. 30 1.35 gwr */ 31 1.35 gwr 32 1.35 gwr /* 33 1.35 gwr * Support for NFS diskless booting, specifically getting information 34 1.35 gwr * about where to mount root from, what pathnames, etc. 35 1.1 glass */ 36 1.57 lukem 37 1.57 lukem #include <sys/cdefs.h> 38 1.90 rin __KERNEL_RCSID(0, "$NetBSD: nfs_boot.c,v 1.90 2024/07/05 04:31:54 rin Exp $"); 39 1.1 glass 40 1.78 ad #ifdef _KERNEL_OPT 41 1.54 bjh21 #include "opt_nfs.h" 42 1.65 manu #include "opt_tftproot.h" 43 1.42 scottr #include "opt_nfs_boot.h" 44 1.78 ad #endif 45 1.42 scottr 46 1.88 thorpej #ifdef NFS_BOOT_TCP 47 1.88 thorpej #undef NFS_BOOT_UDP 48 1.88 thorpej #endif 49 1.88 thorpej 50 1.2 cgd #include <sys/param.h> 51 1.1 glass #include <sys/systm.h> 52 1.1 glass #include <sys/kernel.h> 53 1.30 thorpej #include <sys/device.h> 54 1.1 glass #include <sys/ioctl.h> 55 1.1 glass #include <sys/proc.h> 56 1.1 glass #include <sys/mount.h> 57 1.1 glass #include <sys/mbuf.h> 58 1.16 gwr #include <sys/reboot.h> 59 1.1 glass #include <sys/socket.h> 60 1.16 gwr #include <sys/socketvar.h> 61 1.1 glass 62 1.1 glass #include <net/if.h> 63 1.3 gwr #include <net/route.h> 64 1.31 is #include <net/if_ether.h> 65 1.35 gwr #include <net/if_types.h> 66 1.31 is 67 1.1 glass #include <netinet/in.h> 68 1.31 is #include <netinet/if_inarp.h> 69 1.1 glass 70 1.1 glass #include <nfs/rpcv2.h> 71 1.37 gwr #include <nfs/krpc.h> 72 1.37 gwr #include <nfs/xdr_subs.h> 73 1.37 gwr 74 1.25 fvdl #include <nfs/nfsproto.h> 75 1.44 fvdl #include <nfs/nfs.h> 76 1.44 fvdl #include <nfs/nfsmount.h> 77 1.1 glass #include <nfs/nfsdiskless.h> 78 1.7 paulus 79 1.1 glass /* 80 1.79 nisimura * There are three implementations of NFS diskless boot. 81 1.35 gwr * One implementation uses BOOTP (RFC951, RFC1048), 82 1.79 nisimura * Sun RPC/bootparams or static configuration. See the 83 1.79 nisimura * files: 84 1.79 nisimura * nfs_bootdhcp.c: BOOTP (RFC951, RFC1048) 85 1.79 nisimura * nfs_bootparam.c: Sun RPC/bootparams 86 1.79 nisimura * nfs_bootstatic.c: honour config(1) description 87 1.1 glass */ 88 1.41 scottr #if defined(NFS_BOOT_BOOTP) || defined(NFS_BOOT_DHCP) 89 1.40 drochner int nfs_boot_rfc951 = 1; /* BOOTP enabled (default) */ 90 1.40 drochner #endif 91 1.40 drochner #ifdef NFS_BOOT_BOOTPARAM 92 1.40 drochner int nfs_boot_bootparam = 1; /* BOOTPARAM enabled (default) */ 93 1.40 drochner #endif 94 1.60 cl #ifdef NFS_BOOT_BOOTSTATIC 95 1.60 cl int nfs_boot_bootstatic = 1; /* BOOTSTATIC enabled (default) */ 96 1.60 cl #endif 97 1.3 gwr 98 1.80 cyber #define IP_MIN_MTU 576 99 1.80 cyber 100 1.3 gwr /* mountd RPC */ 101 1.67 dyoung static int md_mount(struct sockaddr_in *mdsin, char *path, 102 1.67 dyoung struct nfs_args *argp, struct lwp *l); 103 1.3 gwr 104 1.87 ozaki static int nfs_boot_delroute_matcher(struct rtentry *, void *); 105 1.67 dyoung static void nfs_boot_defrt(struct in_addr *); 106 1.67 dyoung static int nfs_boot_getfh(struct nfs_dlmount *ndm, struct lwp *); 107 1.35 gwr 108 1.35 gwr 109 1.1 glass /* 110 1.3 gwr * Called with an empty nfs_diskless struct to be filled in. 111 1.35 gwr * Find an interface, determine its ip address (etc.) and 112 1.35 gwr * save all the boot parameters in the nfs_diskless struct. 113 1.1 glass */ 114 1.6 deraadt int 115 1.75 cegger nfs_boot_init(struct nfs_diskless *nd, struct lwp *lwp) 116 1.1 glass { 117 1.3 gwr struct ifnet *ifp; 118 1.65 manu int error = 0; 119 1.81 martin int flags __unused; 120 1.77 cegger 121 1.77 cegger /* Explicitly necessary or build fails 122 1.77 cegger * due to unused variable, otherwise. 123 1.77 cegger */ 124 1.77 cegger flags = 0; 125 1.3 gwr 126 1.3 gwr /* 127 1.35 gwr * Find the network interface. 128 1.3 gwr */ 129 1.70 cegger ifp = ifunit(device_xname(root_device)); 130 1.30 thorpej if (ifp == NULL) { 131 1.35 gwr printf("nfs_boot: '%s' not found\n", 132 1.70 cegger device_xname(root_device)); 133 1.30 thorpej return (ENXIO); 134 1.30 thorpej } 135 1.50 drochner nd->nd_ifp = ifp; 136 1.3 gwr 137 1.40 drochner error = EADDRNOTAVAIL; /* ??? */ 138 1.60 cl #if defined(NFS_BOOT_BOOTSTATIC) 139 1.60 cl if (error && nfs_boot_bootstatic) { 140 1.60 cl printf("nfs_boot: trying static\n"); 141 1.76 cegger error = nfs_bootstatic(nd, lwp, &flags); 142 1.60 cl } 143 1.60 cl #endif 144 1.41 scottr #if defined(NFS_BOOT_BOOTP) || defined(NFS_BOOT_DHCP) 145 1.47 drochner if (error && nfs_boot_rfc951) { 146 1.43 cgd #if defined(NFS_BOOT_DHCP) 147 1.43 cgd printf("nfs_boot: trying DHCP/BOOTP\n"); 148 1.43 cgd #else 149 1.43 cgd printf("nfs_boot: trying BOOTP\n"); 150 1.43 cgd #endif 151 1.76 cegger error = nfs_bootdhcp(nd, lwp, &flags); 152 1.40 drochner } 153 1.40 drochner #endif 154 1.40 drochner #ifdef NFS_BOOT_BOOTPARAM 155 1.47 drochner if (error && nfs_boot_bootparam) { 156 1.35 gwr printf("nfs_boot: trying RARP (and RPC/bootparam)\n"); 157 1.76 cegger error = nfs_bootparam(nd, lwp, &flags); 158 1.30 thorpej } 159 1.40 drochner #endif 160 1.46 tv if (error) 161 1.46 tv return (error); 162 1.3 gwr 163 1.3 gwr /* 164 1.80 cyber * Set MTU if passed 165 1.80 cyber */ 166 1.80 cyber if (nd->nd_mtu >= IP_MIN_MTU ) 167 1.80 cyber nfs_boot_setmtu(nd->nd_ifp, nd->nd_mtu, lwp); 168 1.80 cyber 169 1.80 cyber /* 170 1.3 gwr * If the gateway address is set, add a default route. 171 1.3 gwr * (The mountd RPCs may go across a gateway.) 172 1.1 glass */ 173 1.35 gwr if (nd->nd_gwip.s_addr) 174 1.35 gwr nfs_boot_defrt(&nd->nd_gwip); 175 1.3 gwr 176 1.65 manu #ifdef TFTPROOT 177 1.65 manu if (nd->nd_nomount) 178 1.65 manu goto out; 179 1.65 manu #endif 180 1.37 gwr /* 181 1.37 gwr * Now fetch the NFS file handles as appropriate. 182 1.37 gwr */ 183 1.62 christos error = nfs_boot_getfh(&nd->nd_root, lwp); 184 1.37 gwr 185 1.50 drochner if (error) 186 1.62 christos nfs_boot_cleanup(nd, lwp); 187 1.50 drochner 188 1.66 manu #ifdef TFTPROOT 189 1.65 manu out: 190 1.66 manu #endif 191 1.37 gwr return (error); 192 1.39 drochner } 193 1.39 drochner 194 1.50 drochner void 195 1.75 cegger nfs_boot_cleanup(struct nfs_diskless *nd, struct lwp *lwp) 196 1.50 drochner { 197 1.50 drochner 198 1.62 christos nfs_boot_deladdress(nd->nd_ifp, lwp, nd->nd_myip.s_addr); 199 1.62 christos nfs_boot_ifupdown(nd->nd_ifp, lwp, 0); 200 1.50 drochner nfs_boot_flushrt(nd->nd_ifp); 201 1.50 drochner } 202 1.50 drochner 203 1.50 drochner int 204 1.75 cegger nfs_boot_ifupdown(struct ifnet *ifp, struct lwp *lwp, int up) 205 1.50 drochner { 206 1.50 drochner struct socket *so; 207 1.50 drochner struct ifreq ireq; 208 1.50 drochner int error; 209 1.50 drochner 210 1.50 drochner memset(&ireq, 0, sizeof(ireq)); 211 1.50 drochner memcpy(ireq.ifr_name, ifp->if_xname, IFNAMSIZ); 212 1.50 drochner 213 1.50 drochner /* 214 1.50 drochner * Get a socket to use for various things in here. 215 1.50 drochner * After this, use "goto out" to cleanup and return. 216 1.50 drochner */ 217 1.71 ad error = socreate(AF_INET, &so, SOCK_DGRAM, 0, lwp, NULL); 218 1.50 drochner if (error) { 219 1.50 drochner printf("ifupdown: socreate, error=%d\n", error); 220 1.50 drochner return (error); 221 1.50 drochner } 222 1.50 drochner 223 1.50 drochner /* 224 1.50 drochner * Bring up the interface. (just set the "up" flag) 225 1.50 drochner * Get the old interface flags and or IFF_UP into them so 226 1.50 drochner * things like media selection flags are not clobbered. 227 1.50 drochner */ 228 1.64 christos error = ifioctl(so, SIOCGIFFLAGS, (void *)&ireq, lwp); 229 1.50 drochner if (error) { 230 1.50 drochner printf("ifupdown: GIFFLAGS, error=%d\n", error); 231 1.50 drochner goto out; 232 1.50 drochner } 233 1.50 drochner if (up) 234 1.50 drochner ireq.ifr_flags |= IFF_UP; 235 1.50 drochner else 236 1.50 drochner ireq.ifr_flags &= ~IFF_UP; 237 1.73 dyoung error = ifioctl(so, SIOCSIFFLAGS, &ireq, lwp); 238 1.50 drochner if (error) { 239 1.50 drochner printf("ifupdown: SIFFLAGS, error=%d\n", error); 240 1.50 drochner goto out; 241 1.50 drochner } 242 1.50 drochner 243 1.52 drochner if (up) 244 1.56 enami /* give the link some time to get up */ 245 1.56 enami tsleep(nfs_boot_ifupdown, PZERO, "nfsbif", 3 * hz); 246 1.50 drochner out: 247 1.50 drochner soclose(so); 248 1.50 drochner return (error); 249 1.50 drochner } 250 1.50 drochner 251 1.80 cyber void 252 1.80 cyber nfs_boot_setmtu(struct ifnet *ifp, int mtu, struct lwp *lwp) 253 1.80 cyber { 254 1.80 cyber struct socket *so; 255 1.80 cyber struct ifreq ireq; 256 1.80 cyber int error; 257 1.80 cyber 258 1.80 cyber memset(&ireq, 0, sizeof(ireq)); 259 1.80 cyber memcpy(ireq.ifr_name, ifp->if_xname, IFNAMSIZ); 260 1.80 cyber 261 1.80 cyber /* 262 1.80 cyber * Get a socket to use for various things in here. 263 1.80 cyber * After this, use "goto out" to cleanup and return. 264 1.80 cyber */ 265 1.80 cyber error = socreate(AF_INET, &so, SOCK_DGRAM, 0, lwp, NULL); 266 1.80 cyber if (error) { 267 1.80 cyber printf("setmtu: socreate, error=%d\n", error); 268 1.80 cyber return; 269 1.80 cyber } 270 1.80 cyber 271 1.80 cyber /* 272 1.80 cyber * Get structure, set the new MTU, push structure. 273 1.80 cyber */ 274 1.80 cyber error = ifioctl(so, SIOCGIFMTU, (void *)&ireq, lwp); 275 1.80 cyber if (error) { 276 1.80 cyber printf("setmtu: GIFMTU, error=%d\n", error); 277 1.80 cyber goto out; 278 1.80 cyber } 279 1.80 cyber 280 1.80 cyber ireq.ifr_mtu = mtu; 281 1.80 cyber 282 1.80 cyber error = ifioctl(so, SIOCSIFMTU, &ireq, lwp); 283 1.80 cyber if (error) { 284 1.80 cyber printf("setmtu: SIFMTU, error=%d\n", error); 285 1.80 cyber goto out; 286 1.80 cyber } 287 1.80 cyber 288 1.80 cyber out: 289 1.80 cyber soclose(so); 290 1.80 cyber return; 291 1.80 cyber } 292 1.80 cyber 293 1.50 drochner int 294 1.75 cegger nfs_boot_setaddress(struct ifnet *ifp, struct lwp *lwp, 295 1.75 cegger uint32_t addr, uint32_t netmask, uint32_t braddr) 296 1.50 drochner { 297 1.50 drochner struct socket *so; 298 1.50 drochner struct ifaliasreq iareq; 299 1.50 drochner struct sockaddr_in *sin; 300 1.50 drochner int error; 301 1.50 drochner 302 1.50 drochner /* 303 1.50 drochner * Get a socket to use for various things in here. 304 1.50 drochner * After this, use "goto out" to cleanup and return. 305 1.50 drochner */ 306 1.71 ad error = socreate(AF_INET, &so, SOCK_DGRAM, 0, lwp, NULL); 307 1.50 drochner if (error) { 308 1.50 drochner printf("setaddress: socreate, error=%d\n", error); 309 1.50 drochner return (error); 310 1.50 drochner } 311 1.50 drochner 312 1.50 drochner memset(&iareq, 0, sizeof(iareq)); 313 1.50 drochner memcpy(iareq.ifra_name, ifp->if_xname, IFNAMSIZ); 314 1.50 drochner 315 1.50 drochner /* Set the I/F address */ 316 1.50 drochner sin = (struct sockaddr_in *)&iareq.ifra_addr; 317 1.50 drochner sin->sin_len = sizeof(*sin); 318 1.50 drochner sin->sin_family = AF_INET; 319 1.50 drochner sin->sin_addr.s_addr = addr; 320 1.50 drochner 321 1.50 drochner /* Set the netmask */ 322 1.50 drochner if (netmask != INADDR_ANY) { 323 1.50 drochner sin = (struct sockaddr_in *)&iareq.ifra_mask; 324 1.50 drochner sin->sin_len = sizeof(*sin); 325 1.50 drochner sin->sin_family = AF_INET; 326 1.50 drochner sin->sin_addr.s_addr = netmask; 327 1.50 drochner } /* else leave subnetmask unspecified (len=0) */ 328 1.50 drochner 329 1.50 drochner /* Set the broadcast addr. */ 330 1.50 drochner if (braddr != INADDR_ANY) { 331 1.50 drochner sin = (struct sockaddr_in *)&iareq.ifra_broadaddr; 332 1.50 drochner sin->sin_len = sizeof(*sin); 333 1.50 drochner sin->sin_family = AF_INET; 334 1.50 drochner sin->sin_addr.s_addr = braddr; 335 1.50 drochner } /* else leave broadcast addr unspecified (len=0) */ 336 1.50 drochner 337 1.64 christos error = ifioctl(so, SIOCAIFADDR, (void *)&iareq, lwp); 338 1.50 drochner if (error) { 339 1.50 drochner printf("setaddress, error=%d\n", error); 340 1.50 drochner goto out; 341 1.50 drochner } 342 1.50 drochner 343 1.56 enami /* give the link some time to get up */ 344 1.56 enami tsleep(nfs_boot_setaddress, PZERO, "nfsbtd", 3 * hz); 345 1.50 drochner out: 346 1.50 drochner soclose(so); 347 1.50 drochner return (error); 348 1.50 drochner } 349 1.50 drochner 350 1.50 drochner int 351 1.75 cegger nfs_boot_deladdress(struct ifnet *ifp, struct lwp *lwp, uint32_t addr) 352 1.50 drochner { 353 1.50 drochner struct socket *so; 354 1.69 dyoung struct ifreq ifr; 355 1.69 dyoung struct sockaddr_in sin; 356 1.69 dyoung struct in_addr ia = {.s_addr = addr}; 357 1.50 drochner int error; 358 1.50 drochner 359 1.50 drochner /* 360 1.50 drochner * Get a socket to use for various things in here. 361 1.50 drochner * After this, use "goto out" to cleanup and return. 362 1.50 drochner */ 363 1.71 ad error = socreate(AF_INET, &so, SOCK_DGRAM, 0, lwp, NULL); 364 1.50 drochner if (error) { 365 1.50 drochner printf("deladdress: socreate, error=%d\n", error); 366 1.50 drochner return (error); 367 1.50 drochner } 368 1.50 drochner 369 1.69 dyoung memset(&ifr, 0, sizeof(ifr)); 370 1.69 dyoung memcpy(ifr.ifr_name, ifp->if_xname, IFNAMSIZ); 371 1.50 drochner 372 1.69 dyoung sockaddr_in_init(&sin, &ia, 0); 373 1.86 msaitoh ifreq_setaddr(SIOCDIFADDR, &ifr, sintocsa(&sin)); 374 1.50 drochner 375 1.69 dyoung error = ifioctl(so, SIOCDIFADDR, &ifr, lwp); 376 1.50 drochner if (error) { 377 1.50 drochner printf("deladdress, error=%d\n", error); 378 1.50 drochner goto out; 379 1.50 drochner } 380 1.50 drochner 381 1.50 drochner out: 382 1.50 drochner soclose(so); 383 1.50 drochner return (error); 384 1.50 drochner } 385 1.50 drochner 386 1.50 drochner int 387 1.75 cegger nfs_boot_setrecvtimo(struct socket *so) 388 1.39 drochner { 389 1.74 plunky struct timeval tv; 390 1.74 plunky 391 1.74 plunky tv.tv_sec = 1; 392 1.74 plunky tv.tv_usec = 0; 393 1.39 drochner 394 1.74 plunky return (so_setsockopt(NULL, so, SOL_SOCKET, SO_RCVTIMEO, &tv, 395 1.74 plunky sizeof(tv))); 396 1.39 drochner } 397 1.39 drochner 398 1.50 drochner int 399 1.75 cegger nfs_boot_enbroadcast(struct socket *so) 400 1.39 drochner { 401 1.74 plunky int32_t on; 402 1.39 drochner 403 1.74 plunky on = 1; 404 1.74 plunky return (so_setsockopt(NULL, so, SOL_SOCKET, SO_BROADCAST, &on, 405 1.74 plunky sizeof(on))); 406 1.39 drochner } 407 1.39 drochner 408 1.50 drochner int 409 1.75 cegger nfs_boot_sobind_ipport(struct socket *so, uint16_t port, struct lwp *l) 410 1.39 drochner { 411 1.83 rtr struct sockaddr_in sin; 412 1.39 drochner int error; 413 1.39 drochner 414 1.83 rtr sin.sin_len = sizeof(sin); 415 1.83 rtr sin.sin_family = AF_INET; 416 1.83 rtr sin.sin_addr.s_addr = INADDR_ANY; 417 1.83 rtr sin.sin_port = htons(port); 418 1.83 rtr error = sobind(so, (struct sockaddr *)&sin, l); 419 1.50 drochner return (error); 420 1.39 drochner } 421 1.39 drochner 422 1.39 drochner /* 423 1.39 drochner * What is the longest we will wait before re-sending a request? 424 1.39 drochner * Note this is also the frequency of "timeout" messages. 425 1.39 drochner * The re-send loop counts up linearly to this maximum, so the 426 1.39 drochner * first complaint will happen after (1+2+3+4+5)=15 seconds. 427 1.39 drochner */ 428 1.39 drochner #define MAX_RESEND_DELAY 5 /* seconds */ 429 1.39 drochner #define TOTAL_TIMEOUT 30 /* seconds */ 430 1.39 drochner 431 1.50 drochner int 432 1.85 rtr nfs_boot_sendrecv(struct socket *so, struct sockaddr_in *nam, 433 1.75 cegger int (*sndproc)(struct mbuf *, void *, int), 434 1.75 cegger struct mbuf *snd, 435 1.82 hikaru int (*rcvproc)(struct mbuf **, void *), 436 1.75 cegger struct mbuf **rcv, struct mbuf **from_p, 437 1.75 cegger void *context, struct lwp *lwp) 438 1.39 drochner { 439 1.39 drochner int error, rcvflg, timo, secs, waited; 440 1.39 drochner struct mbuf *m, *from; 441 1.39 drochner struct uio uio; 442 1.39 drochner 443 1.39 drochner /* Free at end if not null. */ 444 1.39 drochner from = NULL; 445 1.39 drochner 446 1.39 drochner /* 447 1.39 drochner * Send it, repeatedly, until a reply is received, 448 1.39 drochner * but delay each re-send by an increasing amount. 449 1.39 drochner * If the delay hits the maximum, start complaining. 450 1.39 drochner */ 451 1.39 drochner waited = timo = 0; 452 1.39 drochner send_again: 453 1.39 drochner waited += timo; 454 1.39 drochner if (waited >= TOTAL_TIMEOUT) 455 1.50 drochner return (ETIMEDOUT); 456 1.39 drochner 457 1.39 drochner /* Determine new timeout. */ 458 1.39 drochner if (timo < MAX_RESEND_DELAY) 459 1.39 drochner timo++; 460 1.39 drochner else 461 1.39 drochner printf("nfs_boot: timeout...\n"); 462 1.39 drochner 463 1.39 drochner if (sndproc) { 464 1.39 drochner error = (*sndproc)(snd, context, waited); 465 1.39 drochner if (error) 466 1.39 drochner goto out; 467 1.39 drochner } 468 1.39 drochner 469 1.39 drochner /* Send request (or re-send). */ 470 1.39 drochner m = m_copypacket(snd, M_WAIT); 471 1.39 drochner if (m == NULL) { 472 1.39 drochner error = ENOBUFS; 473 1.39 drochner goto out; 474 1.39 drochner } 475 1.85 rtr error = (*so->so_send)(so, (struct sockaddr *)nam, NULL, 476 1.84 rtr m, NULL, 0, lwp); 477 1.39 drochner if (error) { 478 1.39 drochner printf("nfs_boot: sosend: %d\n", error); 479 1.39 drochner goto out; 480 1.39 drochner } 481 1.39 drochner m = NULL; 482 1.39 drochner 483 1.39 drochner /* 484 1.39 drochner * Wait for up to timo seconds for a reply. 485 1.39 drochner * The socket receive timeout was set to 1 second. 486 1.39 drochner */ 487 1.39 drochner 488 1.39 drochner secs = timo; 489 1.39 drochner for (;;) { 490 1.90 rin m_freem(from); 491 1.90 rin from = NULL; 492 1.90 rin m_freem(m); 493 1.90 rin m = NULL; 494 1.39 drochner uio.uio_resid = 1 << 16; /* ??? */ 495 1.39 drochner rcvflg = 0; 496 1.45 matt error = (*so->so_receive)(so, &from, &uio, &m, NULL, &rcvflg); 497 1.39 drochner if (error == EWOULDBLOCK) { 498 1.39 drochner if (--secs <= 0) 499 1.39 drochner goto send_again; 500 1.39 drochner continue; 501 1.39 drochner } 502 1.39 drochner if (error) 503 1.39 drochner goto out; 504 1.39 drochner #ifdef DIAGNOSTIC 505 1.39 drochner if (!m || !(m->m_flags & M_PKTHDR) 506 1.39 drochner || (1 << 16) - uio.uio_resid != m->m_pkthdr.len) 507 1.39 drochner panic("nfs_boot_sendrecv: return size"); 508 1.39 drochner #endif 509 1.39 drochner 510 1.82 hikaru if ((*rcvproc)(&m, context)) 511 1.39 drochner continue; 512 1.39 drochner 513 1.39 drochner if (rcv) 514 1.39 drochner *rcv = m; 515 1.39 drochner else 516 1.39 drochner m_freem(m); 517 1.39 drochner if (from_p) { 518 1.39 drochner *from_p = from; 519 1.39 drochner from = NULL; 520 1.39 drochner } 521 1.39 drochner break; 522 1.39 drochner } 523 1.39 drochner out: 524 1.90 rin m_freem(from); 525 1.50 drochner return (error); 526 1.35 gwr } 527 1.1 glass 528 1.35 gwr /* 529 1.35 gwr * Install a default route to the passed IP address. 530 1.35 gwr */ 531 1.35 gwr static void 532 1.75 cegger nfs_boot_defrt(struct in_addr *gw_ip) 533 1.35 gwr { 534 1.35 gwr struct sockaddr dst, gw, mask; 535 1.35 gwr struct sockaddr_in *sin; 536 1.35 gwr int error; 537 1.33 gwr 538 1.35 gwr /* Destination: (default) */ 539 1.64 christos memset((void *)&dst, 0, sizeof(dst)); 540 1.35 gwr dst.sa_len = sizeof(dst); 541 1.35 gwr dst.sa_family = AF_INET; 542 1.35 gwr /* Gateway: */ 543 1.64 christos memset((void *)&gw, 0, sizeof(gw)); 544 1.35 gwr sin = (struct sockaddr_in *)&gw; 545 1.35 gwr sin->sin_len = sizeof(*sin); 546 1.35 gwr sin->sin_family = AF_INET; 547 1.35 gwr sin->sin_addr.s_addr = gw_ip->s_addr; 548 1.35 gwr /* Mask: (zero length) */ 549 1.35 gwr /* XXX - Just pass a null pointer? */ 550 1.48 perry memset(&mask, 0, sizeof(mask)); 551 1.35 gwr 552 1.35 gwr /* add, dest, gw, mask, flags, 0 */ 553 1.35 gwr error = rtrequest(RTM_ADD, &dst, &gw, &mask, 554 1.50 drochner (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL); 555 1.33 gwr if (error) { 556 1.35 gwr printf("nfs_boot: add route, error=%d\n", error); 557 1.33 gwr error = 0; 558 1.33 gwr } 559 1.50 drochner } 560 1.50 drochner 561 1.50 drochner static int 562 1.87 ozaki nfs_boot_delroute_matcher(struct rtentry *rt, void *w) 563 1.50 drochner { 564 1.50 drochner 565 1.68 dyoung if ((void *)rt->rt_ifp != w) 566 1.68 dyoung return 0; 567 1.50 drochner 568 1.87 ozaki return 1; 569 1.50 drochner } 570 1.50 drochner 571 1.50 drochner void 572 1.67 dyoung nfs_boot_flushrt(struct ifnet *ifp) 573 1.50 drochner { 574 1.50 drochner 575 1.89 knakahar rt_delete_matched_entries(AF_INET, nfs_boot_delroute_matcher, ifp, false); 576 1.3 gwr } 577 1.1 glass 578 1.35 gwr /* 579 1.35 gwr * Get an initial NFS file handle using Sun RPC/mountd. 580 1.35 gwr * Separate function because we used to call it twice. 581 1.35 gwr * (once for root and once for swap) 582 1.76 cegger * 583 1.76 cegger * ndm output 584 1.35 gwr */ 585 1.37 gwr static int 586 1.75 cegger nfs_boot_getfh(struct nfs_dlmount *ndm, struct lwp *l) 587 1.3 gwr { 588 1.29 fvdl struct nfs_args *args; 589 1.23 gwr struct sockaddr_in *sin; 590 1.33 gwr char *pathname; 591 1.3 gwr int error; 592 1.35 gwr u_int16_t port; 593 1.1 glass 594 1.33 gwr args = &ndm->ndm_args; 595 1.29 fvdl 596 1.29 fvdl /* Initialize mount args. */ 597 1.64 christos memset((void *) args, 0, sizeof(*args)); 598 1.33 gwr args->addr = &ndm->ndm_saddr; 599 1.29 fvdl args->addrlen = args->addr->sa_len; 600 1.88 thorpej #ifdef NFS_BOOT_UDP 601 1.88 thorpej args->sotype = SOCK_DGRAM; 602 1.88 thorpej #else 603 1.29 fvdl args->sotype = SOCK_STREAM; 604 1.29 fvdl #endif 605 1.33 gwr args->fh = ndm->ndm_fh; 606 1.33 gwr args->hostname = ndm->ndm_host; 607 1.54 bjh21 args->flags = NFSMNT_NOCONN | NFSMNT_RESVPORT; 608 1.29 fvdl 609 1.54 bjh21 #ifndef NFS_V2_ONLY 610 1.54 bjh21 args->flags |= NFSMNT_NFSV3; 611 1.54 bjh21 #endif 612 1.29 fvdl #ifdef NFS_BOOT_OPTIONS 613 1.29 fvdl args->flags |= NFS_BOOT_OPTIONS; 614 1.29 fvdl #endif 615 1.29 fvdl #ifdef NFS_BOOT_RWSIZE 616 1.29 fvdl /* 617 1.29 fvdl * Reduce rsize,wsize for interfaces that consistently 618 1.29 fvdl * drop fragments of long UDP messages. (i.e. wd8003). 619 1.29 fvdl * You can always change these later via remount. 620 1.29 fvdl */ 621 1.29 fvdl args->flags |= NFSMNT_WSIZE | NFSMNT_RSIZE; 622 1.29 fvdl args->wsize = NFS_BOOT_RWSIZE; 623 1.29 fvdl args->rsize = NFS_BOOT_RWSIZE; 624 1.29 fvdl #endif 625 1.29 fvdl 626 1.3 gwr /* 627 1.33 gwr * Find the pathname part of the "server:pathname" 628 1.33 gwr * string left in ndm->ndm_host by nfs_boot_init. 629 1.3 gwr */ 630 1.33 gwr pathname = strchr(ndm->ndm_host, ':'); 631 1.33 gwr if (pathname == 0) { 632 1.33 gwr printf("nfs_boot: getfh - no pathname\n"); 633 1.33 gwr return (EIO); 634 1.33 gwr } 635 1.33 gwr pathname++; 636 1.1 glass 637 1.3 gwr /* 638 1.33 gwr * Get file handle using RPC to mountd/mount 639 1.3 gwr */ 640 1.33 gwr sin = (struct sockaddr_in *)&ndm->ndm_saddr; 641 1.62 christos error = md_mount(sin, pathname, args, l); 642 1.33 gwr if (error) { 643 1.33 gwr printf("nfs_boot: mountd `%s', error=%d\n", 644 1.35 gwr ndm->ndm_host, error); 645 1.33 gwr return (error); 646 1.33 gwr } 647 1.4 pk 648 1.23 gwr /* Set port number for NFS use. */ 649 1.23 gwr /* XXX: NFS port is always 2049, right? */ 650 1.29 fvdl retry: 651 1.29 fvdl error = krpc_portmap(sin, NFS_PROG, 652 1.29 fvdl (args->flags & NFSMNT_NFSV3) ? NFS_VER3 : NFS_VER2, 653 1.29 fvdl (args->sotype == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP, 654 1.62 christos &port, l); 655 1.35 gwr if (port == htons(0)) 656 1.33 gwr error = EIO; 657 1.35 gwr if (error) { 658 1.29 fvdl if (args->sotype == SOCK_STREAM) { 659 1.29 fvdl args->sotype = SOCK_DGRAM; 660 1.29 fvdl goto retry; 661 1.29 fvdl } 662 1.33 gwr printf("nfs_boot: portmap NFS, error=%d\n", error); 663 1.35 gwr return (error); 664 1.29 fvdl } 665 1.35 gwr sin->sin_port = port; 666 1.35 gwr return (0); 667 1.3 gwr } 668 1.1 glass 669 1.1 glass 670 1.1 glass /* 671 1.3 gwr * RPC: mountd/mount 672 1.3 gwr * Given a server pathname, get an NFS file handle. 673 1.3 gwr * Also, sets sin->sin_port to the NFS service port. 674 1.75 cegger * 675 1.75 cegger * mdsin mountd server address 676 1.1 glass */ 677 1.3 gwr static int 678 1.75 cegger md_mount(struct sockaddr_in *mdsin, char *path, 679 1.75 cegger struct nfs_args *argp, struct lwp *lwp) 680 1.1 glass { 681 1.3 gwr /* The RPC structures */ 682 1.3 gwr struct rdata { 683 1.20 cgd u_int32_t errno; 684 1.29 fvdl union { 685 1.29 fvdl u_int8_t v2fh[NFSX_V2FH]; 686 1.29 fvdl struct { 687 1.29 fvdl u_int32_t fhlen; 688 1.29 fvdl u_int8_t fh[1]; 689 1.29 fvdl } v3fh; 690 1.29 fvdl } fh; 691 1.3 gwr } *rdata; 692 1.3 gwr struct mbuf *m; 693 1.29 fvdl u_int8_t *fh; 694 1.29 fvdl int minlen, error; 695 1.35 gwr int mntver; 696 1.3 gwr 697 1.35 gwr mntver = (argp->flags & NFSMNT_NFSV3) ? 3 : 2; 698 1.35 gwr do { 699 1.35 gwr /* 700 1.35 gwr * Get port number for MOUNTD. 701 1.35 gwr */ 702 1.35 gwr error = krpc_portmap(mdsin, RPCPROG_MNT, mntver, 703 1.62 christos IPPROTO_UDP, &mdsin->sin_port, lwp); 704 1.35 gwr if (error) 705 1.35 gwr continue; 706 1.35 gwr 707 1.35 gwr /* This mbuf is consumed by krpc_call. */ 708 1.35 gwr m = xdr_string_encode(path, strlen(path)); 709 1.35 gwr if (m == NULL) 710 1.35 gwr return ENOMEM; 711 1.11 gwr 712 1.35 gwr /* Do RPC to mountd. */ 713 1.35 gwr error = krpc_call(mdsin, RPCPROG_MNT, mntver, 714 1.62 christos RPCMNT_MOUNT, &m, NULL, lwp); 715 1.35 gwr if (error != EPROGMISMATCH) 716 1.35 gwr break; 717 1.35 gwr /* Try lower version of mountd. */ 718 1.35 gwr } while (--mntver >= 1); 719 1.29 fvdl if (error) { 720 1.35 gwr printf("nfs_boot: mountd error=%d\n", error); 721 1.35 gwr return error; 722 1.29 fvdl } 723 1.35 gwr if (mntver != 3) 724 1.35 gwr argp->flags &= ~NFSMNT_NFSV3; 725 1.1 glass 726 1.23 gwr /* The reply might have only the errno. */ 727 1.23 gwr if (m->m_len < 4) 728 1.23 gwr goto bad; 729 1.23 gwr /* Have at least errno, so check that. */ 730 1.23 gwr rdata = mtod(m, struct rdata *); 731 1.23 gwr error = fxdr_unsigned(u_int32_t, rdata->errno); 732 1.23 gwr if (error) 733 1.23 gwr goto out; 734 1.23 gwr 735 1.35 gwr /* Have errno==0, so the fh must be there. */ 736 1.35 gwr if (mntver == 3) { 737 1.29 fvdl argp->fhsize = fxdr_unsigned(u_int32_t, rdata->fh.v3fh.fhlen); 738 1.29 fvdl if (argp->fhsize > NFSX_V3FHMAX) 739 1.29 fvdl goto bad; 740 1.29 fvdl minlen = 2 * sizeof(u_int32_t) + argp->fhsize; 741 1.29 fvdl } else { 742 1.29 fvdl argp->fhsize = NFSX_V2FH; 743 1.29 fvdl minlen = sizeof(u_int32_t) + argp->fhsize; 744 1.29 fvdl } 745 1.29 fvdl 746 1.29 fvdl if (m->m_len < minlen) { 747 1.29 fvdl m = m_pullup(m, minlen); 748 1.16 gwr if (m == NULL) 749 1.29 fvdl return(EBADRPC); 750 1.23 gwr rdata = mtod(m, struct rdata *); 751 1.16 gwr } 752 1.29 fvdl 753 1.35 gwr fh = (mntver == 3) ? 754 1.35 gwr rdata->fh.v3fh.fh : rdata->fh.v2fh; 755 1.48 perry memcpy(argp->fh, fh, argp->fhsize); 756 1.29 fvdl 757 1.3 gwr goto out; 758 1.1 glass 759 1.6 deraadt bad: 760 1.3 gwr error = EBADRPC; 761 1.1 glass 762 1.6 deraadt out: 763 1.3 gwr m_freem(m); 764 1.3 gwr return error; 765 1.7 paulus } 766