/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:rvd.c 12.0$ */
/* $ACIS:rvd.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/netinet/RCS/rvd.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:rvd.c 12.0$";
#endif


#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid_rvd_c = "$Header:rvd.c 12.0$";
#endif	lint

/* rvd.c May 1982 */

/* remote virtual disk protocol */
/* Written by Michael Greenwald, May  1982 */
/* Modified for 4.2 by Larry Allen Nov 1983 */


#include "../h/param.h"
#include "../h/mbuf.h"
#include "../h/protosw.h"
#include "../h/socket.h"
#include "../h/socketvar.h"
#include "../h/ioctl.h"
#include "../h/errno.h"
#ifdef ibm032
#include "../machine/mmu.h"
#define SBMODEL
#ifdef SBMODEL
#define spl_high()	_spl2()		/* at ibm032 level 2 */
#endif
#undef SBMODEL
#endif ibm032
#include "vd.h"
#include "../machine/pte.h"
#include "../net/route.h"
#include "../net/raw_cb.h"
#include "../netinet/in.h"
#include "../netinet/in_pcb.h"
#include "../netinet/in_systm.h"
#include "../netinet/ip.h"
#include "../netinet/ip_var.h"
#include "../netinet/ip_icmp.h"
#include "../h/buf.h"
#include "../machineio/vdreg.h"

#ifdef ibm032
u_long swapw();			/* machine language version of swapw */
				/* in loutil.s, is faster than macro */
caddr_t real_buf_addr();
#endif


#ifdef vax
#define swaph(n) n
#define swapw(n) n
#endif

/* Save macros for porting to other than VAX/IBM.
 */
#ifdef notdef
#define swaph(n) ((((n)>>8)&0xff) | (((n)&0xff) << 8))
#define swapw(n) (swaph((n)>>16) | (swaph((n)) << 16))
#endif

u_long rvdcksum();
extern int vddflg;
extern struct	vd_longstat	vd_longstat;

static struct sockproto rvdproto = { AF_INET, IPPROTO_RVD };
static struct sockaddr_in rvdsrc = { AF_INET };
static struct sockaddr_in rvddst = { AF_INET };


rvd_input(mp)
	register struct mbuf *mp;
{
	register struct	ip *ip;
	register struct	rvd *p;
	int	hlen;
	u_char	r_type;
	u_long	holdcksum;
	u_long	length;

/* Make sure all of header is in first mbuf */

	if ((mp->m_len < (sizeof (struct rvdhdr) +
	    sizeof(struct ip))) && (mp = m_pullup(mp, sizeof (struct rvdhdr) +
	    sizeof(struct ip))) == 0) {
		printf("RVD: short packet");
		goto bad_format;
	}

	ip = mtod(mp, struct ip *);
	hlen = (ip->ip_hl << 2);
	if (hlen > (sizeof (struct ip)))
		ip_stripoptions(ip, (struct mbuf *)0);

	length = ip->ip_len;
	mp->m_off += hlen;	/* Ignore IP header */
	mp->m_len -= hlen;

/* Get pointer to start of RVD packet */

	p = mtod(mp,struct rvd *);

#ifdef RVDDBUG
	if (vddflg) printf("rvdin t%d\n",p->hdrun.allpkt.rvd_type);
#endif

	vd_longstat.vdstat.pkts_rcvd++;

	if (p->hdrun.allpkt.version != RVDVERSION) {
#ifdef RVDDBUG
	    if (vddflg) printf("rvd incompatible version rcvd\n");
#endif
	    vd_longstat.vdstat.bad_vers++;
	    vderror(p->drive,RVDEBVER,p->index,p->nonce,NULL,0);
	    goto clean;
	}

	/* Ignore broadcast packets.
	 */
	if (ip->ip_dst.s_addr == INADDR_ANY)
	    goto bad_format;

	holdcksum = ntohl(p->cksum);
	p->cksum = 0;
	if (holdcksum != (p->cksum = rvdcksum(mp))){
	    vd_longstat.vdstat.bad_cksum++;
#ifdef RVDDBUG
	    if (vddflg) printf("rvd cksm %x %x\n",holdcksum,p->cksum);
#endif
	    goto clean;

	}

	r_type = p->hdrun.allpkt.rvd_type;

	switch(r_type) {

case RVDSPACK:
	p->drive = swapw(p->drive);	/* Convert to VAX byte order */
	p->nonce = swapw(p->nonce);
	p->index = swapw(p->index);
	p->res_un.vd_uid = swapw(p->res_un.vd_uid);

	if ((length < RVDSACKSZ) || (p->drive > NVD)) goto bad_format;
	if ((mp->m_len < RVDSACKSZ) && (mp = m_pullup(mp, RVDSACKSZ)) == 0) {
		printf("RVD: short packet");
		goto bad_format;
	}

	p = mtod(mp,struct rvd *);

	vdspack(p->drive,p->index,p->nonce,
		(u_long)(ntohl(p->pkt_dpnd.spinack.nblocks)),
		(u_short)(ntohs(p->pkt_dpnd.spinack.burst)),
		(u_short)(ntohs(p->pkt_dpnd.spinack.qlen)),
		(u_short)(p->pkt_dpnd.spinack.wb_factor), p->res_un.vd_uid);
	break;

case RVDERROR:
	p->drive = swapw(p->drive);	/* Convert to VAX byte order */
	p->nonce = swapw(p->nonce);
	p->index = swapw(p->index);

	vderror(p->drive,p->hdrun.errhdr.etype,p->index,p->nonce,
		&(p->pkt_dpnd.edata[0]),(mp->m_len - sizeof(struct rvde)));
						/* Note that we only return
						   data from first mbuf!! */
	break;

case RVDACK:
	p->drive = swapw(p->drive);	/* Convert to VAX byte order */
	p->nonce = swapw(p->nonce);
	p->index = swapw(p->index);

	/* Don't have to check for length, because same size as header */
	if (p->drive > NVD) goto bad_format;
	vdack(p->drive,p->index,p->nonce);
	break;

case RVDBLOCK:
	p->drive = swapw(p->drive);	/* Convert to VAX byte order */
	p->nonce = swapw(p->nonce);
	p->index = swapw(p->index);

	if ((length < RVDBLCKSZ) && (length != sizeof(struct rvdb))) {
		printf("bad rvdblock length: length: %d\n", length);
		goto bad_format;
	}
	if (p->drive > NVD) {
		printf("bad rvdblock: invalid driveno: %d\n", p->drive);
		goto bad_format;
	}
	if ((mp->m_len < (sizeof (struct rvdb))) &&
	    (mp = m_pullup(mp, sizeof (struct rvdb))) == 0) {
		printf("RVD: short packet");
		goto bad_format;
	}

	p = mtod(mp,struct rvd *);

	mp->m_len -= sizeof(struct rvdb);	/* Adjust mbuf so that  */
	mp->m_off += sizeof(struct rvdb);	/* rvd_copy_d will work */

	length -= sizeof(struct rvdb);
	vdblock(p->drive, p->index,p->nonce,
		(u_long)(ntohl(p->pkt_dpnd.block.blockn)),
		length/RVDDSIZE,
 		(u_long)(ntohl(p->pkt_dpnd.block.status)),
		(caddr_t)mp);
	break;

case RVDWACK:
	p->drive = swapw(p->drive);	/* Convert to VAX byte order */
	p->nonce = swapw(p->nonce);
	p->index = swapw(p->index);

	if (length < RVDWACKSZ) {
		printf("bad rvdwack length: length: %d\n", length);
		goto bad_format;
	}
	if (p->drive > NVD) {
		printf("bad rvdblock: invalid driveno: %d\n", p->drive);
		goto bad_format;
	}
	if ((mp->m_len < RVDWACKSZ) && 
	    (mp = m_pullup(mp, RVDWACKSZ)) == 0) {
		printf("RVD: short packet");
		goto bad_format;
	}

	p = mtod(mp,struct rvd *);

	vdwack(p->drive, p->index, p->nonce,
		(u_long)(ntohl(p->pkt_dpnd.writeack.blockn)),
		(u_long)(ntohl(p->pkt_dpnd.writeack.status)),
		(u_long)(ntohl(p->pkt_dpnd.writeack.bcount)));
	break;

case RVDSSTAT:
case RVDHOSTR:
	break;

default:
	mp->m_off -= hlen;		/* grot... put the ip hdr back on */
	mp->m_len += hlen;		/* so the server can look at it */
	rvdsrc.sin_addr = ip->ip_src;
	rvddst.sin_addr = ip->ip_dst;
	raw_input(mp, &rvdproto, (struct sockaddr *)&rvdsrc,
	    (struct sockaddr *)&rvddst);
	return;

	}
clean:
	m_freem(mp);
	return;

bad_format:
	vd_longstat.vdstat.bad_frmt++;
	m_freem(mp);
	return;

}

#define	b_nonce	b_pfcent		/* this is really bogus; it won't
					 * work for paging, etc.  But for
					 * now it will do.
					 */

rvdwrite(drive,bp,index,blockc,bindex,count,datap,sa,mwait)
	u_long	drive;
	register struct buf *bp;
	u_long	index;
	u_long	blockc,bindex;
	u_long	count;
	caddr_t datap;
	struct in_addr	*sa;
	int	mwait;
{
	u_long blockn = bp->b_blkno+bindex;
	register struct	mbuf *packet, *m, *n;
	struct mbuf *next;
	int	len, len_to_send;
	int     s;
	register struct rvdw *p;
#ifdef ibm032
	caddr_t rdatap;
#endif

/* Get an mbuf for IP and RVD headers */

	if ((packet = m_get(mwait, MT_HEADER)) == NULL) return(ENOBUFS);

#ifdef RVDDBUG
	if (vddflg > 2)
	printf("rvdw: bn %D bc %d bi %d sz %d\n",blockn,blockc,bindex,count);
#endif RVDDBUG

	packet->m_off = MMAXOFF - (sizeof(struct rvdw) - RVDDSIZE);
	packet->m_len = sizeof(struct rvdw) - RVDDSIZE;
	p = mtod(packet,struct rvdw *);
	p->rvd_type = RVDWRITE;
	p->drive = swapw(drive);
	p->nonce = swapw((u_long)bp->b_nonce);
	p->index = swapw(index);
	p->blockn = htonl(blockn);
	p->blockc = htons((u_short)blockc);
	p->bindex = htons((u_short)bindex);

	len_to_send = count*RVDDSIZE;
	m = packet;			/* chain pointer */
#ifdef ibm032
	s = spl_high();
#endif
	while (len_to_send > 0) {   /* Still more data to write */

/* Here we're trying to use the "cluster" stuff to improve efficiency.
 * The mbuf chain we pass to send_ip will have an mbuf at the start with
 * an RVD header its end (enough room before it for ip header), followed
 * by one or more mbufs whose data parts are stored in cluster-mapped
 * pages.
 */
	    if ((n = m_get(mwait, MT_DATA)) == NULL) {
	    	/* Should really free mbufs already allocated here! */
#ifdef ibm032
	    	splx (s);
#endif
		goto wclean;
	    }
#ifdef	MCLALLOC
	    MCLGET(n);			/* get a page from the cluster */
	    if (n->m_len != CLBYTES) {	/* hmmm, lost... */
#else	MCLALLOC
	    MCLGET(next, 1);		/* get a page from the cluster */
	    if (next == NULL) {		/* hmmm, lost... */
#endif	MCLALLOC
		m_free(n);
#ifdef ibm032
	    	splx(s);
#endif
		goto wclean;
	    }
#ifndef	MCLALLOC
	    n->m_off = (int)next - (int)n;
#endif	MCLALLOC

#ifndef ibm032
	    n->m_len = len = MIN(CLBYTES, len_to_send);
	    bcopy(datap, mtod(n, caddr_t), len);
#else
	    len = MIN(CLBYTES, len_to_send);
	    n->m_len = len = MIN(len,NBPG - ((u_long)datap & PGOFSET));	    
	    rdatap = real_buf_addr(bp,datap);
	    SET_VR0;
	    bcopy(rdatap, mtod(n, caddr_t), len);
	    CLR_VR0;
#endif		
	    datap = (caddr_t) ((u_long)datap + len);
	    len_to_send -= len;		/* mark what we've sent */
	    m->m_next = n;		/* chain it in */
	    m = n;
	}
#ifdef ibm032
	splx(s);
#endif
	return(send_ip(sa,packet,sizeof(struct rvdw) + (count - 1)*RVDDSIZE,
		mwait));


wclean:
	m_freem(packet);
	return(ENOBUFS);
}



rvdread(drive,nonce,index,blockn,blkcnt,sa,mwait)
	u_long	drive;
	u_long	nonce;
	u_long	index;
	u_long	blockn;
	u_long	blkcnt;
	int	mwait;
	struct in_addr	*sa;
{
	register struct mbuf *packet;
	register struct rvdr *p;

/* Get an mbuf for IP and RVD header */

	if ((packet = m_get(mwait, MT_HEADER)) == NULL) return(ENOBUFS);

#ifdef RVDDBUG
	if (vddflg > 2)
	printf("rvdread: drive %d blockn %d count %d\n",drive,blockn,blkcnt);
#endif

	packet->m_off = MMAXOFF - sizeof(struct rvdr);
	packet->m_len = sizeof(struct rvdr);
	p = mtod(packet,struct rvdr *);
	p->rvd_type = RVDREAD;
	p->drive = swapw(drive);
	p->nonce = swapw(nonce);
	p->index = swapw(index);
	p->blockn = htonl(blockn);
	p->blockc = htonl(blkcnt);

	return(send_ip(sa,packet,sizeof(struct rvdr),mwait));
}


rvderror(drive,nonce,index,ertype,data,dlen,sa,mwait)
	u_long	drive;
	u_long	nonce;
	u_long	index;
	u_char	ertype;
	u_long	*data;		/* THIS SHOULD BE ELIMINATED!!  */
	int	dlen;		/*		"		*/
	int	mwait;
	struct in_addr	*sa;
{
	register struct mbuf *packet;
	register struct rvde *p;
	u_long	*datap;
	register int	i;

/* Get an mbuf for IP and RVD header */

	if ((packet = m_get(mwait, MT_HEADER)) == NULL) return(ENOBUFS);

	packet->m_off = MMAXOFF - sizeof(struct rvde);
	packet->m_len = sizeof(struct rvde);
	p = mtod(packet, struct rvde *);
	p->rvd_type = RVDERROR;
	p->etype = ertype;
	p->drive = swapw(drive);
	p->nonce = swapw(nonce);
	p->index = swapw(index);

	return(send_ip(sa,packet,packet->m_len), mwait);
}


rvdrespin(drive, vd_uid, capab, mode, bfactor, nonce, sa)
	u_long	drive;
	u_long	vd_uid;
	char	capab[32];
	u_short	mode;
	u_char	bfactor;
	u_long	nonce;
	struct in_addr	*sa;
{
	register struct	mbuf *packet;
	register struct rvdrs *p;

/* Get an mbuf for IP and RVD header */

	if ((packet = m_get(M_WAIT, MT_HEADER)) == NULL) return(ENOBUFS);

#ifdef RVDDBUG
	printf("rvdrespin: drive %d uid %D\n",drive,vd_uid);
#endif

	packet->m_off = MMAXOFF - sizeof(struct rvdrs);
	packet->m_len = sizeof(struct rvdrs);
	p = mtod(packet, struct rvdrs *);
	p->rvd_type = RVDRESPIN;
	p->mode = mode;
	p->vd_uid = swapw(vd_uid);
	p->drive = swapw(drive);
	p->nonce = swapw(nonce);
	p->index = 0;		/* For first one it will be reset */
	bcopy((caddr_t)capab, (caddr_t)(p->cpblty), 32);
	p->br_factor = bfactor;

	return(send_ip(sa,packet,sizeof(struct rvdrs),M_WAIT));
}

rvdspin(drive,name,capab,mode,bfactor,nonce,sa)
	u_long	drive;
	char	name[32],capab[32];
	u_short	mode;
	u_char	bfactor;
	u_long	nonce;
	struct in_addr	*sa;
{
	register struct	mbuf *packet;
	register struct rvds *p;

/* Get an mbuf for IP and RVD header */

	if ((packet = m_get(M_WAIT, MT_HEADER)) == NULL) return(ENOBUFS);

#ifdef RVDDBUG
	printf("rvdspin: drive %d name %s\n",drive,name);
#endif

	packet->m_off = MMAXOFF - sizeof(struct rvds);
	packet->m_len = sizeof(struct rvds);
	p = mtod(packet, struct rvds *);
	p->rvd_type = RVDSPIN;
	p->mode = mode;
	p->drive = swapw(drive);
	p->nonce = swapw(nonce);
	p->index = 0;		/* For first one it will be reset */
	bcopy((caddr_t)name, (caddr_t)(p->pname), 32);
	bcopy((caddr_t)capab, (caddr_t)(p->cpblty), 32);
	p->br_factor = bfactor;

	return(send_ip(sa,packet,sizeof(struct rvds),M_WAIT));
}

#ifdef	KERBEROS
rvdauthspin(drive,name,authent,mode,bfactor,nonce,sa)
	u_long	drive;
	char	name[32];
	KTEXT	authent;
	u_short	mode;
	u_char	bfactor;
	u_long	nonce;
	struct in_addr	*sa;
{
	register struct	mbuf *packet, *m, *n;
	register struct rvdas *p;
	int	authent_size;
	int	copy_size;
	int	copy_cnt;
	caddr_t	copy_ptr;

/* Get an mbuf for IP and RVD header.
 */
	if ((packet = m_get(M_WAIT, MT_HEADER)) == NULL) 
		return(ENOBUFS);

#ifdef RVDDBUG
	printf("rvdauthspin: drive %d name %s\n",drive,name);
#endif

	packet->m_len = RVDAS_HDR_SIZE;
	packet->m_off = MMAXOFF - packet->m_len;
	p = mtod(packet, struct rvdas *);
	p->rvd_type = RVDAUTHSPIN;
	p->mode = mode;
	p->drive = swapw(drive);
	p->nonce = swapw(nonce);
	p->index = 0;		/* For first one it will be reset */
	bcopy((caddr_t)name, (caddr_t)(p->pname), 32);
	p->br_factor = bfactor;

	authent_size = authent->length + sizeof(int) + sizeof(long);
	if (authent_size > sizeof(struct ktext))
		authent_size = sizeof(struct ktext);

/* Create a chain of mbufs for the authenticator.
 */
	m = packet;
	copy_size = authent_size;
	copy_cnt  = authent_size;
	copy_ptr  = (caddr_t)authent; 
	while (copy_cnt > 0) {

		if ((n = m_get(M_WAIT, MT_DATA)) == NULL) {
			m_freem(packet);
			return(ENOBUFS);
		}

		n->m_len = copy_size = MIN(MLEN, copy_cnt);
		bcopy(copy_ptr, mtod(n, caddr_t), copy_size);

		copy_ptr += copy_size;
		copy_cnt -= copy_size;
		m->m_next = n;
		m = n;
	}

	return(send_ip(sa,packet,(RVDAS_HDR_SIZE+authent_size),M_WAIT));
}
#endif	KERBEROS


rvdspind(drive,index,nonce,cpb,sa)
	u_long	drive;
	u_long	nonce;
	u_long	index;
	char	cpb[32];
	struct in_addr	*sa;
{
	register struct mbuf *packet;
	register struct rvdsd *p;

/* Get an mbuf for IP and RVD header */

	if ((packet = m_get(M_WAIT, MT_HEADER)) == NULL) return(ENOBUFS);

#ifdef RVDDBUG
	printf("rvdspind: drive %d\n",drive);
#endif

	packet->m_off = MMAXOFF - sizeof(struct rvdsd);
	packet->m_len = sizeof(struct rvdsd);
	p = mtod(packet, struct rvdsd *);
	p->rvd_type = RVDSDOWN;
	p->drive = swapw(drive);
	p->nonce = swapw(nonce);
	p->index = swapw(index);
	bcopy((caddr_t)cpb, (caddr_t)(p->spdcpb), 32);

	return(send_ip(sa,packet,sizeof(struct rvdsd),M_WAIT));
}


#ifndef ibm032
/* Copy from an mbuf chain to a contiguous block of core */

caddr_t rvd_copy_d(source,dest,len)

	caddr_t		source;
	register caddr_t dest;
	register int	len;
{
	register struct mbuf *src;
	register int	len_in_mbuf;

	for (src = (struct mbuf *)source; src != NULL && len > 0;){
		len_in_mbuf = min(len,src->m_len);
		bcopy(mtod(src, caddr_t), dest, len_in_mbuf);
		dest += len_in_mbuf;
		len -= len_in_mbuf;
		src->m_len -= len_in_mbuf;
		if (src->m_len != 0) return((caddr_t)src);
		src = src->m_next;
	}
	return((caddr_t)src);
}
#endif

/* The below routine is a TOTAL CROCK. Although an attempt is made to
   store the RVD checksum in network byte order IT IS COMPUTED IN VAX
   byte order. This is an incredibly stupid mistake... but that is the
   way it is.. the protocol is too well known to fix it now I bet.
                    -Jeffrey Schiller 1/25/86 (Late on a Saturday night). */

/* Get 32bit checksum of packet. Packet is stored as a chain of mbufs */
/* This wants to be written in assembly language for efficiency, and */
/* wants to be written in a **real** high level language for clarity */


/* We compute the checksum in 32 bit chunks. The problem is that the packet
 * is stored in mbufs which might not be even multiples of 32 bits. So
 * we do as much as we can in place, and were  necessary, save the last
 * word and first word of an mbuf in chold and add that in.
 */
u_long rvdcksum(mp) 
	struct mbuf *mp;
{
	register u_long cksum;
	register u_long *datap;
	register struct mbuf *ap;
	register i;
	u_long chold;
	register char *cdestp;
	register int cstate;

	cksum = 0;				/* Zero out checksum */
	chold = 0;
	cstate = 0;
	cdestp = (char *)&chold;
	for (ap = mp;ap != NULL;ap=ap->m_next){	/* Loop until end of chain */
	    datap = mtod(ap,u_long *);		/* Find start of data in this mbuf */
	    if (cstate != 0) {			/* Ooops. Last mbuf didn't end on even
						32 bit boundary */
		for (i=cstate;i<4;i++) {	/* Get rest of long word */
		    *cdestp++ = * (char *) datap;
		    datap = (u_long *) (1 + (char *) datap);
		}
		cksum += swapw(chold);          /* Vax chksum */
	    }
	    else cstate = 4;
#ifdef ibm032
	    i = (ap->m_len + cstate - 4)>>2;
	    cksum += swapsum(datap, i);
	    datap += i;
#else
	    for (i=(ap->m_len + cstate - 4)>>2; --i >= 0; )     /* Loop over all data in this mbuf */
	        cksum += swapw(*datap++);        /* Vax chksum */
#endif

	    if ((cstate = (ap->m_len + cstate)%4) != 0) {
						/* Didn't end on even 32 bit boundary */
		cdestp = (char *)&chold;
		for (i=0;i<cstate;i++) {
		    *cdestp++ = *(char *) datap;
		    datap = (u_long *) (1 + (char *) datap);
		}
			/* If this was the last mbuf then something is wrong.
			all packets should be multiples of 32 bytes. So we
			just let it hang. This should cause the checksum to
			barf anyway, so we are not too concerned. */
	    }
	}

	return (cksum);
}

/* Call IP to send it the packet stored in the specified mbuf chain.  This
 * requires reserving space at the start of the first mbuf for the ip
 * header; if there isn't room, another one is allocated.
 */
send_ip(sa, packet, len, mwait)
	struct in_addr	*sa;
	struct mbuf *packet;
	register int len;
	int mwait;
{
	register struct rvd *rvdp;
	register struct ip *p;
	int	trouble;

	vd_longstat.vdstat.pkts_sent++;

	rvdp = mtod(packet,struct rvd *);
	rvdp->hdrun.allpkt.version = RVDVERSION;
	rvdp->cksum = 0;
	rvdp->cksum = htonl(rvdcksum(packet));

	if (MSIZE - packet->m_len < sizeof(struct ip)) {
		register struct mbuf *m;

		if ((m = m_get(mwait, MT_HEADER)) == NULL) {
			m_freem(packet);
			return(ENOBUFS);
		}
		m->m_off = MMAXOFF - sizeof(struct ip);
		m->m_len = sizeof(struct ip);
		m->m_next = packet;
		packet = m;
	} else {
		packet->m_off -= sizeof(struct ip);
		packet->m_len += sizeof(struct ip);
	}

	p = mtod(packet, struct ip *);

	p->ip_p = IPPROTO_RVD;
	p->ip_off = 0;
	p->ip_src.s_addr = INADDR_ANY;
	p->ip_dst.s_addr = sa->s_addr;
	p->ip_len = sizeof(struct ip) + len;
	p->ip_ttl = RVDTTL;

	trouble = ip_output(packet, NULL, (struct route *)0, 0);
	if (trouble) vd_longstat.vdstat.pkt_rej++;
	return(trouble);
}


/*ARGSUSED*/
rvd_usrreq(so, req, m, nam, rights)
	struct socket *so;
	int req;
	struct mbuf *m, *nam, *rights;
{
	register struct rawcb *rp = sotorawcb(so);
	int cmd;
	register int error = 0;

	if (rights && rights->m_len) {
		error = EOPNOTSUPP;
		goto release;
	}
	if (rp == 0 && req != PRU_ATTACH) {
		error = EINVAL;
		goto release;
	}
	switch (req) {

	/*
	 * All requests except the control request are handled by
	 * the raw_usrreq() routine.
	 */
	case PRU_CONTROL:
		cmd = (int)m;
		switch (cmd) {

		case SIOCSHIWAT:
			/* Note that we manually set both the socket high
			 * watermarks and the socket max mbuf counts.
			 * This is slightly bogus, but what sbreserve()
			 * does is even more bogus, so...
			 */
#define MAXSHORT ((1 << (sizeof(short) * NBBY)) - 1)
			if (*(int *)nam < 0 || *(int *)nam > MAXSHORT) {
				error = EINVAL;
				break;
			}
			so->so_snd.sb_hiwat = *(int *)nam;
			so->so_snd.sb_mbmax = *(int *)nam;
			so->so_rcv.sb_hiwat = *(int *)nam;
			so->so_rcv.sb_mbmax = *(int *)nam;
			break;
		case SIOCGHIWAT:
			*(int *)nam = so->so_snd.sb_hiwat;
			break;
		default:
			error = EOPNOTSUPP;
			break;
		}
		m = NULL;
		break;

	default:
		return(raw_usrreq(so, req, m, nam, rights));
	}
release:
	if (m != NULL)
		m_freem(m);
	return (error);
}
