BBN-Vax-TCP/bbnnet-oct82/raw_input.c

#include "../h/param.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../bbnnet/mbuf.h"
#include "../bbnnet/net.h"
#include "../bbnnet/ifcb.h"
#include "../bbnnet/ip.h"
#include "../bbnnet/tcp.h"
#include "../bbnnet/icmp.h"
#include "../bbnnet/raw.h"
#include "../bbnnet/udp.h"
#include "../bbnnet/ucb.h"

extern struct user u;

/*
 * Put raw message input on user receive queue.  Called from ip_input or local
 * net input routine.  Dispatches on link/protocol number and source and
 * destination address.
 */
raw_input(mp, src, dst, link, mask, error)               
register struct mbuf *mp;
struct socket *src;
struct socket *dst;
short link;
int mask, error;
{
	register struct proto *p;
	register struct mbuf *m;
	register struct ucb *up;
	int copied, queued;

	copied = TRUE;
	queued = FALSE;

	/* get proto dispatch queue header */

	p = (struct proto *)&protab[PRHASH(link)];

	while ((p = p->pr_next) != NULL) {
		m = dtom(p);
		up = (struct ucb *)m->m_act;

		/* dispatch on link number, and src/dst address */

		if (p->pr_num == link && up != NULL && 
		      (up->uc_flags & mask) && 
		      (up->uc_host.s_addr == NULL || 
			src->s_addr == up->uc_host.s_addr) &&
		      (up->uc_local.s_addr == NULL ||
			 dst->s_addr == up->uc_local.s_addr) &&
		      (!error || (error && up->uc_flags&RAWERR))) {

			/* if conns for this proto remain, copy the message,
			   otherwise just put it on ucb read queue */

			copied = FALSE;
			if (p->pr_next != NULL) 
				if ((m = m_copy(mp)) != NULL)
					copied = TRUE;

			/* if last copy was successful, enqueue the
			   message, otherwise tell user something
			   was dropped */

			if (mp != NULL) 
				queued = raw_queue(mp, up);
			else
				to_user(up, UDROP);
			mp = m;
		} 
	}

	if (copied)		/* drop anything left over */
		m_freem(mp);
	if (queued)
		netstat.net_drops++;
}

/*
 * Dispatch message to other IP/TCP/ICMP/GGP modules.  For each requestor,
 * copy raw message and place it on user input queue.
 */
raw_disp(m, p)
register struct mbuf *m;
register struct proto *p;
{
	register struct mbuf *n, *mm;
	register struct ucb *up;

	while (p != NULL)  {
		mm = dtom(p);
		if ((up = (struct ucb *)mm->m_act) == NULL)
			return;
		p = p->pr_next;

		/* no mbufs for copy, just tell user;
		   otherwise copy and dispatch */

		if (m == NULL) 
			to_user(up, UDROP);
		else {
			if (p != NULL)
				n = m_copy(m);
			raw_queue(m, up);
			m = n;
		}
	}
}

/*
 * Put raw message chain on specified ucb's read queue.  Make sure buffer
 * limit is not exceeded.  Message will either all fit, or be dropped.
 */
raw_queue(mp, up)
register struct mbuf *mp;
register struct ucb *up;
{
	register count;
	register struct mbuf *m;

	/* drop if no buffer space availble for conn */
	/* have buffer space, chain to user rcv buf */

	for (m = mp, count = 0; m != NULL; m = m->m_next)
		count++;
	if (count + up->uc_rsize <= up->uc_rcv) {

		mp->m_act = NULL;

		while (up->uc_flags & ULOCK)
			sleep(&up->uc_flags, PZERO);
		up->uc_flags |= ULOCK;

		up->uc_rsize += count;
		if ((m = up->uc_rbuf) == NULL)
			up->uc_rbuf = mp;
		else {
			while (m->m_act != NULL) 
				m = m->m_act;
			m->m_act = mp;
		}
		up->uc_flags &= ~ULOCK;
		wakeup(&up->uc_flags);

		/* wake up raw read */

		wakeup(up);
		return(TRUE);
	} else {
		to_user(up, UDROP);
		m_freem(mp);
		return(FALSE);
	}
}

/*
 * Add protocol entry to ucb proto block, adding new blocks as needed.  Insert
 * proto entry to global proto header chain.
 */
raw_add(up, prnum)
register struct ucb *up;
short prnum;
{
	register struct mbuf *m, *n;
	register struct proto *p, *q;
	register i;

	/* first, make sure we don't already have this prnum */

	p = profind(up, prnum);
	while ((p = p->pr_next) != NULL) {
		m = dtom(p);
		if (p->pr_num == prnum && up == (struct ucb *)m->m_act)
			return;
	}

	p = up->uc_proto;
	n = m = dtom(p);
getnew:
	/* find first non-full proto block on ucb list */

	while (m != NULL && m->m_len == NPRMB) {
		n = m;
		m = m->m_next;
	}

	/* no proto block or all full */

	if (p == NULL || m == NULL) {

		/* get new proto block */

		if ((m = m_get(1)) == NULL) {
			u.u_error = ENETBUF;
			return;
		}
		m->m_next = NULL;
		m->m_act = (struct mbuf *)up;
		m->m_len = 0;

		/* mark free all entries in proto block */

		p = (struct proto *)((int)m + MHEAD);
		for (q=p,i=NPRMB; i > 0; i--,q++) {
			q->pr_next = NULL;
			q->pr_flag = 0;
		}

		/* chain new proto block to ucb */

		if (n == NULL) 
			up->uc_proto = p;
		else 
			n->m_next = m;
	}

	/* look for first free entry in proto block */

	for (i=NPRMB; i > 0; i--, p++) {
		if (p->pr_flag == 0) {
			p->pr_num = prnum;
			p->pr_flag |= PRUSED;
			m->m_len++;

			/* find proto header, and add entry to chain */

			q = profind(up, prnum);
			while (q->pr_next != NULL)
				q = q->pr_next;
			q->pr_next = p;
			return;
		}
	}

	/* just in case # of free proto entries lies, make full and retry */

	m->m_len = NPRMB;
	goto getnew;
}

/*
 *  Free all proto entries and blocks on this ucb.
 */
raw_free(up)
register struct ucb *up;
{
	register i;
	register struct proto *p;
	register struct mbuf *m;

	if ((p = up->uc_proto) == NULL) 
		return;
	up->uc_proto = NULL;

	m = dtom(p);
	while (m != NULL) {
		for (i=NPRMB; i > 0; i--,p++)
			if (p->pr_flag & PRUSED)
				prodel(up, p);
		m = m_free(m);
	}
}

/*
 * Find specified proto entry and delete from ucb proto block.
 */
raw_del(up, prnum)
register struct ucb *up;
register short prnum;
{
	register struct proto *p;
	register struct mbuf *m;

	p = profind(up, prnum);
	while ((p = p->pr_next) != NULL)
		if (p->pr_num == prnum) {
			m = dtom(p);
			if (up == (struct ucb *)m->m_act) {
				prodel(up, p);
				return;
			}
		}
	u.u_error = ENETRNG;
}


/*
 * Fill array with protocol numbers from ucb proto block
 */
raw_stat(up, protp, protlen)
struct ucb *up;
register short *protp;
register protlen;
{
	register i;
	register struct proto *p;
	register struct mbuf *m;

	p = up->uc_proto;
	m = dtom(p);
	while (m != NULL) {
		for (i = NPRMB; i > 0; i--,p++) {
			if (p->pr_flag & PRUSED) {
				if (protlen > 0) {
					if (copyout((caddr_t)&p->pr_num,
						    (caddr_t)protp,
						    sizeof(short))) {
						u.u_error = EFAULT;
						return;
					}
					protlen -= sizeof(short);
					protp++;
				} else
					return;
			}
		}
		m = m->m_next;
		p = (struct proto *)((int)m + MHEAD);
	}
}

/*
 * Delete proto entry from ucb proto block and corresponding global proto chain 
 */
prodel(up, p)
struct ucb *up;
register struct proto *p;
{
	register struct proto *q, *r;
	register short prnum;
	register struct mbuf *m;

	if (p == NULL)
		return;
	prnum = p->pr_num;
	r = profind(up, prnum);
	q = r->pr_next;

	while (q != NULL) {
		if (q->pr_num == prnum && q == p) {
			r->pr_next = p->pr_next;
			break;
		}
		r = q;
		q = q->pr_next;
	}
	
	p->pr_next = NULL;
	p->pr_flag = 0;
	m = dtom(p);
	if (m->m_len != 0)
		m->m_len--;
}

/* 
 * Find correct proto chain header.
 */
struct proto *profind(up, prnum)
register struct ucb *up;
register short prnum;
{
	register struct proto *p;

	/* check "built-in" proto headers first, then proto hash table 
	   for header */

	if (prnum == TCPROTO && up->uc_flags&UIP)
		p = (struct proto *)&netcb.n_tcp_proto;
	else if (prnum == up->uc_srcif->if_link && up->uc_flags&URAW)
		p = (struct proto *)&netcb.n_ip_proto;
	else if (prnum == ICMPROTO && up->uc_flags&UIP)
		p = (struct proto *)&netcb.n_icmp_proto;
	else if (prnum == GGPROTO && up->uc_flags&UIP)
		p = (struct proto *)&netcb.n_ggp_proto;
	else if (prnum == UDPROTO && up->uc_flags&UIP)
		p = (struct proto *)&netcb.n_udp_proto;
	else
		p = (struct proto *)&protab[PRHASH(prnum)];

	return(p);
}