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