freebsd-nq/sys/nfs/bootp_subr.c
2000-10-24 22:40:18 +00:00

1329 lines
31 KiB
C

/* $FreeBSD$ */
/*
* 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 "opt_bootp.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/sockio.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/uio.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <nfs/nfsdiskless.h>
#include <nfs/krpc.h>
#include <nfs/xdr_subs.h>
#define BOOTP_MIN_LEN 300 /* Minimum size of bootp udp packet */
/*
* 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(struct sockaddr_in *mdsin, char *path,
u_char *fhp, int *fhsizep,
struct nfs_args *args,struct proc *procp);
static int md_lookup_swap(struct sockaddr_in *mdsin,char *path,
u_char *fhp, int *fhsizep,
struct nfs_args *args,
struct proc *procp);
static int setfs(struct sockaddr_in *addr, char *path, char *p);
static int getdec(char **ptr);
static char *substr(char *a,char *b);
static void mountopts(struct nfs_args *args, char *p);
static int xdr_opaque_decode(struct mbuf **ptr, u_char *buf, int len);
static int xdr_int_decode(struct mbuf **ptr, int *iptr);
static void printip(char *prefix, struct in_addr addr);
#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
static int bootpc_call(struct bootp_packet *call,
struct bootp_packet *reply,
struct proc *procp);
static int bootpc_fakeup_interface(struct ifreq *ireq,
struct socket *so,
struct proc *procp);
static 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(struct sockaddr *sa,
struct sockaddr *ma)
{
if (sa == NULL) {
printf("(sockaddr *) <null>");
return;
}
switch (sa->sa_family) {
case AF_INET:
{
struct sockaddr_in *sin;
sin = (struct sockaddr_in *) sa;
printf("inet %x", ntohl(sin->sin_addr.s_addr));
if (ma != NULL) {
sin = (struct sockaddr_in *) ma;
printf(" mask %x", ntohl(sin->sin_addr.s_addr));
}
}
break;
case AF_LINK:
{
struct sockaddr_dl *sli;
int i;
sli = (struct sockaddr_dl *) sa;
printf("link %.*s ", sli->sdl_nlen, sli->sdl_data);
for (i = 0; i < sli->sdl_alen; i++) {
if (i > 0)
printf(":");
printf("%x", ((unsigned char *) LLADDR(sli))[i]);
}
}
break;
default:
printf("af%d", sa->sa_family);
}
}
void
bootpboot_p_ma(struct sockaddr *ma)
{
if (ma == NULL) {
printf("<null>");
return;
}
printf("%x", *(int *)ma);
}
void
bootpboot_p_rtentry(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", (int) rt->rt_rmx.rmx_expire);
printf(" %s%d\n", rt->rt_ifp->if_name, rt->rt_ifp->if_unit);
}
void
bootpboot_p_tree(struct radix_node *rn)
{
while (rn != NULL) {
if (rn->rn_b < 0) {
if ((rn->rn_flags & RNF_ROOT) != 0) {
} 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 != NULL;
ifp = TAILQ_NEXT(ifp,if_link)) {
for (ifa = TAILQ_FIRST(&ifp->if_addrhead);
ifa != NULL;
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
static int
bootpc_call(struct bootp_packet *call,
struct bootp_packet *reply, /* output */
struct proc *procp)
{
struct socket *so;
struct sockaddr_in *sin, sa;
struct uio auio;
struct sockopt sopt;
struct iovec aio;
struct timeval tv;
int error, on, len, rcvflg, secs, timo;
/*
* Create socket and set its recieve timeout.
*/
if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0,procp)))
goto out;
tv.tv_sec = 1;
tv.tv_usec = 0;
bzero(&sopt, sizeof sopt);
sopt.sopt_level = SOL_SOCKET;
sopt.sopt_name = SO_RCVTIMEO;
sopt.sopt_val = &tv;
sopt.sopt_valsize = sizeof tv;
if ((error = sosetopt(so, &sopt)) != 0)
goto out;
/*
* Enable broadcast.
*/
on = 1;
sopt.sopt_val = &on;
sopt.sopt_valsize = sizeof on;
sopt.sopt_name = SO_BROADCAST;
if ((error = sosetopt(so, &sopt)) != 0)
goto out;
/*
* Bind the local endpoint to a bootp client port.
*/
sin = &sa;
bzero(sin, sizeof *sin);
sin->sin_len = sizeof(*sin);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = INADDR_ANY;
sin->sin_port = htons(IPPORT_BOOTPC);
error = sobind(so, (struct sockaddr *)sin, procp);
if (error) {
printf("bind failed\n");
goto out;
}
/*
* Setup socket address for the server.
*/
sin = &sa;
bzero(sin, sizeof *sin);
sin->sin_len = sizeof(*sin);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = INADDR_BROADCAST;
sin->sin_port = htons(IPPORT_BOOTPS);
/*
* 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, (struct sockaddr *)sin, &auio, NULL,
NULL, 0, procp);
if (error) {
printf("bootpc_call: sosend: %d state %08x\n",
error, (int)so->so_state);
goto out;
}
/* Determine new timeout. */
if (timo < MAX_RESEND_DELAY)
timo++;
else
printf("BOOTP timeout for server 0x%lx\n",
(u_long)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;
/* Do we have the required number of bytes ? */
if (len < BOOTP_MIN_LEN)
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:
soclose(so);
return error;
}
static 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;
}
static 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 != 0) {
printf("nfs_boot: del default route, error=%d\n", error);
return error;
}
/*
* 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 != 0)
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 != 0)
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 != 0)
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 != 0) {
printf("nfs_boot: add net route, error=%d\n", error);
return error;
}
return 0;
}
static int
setfs(struct sockaddr_in *addr, char *path, char *p)
{
unsigned int ip;
int val;
ip = 0;
if (((val = getdec(&p)) < 0) || (val > 255))
return 0;
ip = val << 24;
if (*p != '.')
return 0;
p++;
if (((val = getdec(&p)) < 0) || (val > 255))
return 0;
ip |= (val << 16);
if (*p != '.')
return 0;
p++;
if (((val = getdec(&p)) < 0) || (val > 255))
return 0;
ip |= (val << 8);
if (*p != '.')
return 0;
p++;
if (((val = getdec(&p)) < 0) || (val > 255))
return 0;
ip |= val;
if (*p != ':')
return 0;
p++;
addr->sin_addr.s_addr = htonl(ip);
addr->sin_len = sizeof(struct sockaddr_in);
addr->sin_family = AF_INET;
strncpy(path, p, MNAMELEN - 1);
return 1;
}
static int
getdec(char **ptr)
{
char *p;
int ret;
p = *ptr;
ret = 0;
if ((*p < '0') || (*p > '9'))
return -1;
while ((*p >= '0') && (*p <= '9')) {
ret = ret * 10 + (*p - '0');
p++;
}
*ptr = p;
return ret;
}
static char *
substr(char *a, char *b)
{
char *loc1;
char *loc2;
while (*a != '\0') {
loc1 = a;
loc2 = b;
while (*loc1 == *loc2++) {
if (*loc1 == '\0')
return 0;
loc1++;
if (*loc2 == '\0')
return loc1;
}
a++;
}
return 0;
}
static void
mountopts(struct nfs_args *args, char *p)
{
char *tmp;
args->flags = NFSMNT_RSIZE | NFSMNT_WSIZE | NFSMNT_RESVPORT;
args->sotype = SOCK_DGRAM;
if ((tmp = (char *)substr(p, "rsize=")))
args->rsize = getdec(&tmp);
if ((tmp = (char *)substr(p, "wsize=")))
args->wsize = getdec(&tmp);
if ((tmp = (char *)substr(p, "intr")))
args->flags |= NFSMNT_INT;
if ((tmp = (char *)substr(p, "soft")))
args->flags |= NFSMNT_SOFT;
if ((tmp = (char *)substr(p, "noconn")))
args->flags |= NFSMNT_NOCONN;
if ((tmp = (char *)substr(p, "tcp")))
args->sotype = SOCK_STREAM;
}
static int
xdr_opaque_decode(struct mbuf **mptr, u_char *buf, int len)
{
struct mbuf *m;
int alignedlen;
m = *mptr;
alignedlen = ( len + 3 ) & ~3;
if (m->m_len < alignedlen) {
m = m_pullup(m, alignedlen);
if (m == NULL) {
*mptr = NULL;
return EBADRPC;
}
}
bcopy(mtod(m, u_char *), buf, len);
m_adj(m, alignedlen);
*mptr = m;
return 0;
}
static int
xdr_int_decode(struct mbuf **mptr, int *iptr)
{
u_int32_t i;
if (xdr_opaque_decode(mptr, (u_char *) &i, sizeof(u_int32_t)) != 0)
return EBADRPC;
*iptr = fxdr_unsigned(u_int32_t, i);
return 0;
}
static void
printip(char *prefix, struct in_addr addr)
{
unsigned int ip;
ip = ntohl(addr.s_addr);
printf("%s is %d.%d.%d.%d\n", prefix,
ip >> 24, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255);
}
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,ncode,len;
int j;
char *p;
unsigned int ip;
struct sockaddr_in myaddr;
struct sockaddr_in netmask;
struct sockaddr_in gw;
int gotgw;
int gotnetmask;
int gotrootpath;
int gotswappath;
char lookup_path[24];
#define EALEN 6
struct ifaddr *ifa;
struct sockaddr_dl *sdl;
char *delim;
struct nfsv3_diskless *nd;
struct proc *procp;
gotgw = 0;
gotnetmask = 0;
gotrootpath = 0;
gotswappath = 0;
sdl = NULL;
nd = &nfsv3_diskless;
procp = curproc;
/*
* If already filled in, don't touch it here
*/
if (nfs_diskless_valid != 0)
return;
/*
* Wait until arp entries can be handled.
*/
while (time_second == 0)
tsleep(&time_second, PZERO + 8, "arpkludge", 10);
/*
* Find a network interface.
*/
#ifdef BOOTP_WIRED_TO
printf("bootpc_init: wired to interface '%s'\n",
__XSTRING(BOOTP_WIRED_TO));
#endif
bzero(&ireq, sizeof(ireq));
for (ifp = TAILQ_FIRST(&ifnet);
ifp != NULL;
ifp = TAILQ_NEXT(ifp,if_link))
{
snprintf(ireq.ifr_name, sizeof(ireq.ifr_name),
"%s%d", ifp->if_name, ifp->if_unit);
#ifdef BOOTP_WIRED_TO
if (strcmp(ireq.ifr_name, __XSTRING(BOOTP_WIRED_TO)) == 0)
break;
#else
if ((ifp->if_flags &
(IFF_LOOPBACK | IFF_POINTOPOINT)) == 0)
break;
#endif
}
if (ifp == NULL)
panic("bootpc_init: no suitable interface");
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 != NULL;
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 == NULL)
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);
printf("bootpc hw address is ");
delim = "";
for (j = 0; j < sdl->sdl_alen; j++) {
printf("%s%x",delim,((unsigned char *)LLADDR(sdl))[j]);
delim = ":";
}
printf("\n");
#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);
j = 0;
call.vend[j++] = 99;
call.vend[j++] = 130;
call.vend[j++] = 83;
call.vend[j++] = 99;
/*
* We send an RFC 1533 "Maximum DHCP Message Size" option, saying we
* can do 1200 bytes. If we don't ISC DHCPD will limit the answer to
* 64 bytes and root/swap and similar will be dropped.
*/
call.vend[j++] = 57;
call.vend[j++] = 2;
call.vend[j++] = 1200 / 256;
call.vend[j++] = 1200 % 256;
call.vend[j++] = 255;
call.secs = 0;
call.flags = htons(0x8000); /* We need an broadcast answer */
error = bootpc_call(&call, &reply, procp);
if (error != 0) {
#ifdef BOOTP_NFSROOT
panic("BOOTP call failed");
#endif
return;
}
bzero(&myaddr, sizeof(myaddr));
bzero(&netmask, sizeof(netmask));
bzero(&gw, sizeof(gw));
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;
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);
nd->swap_saddr.sin_len = sizeof(gw);
nd->swap_saddr.sin_family = AF_INET;
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);
myaddr.sin_addr = reply.yiaddr;
ip = ntohl(myaddr.sin_addr.s_addr);
snprintf(lookup_path, sizeof(lookup_path), "swap.%d.%d.%d.%d",
ip >> 24, (ip >> 16) & 255 ,(ip >> 8) & 255 ,ip & 255);
printip("My ip address", myaddr.sin_addr);
printip("Server ip address", reply.siaddr);
gw.sin_addr = reply.giaddr;
printip("Gateway ip address", reply.giaddr);
if (reply.sname[0] != '\0')
printf("Server name is %s\n", reply.sname);
if (reply.file[0] != '\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;
ncode = reply.vend[j];
while (j < sizeof(reply.vend)) {
code = reply.vend[j] = ncode;
if (code == 255)
break;
if (code == 0) {
j++;
continue;
}
len = reply.vend[j + 1];
j += 2;
if (len + j >= sizeof(reply.vend)) {
printf("Truncated field");
break;
}
ncode = reply.vend[j + len];
reply.vend[j + len] = '\0';
p = &reply.vend[j];
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;
printip("Subnet mask", netmask.sin_addr);
break;
case 6: /* Domain Name servers. Unused */
case 16: /* Swap server IP address. unused */
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);
printip("Router", gw.sin_addr);
gotgw = 1;
}
break;
case 17:
if (setfs(&nd->root_saddr,
nd->root_hostnam, p)) {
printf("rootfs is %s\n", p);
gotrootpath = 1;
} else
panic("Failed to set rootfs to %s", p);
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 (setfs(&nd->swap_saddr,
nd->swap_hostnam, p)) {
gotswappath = 1;
printf("swapfs is %s\n", p);
} else
panic("Failed to set swapfs to %s", p);
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;
case 130: /* root mount options */
mountopts(&nd->root_args, p);
break;
case 131: /* swap mount options */
mountopts(&nd->swap_args, p);
break;
default:
printf("Ignoring field type %d\n",code);
}
j += len;
}
}
if (gotswappath == 0)
nd->swap_nblks = 0;
#ifdef BOOTP_NFSROOT
if (gotrootpath == 0)
panic("bootpc: No root path offered");
#endif
if (gotnetmask == 0) {
if (IN_CLASSA(ntohl(myaddr.sin_addr.s_addr)))
netmask.sin_addr.s_addr = htonl(IN_CLASSA_NET);
else if (IN_CLASSB(ntohl(myaddr.sin_addr.s_addr)))
netmask.sin_addr.s_addr = htonl(IN_CLASSB_NET);
else
netmask.sin_addr.s_addr = htonl(IN_CLASSC_NET);
}
if (gotgw == 0) {
/* 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
if (gotrootpath != 0) {
error = md_mount(&nd->root_saddr, nd->root_hostnam,
nd->root_fh, &nd->root_fhsize,
&nd->root_args, procp);
if (error != 0)
panic("nfs_boot: mountd root, error=%d", error);
if (gotswappath != 0) {
error = md_mount(&nd->swap_saddr,
nd->swap_hostnam,
nd->swap_fh, &nd->swap_fhsize,
&nd->swap_args, procp);
if (error != 0)
panic("nfs_boot: mountd swap, error=%d",
error);
error = md_lookup_swap(&nd->swap_saddr,
lookup_path,
nd->swap_fh, &nd->swap_fhsize,
&nd->swap_args, procp);
if (error != 0)
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(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 == 0) {
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 == 0) {
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 != 0)
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 != 0)
return error; /* message already freed */
#ifdef BOOTP_NFSV3
}
#endif
if (xdr_int_decode(&m, &error) != 0 || error != 0)
goto bad;
if ((args->flags & NFSMNT_NFSV3) != 0) {
if (xdr_int_decode(&m, fhsizep) != 0 ||
*fhsizep > NFSX_V3FHMAX ||
*fhsizep <= 0)
goto bad;
} else
*fhsizep = NFSX_V2FH;
if (xdr_opaque_decode(&m, fhp, *fhsizep) != 0)
goto bad;
if (args->flags & NFSMNT_NFSV3) {
if (xdr_int_decode(&m, &authcount) != 0)
goto bad;
authunixok = 0;
if (authcount < 0 || authcount > 100)
goto bad;
while (authcount > 0) {
if (xdr_int_decode(&m, &authver) != 0)
goto bad;
if (authver == RPCAUTH_UNIX)
authunixok = 1;
authcount--;
}
if (authunixok == 0)
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(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;
union {
u_int32_t v2[17];
u_int32_t v3[21];
} fattribs;
m = m_get(M_WAIT,MT_DATA);
if (m == NULL)
return ENOBUFS;
if ((args->flags & NFSMNT_NFSV3) != 0) {
*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 == NULL) {
error = ENOBUFS;
goto out;
}
/* Do RPC to nfsd. */
if ((args->flags & NFSMNT_NFSV3) != 0)
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 != 0)
return error; /* message already freed */
if (xdr_int_decode(&m, &status) != 0)
goto bad;
if (status != 0) {
error = ENOENT;
goto out;
}
if ((args->flags & NFSMNT_NFSV3) != 0) {
if (xdr_int_decode(&m, fhsizep) != 0 ||
*fhsizep > NFSX_V3FHMAX ||
*fhsizep <= 0)
goto bad;
} else
*fhsizep = NFSX_V2FH;
if (xdr_opaque_decode(&m, fhp, *fhsizep) != 0)
goto bad;
if ((args->flags & NFSMNT_NFSV3) != 0) {
if (xdr_int_decode(&m, &attribs_present) != 0)
goto bad;
if (attribs_present != 0) {
if (xdr_opaque_decode(&m, (u_char *) &fattribs.v3,
sizeof(u_int32_t) * 21) != 0)
goto bad;
size = fxdr_unsigned(u_int32_t, fattribs.v3[6]);
}
} else {
if (xdr_opaque_decode(&m,(u_char *) &fattribs.v2,
sizeof(u_int32_t) * 17) != 0)
goto bad;
size = fxdr_unsigned(u_int32_t, fattribs.v2[5]);
}
if (nfsv3_diskless.swap_nblks == 0 && 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;
}