[BACK]Return to hd_input.c CVS log [TXT][DIR] Up to [local] / src / sys / netccitt

File: [local] / src / sys / netccitt / Attic / hd_input.c (download)

Revision 1.3, Sun Apr 21 22:28:52 1996 UTC (28 years, 1 month ago) by deraadt
Branch: MAIN
CVS Tags: kame_19991208, UBC_BASE, SMP_BASE, OPENBSD_3_0_BASE, OPENBSD_3_0, OPENBSD_2_9_BASE, OPENBSD_2_9, OPENBSD_2_8_BASE, OPENBSD_2_8, OPENBSD_2_7_BASE, OPENBSD_2_7, OPENBSD_2_6_BASE, OPENBSD_2_6, OPENBSD_2_5_BASE, OPENBSD_2_5, OPENBSD_2_4_BASE, OPENBSD_2_4, OPENBSD_2_3_BASE, OPENBSD_2_3, OPENBSD_2_2_BASE, OPENBSD_2_2, OPENBSD_2_1_BASE, OPENBSD_2_1, OPENBSD_2_0_BASE, OPENBSD_2_0
Branch point for: UBC, SMP
Changes since 1.2: +3 -4 lines

partial sync with netbsd 960418, more to come

/*	$OpenBSD: hd_input.c,v 1.3 1996/04/21 22:28:52 deraadt Exp $	*/
/*	$NetBSD: hd_input.c,v 1.9 1996/04/13 01:34:16 cgd Exp $	*/

/*
 * Copyright (c) University of British Columbia, 1984
 * Copyright (c) 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Laboratory for Computation Vision and the Computer Science Department
 * of the University of British Columbia.
 *
 * 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, Berkeley 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.
 *
 *	@(#)hd_input.c	8.1 (Berkeley) 6/10/93
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>

#include <net/if.h>

#include <netccitt/hdlc.h>
#include <netccitt/hd_var.h>
#include <netccitt/x25.h>
#include <netccitt/pk_extern.h>

static void frame_reject __P((struct hdcb *, int, struct Hdlc_iframe *));
static void rej_routine __P((register struct hdcb *, int));
static void free_iframes __P((struct hdcb *, int *, int));

/*
 *      HDLC INPUT INTERFACE
 *
 *      This routine is called when the HDLC physical device has
 *      completed reading a frame.
 */

void
hdintr()
{
	register struct mbuf *m;
	register struct hdcb *hdp;
	register struct ifnet *ifp;
	register int    s;
	static struct ifnet *lastifp;
	static struct hdcb *lasthdp;

	for (;;) {
		s = splimp();
		IF_DEQUEUE(&hdintrq, m);
		splx(s);
		if (m == 0)
			break;
		if (m->m_len < HDHEADERLN) {
			printf("hdintr: packet too short (len=%d)\n",
			       m->m_len);
			m_freem(m);
			continue;
		}
		if ((m->m_flags & M_PKTHDR) == 0)
			panic("hdintr");
		ifp = m->m_pkthdr.rcvif;

		/*
		 * look up the appropriate hdlc control block
		 */

		if (ifp == lastifp)
			hdp = lasthdp;
		else {
			for (hdp = hdcbhead; hdp; hdp = hdp->hd_next)
				if (hdp->hd_ifp == ifp)
					break;
			if (hdp == 0) {
				printf("hdintr: unknown interface %p\n", ifp);
				m_freem(m);
				continue;
			}
			lastifp = ifp;
			lasthdp = hdp;
		}

		/*
		 * Process_rxframe returns FALSE if the frame was NOT queued
		 * for the next higher layers.
		 */
		if (process_rxframe(hdp, m) == FALSE)
			m_freem(m);
	}
}

int
process_rxframe(hdp, fbuf)
	register struct hdcb *hdp;
	register struct mbuf *fbuf;
{
	register int    queued = FALSE, frametype, pf;
	register struct Hdlc_frame *frame;
	struct sockaddr *sa = (struct sockaddr *) hdp->hd_pkp;

	frame = mtod(fbuf, struct Hdlc_frame *);
	pf = ((struct Hdlc_iframe *) frame)->pf;

	hd_trace(hdp, RX, frame);
	if (frame->address != ADDRESS_A && frame->address != ADDRESS_B)
		return (queued);

	switch ((frametype = hd_decode(hdp, frame)) + hdp->hd_state) {
	case DM + DISC_SENT:
	case UA + DISC_SENT:
		/*
		 * Link now closed.  Leave timer running
		 * so hd_timer() can periodically check the
		 * status of interface driver flag bit IFF_UP.
		 */
		hdp->hd_state = DISCONNECTED;
		break;

	case DM + INIT:
	case UA + INIT:
		/*
		 * This is a non-standard state change needed for DCEs
		 * that do dynamic link selection.  We can't go into the
		 * usual "SEND DM" state because a DM is a SARM in LAP.
		 */
		hd_writeinternal(hdp, SABM, POLLOFF);
		hdp->hd_state = SABM_SENT;
		SET_TIMER(hdp);
		break;

	case SABM + DM_SENT:
	case SABM + WAIT_SABM:
		hd_writeinternal(hdp, UA, pf);
	case UA + SABM_SENT:
	case UA + WAIT_UA:
		KILL_TIMER(hdp);
		hd_initvars(hdp);
		hdp->hd_state = ABM;
		hd_message(hdp, "Link level operational");
		/* Notify the packet level - to send RESTART. */
		(void) pk_ctlinput(PRC_LINKUP, sa, NULL);
		break;

	case SABM + SABM_SENT:
		/*
		 * Got a SABM collision. Acknowledge the remote's SABM via UA
		 * but still wait for UA.
		 */
		hd_writeinternal(hdp, UA, pf);
		break;

	case SABM + ABM:
		/* Request to reset the link from the remote. */
		KILL_TIMER(hdp);
		hd_message(hdp, "Link reset");
#ifdef HDLCDEBUG
		hd_dumptrace(hdp);
#endif
		hd_flush(hdp->hd_ifp);
		hd_writeinternal(hdp, UA, pf);
		hd_initvars(hdp);
		(void) pk_ctlinput(PRC_LINKRESET, sa, NULL);
		hdp->hd_resets++;
		break;

	case SABM + WAIT_UA:
		hd_writeinternal(hdp, UA, pf);
		break;

	case DM + ABM:
		hd_message(hdp, "DM received: link down");
#ifdef HDLCDEBUG
		hd_dumptrace(hdp);
#endif
		(void) pk_ctlinput(PRC_LINKDOWN, sa, NULL);
		hd_flush(hdp->hd_ifp);
	case DM + DM_SENT:
	case DM + WAIT_SABM:
	case DM + WAIT_UA:
		hd_writeinternal(hdp, SABM, pf);
		hdp->hd_state = SABM_SENT;
		SET_TIMER(hdp);
		break;

	case DISC + INIT:
	case DISC + DM_SENT:
	case DISC + SABM_SENT:
		/* Note: This is a non-standard state change. */
		hd_writeinternal(hdp, UA, pf);
		hd_writeinternal(hdp, SABM, POLLOFF);
		hdp->hd_state = SABM_SENT;
		SET_TIMER(hdp);
		break;

	case DISC + WAIT_UA:
		hd_writeinternal(hdp, DM, pf);
		SET_TIMER(hdp);
		hdp->hd_state = DM_SENT;
		break;

	case DISC + ABM:
		hd_message(hdp, "DISC received: link down");
		(void) pk_ctlinput(PRC_LINKDOWN, sa, NULL);
	case DISC + WAIT_SABM:
		hd_writeinternal(hdp, UA, pf);
		hdp->hd_state = DM_SENT;
		SET_TIMER(hdp);
		break;

	case UA + ABM:
		hd_message(hdp, "UA received: link down");
		(void) pk_ctlinput(PRC_LINKDOWN, sa, NULL);
	case UA + WAIT_SABM:
		hd_writeinternal(hdp, DM, pf);
		hdp->hd_state = DM_SENT;
		SET_TIMER(hdp);
		break;

	case FRMR + DM_SENT:
		hd_writeinternal(hdp, SABM, pf);
		hdp->hd_state = SABM_SENT;
		SET_TIMER(hdp);
		break;

	case FRMR + WAIT_SABM:
		hd_writeinternal(hdp, DM, pf);
		hdp->hd_state = DM_SENT;
		SET_TIMER(hdp);
		break;

	case FRMR + ABM:
		hd_message(hdp, "FRMR received: link down");
		(void) pk_ctlinput(PRC_LINKDOWN, sa, NULL);
#ifdef HDLCDEBUG
		hd_dumptrace(hdp);
#endif
		hd_flush(hdp->hd_ifp);
		hd_writeinternal(hdp, SABM, pf);
		hdp->hd_state = WAIT_UA;
		SET_TIMER(hdp);
		break;

	case RR + ABM:
	case RNR + ABM:
	case REJ + ABM:
		process_sframe(hdp, (struct Hdlc_sframe *) frame, frametype);
		break;

	case IFRAME + ABM:
		queued = process_iframe(hdp, fbuf, (struct Hdlc_iframe *) frame);
		break;

	case IFRAME + SABM_SENT:
	case RR + SABM_SENT:
	case RNR + SABM_SENT:
	case REJ + SABM_SENT:
		hd_writeinternal(hdp, DM, POLLON);
		hdp->hd_state = DM_SENT;
		SET_TIMER(hdp);
		break;

	case IFRAME + WAIT_SABM:
	case RR + WAIT_SABM:
	case RNR + WAIT_SABM:
	case REJ + WAIT_SABM:
		hd_writeinternal(hdp, FRMR, POLLOFF);
		SET_TIMER(hdp);
		break;

	case ILLEGAL + SABM_SENT:
		hdp->hd_unknown++;
		hd_writeinternal(hdp, DM, POLLOFF);
		hdp->hd_state = DM_SENT;
		SET_TIMER(hdp);
		break;

	case ILLEGAL + ABM:
		hd_message(hdp, "Unknown frame received: link down");
		(void) pk_ctlinput(PRC_LINKDOWN, sa, NULL);
	case ILLEGAL + WAIT_SABM:
		hdp->hd_unknown++;
#ifdef HDLCDEBUG
		hd_dumptrace(hdp);
#endif
		hd_writeinternal(hdp, FRMR, POLLOFF);
		hdp->hd_state = WAIT_SABM;
		SET_TIMER(hdp);
		break;
	}

	return (queued);
}

int
process_iframe(hdp, fbuf, frame)
	register struct hdcb *hdp;
	struct mbuf    *fbuf;
	register struct Hdlc_iframe *frame;
{
	register int    nr = frame->nr, ns = frame->ns, pf = frame->pf;
	register int    queued = FALSE;

	/*
	 * Validate the iframe's N(R) value. It's N(R) value must be in sync
	 * with our V(S) value and our "last received nr".
	 */

	if (valid_nr(hdp, nr, FALSE) == FALSE) {
		frame_reject(hdp, Z, frame);
		return (queued);
	}
	/*
	 * This section tests the IFRAME for proper sequence. That is, it's
	 * sequence number N(S) MUST be equal to V(S).
	 */

	if (ns != hdp->hd_vr) {
		hdp->hd_invalid_ns++;
		if (pf || (hdp->hd_condition & REJ_CONDITION) == 0) {
			hdp->hd_condition |= REJ_CONDITION;
			/*
			 * Flush the transmit queue. This is ugly but we
			 * have no choice.  A reject response must be
			 * immediately sent to the DCE.  Failure to do so
			 * may result in another out of sequence iframe
			 * arriving (and thus sending another reject)
			 * before the first reject is transmitted. This
			 * will cause the DCE to receive two or more
			 * rejects back to back, which must never happen.
			 */
			hd_flush(hdp->hd_ifp);
			hd_writeinternal(hdp, REJ, pf);
		}
		return (queued);
	}
	hdp->hd_condition &= ~REJ_CONDITION;

	/*
	 * This section finally tests the IFRAME's sequence number against
	 * the window size (K)  and the sequence number of the  last frame we
	 * have acknowledged.  If the IFRAME is completely correct then it is
	 * queued for the packet level.
	 */

	if (ns != (hdp->hd_lasttxnr + hdp->hd_xcp->xc_lwsize) % MODULUS) {
		hdp->hd_vr = (hdp->hd_vr + 1) % MODULUS;
		if (pf == 1) {
			/* Must generate a RR or RNR with final bit on. */
			hd_writeinternal(hdp, RR, POLLON);
		} else
			/*
			 * Hopefully we can piggyback the RR, if not we will
			 * generate a RR when T3 timer expires.
			 */
		if (hdp->hd_rrtimer == 0)
			hdp->hd_rrtimer = hd_t3;

		/* Forward iframe to packet level of X.25. */
		fbuf->m_data += HDHEADERLN;
		fbuf->m_len -= HDHEADERLN;
		fbuf->m_pkthdr.len -= HDHEADERLN;
		fbuf->m_pkthdr.rcvif = (struct ifnet *) hdp->hd_pkp;
#ifdef BSD4_3
		fbuf->m_act = 0;/* probably not necessary */
#else
		{
			register struct mbuf *m;

			for (m = fbuf; m->m_next; m = m->m_next)
				m->m_act = (struct mbuf *) 0;
			m->m_act = (struct mbuf *) 1;
		}
#endif
		pk_input(fbuf);
		queued = TRUE;
		hd_start(hdp);
	} else {
		/*
		 * Here if the remote station has transmitted more iframes
		 * then the number which have been acknowledged plus K.
		 */
		hdp->hd_invalid_ns++;
		frame_reject(hdp, W, frame);
	}
	return (queued);
}

/*
 * This routine is used to determine if a value (the middle parameter) is
 * between two other values. The low value is  the first  parameter the high
 * value is the last parameter. The routine checks the middle value to see if
 * it is within the range of the first and last values. The reason we need
 * this routine is the values are modulo some  base hence a simple test for
 * greater or less than is not sufficient.
 */

bool
range_check(rear, value, front)
	int             rear, value, front;
{
	register bool   result = FALSE;

	if (front > rear)
		result = (rear <= value) && (value <= front);
	else
		result = (rear <= value) || (value <= front);

	return (result);
}

/*
 * This routine handles all the frame reject conditions which can arise as a
 * result  of secondary  processing.  The frame reject condition Y (frame
 * length error) are handled elsewhere.
 */

static void
frame_reject(hdp, rejectcode, frame)
	struct hdcb    *hdp;
	int rejectcode;
	struct Hdlc_iframe *frame;
{
	register struct Frmr_frame *frmr = &hd_frmr;

	frmr->frmr_control = ((struct Hdlc_frame *) frame)->control;

	frmr->frmr_ns = frame->ns;
	frmr->frmr_f1_0 = 0;
	frmr->frmr_nr = frame->nr;
	frmr->frmr_f2_0 = 0;

	frmr->frmr_0000 = 0;
	frmr->frmr_w = frmr->frmr_x = frmr->frmr_y =
		frmr->frmr_z = 0;
	switch (rejectcode) {
	case Z:
		frmr->frmr_z = 1;	/* invalid N(R). */
		break;

	case Y:
		frmr->frmr_y = 1;	/* iframe length error. */
		break;

	case X:
		frmr->frmr_x = 1;	/* invalid information field. */
		frmr->frmr_w = 1;
		break;

	case W:
		frmr->frmr_w = 1;	/* invalid N(S). */
	}

	hd_writeinternal(hdp, FRMR, POLLOFF);

	hdp->hd_state = WAIT_SABM;
	SET_TIMER(hdp);
}

/*
 * This procedure is invoked when ever we receive a supervisor frame such as
 * RR, RNR and REJ. All processing for these frames is done here.
 */

void
process_sframe(hdp, frame, frametype)
	register struct hdcb *hdp;
	register struct Hdlc_sframe *frame;
	int             frametype;
{
	register int    nr = frame->nr, pf = frame->pf, pollbit = 0;

	if (valid_nr(hdp, nr, pf) == TRUE) {
		switch (frametype) {
		case RR:
			hdp->hd_condition &= ~REMOTE_RNR_CONDITION;
			break;

		case RNR:
			hdp->hd_condition |= REMOTE_RNR_CONDITION;
			hdp->hd_retxcnt = 0;
			break;

		case REJ:
			hdp->hd_condition &= ~REMOTE_RNR_CONDITION;
			rej_routine(hdp, nr);
		}

		if (pf == 1) {
			hdp->hd_retxcnt = 0;
			hdp->hd_condition &= ~TIMER_RECOVERY_CONDITION;

			if (frametype == RR && hdp->hd_lastrxnr == hdp->hd_vs
			    && hdp->hd_timer == 0 && hdp->hd_txq.head == 0)
				hd_writeinternal(hdp, RR, pf);
			else
				/*
				 * If any iframes have been queued because of
				 * the timer condition, transmit then now.
				 */
			if (hdp->hd_condition & REMOTE_RNR_CONDITION) {
				/*
				 * Remote is busy or timer condition, so only
				 * send one.
				 */
				if (hdp->hd_vs != hdp->hd_retxqi)
					hd_send_iframe(hdp,
					    hdp->hd_retxq[(u_char)hdp->hd_vs],
					    pollbit);
			} else	/* Flush the retransmit list first. */
				while (hdp->hd_vs != hdp->hd_retxqi)
					hd_send_iframe(hdp,
					    hdp->hd_retxq[(u_char)hdp->hd_vs],
					    POLLOFF);
		}
		hd_start(hdp);
	} else
		frame_reject(hdp, Z, (struct Hdlc_iframe *) frame);	/* Invalid N(R). */
}

/*
 * This routine tests the validity of the N(R) which we have received. If it
 * is ok,  then all the  iframes which it acknowledges  (if any) will be
 * freed.
 */

bool
valid_nr(hdp, nr, finalbit)
	register struct hdcb *hdp;
	int nr;
	register int    finalbit;
{
	/* Make sure it really does acknowledge something. */
	if (hdp->hd_lastrxnr == nr)
		return (TRUE);

	/*
	 * This section validates the frame's  N(R) value.  It's N(R) value
	 * must be  in syncronization  with  our V(S)  value and  our "last
	 * received nr" variable. If it is correct then we are able to send
	 * more IFRAME's, else frame reject condition is entered.
	 */

	if (range_check(hdp->hd_lastrxnr, nr, hdp->hd_vs) == FALSE) {
		if ((hdp->hd_condition & TIMER_RECOVERY_CONDITION) &&
		    range_check(hdp->hd_vs, nr, hdp->hd_xx) == TRUE)
			hdp->hd_vs = nr;

		else {
			hdp->hd_invalid_nr++;
			return (FALSE);
		}
	}
	/*
	 * If we get to here, we do have a valid frame  but it might be out
	 * of sequence.  However, we should  still accept the receive state
	 * number N(R) since it has already passed our previous test and it
	 * does acknowledge frames which we are sending.
	 */

	KILL_TIMER(hdp);
	free_iframes(hdp, &nr, finalbit);	/* Free all acknowledged
						 * iframes */
	if (nr != hdp->hd_vs)
		SET_TIMER(hdp);

	return (TRUE);
}

/*
 * This routine determines how many iframes need to be retransmitted. It then
 * resets the Send State Variable V(S) to accomplish this.
 */

static void
rej_routine(hdp, rejnr)
	register struct hdcb *hdp;
	register int    rejnr;
{
	register int    anchor;

	/*
	 * Flush the output queue.  Any iframes queued for
	 * transmission will be out of sequence.
	 */

	hd_flush(hdp->hd_ifp);

	/*
	 * Determine how many frames should be re-transmitted. In the case of
	 * a normal REJ this  should be 1 to K.  In the case of a timer
	 * recovery REJ (ie. a REJ with the Final Bit on) this could be 0.
	 */

	anchor = hdp->hd_vs;
	if (hdp->hd_condition & TIMER_RECOVERY_CONDITION)
		anchor = hdp->hd_xx;

	anchor = (anchor - rejnr + 8) % MODULUS;

	if (anchor > 0) {

		/* There is at least one iframe to retransmit. */
		KILL_TIMER(hdp);
		hdp->hd_vs = rejnr;

		while (hdp->hd_vs != hdp->hd_retxqi)
			hd_send_iframe(hdp, hdp->hd_retxq[(u_char)hdp->hd_vs],
				       POLLOFF);

	}
	hd_start(hdp);
}

/*
 * This routine frees iframes from the retransmit queue. It is called when a
 * previously written iframe is acknowledged.
 */

static void
free_iframes(hdp, nr, finalbit)
	register struct hdcb *hdp;
	int            *nr;
	register int    finalbit;

{
	register int    i, k;

	/*
	 * We  need to do the  following  because  of a  funny quirk  in  the
	 * protocol.  This case  occures  when  in Timer  recovery  condition
	 * we get  a  N(R)  which  acknowledges all  the outstanding  iframes
	 * but with  the Final Bit off. In this case we need to save the last
	 * iframe for possible retransmission even though it has already been
	 * acknowledged!
	 */

	if ((hdp->hd_condition & TIMER_RECOVERY_CONDITION) && *nr == hdp->hd_xx && finalbit == 0) {
		*nr = (*nr - 1 + 8) % MODULUS;
		/* printf ("QUIRK\n"); */
	}
	k = (*nr - hdp->hd_lastrxnr + 8) % MODULUS;

	/* Loop here freeing all acknowledged iframes. */
	for (i = 0; i < k; ++i) {
		m_freem(hdp->hd_retxq[(u_char)hdp->hd_lastrxnr]);
		hdp->hd_retxq[(u_char)hdp->hd_lastrxnr] = 0;
		hdp->hd_lastrxnr = (hdp->hd_lastrxnr + 1) % MODULUS;
	}

}