From 6ea632b44dfc80a6f21f88c5f84427f063bee17c Mon Sep 17 00:00:00 2001 From: tegge Date: Sun, 11 May 1997 18:05:39 +0000 Subject: 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/nfsclient/bootp_subr.c | 1319 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1319 insertions(+) create mode 100644 sys/nfsclient/bootp_subr.c (limited to 'sys/nfsclient/bootp_subr.c') diff --git a/sys/nfsclient/bootp_subr.c b/sys/nfsclient/bootp_subr.c new file mode 100644 index 0000000..837ca64 --- /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; +} -- cgit v1.1