From 432aad0e9818ec0d610601dcdf20588645ee7424 Mon Sep 17 00:00:00 2001 From: Tor Egge Date: Sun, 11 May 1997 18:05:39 +0000 Subject: [PATCH] Bring in some kernel bootp support. This removes the need for netboot to fill in the nfs_diskless structure, at the cost of some kernel bloat. The advantage is that this code works on a wider range of network adapters than netboot. Several new kernel options are documented in LINT. Obtained from: parts of the code comes from NetBSD. --- sys/amd64/amd64/autoconf.c | 9 +- sys/conf/NOTES | 9 +- sys/conf/files | 2 + sys/i386/conf/LINT | 9 +- sys/i386/conf/NOTES | 9 +- sys/i386/i386/autoconf.c | 9 +- sys/kern/init_main.c | 9 +- sys/netinet/ip_input.c | 6 +- sys/nfs/bootp_subr.c | 1319 ++++++++++++++++++++++++++++++++++++ sys/nfs/krpc.h | 30 + sys/nfs/krpc_subr.c | 592 ++++++++++++++++ sys/nfs/nfs_vfsops.c | 57 +- sys/nfs/rpcv2.h | 3 +- sys/nfsclient/bootp_subr.c | 1319 ++++++++++++++++++++++++++++++++++++ sys/nfsclient/krpc.h | 30 + sys/nfsclient/krpc_subr.c | 592 ++++++++++++++++ sys/nfsclient/nfs_vfsops.c | 57 +- 17 files changed, 4023 insertions(+), 38 deletions(-) create mode 100644 sys/nfs/bootp_subr.c create mode 100644 sys/nfs/krpc.h create mode 100644 sys/nfs/krpc_subr.c create mode 100644 sys/nfsclient/bootp_subr.c create mode 100644 sys/nfsclient/krpc.h create mode 100644 sys/nfsclient/krpc_subr.c diff --git a/sys/amd64/amd64/autoconf.c b/sys/amd64/amd64/autoconf.c index d18fb01d89ff..79789f88cc51 100644 --- a/sys/amd64/amd64/autoconf.c +++ b/sys/amd64/amd64/autoconf.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)autoconf.c 7.1 (Berkeley) 5/9/91 - * $Id: autoconf.c,v 1.66 1997/04/26 18:57:34 peter Exp $ + * $Id: autoconf.c,v 1.67 1997/05/04 15:24:19 joerg Exp $ */ /* @@ -287,6 +287,13 @@ configure(dummy) } #endif +#ifdef BOOTP_NFSROOT + if (!mountrootfsname && !nfs_diskless_valid) { + if (bootverbose) + printf("Considering BOOTP NFS root f/s.\n"); + mountrootfsname = "nfs"; + } +#endif /* BOOTP_NFSROOT */ #ifdef NFS if (!mountrootfsname && nfs_diskless_valid) { if (bootverbose) diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 15e19f163ce0..a3c14e551f36 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -2,7 +2,7 @@ # LINT -- config file for checking all the sources, tries to pull in # as much of the source tree as it can. # -# $Id: LINT,v 1.335 1997/05/10 11:16:22 jhay Exp $ +# $Id: LINT,v 1.336 1997/05/10 17:40:53 fsmp Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -1169,6 +1169,13 @@ controller pcic1 at crd? options POWERFAIL_NMI # make it beep instead of panicing +# Kernel BOOTP support + +options BOOTP # Use BOOTP to obtain IP address/hostname +options BOOTP_NFSROOT # NFS mount root filesystem using BOOTP info +options "BOOTP_NFSV3" # Use NFS v3 to NFS mount root +options BOOTP_COMPAT # Workaround for broken bootp daemons. + # # An obsolete option to test kern_opt.c. # diff --git a/sys/conf/files b/sys/conf/files index 863121bf241a..753331940e0b 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -320,6 +320,8 @@ nfs/nfs_subs.c optional nfs nfs/nfs_syscalls.c optional nfs nfs/nfs_vfsops.c optional nfs nfs/nfs_vnops.c optional nfs +nfs/bootp_subr.c optional bootp +nfs/krpc_subr.c optional bootp pccard/pccard.c optional crd pccard/pcic.c optional pcic device-driver pci/aic7870.c optional ahc device-driver \ diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT index 15e19f163ce0..a3c14e551f36 100644 --- a/sys/i386/conf/LINT +++ b/sys/i386/conf/LINT @@ -2,7 +2,7 @@ # LINT -- config file for checking all the sources, tries to pull in # as much of the source tree as it can. # -# $Id: LINT,v 1.335 1997/05/10 11:16:22 jhay Exp $ +# $Id: LINT,v 1.336 1997/05/10 17:40:53 fsmp Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -1169,6 +1169,13 @@ controller pcic1 at crd? options POWERFAIL_NMI # make it beep instead of panicing +# Kernel BOOTP support + +options BOOTP # Use BOOTP to obtain IP address/hostname +options BOOTP_NFSROOT # NFS mount root filesystem using BOOTP info +options "BOOTP_NFSV3" # Use NFS v3 to NFS mount root +options BOOTP_COMPAT # Workaround for broken bootp daemons. + # # An obsolete option to test kern_opt.c. # diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index 15e19f163ce0..a3c14e551f36 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -2,7 +2,7 @@ # LINT -- config file for checking all the sources, tries to pull in # as much of the source tree as it can. # -# $Id: LINT,v 1.335 1997/05/10 11:16:22 jhay Exp $ +# $Id: LINT,v 1.336 1997/05/10 17:40:53 fsmp Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -1169,6 +1169,13 @@ controller pcic1 at crd? options POWERFAIL_NMI # make it beep instead of panicing +# Kernel BOOTP support + +options BOOTP # Use BOOTP to obtain IP address/hostname +options BOOTP_NFSROOT # NFS mount root filesystem using BOOTP info +options "BOOTP_NFSV3" # Use NFS v3 to NFS mount root +options BOOTP_COMPAT # Workaround for broken bootp daemons. + # # An obsolete option to test kern_opt.c. # diff --git a/sys/i386/i386/autoconf.c b/sys/i386/i386/autoconf.c index d18fb01d89ff..79789f88cc51 100644 --- a/sys/i386/i386/autoconf.c +++ b/sys/i386/i386/autoconf.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)autoconf.c 7.1 (Berkeley) 5/9/91 - * $Id: autoconf.c,v 1.66 1997/04/26 18:57:34 peter Exp $ + * $Id: autoconf.c,v 1.67 1997/05/04 15:24:19 joerg Exp $ */ /* @@ -287,6 +287,13 @@ configure(dummy) } #endif +#ifdef BOOTP_NFSROOT + if (!mountrootfsname && !nfs_diskless_valid) { + if (bootverbose) + printf("Considering BOOTP NFS root f/s.\n"); + mountrootfsname = "nfs"; + } +#endif /* BOOTP_NFSROOT */ #ifdef NFS if (!mountrootfsname && nfs_diskless_valid) { if (bootverbose) diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 4630283305d8..7af230ffcd00 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -39,7 +39,7 @@ * SUCH DAMAGE. * * @(#)init_main.c 8.9 (Berkeley) 1/21/94 - * $Id: init_main.c,v 1.60 1997/04/07 07:15:59 peter Exp $ + * $Id: init_main.c,v 1.61 1997/04/26 11:46:11 peter Exp $ */ #include "opt_rlimit.h" @@ -456,10 +456,17 @@ SYSINIT(sched_setup, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, sched_setup, NULL) /* ARGSUSED*/ static void xxx_vfs_mountroot __P((void *fsnamep)); +#ifdef BOOTP +extern void bootpc_init __P((void)); +#endif static void xxx_vfs_mountroot(fsnamep) void *fsnamep; { + /* XXX Add a separate SYSINIT entry */ +#ifdef BOOTP + bootpc_init(); +#endif /* Mount the root file system. */ if (vfs_mountrootfs(*((char **) fsnamep))) panic("cannot mount root"); diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index eb7fb7bebc38..538d1c859503 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 - * $Id: ip_input.c,v 1.1.1.2 1997/04/03 10:39:25 darrenr Exp $ + * $Id: ip_input.c,v 1.61 1997/04/03 10:47:10 darrenr Exp $ * $ANA: ip_input.c,v 1.5 1996/09/18 14:34:59 wollman Exp $ */ @@ -386,6 +386,10 @@ tooshort: if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr) goto ours; +#ifdef BOOTP_COMPAT + if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY) + goto ours; +#endif if (ia->ia_ifp && ia->ia_ifp->if_flags & IFF_BROADCAST) { if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr == ip->ip_dst.s_addr) diff --git a/sys/nfs/bootp_subr.c b/sys/nfs/bootp_subr.c new file mode 100644 index 000000000000..837ca6459a92 --- /dev/null +++ b/sys/nfs/bootp_subr.c @@ -0,0 +1,1319 @@ +/* $Id:$ */ + +/* + * Copyright (c) 1995 Gordon Ross, Adam Glass + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * based on: + * nfs/krpc_subr.c + * $NetBSD: krpc_subr.c,v 1.10 1995/08/08 20:43:43 gwr Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#define MIN_REPLY_HDR 16 /* xid, dir, astat, errno */ + +/* + * What is the longest we will wait before re-sending a request? + * Note this is also the frequency of "RPC timeout" messages. + * The re-send loop count sup linearly to this maximum, so the + * first complaint will happen after (1+2+3+4+5)=15 seconds. + */ +#define MAX_RESEND_DELAY 5 /* seconds */ + +/* Definitions from RFC951 */ +struct bootp_packet { + u_int8_t op; + u_int8_t htype; + u_int8_t hlen; + u_int8_t hops; + u_int32_t xid; + u_int16_t secs; + u_int16_t flags; + struct in_addr ciaddr; + struct in_addr yiaddr; + struct in_addr siaddr; + struct in_addr giaddr; + unsigned char chaddr[16]; + char sname[64]; + char file[128]; + unsigned char vend[256]; +}; + +#define IPPORT_BOOTPC 68 +#define IPPORT_BOOTPS 67 + +extern int nfs_diskless_valid; +extern struct nfsv3_diskless nfsv3_diskless; + +/* mountd RPC */ +static int md_mount __P((struct sockaddr_in *mdsin, char *path, + u_char *fhp, int *fhsizep, struct nfs_args *args,struct proc *procp)); +static int md_lookup_swap __P((struct sockaddr_in *mdsin,char *path, + u_char *fhp, int *fhsizep, + struct nfs_args *args, + struct proc *procp)); + +#ifdef BOOTP_DEBUG +void bootpboot_p_sa(struct sockaddr *sa,struct sockaddr *ma); +void bootpboot_p_ma(struct sockaddr *ma); +void bootpboot_p_rtentry(struct rtentry *rt); +void bootpboot_p_tree(struct radix_node *rn); +void bootpboot_p_rtlist(void); +void bootpboot_p_iflist(void); +#endif + +int bootpc_call(struct bootp_packet *call, + struct bootp_packet *reply, + struct proc *procp); + +int bootpc_fakeup_interface(struct ifreq *ireq,struct socket *so, + struct proc *procp); + +int +bootpc_adjust_interface(struct ifreq *ireq,struct socket *so, + struct sockaddr_in *myaddr, + struct sockaddr_in *netmask, + struct sockaddr_in *gw, + struct proc *procp); + +void bootpc_init(void); + +#ifdef BOOTP_DEBUG +void bootpboot_p_sa(sa,ma) + struct sockaddr *sa; + struct sockaddr *ma; +{ + if (!sa) { + printf("(sockaddr *) "); + return; + } + switch (sa->sa_family) { + case AF_INET: + { + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + printf("inet %x",ntohl(sin->sin_addr.s_addr)); + if (ma) { + struct sockaddr_in *sin = (struct sockaddr_in *) ma; + printf(" mask %x",ntohl(sin->sin_addr.s_addr)); + } + } + break; + case AF_LINK: + { + struct sockaddr_dl *sli = (struct sockaddr_dl *) sa; + int i; + printf("link %.*s ",sli->sdl_nlen,sli->sdl_data); + for (i=0;isdl_alen;i++) { + if (i>0) + printf(":"); + printf("%x",(unsigned char) sli->sdl_data[i+sli->sdl_nlen]); + } + } + break; + default: + printf("af%d",sa->sa_family); + } +} + +void bootpboot_p_ma(ma) + struct sockaddr *ma; +{ + if (!ma) { + printf(""); + return; + } + printf("%x",*(int*)ma); +} + +void bootpboot_p_rtentry(rt) + struct rtentry *rt; +{ + bootpboot_p_sa(rt_key(rt),rt_mask(rt)); + printf(" "); + bootpboot_p_ma(rt->rt_genmask); + printf(" "); + bootpboot_p_sa(rt->rt_gateway,NULL); + printf(" "); + printf("flags %x",(unsigned short) rt->rt_flags); + printf(" %d",rt->rt_rmx.rmx_expire); + printf(" %s%d\n",rt->rt_ifp->if_name,rt->rt_ifp->if_unit); +} +void bootpboot_p_tree(rn) + struct radix_node *rn; +{ + while (rn) { + if (rn->rn_b < 0) { + if (rn->rn_flags & RNF_ROOT) { + } else { + bootpboot_p_rtentry((struct rtentry *) rn); + } + rn = rn->rn_dupedkey; + } else { + bootpboot_p_tree(rn->rn_l); + bootpboot_p_tree(rn->rn_r); + return; + } + + } +} + +void bootpboot_p_rtlist(void) +{ + printf("Routing table:\n"); + bootpboot_p_tree(rt_tables[AF_INET]->rnh_treetop); +} + +void bootpboot_p_iflist(void) +{ + struct ifnet *ifp; + struct ifaddr *ifa; + printf("Interface list:\n"); + for (ifp = TAILQ_FIRST(&ifnet); ifp != 0; ifp = TAILQ_NEXT(ifp,if_link)) + { + for (ifa = TAILQ_FIRST(&ifp->if_addrhead) ;ifa; + ifa=TAILQ_NEXT(ifa,ifa_link)) + if (ifa->ifa_addr->sa_family == AF_INET ) { + printf("%s%d flags %x, addr %x, bcast %x, net %x\n", + ifp->if_name,ifp->if_unit, + (unsigned short) ifp->if_flags, + ntohl(((struct sockaddr_in *) ifa->ifa_addr)->sin_addr.s_addr), + ntohl(((struct sockaddr_in *) ifa->ifa_dstaddr)->sin_addr.s_addr), + ntohl(((struct sockaddr_in *) ifa->ifa_netmask)->sin_addr.s_addr) + ); + } + } +} +#endif + +int +bootpc_call(call,reply,procp) + struct bootp_packet *call; + struct bootp_packet *reply; /* output */ + struct proc *procp; +{ + struct socket *so; + struct sockaddr_in *sin,sa; + struct mbuf *m, *nam; + struct uio auio; + struct iovec aio; + int error, rcvflg, timo, secs, len; + u_int tport; + + /* Free at end if not null. */ + nam = NULL; + + /* + * Create socket and set its recieve timeout. + */ + if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0,procp))) + goto out; + + m = m_get(M_WAIT, MT_SOOPTS); + if (m == NULL) { + error = ENOBUFS; + goto out; + } else { + struct timeval *tv; + tv = mtod(m, struct timeval *); + m->m_len = sizeof(*tv); + tv->tv_sec = 1; + tv->tv_usec = 0; + if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m, procp))) + goto out; + } + + /* + * Enable broadcast. + */ + { + int *on; + m = m_get(M_WAIT, MT_SOOPTS); + if (m == NULL) { + error = ENOBUFS; + goto out; + } + on = mtod(m, int *); + m->m_len = sizeof(*on); + *on = 1; + if ((error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m, procp))) + goto out; + } + + /* + * Bind the local endpoint to a bootp client port. + */ + m = m_getclr(M_WAIT, MT_SONAME); + sin = mtod(m, struct sockaddr_in *); + sin->sin_len = m->m_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + sin->sin_port = htons(IPPORT_BOOTPC); + error = sobind(so, m, procp); + m_freem(m); + if (error) { + printf("bind failed\n"); + goto out; + } + + /* + * Setup socket address for the server. + */ + nam = m_get(M_WAIT, MT_SONAME); + if (nam == NULL) { + error = ENOBUFS; + goto out; + } + sin = mtod(nam, struct sockaddr_in *); + sin-> sin_len = sizeof(*sin); + sin-> sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_BROADCAST; + sin->sin_port = htons(IPPORT_BOOTPS); + + nam->m_len = sizeof(*sin); + + /* + * Send it, repeatedly, until a reply is received, + * but delay each re-send by an increasing amount. + * If the delay hits the maximum, start complaining. + */ + timo = 0; + for (;;) { + /* Send BOOTP request (or re-send). */ + + aio.iov_base = (caddr_t) call; + aio.iov_len = sizeof(*call); + + auio.uio_iov = &aio; + auio.uio_iovcnt = 1; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_rw = UIO_WRITE; + auio.uio_offset = 0; + auio.uio_resid = sizeof(*call); + auio.uio_procp = procp; + + error = sosend(so, nam, &auio, NULL, NULL, 0); + if (error) { + printf("bootpc_call: sosend: %d\n", error); + goto out; + } + + /* Determine new timeout. */ + if (timo < MAX_RESEND_DELAY) + timo++; + else + printf("BOOTP timeout for server 0x%x\n", + ntohl(sin->sin_addr.s_addr)); + + /* + * Wait for up to timo seconds for a reply. + * The socket receive timeout was set to 1 second. + */ + secs = timo; + while (secs > 0) { + aio.iov_base = (caddr_t) reply; + aio.iov_len = sizeof(*reply); + + auio.uio_iov = &aio; + auio.uio_iovcnt = 1; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_rw = UIO_READ; + auio.uio_offset = 0; + auio.uio_resid = sizeof(*reply); + auio.uio_procp = procp; + + rcvflg = 0; + error = soreceive(so, NULL, &auio, NULL, NULL, &rcvflg); + if (error == EWOULDBLOCK) { + secs--; + call->secs=htons(ntohs(call->secs)+1); + continue; + } + if (error) + goto out; + len = sizeof(*reply) - auio.uio_resid; + + /* Does the reply contain at least a header? */ + if (len < MIN_REPLY_HDR) + continue; + + /* Is it the right reply? */ + if (reply->op != 2) + continue; + + if (reply->xid != call->xid) + continue; + + if (reply->hlen != call->hlen) + continue; + + if (bcmp(reply->chaddr,call->chaddr,call->hlen)) + continue; + + goto gotreply; /* break two levels */ + + } /* while secs */ + } /* forever send/receive */ + + error = ETIMEDOUT; + goto out; + + gotreply: + out: + if (nam) m_freem(nam); + soclose(so); + return error; +} + +int +bootpc_fakeup_interface(struct ifreq *ireq,struct socket *so, + struct proc *procp) +{ + struct sockaddr_in *sin; + int error; + struct sockaddr_in dst; + struct sockaddr_in gw; + struct sockaddr_in mask; + + /* + * Bring up the interface. + * + * Get the old interface flags and or IFF_UP into them; if + * IFF_UP set blindly, interface selection can be clobbered. + */ + error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)ireq, procp); + if (error) + panic("bootpc_fakeup_interface: GIFFLAGS, error=%d", error); + ireq->ifr_flags |= IFF_UP; + error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)ireq, procp); + if (error) + panic("bootpc_fakeup_interface: SIFFLAGS, error=%d", error); + + /* + * Do enough of ifconfig(8) so that the chosen interface + * can talk to the servers. (just set the address) + */ + + /* addr is 0.0.0.0 */ + + sin = (struct sockaddr_in *)&ireq->ifr_addr; + bzero((caddr_t)sin, sizeof(*sin)); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + error = ifioctl(so, SIOCSIFADDR, (caddr_t)ireq, procp); + if (error) + panic("bootpc_fakeup_interface: set if addr, error=%d", error); + + /* netmask is 0.0.0.0 */ + + sin = (struct sockaddr_in *)&ireq->ifr_addr; + bzero((caddr_t)sin, sizeof(*sin)); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)ireq, procp); + if (error) + panic("bootpc_fakeup_interface: set if net addr, error=%d", error); + + /* Broadcast is 255.255.255.255 */ + + sin = (struct sockaddr_in *)&ireq->ifr_addr; + bzero((caddr_t)sin, sizeof(*sin)); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_BROADCAST; + error = ifioctl(so, SIOCSIFBRDADDR, (caddr_t)ireq, procp); + if (error) + panic("bootpc_fakeup_interface: set if broadcast addr, error=%d", error); + + /* Add default route to 0.0.0.0 so we can send data */ + + bzero((caddr_t) &dst, sizeof(dst)); + dst.sin_len=sizeof(dst); + dst.sin_family=AF_INET; + dst.sin_addr.s_addr = htonl(0); + + bzero((caddr_t) &gw, sizeof(gw)); + gw.sin_len=sizeof(gw); + gw.sin_family=AF_INET; + gw.sin_addr.s_addr = htonl(0x0); + + bzero((caddr_t) &mask, sizeof(mask)); + mask.sin_len=sizeof(mask); + mask.sin_family=AF_INET; + mask.sin_addr.s_addr = htonl(0); + + error = rtrequest(RTM_ADD, + (struct sockaddr *) &dst, + (struct sockaddr *) &gw, + (struct sockaddr *) &mask, + RTF_UP | RTF_STATIC + , NULL); + if (error) + printf("bootpc_fakeup_interface: add default route, error=%d\n", error); + return error; +} + +int +bootpc_adjust_interface(struct ifreq *ireq,struct socket *so, + struct sockaddr_in *myaddr, + struct sockaddr_in *netmask, + struct sockaddr_in *gw, + struct proc *procp) +{ + int error; + struct sockaddr_in oldgw; + struct sockaddr_in olddst; + struct sockaddr_in oldmask; + struct sockaddr_in *sin; + + /* Remove old default route to 0.0.0.0 */ + + bzero((caddr_t) &olddst, sizeof(olddst)); + olddst.sin_len=sizeof(olddst); + olddst.sin_family=AF_INET; + olddst.sin_addr.s_addr = INADDR_ANY; + + bzero((caddr_t) &oldgw, sizeof(oldgw)); + oldgw.sin_len=sizeof(oldgw); + oldgw.sin_family=AF_INET; + oldgw.sin_addr.s_addr = INADDR_ANY; + + bzero((caddr_t) &oldmask, sizeof(oldmask)); + oldmask.sin_len=sizeof(oldmask); + oldmask.sin_family=AF_INET; + oldmask.sin_addr.s_addr = INADDR_ANY; + + error = rtrequest(RTM_DELETE, + (struct sockaddr *) &olddst, + (struct sockaddr *) &oldgw, + (struct sockaddr *) &oldmask, + (RTF_UP | RTF_STATIC), NULL); + if (error) { + printf("nfs_boot: del default route, error=%d\n", error); + return error; + } + +#if 0 + olddst.sin_addr.s_addr = INADDR_BROADCAST; + + error = rtrequest(RTM_DELETE, + (struct sockaddr *) &olddst, + (struct sockaddr *) &oldgw, + (struct sockaddr *) NULL, + (RTF_UP | RTF_HOST | RTF_STATIC), NULL); + if (error) { + printf("nfs_boot: del broadcast route, error=%d\n", error); + } +#endif + + /* + * Do enough of ifconfig(8) so that the chosen interface + * can talk to the servers. (just set the address) + */ + bcopy(netmask,&ireq->ifr_addr,sizeof(*netmask)); + error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)ireq, procp); + if (error) + panic("nfs_boot: set if netmask, error=%d", error); + + /* Broadcast is with host part of IP address all 1's */ + + sin = (struct sockaddr_in *)&ireq->ifr_addr; + bzero((caddr_t)sin, sizeof(*sin)); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = myaddr->sin_addr.s_addr | ~ netmask->sin_addr.s_addr; + error = ifioctl(so, SIOCSIFBRDADDR, (caddr_t)ireq, procp); + if (error) + panic("bootpc_call: set if broadcast addr, error=%d", error); + + bcopy(myaddr,&ireq->ifr_addr,sizeof(*myaddr)); + error = ifioctl(so, SIOCSIFADDR, (caddr_t)ireq, procp); + if (error) + panic("nfs_boot: set if addr, error=%d", error); + + /* Add new default route */ + + error = rtrequest(RTM_ADD, + (struct sockaddr *) &olddst, + (struct sockaddr *) gw, + (struct sockaddr *) &oldmask, + (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL); + if (error) { + printf("nfs_boot: add net route, error=%d\n", error); + return error; + } + +#if 0 + /* Remove default gateway arp entry. This is a kludge, but + somehow the arp entry is added without an arp request + being sent, causing outgoing packets to be dropped onto the floor */ + + error = rtrequest(RTM_DELETE, + (struct sockaddr *) gw, + (struct sockaddr *) NULL, + (struct sockaddr *) NULL, + (RTF_UP | RTF_HOST | RTF_STATIC), NULL); + if (error) { + printf("nfs_boot: del default gateway linklevel route, error=%d\n", error); + } +#endif + + return 0; +} + +void bootp_expand_path(char *str,size_t len,char *hostname, + struct in_addr addr); +void bootp_expand_path(char *str,size_t len,char *hostname, + struct in_addr addr) +{ + char tmpbuf[128]; + char tmpaddr[20]; + + char *p,*q,*savep; + q = tmpbuf; + + savep = NULL; + sprintf(tmpaddr,"%d.%d.%d.%d", + ((unsigned char *) &addr)[0], + ((unsigned char *) &addr)[1], + ((unsigned char *) &addr)[2], + ((unsigned char *) &addr)[3]); + + for (p=str;*p;) { + switch(*p) { + case '%': + if (!savep) { + switch(*++p) { + case 'H': + savep = ++p; + p= hostname; + break; + case 'I': + savep = ++p; + p = tmpaddr; + break; + default: + goto arnej; + } + break; + } + default: + arnej: + if (q+1>=tmpbuf+sizeof(tmpbuf)) + panic("bootp_expand_path: Cannot expand %s\n",str); + else + *q++ = *p++; + if (!*p && savep) { + p = savep; + savep = NULL; + } + } + } + *q++ = 0; + if (q-tmpbuf>len) { + panic("bootp_expand_path: Too long expansion: %s\n",tmpbuf); + } + strcpy(str,tmpbuf); +} + +void +bootpc_init(void) +{ + struct bootp_packet call; + struct bootp_packet reply; + static u_int32_t xid = ~0xFF; + + struct ifreq ireq; + struct ifnet *ifp; + struct socket *so; + int error; + int code,len; + int i,j; + char rootpath[65]; + char swappath[65]; + + struct sockaddr_in myaddr; + struct sockaddr_in netmask; + struct sockaddr_in gw; + struct sockaddr_in server; + int gotgw=0; + int gotnetmask=0; + int gotserver=0; + int gotrootpath=0; + int gotswappath=0; + +#define EALEN 6 + unsigned char ea[EALEN]; + struct ifaddr *ifa; + struct sockaddr_dl *sdl = NULL; + char *delim; + + struct nfsv3_diskless *nd = &nfsv3_diskless; + struct proc *procp = curproc; + + /* + * If already filled in, don't touch it here + */ + if (nfs_diskless_valid) + return; + + /* + * Bump time if 0. + */ + if (!time.tv_sec) + time.tv_sec++; + + /* + * Find a network interface. + */ + for (ifp = TAILQ_FIRST(&ifnet); ifp != 0; ifp = TAILQ_NEXT(ifp,if_link)) + if ((ifp->if_flags & + (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0) + break; + if (ifp == NULL) + panic("bootpc_init: no suitable interface"); + bzero(&ireq,sizeof(ireq)); + sprintf(ireq.ifr_name, "%s%d", ifp->if_name,ifp->if_unit); + strcpy(nd->myif.ifra_name,ireq.ifr_name); + printf("bootpc_init: using network interface '%s'\n", + ireq.ifr_name); + + if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0,procp)) != 0) + panic("nfs_boot: socreate, error=%d", error); + + bootpc_fakeup_interface(&ireq,so,procp); + + printf("Bootpc testing starting\n"); + + /* Get HW address */ + + for (ifa = TAILQ_FIRST(&ifp->if_addrhead) ;ifa; + ifa=TAILQ_NEXT(ifa,ifa_link)) + if (ifa->ifa_addr->sa_family == AF_LINK && + (sdl = ((struct sockaddr_dl *) ifa->ifa_addr)) && + sdl->sdl_type == IFT_ETHER) + break; + + if (!sdl) + panic("bootpc: Unable to find HW address"); + if (sdl->sdl_alen != EALEN ) + panic("bootpc: HW address len is %d, expected value is %d", + sdl->sdl_alen,EALEN); +#if 1 + printf("bootpc hw address is "); + delim=""; + for (j=0;jsdl_alen;j++) { + printf("%s%x",delim,((unsigned char *)LLADDR(sdl))[j]); + delim=":"; + } + printf("\n"); +#endif + +#if 0 + bootpboot_p_iflist(); + bootpboot_p_rtlist(); +#endif + + bzero((caddr_t) &call, sizeof(call)); + + /* bootpc part */ + call.op = 1; /* BOOTREQUEST */ + call.htype= 1; /* 10mb ethernet */ + call.hlen=sdl->sdl_alen; /* Hardware address length */ + call.hops=0; + xid++; + call.xid = txdr_unsigned(xid); + bcopy(LLADDR(sdl),&call.chaddr,sdl->sdl_alen); + + call.vend[0]=99; + call.vend[1]=130; + call.vend[2]=83; + call.vend[3]=99; + call.vend[4]=255; + + call.secs = 0; + call.flags = htons(0x8000); /* We need an broadcast answer */ + + error = bootpc_call(&call,&reply,procp); + + if (error) { +#ifdef BOOTP_NFSROOT + panic("BOOTP call failed"); +#endif + return; + } + + bzero(&myaddr,sizeof(myaddr)); + bzero(&netmask,sizeof(netmask)); + bzero(&gw,sizeof(gw)); + bzero(&server,sizeof(server)); + + myaddr.sin_len = sizeof(myaddr); + myaddr.sin_family = AF_INET; + + netmask.sin_len = sizeof(netmask); + netmask.sin_family = AF_INET; + + gw.sin_len = sizeof(gw); + gw.sin_family= AF_INET; + + server.sin_len = sizeof(gw); + server.sin_family= AF_INET; + + printf("My new ip address is %x\n",htonl(reply.yiaddr.s_addr)); + + myaddr.sin_addr = reply.yiaddr; + + printf("Server ip address is %x\n",htonl(reply.siaddr.s_addr)); + printf("Gateway ip address is %x\n",htonl(reply.giaddr.s_addr)); + + gw.sin_addr = reply.giaddr; + + if (reply.sname[0]) + printf("Server name is %s\n",reply.sname); + if (reply.file[0]) + printf("boot file is %s\n",reply.file); + if (reply.vend[0]==99 && reply.vend[1]==130 && + reply.vend[2]==83 && reply.vend[3]==99) { + j=4; + while (j=sizeof(reply.vend)) { + printf("Truncated field"); + break; + } + switch (code) { + case 1: + if (len!=4) + panic("bootpc: subnet mask len is %d",len); + bcopy(&reply.vend[j],&netmask.sin_addr,4); + gotnetmask=1; + printf("Subnet mask is %d.%d.%d.%d\n", + reply.vend[j], + reply.vend[j+1], + reply.vend[j+2], + reply.vend[j+3]); + break; + case 2: + /* Time offset */ + break; + case 3: + /* Routers */ + if (len % 4) + panic("bootpc: Router Len is %d",len); + if (len > 0) { + bcopy(&reply.vend[j],&gw.sin_addr,4); + gotgw=1; + } + for (i=0;i=sizeof(rootpath)) + panic("bootpc: rootpath >=%d bytes",sizeof(rootpath)); + strncpy(rootpath,&reply.vend[j],len); + rootpath[len]=0; + gotrootpath=1; + printf("Rootpath is %s\n",rootpath); + break; + case 12: + if (len>=MAXHOSTNAMELEN) + panic("bootpc: hostname >=%d bytes",MAXHOSTNAMELEN); + strncpy(nd->my_hostnam,&reply.vend[j],len); + nd->my_hostnam[len]=0; + strncpy(hostname,&reply.vend[j],len); + hostname[len]=0; + printf("Hostname is %s\n",hostname); + break; + case 128: + if (len>=sizeof(swappath)) + panic("bootpc: swappath >=%d bytes",sizeof(swappath)); + strncpy(swappath,&reply.vend[j],len); + swappath[len]=0; + gotswappath=1; + printf("Swappath is %s\n",swappath); + break; + case 129: + { + int swaplen; + if (len!=4) + panic("bootpc: Expected 4 bytes for swaplen, not %d bytes",len); + bcopy(&reply.vend[j],&swaplen,4); + nd->swap_nblks = ntohl(swaplen); + printf("bootpc: Swap size is %d KB\n",nd->swap_nblks); + } + break; + default: + printf("Ignoring field type %d\n",code); + } + j+=len; + } + } + + if (gotrootpath) { + bootp_expand_path(rootpath,sizeof(rootpath), + hostname, + myaddr.sin_addr); + printf("Rootpath is expanded to %s\n",rootpath); + if (gotswappath) { + bootp_expand_path(swappath,sizeof(swappath), + hostname, + myaddr.sin_addr); + printf("Swappath is expanded to %s\n",swappath); + } + else + nd->swap_nblks = 0; + } else { +#ifdef BOOTP_NFSROOT + panic("bootpc: No root path offered"); +#endif + } + + if (!gotserver) { + server.sin_addr = reply.siaddr ; + } + + if (!gotnetmask) { + if (IN_CLASSA(myaddr.sin_addr.s_addr)) + netmask.sin_addr.s_addr = IN_CLASSA_NET; + else if (IN_CLASSB(myaddr.sin_addr.s_addr)) + netmask.sin_addr.s_addr = IN_CLASSB_NET; + else + netmask.sin_addr.s_addr = IN_CLASSC_NET; + } + if (!gotgw) { + /* Use proxyarp */ + gw.sin_addr.s_addr = myaddr.sin_addr.s_addr; + } + +#if 0 + bootpboot_p_iflist(); + bootpboot_p_rtlist(); +#endif + error = bootpc_adjust_interface(&ireq,so, + &myaddr,&netmask,&gw,procp); + + soclose(so); + +#if 0 + bootpboot_p_iflist(); + bootpboot_p_rtlist(); +#endif + + nd->root_args.version = NFS_ARGSVERSION; + nd->root_args.rsize = 8192; + nd->root_args.wsize = 8192; + nd->root_args.sotype = SOCK_DGRAM; + nd->root_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE | + NFSMNT_RESVPORT | NFSMNT_NOCONN); + + nd->swap_args.version = NFS_ARGSVERSION; + nd->swap_args.rsize = 8192; + nd->swap_args.wsize = 8192; + nd->swap_args.sotype = SOCK_DGRAM; + nd->swap_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE | + NFSMNT_RESVPORT | NFSMNT_NOCONN); + + if (gotrootpath) { + if (server.sin_addr.s_addr != reply.siaddr.s_addr ) { + sprintf(nd->root_hostnam,"%d.%d.%d.%d", + ((unsigned char *) &server.sin_addr)[0], + ((unsigned char *) &server.sin_addr)[1], + ((unsigned char *) &server.sin_addr)[2], + ((unsigned char *) &server.sin_addr)[3]); + } else + strcpy(nd->root_hostnam,reply.sname); + bcopy(&server, &nd->root_saddr,sizeof(server)); + + error = md_mount(&nd->root_saddr, rootpath, nd->root_fh, &nd->root_fhsize, + &nd->root_args,procp); + if (error) + panic("nfs_boot: mountd root, error=%d", error); + + if (gotswappath) { + + char *p = swappath; + + while (*p) + p++; + while (p>=swappath && *p != '/') + p--; + + strcpy(nd->swap_hostnam, nd->root_hostnam); + + bcopy(&server, &nd->swap_saddr,sizeof(server)); + + if (p>swappath) + *p = '\0'; + error = md_mount(&nd->swap_saddr, + (p>swappath) ? swappath:"/", + nd->swap_fh, &nd->swap_fhsize,&nd->swap_args,procp); + if (error) + panic("nfs_boot: mountd swap, error=%d", error); + if (p>swappath) + *p = '/'; + + if (p>=swappath) + error = md_lookup_swap(&nd->swap_saddr,p+1,nd->swap_fh, + &nd->swap_fhsize, &nd->swap_args,procp); + if (error) + panic("nfs_boot: lookup swap, error=%d", error); + } + nfs_diskless_valid = 3; + } + + + bcopy(&myaddr,&nd->myif.ifra_addr,sizeof(myaddr)); + bcopy(&myaddr,&nd->myif.ifra_broadaddr,sizeof(myaddr)); + ((struct sockaddr_in *) &nd->myif.ifra_broadaddr)->sin_addr.s_addr = + myaddr.sin_addr.s_addr | ~ netmask.sin_addr.s_addr; + bcopy(&netmask,&nd->myif.ifra_mask,sizeof(netmask)); + +#if 0 + bootpboot_p_iflist(); + bootpboot_p_rtlist(); +#endif + return; +} + +/* + * RPC: mountd/mount + * Given a server pathname, get an NFS file handle. + * Also, sets sin->sin_port to the NFS service port. + */ +static int +md_mount(mdsin, path, fhp, fhsizep, args, procp) + struct sockaddr_in *mdsin; /* mountd server address */ + char *path; + u_char *fhp; + int *fhsizep; + struct nfs_args *args; + struct proc *procp; +{ + struct mbuf *m; + int error; + int authunixok; + int authcount; + int authver; + +#ifdef BOOTP_NFSV3 + /* First try NFS v3 */ + /* Get port number for MOUNTD. */ + error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER3, + &mdsin->sin_port, procp); + if (!error) { + m = xdr_string_encode(path, strlen(path)); + + /* Do RPC to mountd. */ + error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER3, + RPCMNT_MOUNT, &m, NULL, curproc); + } + if (!error) { + args->flags |= NFSMNT_NFSV3; + } else { +#endif + /* Fallback to NFS v2 */ + + /* Get port number for MOUNTD. */ + error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1, + &mdsin->sin_port, procp); + if (error) return error; + + m = xdr_string_encode(path, strlen(path)); + + /* Do RPC to mountd. */ + error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1, + RPCMNT_MOUNT, &m, NULL, curproc); + if (error) + return error; /* message already freed */ + +#ifdef BOOTP_NFSV3 + } +#endif + + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + error = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + if (error) + goto bad; + m_adj(m,sizeof(u_int32_t)); + + if (args->flags & NFSMNT_NFSV3) { + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + *fhsizep = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + if (*fhsizep > NFSX_V3FHMAX || *fhsizep <= 0 ) + goto bad; + m_adj(m,sizeof(u_int32_t)); + } else + *fhsizep = NFSX_V2FH; + + if (m->m_len < (((*fhsizep)+3)&~3)) { + m = m_pullup(m,(((*fhsizep)+3)&~3)); + if (m == NULL) + goto bad; + } + bcopy(mtod(m,u_char *), fhp, *fhsizep); + m_adj(m,(((*fhsizep)+3)&~3)); + + if (args->flags & NFSMNT_NFSV3) { + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + authcount = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + authunixok = 0; + m_adj(m,sizeof(u_int32_t)); + if (authcount<0 || authcount>100) + goto bad; + while (authcount>0) { + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + authver = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + if (authver == RPCAUTH_UNIX) + authunixok = 1; + authcount--; + m_adj(m,sizeof(u_int32_t)); + } + if (!authunixok) + goto bad; + } + + /* Set port number for NFS use. */ + error = krpc_portmap(mdsin, NFS_PROG, + (args->flags & NFSMNT_NFSV3)?NFS_VER3:NFS_VER2, + &mdsin->sin_port, procp); + + goto out; + +bad: + error = EBADRPC; + +out: + m_freem(m); + return error; +} + +static int md_lookup_swap(mdsin, path, fhp, fhsizep, args, procp) + struct sockaddr_in *mdsin; /* mountd server address */ + char *path; + u_char *fhp; + int *fhsizep; + struct nfs_args *args; + struct proc *procp; +{ + struct mbuf *m; + int error; + int size = -1; + int attribs_present; + int status; + + m = m_get(M_WAIT,MT_DATA); + if (!m) + return ENOBUFS; + + if (args->flags & NFSMNT_NFSV3) { + *mtod(m,u_int32_t *) = txdr_unsigned(*fhsizep); + bcopy(fhp,mtod(m,u_char *)+sizeof(u_int32_t),*fhsizep); + m->m_len = *fhsizep + sizeof(u_int32_t); + } else { + bcopy(fhp,mtod(m,u_char *),NFSX_V2FH); + m->m_len = NFSX_V2FH; + } + + m->m_next = xdr_string_encode(path, strlen(path)); + if (!m->m_next) { + error = ENOBUFS; + goto out; + } + + /* Do RPC to nfsd. */ + if (args->flags & NFSMNT_NFSV3) + error = krpc_call(mdsin, NFS_PROG, NFS_VER3, + NFSPROC_LOOKUP, &m, NULL, procp); + else + error = krpc_call(mdsin, NFS_PROG, NFS_VER2, + NFSV2PROC_LOOKUP, &m, NULL, procp); + if (error) + return error; /* message already freed */ + + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + status = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + m_adj(m,sizeof(u_int32_t)); + if (status) { + error = ENOENT; + goto out; + } + + if (args->flags & NFSMNT_NFSV3) { + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + *fhsizep = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + if (*fhsizep > NFSX_V3FHMAX || *fhsizep <= 0 ) + goto bad; + m_adj(m,sizeof(u_int32_t)); + } else + *fhsizep = NFSX_V2FH; + + if (m->m_len < (((*fhsizep)+3)&~3)) { + m = m_pullup(m,(((*fhsizep)+3)&~3)); + if (m == NULL) + goto bad; + } + bcopy(mtod(m,u_char *), fhp, *fhsizep); + m_adj(m,(((*fhsizep)+3)&~3)); + + if (args->flags & NFSMNT_NFSV3) { + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + attribs_present = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + m_adj(m,sizeof(u_int32_t)); + if (attribs_present) { + if (m->m_len < sizeof(u_int32_t)*21) { + m = m_pullup(m, sizeof(u_int32_t)*21); + if (m == NULL) + goto bad; + } + size = fxdr_unsigned(u_int32_t, mtod(m,u_int32_t *)[6]); + } + } else { + if (m->m_len < sizeof(u_int32_t)*17) { + m = m_pullup(m, sizeof(u_int32_t)*17); + if (m == NULL) + goto bad; + } + size = fxdr_unsigned(u_int32_t, mtod(m,u_int32_t *)[5]); + } + + if (!nfsv3_diskless.swap_nblks && size!= -1) { + nfsv3_diskless.swap_nblks = size/1024; + printf("md_lookup_swap: Swap size is %d KB\n", + nfsv3_diskless.swap_nblks); + } + + goto out; + +bad: + error = EBADRPC; + +out: + m_freem(m); + return error; +} diff --git a/sys/nfs/krpc.h b/sys/nfs/krpc.h new file mode 100644 index 000000000000..1da22f13b603 --- /dev/null +++ b/sys/nfs/krpc.h @@ -0,0 +1,30 @@ +/* $NetBSD: krpc.h,v 1.4 1995/12/19 23:07:11 cgd Exp $ */ +/* $Id:$ */ + +#include + +int krpc_call __P((struct sockaddr_in *sin, + u_int prog, u_int vers, u_int func, + struct mbuf **data, struct mbuf **from, struct proc *procp)); + +int krpc_portmap __P((struct sockaddr_in *sin, + u_int prog, u_int vers, u_int16_t *portp,struct proc *procp)); + +struct mbuf *xdr_string_encode __P((char *str, int len)); +struct mbuf *xdr_string_decode __P((struct mbuf *m, char *str, int *len_p)); +struct mbuf *xdr_inaddr_encode __P((struct in_addr *ia)); +struct mbuf *xdr_inaddr_decode __P((struct mbuf *m, struct in_addr *ia)); + + +/* + * RPC definitions for the portmapper + */ +#define PMAPPORT 111 +#define PMAPPROG 100000 +#define PMAPVERS 2 +#define PMAPPROC_NULL 0 +#define PMAPPROC_SET 1 +#define PMAPPROC_UNSET 2 +#define PMAPPROC_GETPORT 3 +#define PMAPPROC_DUMP 4 +#define PMAPPROC_CALLIT 5 diff --git a/sys/nfs/krpc_subr.c b/sys/nfs/krpc_subr.c new file mode 100644 index 000000000000..bb31e4b304ac --- /dev/null +++ b/sys/nfs/krpc_subr.c @@ -0,0 +1,592 @@ +/* $NetBSD: krpc_subr.c,v 1.12.4.1 1996/06/07 00:52:26 cgd Exp $ */ +/* $Id:$ */ + +/* + * Copyright (c) 1995 Gordon Ross, Adam Glass + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * partially based on: + * libnetboot/rpc.c + * @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp (LBL) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +/* + * Kernel support for Sun RPC + * + * Used currently for bootstrapping in nfs diskless configurations. + */ + +/* + * Generic RPC headers + */ + +struct auth_info { + u_int32_t authtype; /* auth type */ + u_int32_t authlen; /* auth length */ +}; + +struct auth_unix { + int32_t ua_time; + int32_t ua_hostname; /* null */ + int32_t ua_uid; + int32_t ua_gid; + int32_t ua_gidlist; /* null */ +}; + +struct rpc_call { + u_int32_t rp_xid; /* request transaction id */ + int32_t rp_direction; /* call direction (0) */ + u_int32_t rp_rpcvers; /* rpc version (2) */ + u_int32_t rp_prog; /* program */ + u_int32_t rp_vers; /* version */ + u_int32_t rp_proc; /* procedure */ + struct auth_info rpc_auth; + struct auth_unix rpc_unix; + struct auth_info rpc_verf; +}; + +struct rpc_reply { + u_int32_t rp_xid; /* request transaction id */ + int32_t rp_direction; /* call direction (1) */ + int32_t rp_astatus; /* accept status (0: accepted) */ + union { + u_int32_t rpu_errno; + struct { + struct auth_info rok_auth; + u_int32_t rok_status; + } rpu_rok; + } rp_u; +}; +#define rp_errno rp_u.rpu_errno +#define rp_auth rp_u.rpu_rok.rok_auth +#define rp_status rp_u.rpu_rok.rok_status + +#define MIN_REPLY_HDR 16 /* xid, dir, astat, errno */ + +/* + * What is the longest we will wait before re-sending a request? + * Note this is also the frequency of "RPC timeout" messages. + * The re-send loop count sup linearly to this maximum, so the + * first complaint will happen after (1+2+3+4+5)=15 seconds. + */ +#define MAX_RESEND_DELAY 5 /* seconds */ + +/* + * Call portmap to lookup a port number for a particular rpc program + * Returns non-zero error on failure. + */ +int +krpc_portmap(sin, prog, vers, portp, procp) + struct sockaddr_in *sin; /* server address */ + u_int prog, vers; /* host order */ + u_int16_t *portp; /* network order */ + struct proc *procp; +{ + struct sdata { + u_int32_t prog; /* call program */ + u_int32_t vers; /* call version */ + u_int32_t proto; /* call protocol */ + u_int32_t port; /* call port (unused) */ + } *sdata; + struct rdata { + u_int16_t pad; + u_int16_t port; + } *rdata; + struct mbuf *m; + int error; + + /* The portmapper port is fixed. */ + if (prog == PMAPPROG) { + *portp = htons(PMAPPORT); + return 0; + } + + m = m_get(M_WAIT, MT_DATA); + if (m == NULL) + return ENOBUFS; + sdata = mtod(m, struct sdata *); + m->m_len = sizeof(*sdata); + + /* Do the RPC to get it. */ + sdata->prog = txdr_unsigned(prog); + sdata->vers = txdr_unsigned(vers); + sdata->proto = txdr_unsigned(IPPROTO_UDP); + sdata->port = 0; + + sin->sin_port = htons(PMAPPORT); + error = krpc_call(sin, PMAPPROG, PMAPVERS, + PMAPPROC_GETPORT, &m, NULL, procp); + if (error) + return error; + + if (m->m_len < sizeof(*rdata)) { + m = m_pullup(m, sizeof(*rdata)); + if (m == NULL) + return ENOBUFS; + } + rdata = mtod(m, struct rdata *); + *portp = rdata->port; + + m_freem(m); + return 0; +} + +/* + * Do a remote procedure call (RPC) and wait for its reply. + * If from_p is non-null, then we are doing broadcast, and + * the address from whence the response came is saved there. + */ +int +krpc_call(sa, prog, vers, func, data, from_p, procp) + struct sockaddr_in *sa; + u_int prog, vers, func; + struct mbuf **data; /* input/output */ + struct mbuf **from_p; /* output */ + struct proc *procp; +{ + struct socket *so; + struct sockaddr_in *sin; + struct mbuf *m, *nam, *mhead, *from; + struct rpc_call *call; + struct rpc_reply *reply; + struct uio auio; + int error, rcvflg, timo, secs, len; + static u_int32_t xid = ~0xFF; + u_int16_t tport; + + /* + * Validate address family. + * Sorry, this is INET specific... + */ + if (sa->sin_family != AF_INET) + return (EAFNOSUPPORT); + + /* Free at end if not null. */ + nam = mhead = NULL; + from = NULL; + + /* + * Create socket and set its recieve timeout. + */ + if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0, procp))) + goto out; + + m = m_get(M_WAIT, MT_SOOPTS); + if (m == NULL) { + error = ENOBUFS; + goto out; + } else { + struct timeval *tv; + tv = mtod(m, struct timeval *); + m->m_len = sizeof(*tv); + tv->tv_sec = 1; + tv->tv_usec = 0; + if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m, procp))) + goto out; + } + + /* + * Enable broadcast if necessary. + */ + if (from_p) { + int32_t *on; + m = m_get(M_WAIT, MT_SOOPTS); + if (m == NULL) { + error = ENOBUFS; + goto out; + } + on = mtod(m, int32_t *); + m->m_len = sizeof(*on); + *on = 1; + if ((error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m, procp))) + goto out; + } + + /* + * Bind the local endpoint to a reserved port, + * because some NFS servers refuse requests from + * non-reserved (non-privileged) ports. + */ + m = m_getclr(M_WAIT, MT_SONAME); + sin = mtod(m, struct sockaddr_in *); + sin->sin_len = m->m_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + tport = IPPORT_RESERVED; + do { + tport--; + sin->sin_port = htons(tport); + error = sobind(so, m, procp); + } while (error == EADDRINUSE && + tport > IPPORT_RESERVED / 2); + m_freem(m); + if (error) { + printf("bind failed\n"); + goto out; + } + + /* + * Setup socket address for the server. + */ + nam = m_get(M_WAIT, MT_SONAME); + if (nam == NULL) { + error = ENOBUFS; + goto out; + } + sin = mtod(nam, struct sockaddr_in *); + bcopy((caddr_t)sa, (caddr_t)sin, + (nam->m_len = sa->sin_len)); + + /* + * Prepend RPC message header. + */ + mhead = m_gethdr(M_WAIT, MT_DATA); + mhead->m_next = *data; + call = mtod(mhead, struct rpc_call *); + mhead->m_len = sizeof(*call); + bzero((caddr_t)call, sizeof(*call)); + /* rpc_call part */ + xid++; + call->rp_xid = txdr_unsigned(xid); + /* call->rp_direction = 0; */ + call->rp_rpcvers = txdr_unsigned(2); + call->rp_prog = txdr_unsigned(prog); + call->rp_vers = txdr_unsigned(vers); + call->rp_proc = txdr_unsigned(func); + /* rpc_auth part (auth_unix as root) */ + call->rpc_auth.authtype = txdr_unsigned(RPCAUTH_UNIX); + call->rpc_auth.authlen = txdr_unsigned(sizeof(struct auth_unix)); + /* rpc_verf part (auth_null) */ + call->rpc_verf.authtype = 0; + call->rpc_verf.authlen = 0; + + /* + * Setup packet header + */ + len = 0; + m = mhead; + while (m) { + len += m->m_len; + m = m->m_next; + } + mhead->m_pkthdr.len = len; + mhead->m_pkthdr.rcvif = NULL; + + /* + * Send it, repeatedly, until a reply is received, + * but delay each re-send by an increasing amount. + * If the delay hits the maximum, start complaining. + */ + timo = 0; + for (;;) { + /* Send RPC request (or re-send). */ + m = m_copym(mhead, 0, M_COPYALL, M_WAIT); + if (m == NULL) { + error = ENOBUFS; + goto out; + } + error = sosend(so, nam, NULL, m, NULL, 0); + if (error) { + printf("krpc_call: sosend: %d\n", error); + goto out; + } + m = NULL; + + /* Determine new timeout. */ + if (timo < MAX_RESEND_DELAY) + timo++; + else + printf("RPC timeout for server 0x%x\n", + ntohl(sin->sin_addr.s_addr)); + + /* + * Wait for up to timo seconds for a reply. + * The socket receive timeout was set to 1 second. + */ + secs = timo; + while (secs > 0) { + if (from) { + m_freem(from); + from = NULL; + } + if (m) { + m_freem(m); + m = NULL; + } + bzero(&auio,sizeof(auio)); + auio.uio_resid = len = 1<<16; + rcvflg = 0; + error = soreceive(so, &from, &auio, &m, NULL, &rcvflg); + if (error == EWOULDBLOCK) { + secs--; + continue; + } + if (error) + goto out; + len -= auio.uio_resid; + + /* Does the reply contain at least a header? */ + if (len < MIN_REPLY_HDR) + continue; + if (m->m_len < MIN_REPLY_HDR) + continue; + reply = mtod(m, struct rpc_reply *); + + /* Is it the right reply? */ + if (reply->rp_direction != txdr_unsigned(RPC_REPLY)) + continue; + + if (reply->rp_xid != txdr_unsigned(xid)) + continue; + + /* Was RPC accepted? (authorization OK) */ + if (reply->rp_astatus != 0) { + error = fxdr_unsigned(u_int32_t, reply->rp_errno); + printf("rpc denied, error=%d\n", error); + continue; + } + + /* Did the call succeed? */ + if (reply->rp_status != 0) { + error = fxdr_unsigned(u_int32_t, reply->rp_status); + if (error == RPC_PROGMISMATCH) { + error = EBADRPC; + goto out; + } + printf("rpc denied, status=%d\n", error); + continue; + } + + goto gotreply; /* break two levels */ + + } /* while secs */ + } /* forever send/receive */ + + error = ETIMEDOUT; + goto out; + + gotreply: + + /* + * Get RPC reply header into first mbuf, + * get its length, then strip it off. + */ + len = sizeof(*reply); + if (m->m_len < len) { + m = m_pullup(m, len); + if (m == NULL) { + error = ENOBUFS; + goto out; + } + } + reply = mtod(m, struct rpc_reply *); + if (reply->rp_auth.authtype != 0) { + len += fxdr_unsigned(u_int32_t, reply->rp_auth.authlen); + len = (len + 3) & ~3; /* XXX? */ + } + m_adj(m, len); + + /* result */ + *data = m; + if (from_p) { + *from_p = from; + from = NULL; + } + + out: + if (nam) m_freem(nam); + if (mhead) m_freem(mhead); + if (from) m_freem(from); + soclose(so); + return error; +} + +/* + * eXternal Data Representation routines. + * (but with non-standard args...) + */ + +/* + * String representation for RPC. + */ +struct xdr_string { + u_int32_t len; /* length without null or padding */ + char data[4]; /* data (longer, of course) */ + /* data is padded to a long-word boundary */ +}; + +struct mbuf * +xdr_string_encode(str, len) + char *str; + int len; +{ + struct mbuf *m; + struct xdr_string *xs; + int dlen; /* padded string length */ + int mlen; /* message length */ + + dlen = (len + 3) & ~3; + mlen = dlen + 4; + + if (mlen > MCLBYTES) /* If too big, we just can't do it. */ + return (NULL); + + m = m_get(M_WAIT, MT_DATA); + if (mlen > MLEN) { + MCLGET(m, M_WAIT); + if ((m->m_flags & M_EXT) == 0) { + (void) m_free(m); /* There can be only one. */ + return (NULL); + } + } + xs = mtod(m, struct xdr_string *); + m->m_len = mlen; + xs->len = txdr_unsigned(len); + bcopy(str, xs->data, len); + return (m); +} + +struct mbuf * +xdr_string_decode(m, str, len_p) + struct mbuf *m; + char *str; + int *len_p; /* bufsize - 1 */ +{ + struct xdr_string *xs; + int mlen; /* message length */ + int slen; /* string length */ + + if (m->m_len < 4) { + m = m_pullup(m, 4); + if (m == NULL) + return (NULL); + } + xs = mtod(m, struct xdr_string *); + slen = fxdr_unsigned(u_int32_t, xs->len); + mlen = 4 + ((slen + 3) & ~3); + + if (slen > *len_p) + slen = *len_p; + m_copydata(m, 4, slen, str); + m_adj(m, mlen); + + str[slen] = '\0'; + *len_p = slen; + + return (m); +} + + +/* + * Inet address in RPC messages + * (Note, really four ints, NOT chars. Blech.) + */ +struct xdr_inaddr { + u_int32_t atype; + u_int32_t addr[4]; +}; + +struct mbuf * +xdr_inaddr_encode(ia) + struct in_addr *ia; /* already in network order */ +{ + struct mbuf *m; + struct xdr_inaddr *xi; + u_int8_t *cp; + u_int32_t *ip; + + m = m_get(M_WAIT, MT_DATA); + xi = mtod(m, struct xdr_inaddr *); + m->m_len = sizeof(*xi); + xi->atype = txdr_unsigned(1); + ip = xi->addr; + cp = (u_int8_t *)&ia->s_addr; + *ip++ = txdr_unsigned(*cp++); + *ip++ = txdr_unsigned(*cp++); + *ip++ = txdr_unsigned(*cp++); + *ip++ = txdr_unsigned(*cp++); + + return (m); +} + +struct mbuf * +xdr_inaddr_decode(m, ia) + struct mbuf *m; + struct in_addr *ia; /* already in network order */ +{ + struct xdr_inaddr *xi; + u_int8_t *cp; + u_int32_t *ip; + + if (m->m_len < sizeof(*xi)) { + m = m_pullup(m, sizeof(*xi)); + if (m == NULL) + return (NULL); + } + xi = mtod(m, struct xdr_inaddr *); + if (xi->atype != txdr_unsigned(1)) { + ia->s_addr = INADDR_ANY; + goto out; + } + ip = xi->addr; + cp = (u_int8_t *)&ia->s_addr; + *cp++ = fxdr_unsigned(u_int8_t, *ip++); + *cp++ = fxdr_unsigned(u_int8_t, *ip++); + *cp++ = fxdr_unsigned(u_int8_t, *ip++); + *cp++ = fxdr_unsigned(u_int8_t, *ip++); + +out: + m_adj(m, sizeof(*xi)); + return (m); +} diff --git a/sys/nfs/nfs_vfsops.c b/sys/nfs/nfs_vfsops.c index 43e531bcbc94..a6a9d5ac6473 100644 --- a/sys/nfs/nfs_vfsops.c +++ b/sys/nfs/nfs_vfsops.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfs_vfsops.c 8.12 (Berkeley) 5/20/95 - * $Id: nfs_vfsops.c,v 1.39 1997/05/03 13:42:50 phk Exp $ + * $Id: nfs_vfsops.c,v 1.40 1997/05/04 15:04:49 phk Exp $ */ #include @@ -131,23 +131,24 @@ VFS_SET(nfs_vfsops, nfs, MOUNT_NFS, VFCF_NETWORK); * to ensure that it is allocated to initialized data (.data not .bss). */ struct nfs_diskless nfs_diskless = { 0 }; +struct nfsv3_diskless nfsv3_diskless = { 0 }; int nfs_diskless_valid = 0; SYSCTL_INT(_vfs_nfs, OID_AUTO, diskless_valid, CTLFLAG_RD, &nfs_diskless_valid, 0, ""); SYSCTL_STRING(_vfs_nfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD, - nfs_diskless.root_hostnam, 0, ""); + nfsv3_diskless.root_hostnam, 0, ""); SYSCTL_OPAQUE(_vfs_nfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD, - &nfs_diskless.root_saddr, sizeof nfs_diskless.root_saddr, + &nfsv3_diskless.root_saddr, sizeof nfsv3_diskless.root_saddr, "%Ssockaddr_in", ""); SYSCTL_STRING(_vfs_nfs, OID_AUTO, diskless_swappath, CTLFLAG_RD, - nfs_diskless.swap_hostnam, 0, ""); + nfsv3_diskless.swap_hostnam, 0, ""); SYSCTL_OPAQUE(_vfs_nfs, OID_AUTO, diskless_swapaddr, CTLFLAG_RD, - &nfs_diskless.swap_saddr, sizeof nfs_diskless.swap_saddr, + &nfsv3_diskless.swap_saddr, sizeof nfsv3_diskless.swap_saddr, "%Ssockaddr_in",""); @@ -156,6 +157,7 @@ static int nfs_mountdiskless __P((char *, char *, int, struct sockaddr_in *, struct nfs_args *, struct proc *, struct vnode **, struct mount **)); +void nfs_convert_diskless __P((void)); static int nfs_iosize(nmp) struct nfsmount* nmp; @@ -173,6 +175,35 @@ static int nfs_iosize(nmp) return iosize; } +void nfs_convert_diskless() +{ + bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif, + sizeof(struct ifaliasreq)); + bcopy(&nfs_diskless.swap_args,&nfsv3_diskless.swap_args, + sizeof(struct nfs_args)); + nfsv3_diskless.swap_fhsize = NFSX_V2FH; + bcopy(nfs_diskless.swap_fh,nfsv3_diskless.swap_fh,NFSX_V2FH); + bcopy(&nfs_diskless.swap_saddr,&nfsv3_diskless.swap_saddr, + sizeof(struct sockaddr_in)); + bcopy(nfs_diskless.swap_hostnam,nfsv3_diskless.swap_hostnam, + MNAMELEN); + nfsv3_diskless.swap_nblks = nfs_diskless.swap_nblks; + bcopy(&nfs_diskless.swap_ucred, &nfsv3_diskless.swap_ucred, + sizeof(struct ucred)); + bcopy(&nfs_diskless.root_args,&nfsv3_diskless.root_args, + sizeof(struct nfs_args)); + nfsv3_diskless.root_fhsize = NFSX_V2FH; + bcopy(nfs_diskless.root_fh,nfsv3_diskless.root_fh,NFSX_V2FH); + bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr, + sizeof(struct sockaddr_in)); + bcopy(nfs_diskless.root_hostnam,nfsv3_diskless.root_hostnam, + MNAMELEN); + nfsv3_diskless.root_time = nfs_diskless.root_time; + bcopy(nfs_diskless.my_hostnam,nfsv3_diskless.my_hostnam, + MAXHOSTNAMELEN); + nfs_diskless_valid = 3; +} + /* * nfs statfs call */ @@ -330,7 +361,7 @@ nfs_mountroot(mp) struct mount *mp; { struct mount *swap_mp; - struct nfs_diskless *nd = &nfs_diskless; + struct nfsv3_diskless *nd = &nfsv3_diskless; struct socket *so; struct vnode *vp; struct proc *p = curproc; /* XXX */ @@ -345,6 +376,9 @@ nfs_mountroot(mp) if (time.tv_sec == 0) time.tv_sec = 1; + if (nfs_diskless_valid==1) + nfs_convert_diskless(); + /* * XXX splnet, so networks will receive... */ @@ -410,10 +444,7 @@ nfs_mountroot(mp) * Create the rootfs mount point. */ nd->root_args.fh = nd->root_fh; - /* - * If using nfsv3_diskless, replace NFSX_V2FH with nd->root_fhsize. - */ - nd->root_args.fhsize = NFSX_V2FH; + nd->root_args.fhsize = nd->root_fhsize; l = ntohl(nd->root_saddr.sin_addr.s_addr); sprintf(buf,"%ld.%ld.%ld.%ld:%s", (l >> 24) & 0xff, (l >> 16) & 0xff, @@ -439,11 +470,7 @@ nfs_mountroot(mp) * swap file can be on a different server from the rootfs. */ nd->swap_args.fh = nd->swap_fh; - /* - * If using nfsv3_diskless, replace NFSX_V2FH with - * nd->swap_fhsize. - */ - nd->swap_args.fhsize = NFSX_V2FH; + nd->swap_args.fhsize = nd->swap_fhsize; l = ntohl(nd->swap_saddr.sin_addr.s_addr); sprintf(buf,"%ld.%ld.%ld.%ld:%s", (l >> 24) & 0xff, (l >> 16) & 0xff, diff --git a/sys/nfs/rpcv2.h b/sys/nfs/rpcv2.h index 86b82cc80483..fcb2694e426c 100644 --- a/sys/nfs/rpcv2.h +++ b/sys/nfs/rpcv2.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)rpcv2.h 8.2 (Berkeley) 3/30/95 - * $Id$ + * $Id: rpcv2.h,v 1.7 1997/02/22 09:42:53 peter Exp $ */ @@ -91,6 +91,7 @@ /* RPC Prog definitions */ #define RPCPROG_MNT 100005 #define RPCMNT_VER1 1 +#define RPCMNT_VER3 3 #define RPCMNT_MOUNT 1 #define RPCMNT_DUMP 2 #define RPCMNT_UMOUNT 3 diff --git a/sys/nfsclient/bootp_subr.c b/sys/nfsclient/bootp_subr.c new file mode 100644 index 000000000000..837ca6459a92 --- /dev/null +++ b/sys/nfsclient/bootp_subr.c @@ -0,0 +1,1319 @@ +/* $Id:$ */ + +/* + * Copyright (c) 1995 Gordon Ross, Adam Glass + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * based on: + * nfs/krpc_subr.c + * $NetBSD: krpc_subr.c,v 1.10 1995/08/08 20:43:43 gwr Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#define MIN_REPLY_HDR 16 /* xid, dir, astat, errno */ + +/* + * What is the longest we will wait before re-sending a request? + * Note this is also the frequency of "RPC timeout" messages. + * The re-send loop count sup linearly to this maximum, so the + * first complaint will happen after (1+2+3+4+5)=15 seconds. + */ +#define MAX_RESEND_DELAY 5 /* seconds */ + +/* Definitions from RFC951 */ +struct bootp_packet { + u_int8_t op; + u_int8_t htype; + u_int8_t hlen; + u_int8_t hops; + u_int32_t xid; + u_int16_t secs; + u_int16_t flags; + struct in_addr ciaddr; + struct in_addr yiaddr; + struct in_addr siaddr; + struct in_addr giaddr; + unsigned char chaddr[16]; + char sname[64]; + char file[128]; + unsigned char vend[256]; +}; + +#define IPPORT_BOOTPC 68 +#define IPPORT_BOOTPS 67 + +extern int nfs_diskless_valid; +extern struct nfsv3_diskless nfsv3_diskless; + +/* mountd RPC */ +static int md_mount __P((struct sockaddr_in *mdsin, char *path, + u_char *fhp, int *fhsizep, struct nfs_args *args,struct proc *procp)); +static int md_lookup_swap __P((struct sockaddr_in *mdsin,char *path, + u_char *fhp, int *fhsizep, + struct nfs_args *args, + struct proc *procp)); + +#ifdef BOOTP_DEBUG +void bootpboot_p_sa(struct sockaddr *sa,struct sockaddr *ma); +void bootpboot_p_ma(struct sockaddr *ma); +void bootpboot_p_rtentry(struct rtentry *rt); +void bootpboot_p_tree(struct radix_node *rn); +void bootpboot_p_rtlist(void); +void bootpboot_p_iflist(void); +#endif + +int bootpc_call(struct bootp_packet *call, + struct bootp_packet *reply, + struct proc *procp); + +int bootpc_fakeup_interface(struct ifreq *ireq,struct socket *so, + struct proc *procp); + +int +bootpc_adjust_interface(struct ifreq *ireq,struct socket *so, + struct sockaddr_in *myaddr, + struct sockaddr_in *netmask, + struct sockaddr_in *gw, + struct proc *procp); + +void bootpc_init(void); + +#ifdef BOOTP_DEBUG +void bootpboot_p_sa(sa,ma) + struct sockaddr *sa; + struct sockaddr *ma; +{ + if (!sa) { + printf("(sockaddr *) "); + return; + } + switch (sa->sa_family) { + case AF_INET: + { + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + printf("inet %x",ntohl(sin->sin_addr.s_addr)); + if (ma) { + struct sockaddr_in *sin = (struct sockaddr_in *) ma; + printf(" mask %x",ntohl(sin->sin_addr.s_addr)); + } + } + break; + case AF_LINK: + { + struct sockaddr_dl *sli = (struct sockaddr_dl *) sa; + int i; + printf("link %.*s ",sli->sdl_nlen,sli->sdl_data); + for (i=0;isdl_alen;i++) { + if (i>0) + printf(":"); + printf("%x",(unsigned char) sli->sdl_data[i+sli->sdl_nlen]); + } + } + break; + default: + printf("af%d",sa->sa_family); + } +} + +void bootpboot_p_ma(ma) + struct sockaddr *ma; +{ + if (!ma) { + printf(""); + return; + } + printf("%x",*(int*)ma); +} + +void bootpboot_p_rtentry(rt) + struct rtentry *rt; +{ + bootpboot_p_sa(rt_key(rt),rt_mask(rt)); + printf(" "); + bootpboot_p_ma(rt->rt_genmask); + printf(" "); + bootpboot_p_sa(rt->rt_gateway,NULL); + printf(" "); + printf("flags %x",(unsigned short) rt->rt_flags); + printf(" %d",rt->rt_rmx.rmx_expire); + printf(" %s%d\n",rt->rt_ifp->if_name,rt->rt_ifp->if_unit); +} +void bootpboot_p_tree(rn) + struct radix_node *rn; +{ + while (rn) { + if (rn->rn_b < 0) { + if (rn->rn_flags & RNF_ROOT) { + } else { + bootpboot_p_rtentry((struct rtentry *) rn); + } + rn = rn->rn_dupedkey; + } else { + bootpboot_p_tree(rn->rn_l); + bootpboot_p_tree(rn->rn_r); + return; + } + + } +} + +void bootpboot_p_rtlist(void) +{ + printf("Routing table:\n"); + bootpboot_p_tree(rt_tables[AF_INET]->rnh_treetop); +} + +void bootpboot_p_iflist(void) +{ + struct ifnet *ifp; + struct ifaddr *ifa; + printf("Interface list:\n"); + for (ifp = TAILQ_FIRST(&ifnet); ifp != 0; ifp = TAILQ_NEXT(ifp,if_link)) + { + for (ifa = TAILQ_FIRST(&ifp->if_addrhead) ;ifa; + ifa=TAILQ_NEXT(ifa,ifa_link)) + if (ifa->ifa_addr->sa_family == AF_INET ) { + printf("%s%d flags %x, addr %x, bcast %x, net %x\n", + ifp->if_name,ifp->if_unit, + (unsigned short) ifp->if_flags, + ntohl(((struct sockaddr_in *) ifa->ifa_addr)->sin_addr.s_addr), + ntohl(((struct sockaddr_in *) ifa->ifa_dstaddr)->sin_addr.s_addr), + ntohl(((struct sockaddr_in *) ifa->ifa_netmask)->sin_addr.s_addr) + ); + } + } +} +#endif + +int +bootpc_call(call,reply,procp) + struct bootp_packet *call; + struct bootp_packet *reply; /* output */ + struct proc *procp; +{ + struct socket *so; + struct sockaddr_in *sin,sa; + struct mbuf *m, *nam; + struct uio auio; + struct iovec aio; + int error, rcvflg, timo, secs, len; + u_int tport; + + /* Free at end if not null. */ + nam = NULL; + + /* + * Create socket and set its recieve timeout. + */ + if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0,procp))) + goto out; + + m = m_get(M_WAIT, MT_SOOPTS); + if (m == NULL) { + error = ENOBUFS; + goto out; + } else { + struct timeval *tv; + tv = mtod(m, struct timeval *); + m->m_len = sizeof(*tv); + tv->tv_sec = 1; + tv->tv_usec = 0; + if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m, procp))) + goto out; + } + + /* + * Enable broadcast. + */ + { + int *on; + m = m_get(M_WAIT, MT_SOOPTS); + if (m == NULL) { + error = ENOBUFS; + goto out; + } + on = mtod(m, int *); + m->m_len = sizeof(*on); + *on = 1; + if ((error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m, procp))) + goto out; + } + + /* + * Bind the local endpoint to a bootp client port. + */ + m = m_getclr(M_WAIT, MT_SONAME); + sin = mtod(m, struct sockaddr_in *); + sin->sin_len = m->m_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + sin->sin_port = htons(IPPORT_BOOTPC); + error = sobind(so, m, procp); + m_freem(m); + if (error) { + printf("bind failed\n"); + goto out; + } + + /* + * Setup socket address for the server. + */ + nam = m_get(M_WAIT, MT_SONAME); + if (nam == NULL) { + error = ENOBUFS; + goto out; + } + sin = mtod(nam, struct sockaddr_in *); + sin-> sin_len = sizeof(*sin); + sin-> sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_BROADCAST; + sin->sin_port = htons(IPPORT_BOOTPS); + + nam->m_len = sizeof(*sin); + + /* + * Send it, repeatedly, until a reply is received, + * but delay each re-send by an increasing amount. + * If the delay hits the maximum, start complaining. + */ + timo = 0; + for (;;) { + /* Send BOOTP request (or re-send). */ + + aio.iov_base = (caddr_t) call; + aio.iov_len = sizeof(*call); + + auio.uio_iov = &aio; + auio.uio_iovcnt = 1; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_rw = UIO_WRITE; + auio.uio_offset = 0; + auio.uio_resid = sizeof(*call); + auio.uio_procp = procp; + + error = sosend(so, nam, &auio, NULL, NULL, 0); + if (error) { + printf("bootpc_call: sosend: %d\n", error); + goto out; + } + + /* Determine new timeout. */ + if (timo < MAX_RESEND_DELAY) + timo++; + else + printf("BOOTP timeout for server 0x%x\n", + ntohl(sin->sin_addr.s_addr)); + + /* + * Wait for up to timo seconds for a reply. + * The socket receive timeout was set to 1 second. + */ + secs = timo; + while (secs > 0) { + aio.iov_base = (caddr_t) reply; + aio.iov_len = sizeof(*reply); + + auio.uio_iov = &aio; + auio.uio_iovcnt = 1; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_rw = UIO_READ; + auio.uio_offset = 0; + auio.uio_resid = sizeof(*reply); + auio.uio_procp = procp; + + rcvflg = 0; + error = soreceive(so, NULL, &auio, NULL, NULL, &rcvflg); + if (error == EWOULDBLOCK) { + secs--; + call->secs=htons(ntohs(call->secs)+1); + continue; + } + if (error) + goto out; + len = sizeof(*reply) - auio.uio_resid; + + /* Does the reply contain at least a header? */ + if (len < MIN_REPLY_HDR) + continue; + + /* Is it the right reply? */ + if (reply->op != 2) + continue; + + if (reply->xid != call->xid) + continue; + + if (reply->hlen != call->hlen) + continue; + + if (bcmp(reply->chaddr,call->chaddr,call->hlen)) + continue; + + goto gotreply; /* break two levels */ + + } /* while secs */ + } /* forever send/receive */ + + error = ETIMEDOUT; + goto out; + + gotreply: + out: + if (nam) m_freem(nam); + soclose(so); + return error; +} + +int +bootpc_fakeup_interface(struct ifreq *ireq,struct socket *so, + struct proc *procp) +{ + struct sockaddr_in *sin; + int error; + struct sockaddr_in dst; + struct sockaddr_in gw; + struct sockaddr_in mask; + + /* + * Bring up the interface. + * + * Get the old interface flags and or IFF_UP into them; if + * IFF_UP set blindly, interface selection can be clobbered. + */ + error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)ireq, procp); + if (error) + panic("bootpc_fakeup_interface: GIFFLAGS, error=%d", error); + ireq->ifr_flags |= IFF_UP; + error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)ireq, procp); + if (error) + panic("bootpc_fakeup_interface: SIFFLAGS, error=%d", error); + + /* + * Do enough of ifconfig(8) so that the chosen interface + * can talk to the servers. (just set the address) + */ + + /* addr is 0.0.0.0 */ + + sin = (struct sockaddr_in *)&ireq->ifr_addr; + bzero((caddr_t)sin, sizeof(*sin)); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + error = ifioctl(so, SIOCSIFADDR, (caddr_t)ireq, procp); + if (error) + panic("bootpc_fakeup_interface: set if addr, error=%d", error); + + /* netmask is 0.0.0.0 */ + + sin = (struct sockaddr_in *)&ireq->ifr_addr; + bzero((caddr_t)sin, sizeof(*sin)); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)ireq, procp); + if (error) + panic("bootpc_fakeup_interface: set if net addr, error=%d", error); + + /* Broadcast is 255.255.255.255 */ + + sin = (struct sockaddr_in *)&ireq->ifr_addr; + bzero((caddr_t)sin, sizeof(*sin)); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_BROADCAST; + error = ifioctl(so, SIOCSIFBRDADDR, (caddr_t)ireq, procp); + if (error) + panic("bootpc_fakeup_interface: set if broadcast addr, error=%d", error); + + /* Add default route to 0.0.0.0 so we can send data */ + + bzero((caddr_t) &dst, sizeof(dst)); + dst.sin_len=sizeof(dst); + dst.sin_family=AF_INET; + dst.sin_addr.s_addr = htonl(0); + + bzero((caddr_t) &gw, sizeof(gw)); + gw.sin_len=sizeof(gw); + gw.sin_family=AF_INET; + gw.sin_addr.s_addr = htonl(0x0); + + bzero((caddr_t) &mask, sizeof(mask)); + mask.sin_len=sizeof(mask); + mask.sin_family=AF_INET; + mask.sin_addr.s_addr = htonl(0); + + error = rtrequest(RTM_ADD, + (struct sockaddr *) &dst, + (struct sockaddr *) &gw, + (struct sockaddr *) &mask, + RTF_UP | RTF_STATIC + , NULL); + if (error) + printf("bootpc_fakeup_interface: add default route, error=%d\n", error); + return error; +} + +int +bootpc_adjust_interface(struct ifreq *ireq,struct socket *so, + struct sockaddr_in *myaddr, + struct sockaddr_in *netmask, + struct sockaddr_in *gw, + struct proc *procp) +{ + int error; + struct sockaddr_in oldgw; + struct sockaddr_in olddst; + struct sockaddr_in oldmask; + struct sockaddr_in *sin; + + /* Remove old default route to 0.0.0.0 */ + + bzero((caddr_t) &olddst, sizeof(olddst)); + olddst.sin_len=sizeof(olddst); + olddst.sin_family=AF_INET; + olddst.sin_addr.s_addr = INADDR_ANY; + + bzero((caddr_t) &oldgw, sizeof(oldgw)); + oldgw.sin_len=sizeof(oldgw); + oldgw.sin_family=AF_INET; + oldgw.sin_addr.s_addr = INADDR_ANY; + + bzero((caddr_t) &oldmask, sizeof(oldmask)); + oldmask.sin_len=sizeof(oldmask); + oldmask.sin_family=AF_INET; + oldmask.sin_addr.s_addr = INADDR_ANY; + + error = rtrequest(RTM_DELETE, + (struct sockaddr *) &olddst, + (struct sockaddr *) &oldgw, + (struct sockaddr *) &oldmask, + (RTF_UP | RTF_STATIC), NULL); + if (error) { + printf("nfs_boot: del default route, error=%d\n", error); + return error; + } + +#if 0 + olddst.sin_addr.s_addr = INADDR_BROADCAST; + + error = rtrequest(RTM_DELETE, + (struct sockaddr *) &olddst, + (struct sockaddr *) &oldgw, + (struct sockaddr *) NULL, + (RTF_UP | RTF_HOST | RTF_STATIC), NULL); + if (error) { + printf("nfs_boot: del broadcast route, error=%d\n", error); + } +#endif + + /* + * Do enough of ifconfig(8) so that the chosen interface + * can talk to the servers. (just set the address) + */ + bcopy(netmask,&ireq->ifr_addr,sizeof(*netmask)); + error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)ireq, procp); + if (error) + panic("nfs_boot: set if netmask, error=%d", error); + + /* Broadcast is with host part of IP address all 1's */ + + sin = (struct sockaddr_in *)&ireq->ifr_addr; + bzero((caddr_t)sin, sizeof(*sin)); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = myaddr->sin_addr.s_addr | ~ netmask->sin_addr.s_addr; + error = ifioctl(so, SIOCSIFBRDADDR, (caddr_t)ireq, procp); + if (error) + panic("bootpc_call: set if broadcast addr, error=%d", error); + + bcopy(myaddr,&ireq->ifr_addr,sizeof(*myaddr)); + error = ifioctl(so, SIOCSIFADDR, (caddr_t)ireq, procp); + if (error) + panic("nfs_boot: set if addr, error=%d", error); + + /* Add new default route */ + + error = rtrequest(RTM_ADD, + (struct sockaddr *) &olddst, + (struct sockaddr *) gw, + (struct sockaddr *) &oldmask, + (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL); + if (error) { + printf("nfs_boot: add net route, error=%d\n", error); + return error; + } + +#if 0 + /* Remove default gateway arp entry. This is a kludge, but + somehow the arp entry is added without an arp request + being sent, causing outgoing packets to be dropped onto the floor */ + + error = rtrequest(RTM_DELETE, + (struct sockaddr *) gw, + (struct sockaddr *) NULL, + (struct sockaddr *) NULL, + (RTF_UP | RTF_HOST | RTF_STATIC), NULL); + if (error) { + printf("nfs_boot: del default gateway linklevel route, error=%d\n", error); + } +#endif + + return 0; +} + +void bootp_expand_path(char *str,size_t len,char *hostname, + struct in_addr addr); +void bootp_expand_path(char *str,size_t len,char *hostname, + struct in_addr addr) +{ + char tmpbuf[128]; + char tmpaddr[20]; + + char *p,*q,*savep; + q = tmpbuf; + + savep = NULL; + sprintf(tmpaddr,"%d.%d.%d.%d", + ((unsigned char *) &addr)[0], + ((unsigned char *) &addr)[1], + ((unsigned char *) &addr)[2], + ((unsigned char *) &addr)[3]); + + for (p=str;*p;) { + switch(*p) { + case '%': + if (!savep) { + switch(*++p) { + case 'H': + savep = ++p; + p= hostname; + break; + case 'I': + savep = ++p; + p = tmpaddr; + break; + default: + goto arnej; + } + break; + } + default: + arnej: + if (q+1>=tmpbuf+sizeof(tmpbuf)) + panic("bootp_expand_path: Cannot expand %s\n",str); + else + *q++ = *p++; + if (!*p && savep) { + p = savep; + savep = NULL; + } + } + } + *q++ = 0; + if (q-tmpbuf>len) { + panic("bootp_expand_path: Too long expansion: %s\n",tmpbuf); + } + strcpy(str,tmpbuf); +} + +void +bootpc_init(void) +{ + struct bootp_packet call; + struct bootp_packet reply; + static u_int32_t xid = ~0xFF; + + struct ifreq ireq; + struct ifnet *ifp; + struct socket *so; + int error; + int code,len; + int i,j; + char rootpath[65]; + char swappath[65]; + + struct sockaddr_in myaddr; + struct sockaddr_in netmask; + struct sockaddr_in gw; + struct sockaddr_in server; + int gotgw=0; + int gotnetmask=0; + int gotserver=0; + int gotrootpath=0; + int gotswappath=0; + +#define EALEN 6 + unsigned char ea[EALEN]; + struct ifaddr *ifa; + struct sockaddr_dl *sdl = NULL; + char *delim; + + struct nfsv3_diskless *nd = &nfsv3_diskless; + struct proc *procp = curproc; + + /* + * If already filled in, don't touch it here + */ + if (nfs_diskless_valid) + return; + + /* + * Bump time if 0. + */ + if (!time.tv_sec) + time.tv_sec++; + + /* + * Find a network interface. + */ + for (ifp = TAILQ_FIRST(&ifnet); ifp != 0; ifp = TAILQ_NEXT(ifp,if_link)) + if ((ifp->if_flags & + (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0) + break; + if (ifp == NULL) + panic("bootpc_init: no suitable interface"); + bzero(&ireq,sizeof(ireq)); + sprintf(ireq.ifr_name, "%s%d", ifp->if_name,ifp->if_unit); + strcpy(nd->myif.ifra_name,ireq.ifr_name); + printf("bootpc_init: using network interface '%s'\n", + ireq.ifr_name); + + if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0,procp)) != 0) + panic("nfs_boot: socreate, error=%d", error); + + bootpc_fakeup_interface(&ireq,so,procp); + + printf("Bootpc testing starting\n"); + + /* Get HW address */ + + for (ifa = TAILQ_FIRST(&ifp->if_addrhead) ;ifa; + ifa=TAILQ_NEXT(ifa,ifa_link)) + if (ifa->ifa_addr->sa_family == AF_LINK && + (sdl = ((struct sockaddr_dl *) ifa->ifa_addr)) && + sdl->sdl_type == IFT_ETHER) + break; + + if (!sdl) + panic("bootpc: Unable to find HW address"); + if (sdl->sdl_alen != EALEN ) + panic("bootpc: HW address len is %d, expected value is %d", + sdl->sdl_alen,EALEN); +#if 1 + printf("bootpc hw address is "); + delim=""; + for (j=0;jsdl_alen;j++) { + printf("%s%x",delim,((unsigned char *)LLADDR(sdl))[j]); + delim=":"; + } + printf("\n"); +#endif + +#if 0 + bootpboot_p_iflist(); + bootpboot_p_rtlist(); +#endif + + bzero((caddr_t) &call, sizeof(call)); + + /* bootpc part */ + call.op = 1; /* BOOTREQUEST */ + call.htype= 1; /* 10mb ethernet */ + call.hlen=sdl->sdl_alen; /* Hardware address length */ + call.hops=0; + xid++; + call.xid = txdr_unsigned(xid); + bcopy(LLADDR(sdl),&call.chaddr,sdl->sdl_alen); + + call.vend[0]=99; + call.vend[1]=130; + call.vend[2]=83; + call.vend[3]=99; + call.vend[4]=255; + + call.secs = 0; + call.flags = htons(0x8000); /* We need an broadcast answer */ + + error = bootpc_call(&call,&reply,procp); + + if (error) { +#ifdef BOOTP_NFSROOT + panic("BOOTP call failed"); +#endif + return; + } + + bzero(&myaddr,sizeof(myaddr)); + bzero(&netmask,sizeof(netmask)); + bzero(&gw,sizeof(gw)); + bzero(&server,sizeof(server)); + + myaddr.sin_len = sizeof(myaddr); + myaddr.sin_family = AF_INET; + + netmask.sin_len = sizeof(netmask); + netmask.sin_family = AF_INET; + + gw.sin_len = sizeof(gw); + gw.sin_family= AF_INET; + + server.sin_len = sizeof(gw); + server.sin_family= AF_INET; + + printf("My new ip address is %x\n",htonl(reply.yiaddr.s_addr)); + + myaddr.sin_addr = reply.yiaddr; + + printf("Server ip address is %x\n",htonl(reply.siaddr.s_addr)); + printf("Gateway ip address is %x\n",htonl(reply.giaddr.s_addr)); + + gw.sin_addr = reply.giaddr; + + if (reply.sname[0]) + printf("Server name is %s\n",reply.sname); + if (reply.file[0]) + printf("boot file is %s\n",reply.file); + if (reply.vend[0]==99 && reply.vend[1]==130 && + reply.vend[2]==83 && reply.vend[3]==99) { + j=4; + while (j=sizeof(reply.vend)) { + printf("Truncated field"); + break; + } + switch (code) { + case 1: + if (len!=4) + panic("bootpc: subnet mask len is %d",len); + bcopy(&reply.vend[j],&netmask.sin_addr,4); + gotnetmask=1; + printf("Subnet mask is %d.%d.%d.%d\n", + reply.vend[j], + reply.vend[j+1], + reply.vend[j+2], + reply.vend[j+3]); + break; + case 2: + /* Time offset */ + break; + case 3: + /* Routers */ + if (len % 4) + panic("bootpc: Router Len is %d",len); + if (len > 0) { + bcopy(&reply.vend[j],&gw.sin_addr,4); + gotgw=1; + } + for (i=0;i=sizeof(rootpath)) + panic("bootpc: rootpath >=%d bytes",sizeof(rootpath)); + strncpy(rootpath,&reply.vend[j],len); + rootpath[len]=0; + gotrootpath=1; + printf("Rootpath is %s\n",rootpath); + break; + case 12: + if (len>=MAXHOSTNAMELEN) + panic("bootpc: hostname >=%d bytes",MAXHOSTNAMELEN); + strncpy(nd->my_hostnam,&reply.vend[j],len); + nd->my_hostnam[len]=0; + strncpy(hostname,&reply.vend[j],len); + hostname[len]=0; + printf("Hostname is %s\n",hostname); + break; + case 128: + if (len>=sizeof(swappath)) + panic("bootpc: swappath >=%d bytes",sizeof(swappath)); + strncpy(swappath,&reply.vend[j],len); + swappath[len]=0; + gotswappath=1; + printf("Swappath is %s\n",swappath); + break; + case 129: + { + int swaplen; + if (len!=4) + panic("bootpc: Expected 4 bytes for swaplen, not %d bytes",len); + bcopy(&reply.vend[j],&swaplen,4); + nd->swap_nblks = ntohl(swaplen); + printf("bootpc: Swap size is %d KB\n",nd->swap_nblks); + } + break; + default: + printf("Ignoring field type %d\n",code); + } + j+=len; + } + } + + if (gotrootpath) { + bootp_expand_path(rootpath,sizeof(rootpath), + hostname, + myaddr.sin_addr); + printf("Rootpath is expanded to %s\n",rootpath); + if (gotswappath) { + bootp_expand_path(swappath,sizeof(swappath), + hostname, + myaddr.sin_addr); + printf("Swappath is expanded to %s\n",swappath); + } + else + nd->swap_nblks = 0; + } else { +#ifdef BOOTP_NFSROOT + panic("bootpc: No root path offered"); +#endif + } + + if (!gotserver) { + server.sin_addr = reply.siaddr ; + } + + if (!gotnetmask) { + if (IN_CLASSA(myaddr.sin_addr.s_addr)) + netmask.sin_addr.s_addr = IN_CLASSA_NET; + else if (IN_CLASSB(myaddr.sin_addr.s_addr)) + netmask.sin_addr.s_addr = IN_CLASSB_NET; + else + netmask.sin_addr.s_addr = IN_CLASSC_NET; + } + if (!gotgw) { + /* Use proxyarp */ + gw.sin_addr.s_addr = myaddr.sin_addr.s_addr; + } + +#if 0 + bootpboot_p_iflist(); + bootpboot_p_rtlist(); +#endif + error = bootpc_adjust_interface(&ireq,so, + &myaddr,&netmask,&gw,procp); + + soclose(so); + +#if 0 + bootpboot_p_iflist(); + bootpboot_p_rtlist(); +#endif + + nd->root_args.version = NFS_ARGSVERSION; + nd->root_args.rsize = 8192; + nd->root_args.wsize = 8192; + nd->root_args.sotype = SOCK_DGRAM; + nd->root_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE | + NFSMNT_RESVPORT | NFSMNT_NOCONN); + + nd->swap_args.version = NFS_ARGSVERSION; + nd->swap_args.rsize = 8192; + nd->swap_args.wsize = 8192; + nd->swap_args.sotype = SOCK_DGRAM; + nd->swap_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE | + NFSMNT_RESVPORT | NFSMNT_NOCONN); + + if (gotrootpath) { + if (server.sin_addr.s_addr != reply.siaddr.s_addr ) { + sprintf(nd->root_hostnam,"%d.%d.%d.%d", + ((unsigned char *) &server.sin_addr)[0], + ((unsigned char *) &server.sin_addr)[1], + ((unsigned char *) &server.sin_addr)[2], + ((unsigned char *) &server.sin_addr)[3]); + } else + strcpy(nd->root_hostnam,reply.sname); + bcopy(&server, &nd->root_saddr,sizeof(server)); + + error = md_mount(&nd->root_saddr, rootpath, nd->root_fh, &nd->root_fhsize, + &nd->root_args,procp); + if (error) + panic("nfs_boot: mountd root, error=%d", error); + + if (gotswappath) { + + char *p = swappath; + + while (*p) + p++; + while (p>=swappath && *p != '/') + p--; + + strcpy(nd->swap_hostnam, nd->root_hostnam); + + bcopy(&server, &nd->swap_saddr,sizeof(server)); + + if (p>swappath) + *p = '\0'; + error = md_mount(&nd->swap_saddr, + (p>swappath) ? swappath:"/", + nd->swap_fh, &nd->swap_fhsize,&nd->swap_args,procp); + if (error) + panic("nfs_boot: mountd swap, error=%d", error); + if (p>swappath) + *p = '/'; + + if (p>=swappath) + error = md_lookup_swap(&nd->swap_saddr,p+1,nd->swap_fh, + &nd->swap_fhsize, &nd->swap_args,procp); + if (error) + panic("nfs_boot: lookup swap, error=%d", error); + } + nfs_diskless_valid = 3; + } + + + bcopy(&myaddr,&nd->myif.ifra_addr,sizeof(myaddr)); + bcopy(&myaddr,&nd->myif.ifra_broadaddr,sizeof(myaddr)); + ((struct sockaddr_in *) &nd->myif.ifra_broadaddr)->sin_addr.s_addr = + myaddr.sin_addr.s_addr | ~ netmask.sin_addr.s_addr; + bcopy(&netmask,&nd->myif.ifra_mask,sizeof(netmask)); + +#if 0 + bootpboot_p_iflist(); + bootpboot_p_rtlist(); +#endif + return; +} + +/* + * RPC: mountd/mount + * Given a server pathname, get an NFS file handle. + * Also, sets sin->sin_port to the NFS service port. + */ +static int +md_mount(mdsin, path, fhp, fhsizep, args, procp) + struct sockaddr_in *mdsin; /* mountd server address */ + char *path; + u_char *fhp; + int *fhsizep; + struct nfs_args *args; + struct proc *procp; +{ + struct mbuf *m; + int error; + int authunixok; + int authcount; + int authver; + +#ifdef BOOTP_NFSV3 + /* First try NFS v3 */ + /* Get port number for MOUNTD. */ + error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER3, + &mdsin->sin_port, procp); + if (!error) { + m = xdr_string_encode(path, strlen(path)); + + /* Do RPC to mountd. */ + error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER3, + RPCMNT_MOUNT, &m, NULL, curproc); + } + if (!error) { + args->flags |= NFSMNT_NFSV3; + } else { +#endif + /* Fallback to NFS v2 */ + + /* Get port number for MOUNTD. */ + error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1, + &mdsin->sin_port, procp); + if (error) return error; + + m = xdr_string_encode(path, strlen(path)); + + /* Do RPC to mountd. */ + error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1, + RPCMNT_MOUNT, &m, NULL, curproc); + if (error) + return error; /* message already freed */ + +#ifdef BOOTP_NFSV3 + } +#endif + + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + error = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + if (error) + goto bad; + m_adj(m,sizeof(u_int32_t)); + + if (args->flags & NFSMNT_NFSV3) { + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + *fhsizep = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + if (*fhsizep > NFSX_V3FHMAX || *fhsizep <= 0 ) + goto bad; + m_adj(m,sizeof(u_int32_t)); + } else + *fhsizep = NFSX_V2FH; + + if (m->m_len < (((*fhsizep)+3)&~3)) { + m = m_pullup(m,(((*fhsizep)+3)&~3)); + if (m == NULL) + goto bad; + } + bcopy(mtod(m,u_char *), fhp, *fhsizep); + m_adj(m,(((*fhsizep)+3)&~3)); + + if (args->flags & NFSMNT_NFSV3) { + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + authcount = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + authunixok = 0; + m_adj(m,sizeof(u_int32_t)); + if (authcount<0 || authcount>100) + goto bad; + while (authcount>0) { + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + authver = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + if (authver == RPCAUTH_UNIX) + authunixok = 1; + authcount--; + m_adj(m,sizeof(u_int32_t)); + } + if (!authunixok) + goto bad; + } + + /* Set port number for NFS use. */ + error = krpc_portmap(mdsin, NFS_PROG, + (args->flags & NFSMNT_NFSV3)?NFS_VER3:NFS_VER2, + &mdsin->sin_port, procp); + + goto out; + +bad: + error = EBADRPC; + +out: + m_freem(m); + return error; +} + +static int md_lookup_swap(mdsin, path, fhp, fhsizep, args, procp) + struct sockaddr_in *mdsin; /* mountd server address */ + char *path; + u_char *fhp; + int *fhsizep; + struct nfs_args *args; + struct proc *procp; +{ + struct mbuf *m; + int error; + int size = -1; + int attribs_present; + int status; + + m = m_get(M_WAIT,MT_DATA); + if (!m) + return ENOBUFS; + + if (args->flags & NFSMNT_NFSV3) { + *mtod(m,u_int32_t *) = txdr_unsigned(*fhsizep); + bcopy(fhp,mtod(m,u_char *)+sizeof(u_int32_t),*fhsizep); + m->m_len = *fhsizep + sizeof(u_int32_t); + } else { + bcopy(fhp,mtod(m,u_char *),NFSX_V2FH); + m->m_len = NFSX_V2FH; + } + + m->m_next = xdr_string_encode(path, strlen(path)); + if (!m->m_next) { + error = ENOBUFS; + goto out; + } + + /* Do RPC to nfsd. */ + if (args->flags & NFSMNT_NFSV3) + error = krpc_call(mdsin, NFS_PROG, NFS_VER3, + NFSPROC_LOOKUP, &m, NULL, procp); + else + error = krpc_call(mdsin, NFS_PROG, NFS_VER2, + NFSV2PROC_LOOKUP, &m, NULL, procp); + if (error) + return error; /* message already freed */ + + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + status = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + m_adj(m,sizeof(u_int32_t)); + if (status) { + error = ENOENT; + goto out; + } + + if (args->flags & NFSMNT_NFSV3) { + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + *fhsizep = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + if (*fhsizep > NFSX_V3FHMAX || *fhsizep <= 0 ) + goto bad; + m_adj(m,sizeof(u_int32_t)); + } else + *fhsizep = NFSX_V2FH; + + if (m->m_len < (((*fhsizep)+3)&~3)) { + m = m_pullup(m,(((*fhsizep)+3)&~3)); + if (m == NULL) + goto bad; + } + bcopy(mtod(m,u_char *), fhp, *fhsizep); + m_adj(m,(((*fhsizep)+3)&~3)); + + if (args->flags & NFSMNT_NFSV3) { + if (m->m_len < sizeof(u_int32_t)) { + m = m_pullup(m, sizeof(u_int32_t)); + if (m == NULL) + goto bad; + } + attribs_present = fxdr_unsigned(u_int32_t, *mtod(m,u_int32_t *)); + m_adj(m,sizeof(u_int32_t)); + if (attribs_present) { + if (m->m_len < sizeof(u_int32_t)*21) { + m = m_pullup(m, sizeof(u_int32_t)*21); + if (m == NULL) + goto bad; + } + size = fxdr_unsigned(u_int32_t, mtod(m,u_int32_t *)[6]); + } + } else { + if (m->m_len < sizeof(u_int32_t)*17) { + m = m_pullup(m, sizeof(u_int32_t)*17); + if (m == NULL) + goto bad; + } + size = fxdr_unsigned(u_int32_t, mtod(m,u_int32_t *)[5]); + } + + if (!nfsv3_diskless.swap_nblks && size!= -1) { + nfsv3_diskless.swap_nblks = size/1024; + printf("md_lookup_swap: Swap size is %d KB\n", + nfsv3_diskless.swap_nblks); + } + + goto out; + +bad: + error = EBADRPC; + +out: + m_freem(m); + return error; +} diff --git a/sys/nfsclient/krpc.h b/sys/nfsclient/krpc.h new file mode 100644 index 000000000000..1da22f13b603 --- /dev/null +++ b/sys/nfsclient/krpc.h @@ -0,0 +1,30 @@ +/* $NetBSD: krpc.h,v 1.4 1995/12/19 23:07:11 cgd Exp $ */ +/* $Id:$ */ + +#include + +int krpc_call __P((struct sockaddr_in *sin, + u_int prog, u_int vers, u_int func, + struct mbuf **data, struct mbuf **from, struct proc *procp)); + +int krpc_portmap __P((struct sockaddr_in *sin, + u_int prog, u_int vers, u_int16_t *portp,struct proc *procp)); + +struct mbuf *xdr_string_encode __P((char *str, int len)); +struct mbuf *xdr_string_decode __P((struct mbuf *m, char *str, int *len_p)); +struct mbuf *xdr_inaddr_encode __P((struct in_addr *ia)); +struct mbuf *xdr_inaddr_decode __P((struct mbuf *m, struct in_addr *ia)); + + +/* + * RPC definitions for the portmapper + */ +#define PMAPPORT 111 +#define PMAPPROG 100000 +#define PMAPVERS 2 +#define PMAPPROC_NULL 0 +#define PMAPPROC_SET 1 +#define PMAPPROC_UNSET 2 +#define PMAPPROC_GETPORT 3 +#define PMAPPROC_DUMP 4 +#define PMAPPROC_CALLIT 5 diff --git a/sys/nfsclient/krpc_subr.c b/sys/nfsclient/krpc_subr.c new file mode 100644 index 000000000000..bb31e4b304ac --- /dev/null +++ b/sys/nfsclient/krpc_subr.c @@ -0,0 +1,592 @@ +/* $NetBSD: krpc_subr.c,v 1.12.4.1 1996/06/07 00:52:26 cgd Exp $ */ +/* $Id:$ */ + +/* + * Copyright (c) 1995 Gordon Ross, Adam Glass + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * partially based on: + * libnetboot/rpc.c + * @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp (LBL) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +/* + * Kernel support for Sun RPC + * + * Used currently for bootstrapping in nfs diskless configurations. + */ + +/* + * Generic RPC headers + */ + +struct auth_info { + u_int32_t authtype; /* auth type */ + u_int32_t authlen; /* auth length */ +}; + +struct auth_unix { + int32_t ua_time; + int32_t ua_hostname; /* null */ + int32_t ua_uid; + int32_t ua_gid; + int32_t ua_gidlist; /* null */ +}; + +struct rpc_call { + u_int32_t rp_xid; /* request transaction id */ + int32_t rp_direction; /* call direction (0) */ + u_int32_t rp_rpcvers; /* rpc version (2) */ + u_int32_t rp_prog; /* program */ + u_int32_t rp_vers; /* version */ + u_int32_t rp_proc; /* procedure */ + struct auth_info rpc_auth; + struct auth_unix rpc_unix; + struct auth_info rpc_verf; +}; + +struct rpc_reply { + u_int32_t rp_xid; /* request transaction id */ + int32_t rp_direction; /* call direction (1) */ + int32_t rp_astatus; /* accept status (0: accepted) */ + union { + u_int32_t rpu_errno; + struct { + struct auth_info rok_auth; + u_int32_t rok_status; + } rpu_rok; + } rp_u; +}; +#define rp_errno rp_u.rpu_errno +#define rp_auth rp_u.rpu_rok.rok_auth +#define rp_status rp_u.rpu_rok.rok_status + +#define MIN_REPLY_HDR 16 /* xid, dir, astat, errno */ + +/* + * What is the longest we will wait before re-sending a request? + * Note this is also the frequency of "RPC timeout" messages. + * The re-send loop count sup linearly to this maximum, so the + * first complaint will happen after (1+2+3+4+5)=15 seconds. + */ +#define MAX_RESEND_DELAY 5 /* seconds */ + +/* + * Call portmap to lookup a port number for a particular rpc program + * Returns non-zero error on failure. + */ +int +krpc_portmap(sin, prog, vers, portp, procp) + struct sockaddr_in *sin; /* server address */ + u_int prog, vers; /* host order */ + u_int16_t *portp; /* network order */ + struct proc *procp; +{ + struct sdata { + u_int32_t prog; /* call program */ + u_int32_t vers; /* call version */ + u_int32_t proto; /* call protocol */ + u_int32_t port; /* call port (unused) */ + } *sdata; + struct rdata { + u_int16_t pad; + u_int16_t port; + } *rdata; + struct mbuf *m; + int error; + + /* The portmapper port is fixed. */ + if (prog == PMAPPROG) { + *portp = htons(PMAPPORT); + return 0; + } + + m = m_get(M_WAIT, MT_DATA); + if (m == NULL) + return ENOBUFS; + sdata = mtod(m, struct sdata *); + m->m_len = sizeof(*sdata); + + /* Do the RPC to get it. */ + sdata->prog = txdr_unsigned(prog); + sdata->vers = txdr_unsigned(vers); + sdata->proto = txdr_unsigned(IPPROTO_UDP); + sdata->port = 0; + + sin->sin_port = htons(PMAPPORT); + error = krpc_call(sin, PMAPPROG, PMAPVERS, + PMAPPROC_GETPORT, &m, NULL, procp); + if (error) + return error; + + if (m->m_len < sizeof(*rdata)) { + m = m_pullup(m, sizeof(*rdata)); + if (m == NULL) + return ENOBUFS; + } + rdata = mtod(m, struct rdata *); + *portp = rdata->port; + + m_freem(m); + return 0; +} + +/* + * Do a remote procedure call (RPC) and wait for its reply. + * If from_p is non-null, then we are doing broadcast, and + * the address from whence the response came is saved there. + */ +int +krpc_call(sa, prog, vers, func, data, from_p, procp) + struct sockaddr_in *sa; + u_int prog, vers, func; + struct mbuf **data; /* input/output */ + struct mbuf **from_p; /* output */ + struct proc *procp; +{ + struct socket *so; + struct sockaddr_in *sin; + struct mbuf *m, *nam, *mhead, *from; + struct rpc_call *call; + struct rpc_reply *reply; + struct uio auio; + int error, rcvflg, timo, secs, len; + static u_int32_t xid = ~0xFF; + u_int16_t tport; + + /* + * Validate address family. + * Sorry, this is INET specific... + */ + if (sa->sin_family != AF_INET) + return (EAFNOSUPPORT); + + /* Free at end if not null. */ + nam = mhead = NULL; + from = NULL; + + /* + * Create socket and set its recieve timeout. + */ + if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0, procp))) + goto out; + + m = m_get(M_WAIT, MT_SOOPTS); + if (m == NULL) { + error = ENOBUFS; + goto out; + } else { + struct timeval *tv; + tv = mtod(m, struct timeval *); + m->m_len = sizeof(*tv); + tv->tv_sec = 1; + tv->tv_usec = 0; + if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m, procp))) + goto out; + } + + /* + * Enable broadcast if necessary. + */ + if (from_p) { + int32_t *on; + m = m_get(M_WAIT, MT_SOOPTS); + if (m == NULL) { + error = ENOBUFS; + goto out; + } + on = mtod(m, int32_t *); + m->m_len = sizeof(*on); + *on = 1; + if ((error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m, procp))) + goto out; + } + + /* + * Bind the local endpoint to a reserved port, + * because some NFS servers refuse requests from + * non-reserved (non-privileged) ports. + */ + m = m_getclr(M_WAIT, MT_SONAME); + sin = mtod(m, struct sockaddr_in *); + sin->sin_len = m->m_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; + tport = IPPORT_RESERVED; + do { + tport--; + sin->sin_port = htons(tport); + error = sobind(so, m, procp); + } while (error == EADDRINUSE && + tport > IPPORT_RESERVED / 2); + m_freem(m); + if (error) { + printf("bind failed\n"); + goto out; + } + + /* + * Setup socket address for the server. + */ + nam = m_get(M_WAIT, MT_SONAME); + if (nam == NULL) { + error = ENOBUFS; + goto out; + } + sin = mtod(nam, struct sockaddr_in *); + bcopy((caddr_t)sa, (caddr_t)sin, + (nam->m_len = sa->sin_len)); + + /* + * Prepend RPC message header. + */ + mhead = m_gethdr(M_WAIT, MT_DATA); + mhead->m_next = *data; + call = mtod(mhead, struct rpc_call *); + mhead->m_len = sizeof(*call); + bzero((caddr_t)call, sizeof(*call)); + /* rpc_call part */ + xid++; + call->rp_xid = txdr_unsigned(xid); + /* call->rp_direction = 0; */ + call->rp_rpcvers = txdr_unsigned(2); + call->rp_prog = txdr_unsigned(prog); + call->rp_vers = txdr_unsigned(vers); + call->rp_proc = txdr_unsigned(func); + /* rpc_auth part (auth_unix as root) */ + call->rpc_auth.authtype = txdr_unsigned(RPCAUTH_UNIX); + call->rpc_auth.authlen = txdr_unsigned(sizeof(struct auth_unix)); + /* rpc_verf part (auth_null) */ + call->rpc_verf.authtype = 0; + call->rpc_verf.authlen = 0; + + /* + * Setup packet header + */ + len = 0; + m = mhead; + while (m) { + len += m->m_len; + m = m->m_next; + } + mhead->m_pkthdr.len = len; + mhead->m_pkthdr.rcvif = NULL; + + /* + * Send it, repeatedly, until a reply is received, + * but delay each re-send by an increasing amount. + * If the delay hits the maximum, start complaining. + */ + timo = 0; + for (;;) { + /* Send RPC request (or re-send). */ + m = m_copym(mhead, 0, M_COPYALL, M_WAIT); + if (m == NULL) { + error = ENOBUFS; + goto out; + } + error = sosend(so, nam, NULL, m, NULL, 0); + if (error) { + printf("krpc_call: sosend: %d\n", error); + goto out; + } + m = NULL; + + /* Determine new timeout. */ + if (timo < MAX_RESEND_DELAY) + timo++; + else + printf("RPC timeout for server 0x%x\n", + ntohl(sin->sin_addr.s_addr)); + + /* + * Wait for up to timo seconds for a reply. + * The socket receive timeout was set to 1 second. + */ + secs = timo; + while (secs > 0) { + if (from) { + m_freem(from); + from = NULL; + } + if (m) { + m_freem(m); + m = NULL; + } + bzero(&auio,sizeof(auio)); + auio.uio_resid = len = 1<<16; + rcvflg = 0; + error = soreceive(so, &from, &auio, &m, NULL, &rcvflg); + if (error == EWOULDBLOCK) { + secs--; + continue; + } + if (error) + goto out; + len -= auio.uio_resid; + + /* Does the reply contain at least a header? */ + if (len < MIN_REPLY_HDR) + continue; + if (m->m_len < MIN_REPLY_HDR) + continue; + reply = mtod(m, struct rpc_reply *); + + /* Is it the right reply? */ + if (reply->rp_direction != txdr_unsigned(RPC_REPLY)) + continue; + + if (reply->rp_xid != txdr_unsigned(xid)) + continue; + + /* Was RPC accepted? (authorization OK) */ + if (reply->rp_astatus != 0) { + error = fxdr_unsigned(u_int32_t, reply->rp_errno); + printf("rpc denied, error=%d\n", error); + continue; + } + + /* Did the call succeed? */ + if (reply->rp_status != 0) { + error = fxdr_unsigned(u_int32_t, reply->rp_status); + if (error == RPC_PROGMISMATCH) { + error = EBADRPC; + goto out; + } + printf("rpc denied, status=%d\n", error); + continue; + } + + goto gotreply; /* break two levels */ + + } /* while secs */ + } /* forever send/receive */ + + error = ETIMEDOUT; + goto out; + + gotreply: + + /* + * Get RPC reply header into first mbuf, + * get its length, then strip it off. + */ + len = sizeof(*reply); + if (m->m_len < len) { + m = m_pullup(m, len); + if (m == NULL) { + error = ENOBUFS; + goto out; + } + } + reply = mtod(m, struct rpc_reply *); + if (reply->rp_auth.authtype != 0) { + len += fxdr_unsigned(u_int32_t, reply->rp_auth.authlen); + len = (len + 3) & ~3; /* XXX? */ + } + m_adj(m, len); + + /* result */ + *data = m; + if (from_p) { + *from_p = from; + from = NULL; + } + + out: + if (nam) m_freem(nam); + if (mhead) m_freem(mhead); + if (from) m_freem(from); + soclose(so); + return error; +} + +/* + * eXternal Data Representation routines. + * (but with non-standard args...) + */ + +/* + * String representation for RPC. + */ +struct xdr_string { + u_int32_t len; /* length without null or padding */ + char data[4]; /* data (longer, of course) */ + /* data is padded to a long-word boundary */ +}; + +struct mbuf * +xdr_string_encode(str, len) + char *str; + int len; +{ + struct mbuf *m; + struct xdr_string *xs; + int dlen; /* padded string length */ + int mlen; /* message length */ + + dlen = (len + 3) & ~3; + mlen = dlen + 4; + + if (mlen > MCLBYTES) /* If too big, we just can't do it. */ + return (NULL); + + m = m_get(M_WAIT, MT_DATA); + if (mlen > MLEN) { + MCLGET(m, M_WAIT); + if ((m->m_flags & M_EXT) == 0) { + (void) m_free(m); /* There can be only one. */ + return (NULL); + } + } + xs = mtod(m, struct xdr_string *); + m->m_len = mlen; + xs->len = txdr_unsigned(len); + bcopy(str, xs->data, len); + return (m); +} + +struct mbuf * +xdr_string_decode(m, str, len_p) + struct mbuf *m; + char *str; + int *len_p; /* bufsize - 1 */ +{ + struct xdr_string *xs; + int mlen; /* message length */ + int slen; /* string length */ + + if (m->m_len < 4) { + m = m_pullup(m, 4); + if (m == NULL) + return (NULL); + } + xs = mtod(m, struct xdr_string *); + slen = fxdr_unsigned(u_int32_t, xs->len); + mlen = 4 + ((slen + 3) & ~3); + + if (slen > *len_p) + slen = *len_p; + m_copydata(m, 4, slen, str); + m_adj(m, mlen); + + str[slen] = '\0'; + *len_p = slen; + + return (m); +} + + +/* + * Inet address in RPC messages + * (Note, really four ints, NOT chars. Blech.) + */ +struct xdr_inaddr { + u_int32_t atype; + u_int32_t addr[4]; +}; + +struct mbuf * +xdr_inaddr_encode(ia) + struct in_addr *ia; /* already in network order */ +{ + struct mbuf *m; + struct xdr_inaddr *xi; + u_int8_t *cp; + u_int32_t *ip; + + m = m_get(M_WAIT, MT_DATA); + xi = mtod(m, struct xdr_inaddr *); + m->m_len = sizeof(*xi); + xi->atype = txdr_unsigned(1); + ip = xi->addr; + cp = (u_int8_t *)&ia->s_addr; + *ip++ = txdr_unsigned(*cp++); + *ip++ = txdr_unsigned(*cp++); + *ip++ = txdr_unsigned(*cp++); + *ip++ = txdr_unsigned(*cp++); + + return (m); +} + +struct mbuf * +xdr_inaddr_decode(m, ia) + struct mbuf *m; + struct in_addr *ia; /* already in network order */ +{ + struct xdr_inaddr *xi; + u_int8_t *cp; + u_int32_t *ip; + + if (m->m_len < sizeof(*xi)) { + m = m_pullup(m, sizeof(*xi)); + if (m == NULL) + return (NULL); + } + xi = mtod(m, struct xdr_inaddr *); + if (xi->atype != txdr_unsigned(1)) { + ia->s_addr = INADDR_ANY; + goto out; + } + ip = xi->addr; + cp = (u_int8_t *)&ia->s_addr; + *cp++ = fxdr_unsigned(u_int8_t, *ip++); + *cp++ = fxdr_unsigned(u_int8_t, *ip++); + *cp++ = fxdr_unsigned(u_int8_t, *ip++); + *cp++ = fxdr_unsigned(u_int8_t, *ip++); + +out: + m_adj(m, sizeof(*xi)); + return (m); +} diff --git a/sys/nfsclient/nfs_vfsops.c b/sys/nfsclient/nfs_vfsops.c index 43e531bcbc94..a6a9d5ac6473 100644 --- a/sys/nfsclient/nfs_vfsops.c +++ b/sys/nfsclient/nfs_vfsops.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfs_vfsops.c 8.12 (Berkeley) 5/20/95 - * $Id: nfs_vfsops.c,v 1.39 1997/05/03 13:42:50 phk Exp $ + * $Id: nfs_vfsops.c,v 1.40 1997/05/04 15:04:49 phk Exp $ */ #include @@ -131,23 +131,24 @@ VFS_SET(nfs_vfsops, nfs, MOUNT_NFS, VFCF_NETWORK); * to ensure that it is allocated to initialized data (.data not .bss). */ struct nfs_diskless nfs_diskless = { 0 }; +struct nfsv3_diskless nfsv3_diskless = { 0 }; int nfs_diskless_valid = 0; SYSCTL_INT(_vfs_nfs, OID_AUTO, diskless_valid, CTLFLAG_RD, &nfs_diskless_valid, 0, ""); SYSCTL_STRING(_vfs_nfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD, - nfs_diskless.root_hostnam, 0, ""); + nfsv3_diskless.root_hostnam, 0, ""); SYSCTL_OPAQUE(_vfs_nfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD, - &nfs_diskless.root_saddr, sizeof nfs_diskless.root_saddr, + &nfsv3_diskless.root_saddr, sizeof nfsv3_diskless.root_saddr, "%Ssockaddr_in", ""); SYSCTL_STRING(_vfs_nfs, OID_AUTO, diskless_swappath, CTLFLAG_RD, - nfs_diskless.swap_hostnam, 0, ""); + nfsv3_diskless.swap_hostnam, 0, ""); SYSCTL_OPAQUE(_vfs_nfs, OID_AUTO, diskless_swapaddr, CTLFLAG_RD, - &nfs_diskless.swap_saddr, sizeof nfs_diskless.swap_saddr, + &nfsv3_diskless.swap_saddr, sizeof nfsv3_diskless.swap_saddr, "%Ssockaddr_in",""); @@ -156,6 +157,7 @@ static int nfs_mountdiskless __P((char *, char *, int, struct sockaddr_in *, struct nfs_args *, struct proc *, struct vnode **, struct mount **)); +void nfs_convert_diskless __P((void)); static int nfs_iosize(nmp) struct nfsmount* nmp; @@ -173,6 +175,35 @@ static int nfs_iosize(nmp) return iosize; } +void nfs_convert_diskless() +{ + bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif, + sizeof(struct ifaliasreq)); + bcopy(&nfs_diskless.swap_args,&nfsv3_diskless.swap_args, + sizeof(struct nfs_args)); + nfsv3_diskless.swap_fhsize = NFSX_V2FH; + bcopy(nfs_diskless.swap_fh,nfsv3_diskless.swap_fh,NFSX_V2FH); + bcopy(&nfs_diskless.swap_saddr,&nfsv3_diskless.swap_saddr, + sizeof(struct sockaddr_in)); + bcopy(nfs_diskless.swap_hostnam,nfsv3_diskless.swap_hostnam, + MNAMELEN); + nfsv3_diskless.swap_nblks = nfs_diskless.swap_nblks; + bcopy(&nfs_diskless.swap_ucred, &nfsv3_diskless.swap_ucred, + sizeof(struct ucred)); + bcopy(&nfs_diskless.root_args,&nfsv3_diskless.root_args, + sizeof(struct nfs_args)); + nfsv3_diskless.root_fhsize = NFSX_V2FH; + bcopy(nfs_diskless.root_fh,nfsv3_diskless.root_fh,NFSX_V2FH); + bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr, + sizeof(struct sockaddr_in)); + bcopy(nfs_diskless.root_hostnam,nfsv3_diskless.root_hostnam, + MNAMELEN); + nfsv3_diskless.root_time = nfs_diskless.root_time; + bcopy(nfs_diskless.my_hostnam,nfsv3_diskless.my_hostnam, + MAXHOSTNAMELEN); + nfs_diskless_valid = 3; +} + /* * nfs statfs call */ @@ -330,7 +361,7 @@ nfs_mountroot(mp) struct mount *mp; { struct mount *swap_mp; - struct nfs_diskless *nd = &nfs_diskless; + struct nfsv3_diskless *nd = &nfsv3_diskless; struct socket *so; struct vnode *vp; struct proc *p = curproc; /* XXX */ @@ -345,6 +376,9 @@ nfs_mountroot(mp) if (time.tv_sec == 0) time.tv_sec = 1; + if (nfs_diskless_valid==1) + nfs_convert_diskless(); + /* * XXX splnet, so networks will receive... */ @@ -410,10 +444,7 @@ nfs_mountroot(mp) * Create the rootfs mount point. */ nd->root_args.fh = nd->root_fh; - /* - * If using nfsv3_diskless, replace NFSX_V2FH with nd->root_fhsize. - */ - nd->root_args.fhsize = NFSX_V2FH; + nd->root_args.fhsize = nd->root_fhsize; l = ntohl(nd->root_saddr.sin_addr.s_addr); sprintf(buf,"%ld.%ld.%ld.%ld:%s", (l >> 24) & 0xff, (l >> 16) & 0xff, @@ -439,11 +470,7 @@ nfs_mountroot(mp) * swap file can be on a different server from the rootfs. */ nd->swap_args.fh = nd->swap_fh; - /* - * If using nfsv3_diskless, replace NFSX_V2FH with - * nd->swap_fhsize. - */ - nd->swap_args.fhsize = NFSX_V2FH; + nd->swap_args.fhsize = nd->swap_fhsize; l = ntohl(nd->swap_saddr.sin_addr.s_addr); sprintf(buf,"%ld.%ld.%ld.%ld:%s", (l >> 24) & 0xff, (l >> 16) & 0xff,