home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) University of British Columbia, 1984
- * Copyright (c) 1990 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 7.7 (Berkeley) 5/29/91
- */
-
- #include "param.h"
- #include "systm.h"
- #include "mbuf.h"
- #include "domain.h"
- #include "socket.h"
- #include "protosw.h"
- #include "errno.h"
- #include "time.h"
- #include "kernel.h"
-
- #include "../net/if.h"
-
- #include "hdlc.h"
- #include "hd_var.h"
- #include "x25.h"
-
- /*
- * 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;
- extern struct ifqueue pkintrq;
- 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);
- }
- if (pkintrq.ifq_len)
- pkintr ();
- }
-
- 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;
- }
-
- }
-