/* * * =================================== * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef lint __RCSID("@(#) $FreeBSD$"); #endif /* * Local functions */ static KBuffer * sscop_stat_init(struct sscop *); static KBuffer * sscop_stat_add(sscop_seq, KBuffer *); static int sscop_stat_end(struct sscop *, sscop_seq, KBuffer *, KBuffer *); static int sscop_recv_locate(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); }