diff options
Diffstat (limited to 'sys/netatm/uni/sscop_pdu.c')
-rw-r--r-- | sys/netatm/uni/sscop_pdu.c | 1237 |
1 files changed, 1237 insertions, 0 deletions
diff --git a/sys/netatm/uni/sscop_pdu.c b/sys/netatm/uni/sscop_pdu.c new file mode 100644 index 0000000..b9aec03 --- /dev/null +++ b/sys/netatm/uni/sscop_pdu.c @@ -0,0 +1,1237 @@ +/* + * + * =================================== + * 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. + * + * @(#) $Id: sscop_pdu.c,v 1.6 1998/04/07 23:21:36 mks Exp $ + * + */ + +/* + * ATM Forum UNI Support + * --------------------- + * + * SSCOP - PDU subroutines + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: sscop_pdu.c,v 1.6 1998/04/07 23:21:36 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <netatm/uni/uni.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 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; + sprintf(buf, "sscop %s: vcc=(%d,%d)\n", msg, vcp->vc_vpi, vcp->vc_vci); + atm_pdu_print(m, buf); +} + |