1 /* $NetBSD: nfs_boot.c,v 1.78 2008/11/19 18:36:09 ad Exp $ */
4 * Copyright (c) 1995, 1997 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Adam Glass and Gordon W. Ross.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
33 * Support for NFS diskless booting, specifically getting information
34 * about where to mount root from, what pathnames, etc.
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: nfs_boot.c,v 1.78 2008/11/19 18:36:09 ad Exp $");
42 #include "opt_tftproot.h"
43 #include "opt_nfs_boot.h"
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>
52 #include <sys/mount.h>
54 #include <sys/reboot.h>
55 #include <sys/socket.h>
56 #include <sys/socketvar.h>
59 #include <net/route.h>
60 #include <net/if_ether.h>
61 #include <net/if_types.h>
63 #include <netinet/in.h>
64 #include <netinet/if_inarp.h>
66 #include <nfs/rpcv2.h>
68 #include <nfs/xdr_subs.h>
70 #include <nfs/nfsproto.h>
72 #include <nfs/nfsmount.h>
73 #include <nfs/nfsdiskless.h>
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
80 * nfs_bootdhcp.c: BOOTP (RFC951, RFC1048)
81 * nfs_bootparam.c: Sun RPC/bootparams
82 * nfs_bootstatic.c: honour config(1) description
84 #if defined(NFS_BOOT_BOOTP) || defined(NFS_BOOT_DHCP)
85 int nfs_boot_rfc951
= 1; /* BOOTP enabled (default) */
87 #ifdef NFS_BOOT_BOOTPARAM
88 int nfs_boot_bootparam
= 1; /* BOOTPARAM enabled (default) */
90 #ifdef NFS_BOOT_BOOTSTATIC
91 int nfs_boot_bootstatic
= 1; /* BOOTSTATIC enabled (default) */
95 static int md_mount(struct sockaddr_in
*mdsin
, char *path
,
96 struct nfs_args
*argp
, struct lwp
*l
);
98 static int nfs_boot_delroute(struct rtentry
*, void *);
99 static void nfs_boot_defrt(struct in_addr
*);
100 static int nfs_boot_getfh(struct nfs_dlmount
*ndm
, struct lwp
*);
104 * Called with an empty nfs_diskless struct to be filled in.
105 * Find an interface, determine its ip address (etc.) and
106 * save all the boot parameters in the nfs_diskless struct.
109 nfs_boot_init(struct nfs_diskless
*nd
, struct lwp
*lwp
)
115 /* Explicitly necessary or build fails
116 * due to unused variable, otherwise.
121 * Find the network interface.
123 ifp
= ifunit(device_xname(root_device
));
125 printf("nfs_boot: '%s' not found\n",
126 device_xname(root_device
));
131 error
= EADDRNOTAVAIL
; /* ??? */
132 #if defined(NFS_BOOT_BOOTSTATIC)
133 if (error
&& nfs_boot_bootstatic
) {
134 printf("nfs_boot: trying static\n");
135 error
= nfs_bootstatic(nd
, lwp
, &flags
);
138 #if defined(NFS_BOOT_BOOTP) || defined(NFS_BOOT_DHCP)
139 if (error
&& nfs_boot_rfc951
) {
140 #if defined(NFS_BOOT_DHCP)
141 printf("nfs_boot: trying DHCP/BOOTP\n");
143 printf("nfs_boot: trying BOOTP\n");
145 error
= nfs_bootdhcp(nd
, lwp
, &flags
);
148 #ifdef NFS_BOOT_BOOTPARAM
149 if (error
&& nfs_boot_bootparam
) {
150 printf("nfs_boot: trying RARP (and RPC/bootparam)\n");
151 error
= nfs_bootparam(nd
, lwp
, &flags
);
158 * If the gateway address is set, add a default route.
159 * (The mountd RPCs may go across a gateway.)
161 if (nd
->nd_gwip
.s_addr
)
162 nfs_boot_defrt(&nd
->nd_gwip
);
169 * Now fetch the NFS file handles as appropriate.
171 error
= nfs_boot_getfh(&nd
->nd_root
, lwp
);
174 nfs_boot_cleanup(nd
, lwp
);
183 nfs_boot_cleanup(struct nfs_diskless
*nd
, struct lwp
*lwp
)
186 nfs_boot_deladdress(nd
->nd_ifp
, lwp
, nd
->nd_myip
.s_addr
);
187 nfs_boot_ifupdown(nd
->nd_ifp
, lwp
, 0);
188 nfs_boot_flushrt(nd
->nd_ifp
);
192 nfs_boot_ifupdown(struct ifnet
*ifp
, struct lwp
*lwp
, int up
)
198 memset(&ireq
, 0, sizeof(ireq
));
199 memcpy(ireq
.ifr_name
, ifp
->if_xname
, IFNAMSIZ
);
202 * Get a socket to use for various things in here.
203 * After this, use "goto out" to cleanup and return.
205 error
= socreate(AF_INET
, &so
, SOCK_DGRAM
, 0, lwp
, NULL
);
207 printf("ifupdown: socreate, error=%d\n", error
);
212 * Bring up the interface. (just set the "up" flag)
213 * Get the old interface flags and or IFF_UP into them so
214 * things like media selection flags are not clobbered.
216 error
= ifioctl(so
, SIOCGIFFLAGS
, (void *)&ireq
, lwp
);
218 printf("ifupdown: GIFFLAGS, error=%d\n", error
);
222 ireq
.ifr_flags
|= IFF_UP
;
224 ireq
.ifr_flags
&= ~IFF_UP
;
225 error
= ifioctl(so
, SIOCSIFFLAGS
, &ireq
, lwp
);
227 printf("ifupdown: SIFFLAGS, error=%d\n", error
);
232 /* give the link some time to get up */
233 tsleep(nfs_boot_ifupdown
, PZERO
, "nfsbif", 3 * hz
);
240 nfs_boot_setaddress(struct ifnet
*ifp
, struct lwp
*lwp
,
241 uint32_t addr
, uint32_t netmask
, uint32_t braddr
)
244 struct ifaliasreq iareq
;
245 struct sockaddr_in
*sin
;
249 * Get a socket to use for various things in here.
250 * After this, use "goto out" to cleanup and return.
252 error
= socreate(AF_INET
, &so
, SOCK_DGRAM
, 0, lwp
, NULL
);
254 printf("setaddress: socreate, error=%d\n", error
);
258 memset(&iareq
, 0, sizeof(iareq
));
259 memcpy(iareq
.ifra_name
, ifp
->if_xname
, IFNAMSIZ
);
261 /* Set the I/F address */
262 sin
= (struct sockaddr_in
*)&iareq
.ifra_addr
;
263 sin
->sin_len
= sizeof(*sin
);
264 sin
->sin_family
= AF_INET
;
265 sin
->sin_addr
.s_addr
= addr
;
267 /* Set the netmask */
268 if (netmask
!= INADDR_ANY
) {
269 sin
= (struct sockaddr_in
*)&iareq
.ifra_mask
;
270 sin
->sin_len
= sizeof(*sin
);
271 sin
->sin_family
= AF_INET
;
272 sin
->sin_addr
.s_addr
= netmask
;
273 } /* else leave subnetmask unspecified (len=0) */
275 /* Set the broadcast addr. */
276 if (braddr
!= INADDR_ANY
) {
277 sin
= (struct sockaddr_in
*)&iareq
.ifra_broadaddr
;
278 sin
->sin_len
= sizeof(*sin
);
279 sin
->sin_family
= AF_INET
;
280 sin
->sin_addr
.s_addr
= braddr
;
281 } /* else leave broadcast addr unspecified (len=0) */
283 error
= ifioctl(so
, SIOCAIFADDR
, (void *)&iareq
, lwp
);
285 printf("setaddress, error=%d\n", error
);
289 /* give the link some time to get up */
290 tsleep(nfs_boot_setaddress
, PZERO
, "nfsbtd", 3 * hz
);
297 nfs_boot_deladdress(struct ifnet
*ifp
, struct lwp
*lwp
, uint32_t addr
)
301 struct sockaddr_in sin
;
302 struct in_addr ia
= {.s_addr
= addr
};
306 * Get a socket to use for various things in here.
307 * After this, use "goto out" to cleanup and return.
309 error
= socreate(AF_INET
, &so
, SOCK_DGRAM
, 0, lwp
, NULL
);
311 printf("deladdress: socreate, error=%d\n", error
);
315 memset(&ifr
, 0, sizeof(ifr
));
316 memcpy(ifr
.ifr_name
, ifp
->if_xname
, IFNAMSIZ
);
318 sockaddr_in_init(&sin
, &ia
, 0);
319 ifreq_setaddr(SIOCDIFADDR
, &ifr
, sintocsa(&sin
));
321 error
= ifioctl(so
, SIOCDIFADDR
, &ifr
, lwp
);
323 printf("deladdress, error=%d\n", error
);
333 nfs_boot_setrecvtimo(struct socket
*so
)
340 return (so_setsockopt(NULL
, so
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
,
345 nfs_boot_enbroadcast(struct socket
*so
)
350 return (so_setsockopt(NULL
, so
, SOL_SOCKET
, SO_BROADCAST
, &on
,
355 nfs_boot_sobind_ipport(struct socket
*so
, uint16_t port
, struct lwp
*l
)
358 struct sockaddr_in
*sin
;
361 m
= m_getclr(M_WAIT
, MT_SONAME
);
362 sin
= mtod(m
, struct sockaddr_in
*);
363 sin
->sin_len
= m
->m_len
= sizeof(*sin
);
364 sin
->sin_family
= AF_INET
;
365 sin
->sin_addr
.s_addr
= INADDR_ANY
;
366 sin
->sin_port
= htons(port
);
367 error
= sobind(so
, m
, l
);
373 * What is the longest we will wait before re-sending a request?
374 * Note this is also the frequency of "timeout" messages.
375 * The re-send loop counts up linearly to this maximum, so the
376 * first complaint will happen after (1+2+3+4+5)=15 seconds.
378 #define MAX_RESEND_DELAY 5 /* seconds */
379 #define TOTAL_TIMEOUT 30 /* seconds */
382 nfs_boot_sendrecv(struct socket
*so
, struct mbuf
*nam
,
383 int (*sndproc
)(struct mbuf
*, void *, int),
385 int (*rcvproc
)(struct mbuf
*, void *),
386 struct mbuf
**rcv
, struct mbuf
**from_p
,
387 void *context
, struct lwp
*lwp
)
389 int error
, rcvflg
, timo
, secs
, waited
;
390 struct mbuf
*m
, *from
;
393 /* Free at end if not null. */
397 * Send it, repeatedly, until a reply is received,
398 * but delay each re-send by an increasing amount.
399 * If the delay hits the maximum, start complaining.
404 if (waited
>= TOTAL_TIMEOUT
)
407 /* Determine new timeout. */
408 if (timo
< MAX_RESEND_DELAY
)
411 printf("nfs_boot: timeout...\n");
414 error
= (*sndproc
)(snd
, context
, waited
);
419 /* Send request (or re-send). */
420 m
= m_copypacket(snd
, M_WAIT
);
425 error
= (*so
->so_send
)(so
, nam
, NULL
, m
, NULL
, 0, lwp
);
427 printf("nfs_boot: sosend: %d\n", error
);
433 * Wait for up to timo seconds for a reply.
434 * The socket receive timeout was set to 1 second.
447 uio
.uio_resid
= 1 << 16; /* ??? */
449 error
= (*so
->so_receive
)(so
, &from
, &uio
, &m
, NULL
, &rcvflg
);
450 if (error
== EWOULDBLOCK
) {
458 if (!m
|| !(m
->m_flags
& M_PKTHDR
)
459 || (1 << 16) - uio
.uio_resid
!= m
->m_pkthdr
.len
)
460 panic("nfs_boot_sendrecv: return size");
463 if ((*rcvproc
)(m
, context
))
477 if (from
) m_freem(from
);
482 * Install a default route to the passed IP address.
485 nfs_boot_defrt(struct in_addr
*gw_ip
)
487 struct sockaddr dst
, gw
, mask
;
488 struct sockaddr_in
*sin
;
491 /* Destination: (default) */
492 memset((void *)&dst
, 0, sizeof(dst
));
493 dst
.sa_len
= sizeof(dst
);
494 dst
.sa_family
= AF_INET
;
496 memset((void *)&gw
, 0, sizeof(gw
));
497 sin
= (struct sockaddr_in
*)&gw
;
498 sin
->sin_len
= sizeof(*sin
);
499 sin
->sin_family
= AF_INET
;
500 sin
->sin_addr
.s_addr
= gw_ip
->s_addr
;
501 /* Mask: (zero length) */
502 /* XXX - Just pass a null pointer? */
503 memset(&mask
, 0, sizeof(mask
));
505 /* add, dest, gw, mask, flags, 0 */
506 error
= rtrequest(RTM_ADD
, &dst
, &gw
, &mask
,
507 (RTF_UP
| RTF_GATEWAY
| RTF_STATIC
), NULL
);
509 printf("nfs_boot: add route, error=%d\n", error
);
515 nfs_boot_delroute(struct rtentry
*rt
, void *w
)
519 if ((void *)rt
->rt_ifp
!= w
)
522 error
= rtrequest(RTM_DELETE
, rt_getkey(rt
), NULL
, rt_mask(rt
), 0,
525 printf("%s: del route, error=%d\n", __func__
, error
);
531 nfs_boot_flushrt(struct ifnet
*ifp
)
534 rt_walktree(AF_INET
, nfs_boot_delroute
, ifp
);
538 * Get an initial NFS file handle using Sun RPC/mountd.
539 * Separate function because we used to call it twice.
540 * (once for root and once for swap)
545 nfs_boot_getfh(struct nfs_dlmount
*ndm
, struct lwp
*l
)
547 struct nfs_args
*args
;
548 struct sockaddr_in
*sin
;
553 args
= &ndm
->ndm_args
;
555 /* Initialize mount args. */
556 memset((void *) args
, 0, sizeof(*args
));
557 args
->addr
= &ndm
->ndm_saddr
;
558 args
->addrlen
= args
->addr
->sa_len
;
560 args
->sotype
= SOCK_STREAM
;
562 args
->sotype
= SOCK_DGRAM
;
564 args
->fh
= ndm
->ndm_fh
;
565 args
->hostname
= ndm
->ndm_host
;
566 args
->flags
= NFSMNT_NOCONN
| NFSMNT_RESVPORT
;
569 args
->flags
|= NFSMNT_NFSV3
;
571 #ifdef NFS_BOOT_OPTIONS
572 args
->flags
|= NFS_BOOT_OPTIONS
;
574 #ifdef NFS_BOOT_RWSIZE
576 * Reduce rsize,wsize for interfaces that consistently
577 * drop fragments of long UDP messages. (i.e. wd8003).
578 * You can always change these later via remount.
580 args
->flags
|= NFSMNT_WSIZE
| NFSMNT_RSIZE
;
581 args
->wsize
= NFS_BOOT_RWSIZE
;
582 args
->rsize
= NFS_BOOT_RWSIZE
;
586 * Find the pathname part of the "server:pathname"
587 * string left in ndm->ndm_host by nfs_boot_init.
589 pathname
= strchr(ndm
->ndm_host
, ':');
591 printf("nfs_boot: getfh - no pathname\n");
597 * Get file handle using RPC to mountd/mount
599 sin
= (struct sockaddr_in
*)&ndm
->ndm_saddr
;
600 error
= md_mount(sin
, pathname
, args
, l
);
602 printf("nfs_boot: mountd `%s', error=%d\n",
603 ndm
->ndm_host
, error
);
607 /* Set port number for NFS use. */
608 /* XXX: NFS port is always 2049, right? */
610 error
= krpc_portmap(sin
, NFS_PROG
,
611 (args
->flags
& NFSMNT_NFSV3
) ? NFS_VER3
: NFS_VER2
,
612 (args
->sotype
== SOCK_STREAM
) ? IPPROTO_TCP
: IPPROTO_UDP
,
614 if (port
== htons(0))
617 if (args
->sotype
== SOCK_STREAM
) {
618 args
->sotype
= SOCK_DGRAM
;
621 printf("nfs_boot: portmap NFS, error=%d\n", error
);
624 sin
->sin_port
= port
;
631 * Given a server pathname, get an NFS file handle.
632 * Also, sets sin->sin_port to the NFS service port.
634 * mdsin mountd server address
637 md_mount(struct sockaddr_in
*mdsin
, char *path
,
638 struct nfs_args
*argp
, struct lwp
*lwp
)
640 /* The RPC structures */
644 u_int8_t v2fh
[NFSX_V2FH
];
656 mntver
= (argp
->flags
& NFSMNT_NFSV3
) ? 3 : 2;
659 * Get port number for MOUNTD.
661 error
= krpc_portmap(mdsin
, RPCPROG_MNT
, mntver
,
662 IPPROTO_UDP
, &mdsin
->sin_port
, lwp
);
666 /* This mbuf is consumed by krpc_call. */
667 m
= xdr_string_encode(path
, strlen(path
));
671 /* Do RPC to mountd. */
672 error
= krpc_call(mdsin
, RPCPROG_MNT
, mntver
,
673 RPCMNT_MOUNT
, &m
, NULL
, lwp
);
674 if (error
!= EPROGMISMATCH
)
676 /* Try lower version of mountd. */
677 } while (--mntver
>= 1);
679 printf("nfs_boot: mountd error=%d\n", error
);
683 argp
->flags
&= ~NFSMNT_NFSV3
;
685 /* The reply might have only the errno. */
688 /* Have at least errno, so check that. */
689 rdata
= mtod(m
, struct rdata
*);
690 error
= fxdr_unsigned(u_int32_t
, rdata
->errno
);
694 /* Have errno==0, so the fh must be there. */
696 argp
->fhsize
= fxdr_unsigned(u_int32_t
, rdata
->fh
.v3fh
.fhlen
);
697 if (argp
->fhsize
> NFSX_V3FHMAX
)
699 minlen
= 2 * sizeof(u_int32_t
) + argp
->fhsize
;
701 argp
->fhsize
= NFSX_V2FH
;
702 minlen
= sizeof(u_int32_t
) + argp
->fhsize
;
705 if (m
->m_len
< minlen
) {
706 m
= m_pullup(m
, minlen
);
709 rdata
= mtod(m
, struct rdata
*);
713 rdata
->fh
.v3fh
.fh
: rdata
->fh
.v2fh
;
714 memcpy(argp
->fh
, fh
, argp
->fhsize
);