freebsd-skq/sys/netatm/uni/sscop_pdu.c

1255 lines
23 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 - PDU 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 <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_vc.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 KBuffer * sscop_stat_init __P((struct sscop *));
static KBuffer * sscop_stat_add __P((sscop_seq, KBuffer *));
static int sscop_stat_end __P((struct sscop *, sscop_seq,
KBuffer *, KBuffer *));
static int sscop_recv_locate __P((struct sscop *, sscop_seq,
struct pdu_hdr **));
/*
* Build and send BGN PDU
*
* A BGN PDU will be constructed and passed down the protocol stack.
* The SSCOP-UU/N(UU) field is not supported.
*
* Arguments:
* sop pointer to sscop connection control block
* source originator of BGN PDU (Q.SAAL1 only)
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send pdu
*
*/
int
sscop_send_bgn(sop, source)
struct sscop *sop;
int source;
{
KBuffer *m;
struct bgn_pdu *bp;
int err;
/*
* Get buffer for PDU
*/
KB_ALLOCPKT(m, sizeof(struct bgn_pdu), KB_F_NOWAIT, KB_T_HEADER);
if (m == NULL)
return (1);
/*
* Setup buffer controls
*/
KB_HEADSET(m, MIN(sop->so_headout,
KB_BFRLEN(m) - sizeof(struct bgn_pdu)));
KB_LEN(m) = sizeof(struct bgn_pdu);
/*
* Build PDU
*/
KB_DATASTART(m, bp, struct bgn_pdu *);
*(int *)&bp->bgn_rsvd[0] = 0;
if (sop->so_vers != SSCOP_VERS_QSAAL)
bp->bgn_nsq = sop->so_sendconn;
bp->bgn_nmr =
htonl((PT_BGN << PT_TYPE_SHIFT) | SEQ_VAL(sop->so_rcvmax));
if ((sop->so_vers == SSCOP_VERS_QSAAL) &&
(source == SSCOP_SOURCE_SSCOP))
bp->bgn_type |= PT_SOURCE_SSCOP;
/*
* Send PDU towards peer
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)m, 0, err);
if (err)
KB_FREEALL(m);
return (err);
}
/*
* Build and send BGAK PDU
*
* A BGAK PDU will be constructed and passed down the protocol stack.
* The SSCOP-UU/N(UU) field is not supported.
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send pdu
*
*/
int
sscop_send_bgak(sop)
struct sscop *sop;
{
KBuffer *m;
struct bgak_pdu *bp;
int err;
/*
* Get buffer for PDU
*/
KB_ALLOCPKT(m, sizeof(struct bgak_pdu), KB_F_NOWAIT, KB_T_HEADER);
if (m == NULL)
return (1);
/*
* Setup buffer controls
*/
KB_HEADSET(m, MIN(sop->so_headout,
KB_BFRLEN(m) - sizeof(struct bgak_pdu)));
KB_LEN(m) = sizeof(struct bgak_pdu);
/*
* Build PDU
*/
KB_DATASTART(m, bp, struct bgak_pdu *);
bp->bgak_rsvd = 0;
bp->bgak_nmr =
htonl((PT_BGAK << PT_TYPE_SHIFT) | SEQ_VAL(sop->so_rcvmax));
/*
* Send PDU towards peer
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)m, 0, err);
if (err)
KB_FREEALL(m);
return (err);
}
/*
* Build and send BGREJ PDU
*
* A BGREJ PDU will be constructed and passed down the protocol stack.
* The SSCOP-UU/N(UU) field is not supported.
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send pdu
*
*/
int
sscop_send_bgrej(sop)
struct sscop *sop;
{
KBuffer *m;
struct bgrej_pdu *bp;
int err;
/*
* Get buffer for PDU
*/
KB_ALLOCPKT(m, sizeof(struct bgrej_pdu), KB_F_NOWAIT, KB_T_HEADER);
if (m == NULL)
return (1);
/*
* Setup buffer controls
*/
KB_HEADSET(m, MIN(sop->so_headout,
KB_BFRLEN(m) - sizeof(struct bgrej_pdu)));
KB_LEN(m) = sizeof(struct bgrej_pdu);
/*
* Build PDU
*/
KB_DATASTART(m, bp, struct bgrej_pdu *);
bp->bgrej_rsvd2 = 0;
*(u_int *)&bp->bgrej_type = htonl((PT_BGREJ << PT_TYPE_SHIFT) | 0);
/*
* Send PDU towards peer
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)m, 0, err);
if (err)
KB_FREEALL(m);
return (err);
}
/*
* Build and send END PDU
*
* An END PDU will be constructed and passed down the protocol stack.
* The SSCOP-UU/N(UU) field is not supported.
*
* Arguments:
* sop pointer to sscop connection control block
* source originator of END PDU
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send pdu
*
*/
int
sscop_send_end(sop, source)
struct sscop *sop;
int source;
{
KBuffer *m;
struct end_pdu *ep;
int err;
/*
* Get buffer for PDU
*/
KB_ALLOCPKT(m, sizeof(struct end_pdu), KB_F_NOWAIT, KB_T_HEADER);
if (m == NULL)
return (1);
/*
* Setup buffer controls
*/
KB_HEADSET(m, MIN(sop->so_headout,
KB_BFRLEN(m) - sizeof(struct end_pdu)));
KB_LEN(m) = sizeof(struct end_pdu);
/*
* Build PDU
*/
KB_DATASTART(m, ep, struct end_pdu *);
ep->end_rsvd2 = 0;
*(u_int *)&ep->end_type = htonl((PT_END << PT_TYPE_SHIFT) | 0);
if (source == SSCOP_SOURCE_SSCOP) {
ep->end_type |= PT_SOURCE_SSCOP;
sop->so_flags |= SOF_ENDSSCOP;
} else if (source == SSCOP_SOURCE_USER)
sop->so_flags &= ~SOF_ENDSSCOP;
else if ((source == SSCOP_SOURCE_LAST) &&
(sop->so_flags & SOF_ENDSSCOP))
ep->end_type |= PT_SOURCE_SSCOP;
/*
* Send PDU towards peer
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)m, 0, err);
if (err)
KB_FREEALL(m);
return (err);
}
/*
* Build and send ENDAK PDU
*
* An ENDAK PDU will be constructed and passed down the protocol stack.
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send pdu
*
*/
int
sscop_send_endak(sop)
struct sscop *sop;
{
KBuffer *m;
struct endak_q2110_pdu *e2p;
struct endak_qsaal_pdu *esp;
int err, size;
/*
* Get size of PDU
*/
if (sop->so_vers == SSCOP_VERS_QSAAL)
size = sizeof(struct endak_qsaal_pdu);
else
size = sizeof(struct endak_q2110_pdu);
/*
* Get buffer for PDU
*/
KB_ALLOCPKT(m, size, KB_F_NOWAIT, KB_T_HEADER);
if (m == NULL)
return (1);
/*
* Setup buffer controls
*/
KB_HEADSET(m, MIN(sop->so_headout, KB_BFRLEN(m) - size));
KB_LEN(m) = size;
/*
* Build PDU
*/
if (sop->so_vers == SSCOP_VERS_QSAAL) {
KB_DATASTART(m, esp, struct endak_qsaal_pdu *);
*(u_int *)&esp->endak_type =
htonl((PT_ENDAK << PT_TYPE_SHIFT) | 0);
} else {
KB_DATASTART(m, e2p, struct endak_q2110_pdu *);
e2p->endak_rsvd2 = 0;
*(u_int *)&e2p->endak_type =
htonl((PT_ENDAK << PT_TYPE_SHIFT) | 0);
}
/*
* Send PDU towards peer
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)m, 0, err);
if (err)
KB_FREEALL(m);
return (err);
}
/*
* Build and send RS PDU
*
* A RS PDU will be constructed and passed down the protocol stack.
* The SSCOP-UU/N(UU) field is not supported.
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send pdu
*
*/
int
sscop_send_rs(sop)
struct sscop *sop;
{
KBuffer *m;
struct rs_pdu *rp;
int err;
/*
* Get buffer for PDU
*/
KB_ALLOCPKT(m, sizeof(struct rs_pdu), KB_F_NOWAIT, KB_T_HEADER);
if (m == NULL)
return (1);
/*
* Setup buffer controls
*/
KB_HEADSET(m, MIN(sop->so_headout,
KB_BFRLEN(m) - sizeof(struct rs_pdu)));
KB_LEN(m) = sizeof(struct rs_pdu);
/*
* Build PDU
*/
KB_DATASTART(m, rp, struct rs_pdu *);
*(int *)&rp->rs_rsvd[0] = 0;
if (sop->so_vers != SSCOP_VERS_QSAAL) {
rp->rs_nsq = sop->so_sendconn;
rp->rs_nmr = htonl((PT_RS << PT_TYPE_SHIFT) |
SEQ_VAL(sop->so_rcvmax));
} else {
rp->rs_nmr = htonl((PT_RS << PT_TYPE_SHIFT) | 0);
}
/*
* Send PDU towards peer
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)m, 0, err);
if (err)
KB_FREEALL(m);
return (err);
}
/*
* Build and send RSAK PDU
*
* An RSAK PDU will be constructed and passed down the protocol stack.
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send pdu
*
*/
int
sscop_send_rsak(sop)
struct sscop *sop;
{
KBuffer *m;
struct rsak_q2110_pdu *r2p;
struct rsak_qsaal_pdu *rsp;
int err, size;
/*
* Get size of PDU
*/
if (sop->so_vers == SSCOP_VERS_QSAAL)
size = sizeof(struct rsak_qsaal_pdu);
else
size = sizeof(struct rsak_q2110_pdu);
/*
* Get buffer for PDU
*/
KB_ALLOCPKT(m, size, KB_F_NOWAIT, KB_T_HEADER);
if (m == NULL)
return (1);
/*
* Setup buffer controls
*/
KB_HEADSET(m, MIN(sop->so_headout, KB_BFRLEN(m) - size));
KB_LEN(m) = size;
/*
* Build PDU
*/
if (sop->so_vers == SSCOP_VERS_QSAAL) {
KB_DATASTART(m, rsp, struct rsak_qsaal_pdu *);
*(u_int *)&rsp->rsaks_type =
htonl((PT_RSAK << PT_TYPE_SHIFT) | 0);
} else {
KB_DATASTART(m, r2p, struct rsak_q2110_pdu *);
r2p->rsak_rsvd = 0;
r2p->rsak_nmr = htonl((PT_RSAK << PT_TYPE_SHIFT) |
SEQ_VAL(sop->so_rcvmax));
}
/*
* Send PDU towards peer
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)m, 0, err);
if (err)
KB_FREEALL(m);
return (err);
}
/*
* Build and send ER PDU
*
* An ER PDU will be constructed and passed down the protocol stack.
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send pdu
*
*/
int
sscop_send_er(sop)
struct sscop *sop;
{
KBuffer *m;
struct er_pdu *ep;
int err;
/*
* Get buffer for PDU
*/
KB_ALLOCPKT(m, sizeof(struct er_pdu), KB_F_NOWAIT, KB_T_HEADER);
if (m == NULL)
return (1);
/*
* Setup buffer controls
*/
KB_HEADSET(m, MIN(sop->so_headout,
KB_BFRLEN(m) - sizeof(struct er_pdu)));
KB_LEN(m) = sizeof(struct er_pdu);
/*
* Build PDU
*/
KB_DATASTART(m, ep, struct er_pdu *);
*(int *)&ep->er_rsvd[0] = 0;
ep->er_nsq = sop->so_sendconn;
ep->er_nmr = htonl((PT_ER << PT_TYPE_SHIFT) | SEQ_VAL(sop->so_rcvmax));
/*
* Send PDU towards peer
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)m, 0, err);
if (err)
KB_FREEALL(m);
return (err);
}
/*
* Build and send ERAK PDU
*
* An ERAK PDU will be constructed and passed down the protocol stack.
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send pdu
*
*/
int
sscop_send_erak(sop)
struct sscop *sop;
{
KBuffer *m;
struct erak_pdu *ep;
int err;
/*
* Get buffer for PDU
*/
KB_ALLOCPKT(m, sizeof(struct erak_pdu), KB_F_NOWAIT, KB_T_HEADER);
if (m == NULL)
return (1);
/*
* Setup buffer controls
*/
KB_HEADSET(m, MIN(sop->so_headout,
KB_BFRLEN(m) - sizeof(struct erak_pdu)));
KB_LEN(m) = sizeof(struct erak_pdu);
/*
* Build PDU
*/
KB_DATASTART(m, ep, struct erak_pdu *);
ep->erak_rsvd = 0;
ep->erak_nmr = htonl((PT_ERAK << PT_TYPE_SHIFT) |
SEQ_VAL(sop->so_rcvmax));
/*
* Send PDU towards peer
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)m, 0, err);
if (err)
KB_FREEALL(m);
return (err);
}
/*
* Build and send POLL PDU
*
* A POLL PDU will be constructed and passed down the protocol stack.
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send pdu
*
*/
int
sscop_send_poll(sop)
struct sscop *sop;
{
KBuffer *m;
struct poll_pdu *pp;
int err;
/*
* Get buffer for PDU
*/
KB_ALLOCPKT(m, sizeof(struct poll_pdu), KB_F_NOWAIT, KB_T_HEADER);
if (m == NULL)
return (1);
/*
* Setup buffer controls
*/
KB_HEADSET(m, MIN(sop->so_headout,
KB_BFRLEN(m) - sizeof(struct poll_pdu)));
KB_LEN(m) = sizeof(struct poll_pdu);
/*
* Build PDU
*/
KB_DATASTART(m, pp, struct poll_pdu *);
pp->poll_nps = htonl(SEQ_VAL(sop->so_pollsend));
pp->poll_ns = htonl((PT_POLL << PT_TYPE_SHIFT) | SEQ_VAL(sop->so_send));
/*
* Send PDU towards peer
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)m, 0, err);
if (err)
KB_FREEALL(m);
return (err);
}
/*
* STAT PDU Construction - Initialize for new PDU
*
* Arguments:
* sop pointer to sscop connection control block
*
* Returns:
* addr pointer to initialized buffer
* 0 unable to allocate buffer
*
*/
static KBuffer *
sscop_stat_init(sop)
struct sscop *sop;
{
KBuffer *m;
#define STAT_INIT_SIZE (sizeof(struct stat_pdu) + 2 * sizeof(sscop_seq))
/*
* Get buffer for PDU
*/
KB_ALLOCPKT(m, STAT_INIT_SIZE, KB_F_NOWAIT, KB_T_HEADER);
if (m == NULL)
return (0);
/*
* Setup buffer controls
*/
KB_HEADSET(m, sop->so_headout < (KB_BFRLEN(m) - STAT_INIT_SIZE) ?
sop->so_headout : 0);
KB_LEN(m) = 0;
return (m);
#undef STAT_INIT_SIZE
}
/*
* STAT PDU Construction - Add List Element
*
* Arguments:
* elem sequence number to add to list
* m pointer to current buffer
*
* Returns:
* addr pointer to current buffer (updated)
* 0 buffer allocation failure
*
*/
static KBuffer *
sscop_stat_add(elem, m)
sscop_seq elem;
KBuffer *m;
{
KBuffer *n;
sscop_seq *sp;
int space;
/*
* See if new element will fit in current buffer
*/
KB_TAILROOM(m, space);
if (space < sizeof(elem)) {
/*
* Nope, so get another buffer
*/
KB_ALLOC(n, sizeof(elem), KB_F_NOWAIT, KB_T_DATA);
if (n == NULL)
return (0);
/*
* Link in new buffer
*/
KB_LINK(n, m);
KB_LEN(n) = 0;
m = n;
}
/*
* Add new element
*/
KB_DATAEND(m, sp, sscop_seq *);
*sp = htonl(elem);
KB_LEN(m) += sizeof (elem);
return (m);
}
/*
* STAT PDU Construction - Add Trailer and Send
*
* Arguments:
* sop pointer to sscop connection control block
* nps received poll sequence number (POLL.N(PS))
* head pointer to head of buffer chain
* m pointer to current (last) buffer
*
* Returns:
* 0 STAT successfully sent
* else unable to send STAT or truncated STAT was sent - buffer freed
*
*/
static int
sscop_stat_end(sop, nps, head, m)
struct sscop *sop;
sscop_seq nps;
KBuffer *head;
KBuffer *m;
{
struct stat_pdu *sp;
KBuffer *n;
int err, space, trunc = 0;
/*
* See if PDU trailer will fit in current buffer
*/
KB_TAILROOM(m, space);
if (space < sizeof(struct stat_pdu)) {
/*
* Doesn't fit, so get another buffer
*/
KB_ALLOC(n, sizeof(struct stat_pdu), KB_F_NOWAIT, KB_T_DATA);
if (n == NULL) {
/*
* Out of buffers - truncate elements and send
* what we can, but tell caller that we can't
* send any more segments.
*/
trunc = 1;
do {
KB_LEN(m) -= sizeof(sscop_seq);
space += sizeof(sscop_seq);
} while (space < sizeof(struct stat_pdu));
} else {
/*
* Link in new buffer
*/
KB_LINK(n, m);
KB_LEN(n) = 0;
m = n;
}
}
/*
* Build PDU trailer
*/
KB_DATAEND(m, sp, struct stat_pdu *);
sp->stat_nps = htonl(nps);
sp->stat_nmr = htonl(sop->so_rcvmax);
sp->stat_nr = htonl(sop->so_rcvnext);
sp->stat_type = PT_STAT;
KB_LEN(m) += sizeof(struct stat_pdu);
/*
* Finally, send the STAT
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)head, 0, err);
if (err) {
/*
* We lie about the STACK_CALL failing...
*/
KB_FREEALL(head);
}
if (trunc)
return (1);
else
return (0);
}
/*
* Check for PDU in Receive Queue
*
* A receive queue will be searched for an SD PDU matching the requested
* sequence number. The caller must supply a pointer to the address of the
* PDU in the particular receive queue at which to begin the search. This
* function will update that pointer as it traverses the queue.
*
* Arguments:
* sop pointer to sscop connection control block
* seq sequence number of PDU to locate
* currp address of pointer to PDU in receive queue to start search
*
* Returns:
* 0 reqeusted PDU not in receive queue
* 1 requested PDU located in receive queue
*
*/
static int
sscop_recv_locate(sop, seq, currp)
struct sscop *sop;
sscop_seq seq;
struct pdu_hdr **currp;
{
sscop_seq cs;
/*
* Search queue until we know the answer
*/
while (1) {
/*
* If we're at the end of the queue, the PDU isn't there
*/
if (*currp == NULL)
return (0);
/*
* Get the current PDU sequence number
*/
cs = (*currp)->ph_ns;
/*
* See if we're at the requested PDU
*/
if (seq == cs)
return (1);
/*
* If we're past the requested seq number,
* the PDU isn't there
*/
if (SEQ_LT(seq, cs, sop->so_rcvnext))
return (0);
/*
* Go to next PDU and keep looking
*/
*currp = (*currp)->ph_recv_lk;
}
}
/*
* Build and send STAT PDU
*
* A STAT PDU will be constructed and passed down the protocol stack.
*
* Arguments:
* sop pointer to sscop connection control block
* nps received poll sequence number (POLL.N(PS))
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send complete pdu
*
*/
int
sscop_send_stat(sop, nps)
struct sscop *sop;
sscop_seq nps;
{
KBuffer *head, *curr, *n;
struct pdu_hdr *rq = sop->so_recv_hd;
sscop_seq i;
sscop_seq vrh = sop->so_rcvhigh;
sscop_seq vrr = sop->so_rcvnext;
int len = 0;
/*
* Initialize for start of STAT PDU
*/
head = sscop_stat_init(sop);
if (head == NULL)
return (1);
curr = head;
/*
* Start with first PDU not yet received
*/
i = vrr;
/*
* Keep looping until we get to last sent PDU
*/
while (i != vrh) {
/*
* Find next missing PDU
*/
while (SEQ_LT(i, vrh, vrr) && sscop_recv_locate(sop, i, &rq)) {
SEQ_INCR(i, 1);
}
/*
* Add odd (start of missing gap) STAT element
*/
n = sscop_stat_add(i, curr);
if (n == NULL) {
goto nobufs;
}
curr = n;
len++;
/*
* Have we reached the last sent PDU sequence number??
*/
if (i == vrh) {
/*
* Yes, then we're done, send STAT
*/
break;
}
/*
* Have we reached the max STAT size yet??
*/
if (len >= PDU_MAX_ELEM) {
/*
* Yes, send this STAT segment
*/
if (sscop_stat_end(sop, nps, head, curr)) {
return (1);
}
/*
* Start a new segment
*/
head = sscop_stat_init(sop);
if (head == NULL)
return (1);
curr = head;
/*
* Restart missing gap
*/
curr = sscop_stat_add(i, curr);
if (curr == NULL) {
KB_FREEALL(head);
return (1);
}
len = 1;
}
/*
* Now find the end of the missing gap
*/
do {
SEQ_INCR(i, 1);
} while (SEQ_LT(i, vrh, vrr) &&
(sscop_recv_locate(sop, i, &rq) == 0));
/*
* Add even (start of received gap) STAT element
*/
n = sscop_stat_add(i, curr);
if (n == NULL) {
goto nobufs;
}
curr = n;
len++;
}
/*
* Finally, send the STAT PDU (or last STAT segment)
*/
if (sscop_stat_end(sop, nps, head, curr)) {
return (1);
}
return (0);
nobufs:
/*
* Send a truncated STAT PDU
*/
sscop_stat_end(sop, nps, head, curr);
return (1);
}
/*
* Build and send USTAT PDU
*
* A USTAT PDU will be constructed and passed down the protocol stack.
*
* Arguments:
* sop pointer to sscop connection control block
* ns sequence number for second list element
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send pdu
*
*/
int
sscop_send_ustat(sop, ns)
struct sscop *sop;
sscop_seq ns;
{
KBuffer *m;
struct ustat_pdu *up;
int err;
/*
* Get buffer for PDU
*/
KB_ALLOCPKT(m, sizeof(struct ustat_pdu), KB_F_NOWAIT, KB_T_HEADER);
if (m == NULL)
return (1);
/*
* Setup buffer controls
*/
KB_HEADSET(m, MIN(sop->so_headout,
KB_BFRLEN(m) - sizeof(struct ustat_pdu)));
KB_LEN(m) = sizeof(struct ustat_pdu);
/*
* Build PDU
*/
KB_DATASTART(m, up, struct ustat_pdu *);
up->ustat_le1 = htonl(SEQ_VAL(sop->so_rcvhigh));
up->ustat_le2 = htonl(SEQ_VAL(ns));
up->ustat_nmr = htonl(SEQ_VAL(sop->so_rcvmax));
up->ustat_nr =
htonl((PT_USTAT << PT_TYPE_SHIFT) | SEQ_VAL(sop->so_rcvnext));
/*
* Send PDU towards peer
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)m, 0, err);
if (err)
KB_FREEALL(m);
return (err);
}
/*
* Build and send UD PDU
*
* A UD PDU will be constructed and passed down the protocol stack.
*
* Arguments:
* sop pointer to sscop connection control block
* m pointer to user data buffer chain
*
* Returns:
* 0 PDU successfully built and passed down the stack
* else unable to build or send pdu (buffer released)
*
*/
int
sscop_send_ud(sop, m)
struct sscop *sop;
KBuffer *m;
{
KBuffer *ml, *n;
int len = 0, err;
int pad, trlen, space;
u_char *cp;
/*
* 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) {
KB_FREEALL(m);
sscop_abort(sop, "sscop: maximum unitdata size exceeded\n");
return (1);
}
/*
* Figure out how much padding we'll need
*/
pad = ((len + (PDU_PAD_ALIGN - 1)) & ~(PDU_PAD_ALIGN - 1)) - len;
trlen = pad + sizeof(struct ud_pdu);
/*
* 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.
*/
KB_DATAEND(ml, cp, u_char *);
cp += pad;
*cp++ = (pad << PT_PAD_SHIFT) | PT_UD;
KM_ZERO(cp, 3);
KB_LEN(ml) += trlen;
/*
* Now pass PDU down the stack
*/
STACK_CALL(CPCS_UNITDATA_INV, sop->so_lower, sop->so_tokl,
sop->so_connvc, (int)m, 0, err);
if (err) {
KB_FREEALL(m);
return (1);
}
return (0);
}
/*
* Print an SSCOP PDU
*
* Arguments:
* sop pointer to sscop connection control block
* m pointer to pdu buffer chain
* msg pointer to message string
*
* Returns:
* none
*
*/
void
sscop_pdu_print(sop, m, msg)
struct sscop *sop;
KBuffer *m;
char *msg;
{
char buf[128];
struct vccb *vcp;
vcp = sop->so_connvc->cvc_vcc;
snprintf(buf, sizeof(buf),
"sscop %s: vcc=(%d,%d)\n", msg, vcp->vc_vpi, vcp->vc_vci);
atm_pdu_print(m, buf);
}