1768 lines
34 KiB
C
1768 lines
34 KiB
C
/*
|
|
* ===================================
|
|
* HARP | Host ATM Research Platform
|
|
* ===================================
|
|
*
|
|
*
|
|
* This Host ATM Research Platform ("HARP") file (the "Software") is
|
|
* made available by Network Computing Services, Inc. ("NetworkCS")
|
|
* "AS IS". NetworkCS does not provide maintenance, improvements or
|
|
* support of any kind.
|
|
*
|
|
* NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
|
|
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
|
|
* SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
|
|
* In no event shall NetworkCS be responsible for any damages, including
|
|
* but not limited to consequential damages, arising from or relating to
|
|
* any use of the Software or related support.
|
|
*
|
|
* Copyright 1994-1998 Network Computing Services, Inc.
|
|
*
|
|
* Copies of this Software may be made, however, the above copyright
|
|
* notice must be reproduced on all copies.
|
|
*/
|
|
|
|
/*
|
|
* ATM Forum UNI Support
|
|
* ---------------------
|
|
*
|
|
* ITU-T Q.2110 - Process CPCS-signals (SSCOP PDUs)
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <net/if.h>
|
|
#include <netatm/port.h>
|
|
#include <netatm/queue.h>
|
|
#include <netatm/atm.h>
|
|
#include <netatm/atm_sys.h>
|
|
#include <netatm/atm_sap.h>
|
|
#include <netatm/atm_cm.h>
|
|
#include <netatm/atm_if.h>
|
|
#include <netatm/atm_stack.h>
|
|
#include <netatm/atm_pcb.h>
|
|
#include <netatm/atm_var.h>
|
|
|
|
#include <netatm/uni/sscop.h>
|
|
#include <netatm/uni/sscop_misc.h>
|
|
#include <netatm/uni/sscop_pdu.h>
|
|
#include <netatm/uni/sscop_var.h>
|
|
|
|
/*
|
|
* Local functions
|
|
*/
|
|
static void sscop_bgn_outconn(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_bgn_inconn(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_bgn_ready(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_bgrej_outrecov(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_end_outrecov(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_end_ready(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_endak_outrecov(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_rs_outresyn(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_rs_inresyn(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_rs_outrecov(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_rs_ready(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_er_error(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_er_idle(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_er_outrecov(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_er_recovrsp(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_er_inrecov(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_er_ready(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_erak_error(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_erak_idle(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_erak_outrecov(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_sd_ready(struct sscop *, KBuffer *, caddr_t);
|
|
static void sscop_poll_ready(struct sscop *, KBuffer *, caddr_t);
|
|
|
|
|
|
/*
|
|
* PDU type state lookup tables
|
|
*/
|
|
/* BGN PDU */
|
|
static void (*sscop_bgn_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_bgn_idle, /* SOS_IDLE */
|
|
sscop_bgn_outconn, /* SOS_OUTCONN */
|
|
sscop_bgn_inconn, /* SOS_INCONN */
|
|
sscop_bgn_outdisc, /* SOS_OUTDISC */
|
|
sscop_bgn_outresyn, /* SOS_OUTRESYN */
|
|
sscop_bgn_inresyn, /* SOS_INRESYN */
|
|
sscop_bgn_inresyn, /* SOS_OUTRECOV */
|
|
sscop_bgn_inresyn, /* SOS_RECOVRSP */
|
|
sscop_bgn_inresyn, /* SOS_INRECOV */
|
|
sscop_bgn_ready, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* BGAK PDU */
|
|
static void (*sscop_bgak_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_bgak_idle, /* SOS_IDLE */
|
|
sscop_bgak_outconn, /* SOS_OUTCONN */
|
|
sscop_bgak_error, /* SOS_INCONN */
|
|
sscop_noop, /* SOS_OUTDISC */
|
|
sscop_noop, /* SOS_OUTRESYN */
|
|
sscop_bgak_error, /* SOS_INRESYN */
|
|
sscop_bgak_error, /* SOS_OUTRECOV */
|
|
sscop_bgak_error, /* SOS_RECOVRSP */
|
|
sscop_bgak_error, /* SOS_INRECOV */
|
|
sscop_noop, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* BGREJ PDU */
|
|
static void (*sscop_bgrej_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_bgrej_error, /* SOS_IDLE */
|
|
sscop_bgrej_outconn, /* SOS_OUTCONN */
|
|
sscop_bgrej_inconn, /* SOS_INCONN */
|
|
sscop_endak_outdisc, /* SOS_OUTDISC */
|
|
sscop_bgrej_outresyn, /* SOS_OUTRESYN */
|
|
sscop_bgrej_inconn, /* SOS_INRESYN */
|
|
sscop_bgrej_outrecov, /* SOS_OUTRECOV */
|
|
sscop_bgrej_inconn, /* SOS_RECOVRSP */
|
|
sscop_bgrej_inconn, /* SOS_INRECOV */
|
|
sscop_bgrej_ready, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* END PDU */
|
|
static void (*sscop_end_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_end_idle, /* SOS_IDLE */
|
|
sscop_noop, /* SOS_OUTCONN */
|
|
sscop_end_inconn, /* SOS_INCONN */
|
|
sscop_end_outdisc, /* SOS_OUTDISC */
|
|
sscop_end_inconn, /* SOS_OUTRESYN */
|
|
sscop_end_inconn, /* SOS_INRESYN */
|
|
sscop_end_outrecov, /* SOS_OUTRECOV */
|
|
sscop_end_inconn, /* SOS_RECOVRSP */
|
|
sscop_end_inconn, /* SOS_INRECOV */
|
|
sscop_end_ready, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* ENDAK PDU */
|
|
static void (*sscop_endak_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_noop, /* SOS_IDLE */
|
|
sscop_noop, /* SOS_OUTCONN */
|
|
sscop_endak_inconn, /* SOS_INCONN */
|
|
sscop_endak_outdisc, /* SOS_OUTDISC */
|
|
sscop_endak_inconn, /* SOS_OUTRESYN */
|
|
sscop_endak_inconn, /* SOS_INRESYN */
|
|
sscop_endak_outrecov, /* SOS_OUTRECOV */
|
|
sscop_endak_inconn, /* SOS_RECOVRSP */
|
|
sscop_endak_inconn, /* SOS_INRECOV */
|
|
sscop_endak_ready, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* RS PDU */
|
|
static void (*sscop_rs_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_rs_idle, /* SOS_IDLE */
|
|
sscop_noop, /* SOS_OUTCONN */
|
|
sscop_rs_error, /* SOS_INCONN */
|
|
sscop_noop, /* SOS_OUTDISC */
|
|
sscop_rs_outresyn, /* SOS_OUTRESYN */
|
|
sscop_rs_inresyn, /* SOS_INRESYN */
|
|
sscop_rs_outrecov, /* SOS_OUTRECOV */
|
|
sscop_rs_outrecov, /* SOS_RECOVRSP */
|
|
sscop_rs_outrecov, /* SOS_INRECOV */
|
|
sscop_rs_ready, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* RSAK PDU */
|
|
static void (*sscop_rsak_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_rsak_idle, /* SOS_IDLE */
|
|
sscop_noop, /* SOS_OUTCONN */
|
|
sscop_rsak_error, /* SOS_INCONN */
|
|
sscop_noop, /* SOS_OUTDISC */
|
|
sscop_rsak_outresyn, /* SOS_OUTRESYN */
|
|
sscop_rsak_error, /* SOS_INRESYN */
|
|
sscop_rsak_error, /* SOS_OUTRECOV */
|
|
sscop_rsak_error, /* SOS_RECOVRSP */
|
|
sscop_rsak_error, /* SOS_INRECOV */
|
|
sscop_noop, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* ER PDU */
|
|
static void (*sscop_er_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_er_idle, /* SOS_IDLE */
|
|
sscop_noop, /* SOS_OUTCONN */
|
|
sscop_er_error, /* SOS_INCONN */
|
|
sscop_noop, /* SOS_OUTDISC */
|
|
sscop_noop, /* SOS_OUTRESYN */
|
|
sscop_er_error, /* SOS_INRESYN */
|
|
sscop_er_outrecov, /* SOS_OUTRECOV */
|
|
sscop_er_recovrsp, /* SOS_RECOVRSP */
|
|
sscop_er_inrecov, /* SOS_INRECOV */
|
|
sscop_er_ready, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* ERAK PDU */
|
|
static void (*sscop_erak_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_erak_idle, /* SOS_IDLE */
|
|
sscop_noop, /* SOS_OUTCONN */
|
|
sscop_erak_error, /* SOS_INCONN */
|
|
sscop_noop, /* SOS_OUTDISC */
|
|
sscop_noop, /* SOS_OUTRESYN */
|
|
sscop_erak_error, /* SOS_INRESYN */
|
|
sscop_erak_outrecov, /* SOS_OUTRECOV */
|
|
sscop_noop, /* SOS_RECOVRSP */
|
|
sscop_erak_error, /* SOS_INRECOV */
|
|
sscop_noop, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* SD PDU */
|
|
static void (*sscop_sd_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_sd_idle, /* SOS_IDLE */
|
|
sscop_noop, /* SOS_OUTCONN */
|
|
sscop_sd_inconn, /* SOS_INCONN */
|
|
sscop_noop, /* SOS_OUTDISC */
|
|
sscop_noop, /* SOS_OUTRESYN */
|
|
sscop_sd_inconn, /* SOS_INRESYN */
|
|
sscop_noop, /* SOS_OUTRECOV */
|
|
sscop_noop, /* SOS_RECOVRSP */
|
|
sscop_sd_inconn, /* SOS_INRECOV */
|
|
sscop_sd_ready, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* POLL PDU */
|
|
static void (*sscop_poll_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_poll_idle, /* SOS_IDLE */
|
|
sscop_noop, /* SOS_OUTCONN */
|
|
sscop_poll_inconn, /* SOS_INCONN */
|
|
sscop_noop, /* SOS_OUTDISC */
|
|
sscop_noop, /* SOS_OUTRESYN */
|
|
sscop_poll_inconn, /* SOS_INRESYN */
|
|
sscop_noop, /* SOS_OUTRECOV */
|
|
sscop_noop, /* SOS_RECOVRSP */
|
|
sscop_poll_inconn, /* SOS_INRECOV */
|
|
sscop_poll_ready, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* STAT PDU */
|
|
static void (*sscop_stat_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_stat_idle, /* SOS_IDLE */
|
|
sscop_noop, /* SOS_OUTCONN */
|
|
sscop_stat_inconn, /* SOS_INCONN */
|
|
sscop_noop, /* SOS_OUTDISC */
|
|
sscop_noop, /* SOS_OUTRESYN */
|
|
sscop_stat_inconn, /* SOS_INRESYN */
|
|
sscop_noop, /* SOS_OUTRECOV */
|
|
sscop_stat_inconn, /* SOS_RECOVRSP */
|
|
sscop_stat_inconn, /* SOS_INRECOV */
|
|
sscop_stat_ready, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* USTAT PDU */
|
|
static void (*sscop_ustat_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_ustat_idle, /* SOS_IDLE */
|
|
sscop_noop, /* SOS_OUTCONN */
|
|
sscop_ustat_inconn, /* SOS_INCONN */
|
|
sscop_noop, /* SOS_OUTDISC */
|
|
sscop_noop, /* SOS_OUTRESYN */
|
|
sscop_ustat_inconn, /* SOS_INRESYN */
|
|
sscop_noop, /* SOS_OUTRECOV */
|
|
sscop_ustat_inconn, /* SOS_RECOVRSP */
|
|
sscop_ustat_inconn, /* SOS_INRECOV */
|
|
sscop_ustat_ready, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* UD PDU */
|
|
static void (*sscop_ud_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_ud_all, /* SOS_IDLE */
|
|
sscop_ud_all, /* SOS_OUTCONN */
|
|
sscop_ud_all, /* SOS_INCONN */
|
|
sscop_ud_all, /* SOS_OUTDISC */
|
|
sscop_ud_all, /* SOS_OUTRESYN */
|
|
sscop_ud_all, /* SOS_INRESYN */
|
|
sscop_ud_all, /* SOS_OUTRECOV */
|
|
sscop_ud_all, /* SOS_RECOVRSP */
|
|
sscop_ud_all, /* SOS_INRECOV */
|
|
sscop_ud_all, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
/* MD PDU */
|
|
static void (*sscop_md_tab[SOS_NUMSTATES])
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL, /* SOS_INST */
|
|
sscop_md_all, /* SOS_IDLE */
|
|
sscop_md_all, /* SOS_OUTCONN */
|
|
sscop_md_all, /* SOS_INCONN */
|
|
sscop_md_all, /* SOS_OUTDISC */
|
|
sscop_md_all, /* SOS_OUTRESYN */
|
|
sscop_md_all, /* SOS_INRESYN */
|
|
sscop_md_all, /* SOS_OUTRECOV */
|
|
sscop_md_all, /* SOS_RECOVRSP */
|
|
sscop_md_all, /* SOS_INRECOV */
|
|
sscop_md_all, /* SOS_READY */
|
|
sscop_noop /* SOS_TERM */
|
|
};
|
|
|
|
|
|
/*
|
|
* PDU type lookup table
|
|
*/
|
|
void (*(*sscop_q2110_pdutab[]))
|
|
(struct sscop *, KBuffer *, caddr_t) = {
|
|
NULL,
|
|
sscop_bgn_tab,
|
|
sscop_bgak_tab,
|
|
sscop_end_tab,
|
|
sscop_endak_tab,
|
|
sscop_rs_tab,
|
|
sscop_rsak_tab,
|
|
sscop_bgrej_tab,
|
|
sscop_sd_tab,
|
|
sscop_er_tab,
|
|
sscop_poll_tab,
|
|
sscop_stat_tab,
|
|
sscop_ustat_tab,
|
|
sscop_ud_tab,
|
|
sscop_md_tab,
|
|
sscop_erak_tab
|
|
};
|
|
|
|
|
|
/*
|
|
* BGN PDU / SOS_OUTCONN Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_bgn_outconn(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct bgn_pdu *bp = (struct bgn_pdu *)trlr;
|
|
int err;
|
|
|
|
/*
|
|
* If retransmitted BGN, ignore it
|
|
*/
|
|
if (sscop_is_rexmit(sop, bp->bgn_nsq)) {
|
|
KB_FREEALL(m);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Stop retransmit timer
|
|
*/
|
|
sop->so_timer[SSCOP_T_CC] = 0;
|
|
|
|
/*
|
|
* Initialize state variables
|
|
*/
|
|
SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr));
|
|
SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin);
|
|
q2110_init_state(sop);
|
|
|
|
/*
|
|
* Return an ACK to peer
|
|
*/
|
|
(void) sscop_send_bgak(sop);
|
|
|
|
/*
|
|
* Notify user of connection establishment
|
|
*/
|
|
STACK_CALL(SSCOP_ESTABLISH_CNF, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, (intptr_t)m, 0, err);
|
|
if (err) {
|
|
KB_FREEALL(m);
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Start data transfer timers
|
|
*/
|
|
sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
|
|
sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
|
|
|
|
/*
|
|
* OK, we're ready for data
|
|
*/
|
|
sop->so_state = SOS_READY;
|
|
|
|
/*
|
|
* See if transmit queues need servicing
|
|
*/
|
|
if (sop->so_flags & SOF_XMITSRVC)
|
|
sscop_service_xmit(sop);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* BGN PDU / SOS_INCONN Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_bgn_inconn(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct bgn_pdu *bp = (struct bgn_pdu *)trlr;
|
|
int err;
|
|
|
|
/*
|
|
* If retransmitted BGN, ignore it
|
|
*/
|
|
if (sscop_is_rexmit(sop, bp->bgn_nsq)) {
|
|
KB_FREEALL(m);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Initialize transmit window
|
|
*/
|
|
SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr));
|
|
|
|
/*
|
|
* First, tell user current connection has been released
|
|
*/
|
|
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_USER, err);
|
|
if (err) {
|
|
KB_FREEALL(m);
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Now, tell user of new connection establishment
|
|
*/
|
|
STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, (intptr_t)m, 0, err);
|
|
if (err) {
|
|
KB_FREEALL(m);
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* BGN PDU / SOS_READY Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_bgn_ready(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct bgn_pdu *bp = (struct bgn_pdu *)trlr;
|
|
int err;
|
|
|
|
/*
|
|
* If retransmitted BGN, just ACK it again
|
|
*/
|
|
if (sscop_is_rexmit(sop, bp->bgn_nsq)) {
|
|
KB_FREEALL(m);
|
|
sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
|
|
(void) sscop_send_bgak(sop);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Stop data transfer timers
|
|
*/
|
|
sop->so_timer[SSCOP_T_POLL] = 0;
|
|
sop->so_timer[SSCOP_T_NORESP] = 0;
|
|
sop->so_timer[SSCOP_T_IDLE] = 0;
|
|
sop->so_flags &= ~SOF_KEEPALIVE;
|
|
|
|
/*
|
|
* Initialize transmit window
|
|
*/
|
|
SEQ_SET(sop->so_sendmax, ntohl(bp->bgn_nmr));
|
|
|
|
/*
|
|
* Clear out appropriate queues
|
|
*/
|
|
q2110_prep_retrieve(sop);
|
|
|
|
/*
|
|
* Tell user current connection has been released
|
|
*/
|
|
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_USER, err);
|
|
if (err) {
|
|
KB_FREEALL(m);
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Tell user of incoming connection
|
|
*/
|
|
STACK_CALL(SSCOP_ESTABLISH_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, (intptr_t)m, 0, err);
|
|
if (err) {
|
|
KB_FREEALL(m);
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Wait for user's response
|
|
*/
|
|
sop->so_state = SOS_INCONN;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* BGREJ PDU / SOS_OUTRECOV Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_bgrej_outrecov(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
int err;
|
|
|
|
/*
|
|
* Stop retransmit timer
|
|
*/
|
|
sop->so_timer[SSCOP_T_CC] = 0;
|
|
|
|
/*
|
|
* Report protocol error
|
|
*/
|
|
sscop_bgrej_error(sop, m, trlr);
|
|
|
|
/*
|
|
* Notify user of connection failure
|
|
*/
|
|
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err);
|
|
if (err) {
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Clear receiver buffer
|
|
*/
|
|
sscop_rcvr_drain(sop);
|
|
|
|
/*
|
|
* Back to idle state
|
|
*/
|
|
sop->so_state = SOS_IDLE;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* END PDU / SOS_OUTRECOV Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_end_outrecov(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct end_pdu *ep = (struct end_pdu *)trlr;
|
|
int err, source;
|
|
|
|
/*
|
|
* Stop retransmit timer
|
|
*/
|
|
sop->so_timer[SSCOP_T_CC] = 0;
|
|
|
|
/*
|
|
* Acknowledge END
|
|
*/
|
|
(void) sscop_send_endak(sop);
|
|
|
|
/*
|
|
* Get Source value
|
|
*/
|
|
if (ep->end_type & PT_SOURCE_SSCOP)
|
|
source = SSCOP_SOURCE_SSCOP;
|
|
else
|
|
source = SSCOP_SOURCE_USER;
|
|
|
|
/*
|
|
* Notify user of connection termination
|
|
*/
|
|
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, (intptr_t)m, source, err);
|
|
if (err) {
|
|
KB_FREEALL(m);
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Clear receiver buffer
|
|
*/
|
|
sscop_rcvr_drain(sop);
|
|
|
|
/*
|
|
* Back to idle state
|
|
*/
|
|
sop->so_state = SOS_IDLE;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* END PDU / SOS_READY Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_end_ready(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct end_pdu *ep = (struct end_pdu *)trlr;
|
|
int err, source;
|
|
|
|
/*
|
|
* Stop data transfer timers
|
|
*/
|
|
sop->so_timer[SSCOP_T_POLL] = 0;
|
|
sop->so_timer[SSCOP_T_NORESP] = 0;
|
|
sop->so_timer[SSCOP_T_IDLE] = 0;
|
|
sop->so_flags &= ~SOF_KEEPALIVE;
|
|
|
|
/*
|
|
* Acknowledge END
|
|
*/
|
|
(void) sscop_send_endak(sop);
|
|
|
|
/*
|
|
* Get Source value
|
|
*/
|
|
if (ep->end_type & PT_SOURCE_SSCOP)
|
|
source = SSCOP_SOURCE_SSCOP;
|
|
else
|
|
source = SSCOP_SOURCE_USER;
|
|
|
|
/*
|
|
* Notify user of connection termination
|
|
*/
|
|
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, (intptr_t)m, source, err);
|
|
if (err) {
|
|
KB_FREEALL(m);
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Clear out appropriate queues
|
|
*/
|
|
q2110_prep_retrieve(sop);
|
|
|
|
/*
|
|
* Back to idle state
|
|
*/
|
|
sop->so_state = SOS_IDLE;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* ENDAK PDU / SOS_OUTRECOV Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_endak_outrecov(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
int err;
|
|
|
|
/*
|
|
* Stop retransmit timer
|
|
*/
|
|
sop->so_timer[SSCOP_T_CC] = 0;
|
|
|
|
/*
|
|
* Report protocol error
|
|
*/
|
|
sscop_endak_error(sop, m, trlr);
|
|
|
|
/*
|
|
* Notify user of connection failure
|
|
*/
|
|
STACK_CALL(SSCOP_RELEASE_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, SSCOP_UU_NULL, SSCOP_SOURCE_SSCOP, err);
|
|
if (err) {
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Clear receiver buffer
|
|
*/
|
|
sscop_rcvr_drain(sop);
|
|
|
|
/*
|
|
* Back to idle state
|
|
*/
|
|
sop->so_state = SOS_IDLE;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* RS PDU / SOS_OUTRESYN Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_rs_outresyn(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct rs_pdu *rp = (struct rs_pdu *)trlr;
|
|
int err;
|
|
|
|
/*
|
|
* If retransmitted RS, ignore it
|
|
*/
|
|
if (sscop_is_rexmit(sop, rp->rs_nsq)) {
|
|
KB_FREEALL(m);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Stop retransmit timer
|
|
*/
|
|
sop->so_timer[SSCOP_T_CC] = 0;
|
|
|
|
/*
|
|
* Initialize state variables
|
|
*/
|
|
SEQ_SET(sop->so_sendmax, ntohl(rp->rs_nmr));
|
|
SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin);
|
|
q2110_init_state(sop);
|
|
|
|
/*
|
|
* Free PDU buffers
|
|
*/
|
|
KB_FREEALL(m);
|
|
|
|
/*
|
|
* Return an ACK to peer
|
|
*/
|
|
(void) sscop_send_rsak(sop);
|
|
|
|
/*
|
|
* Notify user of connection resynchronization
|
|
*/
|
|
STACK_CALL(SSCOP_RESYNC_CNF, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, 0, 0, err);
|
|
if (err) {
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Start data transfer timers
|
|
*/
|
|
sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
|
|
sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
|
|
|
|
/*
|
|
* OK, we're ready for data
|
|
*/
|
|
sop->so_state = SOS_READY;
|
|
|
|
/*
|
|
* See if transmit queues need servicing
|
|
*/
|
|
if (sop->so_flags & SOF_XMITSRVC)
|
|
sscop_service_xmit(sop);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* RS PDU / SOS_INRESYN Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_rs_inresyn(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct rs_pdu *rp = (struct rs_pdu *)trlr;
|
|
|
|
/*
|
|
* If retransmitted RS, ignore it
|
|
*/
|
|
if (sscop_is_rexmit(sop, rp->rs_nsq)) {
|
|
KB_FREEALL(m);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Report error condition
|
|
*/
|
|
sscop_rs_error(sop, m, trlr);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* RS PDU / SOS_OUTRECOV Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_rs_outrecov(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct rs_pdu *rp = (struct rs_pdu *)trlr;
|
|
int err;
|
|
|
|
/*
|
|
* If retransmitted RS, report an error
|
|
*/
|
|
if (sscop_is_rexmit(sop, rp->rs_nsq)) {
|
|
sscop_rs_error(sop, m, trlr);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Stop retransmit timer
|
|
*/
|
|
sop->so_timer[SSCOP_T_CC] = 0;
|
|
|
|
/*
|
|
* Initialize transmit window
|
|
*/
|
|
SEQ_SET(sop->so_sendmax, ntohl(rp->rs_nmr));
|
|
|
|
/*
|
|
* Notify user of connection resynchronization
|
|
*/
|
|
STACK_CALL(SSCOP_RESYNC_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, (intptr_t)m, 0, err);
|
|
if (err) {
|
|
KB_FREEALL(m);
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Clear receiver buffer
|
|
*/
|
|
sscop_rcvr_drain(sop);
|
|
|
|
/*
|
|
* Wait for user response
|
|
*/
|
|
sop->so_state = SOS_INRESYN;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* RS PDU / SOS_READY Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_rs_ready(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct rs_pdu *rp = (struct rs_pdu *)trlr;
|
|
int err;
|
|
|
|
/*
|
|
* If retransmitted RS, just ACK it
|
|
*/
|
|
if (sscop_is_rexmit(sop, rp->rs_nsq)) {
|
|
KB_FREEALL(m);
|
|
sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
|
|
sscop_send_rsak(sop);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Stop data transfer timers
|
|
*/
|
|
sop->so_timer[SSCOP_T_POLL] = 0;
|
|
sop->so_timer[SSCOP_T_NORESP] = 0;
|
|
sop->so_timer[SSCOP_T_IDLE] = 0;
|
|
sop->so_flags &= ~SOF_KEEPALIVE;
|
|
|
|
/*
|
|
* Initialize transmit window
|
|
*/
|
|
SEQ_SET(sop->so_sendmax, ntohl(rp->rs_nmr));
|
|
|
|
/*
|
|
* Notify user of connection resynchronization
|
|
*/
|
|
STACK_CALL(SSCOP_RESYNC_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, (intptr_t)m, 0, err);
|
|
if (err) {
|
|
KB_FREEALL(m);
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Clear out appropriate queues
|
|
*/
|
|
q2110_prep_retrieve(sop);
|
|
|
|
/*
|
|
* Wait for user response
|
|
*/
|
|
sop->so_state = SOS_INRESYN;
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* ER PDU / Protocol Error
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_er_error(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
|
|
/*
|
|
* Record error condition
|
|
*/
|
|
sscop_maa_error(sop, 'L');
|
|
KB_FREEALL(m);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* ER PDU / SOS_IDLE Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_er_idle(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
|
|
/*
|
|
* Record error condition
|
|
*/
|
|
sscop_er_error(sop, m, trlr);
|
|
|
|
/*
|
|
* Return an END to peer
|
|
*/
|
|
(void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* ER PDU / SOS_OUTRECOV Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_er_outrecov(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct er_pdu *ep = (struct er_pdu *)trlr;
|
|
int err;
|
|
|
|
/*
|
|
* If retransmitted ER, report an error
|
|
*/
|
|
if (sscop_is_rexmit(sop, ep->er_nsq)) {
|
|
sscop_er_error(sop, m, trlr);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Stop retransmit timer
|
|
*/
|
|
sop->so_timer[SSCOP_T_CC] = 0;
|
|
|
|
/*
|
|
* Initialize transmit window
|
|
*/
|
|
SEQ_SET(sop->so_sendmax, ntohl(ep->er_nmr));
|
|
|
|
/*
|
|
* Initialize receiver window
|
|
*/
|
|
SEQ_SET(sop->so_rcvmax, sop->so_parm.sp_rcvwin);
|
|
|
|
/*
|
|
* Free PDU buffers
|
|
*/
|
|
KB_FREEALL(m);
|
|
|
|
/*
|
|
* Acknowledge ER
|
|
*/
|
|
(void) sscop_send_erak(sop);
|
|
|
|
/*
|
|
* Deliver any outstanding data to user
|
|
*/
|
|
q2110_deliver_data(sop);
|
|
|
|
/*
|
|
* Notify user of connection recovery
|
|
*/
|
|
STACK_CALL(SSCOP_RECOVER_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, 0, 0, err);
|
|
if (err) {
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Wait for user response
|
|
*/
|
|
sop->so_state = SOS_RECOVRSP;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* ER PDU / SOS_RECOVRSP Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_er_recovrsp(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct er_pdu *ep = (struct er_pdu *)trlr;
|
|
|
|
/*
|
|
* If retransmitted ER, just ACK it
|
|
*/
|
|
if (sscop_is_rexmit(sop, ep->er_nsq)) {
|
|
KB_FREEALL(m);
|
|
(void) sscop_send_erak(sop);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Report error condition
|
|
*/
|
|
sscop_er_error(sop, m, trlr);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* ER PDU / SOS_INRECOV Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_er_inrecov(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct er_pdu *ep = (struct er_pdu *)trlr;
|
|
|
|
/*
|
|
* If retransmitted ER, just ignore it
|
|
*/
|
|
if (sscop_is_rexmit(sop, ep->er_nsq)) {
|
|
KB_FREEALL(m);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Report error condition
|
|
*/
|
|
sscop_er_error(sop, m, trlr);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* ER PDU / SOS_READY Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_er_ready(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct er_pdu *ep = (struct er_pdu *)trlr;
|
|
int err;
|
|
|
|
/*
|
|
* If retransmitted ER, just ACK it
|
|
*/
|
|
if (sscop_is_rexmit(sop, ep->er_nsq)) {
|
|
KB_FREEALL(m);
|
|
sop->so_timer[SSCOP_T_NORESP] = sop->so_parm.sp_timeresp;
|
|
sscop_send_erak(sop);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Stop data transfer timers
|
|
*/
|
|
sop->so_timer[SSCOP_T_POLL] = 0;
|
|
sop->so_timer[SSCOP_T_NORESP] = 0;
|
|
sop->so_timer[SSCOP_T_IDLE] = 0;
|
|
sop->so_flags &= ~SOF_KEEPALIVE;
|
|
|
|
/*
|
|
* Initialize transmit window
|
|
*/
|
|
SEQ_SET(sop->so_sendmax, ntohl(ep->er_nmr));
|
|
|
|
/*
|
|
* Free PDU buffers
|
|
*/
|
|
KB_FREEALL(m);
|
|
|
|
/*
|
|
* Clear out appropriate queues
|
|
*/
|
|
q2110_prep_recovery(sop);
|
|
|
|
/*
|
|
* Deliver any outstanding data to user
|
|
*/
|
|
q2110_deliver_data(sop);
|
|
|
|
/*
|
|
* Notify user of connection recovery
|
|
*/
|
|
STACK_CALL(SSCOP_RECOVER_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, 0, 0, err);
|
|
if (err) {
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Wait for user response
|
|
*/
|
|
sop->so_state = SOS_INRECOV;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* ERAK PDU / Protocol Error
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_erak_error(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
|
|
/*
|
|
* Record error condition
|
|
*/
|
|
sscop_maa_error(sop, 'M');
|
|
KB_FREEALL(m);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* ERAK PDU / SOS_IDLE Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_erak_idle(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
|
|
/*
|
|
* Record error condition
|
|
*/
|
|
sscop_erak_error(sop, m, trlr);
|
|
|
|
/*
|
|
* Return an END to peer
|
|
*/
|
|
(void) sscop_send_end(sop, SSCOP_SOURCE_SSCOP);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* ERAK PDU / SOS_OUTRECOV Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_erak_outrecov(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct erak_pdu *ep = (struct erak_pdu *)trlr;
|
|
int err;
|
|
|
|
/*
|
|
* Stop retransmit timer
|
|
*/
|
|
sop->so_timer[SSCOP_T_CC] = 0;
|
|
|
|
/*
|
|
* Initialize transmit window
|
|
*/
|
|
SEQ_SET(sop->so_sendmax, ntohl(ep->erak_nmr));
|
|
|
|
/*
|
|
* Free PDU buffers
|
|
*/
|
|
KB_FREEALL(m);
|
|
|
|
/*
|
|
* Deliver any outstanding data to user
|
|
*/
|
|
q2110_deliver_data(sop);
|
|
|
|
/*
|
|
* Notify user of connection recovery
|
|
*/
|
|
STACK_CALL(SSCOP_RECOVER_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, 0, 0, err);
|
|
if (err) {
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Wait for user response
|
|
*/
|
|
sop->so_state = SOS_RECOVRSP;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* SD PDU / SOS_READY Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_sd_ready(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct sd_pdu *sp = (struct sd_pdu *)trlr;
|
|
struct pdu_hdr *php;
|
|
KBuffer *n;
|
|
sscop_seq ns;
|
|
int err, space;
|
|
|
|
/*
|
|
* Get PDU sequence number
|
|
*/
|
|
SEQ_SET(ns, ntohl(sp->sd_ns));
|
|
|
|
/*
|
|
* Ensure that the sequence number fits within the window
|
|
*/
|
|
if (SEQ_GEQ(ns, sop->so_rcvmax, sop->so_rcvnext)) {
|
|
/*
|
|
* It doesn't, drop received data
|
|
*/
|
|
KB_FREEALL(m);
|
|
|
|
/*
|
|
* If next highest PDU hasn't reached window end yet,
|
|
* then send a USTAT to inform transmitter of this gap
|
|
*/
|
|
if (SEQ_LT(sop->so_rcvhigh, sop->so_rcvmax, sop->so_rcvnext)) {
|
|
(void) sscop_send_ustat(sop, sop->so_rcvmax);
|
|
sop->so_rcvhigh = sop->so_rcvmax;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If this is the next in-sequence PDU, hand it to user
|
|
*/
|
|
if (ns == sop->so_rcvnext) {
|
|
STACK_CALL(SSCOP_DATA_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, (intptr_t)m, ns, err);
|
|
if (err) {
|
|
KB_FREEALL(m);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Bump next expected sequence number
|
|
*/
|
|
SEQ_INCR(sop->so_rcvnext, 1);
|
|
|
|
/*
|
|
* Slide receive window down
|
|
*/
|
|
SEQ_INCR(sop->so_rcvmax, 1);
|
|
|
|
/*
|
|
* Is this the highest sequence PDU we've received??
|
|
*/
|
|
if (ns == sop->so_rcvhigh) {
|
|
/*
|
|
* Yes, bump the limit and exit
|
|
*/
|
|
sop->so_rcvhigh = sop->so_rcvnext;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This is a retransmitted PDU, so see if we have
|
|
* more in-sequence PDUs already queued up
|
|
*/
|
|
while ((php = sop->so_recv_hd) &&
|
|
(php->ph_ns == sop->so_rcvnext)) {
|
|
|
|
/*
|
|
* Yup we do, so remove next PDU from queue and
|
|
* pass it up to the user as well
|
|
*/
|
|
sop->so_recv_hd = php->ph_recv_lk;
|
|
if (sop->so_recv_hd == NULL)
|
|
sop->so_recv_tl = NULL;
|
|
STACK_CALL(SSCOP_DATA_IND, sop->so_upper, sop->so_toku,
|
|
sop->so_connvc, (intptr_t)php->ph_buf,
|
|
php->ph_ns, err);
|
|
if (err) {
|
|
/*
|
|
* Should never happen, but...
|
|
*/
|
|
KB_FREEALL(php->ph_buf);
|
|
sscop_abort(sop, "stack memory\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Bump next expected sequence number
|
|
*/
|
|
SEQ_INCR(sop->so_rcvnext, 1);
|
|
|
|
/*
|
|
* Slide receive window down
|
|
*/
|
|
SEQ_INCR(sop->so_rcvmax, 1);
|
|
}
|
|
|
|
/*
|
|
* Finished with data delivery...
|
|
*/
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We're gonna have to queue this PDU, so find space
|
|
* for the PDU header
|
|
*/
|
|
KB_HEADROOM(m, space);
|
|
|
|
/*
|
|
* If there's not enough room in the received buffer,
|
|
* allocate & link a new buffer for the header
|
|
*/
|
|
if (space < sizeof(struct pdu_hdr)) {
|
|
|
|
KB_ALLOC(n, sizeof(struct pdu_hdr), KB_F_NOWAIT, KB_T_HEADER);
|
|
if (n == NULL) {
|
|
KB_FREEALL(m);
|
|
return;
|
|
}
|
|
KB_HEADSET(n, sizeof(struct pdu_hdr));
|
|
KB_LEN(n) = 0;
|
|
KB_LINKHEAD(n, m);
|
|
m = n;
|
|
}
|
|
|
|
/*
|
|
* Build PDU header
|
|
*
|
|
* We can at least assume/require that the start of
|
|
* the user data is aligned. Also note that we don't
|
|
* include this header in the buffer len/offset fields.
|
|
*/
|
|
KB_DATASTART(m, php, struct pdu_hdr *);
|
|
php--;
|
|
php->ph_ns = ns;
|
|
php->ph_buf = m;
|
|
|
|
/*
|
|
* Insert PDU into the receive queue
|
|
*/
|
|
if (sscop_recv_insert(sop, php)) {
|
|
/*
|
|
* Oops, a duplicate sequence number PDU is already on
|
|
* the queue, somethings wrong here.
|
|
*/
|
|
sscop_maa_error(sop, 'Q');
|
|
|
|
/*
|
|
* Free buffers
|
|
*/
|
|
KB_FREEALL(m);
|
|
|
|
/*
|
|
* Go into recovery mode
|
|
*/
|
|
q2110_error_recovery(sop);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Are we at the high-water mark??
|
|
*/
|
|
if (ns == sop->so_rcvhigh) {
|
|
/*
|
|
* Yes, just bump the mark
|
|
*/
|
|
SEQ_INCR(sop->so_rcvhigh, 1);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Are we beyond the high-water mark??
|
|
*/
|
|
if (SEQ_GT(ns, sop->so_rcvhigh, sop->so_rcvnext)) {
|
|
/*
|
|
* Yes, then there's a missing PDU, so inform the transmitter
|
|
*/
|
|
(void) sscop_send_ustat(sop, ns);
|
|
|
|
/*
|
|
* Update high-water mark
|
|
*/
|
|
sop->so_rcvhigh = SEQ_ADD(ns, 1);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* POLL PDU / SOS_READY Processor
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* m pointer to PDU buffer (without trailer)
|
|
* trlr pointer to PDU trailer
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
static void
|
|
sscop_poll_ready(sop, m, trlr)
|
|
struct sscop *sop;
|
|
KBuffer *m;
|
|
caddr_t trlr;
|
|
{
|
|
struct poll_pdu *pp = (struct poll_pdu *)trlr;
|
|
sscop_seq nps;
|
|
|
|
pp->poll_ns = ntohl(pp->poll_ns);
|
|
|
|
/*
|
|
* If the poll sequence number is less than highest number
|
|
* we've already seen, something's wrong
|
|
*/
|
|
if (SEQ_LT(pp->poll_ns, sop->so_rcvhigh, sop->so_rcvnext)) {
|
|
/*
|
|
* Record error condition
|
|
*/
|
|
sscop_maa_error(sop, 'Q');
|
|
|
|
/*
|
|
* Free buffers
|
|
*/
|
|
KB_FREEALL(m);
|
|
|
|
/*
|
|
* Go into recovery mode
|
|
*/
|
|
q2110_error_recovery(sop);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Set a new "next highest" sequence number expected
|
|
*/
|
|
if (SEQ_LT(pp->poll_ns, sop->so_rcvmax, sop->so_rcvnext))
|
|
SEQ_SET(sop->so_rcvhigh, pp->poll_ns);
|
|
else
|
|
sop->so_rcvhigh = sop->so_rcvmax;
|
|
|
|
/*
|
|
* Return a STAT PDU to peer
|
|
*/
|
|
SEQ_SET(nps, ntohl(pp->poll_nps));
|
|
KB_FREEALL(m);
|
|
(void) sscop_send_stat(sop, nps);
|
|
|
|
return;
|
|
}
|
|
|