989 lines
18 KiB
C
989 lines
18 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.
|
|
*
|
|
* @(#) $FreeBSD$
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* ATM Forum UNI Support
|
|
* ---------------------
|
|
*
|
|
* SSCOP - Subroutines
|
|
*
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/syslog.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>
|
|
|
|
#ifndef lint
|
|
__RCSID("@(#) $FreeBSD$");
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Local functions
|
|
*/
|
|
static int sscop_proc_xmit __P((struct sscop *));
|
|
|
|
|
|
/*
|
|
* Get Next Element from STAT PDU
|
|
*
|
|
* Arguments:
|
|
* m pointer to current buffer in STAT PDU
|
|
* pelem pointer to location to store element value
|
|
*
|
|
* Returns:
|
|
* addr pointer to updated current buffer in STAT PDU
|
|
*
|
|
*/
|
|
KBuffer *
|
|
sscop_stat_getelem(m, pelem)
|
|
KBuffer *m;
|
|
sscop_seq *pelem;
|
|
{
|
|
caddr_t cp;
|
|
|
|
/*
|
|
* Get to start of element
|
|
*
|
|
* Note that we always ensure that the current buffer has
|
|
* at least one byte of the next element.
|
|
*/
|
|
KB_DATASTART(m, cp, caddr_t);
|
|
|
|
/*
|
|
* See how much of element is in this buffer
|
|
*/
|
|
if (KB_LEN(m) >= sizeof(sscop_seq)) {
|
|
/*
|
|
* Get element from this buffer
|
|
*/
|
|
if ((int)cp & (sizeof(sscop_seq) - 1))
|
|
KM_COPY(cp, (caddr_t)pelem, sizeof(sscop_seq));
|
|
else
|
|
*pelem = *(sscop_seq *)cp;
|
|
|
|
/*
|
|
* Update buffer controls
|
|
*/
|
|
KB_HEADADJ(m, -sizeof(sscop_seq));
|
|
} else {
|
|
/*
|
|
* Get element split between two buffers
|
|
*/
|
|
int i, j;
|
|
|
|
/*
|
|
* Copy what's in this buffer
|
|
*/
|
|
i = KB_LEN(m);
|
|
KM_COPY(cp, (caddr_t)pelem, i);
|
|
KB_LEN(m) = 0;
|
|
|
|
/*
|
|
* Now get to next buffer
|
|
*/
|
|
while (m && (KB_LEN(m) == 0))
|
|
m = KB_NEXT(m);
|
|
|
|
/*
|
|
* And copy remainder of element
|
|
*/
|
|
j = sizeof(sscop_seq) - i;
|
|
KB_DATASTART(m, cp, caddr_t);
|
|
KM_COPY(cp, (caddr_t)pelem + i, j);
|
|
|
|
/*
|
|
* Update buffer controls
|
|
*/
|
|
KB_HEADADJ(m, -j);
|
|
}
|
|
|
|
/*
|
|
* Put element (sequence number) into host order
|
|
*/
|
|
NTOHL(*pelem);
|
|
|
|
/*
|
|
* Get pointers set for next call
|
|
*/
|
|
while (m && (KB_LEN(m) == 0))
|
|
m = KB_NEXT(m);
|
|
|
|
return (m);
|
|
}
|
|
|
|
|
|
/*
|
|
* Locate SD PDU on Pending Ack Queue
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* seq sequence number of PDU to locate
|
|
*
|
|
* Returns:
|
|
* addr pointer to located PDU header
|
|
* 0 SD PDU sequence number not found
|
|
*
|
|
*/
|
|
struct pdu_hdr *
|
|
sscop_pack_locate(sop, seq)
|
|
struct sscop *sop;
|
|
sscop_seq seq;
|
|
{
|
|
struct pdu_hdr *php;
|
|
|
|
/*
|
|
* Loop thru queue until we either find the PDU or the queue's
|
|
* sequence numbers are greater than the PDU's sequence number,
|
|
* indicating that the PDU is not on the queue.
|
|
*/
|
|
for (php = sop->so_pack_hd; php; php = php->ph_pack_lk) {
|
|
if (php->ph_ns == seq)
|
|
break;
|
|
|
|
if (SEQ_GT(php->ph_ns, seq, sop->so_ack)) {
|
|
php = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (php);
|
|
}
|
|
|
|
|
|
/*
|
|
* Free Acknowledged SD PDU
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* seq sequence number of PDU to free
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
sscop_pack_free(sop, seq)
|
|
struct sscop *sop;
|
|
sscop_seq seq;
|
|
{
|
|
struct pdu_hdr *php, *prev;
|
|
|
|
/*
|
|
* Unlink PDU from pending ack queue
|
|
*
|
|
* First, check for an empty queue
|
|
*/
|
|
php = sop->so_pack_hd;
|
|
if (php == NULL)
|
|
return;
|
|
|
|
/*
|
|
* Now check for PDU at head of queue
|
|
*/
|
|
if (php->ph_ns == seq) {
|
|
if ((sop->so_pack_hd = php->ph_pack_lk) == NULL)
|
|
sop->so_pack_tl = NULL;
|
|
goto found;
|
|
}
|
|
|
|
/*
|
|
* Otherwise, loop thru queue until we either find the PDU or
|
|
* the queue's sequence numbers are greater than the PDU's
|
|
* sequence number, indicating that the PDU is not on the queue.
|
|
*/
|
|
prev = php;
|
|
php = php->ph_pack_lk;
|
|
while (php) {
|
|
if (php->ph_ns == seq) {
|
|
if ((prev->ph_pack_lk = php->ph_pack_lk) == NULL)
|
|
sop->so_pack_tl = prev;
|
|
goto found;
|
|
}
|
|
|
|
if (SEQ_GT(php->ph_ns, seq, sop->so_ack))
|
|
return;
|
|
|
|
prev = php;
|
|
php = php->ph_pack_lk;
|
|
}
|
|
|
|
return;
|
|
|
|
found:
|
|
/*
|
|
* We've got the ack'ed PDU - unlink it from retransmit queue
|
|
*/
|
|
sscop_rexmit_unlink(sop, php);
|
|
|
|
/*
|
|
* Free PDU buffers
|
|
*/
|
|
KB_FREEALL(php->ph_buf);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Insert SD PDU into Retransmit Queue
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* php pointer to SD PDU header
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
sscop_rexmit_insert(sop, php)
|
|
struct sscop *sop;
|
|
struct pdu_hdr *php;
|
|
{
|
|
struct pdu_hdr *curr, *next;
|
|
sscop_seq seq = php->ph_ns;
|
|
|
|
/*
|
|
* Check for an empty queue
|
|
*/
|
|
if ((curr = sop->so_rexmit_hd) == NULL) {
|
|
php->ph_rexmit_lk = NULL;
|
|
sop->so_rexmit_hd = php;
|
|
sop->so_rexmit_tl = php;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Now see if PDU belongs at head of queue
|
|
*/
|
|
if (SEQ_LT(seq, curr->ph_ns, sop->so_ack)) {
|
|
php->ph_rexmit_lk = curr;
|
|
sop->so_rexmit_hd = php;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Otherwise, loop thru the queue until we find the
|
|
* proper insertion point for the PDU
|
|
*/
|
|
while ((next = curr->ph_rexmit_lk) != NULL) {
|
|
if (SEQ_LT(seq, next->ph_ns, sop->so_ack)) {
|
|
php->ph_rexmit_lk = next;
|
|
curr->ph_rexmit_lk = php;
|
|
return;
|
|
}
|
|
curr = next;
|
|
}
|
|
|
|
/*
|
|
* Insert PDU at end of queue
|
|
*/
|
|
php->ph_rexmit_lk = NULL;
|
|
curr->ph_rexmit_lk = php;
|
|
sop->so_rexmit_tl = php;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Unlink SD PDU from Retransmit Queue
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* php pointer to PDU header to unlink
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
sscop_rexmit_unlink(sop, php)
|
|
struct sscop *sop;
|
|
struct pdu_hdr *php;
|
|
{
|
|
struct pdu_hdr *curr;
|
|
|
|
/*
|
|
* See if PDU is on retransmit queue
|
|
*/
|
|
if ((php->ph_rexmit_lk == NULL) && (sop->so_rexmit_tl != php))
|
|
return;
|
|
|
|
/*
|
|
* It's here somewhere, so first check for the PDU at the
|
|
* head of the queue
|
|
*/
|
|
if (php == sop->so_rexmit_hd) {
|
|
if ((sop->so_rexmit_hd = php->ph_rexmit_lk) == NULL)
|
|
sop->so_rexmit_tl = NULL;
|
|
php->ph_rexmit_lk = NULL;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Otherwise, loop thru the queue until we find the PDU
|
|
*/
|
|
for (curr = sop->so_rexmit_hd; curr; curr = curr->ph_rexmit_lk) {
|
|
if (curr->ph_rexmit_lk == php)
|
|
break;
|
|
}
|
|
if (curr) {
|
|
if ((curr->ph_rexmit_lk = php->ph_rexmit_lk) == NULL)
|
|
sop->so_rexmit_tl = curr;
|
|
} else {
|
|
log(LOG_ERR,
|
|
"sscop_rexmit_unlink: Not found - sop=%p, php=%p\n",
|
|
sop, php);
|
|
#ifdef DIAGNOSTIC
|
|
panic("sscop_rexmit_unlink: Not found");
|
|
#endif
|
|
}
|
|
php->ph_rexmit_lk = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Drain Transmission Queues
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
sscop_xmit_drain(sop)
|
|
struct sscop *sop;
|
|
{
|
|
KBuffer *m;
|
|
struct pdu_hdr *php;
|
|
|
|
/*
|
|
* Free transmission queue buffers
|
|
*/
|
|
while ((m = sop->so_xmit_hd) != NULL) {
|
|
sop->so_xmit_hd = KB_QNEXT(m);
|
|
KB_FREEALL(m);
|
|
}
|
|
sop->so_xmit_tl = NULL;
|
|
|
|
/*
|
|
* Free retransmission queue
|
|
*
|
|
* All retranmission buffers are also on the pending ack
|
|
* queue (but not the converse), so we just clear the queue
|
|
* pointers here and do all the real work below.
|
|
*/
|
|
sop->so_rexmit_hd = NULL;
|
|
sop->so_rexmit_tl = NULL;
|
|
|
|
/*
|
|
* Free pending ack queue buffers
|
|
*/
|
|
while ((php = sop->so_pack_hd) != NULL) {
|
|
sop->so_pack_hd = php->ph_pack_lk;
|
|
KB_FREEALL(php->ph_buf);
|
|
}
|
|
sop->so_pack_tl = NULL;
|
|
|
|
/*
|
|
* Clear service required flag
|
|
*/
|
|
sop->so_flags &= ~SOF_XMITSRVC;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Insert SD PDU into Receive Queue
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* php pointer to SD PDU header
|
|
*
|
|
* Returns:
|
|
* 0 PDU successfully inserted into queue
|
|
* 1 duplicate sequence number PDU on queue, PDU not inserted
|
|
*
|
|
*/
|
|
int
|
|
sscop_recv_insert(sop, php)
|
|
struct sscop *sop;
|
|
struct pdu_hdr *php;
|
|
{
|
|
struct pdu_hdr *curr, *next;
|
|
sscop_seq seq = php->ph_ns;
|
|
|
|
/*
|
|
* Check for an empty queue
|
|
*/
|
|
if ((curr = sop->so_recv_hd) == NULL) {
|
|
php->ph_recv_lk = NULL;
|
|
sop->so_recv_hd = php;
|
|
sop->so_recv_tl = php;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Now see if PDU belongs at head of queue
|
|
*/
|
|
if (SEQ_LT(seq, curr->ph_ns, sop->so_rcvnext)) {
|
|
php->ph_recv_lk = curr;
|
|
sop->so_recv_hd = php;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Otherwise, loop thru the queue until we find the
|
|
* proper insertion point for the PDU. We also check
|
|
* to make sure there isn't a PDU already on the queue
|
|
* with a matching sequence number.
|
|
*/
|
|
while ((next = curr->ph_recv_lk) != NULL) {
|
|
if (SEQ_LT(seq, next->ph_ns, sop->so_rcvnext)) {
|
|
if (seq == curr->ph_ns)
|
|
return (1);
|
|
php->ph_recv_lk = next;
|
|
curr->ph_recv_lk = php;
|
|
return (0);
|
|
}
|
|
curr = next;
|
|
}
|
|
|
|
/*
|
|
* Insert PDU at end of queue
|
|
*/
|
|
if (seq == curr->ph_ns)
|
|
return (1);
|
|
php->ph_recv_lk = NULL;
|
|
curr->ph_recv_lk = php;
|
|
sop->so_recv_tl = php;
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Drain Receiver Queues
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
sscop_rcvr_drain(sop)
|
|
struct sscop *sop;
|
|
{
|
|
struct pdu_hdr *php;
|
|
|
|
/*
|
|
* Free receive queue buffers
|
|
*/
|
|
while ((php = sop->so_recv_hd) != NULL) {
|
|
sop->so_recv_hd = php->ph_recv_lk;
|
|
KB_FREEALL(php->ph_buf);
|
|
}
|
|
sop->so_recv_tl = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Service connection's transmit queues
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
sscop_service_xmit(sop)
|
|
struct sscop *sop;
|
|
{
|
|
KBuffer *m, *n;
|
|
struct pdu_hdr *php;
|
|
int err = 0, pollsent = 0;
|
|
|
|
/*
|
|
* Initially assume we need service
|
|
*/
|
|
sop->so_flags |= SOF_XMITSRVC;
|
|
|
|
/*
|
|
* Loop until done with queues
|
|
*
|
|
* (Congestion control will be added later)
|
|
*/
|
|
while (1) {
|
|
if ((php = sop->so_rexmit_hd) != NULL) {
|
|
|
|
/*
|
|
* Send SD PDU from retransmit queue
|
|
*
|
|
* First, get a copy of the PDU to send
|
|
*/
|
|
m = php->ph_buf;
|
|
if (KB_LEN(m) == 0)
|
|
m = KB_NEXT(m);
|
|
KB_COPY(m, 0, KB_COPYALL, n, KB_F_NOWAIT);
|
|
if (n == NULL) {
|
|
err = 1;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Now pass it down the stack
|
|
*/
|
|
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower,
|
|
sop->so_tokl, sop->so_connvc, (int)n, 0, err);
|
|
if (err) {
|
|
KB_FREEALL(n);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* PDU is on its way, so remove it from
|
|
* the retransmit queue
|
|
*/
|
|
if (sop->so_rexmit_tl == php) {
|
|
sop->so_rexmit_hd = NULL;
|
|
sop->so_rexmit_tl = NULL;
|
|
} else {
|
|
sop->so_rexmit_hd = php->ph_rexmit_lk;
|
|
}
|
|
php->ph_rexmit_lk = NULL;
|
|
|
|
/*
|
|
* Update PDU's poll sequence
|
|
*/
|
|
php->ph_nps = sop->so_pollsend;
|
|
|
|
} else if (sop->so_xmit_hd) {
|
|
|
|
/*
|
|
* Newly arrived data waiting to be sent.
|
|
* See if transmit window allows us to send it.
|
|
*/
|
|
if (SEQ_LT(sop->so_send, sop->so_sendmax, sop->so_ack)){
|
|
/*
|
|
* OK, send SD PDU from transmission queue
|
|
*/
|
|
err = sscop_proc_xmit(sop);
|
|
if (err)
|
|
break;
|
|
} else {
|
|
/*
|
|
* Can't send now, so leave idle phase.
|
|
*/
|
|
if (sop->so_timer[SSCOP_T_IDLE] != 0) {
|
|
sop->so_timer[SSCOP_T_IDLE] = 0;
|
|
sop->so_timer[SSCOP_T_NORESP] =
|
|
sop->so_parm.sp_timeresp;
|
|
err = 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
* We're finished, so clear service required flag
|
|
*/
|
|
sop->so_flags &= ~SOF_XMITSRVC;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* We've sent another SD PDU
|
|
*/
|
|
sop->so_polldata++;
|
|
|
|
/*
|
|
* Transition into active (polling) phase
|
|
*/
|
|
if (sop->so_timer[SSCOP_T_POLL] != 0) {
|
|
if (sop->so_flags & SOF_KEEPALIVE) {
|
|
/*
|
|
* Leaving transient phase
|
|
*/
|
|
sop->so_flags &= ~SOF_KEEPALIVE;
|
|
sop->so_timer[SSCOP_T_POLL] =
|
|
sop->so_parm.sp_timepoll;
|
|
}
|
|
} else {
|
|
/*
|
|
* Leaving idle phase
|
|
*/
|
|
sop->so_timer[SSCOP_T_IDLE] = 0;
|
|
sop->so_timer[SSCOP_T_NORESP] =
|
|
sop->so_parm.sp_timeresp;
|
|
sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
|
|
}
|
|
|
|
/*
|
|
* Let's see if we need to send a POLL yet
|
|
*/
|
|
if (sop->so_polldata < sop->so_parm.sp_maxpd)
|
|
continue;
|
|
|
|
/*
|
|
* Yup, send another poll out
|
|
*/
|
|
SEQ_INCR(sop->so_pollsend, 1);
|
|
(void) sscop_send_poll(sop);
|
|
pollsent++;
|
|
|
|
/*
|
|
* Reset data counter for this poll cycle
|
|
*/
|
|
sop->so_polldata = 0;
|
|
|
|
/*
|
|
* Restart polling timer in active phase
|
|
*/
|
|
sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
|
|
}
|
|
|
|
/*
|
|
* If we need/want to send a poll, but haven't sent any yet
|
|
* on this servicing, send one now
|
|
*/
|
|
if (err && (pollsent == 0)) {
|
|
/*
|
|
* Send poll
|
|
*/
|
|
SEQ_INCR(sop->so_pollsend, 1);
|
|
(void) sscop_send_poll(sop);
|
|
|
|
/*
|
|
* Reset data counter for this poll cycle
|
|
*/
|
|
sop->so_polldata = 0;
|
|
|
|
/*
|
|
* Restart polling timer in active phase
|
|
*/
|
|
sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
|
|
sop->so_flags &= ~SOF_KEEPALIVE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Process Transmission Queue PDU
|
|
*
|
|
* For the first entry on the transmission queue: add a PDU header and
|
|
* trailer, send a copy of the PDU down the stack and move the PDU from
|
|
* the transmission queue to the pending ack queue.
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
*
|
|
* Returns:
|
|
* 0 head of transmission queue successfully processed
|
|
* else processing error, tranmission queue unchanged
|
|
*
|
|
*/
|
|
static int
|
|
sscop_proc_xmit(sop)
|
|
struct sscop *sop;
|
|
{
|
|
KBuffer *m, *ml, *n;
|
|
struct pdu_hdr *php;
|
|
sscop_seq seq;
|
|
int len = 0, err;
|
|
int pad, trlen, space;
|
|
u_char *cp;
|
|
|
|
/*
|
|
* Get first buffer chain on queue
|
|
*/
|
|
if ((m = sop->so_xmit_hd) == NULL)
|
|
return (0);
|
|
|
|
/*
|
|
* Count data and get to last buffer in chain
|
|
*/
|
|
for (ml = m; ; ml = KB_NEXT(ml)) {
|
|
len += KB_LEN(ml);
|
|
if (KB_NEXT(ml) == NULL)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Verify data length
|
|
*/
|
|
if (len > sop->so_parm.sp_maxinfo) {
|
|
sscop_abort(sop, "sscop: maximum data size exceeded\n");
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Get space for PDU header
|
|
*/
|
|
KB_HEADROOM(m, space);
|
|
if (space < sizeof(struct pdu_hdr)) {
|
|
/*
|
|
* Allocate & link buffer for header
|
|
*/
|
|
KB_ALLOC(n, sizeof(struct pdu_hdr), KB_F_NOWAIT, KB_T_HEADER);
|
|
if (n == NULL)
|
|
return (1);
|
|
|
|
KB_LEN(n) = 0;
|
|
KB_HEADSET(n, sizeof(struct pdu_hdr));
|
|
KB_LINKHEAD(n, m);
|
|
KB_QNEXT(n) = KB_QNEXT(m);
|
|
KB_QNEXT(m) = NULL;
|
|
sop->so_xmit_hd = n;
|
|
if (sop->so_xmit_tl == m)
|
|
sop->so_xmit_tl = n;
|
|
m = n;
|
|
}
|
|
|
|
/*
|
|
* Figure out how much padding we'll need
|
|
*/
|
|
pad = ((len + (PDU_PAD_ALIGN - 1)) & ~(PDU_PAD_ALIGN - 1)) - len;
|
|
trlen = pad + sizeof(struct sd_pdu);
|
|
|
|
/*
|
|
* Now get space for PDU trailer and padding
|
|
*/
|
|
KB_TAILROOM(ml, space);
|
|
if (space < trlen) {
|
|
/*
|
|
* Allocate & link buffer for pad and trailer
|
|
*/
|
|
KB_ALLOC(n, trlen, KB_F_NOWAIT, KB_T_HEADER);
|
|
if (n == NULL)
|
|
return (1);
|
|
|
|
KB_LEN(n) = 0;
|
|
KB_LINK(n, ml);
|
|
ml = n;
|
|
}
|
|
|
|
/*
|
|
* Build the PDU trailer
|
|
*
|
|
* Since we can't be sure of alignment in the buffers, we
|
|
* have to move this a byte at a time and we have to be
|
|
* careful with host byte order issues.
|
|
*/
|
|
KB_DATASTART(ml, cp, u_char *);
|
|
cp += KB_LEN(ml) + pad;
|
|
*cp++ = (pad << PT_PAD_SHIFT) | PT_SD;
|
|
seq = sop->so_send;
|
|
*(cp + 2) = (u_char)(seq & 0xff);
|
|
seq >>= 8;
|
|
*(cp + 1) = (u_char)(seq & 0xff);
|
|
seq >>= 8;
|
|
*(cp) = (u_char)(seq & 0xff);
|
|
KB_LEN(ml) += trlen;
|
|
|
|
/*
|
|
* Get a copy of the SD PDU to send
|
|
*/
|
|
if (KB_LEN(m) == 0)
|
|
n = KB_NEXT(m);
|
|
else
|
|
n = m;
|
|
KB_COPY(n, 0, KB_COPYALL, n, KB_F_NOWAIT);
|
|
if (n == NULL) {
|
|
KB_LEN(ml) -= trlen;
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Now pass copy down the stack
|
|
*/
|
|
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
|
|
sop->so_connvc, (int)n, 0, err);
|
|
if (err) {
|
|
KB_FREEALL(n);
|
|
KB_LEN(ml) -= trlen;
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* PDU is on its way, so remove buffer from
|
|
* the transmission queue
|
|
*/
|
|
if (sop->so_xmit_tl == m) {
|
|
sop->so_xmit_hd = NULL;
|
|
sop->so_xmit_tl = NULL;
|
|
} else {
|
|
sop->so_xmit_hd = KB_QNEXT(m);
|
|
}
|
|
KB_QNEXT(m) = NULL;
|
|
|
|
/*
|
|
* 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 = sop->so_send;
|
|
php->ph_nps = sop->so_pollsend;
|
|
php->ph_buf = m;
|
|
php->ph_rexmit_lk = NULL;
|
|
php->ph_pack_lk = NULL;
|
|
|
|
/*
|
|
* Put PDU onto the pending ack queue
|
|
*/
|
|
if (sop->so_pack_hd == NULL)
|
|
sop->so_pack_hd = php;
|
|
else
|
|
sop->so_pack_tl->ph_pack_lk = php;
|
|
sop->so_pack_tl = php;
|
|
|
|
/*
|
|
* Finally, bump send sequence number
|
|
*/
|
|
SEQ_INCR(sop->so_send, 1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Detect Retransmitted PDUs
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
* nsq connection sequence value (N(SQ)) from received PDU
|
|
*
|
|
* Returns:
|
|
* 0 received PDU was NOT retransmitted
|
|
* 1 received PDU was retransmitted
|
|
*
|
|
*/
|
|
int
|
|
sscop_is_rexmit(sop, nsq)
|
|
struct sscop *sop;
|
|
u_char nsq;
|
|
{
|
|
|
|
/*
|
|
* For Q.SAAL1, N(SQ) doesn't exist
|
|
*/
|
|
if (sop->so_vers == SSCOP_VERS_QSAAL)
|
|
return (0);
|
|
|
|
/*
|
|
* If we've already received the N(SQ) value,
|
|
* then this PDU has been retransmitted
|
|
*/
|
|
if (nsq == sop->so_rcvconn)
|
|
return (1);
|
|
|
|
/*
|
|
* New PDU, save its N(SQ)
|
|
*/
|
|
sop->so_rcvconn = nsq;
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Start connection poll timer
|
|
*
|
|
* Arguments:
|
|
* sop pointer to sscop connection block
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
sscop_set_poll(sop)
|
|
struct sscop *sop;
|
|
{
|
|
|
|
/*
|
|
* Decide which polling timer value to set
|
|
*/
|
|
if ((sop->so_xmit_hd != NULL) || SEQ_NEQ(sop->so_send, sop->so_ack)) {
|
|
/*
|
|
* Data outstanding, poll frequently
|
|
*/
|
|
sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timepoll;
|
|
sop->so_flags &= ~SOF_KEEPALIVE;
|
|
} else {
|
|
/*
|
|
* No data outstanding, just poll occassionally
|
|
*/
|
|
sop->so_timer[SSCOP_T_POLL] = sop->so_parm.sp_timekeep;
|
|
sop->so_flags |= SOF_KEEPALIVE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|