BBN-Vax-TCP/bbnnet/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/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 host *hp;
	register struct if_local *lp;
	register struct ucb *cp;
	int error = FALSE;
	struct socket naddr;
	int netreset();

	/* process all messages on input queue */

	while ((m = ip->if_inq_hd) != NULL) {     

		ip->if_inq_hd = m->m_act;
		m->m_act = NULL;
		p = (struct imp *)((int)m + m->m_off);               

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

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

		case 0:                         /* 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 == IPLINK) {
        			m->m_off += sizeof(struct imp);
        			m->m_len -= sizeof(struct imp);
				ip_input(m, ip);
			} else 
				goto dispraw;
			break;

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

		 case 2:                         /* imp going down msg */

			switch (p->i_link & IMPWHYDOWN) {
			case 0:		
				log_err(ip, "imp going down in 30 secs");
				ip->if_avail = FALSE;
				timeout((caddr_t)netreset, (caddr_t)ip, 30*60);
				goto free;
			case 1:
				log_err(ip, "imp going down for hardware PM");
				goto free;
			case 2:
				log_err(ip, "imp going down for software reload");
				goto free;
			case 3:
				log_err(ip, "imp emergency restart");
				goto free;

			}

		case 4:                         /* no-op */

			/* compare local addr with that in message,
			   warn and reset if no match */
			
			for (lp=locnet; lp < &locnet[nlocnet]; lp++) 
				if (lp->if_ifcb == ip && 
				    (p->i_host != lp->if_addr.s_host ||
				     p->i_impno != lp->if_addr.s_impno)) {
					lp->if_addr.s_host = p->i_host;
					lp->if_addr.s_imp = p->i_imp;
			log_err("warning: ifcb address does not match imp's:"); 
			printf("    reset to %d/%d\n",  p->i_host, p->i_impno);
				}
			goto free;

		case 5:                         /* RFNM */
		case 9:                         /* incomplete */
			/* record in host table and free */

			p->i_htype = netoint(ip->if_net);
			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, FALSE);
					}
				}
			goto disperr;

		case 6:                         /* dead host status */
			/* record in host table and free */

			p->i_htype = netoint(ip->if_net);
			if ((hp = h_find(&p->i_htype)) != NULL)
				hp->h_stat |= (p->i_stype & 0xf) << 4;
			goto free;

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

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

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

				/* signal all conns to this host */

				for (cp = contab; cp < conNCON; cp++)
					if (cp->uc_proc != NULL && 
					    cp->uc_route == hp)
						to_user(cp, UDEAD);
			}

			goto disperr;

		case 8:				/* 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(ip->if_net);
			if ((hp = h_find(&p->i_htype)) != NULL) 
				hp->h_rfnm = 0;
			imp_noops(ip);
			goto disperr;

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

		default:                        /* illegal type */
			/* record in status and free */

			netstat.imp_drops++;
			goto free;

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

			error = TRUE;
			if (p->i_link != IPLINK) {

		dispraw:
			/* dipatch control and data messages to raw conns */

				naddr.s_addr = (long)ip->if_net;
				naddr.s_imp = p->i_imp;
				naddr.s_host = p->i_host;
				raw_input(m, ip, &naddr, NULL, p->i_link,
					  URAW, error);    
			} else
		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 the header and calls imp_snd to
 * enqueue the message for the interface driver.
 */
imp_output(m, ip, addr, len, link, asis)
register struct mbuf *m;
struct ifcb *ip;
struct socket *addr;
register len;
int link, asis;
{
	register struct imp *p;

	/* build header in current mbuf */

	m->m_off -= sizeof(struct imp);
	m->m_len += sizeof(struct imp);
	p = (struct imp *)((int)m + m->m_off);
	 
	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 = 0xf;
	p->i_dnet = 0;
	p->i_lflgs = 0;
	p->i_type = 0;
	p->i_stype = 0;
	p->i_htype = 0;

	/* send message to i/f driver */

	imp_snd(m, ip, asis);
}

/* 
 * Enque a message passed as an mbuf chain from the higher level protocol
 * routines.  Do RFNM counting: if more than 8 messages are in flight,
 * hold on a RFNM queue until the i/f clears.
 */
imp_snd(mp, ip, asis)             
register struct mbuf *mp;
register struct ifcb *ip;
int asis;
{
	register struct imp *p;
	register struct host *hp;
	register struct mbuf *m;
	int i;
	struct socket ohost;

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

	p = (struct imp *)((int)mp + mp->m_off);        /* -> 1822 leader */
	mp->m_act = NULL;               

#ifndef IMPLOOP

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

	if (p->i_type == 0 && !asis) {

                ohost.s_addr = (long)ip->if_net;
                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++;
	}

	/* 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.
 * Either call imp_output to supply an 1822 leader and send, or assume a
 * user supplied leader and send direct to imp_snd.
 */
imp_raw(up, m, len)
register struct ucb *up;
register struct mbuf *m;
int len;
{

	switch (up->uc_flags & RAWFLAG) {

	case RAWCOMP:		/* system supplies leader */

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

		m->m_off = MSIZE;
		m->m_len = 0;
		if (!imp_output(m, up->uc_srcif->if_ifcb, &up->uc_host,
				len, up->uc_lo, FALSE))
			u.u_error = ERAWBAD;
		break;

	case RAWASIS:		/* user supplies leader */

		m = m_free(m);		/* ->user supplied header */
		if (!imp_snd(m, up->uc_srcif->if_ifcb, TRUE))
			u.u_error = ERAWBAD;
		break;	

	default:
		u.u_error = ERAWBAD;
	}
}

/*
 * 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;

	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 = (struct imp *)((int)m + m->m_off);

		l->i_nff = 0xf;
		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 = 4;        

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

		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;
		spl0();	
	}

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

log_err(ip, s)
struct ifcb *ip;
char *s;
{
	printf("dev %d/%d: impio: %s\n", major(ip->if_dev),minor(ip->if_dev),s);
}