New diskless boot code (uses RARP, bootparamd).
This commit is contained in:
parent
f5ef67b2d1
commit
0784b58d30
|
@ -38,7 +38,7 @@
|
|||
* partially based on:
|
||||
* libnetboot/rpc.c
|
||||
* @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp (LBL)
|
||||
* $Id: krpc_subr.c,v 1.1 1994/04/18 06:18:19 glass Exp $
|
||||
* $Id: krpc_subr.c,v 1.2 1994/06/13 15:28:55 gwr Exp $
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
|
@ -105,9 +105,17 @@ struct rpc_reply {
|
|||
|
||||
#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 the port number in host order, or ZERO if it couldn't.
|
||||
* Returns non-zero error on failure.
|
||||
*/
|
||||
int
|
||||
krpc_portmap(sa, prog, vers, portp)
|
||||
|
@ -134,10 +142,11 @@ krpc_portmap(sa, prog, vers, portp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
m = m_get(M_WAIT, MT_DATA);
|
||||
m = m_gethdr(M_WAIT, MT_DATA);
|
||||
if (m == NULL)
|
||||
return ENOBUFS;
|
||||
m->m_len = sizeof(*sdata);
|
||||
m->m_pkthdr.len = m->m_len;
|
||||
sdata = mtod(m, struct sdata *);
|
||||
|
||||
/* Do the RPC to get it. */
|
||||
|
@ -146,8 +155,8 @@ krpc_portmap(sa, prog, vers, portp)
|
|||
sdata->proto = htonl(IPPROTO_UDP);
|
||||
sdata->port = 0;
|
||||
|
||||
error = krpc_call(sa, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT,
|
||||
&m, sizeof(*rdata));
|
||||
error = krpc_call(sa, PMAPPROG, PMAPVERS,
|
||||
PMAPPROC_GETPORT, &m);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
@ -162,11 +171,10 @@ krpc_portmap(sa, prog, vers, portp)
|
|||
* Do a remote procedure call (RPC) and wait for its reply.
|
||||
*/
|
||||
int
|
||||
krpc_call(sa, prog, vers, func, data, want)
|
||||
krpc_call(sa, prog, vers, func, data)
|
||||
struct sockaddr *sa;
|
||||
u_long prog, vers, func;
|
||||
struct mbuf **data; /* input/output */
|
||||
int want; /* required response data length */
|
||||
{
|
||||
struct socket *so;
|
||||
struct sockaddr_in *sin;
|
||||
|
@ -224,14 +232,27 @@ krpc_call(sa, prog, vers, func, data, want)
|
|||
goto out;
|
||||
|
||||
/*
|
||||
* Build the RPC message header.
|
||||
* Prepend RPC message header.
|
||||
*/
|
||||
mhead = m_gethdr(M_WAIT, MT_DATA);
|
||||
m = *data;
|
||||
*data = NULL;
|
||||
#ifdef DIAGNOSTIC
|
||||
if ((m->m_flags & M_PKTHDR) == 0)
|
||||
panic("krpc_call: send data w/o pkthdr");
|
||||
if (m->m_pkthdr.len < m->m_len)
|
||||
panic("krpc_call: pkthdr.len not set");
|
||||
#endif
|
||||
mhead = m_prepend(m, sizeof(*call), M_WAIT);
|
||||
if (mhead == NULL) {
|
||||
error = ENOBUFS;
|
||||
goto out;
|
||||
}
|
||||
mhead->m_len = sizeof(*call);
|
||||
mhead->m_pkthdr.len += sizeof(*call);
|
||||
mhead->m_pkthdr.rcvif = NULL;
|
||||
|
||||
/*
|
||||
* Fill in the RPC header
|
||||
*/
|
||||
call = mtod(mhead, struct rpc_call *);
|
||||
bzero((caddr_t)call, sizeof(*call));
|
||||
call->rp_xid = ++xid; /* no need to put in network order */
|
||||
|
@ -244,34 +265,30 @@ krpc_call(sa, prog, vers, func, data, want)
|
|||
/* call->rp_verf = 0; */
|
||||
|
||||
/*
|
||||
* Prepend RPC header and setup packet header.
|
||||
*/
|
||||
for (len = 0, m = *data; m ; m = m->m_next)
|
||||
len += m->m_len;
|
||||
mhead->m_next = *data;
|
||||
mhead->m_pkthdr.len = mhead->m_len + len;
|
||||
mhead->m_pkthdr.rcvif = NULL;
|
||||
*data = NULL;
|
||||
|
||||
/*
|
||||
* Send it, repeatedly, until a reply is received, but
|
||||
* delay each send by an increasing amount. (10 sec. max)
|
||||
* When the send delay hits 10 sec. start complaining.
|
||||
* 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);
|
||||
error = sosend(so, nam, NULL, m, NULL, 0);
|
||||
if (error)
|
||||
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 (1 to 10) */
|
||||
if (timo < 10)
|
||||
/* Determine new timeout. */
|
||||
if (timo < MAX_RESEND_DELAY)
|
||||
timo++;
|
||||
else
|
||||
printf("RPC timeout for server 0x%X\n",
|
||||
printf("RPC timeout for server 0x%x\n",
|
||||
ntohl(sin->sin_addr.s_addr));
|
||||
|
||||
/*
|
||||
|
@ -310,30 +327,47 @@ krpc_call(sa, prog, vers, func, data, want)
|
|||
gotreply:
|
||||
|
||||
/*
|
||||
* Got the reply. Check and strip header.
|
||||
* Make result buffer contiguous.
|
||||
*/
|
||||
#ifdef DIAGNOSTIC
|
||||
if ((m->m_flags & M_PKTHDR) == 0)
|
||||
panic("krpc_call: received pkt w/o header?");
|
||||
#endif
|
||||
len = m->m_pkthdr.len;
|
||||
if (m->m_len < len) {
|
||||
m = m_pullup(m, len);
|
||||
if (m == NULL) {
|
||||
error = ENOBUFS;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
reply = mtod(m, struct rpc_reply *);
|
||||
|
||||
/*
|
||||
* Check RPC acceptance and status.
|
||||
*/
|
||||
if (reply->rp_astatus != 0) {
|
||||
error = reply->rp_u.rpu_errno;
|
||||
printf("rpc denied, error=%d\n", error);
|
||||
m_freem(m);
|
||||
goto out;
|
||||
}
|
||||
if ((error = reply->rp_u.rpu_ok.rp_rstatus) != 0) {
|
||||
printf("rpc status=%d\n", error);
|
||||
m_freem(m);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Strip RPC header
|
||||
*/
|
||||
len = sizeof(*reply);
|
||||
if (reply->rp_u.rpu_ok.rp_auth.rp_atype != 0) {
|
||||
len += ntohl(reply->rp_u.rpu_ok.rp_auth.rp_alen);
|
||||
len = (len + 3) & ~3; /* XXX? */
|
||||
}
|
||||
m_adj(m, len);
|
||||
if (m == NULL) {
|
||||
error = ENOBUFS;
|
||||
goto out;
|
||||
}
|
||||
if (m->m_len < want) {
|
||||
m = m_pullup(m, want);
|
||||
if (m == NULL) {
|
||||
error = ENOBUFS;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* result */
|
||||
*data = m;
|
||||
|
||||
|
@ -343,5 +377,3 @@ krpc_call(sa, prog, vers, func, data, want)
|
|||
soclose(so);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -10,25 +10,21 @@
|
|||
* 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 Adam Glass.
|
||||
* 4. The name of the Author may not be used to endorse or promote products
|
||||
* 3. The name of the authors may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Adam Glass ``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 Adam Glass 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.
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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.
|
||||
*
|
||||
* $Id: nfs_boot.c,v 1.2 1994/05/05 05:39:42 cgd Exp $
|
||||
* $Id: nfs_boot.c,v 1.3 1994/06/13 15:28:59 gwr Exp $
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
|
@ -43,7 +39,10 @@
|
|||
#include <sys/reboot.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/route.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/if_ether.h>
|
||||
|
||||
#include <nfs/rpcv2.h>
|
||||
#include <nfs/nfsv2.h>
|
||||
|
@ -54,347 +53,538 @@
|
|||
* Support for NFS diskless booting, specifically getting information
|
||||
* about where to boot from, what pathnames, etc.
|
||||
*
|
||||
* We currently support the RPC bootparam protocol.
|
||||
* This implememtation uses RARP and the bootparam RPC.
|
||||
* We are forced to implement RPC anyway (to get file handles)
|
||||
* so we might as well take advantage of it for bootparam too.
|
||||
*
|
||||
* We'd like to support BOOTP, but someone needs to write small kernel-ized
|
||||
* BOOTP client
|
||||
* The diskless boot sequence goes as follows:
|
||||
* (1) Get our interface address using RARP
|
||||
* (also save the address of the RARP server)
|
||||
* (2) Get our hostname using RPC/bootparam/whoami
|
||||
* (all boopararms RPCs to the RARP server)
|
||||
* (3) Get the root path using RPC/bootparam/getfile
|
||||
* (4) Get the root file handle using RPC/mountd
|
||||
* (5) Get the swap path using RPC/bootparam/getfile
|
||||
* (6) Get the swap file handle using RPC/mountd
|
||||
*
|
||||
* (This happens to be the way Sun does it too.)
|
||||
*/
|
||||
|
||||
/* from rfc951 to avoid bringing in cmu header file */
|
||||
/* bootparam RPC */
|
||||
static int bp_whoami(struct sockaddr_in *bpsin,
|
||||
struct in_addr *my_ip,
|
||||
struct in_addr *gw_ip);
|
||||
static int bp_getfile(struct sockaddr_in *bpsin, char *key,
|
||||
struct sockaddr_in *mdsin,
|
||||
char *servname, char *path);
|
||||
|
||||
#define UDP_BOOTPSERVER 67
|
||||
#define UDP_BOOTPCLIENT 68
|
||||
/* mountd RPC */
|
||||
static int md_mount(struct sockaddr_in *mdsin, char *path, u_char *fh);
|
||||
|
||||
#define BOOTP_REQUEST 1
|
||||
#define BOOTP_REPLY 2
|
||||
/* other helpers */
|
||||
static void get_path_and_handle(struct sockaddr_in *bpsin, char *key,
|
||||
struct nfs_dlmount *ndmntp);
|
||||
|
||||
/* rfc1048 tag bytes, (from rfc1497), only the semi-useful bits */
|
||||
#define TAG_PAD 0 /* [1] no data */
|
||||
#define TAG_SUBNET_MASK 1 /* [4] subnet mask bytes */
|
||||
#define TAG_GATEWAY_ADDR 3 /* [addr] gateway address */
|
||||
#define TAG_DNS_ADDR 6 /* [addr] dns name server */
|
||||
#define TAG_HOSTNAME 12 /* [n] hostname */
|
||||
#define TAG_BOOT_SIZE 13 /* [2] boot file size?*/
|
||||
#define TAG_DOMAIN_NAME 15 /* [n] domain name */
|
||||
#define TAG_SWAP_ADDR 16 /* [addr] swap server */
|
||||
#define TAG_ROOT_PATH 17 /* [n] root path */
|
||||
#define TAG_END 255
|
||||
|
||||
#define BOOTP_ROOT 1
|
||||
#define BOOTP_SWAP 2
|
||||
#define BOOTP_COMPLETED (BOOTP_ROOT|BOOTP_SWAP)
|
||||
|
||||
struct bootp_msg {
|
||||
u_char bpm_op; /* packet op code / message type */
|
||||
u_char bpm_htype; /* hardware address type */
|
||||
u_char bpm_hlen; /* hardware address length */
|
||||
u_char bpm_hops; /* bootp hops XXX ugly*/
|
||||
u_long bpm_xid; /* transaction ID */
|
||||
u_short bpm_secs; /* seconds elapsed since boot */
|
||||
u_short bpm_unused;
|
||||
struct in_addr bpm_ciaddr; /* client IP address */
|
||||
struct in_addr bpm_yiaddr; /* 'your' (client) IP address */
|
||||
struct in_addr bpm_siaddr; /* server IP address */
|
||||
struct in_addr bpm_giaddr; /* gateway IP address */
|
||||
u_char bpm_chaddr[16]; /* client hardware address */
|
||||
u_char bpm_sname[64]; /* optional server host name */
|
||||
u_char bpm_file[128]; /* boot file name */
|
||||
u_char bpm_vendor[64]; /* vendor-specific data */
|
||||
};
|
||||
|
||||
static u_char vend_rfc1048[4] = {
|
||||
99, 130, 83, 99,
|
||||
};
|
||||
|
||||
/*
|
||||
* Get a file handle given a path name.
|
||||
* Called with an empty nfs_diskless struct to be filled in.
|
||||
*/
|
||||
int nfs_boot_init(nd, procp)
|
||||
struct nfs_diskless *nd;
|
||||
struct proc *procp;
|
||||
{
|
||||
struct ifreq ireq;
|
||||
struct in_addr my_ip, srv_ip, gw_ip;
|
||||
struct sockaddr_in bp_sin;
|
||||
struct sockaddr_in *sin;
|
||||
struct ifnet *ifp;
|
||||
struct socket *so;
|
||||
int error, len;
|
||||
u_short port;
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* XXX time must be non-zero when we init the interface or else
|
||||
* the arp code will wedge... (Fixed in if_ether.c -gwr)
|
||||
*/
|
||||
if (time.tv_sec == 0)
|
||||
time.tv_sec = 1;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Find an interface, rarp for its ip address, stuff it, the
|
||||
* implied broadcast addr, and netmask into a nfs_diskless struct.
|
||||
*
|
||||
* This was moved here from nfs_vfsops.c because this procedure
|
||||
* would be quite different if someone decides to write (i.e.) a
|
||||
* BOOTP version of this file (might not use RARP, etc.) -gwr
|
||||
*/
|
||||
|
||||
/*
|
||||
* Find a network interface.
|
||||
* XXX - This should use the specified boot device.
|
||||
*/
|
||||
for (ifp = ifnet; ifp; ifp = ifp->if_next)
|
||||
if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
|
||||
break;
|
||||
if (ifp == NULL)
|
||||
panic("nfs_boot: no suitable interface");
|
||||
sprintf(ireq.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
|
||||
printf("nfs_boot: using network interface '%s'\n",
|
||||
ireq.ifr_name);
|
||||
|
||||
/*
|
||||
* Bring up the interface.
|
||||
*/
|
||||
if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0)
|
||||
panic("nfs_boot: socreate, error=%d", error);
|
||||
ireq.ifr_flags = IFF_UP;
|
||||
error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp);
|
||||
if (error) panic("nfs_boot: SIFFLAGS, error=%d", error);
|
||||
|
||||
/*
|
||||
* Do RARP for the interface address. Also
|
||||
* save the server address for bootparam RPC.
|
||||
*/
|
||||
if ((error = revarpwhoarewe(ifp, &srv_ip, &my_ip)) != 0)
|
||||
panic("revarp failed, error=%d", error);
|
||||
printf("nfs_boot: client=0x%x, server=0x%x\n",
|
||||
my_ip.s_addr, srv_ip.s_addr);
|
||||
|
||||
/*
|
||||
* Do enough of ifconfig(8) so that the chosen interface can
|
||||
* talk to the server(s). (also get brcast addr and netmask)
|
||||
*/
|
||||
/* Set interface address. */
|
||||
sin = (struct sockaddr_in *)&ireq.ifr_addr;
|
||||
sin->sin_len = sizeof(*sin);
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_addr.s_addr = my_ip.s_addr;
|
||||
error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp);
|
||||
if (error) panic("nfs_boot: set if addr, error=%d", error);
|
||||
|
||||
soclose(so);
|
||||
|
||||
/*
|
||||
* Get client name and gateway address.
|
||||
* RPC: bootparam/whoami
|
||||
*/
|
||||
bp_sin.sin_len = sizeof(bp_sin);
|
||||
bp_sin.sin_family = AF_INET;
|
||||
bp_sin.sin_addr.s_addr = srv_ip.s_addr;
|
||||
hostnamelen = MAXHOSTNAMELEN;
|
||||
error = bp_whoami(&bp_sin, /* input: where to send RPC */
|
||||
&my_ip, /* input: client IP */
|
||||
&gw_ip); /* ouptut: gateway IP */
|
||||
if (error)
|
||||
panic("nfs_boot: bootparam whoami, error=%d", error);
|
||||
printf("nfs_boot: hostname=%s\n", hostname);
|
||||
|
||||
#ifdef NFS_BOOT_GATEWAY
|
||||
/*
|
||||
* XXX - Server supplied gateway is usually bogus...
|
||||
* (At least for SunOS 4.1.3 servers it is.)
|
||||
* If your server is OK, you can turn on this option.
|
||||
*
|
||||
* If the gateway address is set, add a default route.
|
||||
* (The mountd RPCs may go across a gateway.)
|
||||
*/
|
||||
if (gw_ip.s_addr) {
|
||||
/* Destination: (default) */
|
||||
struct sockaddr_in dst, gw;
|
||||
bzero(&dst, sizeof(dst));
|
||||
dst.sin_len = sizeof(dst);
|
||||
dst.sin_family = AF_INET;
|
||||
/* Gateway: */
|
||||
bzero(&gw, sizeof(gw));
|
||||
gw.sin_len = sizeof(gw);
|
||||
gw.sin_family = AF_INET;
|
||||
gw.sin_addr.s_addr = gw_ip.s_addr;
|
||||
/* Netmask: */
|
||||
error = ifioctl(so, SIOCGIFNETMASK, (caddr_t)&ireq, procp);
|
||||
if (error) panic("nfs_boot: get netmask, error=%d", error);
|
||||
|
||||
/* add, dest, gw, mask, flags, 0 */
|
||||
error = rtrequest(RTM_ADD, &dst, &gw, &ifr.ifr_addr,
|
||||
(RTF_UP | RTF_GATEWAY), NULL);
|
||||
if (error)
|
||||
printf("nfs_boot: add route, error=%d\n", error);
|
||||
}
|
||||
#endif
|
||||
|
||||
get_path_and_handle(&bp_sin, "root", &nd->nd_root);
|
||||
get_path_and_handle(&bp_sin, "swap", &nd->nd_swap);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
get_path_and_handle(bpsin, key, ndmntp)
|
||||
struct sockaddr_in *bpsin; /* bootparam server */
|
||||
char *key; /* root or swap */
|
||||
struct nfs_dlmount *ndmntp; /* output */
|
||||
{
|
||||
char pathname[MAXPATHLEN];
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Get server:pathname for "key" (root or swap)
|
||||
* using RPC to bootparam/getfile
|
||||
*/
|
||||
error = bp_getfile(bpsin, key,
|
||||
&ndmntp->ndm_saddr,
|
||||
ndmntp->ndm_host,
|
||||
pathname);
|
||||
if (error)
|
||||
panic("nfs_boot: bootparam get %s: %d", key, error);
|
||||
printf("%s on %s:%s\n", key, ndmntp->ndm_host, pathname);
|
||||
|
||||
/*
|
||||
* Get file handle for "key" (root or swap)
|
||||
* using RPC to mountd/mount
|
||||
*/
|
||||
error = md_mount(&ndmntp->ndm_saddr,
|
||||
pathname,
|
||||
ndmntp->ndm_fh);
|
||||
if (error)
|
||||
panic("nfs_boot: mountd %s, error=%d", key, error);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get an mbuf with the given length, and
|
||||
* initialize the pkthdr length field.
|
||||
*/
|
||||
static struct mbuf *
|
||||
m_get_len(int msg_len)
|
||||
{
|
||||
struct mbuf *m;
|
||||
m = m_gethdr(M_WAIT, MT_DATA);
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
if (msg_len > MHLEN) {
|
||||
if (msg_len > MCLBYTES)
|
||||
panic("nfs_boot: msg_len > MCLBYTES");
|
||||
MCLGET(m, M_WAIT);
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
}
|
||||
m->m_len = msg_len;
|
||||
m->m_pkthdr.len = m->m_len;
|
||||
return (m);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* String representation for RPC.
|
||||
*/
|
||||
struct rpc_string {
|
||||
u_long len; /* length without null or padding */
|
||||
u_char data[4]; /* data (longer, of course) */
|
||||
/* data is padded to a long-word boundary */
|
||||
};
|
||||
/* Compute space used given string length. */
|
||||
#define RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3))
|
||||
|
||||
/*
|
||||
* Inet address in RPC messages
|
||||
* (Note, really four longs, NOT chars. Blech.)
|
||||
*/
|
||||
struct bp_inaddr {
|
||||
u_long atype;
|
||||
long addr[4];
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* RPC definitions for bootparamd
|
||||
* (XXX - move to a header file?)
|
||||
*/
|
||||
#define BOOTPARAM_PROG 100026
|
||||
#define BOOTPARAM_VERS 1
|
||||
#define BOOTPARAM_WHOAMI 1
|
||||
#define BOOTPARAM_GETFILE 2
|
||||
|
||||
|
||||
/*
|
||||
* RPC: bootparam/whoami
|
||||
* Given client IP address, get:
|
||||
* client name (hostname)
|
||||
* domain name (domainname)
|
||||
* gateway address
|
||||
*
|
||||
* Setting the hostname and domainname here may be somewhat
|
||||
* controvercial, but it is so easy to do it here. -gwr
|
||||
*/
|
||||
static int
|
||||
nfs_boot_getfh(sa, path, fhp)
|
||||
struct sockaddr *sa; /* server address */
|
||||
bp_whoami(struct sockaddr_in *bpsin,
|
||||
struct in_addr *my_ip,
|
||||
struct in_addr *gw_ip)
|
||||
{
|
||||
/* The RPC structures */
|
||||
struct bp_inaddr *bia;
|
||||
struct rpc_string *str;
|
||||
struct mbuf *m;
|
||||
struct sockaddr_in *sin;
|
||||
int error, msg_len;
|
||||
int cn_len, dn_len;
|
||||
u_char *p;
|
||||
|
||||
/*
|
||||
* Get message buffer of sufficient size.
|
||||
*/
|
||||
msg_len = sizeof(*bia);
|
||||
m = m_get_len(msg_len);
|
||||
if (m == NULL)
|
||||
return ENOBUFS;
|
||||
|
||||
/*
|
||||
* Build request message.
|
||||
*/
|
||||
/* client IP address */
|
||||
bia = mtod(m, struct bp_inaddr *);
|
||||
bia->atype = htonl(1);
|
||||
p = (u_char*)my_ip; /* ugh! */
|
||||
bia->addr[0] = htonl(*p); p++;
|
||||
bia->addr[1] = htonl(*p); p++;
|
||||
bia->addr[2] = htonl(*p); p++;
|
||||
bia->addr[3] = htonl(*p); p++;
|
||||
|
||||
/* RPC: bootparam/whoami */
|
||||
error = krpc_call((struct sockaddr *)bpsin,
|
||||
BOOTPARAM_PROG, BOOTPARAM_VERS,
|
||||
BOOTPARAM_WHOAMI, &m);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* Parse result message.
|
||||
*/
|
||||
msg_len = m->m_len;
|
||||
p = mtod(m, char *);
|
||||
|
||||
/* client name */
|
||||
if (msg_len < sizeof(*str))
|
||||
goto bad;
|
||||
str = (struct rpc_string *)p;
|
||||
cn_len = ntohl(str->len);
|
||||
if (msg_len < cn_len)
|
||||
goto bad;
|
||||
if (cn_len >= MAXHOSTNAMELEN)
|
||||
goto bad;
|
||||
bcopy(str->data, hostname, cn_len);
|
||||
hostname[cn_len] = '\0';
|
||||
hostnamelen = cn_len;
|
||||
p += RPC_STR_SIZE(cn_len);
|
||||
msg_len -= RPC_STR_SIZE(cn_len);
|
||||
|
||||
/* domain name */
|
||||
if (msg_len < sizeof(*str))
|
||||
goto bad;
|
||||
str = (struct rpc_string *)p;
|
||||
dn_len = ntohl(str->len);
|
||||
if (msg_len < dn_len)
|
||||
goto bad;
|
||||
if (dn_len >= MAXHOSTNAMELEN)
|
||||
goto bad;
|
||||
bcopy(str->data, domainname, dn_len);
|
||||
domainname[dn_len] = '\0';
|
||||
domainnamelen = dn_len;
|
||||
p += RPC_STR_SIZE(dn_len);
|
||||
msg_len -= RPC_STR_SIZE(dn_len);
|
||||
|
||||
/* gateway address */
|
||||
if (msg_len < sizeof(*bia))
|
||||
goto bad;
|
||||
bia = (struct bp_inaddr *)p;
|
||||
if (bia->atype != htonl(1))
|
||||
goto bad;
|
||||
p = (u_char*)gw_ip;
|
||||
*p++ = ntohl(bia->addr[0]);
|
||||
*p++ = ntohl(bia->addr[1]);
|
||||
*p++ = ntohl(bia->addr[2]);
|
||||
*p++ = ntohl(bia->addr[3]);
|
||||
goto out;
|
||||
|
||||
bad:
|
||||
printf("nfs_boot: bootparam_whoami: bad reply\n");
|
||||
error = EBADRPC;
|
||||
|
||||
out:
|
||||
m_freem(m);
|
||||
return(error);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RPC: bootparam/getfile
|
||||
* Given client name and file "key", get:
|
||||
* server name
|
||||
* server IP address
|
||||
* server pathname
|
||||
*/
|
||||
static int
|
||||
bp_getfile(struct sockaddr_in *bpsin, char *key,
|
||||
struct sockaddr_in *md_sin, char *serv_name, char *pathname)
|
||||
{
|
||||
struct rpc_string *str;
|
||||
struct mbuf *m;
|
||||
struct bp_inaddr *bia;
|
||||
struct sockaddr_in *sin;
|
||||
u_char *p, *q;
|
||||
int error, msg_len;
|
||||
int cn_len, key_len, sn_len, path_len;
|
||||
|
||||
/*
|
||||
* Get message buffer of sufficient size.
|
||||
*/
|
||||
cn_len = hostnamelen;
|
||||
key_len = strlen(key);
|
||||
msg_len = 0;
|
||||
msg_len += RPC_STR_SIZE(cn_len);
|
||||
msg_len += RPC_STR_SIZE(key_len);
|
||||
m = m_get_len(msg_len);
|
||||
if (m == NULL)
|
||||
return ENOBUFS;
|
||||
|
||||
/*
|
||||
* Build request message.
|
||||
*/
|
||||
p = mtod(m, u_char *);
|
||||
bzero(p, msg_len);
|
||||
/* client name (hostname) */
|
||||
str = (struct rpc_string *)p;
|
||||
str->len = htonl(cn_len);
|
||||
bcopy(hostname, str->data, cn_len);
|
||||
p += RPC_STR_SIZE(cn_len);
|
||||
/* key name (root or swap) */
|
||||
str = (struct rpc_string *)p;
|
||||
str->len = htonl(key_len);
|
||||
bcopy(key, str->data, key_len);
|
||||
|
||||
/* RPC: bootparam/getfile */
|
||||
error = krpc_call((struct sockaddr *)bpsin,
|
||||
BOOTPARAM_PROG, BOOTPARAM_VERS,
|
||||
BOOTPARAM_GETFILE, &m);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* Parse result message.
|
||||
*/
|
||||
p = mtod(m, u_char *);
|
||||
msg_len = m->m_len;
|
||||
|
||||
/* server name */
|
||||
if (msg_len < sizeof(*str))
|
||||
goto bad;
|
||||
str = (struct rpc_string *)p;
|
||||
sn_len = ntohl(str->len);
|
||||
if (msg_len < sn_len)
|
||||
goto bad;
|
||||
if (sn_len >= MNAMELEN)
|
||||
goto bad;
|
||||
bcopy(str->data, serv_name, sn_len);
|
||||
serv_name[sn_len] = '\0';
|
||||
p += RPC_STR_SIZE(sn_len);
|
||||
msg_len -= RPC_STR_SIZE(sn_len);
|
||||
|
||||
/* server IP address (mountd) */
|
||||
if (msg_len < sizeof(*bia))
|
||||
goto bad;
|
||||
bia = (struct bp_inaddr *)p;
|
||||
if (bia->atype != htonl(1))
|
||||
goto bad;
|
||||
sin = md_sin;
|
||||
sin->sin_len = sizeof(*sin);
|
||||
sin->sin_family = AF_INET;
|
||||
q = (u_char*) &sin->sin_addr;
|
||||
*q++ = ntohl(bia->addr[0]);
|
||||
*q++ = ntohl(bia->addr[1]);
|
||||
*q++ = ntohl(bia->addr[2]);
|
||||
*q++ = ntohl(bia->addr[3]);
|
||||
p += sizeof(*bia);
|
||||
msg_len -= sizeof(*bia);
|
||||
|
||||
/* server pathname */
|
||||
if (msg_len < sizeof(*str))
|
||||
goto bad;
|
||||
str = (struct rpc_string *)p;
|
||||
path_len = ntohl(str->len);
|
||||
if (msg_len < path_len)
|
||||
goto bad;
|
||||
if (path_len >= MAXPATHLEN)
|
||||
goto bad;
|
||||
bcopy(str->data, pathname, path_len);
|
||||
pathname[path_len] = '\0';
|
||||
goto out;
|
||||
|
||||
bad:
|
||||
printf("nfs_boot: bootparam_getfile: bad reply\n");
|
||||
error = EBADRPC;
|
||||
|
||||
out:
|
||||
m_freem(m);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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)
|
||||
struct sockaddr_in *mdsin; /* mountd server address */
|
||||
char *path;
|
||||
u_char *fhp;
|
||||
{
|
||||
/* The RPC structures */
|
||||
struct sdata {
|
||||
u_long len;
|
||||
u_char path[4]; /* longer, of course */
|
||||
} *sdata;
|
||||
struct rpc_string *str;
|
||||
struct rdata {
|
||||
u_long errno;
|
||||
u_char fh[NFS_FHSIZE];
|
||||
} *rdata;
|
||||
struct sockaddr_in *sin;
|
||||
struct mbuf *m;
|
||||
int error, mlen, slen;
|
||||
|
||||
/*
|
||||
* Validate address family.
|
||||
* Sorry, this is INET specific...
|
||||
*/
|
||||
if (sa->sa_family != AF_INET)
|
||||
return EAFNOSUPPORT;
|
||||
|
||||
slen = strlen(path);
|
||||
if (slen > (MLEN-4))
|
||||
slen = (MLEN-4);
|
||||
mlen = 4 + ((slen + 3) & ~3); /* XXX ??? */
|
||||
mlen = RPC_STR_SIZE(slen);
|
||||
|
||||
m = m_get(M_WAIT, MT_DATA);
|
||||
m = m_get_len(mlen);
|
||||
if (m == NULL)
|
||||
return ENOBUFS;
|
||||
m->m_len = mlen;
|
||||
sdata = mtod(m, struct sdata *);
|
||||
sdata->len = htonl(slen);
|
||||
bcopy(path, sdata->path, slen);
|
||||
str = mtod(m, struct rpc_string *);
|
||||
str->len = htonl(slen);
|
||||
bcopy(path, str->data, slen);
|
||||
|
||||
/* Do RPC to mountd. */
|
||||
error = krpc_call(sa, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
|
||||
&m, sizeof(*rdata));
|
||||
error = krpc_call((struct sockaddr *)mdsin,
|
||||
RPCPROG_MNT, RPCMNT_VER1,
|
||||
RPCMNT_MOUNT, &m);
|
||||
if (error)
|
||||
return error;
|
||||
return error; /* message already freed */
|
||||
|
||||
mlen = m->m_len;
|
||||
if (mlen < sizeof(*rdata))
|
||||
goto bad;
|
||||
rdata = mtod(m, struct rdata *);
|
||||
error = ntohl(rdata->errno);
|
||||
if (!error)
|
||||
bcopy(rdata->fh, fhp, NFS_FHSIZE);
|
||||
if (error)
|
||||
goto bad;
|
||||
bcopy(rdata->fh, fhp, NFS_FHSIZE);
|
||||
|
||||
/* Set port number for NFS use. */
|
||||
error = krpc_portmap((struct sockaddr *)mdsin,
|
||||
NFS_PROG, NFS_VER2,
|
||||
&mdsin->sin_port);
|
||||
goto out;
|
||||
|
||||
bad:
|
||||
error = EBADRPC;
|
||||
|
||||
out:
|
||||
m_freem(m);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive a bootp reply
|
||||
*/
|
||||
static int bootp_receive(so, msg)
|
||||
struct socket *so;
|
||||
struct bootp_msg *msg;
|
||||
{
|
||||
int error, rcvflag = 0;
|
||||
struct mbuf *m;
|
||||
struct uio auio;
|
||||
|
||||
auio.uio_resid = (1<<16);
|
||||
|
||||
error = soreceive(so, NULL, &auio, &m, NULL, &rcvflag);
|
||||
if (error)
|
||||
return error;
|
||||
if (m->m_len < sizeof(*msg)) {
|
||||
m = m_pullup(m, sizeof(*msg));
|
||||
if (m == NULL)
|
||||
return ENOBUFS;
|
||||
}
|
||||
if (((1<<16) - auio.uio_resid) != sizeof(*msg))
|
||||
return EMSGSIZE;
|
||||
m_copydata(m, 0, sizeof(*msg), (caddr_t) msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a bootp request
|
||||
*/
|
||||
static int bootp_send(so, nam, start, msg)
|
||||
struct socket *so;
|
||||
struct mbuf *nam;
|
||||
time_t start;
|
||||
struct bootp_msg *msg;
|
||||
{
|
||||
int error;
|
||||
struct mbuf *m;
|
||||
struct bootp_msg *bpm;
|
||||
|
||||
MGETHDR(m, M_WAIT, MT_DATA);
|
||||
m->m_len = MHLEN;
|
||||
if (m->m_len < sizeof(*msg)) {
|
||||
MCLGET(m, M_WAIT);
|
||||
m->m_len = min(sizeof(*msg), MCLBYTES);
|
||||
}
|
||||
m->m_pkthdr.len = sizeof(*msg);
|
||||
m->m_pkthdr.rcvif = NULL;
|
||||
bpm = mtod(m, struct bootp_msg *);
|
||||
bcopy((caddr_t) msg, (caddr_t) bpm, sizeof(*bpm));
|
||||
bpm->bpm_secs = time.tv_sec - start;
|
||||
|
||||
error = sosend(so, nam, NULL, m, NULL, 0);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in as much of the nfs_diskless struct using the results
|
||||
* of bootp requests.
|
||||
*/
|
||||
int nfs_boot_bootp(diskless, rpath, spath)
|
||||
struct nfs_diskless *diskless;
|
||||
char *rpath, *spath;
|
||||
{
|
||||
int error, bootp_timeout = 30, *opt_val, have, need;
|
||||
time_t start;
|
||||
struct socket *so;
|
||||
struct sockaddr *sa;
|
||||
struct sockaddr_in *sin;
|
||||
struct mbuf *m, *nam;
|
||||
struct timeval *tv;
|
||||
struct bootp_msg outgoing, incoming;
|
||||
|
||||
sa = &diskless->myif.ifra_broadaddr;
|
||||
if (sa->sa_family != AF_INET)
|
||||
return EAFNOSUPPORT;
|
||||
|
||||
/*
|
||||
* Create socket and set its recieve timeout.
|
||||
*/
|
||||
if (error = socreate(AF_INET, &so, SOCK_DGRAM, 0))
|
||||
return error;
|
||||
nam = m_get(M_WAIT, MT_SONAME);
|
||||
sin = mtod(nam, struct sockaddr_in *);
|
||||
bzero((caddr_t) sin, sizeof(*sin));
|
||||
sin->sin_len = sizeof(struct sockaddr_in);
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
sin->sin_port = htons(UDP_BOOTPCLIENT);
|
||||
nam->m_len = sizeof(struct sockaddr_in);
|
||||
if (error = sobind(so, nam))
|
||||
goto out;
|
||||
m = m_get(M_WAIT, MT_SOOPTS);
|
||||
tv = mtod(m, struct timeval *);
|
||||
m->m_len = sizeof(*tv);
|
||||
tv->tv_sec = 5;
|
||||
tv->tv_usec = 0;
|
||||
if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m)))
|
||||
goto out;
|
||||
m = m_get(M_WAIT, MT_SOOPTS);
|
||||
opt_val = mtod(m, int *);
|
||||
m->m_len = sizeof(*opt_val);
|
||||
*opt_val = 1;
|
||||
if ((error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m)))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Setup socket address for the server.
|
||||
*/
|
||||
nam = m_get(M_WAIT, MT_SONAME);
|
||||
sin = mtod(nam, struct sockaddr_in *);
|
||||
bcopy((caddr_t)sa, (caddr_t)sin, sizeof(struct sockaddr));
|
||||
sin->sin_port = htons(UDP_BOOTPSERVER);
|
||||
nam->m_len = sizeof(struct sockaddr);
|
||||
|
||||
bzero((caddr_t) &outgoing, sizeof(outgoing));
|
||||
outgoing.bpm_op = BOOTP_REQUEST;
|
||||
outgoing.bpm_htype = 1;
|
||||
outgoing.bpm_hlen = 6;
|
||||
outgoing.bpm_hops = 0;
|
||||
sin = (struct sockaddr_in *) &diskless->myif.ifra_addr;
|
||||
bcopy((caddr_t) &sin->sin_addr, (caddr_t) &outgoing.bpm_ciaddr, 4);
|
||||
bcopy((caddr_t) &sin->sin_addr, (caddr_t) &outgoing.bpm_yiaddr, 4);
|
||||
bcopy((caddr_t) vend_rfc1048, (caddr_t) outgoing.bpm_vendor, 4);
|
||||
outgoing.bpm_xid = time.tv_usec;
|
||||
outgoing.bpm_vendor[4] = TAG_END;
|
||||
start = time.tv_sec;
|
||||
|
||||
have = 0;
|
||||
while (bootp_timeout-- && (have != BOOTP_COMPLETED)) {
|
||||
if ((have & BOOTP_ROOT) == 0)
|
||||
strcpy(outgoing.bpm_file, "root");
|
||||
else if ((have & BOOTP_SWAP) == 0)
|
||||
strcpy(outgoing.bpm_file, "swap");
|
||||
if (error = bootp_send(so, nam, start, &outgoing)) {
|
||||
goto out;
|
||||
}
|
||||
error = bootp_receive(so, &incoming);
|
||||
if (error == EWOULDBLOCK)
|
||||
continue;
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (outgoing.bpm_xid != incoming.bpm_xid)
|
||||
continue;
|
||||
if ((have & BOOTP_ROOT) == 0) {
|
||||
sin = (struct sockaddr_in *) &diskless->root_saddr;
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_len = sizeof(*sin);
|
||||
bcopy((caddr_t) &incoming.bpm_siaddr,
|
||||
(caddr_t) &sin->sin_addr, sizeof(sin->sin_addr));
|
||||
strcpy(diskless->root_hostnam, incoming.bpm_sname);
|
||||
strcpy(rpath, incoming.bpm_file);
|
||||
have |= BOOTP_ROOT;
|
||||
outgoing.bpm_xid++;
|
||||
}
|
||||
else if ((have & BOOTP_SWAP) == 0) {
|
||||
sin = (struct sockaddr_in *) &diskless->swap_saddr;
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_len = sizeof(*sin);
|
||||
bcopy((caddr_t) &incoming.bpm_siaddr,
|
||||
(caddr_t) &sin->sin_addr, sizeof(sin->sin_addr));
|
||||
strcpy(diskless->swap_hostnam, incoming.bpm_sname);
|
||||
strcpy(spath, incoming.bpm_file);
|
||||
have |= BOOTP_SWAP;
|
||||
}
|
||||
}
|
||||
|
||||
if (have != BOOTP_COMPLETED)
|
||||
error = ETIMEDOUT;
|
||||
out:
|
||||
if (nam)
|
||||
m_freem(nam);
|
||||
soclose(so);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with a nfs_diskless struct in which a interface ip addr,
|
||||
* broadcast addr, and netmask have been specified.,
|
||||
*
|
||||
* The responsibility of this routine is to fill out the rest of the
|
||||
* struct using whatever mechanism.
|
||||
*
|
||||
*/
|
||||
int nfs_boot(diskless)
|
||||
struct nfs_diskless *diskless;
|
||||
{
|
||||
int error;
|
||||
u_short port;
|
||||
struct sockaddr_in *sin;
|
||||
char root_path[MAXPATHLEN], swap_path[MAXPATHLEN];
|
||||
|
||||
error = nfs_boot_bootp(diskless, root_path, swap_path);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
sin = (struct sockaddr_in *) &diskless->root_saddr;
|
||||
error = nfs_boot_getfh(&diskless->root_saddr, root_path,
|
||||
diskless->root_fh);
|
||||
if (error)
|
||||
return error;
|
||||
error = krpc_portmap(&diskless->root_saddr,
|
||||
NFS_PROG, NFS_VER2, &sin->sin_port);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
sin = (struct sockaddr_in *) &diskless->swap_saddr;
|
||||
error = nfs_boot_getfh(&diskless->swap_saddr, swap_path,
|
||||
diskless->swap_fh);
|
||||
if (error)
|
||||
return error;
|
||||
error = krpc_portmap(&diskless->swap_saddr,
|
||||
NFS_PROG, NFS_VER2, &sin->sin_port);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
printf("root on %x:%s\n", diskless->root_hostnam, root_path);
|
||||
printf("swap on %x:%s\n", diskless->swap_hostnam, swap_path);
|
||||
diskless->root_args.addr = &diskless->root_saddr;
|
||||
diskless->swap_args.addr = &diskless->swap_saddr;
|
||||
diskless->root_args.sotype = diskless->swap_args.sotype = SOCK_DGRAM;
|
||||
diskless->root_args.proto = diskless->swap_args.proto = 0;
|
||||
diskless->root_args.flags = diskless->swap_args.flags = 0;
|
||||
diskless->root_args.wsize = diskless->swap_args.wsize = NFS_WSIZE;
|
||||
diskless->root_args.rsize = diskless->swap_args.rsize = NFS_RSIZE;
|
||||
diskless->root_args.timeo = diskless->swap_args.timeo = NFS_TIMEO;
|
||||
diskless->root_args.retrans = diskless->swap_args.retrans =
|
||||
NFS_RETRANS;
|
||||
diskless->root_args.hostname = diskless->root_hostnam;
|
||||
diskless->swap_args.hostname = diskless->swap_hostnam;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
* SUCH DAMAGE.
|
||||
*
|
||||
* from: @(#)nfs_vfsops.c 8.3 (Berkeley) 1/4/94
|
||||
* $Id: nfs_vfsops.c,v 1.21 1994/06/08 11:37:03 mycroft Exp $
|
||||
* $Id: nfs_vfsops.c,v 1.22 1994/06/13 15:29:01 gwr Exp $
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
|
@ -83,19 +83,12 @@ struct vfsops nfs_vfsops = {
|
|||
nfs_init,
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure must be filled in by a primary bootstrap or bootstrap
|
||||
* server for a diskless/dataless machine. It is initialized below just
|
||||
* to ensure that it is allocated to initialized data (.data not .bss).
|
||||
*/
|
||||
struct nfs_diskless nfs_diskless = { 0 };
|
||||
|
||||
extern u_long nfs_procids[NFS_NPROCS];
|
||||
extern u_long nfs_prog, nfs_vers;
|
||||
void nfs_disconnect __P((struct nfsmount *));
|
||||
void nfsargs_ntoh __P((struct nfs_args *));
|
||||
static struct mount *nfs_mountdiskless __P((char *, char *, int,
|
||||
struct sockaddr_in *, struct nfs_args *, register struct vnode **));
|
||||
|
||||
static struct mount *
|
||||
nfs_mount_diskless __P((struct nfs_dlmount *, char *, int, struct vnode **));
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
@ -163,14 +156,9 @@ nfs_statfs(mp, sbp, p)
|
|||
}
|
||||
|
||||
/*
|
||||
* Mount a remote root fs via. nfs. This depends on the info in the
|
||||
* nfs_diskless structure that has been filled in properly by some primary
|
||||
* bootstrap.
|
||||
* It goes something like this:
|
||||
* - do enough of "ifconfig" by calling ifioctl() so that the system
|
||||
* can talk to the server
|
||||
* - If nfs_diskless.mygateway is filled in, use that address as
|
||||
* a default gateway.
|
||||
* Mount a remote root fs via. NFS. It goes like this:
|
||||
* - Call nfs_boot_init() to fill in the nfs_diskless struct
|
||||
* (using RARP, bootparam RPC, mountd RPC)
|
||||
* - hand craft the swap nfs vnode hanging off a fake mount point
|
||||
* if swdevt[0].sw_dev == NODEV
|
||||
* - build the rootfs mount point and call mountnfs() to do the rest.
|
||||
|
@ -178,88 +166,32 @@ nfs_statfs(mp, sbp, p)
|
|||
int
|
||||
nfs_mountroot()
|
||||
{
|
||||
register struct mount *mp;
|
||||
register struct nfs_diskless *nd = &nfs_diskless;
|
||||
struct socket *so;
|
||||
struct nfs_diskless nd;
|
||||
struct vattr attr;
|
||||
struct mount *mp;
|
||||
struct vnode *vp;
|
||||
struct proc *p = curproc; /* XXX */
|
||||
int error, i;
|
||||
struct proc *procp;
|
||||
struct ucred *cred;
|
||||
long n;
|
||||
int error;
|
||||
|
||||
procp = curproc; /* XXX */
|
||||
|
||||
/*
|
||||
* XXX time must be non-zero when we init the interface or else
|
||||
* the arp code will wedge...
|
||||
* Call nfs_boot_init() to fill in the nfs_diskless struct.
|
||||
* Side effect: Finds and configures a network interface.
|
||||
*/
|
||||
if (time.tv_sec == 0)
|
||||
time.tv_sec = 1;
|
||||
|
||||
#ifdef notyet
|
||||
/* Set up swap credentials. */
|
||||
proc0.p_ucred->cr_uid = ntohl(nd->swap_ucred.cr_uid);
|
||||
proc0.p_ucred->cr_gid = ntohl(nd->swap_ucred.cr_gid);
|
||||
if ((proc0.p_ucred->cr_ngroups = ntohs(nd->swap_ucred.cr_ngroups)) >
|
||||
NGROUPS)
|
||||
proc0.p_ucred->cr_ngroups = NGROUPS;
|
||||
for (i = 0; i < proc0.p_ucred->cr_ngroups; i++)
|
||||
proc0.p_ucred->cr_groups[i] = ntohl(nd->swap_ucred.cr_groups[i]);
|
||||
#endif
|
||||
bzero((caddr_t) &nd, sizeof(nd));
|
||||
nfs_boot_init(&nd, procp);
|
||||
|
||||
/*
|
||||
* Do enough of ifconfig(8) so that the critical net interface can
|
||||
* talk to the server.
|
||||
* Create the root mount point.
|
||||
*/
|
||||
if (error = socreate(nd->myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0))
|
||||
panic("nfs_mountroot: socreate: %d", error);
|
||||
if (error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, p))
|
||||
panic("nfs_mountroot: SIOCAIFADDR: %d", error);
|
||||
soclose(so);
|
||||
mp = nfs_mount_diskless(&nd.nd_root, "/", MNT_RDONLY, &vp);
|
||||
|
||||
/*
|
||||
* If the gateway field is filled in, set it as the default route.
|
||||
* Link it into the mount list.
|
||||
*/
|
||||
if (nd->mygateway.sin_len != 0) {
|
||||
struct sockaddr_in mask, sin;
|
||||
|
||||
bzero((caddr_t)&mask, sizeof(mask));
|
||||
sin = mask;
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_len = sizeof(sin);
|
||||
if (error = rtrequest(RTM_ADD, (struct sockaddr *)&sin,
|
||||
(struct sockaddr *)&nd->mygateway,
|
||||
(struct sockaddr *)&mask,
|
||||
RTF_UP | RTF_GATEWAY, (struct rtentry **)0))
|
||||
panic("nfs_mountroot: RTM_ADD: %d", error);
|
||||
}
|
||||
|
||||
/*
|
||||
* If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV):
|
||||
* Create a fake mount point just for the swap vnode so that the
|
||||
* swap file can be on a different server from the rootfs.
|
||||
*/
|
||||
if (swdevt[0].sw_dev == NODEV) {
|
||||
nd->swap_args.fh = (nfsv2fh_t *)nd->swap_fh;
|
||||
(void) nfs_mountdiskless(nd->swap_hostnam, "/swap", 0,
|
||||
&nd->swap_saddr, &nd->swap_args, &vp);
|
||||
|
||||
/*
|
||||
* Since the swap file is not the root dir of a file system,
|
||||
* hack it to a regular file.
|
||||
*/
|
||||
vp->v_type = VREG;
|
||||
vp->v_flag = 0;
|
||||
swapdev_vp = vp;
|
||||
VREF(vp);
|
||||
swdevt[0].sw_vp = vp;
|
||||
swdevt[0].sw_nblks = ntohl(nd->swap_nblks);
|
||||
} else if (bdevvp(swapdev, &swapdev_vp))
|
||||
panic("nfs_mountroot: can't setup swapdev_vp");
|
||||
|
||||
/*
|
||||
* Create the rootfs mount point.
|
||||
*/
|
||||
nd->root_args.fh = (nfsv2fh_t *)nd->root_fh;
|
||||
mp = nfs_mountdiskless(nd->root_hostnam, "/", MNT_RDONLY,
|
||||
&nd->root_saddr, &nd->root_args, &vp);
|
||||
|
||||
if (vfs_lock(mp))
|
||||
panic("nfs_mountroot: vfs_lock");
|
||||
TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
|
||||
|
@ -268,18 +200,64 @@ nfs_mountroot()
|
|||
vfs_unlock(mp);
|
||||
rootvp = vp;
|
||||
|
||||
|
||||
/* Get root attributes (for the time). */
|
||||
error = VOP_GETATTR(vp, &attr, procp->p_cred->pc_ucred, procp);
|
||||
if (error) panic("nfs_mountroot: getattr for root");
|
||||
n = attr.va_mtime.ts_sec; /* XXX - Always zero. Why? -gwr */
|
||||
printf(" root time: 0x%x\n", n);
|
||||
inittodr(n);
|
||||
|
||||
#ifdef notyet
|
||||
/* Set up swap credentials. */
|
||||
proc0.p_ucred->cr_uid = ntohl(nd.swap_ucred.cr_uid);
|
||||
proc0.p_ucred->cr_gid = ntohl(nd.swap_ucred.cr_gid);
|
||||
if ((proc0.p_ucred->cr_ngroups = ntohs(nd.swap_ucred.cr_ngroups)) >
|
||||
NGROUPS)
|
||||
proc0.p_ucred->cr_ngroups = NGROUPS;
|
||||
for (i = 0; i < proc0.p_ucred->cr_ngroups; i++)
|
||||
proc0.p_ucred->cr_groups[i] = ntohl(nd.swap_ucred.cr_groups[i]);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is not really an nfs issue, but it is much easier to
|
||||
* set hostname here and then let the "/etc/rc.xxx" files
|
||||
* mount the right /var based upon its preset value.
|
||||
* "Mount" the swap device.
|
||||
*
|
||||
* On a "dataless" configuration (swap on disk) we will have:
|
||||
* (swdevt[0].sw_dev != NODEV) identifying the swap device.
|
||||
*/
|
||||
bcopy(nd->my_hostnam, hostname, MAXHOSTNAMELEN);
|
||||
hostname[MAXHOSTNAMELEN - 1] = '\0';
|
||||
for (i = 0; i < MAXHOSTNAMELEN; i++)
|
||||
if (hostname[i] == '\0')
|
||||
break;
|
||||
hostnamelen = i;
|
||||
inittodr(ntohl(nd->root_time));
|
||||
if (swdevt[0].sw_dev != NODEV) {
|
||||
if (bdevvp(swapdev, &swapdev_vp))
|
||||
panic("nfs_mountroot: can't get swap vp for dev %d,%d",
|
||||
major(swdevt[0].sw_dev), minor(swdevt[0].sw_dev));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If swapping to an nfs node: (swdevt[0].sw_dev == NODEV)
|
||||
* Create a fake mount point just for the swap vnode so that the
|
||||
* swap file can be on a different server from the rootfs.
|
||||
*/
|
||||
mp = nfs_mount_diskless(&nd.nd_swap, "/swap", 0, &vp);
|
||||
|
||||
/*
|
||||
* Since the swap file is not the root dir of a file system,
|
||||
* hack it to a regular file.
|
||||
*/
|
||||
vp->v_type = VREG;
|
||||
vp->v_flag = 0;
|
||||
swapdev_vp = vp;
|
||||
VREF(vp);
|
||||
swdevt[0].sw_vp = vp;
|
||||
|
||||
/*
|
||||
* Find out how large the swap file is.
|
||||
*/
|
||||
error = VOP_GETATTR(vp, &attr, procp->p_cred->pc_ucred, procp);
|
||||
if (error) panic("nfs_mountroot: getattr for swap");
|
||||
n = (long) (attr.va_size / DEV_BSIZE);
|
||||
printf(" swap size: 0x%x (blocks)\n", n); /* XXX */
|
||||
swdevt[0].sw_nblks = n;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -287,60 +265,47 @@ nfs_mountroot()
|
|||
* Internal version of mount system call for diskless setup.
|
||||
*/
|
||||
static struct mount *
|
||||
nfs_mountdiskless(path, which, mountflag, sin, args, vpp)
|
||||
char *path;
|
||||
char *which;
|
||||
int mountflag;
|
||||
struct sockaddr_in *sin;
|
||||
struct nfs_args *args;
|
||||
register struct vnode **vpp;
|
||||
nfs_mount_diskless(ndmntp, mntname, mntflag, vpp)
|
||||
struct nfs_dlmount *ndmntp;
|
||||
char *mntname;
|
||||
int mntflag;
|
||||
struct vnode **vpp;
|
||||
{
|
||||
register struct mount *mp;
|
||||
register struct mbuf *m;
|
||||
register int error;
|
||||
struct nfs_args args;
|
||||
struct mount *mp;
|
||||
struct mbuf *m;
|
||||
int error;
|
||||
|
||||
/* Create the mount point. */
|
||||
mp = (struct mount *)malloc((u_long)sizeof(struct mount),
|
||||
M_MOUNT, M_NOWAIT);
|
||||
if (mp == NULL)
|
||||
panic("nfs_mountroot: %s mount malloc", which);
|
||||
panic("nfs_mountroot: malloc mount for %s", mntname);
|
||||
bzero((char *)mp, (u_long)sizeof(struct mount));
|
||||
mp->mnt_op = &nfs_vfsops;
|
||||
mp->mnt_flag = mountflag;
|
||||
mp->mnt_flag = mntflag;
|
||||
|
||||
/* Initialize mount args. */
|
||||
bzero((caddr_t) &args, sizeof(args));
|
||||
args.addr = (struct sockaddr *)&ndmntp->ndm_saddr;
|
||||
args.addrlen = args.addr->sa_len;
|
||||
args.sotype = SOCK_DGRAM;
|
||||
args.fh = (nfsv2fh_t *)ndmntp->ndm_fh;
|
||||
args.hostname = ndmntp->ndm_host;
|
||||
|
||||
/* Get mbuf for server sockaddr. */
|
||||
MGET(m, MT_SONAME, M_DONTWAIT);
|
||||
if (m == NULL)
|
||||
panic("nfs_mountroot: %s mount mbuf", which);
|
||||
bcopy((caddr_t)sin, mtod(m, caddr_t), sin->sin_len);
|
||||
m->m_len = sin->sin_len;
|
||||
nfsargs_ntoh(args);
|
||||
if (error = mountnfs(args, mp, m, which, path, vpp))
|
||||
panic("nfs_mountroot: mount %s on %s: %d", path, which, error);
|
||||
panic("nfs_mountroot: mget soname for %s", mntname);
|
||||
bcopy((caddr_t)args.addr, mtod(m, caddr_t),
|
||||
(m->m_len = args.addr->sa_len));
|
||||
|
||||
if (error = mountnfs(&args, mp, m, mntname, args.hostname, vpp))
|
||||
panic("nfs_mountroot: mount %s failed: %d", mntname);
|
||||
|
||||
return (mp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the integer fields of the nfs_args structure from net byte order
|
||||
* to host byte order. Called by nfs_mountroot() above.
|
||||
*/
|
||||
void
|
||||
nfsargs_ntoh(nfsp)
|
||||
register struct nfs_args *nfsp;
|
||||
{
|
||||
|
||||
NTOHL(nfsp->sotype);
|
||||
NTOHL(nfsp->proto);
|
||||
NTOHL(nfsp->flags);
|
||||
NTOHL(nfsp->wsize);
|
||||
NTOHL(nfsp->rsize);
|
||||
NTOHL(nfsp->timeo);
|
||||
NTOHL(nfsp->retrans);
|
||||
NTOHL(nfsp->maxgrouplist);
|
||||
NTOHL(nfsp->readahead);
|
||||
NTOHL(nfsp->leaseterm);
|
||||
NTOHL(nfsp->deadthresh);
|
||||
}
|
||||
|
||||
void
|
||||
nfs_decode_args(nmp, argp)
|
||||
struct nfsmount *nmp;
|
||||
|
|
|
@ -34,34 +34,26 @@
|
|||
* SUCH DAMAGE.
|
||||
*
|
||||
* from: @(#)nfsdiskless.h 8.1 (Berkeley) 6/10/93
|
||||
* $Id: nfsdiskless.h,v 1.5 1994/06/08 11:37:07 mycroft Exp $
|
||||
* $Id: nfsdiskless.h,v 1.6 1994/06/13 15:29:04 gwr Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
* Structure that must be initialized for a diskless nfs client.
|
||||
* This structure is used by nfs_mountroot() to set up the root and swap
|
||||
* vnodes plus do a partial ifconfig(8) and route(8) so that the critical net
|
||||
* interface can communicate with the server.
|
||||
* The primary bootstrap is expected to fill in the appropriate fields before
|
||||
* starting vmunix. Whether or not the swap area is nfs mounted is determined
|
||||
* vnodes plus do a partial ifconfig(8) and route(8) so that the critical
|
||||
* net interface can communicate with the server.
|
||||
* Whether or not the swap area is nfs mounted is determined
|
||||
* by the value in swdevt[0]. (equal to NODEV --> swap over nfs)
|
||||
* Currently only works for AF_INET protocols.
|
||||
* NB: All fields are stored in net byte order to avoid hassles with
|
||||
* client/server byte ordering differences.
|
||||
*/
|
||||
struct nfs_diskless {
|
||||
struct ifaliasreq myif; /* Default interface */
|
||||
struct sockaddr_in mygateway; /* Default gateway */
|
||||
struct nfs_args swap_args; /* Mount args for swap file */
|
||||
u_char swap_fh[NFS_FHSIZE]; /* Swap file's file handle */
|
||||
struct sockaddr_in swap_saddr; /* Address of swap server */
|
||||
char swap_hostnam[MNAMELEN]; /* Host name for mount pt */
|
||||
int swap_nblks; /* Size of server swap file */
|
||||
struct ucred swap_ucred; /* Swap credentials */
|
||||
struct nfs_args root_args; /* Mount args for root fs */
|
||||
u_char root_fh[NFS_FHSIZE]; /* File handle of root dir */
|
||||
struct sockaddr_in root_saddr; /* Address of root server */
|
||||
char root_hostnam[MNAMELEN]; /* Host name for mount pt */
|
||||
long root_time; /* Timestamp of root fs */
|
||||
char my_hostnam[MAXHOSTNAMELEN]; /* Client host name */
|
||||
struct nfs_dlmount {
|
||||
struct sockaddr_in ndm_saddr; /* Address of file server */
|
||||
char ndm_host[MNAMELEN]; /* Host name for mount pt */
|
||||
u_char ndm_fh[NFS_FHSIZE]; /* The file's file handle */
|
||||
};
|
||||
struct nfs_diskless {
|
||||
struct nfs_dlmount nd_root; /* Mount info for root */
|
||||
struct nfs_dlmount nd_swap; /* Mount info for swap */
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue