summaryrefslogtreecommitdiffstats
path: root/sys/netccitt/hd_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netccitt/hd_input.c')
-rw-r--r--sys/netccitt/hd_input.c669
1 files changed, 669 insertions, 0 deletions
diff --git a/sys/netccitt/hd_input.c b/sys/netccitt/hd_input.c
new file mode 100644
index 0000000..eb939d0
--- /dev/null
+++ b/sys/netccitt/hd_input.c
@@ -0,0 +1,669 @@
+/*
+ * 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>
+
+static frame_reject();
+static rej_routine();
+static free_iframes();
+/*
+ * HDLC INPUT INTERFACE
+ *
+ * This routine is called when the HDLC physical device has
+ * completed reading a frame.
+ */
+
+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 %x\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);
+ }
+}
+
+process_rxframe (hdp, fbuf)
+register struct hdcb *hdp;
+register struct mbuf *fbuf;
+{
+ register int queued = FALSE, frametype, pf;
+ register struct Hdlc_frame *frame;
+
+ 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, hdp->hd_pkp);
+ 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, hdp->hd_pkp);
+ 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, hdp->hd_pkp);
+ 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, hdp->hd_pkp);
+ 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, hdp->hd_pkp);
+ 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, hdp->hd_pkp);
+#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, hdp->hd_pkp);
+ 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);
+}
+
+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
+frame_reject (hdp, rejectcode, frame)
+struct hdcb *hdp;
+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.
+ */
+
+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[hdp->hd_vs], pollbit);
+ }
+ else /* Flush the retransmit list first. */
+ while (hdp->hd_vs != hdp->hd_retxqi)
+ hd_send_iframe (hdp, hdp->hd_retxq[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;
+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
+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[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
+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[hdp->hd_lastrxnr]);
+ hdp->hd_retxq[hdp->hd_lastrxnr] = 0;
+ hdp->hd_lastrxnr = (hdp->hd_lastrxnr + 1) % MODULUS;
+ }
+
+}
OpenPOWER on IntegriCloud