freebsd-dev/sys/netatm/uni/sscop_upper.c
2005-01-07 01:45:51 +00:00

422 lines
10 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
* ---------------------
*
* SSCOP - CPCS SAP interface processing
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.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>
/*
* Local functions
*/
static caddr_t sscop_pdu_receive(KBuffer *, struct sscop *, int *);
/*
* Local variables
*/
static union {
struct bgn_pdu t_bgn;
struct bgak_pdu t_bgak;
struct end_pdu t_end;
struct endak_q2110_pdu t_endak_q2110;
struct endak_qsaal_pdu t_endak_qsaal;
struct rs_pdu t_rs;
struct rsak_q2110_pdu t_rsak_q2110;
struct rsak_qsaal_pdu t_rsak_qsaal;
struct bgrej_pdu t_bgrej;
struct sd_pdu t_sd;
struct sdp_pdu t_sdp;
struct er_pdu t_er;
struct poll_pdu t_poll;
struct stat_pdu t_stat;
struct ustat_pdu t_ustat;
struct ud_pdu t_ud;
struct md_pdu t_md;
struct erak_pdu t_erak;
} sscop_trailer;
/*
* PDU length validation table
*/
struct pdulen {
int min;
int max;
};
static struct pdulen qsaal_pdulen[] = {
{0, 0},
{sizeof(struct bgn_pdu), sizeof(struct bgn_pdu)},
{sizeof(struct bgak_pdu), sizeof(struct bgak_pdu)},
{sizeof(struct end_pdu), sizeof(struct end_pdu)},
{sizeof(struct endak_qsaal_pdu),sizeof(struct endak_qsaal_pdu)},
{sizeof(struct rs_pdu), sizeof(struct rs_pdu)},
{sizeof(struct rsak_qsaal_pdu), sizeof(struct rsak_qsaal_pdu)},
{sizeof(struct bgrej_pdu), sizeof(struct bgrej_pdu)},
{sizeof(struct sd_pdu), sizeof(struct sd_pdu) + PDU_MAX_INFO},
{sizeof(struct sdp_pdu), sizeof(struct sdp_pdu) + PDU_MAX_INFO},
{sizeof(struct poll_pdu), sizeof(struct poll_pdu)},
{sizeof(struct stat_pdu), sizeof(struct stat_pdu) + PDU_MAX_STAT},
{sizeof(struct ustat_pdu), sizeof(struct ustat_pdu)},
{sizeof(struct ud_pdu), sizeof(struct ud_pdu) + PDU_MAX_INFO},
{sizeof(struct md_pdu), sizeof(struct md_pdu) + PDU_MAX_INFO},
{0, 0}
};
static struct pdulen q2110_pdulen[] = {
{0, 0},
{sizeof(struct bgn_pdu), sizeof(struct bgn_pdu) + PDU_MAX_UU},
{sizeof(struct bgak_pdu), sizeof(struct bgak_pdu) + PDU_MAX_UU},
{sizeof(struct end_pdu), sizeof(struct end_pdu) + PDU_MAX_UU},
{sizeof(struct endak_q2110_pdu),sizeof(struct endak_q2110_pdu)},
{sizeof(struct rs_pdu), sizeof(struct rs_pdu) + PDU_MAX_UU},
{sizeof(struct rsak_q2110_pdu), sizeof(struct rsak_q2110_pdu)},
{sizeof(struct bgrej_pdu), sizeof(struct bgrej_pdu) + PDU_MAX_UU},
{sizeof(struct sd_pdu), sizeof(struct sd_pdu) + PDU_MAX_INFO},
{sizeof(struct er_pdu), sizeof(struct er_pdu)},
{sizeof(struct poll_pdu), sizeof(struct poll_pdu)},
{sizeof(struct stat_pdu), sizeof(struct stat_pdu) + PDU_MAX_STAT},
{sizeof(struct ustat_pdu), sizeof(struct ustat_pdu)},
{sizeof(struct ud_pdu), sizeof(struct ud_pdu) + PDU_MAX_INFO},
{sizeof(struct md_pdu), sizeof(struct md_pdu) + PDU_MAX_INFO},
{sizeof(struct erak_pdu), sizeof(struct erak_pdu)}
};
/*
* PDUs with Pad Length Fields
*/
static u_char qsaal_padlen[] = {
0, /* --- */
0, /* BGN */
0, /* BGAK */
0, /* END */
0, /* ENDAK */
0, /* RS */
0, /* RSAK */
0, /* BGREJ */
1, /* SD */
1, /* SDP */
0, /* POLL */
0, /* STAT */
0, /* USTAT */
1, /* UD */
1, /* MD */
0 /* --- */
};
static u_char q2110_padlen[] = {
0, /* --- */
1, /* BGN */
1, /* BGAK */
1, /* END */
0, /* ENDAK */
1, /* RS */
0, /* RSAK */
1, /* BGREJ */
1, /* SD */
0, /* ER */
0, /* POLL */
0, /* STAT */
0, /* USTAT */
1, /* UD */
1, /* MD */
0 /* ERAK */
};
/*
* SSCOP Upper Stack Command Handler
*
* This function will receive all of the stack commands issued from the
* layer below SSCOP (ie. CPCS). Currently, only incoming PDUs will be
* received here. The appropriate processing function will be determined
* based on the received PDU type and the current sscop control block state.
*
* Arguments:
* cmd stack command code
* tok session token
* arg1 command specific argument
* arg2 command specific argument
*
* Returns:
* none
*
*/
void
sscop_upper(cmd, tok, arg1, arg2)
int cmd;
void *tok;
intptr_t arg1;
intptr_t arg2;
{
struct sscop *sop = (struct sscop *)tok;
void (**ptab)(struct sscop *, KBuffer *, caddr_t);
void (*func)(struct sscop *, KBuffer *, caddr_t);
caddr_t trlr;
int type;
ATM_DEBUG5("sscop_upper: cmd=0x%x, sop=%p, state=%d, arg1=%p, arg2=%p\n",
cmd, sop, sop->so_state, (void *)arg1, (void *)arg2);
switch (cmd) {
case CPCS_UNITDATA_SIG:
/*
* Decode/validate received PDU
*/
trlr = sscop_pdu_receive((KBuffer *)arg1, sop, &type);
if (trlr == NULL) {
return;
}
/*
* Validate sscop state
*/
if (sop->so_state > SOS_MAXSTATE) {
log(LOG_ERR,
"sscop_upper: invalid state sop=%p, state=%d\n",
sop, sop->so_state);
KB_FREEALL((KBuffer *)arg1);
return;
}
/*
* Call event processing function
*/
ptab = sop->so_vers == SSCOP_VERS_QSAAL ?
sscop_qsaal_pdutab[type]:
sscop_q2110_pdutab[type];
func = ptab[sop->so_state];
if (func == NULL) {
log(LOG_ERR,
"sscop_upper: unsupported pdu=%d, state=%d\n",
type, sop->so_state);
break;
}
(*func)(sop, (KBuffer *)arg1, trlr);
break;
default:
log(LOG_ERR, "sscop_upper: unknown cmd 0x%x, sop=%p\n",
cmd, sop);
}
return;
}
/*
* Decode and Validate Received PDU
*
* This function will process all received SSCOP PDUs. The PDU type will be
* determined and PDU format validation will be performed. If the PDU is
* successfully decoded and validated, the buffer chain will have the PDU
* trailer removed, but any resultant zero-length buffers will NOT be freed.
* If the PDU fails validation, then the buffer chain will be freed.
*
* Arguments:
* m pointer to PDU buffer chain
* sop pointer to sscop connection block
* typep address to store PDU type
*
* Returns:
* addr pointer to (contiguous) PDU trailer
* 0 invalid PDU, buffer chain freed
*
*/
static caddr_t
sscop_pdu_receive(m, sop, typep)
KBuffer *m;
struct sscop *sop;
int *typep;
{
KBuffer *m0, *ml, *mn;
caddr_t cp, tp;
int len, tlen, type, plen;
/*
* Calculate PDU length and find the last two buffers in the chain
*/
len = 0;
for (m0 = m, ml = mn = NULL; m0; m0 = KB_NEXT(m0)) {
len += KB_LEN(m0);
mn = ml;
ml = m0;
}
/*
* Make sure we've got a minimum sized PDU
*/
if (len < PDU_MIN_LEN)
goto badpdu;
/*
* Get PDU type field
*/
if (KB_LEN(ml) >= PDU_MIN_LEN) {
KB_DATAEND(ml, tp, caddr_t);
tp -= PDU_MIN_LEN;
} else {
KB_DATAEND(mn, tp, caddr_t);
tp -= (PDU_MIN_LEN - KB_LEN(ml));
}
*typep = type = *tp & PT_TYPE_MASK;
/*
* Check up on PDU length
*/
if (sop->so_vers == SSCOP_VERS_QSAAL) {
if ((len < (tlen = qsaal_pdulen[type].min)) ||
(len > qsaal_pdulen[type].max) ||
(len & PDU_LEN_MASK))
goto badpdu;
} else {
if ((len < (tlen = q2110_pdulen[type].min)) ||
(len > q2110_pdulen[type].max) ||
(len & PDU_LEN_MASK))
goto badpdu;
}
/*
* Get a contiguous, aligned PDU trailer and adjust buffer
* controls to remove trailer
*/
if (KB_LEN(ml) >= tlen) {
/*
* Trailer is contained in last buffer
*/
KB_TAILADJ(ml, -tlen);
KB_DATAEND(ml, cp, caddr_t);
if ((intptr_t)cp & PDU_ADDR_MASK) {
/*
* Trailer not aligned in buffer, use local memory
*/
bcopy(cp, (caddr_t)&sscop_trailer, tlen);
cp = (caddr_t)&sscop_trailer;
}
} else {
/*
* Trailer is split across buffers, use local memory
*/
caddr_t cp1;
int off = tlen - KB_LEN(ml);
cp = (caddr_t)&sscop_trailer;
/*
* Ensure trailer is within last two buffers
*/
if ((mn == NULL) || (KB_LEN(mn) < off))
goto badpdu;
KB_DATASTART(ml, cp1, caddr_t);
bcopy(cp1, cp + off, KB_LEN(ml));
KB_LEN(ml) = 0;
KB_TAILADJ(mn, -off);
KB_DATAEND(mn, cp1, caddr_t);
bcopy(cp1, cp, off);
}
/*
* Get possible PDU Pad Length
*/
if (sop->so_vers == SSCOP_VERS_QSAAL) {
if (qsaal_padlen[type])
plen = (*tp & PT_PAD_MASK) >> PT_PAD_SHIFT;
else
plen = 0;
} else {
if (q2110_padlen[type])
plen = (*tp & PT_PAD_MASK) >> PT_PAD_SHIFT;
else
plen = 0;
}
/*
* Perform Pad Length adjustments
*/
if (plen) {
if (KB_LEN(ml) >= plen) {
/*
* All pad bytes in last buffer
*/
KB_TAILADJ(ml, -plen);
} else {
/*
* Pad bytes split between buffers
*/
plen -= KB_LEN(ml);
if ((mn == NULL) || (KB_LEN(mn) < plen))
goto badpdu;
KB_LEN(ml) = 0;
KB_TAILADJ(mn, -plen);
}
}
return (cp);
badpdu:
/*
* This MAA Error is only supposed to be for a PDU length violation,
* but we use it for any PDU format error.
*/
sscop_maa_error(sop, 'U');
sscop_pdu_print(sop, m, "badpdu received");
KB_FREEALL(m);
return (NULL);
}