BBN-Vax-TCP/bbnnet-oct82/imp_io.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/imp.h"
#include "../bbnnet/raw.h"
#include "../bbnnet/ucb.h"
#include "../bbnnet/fsm.h"

extern struct user u;

/*
 * ARPANET 1822 input routine
 *
 * Called from netmain to interpret 1822 imp-host messages.  Completed messages
 * are placed on the input queue by the driver.  Type 0 messages (non-control)
 * are passed to higher level protocol processors on the basis of link
 * number.  All other type messages (control) are handled here.
 */
imp_rcv(ip)
register struct ifcb *ip;
{
	register struct mbuf *m, *n;
	register struct imp *p;
	register struct proto *q;
	register struct host *hp;
	register struct ucb *up;
	int i;
	int error = FALSE;
	struct socket naddr;
	int netreset();

	/* process all messages on input queue */

	for (;;) {
		spl5();
		if ((m = ip->if_inq_hd) == NULL) {     
			spl0();
			break;
		}
		ip->if_inq_hd = m->m_act;
		spl0();
		m->m_act = NULL;
		p = mtod(m, struct imp *);               

		if (p->i_nff != IMP_NFF)    /* make sure leader is ok */
			goto free;

		switch (p->i_type) {            /* case on msg type */

		case IMP_DATA:			/* normal msg */

			p->i_mlen = short_from_net(p->i_mlen) >> 3;

			/* dispatch on link number, pass to either
			  ip or raw input handler */

			if (p->i_link == ip->if_link) {
        			m->m_off += sizeof(struct imp);
        			m->m_len -= sizeof(struct imp);

#ifdef	IPADJ
				/* remove any IMP message padding */

				ip_adj(m, (struct ip *)((int)p + 
							sizeof(struct imp)));
#endif	IPADJ

				/* if any user IPs, copy message */

				if ((q = netcb.n_ip_proto) != NULL)
					n = m_copy(m);
				ip_input(m, ip);	

				/* dispatch to other IPs */

				if (q != NULL) {
					n->m_off -= sizeof(struct imp);
					n->m_len += sizeof(struct imp);
					raw_disp(n, q);
				}
			} else 
				goto dispraw;
			break;

		 case IMP_LERROR:		/* imp leader error */
			log_err(ip, "leader error");
			h_reset(iptonet(ip->if_addr));
			imp_noops(ip);		 /* send no-ops to clear i/f */
			goto free;

		 case IMP_DOWN:			/* imp going down msg */
			log_err(ip, "going down");
			ip->if_avail = FALSE;
			timeout((caddr_t)netreset, (caddr_t)ip, 30*60);
			goto free;

		case IMP_NOP:			/* no-op */
			/* compare local addr with that in message,
			   warn and reset if no match */
			
			if (p->i_host != ip->if_addr.s_host ||
			     p->i_imp != ip->if_addr.s_imp) {
				ip->if_addr.s_host = p->i_host;
				ip->if_addr.s_imp = p->i_imp;
				printf("%s%d imp: address mismatch, reset to %d/%d\n",
					ip->if_name, ip->if_unit, 
					p->i_host, p->i_impno);
			}
			goto free;

		case IMP_RFNM:			/* RFNM */
		case IMP_INCOMP:		/* incomplete */
			/* record in host table and free */

			p->i_htype = netoint(iptonet(ip->if_addr));
			if ((hp = h_find(&p->i_htype)) != NULL) 
 				if (hp->h_rfnm != 0) {
					hp->h_rfnm--;

					/* send any blocked msgs */

					if ((n = hp->h_outq) != NULL) {
						hp->h_outq = n->m_act;
						imp_snd(n, ip, NULL,0,0,TRUE);

					/* delete any temp host entries if
					   RFNM count is zero */

					} else if (hp->h_rfnm == 0 && 
						   hp->h_stat&HTEMP)
						h_free(hp);
						
				}
			goto disperr;

		case IMP_DEAD:			/* unreachable host */
			/* set status, record in host tab and free */

			p->i_htype = netoint(iptonet(ip->if_addr));
			if ((hp = h_find(&p->i_htype)) != NULL) {

				hp->h_stat &= ~HUP;
				hp->h_rfnm = 0;

				/* signal all conns to this host */

				for (up=netcb.n_ucb_hd; up != NULL; 
							      up = up->uc_next)

					if (up->uc_proc != NULL && 
					    up->uc_route == hp) 

						/* if route addr different from 
						   dest addr, must be dead 
						   gateway */

						if (hp->h_addr.s_addr ==
						    addr_1822(up->uc_host)) {
							up->uc_xstat = p->i_stype;
							to_user(up, 
							        (p->i_stype == 
								 IMP_DEAD_HOST 
								    ? UDEAD
								    : UNRCH));

						/* look for another gateway to
						   continue with */

						} else if (!ip_reroute(up, hp)){
							up->uc_xstat = 0;
							to_user(up, UNRCH);
						}
			}

			goto disperr;

		case IMP_DERROR:			/* imp data error */
			/* clear RFNM status for this host and send no-ops
			   to clear i/f */

			log_err(ip, "data error");
			p->i_htype = netoint(iptonet(ip->if_addr));
			if ((hp = h_find(&p->i_htype)) != NULL) 
				hp->h_rfnm = 0;
			imp_noops(ip);
			goto disperr;

		case IMP_RESET:				/* reset */
			log_err(ip, "reset complete");
			goto free;

		default:				/* illegal type */
			goto free;

		disperr:		
			/* dispatch control messages to raw connections */

			if (p->i_link == ip->if_link && 
			    netcb.n_ip_proto == NULL)
				goto free;
			error = TRUE;
		dispraw:
			/* dipatch control and data messages to raw conns */

			naddr.s_addr = ip->if_addr.s_addr;
			naddr.s_imp = p->i_imp;
			naddr.s_host = p->i_host;
			raw_input(m, &naddr, &ip->if_addr, 
				  (short)p->i_link, URAW, error);    
			break;
		free:
			m_freem(m);     /* free message chain */
		}
	}
}

/*
 * ARPANET 1822 output routine
 * 
 * Called from higher level protocol routines to set up messages for
 * transmission to the imp.  Sets up header for non-asis messages.
 * Enques a message passed as an mbuf chain from the higher level protocol
 * routines.  Does RFNM counting: if more than 8 messages are in flight,
 * hold on a RFNM queue until the i/f clears.
 */
imp_snd(mp, ip, addr, len, link, asis)
register struct mbuf *mp;
register struct ifcb *ip;
struct socket *addr;
register len;
int link, asis;
{
	register struct mbuf *m;
	register struct imp *p;
	register struct host *hp;
	int i;
	struct socket ohost;

	if (!ip->if_avail) {			 /* make sure i/f is up */
		m_freem(mp);
		return(FALSE);
	}

	mp->m_act = NULL;
	if (asis) 
		p = mtod(mp, struct imp *);        /* -> 1822 leader */
	else {
		/* build header in current mbuf */

		mp->m_off -= sizeof(struct imp);
		mp->m_len += sizeof(struct imp);
		p = mtod(mp, struct imp *);
		 
		p->i_host = addr->s_host;
		p->i_impno = addr->s_impno;
		p->i_lh = 0;		/* do not send logical host field */
		p->i_mlen = len + sizeof(struct imp);
		p->i_link = link;

		p->i_nff = IMP_NFF;
		p->i_dnet = 0;
		p->i_lflgs = 0;
		p->i_type = IMP_DATA;
		p->i_stype = 0;
		p->i_htype = 0;
	}

#ifndef IMPLOOP

	/* do RFNM counting for type 0 messages (no more than 8 outstanding
	   to any host)  */ 

	if (p->i_type == 0) {
                ohost.s_addr = ip->if_addr.s_addr;
                ohost.s_host = p->i_host;
                ohost.s_imp = p->i_imp;
        	hp = h_find(&ohost);            /* get host table entry */

		/* if imp would block, put msg on queue until rfnm */

		if (hp != NULL) {
			if (hp->h_rfnm > 8) {
				if ((m = hp->h_outq) == NULL)
					hp->h_outq = mp;
				else {

					/* don't pile up too many messages */

					i = 8;
					while (m->m_act != NULL) {
						if (--i > 0) 
							m = m->m_act;
						else {
							m_freem(mp);
							return(FALSE);
						}
					}
					m->m_act = mp;
				}
				return(TRUE);
			} else
				hp->h_rfnm++;
		} else {

		/* make a temporary host entry if none exists already */

			if ((hp = h_make(&ohost, TRUE)) != NULL) 
				hp->h_rfnm++;
		}
	}

	/* put output message on queue */

        spl5();
	if (ip->if_outq_hd != NULL)
		ip->if_outq_tl->m_act = mp;
	else
		ip->if_outq_hd = mp;
	ip->if_outq_tl = mp;
	spl0();

	/* if no outstanding output, start some */

	if (!ip->if_active)
		(*ip->if_out)(ip);

#else
	/* software looping: put msg chain on input queue */

	if (ip->if_inq_hd != NULL)
		ip->if_inq_tl->m_act = mp;
	else
		ip->if_inq_hd = mp;
	ip->if_inq_tl = mp;

#endif IMPLOOP

	return(TRUE);
}

/*
 * Raw ARPANET 1822 output routine.  Called from raw_write with an empty
 * mbuf chained to the user supplied message chain for header construction.
 */
imp_raw(up, m, len)
register struct ucb *up;
register struct mbuf *m;
int len;
{
	register struct proto *p;

	switch (up->uc_flags & RAWMASK) {

	case RAWCOMP:		/* system supplies leader */

		/* leader will be constructed at end of first mbuf */

		m->m_off = MSIZE;
		m->m_len = 0;
		if ((p = up->uc_proto) == NULL) {
			u.u_error = ENETRNG;
			m_freem(m);
		} else if (!imp_snd(m, up->uc_srcif, &up->uc_host, len, 
				     p->pr_num, FALSE)) 
			u.u_error = EBLOCK;
		break;

	case RAWASIS:		/* user supplies leader */

		/* ->user supplied header */

		if ((m = m_free(m)) == NULL)
			u.u_error = ERAWBAD;	
		else if (!imp_snd(m, up->uc_srcif, NULL, 0, 0, TRUE))
			u.u_error = EBLOCK;
		break;	

	default:
		u.u_error = ENETPARM;
		m_freem(m);
	}
}

/*
 * Put three 1822 no-ops at the head of the output queue.  Part of host-imp
 * initialization procedure.
 */
imp_noops(ip)             
register struct ifcb *ip;
{
	register inx;
	register struct mbuf *m;
	register struct imp *l;
	int s;

	for (inx = 0; inx < 3; inx++ ) { 

		/* get a buffer for the no-op */

		if ((m = m_get(0)) == NULL) 
			return(FALSE);
		m->m_off = MHEAD;
		m->m_len = 10;
		l = mtod(m, struct imp *);

		l->i_nff = IMP_NFF;
		l->i_dnet = 0;
		l->i_lflgs = 0;
                l->i_htype = 0;
                l->i_host = 0;
                l->i_imp = 0;
                l->i_link = inx;
                l->i_stype = 0;
                l->i_mlen = 0;
                l->i_type = IMP_NOP;        

		/* put no-op at head of output queue */

		s = spl5();
		if (ip->if_outq_hd == NULL)
			ip->if_outq_tl = m;
		else
			m->m_act = ip->if_outq_hd;
		ip->if_outq_hd = m;
		splx(s);	
	}

	if (!ip->if_active)
		(*ip->if_out)(ip);
	return(TRUE);
}

log_err(ip, s)
register struct ifcb *ip;
char *s;
{
	printf("%s%d imp: %s\n", ip->if_name, ip->if_unit, s);
}