diff options
Diffstat (limited to 'sys/contrib/ngatm/netnatm/api/cc_user.c')
-rw-r--r-- | sys/contrib/ngatm/netnatm/api/cc_user.c | 1922 |
1 files changed, 1922 insertions, 0 deletions
diff --git a/sys/contrib/ngatm/netnatm/api/cc_user.c b/sys/contrib/ngatm/netnatm/api/cc_user.c new file mode 100644 index 0000000..75ce91e --- /dev/null +++ b/sys/contrib/ngatm/netnatm/api/cc_user.c @@ -0,0 +1,1922 @@ +/* + * Copyright (c) 2003-2004 + * Hartmut Brandt + * All rights reserved. + * + * Copyright (c) 2001-2002 + * Fraunhofer Institute for Open Communication Systems (FhG Fokus). + * All rights reserved. + * + * Author: Harti Brandt <harti@freebsd.org> + * + * Redistribution of this software and documentation and use in source and + * binary forms, with or without modification, are permitted provided that + * the following conditions are met: + * + * 1. Redistributions of source code or documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR + * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Begemot: libunimsg/netnatm/api/cc_user.c,v 1.3 2004/07/16 18:46:55 brandt Exp $ + * + * ATM API as defined per af-saa-0108 + * + * User side (upper half) + */ + +#include <netnatm/unimsg.h> +#include <netnatm/msg/unistruct.h> +#include <netnatm/msg/unimsglib.h> +#include <netnatm/api/unisap.h> +#include <netnatm/sig/unidef.h> +#include <netnatm/api/atmapi.h> +#include <netnatm/api/ccatm.h> +#include <netnatm/api/ccpriv.h> + +/* +* This file handles messages to a USER. +*/ +static const char *stab[] = { +#define DEF(N) [N] = #N, + USER_STATES +#undef DEF +}; + +const char * +cc_user_state2str(u_int s) +{ + if (s >= sizeof(stab) / sizeof(stab[0]) || stab[s] == NULL) + return ("?"); + return (stab[s]); +} + +static __inline void +set_state(struct ccuser *user, enum user_state ns) +{ + if (user->state != ns) { + if (user->cc->log & CCLOG_USER_STATE) + cc_user_log(user, "%s -> %s", + stab[user->state], stab[ns]); + user->state = ns; + } +} + +static __inline void +cc_user_send(struct ccuser *user, u_int op, void *arg, size_t len) +{ + user->cc->funcs->send_user(user, user->uarg, op, arg, len); +} + +static __inline void +cc_user_ok(struct ccuser *user, u_int data, void *arg, size_t len) +{ + user->cc->funcs->respond_user(user, user->uarg, + ATMERR_OK, data, arg, len); +} + +static __inline void +cc_user_err(struct ccuser *user, int err) +{ + user->cc->funcs->respond_user(user, user->uarg, + err, ATMRESP_NONE, NULL, 0); +} + + +/********************************************************************** +* +* INSTANCE MANAGEMENT +*/ +/* +* New endpoint created +*/ +struct ccuser * +cc_user_create(struct ccdata *cc, void *uarg, const char *name) +{ + struct ccuser *user; + + user = CCZALLOC(sizeof(*user)); + if (user == NULL) + return (NULL); + + user->cc = cc; + user->state = USER_NULL; + user->uarg = uarg; + strncpy(user->name, name, sizeof(user->name)); + user->name[sizeof(user->name) - 1] = '\0'; + TAILQ_INIT(&user->connq); + LIST_INSERT_HEAD(&cc->user_list, user, node_link); + + if (user->cc->log & CCLOG_USER_INST) + cc_user_log(user, "created with name '%s'", name); + + return (user); +} + +/* + * Reset a user instance + */ +static void +cc_user_reset(struct ccuser *user) +{ + + CCASSERT(TAILQ_EMPTY(&user->connq), ("connq not empty")); + + if (user->sap != NULL) { + CCFREE(user->sap); + user->sap = NULL; + } + + if (user->accepted != NULL) { + user->accepted->acceptor = NULL; + user->accepted = NULL; + } + user->config = USER_P2P; + user->queue_act = 0; + user->queue_max = 0; + user->aborted = 0; + + set_state(user, USER_NULL); + + cc_user_sig_flush(user); +} + +static void +cc_user_abort(struct ccuser *user, const struct uni_ie_cause *cause) +{ + struct ccconn *conn; + + /* + * Although the standard state that 'all connections + * associated with this endpoint are aborted' we only + * have to abort the head one, because in state A6 + * (call present) the endpoint is only associated to the + * head connection - the others are 'somewhere else' and + * need to be redispatched. + * + * First bring user into a state that the connections + * are not dispatched back to it. + */ + set_state(user, USER_NULL); + if (!user->aborted) { + if ((conn = TAILQ_FIRST(&user->connq)) != NULL) { + memset(conn->cause, 0, sizeof(conn->cause)); + if (cause != NULL) + conn->cause[0] = *cause; + cc_conn_reset_acceptor(conn); + cc_disconnect_from_user(conn); + cc_conn_sig(conn, CONN_SIG_USER_ABORT, NULL); + } + } + + while ((conn = TAILQ_FIRST(&user->connq)) != NULL) { + /* these should be in C21 */ + cc_disconnect_from_user(conn); + cc_conn_dispatch(conn); + } + + cc_user_reset(user); +} + +/* + * Application has closed this endpoint. Clean up all user resources and + * abort all connections. This can be called in any state. + */ +void +cc_user_destroy(struct ccuser *user) +{ + + if (user->cc->log & CCLOG_USER_INST) + cc_user_log(user, "destroy '%s'", user->name); + + cc_user_abort(user, NULL); + + if (user->sap != NULL) + CCFREE(user->sap); + + cc_user_sig_flush(user); + + LIST_REMOVE(user, node_link); + CCFREE(user); +} + +/********************************************************************** + * + * OUTGOING CALLS + */ +/* + * Return true when the calling address of the connection matches the address. + */ +static int +addr_matches(const struct ccaddr *addr, const struct ccconn *conn) +{ + + if (!IE_ISPRESENT(conn->calling)) + return (0); + + return (addr->addr.type == conn->calling.addr.type && + addr->addr.plan == conn->calling.addr.plan && + addr->addr.len == conn->calling.addr.len && + memcmp(addr->addr.addr, conn->calling.addr.addr, + addr->addr.len) == 0); +} + +/* + * Check if the user's SAP (given he is in the right state) and + * the given SAP overlap + */ +static int +check_overlap(struct ccuser *user, struct uni_sap *sap) +{ + return ((user->state == USER_IN_PREPARING || + user->state == USER_IN_WAITING) && + unisve_overlap_sap(user->sap, sap)); +} + +/* + * Send arrival notification to user + */ +static void +do_arrival(struct ccuser *user) +{ + struct ccconn *conn; + + user->aborted = 0; + if ((conn = TAILQ_FIRST(&user->connq)) != NULL) { + set_state(user, USER_IN_ARRIVED); + cc_user_send(user, ATMOP_ARRIVAL_OF_INCOMING_CALL, NULL, 0); + cc_conn_sig(conn, CONN_SIG_ARRIVAL, NULL); + } +} + +/********************************************************************** + * + * ATTRIBUTES + */ +/* + * Query an attribute. This is possible only in some states: preparation + * of an outgoing call, after an incoming call was offered to the application + * and in the three active states (P2P, P2PLeaf, P2PRoot). + */ +static struct ccconn * +cc_query_check(struct ccuser *user) +{ + + switch (user->state) { + + case USER_OUT_PREPARING: + case USER_IN_ARRIVED: + case USER_ACTIVE: + return (TAILQ_FIRST(&user->connq)); + + case USER_NULL: + /* if we are waiting for the SETUP_confirm, we are in + * the NULL state still (we are the new endpoint), but + * have a connection in 'accepted' that is in the + * CONN_IN_WAIT_ACCEPT_OK state. + */ + if (user->accepted != NULL && + user->accepted->state == CONN_IN_WAIT_ACCEPT_OK) + return (user->accepted); + /* FALLTHRU */ + + default: + return (NULL); + } +} + +/* + * Query attributes + */ +static void +cc_attr_query(struct ccuser *user, struct ccconn *conn, + uint32_t *attr, u_int count) +{ + void *val, *ptr; + size_t total, len; + u_int i; + uint32_t *atab; + + /* determine the length of the total attribute buffer */ + total = sizeof(uint32_t) + count * sizeof(uint32_t); + for (i = 0; i < count; i++) { + len = 0; + switch ((enum atm_attribute)attr[i]) { + + case ATM_ATTR_NONE: + break; + + case ATM_ATTR_BLLI_SELECTOR: + len = sizeof(uint32_t); + break; + + case ATM_ATTR_BLLI: + len = sizeof(struct uni_ie_blli); + break; + + case ATM_ATTR_BEARER: + len = sizeof(struct uni_ie_bearer); + break; + + case ATM_ATTR_TRAFFIC: + len = sizeof(struct uni_ie_traffic); + break; + + case ATM_ATTR_QOS: + len = sizeof(struct uni_ie_qos); + break; + + case ATM_ATTR_EXQOS: + len = sizeof(struct uni_ie_exqos); + break; + + case ATM_ATTR_CALLED: + len = sizeof(struct uni_ie_called); + break; + + case ATM_ATTR_CALLEDSUB: + len = sizeof(struct uni_ie_calledsub); + break; + + case ATM_ATTR_CALLING: + len = sizeof(struct uni_ie_calling); + break; + + case ATM_ATTR_CALLINGSUB: + len = sizeof(struct uni_ie_callingsub); + break; + + case ATM_ATTR_AAL: + len = sizeof(struct uni_ie_aal); + break; + + case ATM_ATTR_EPREF: + len = sizeof(struct uni_ie_epref); + break; + + case ATM_ATTR_CONNED: + len = sizeof(struct uni_ie_conned); + break; + + case ATM_ATTR_CONNEDSUB: + len = sizeof(struct uni_ie_connedsub); + break; + + case ATM_ATTR_EETD: + len = sizeof(struct uni_ie_eetd); + break; + + case ATM_ATTR_ABRSETUP: + len = sizeof(struct uni_ie_abrsetup); + break; + + case ATM_ATTR_ABRADD: + len = sizeof(struct uni_ie_abradd); + break; + + case ATM_ATTR_CONNID: + len = sizeof(struct uni_ie_connid); + break; + + case ATM_ATTR_MDCR: + len = sizeof(struct uni_ie_mdcr); + break; + } + if (len == 0) { + cc_user_err(user, ATMERR_BAD_ATTR); + return; + } + total += len; + } + + /* allocate buffer */ + val = CCMALLOC(total); + if (val == NULL) + return; + + atab = val; + atab[0] = count; + + /* fill */ + ptr = (u_char *)val + (sizeof(uint32_t) + count * sizeof(uint32_t)); + for (i = 0; i < count; i++) { + len = 0; + atab[i + 1] = attr[i]; + switch (attr[i]) { + + case ATM_ATTR_NONE: + break; + + case ATM_ATTR_BLLI_SELECTOR: + len = sizeof(uint32_t); + memcpy(ptr, &conn->blli_selector, len); + break; + + case ATM_ATTR_BLLI: + /* in A6 the blli_selector may be 0 when + * there was no blli in the SETUP. + */ + len = sizeof(struct uni_ie_blli); + if (conn->blli_selector == 0) + memset(ptr, 0, len); + else + memcpy(ptr, &conn->blli[conn->blli_selector - + 1], len); + break; + + case ATM_ATTR_BEARER: + len = sizeof(struct uni_ie_bearer); + memcpy(ptr, &conn->bearer, len); + break; + + case ATM_ATTR_TRAFFIC: + len = sizeof(struct uni_ie_traffic); + memcpy(ptr, &conn->traffic, len); + break; + + case ATM_ATTR_QOS: + len = sizeof(struct uni_ie_qos); + memcpy(ptr, &conn->qos, len); + break; + + case ATM_ATTR_EXQOS: + len = sizeof(struct uni_ie_exqos); + memcpy(ptr, &conn->exqos, len); + break; + + case ATM_ATTR_CALLED: + len = sizeof(struct uni_ie_called); + memcpy(ptr, &conn->called, len); + break; + + case ATM_ATTR_CALLEDSUB: + len = sizeof(struct uni_ie_calledsub); + memcpy(ptr, &conn->calledsub, len); + break; + + case ATM_ATTR_CALLING: + len = sizeof(struct uni_ie_calling); + memcpy(ptr, &conn->calling, len); + break; + + case ATM_ATTR_CALLINGSUB: + len = sizeof(struct uni_ie_callingsub); + memcpy(ptr, &conn->callingsub, len); + break; + + case ATM_ATTR_AAL: + len = sizeof(struct uni_ie_aal); + memcpy(ptr, &conn->aal, len); + break; + + case ATM_ATTR_EPREF: + len = sizeof(struct uni_ie_epref); + memcpy(ptr, &conn->epref, len); + break; + + case ATM_ATTR_CONNED: + len = sizeof(struct uni_ie_conned); + memcpy(ptr, &conn->conned, len); + break; + + case ATM_ATTR_CONNEDSUB: + len = sizeof(struct uni_ie_connedsub); + memcpy(ptr, &conn->connedsub, len); + break; + + case ATM_ATTR_EETD: + len = sizeof(struct uni_ie_eetd); + memcpy(ptr, &conn->eetd, len); + break; + + case ATM_ATTR_ABRSETUP: + len = sizeof(struct uni_ie_abrsetup); + memcpy(ptr, &conn->abrsetup, len); + break; + + case ATM_ATTR_ABRADD: + len = sizeof(struct uni_ie_abradd); + memcpy(ptr, &conn->abradd, len); + break; + + case ATM_ATTR_CONNID: + len = sizeof(struct uni_ie_connid); + memcpy(ptr, &conn->connid, len); + break; + + case ATM_ATTR_MDCR: + len = sizeof(struct uni_ie_mdcr); + memcpy(ptr, &conn->mdcr, len); + break; + } + ptr = (u_char *)ptr + len; + } + + cc_user_ok(user, ATMRESP_ATTRS, val, total); + + CCFREE(val); +} + +/* + * Check whether the state is ok and return the connection + */ +static struct ccconn * +cc_set_check(struct ccuser *user) +{ + switch(user->state) { + + case USER_OUT_PREPARING: + case USER_IN_ARRIVED: + return (TAILQ_FIRST(&user->connq)); + + default: + return (NULL); + } +} + +/* + * Set connection attribute(s) + */ +static void +cc_attr_set(struct ccuser *user, struct ccconn *conn, uint32_t *attr, + u_int count, u_char *val, size_t vallen) +{ + size_t total, len; + u_int i; + u_char *ptr; + + /* determine the length of the total attribute buffer */ + total = 0; + ptr = val; + for (i = 0; i < count; i++) { + len = 0; + switch ((enum atm_attribute)attr[i]) { + + case ATM_ATTR_NONE: + break; + + case ATM_ATTR_BLLI_SELECTOR: + { + uint32_t sel; + + if (conn->state != CONN_OUT_PREPARING) + goto rdonly; + memcpy(&sel, ptr, sizeof(sel)); + if (sel == 0 || sel > UNI_NUM_IE_BLLI) + goto bad_val; + len = sizeof(uint32_t); + break; + } + + case ATM_ATTR_BLLI: + len = sizeof(struct uni_ie_blli); + break; + + case ATM_ATTR_BEARER: + if (conn->state != CONN_OUT_PREPARING) + goto rdonly; + len = sizeof(struct uni_ie_bearer); + break; + + case ATM_ATTR_TRAFFIC: + len = sizeof(struct uni_ie_traffic); + break; + + case ATM_ATTR_QOS: + if (conn->state != CONN_OUT_PREPARING) + goto rdonly; + len = sizeof(struct uni_ie_qos); + break; + + case ATM_ATTR_EXQOS: + len = sizeof(struct uni_ie_exqos); + break; + + case ATM_ATTR_CALLED: + goto rdonly; + + case ATM_ATTR_CALLEDSUB: + if (conn->state != CONN_OUT_PREPARING) + goto rdonly; + len = sizeof(struct uni_ie_calledsub); + break; + + case ATM_ATTR_CALLING: + if (conn->state != CONN_OUT_PREPARING) + goto rdonly; + len = sizeof(struct uni_ie_calling); + break; + + case ATM_ATTR_CALLINGSUB: + if (conn->state != CONN_OUT_PREPARING) + goto rdonly; + len = sizeof(struct uni_ie_callingsub); + break; + + case ATM_ATTR_AAL: + len = sizeof(struct uni_ie_aal); + break; + + case ATM_ATTR_EPREF: + goto rdonly; + + case ATM_ATTR_CONNED: + goto rdonly; + + case ATM_ATTR_CONNEDSUB: + goto rdonly; + + case ATM_ATTR_EETD: + len = sizeof(struct uni_ie_eetd); + break; + + case ATM_ATTR_ABRSETUP: + len = sizeof(struct uni_ie_abrsetup); + break; + + case ATM_ATTR_ABRADD: + len = sizeof(struct uni_ie_abradd); + break; + + case ATM_ATTR_CONNID: + len = sizeof(struct uni_ie_connid); + break; + + case ATM_ATTR_MDCR: + if (conn->state != CONN_OUT_PREPARING) + goto rdonly; + len = sizeof(struct uni_ie_mdcr); + break; + } + if (len == 0) { + cc_user_err(user, ATMERR_BAD_ATTR); + return; + } + total += len; + ptr += len; + } + + /* check the length */ + if (vallen != total) { + cc_user_err(user, ATMERR_BAD_ARGS); + return; + } + + ptr = val; + for (i = 0; i < count; i++) { + len = 0; + switch ((enum atm_attribute)attr[i]) { + + case ATM_ATTR_NONE: + break; + + case ATM_ATTR_BLLI_SELECTOR: + { + uint32_t sel; + + memcpy(&sel, ptr, sizeof(sel)); + conn->blli_selector = sel; + len = sizeof(uint32_t); + break; + } + + case ATM_ATTR_BLLI: + len = sizeof(struct uni_ie_blli); + memcpy(&conn->blli[conn->blli_selector - 1], ptr, len); + conn->dirty_attr |= CCDIRTY_BLLI; + break; + + case ATM_ATTR_BEARER: + len = sizeof(struct uni_ie_bearer); + memcpy(&conn->bearer, ptr, len); + break; + + case ATM_ATTR_TRAFFIC: + len = sizeof(struct uni_ie_traffic); + memcpy(&conn->traffic, ptr, len); + conn->dirty_attr |= CCDIRTY_TRAFFIC; + break; + + case ATM_ATTR_QOS: + len = sizeof(struct uni_ie_qos); + memcpy(&conn->qos, ptr, len); + break; + + case ATM_ATTR_EXQOS: + len = sizeof(struct uni_ie_exqos); + memcpy(&conn->exqos, ptr, len); + conn->dirty_attr |= CCDIRTY_EXQOS; + break; + + case ATM_ATTR_CALLED: + len = sizeof(struct uni_ie_called); + break; + + case ATM_ATTR_CALLEDSUB: + len = sizeof(struct uni_ie_calledsub); + memcpy(&conn->calledsub, ptr, len); + break; + + case ATM_ATTR_CALLING: + len = sizeof(struct uni_ie_calling); + memcpy(&conn->calling, ptr, len); + break; + + case ATM_ATTR_CALLINGSUB: + len = sizeof(struct uni_ie_callingsub); + memcpy(&conn->callingsub, ptr, len); + break; + + case ATM_ATTR_AAL: + len = sizeof(struct uni_ie_aal); + memcpy(&conn->aal, ptr, len); + conn->dirty_attr |= CCDIRTY_AAL; + break; + + case ATM_ATTR_EPREF: + len = sizeof(struct uni_ie_epref); + break; + + case ATM_ATTR_CONNED: + len = sizeof(struct uni_ie_conned); + break; + + case ATM_ATTR_CONNEDSUB: + len = sizeof(struct uni_ie_connedsub); + break; + + case ATM_ATTR_EETD: + len = sizeof(struct uni_ie_eetd); + memcpy(&conn->eetd, ptr, len); + conn->dirty_attr |= CCDIRTY_EETD; + break; + + case ATM_ATTR_ABRSETUP: + len = sizeof(struct uni_ie_abrsetup); + memcpy(&conn->abrsetup, ptr, len); + conn->dirty_attr |= CCDIRTY_ABRSETUP; + break; + + case ATM_ATTR_ABRADD: + len = sizeof(struct uni_ie_abradd); + memcpy(&conn->abradd, ptr, len); + conn->dirty_attr |= CCDIRTY_ABRADD; + break; + + case ATM_ATTR_CONNID: + len = sizeof(struct uni_ie_connid); + memcpy(&conn->connid, ptr, len); + conn->dirty_attr |= CCDIRTY_CONNID; + break; + + case ATM_ATTR_MDCR: + len = sizeof(struct uni_ie_mdcr); + memcpy(&conn->mdcr, ptr, len); + break; + } + ptr += len; + } + + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + return; + + bad_val: + cc_user_err(user, ATMERR_BAD_VALUE); + return; + + rdonly: + cc_user_err(user, ATMERR_RDONLY); + return; +} + +#ifdef CCATM_DEBUG +static const char *op_names[] = { +#define S(OP) [ATMOP_##OP] = #OP + S(RESP), + S(ABORT_CONNECTION), + S(ACCEPT_INCOMING_CALL), + S(ADD_PARTY), + S(ADD_PARTY_REJECT), + S(ADD_PARTY_SUCCESS), + S(ARRIVAL_OF_INCOMING_CALL), + S(CALL_RELEASE), + S(CONNECT_OUTGOING_CALL), + S(DROP_PARTY), + S(GET_LOCAL_PORT_INFO), + S(P2MP_CALL_ACTIVE), + S(P2P_CALL_ACTIVE), + S(PREPARE_INCOMING_CALL), + S(PREPARE_OUTGOING_CALL), + S(QUERY_CONNECTION_ATTRIBUTES), + S(REJECT_INCOMING_CALL), + S(SET_CONNECTION_ATTRIBUTES), + S(WAIT_ON_INCOMING_CALL), + S(SET_CONNECTION_ATTRIBUTES_X), + S(QUERY_CONNECTION_ATTRIBUTES_X), + S(QUERY_STATE), +#undef S +}; +#endif + +/* + * Signal from user - map this to our internal signals and queue + * the mapped signal. + */ +int +cc_user_signal(struct ccuser *user, enum atmop sig, struct uni_msg *msg) +{ + size_t len = uni_msg_len(msg); + int err = EINVAL; + + if (user->cc->log & CCLOG_USER_SIG) + cc_user_log(user, "signal %s to user", op_names[sig]); + + if ((u_int)sig > ATMOP_QUERY_STATE) + goto bad_signal; + + switch (sig) { + + case ATMOP_ABORT_CONNECTION: + if (len != sizeof(struct atm_abort_connection)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_ABORT_CONNECTION, msg); + break; + + case ATMOP_ACCEPT_INCOMING_CALL: + if (len != sizeof(struct atm_accept_incoming_call)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_ACCEPT_INCOMING, msg); + break; + + case ATMOP_ADD_PARTY: + if (len != sizeof(struct atm_add_party)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_ADD_PARTY, msg); + break; + + case ATMOP_CALL_RELEASE: + if (len != sizeof(struct atm_call_release)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_CALL_RELEASE, msg); + break; + + case ATMOP_CONNECT_OUTGOING_CALL: + if (len != sizeof(struct atm_connect_outgoing_call)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_CONNECT_OUTGOING, msg); + break; + + case ATMOP_DROP_PARTY: + if (len != sizeof(struct atm_drop_party)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_DROP_PARTY, msg); + break; + + case ATMOP_GET_LOCAL_PORT_INFO: + if (len != sizeof(struct atm_get_local_port_info)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_GET_LOCAL_PORT_INFO, msg); + break; + + case ATMOP_PREPARE_INCOMING_CALL: + if (len != sizeof(struct atm_prepare_incoming_call)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_PREPARE_INCOMING, msg); + break; + + case ATMOP_PREPARE_OUTGOING_CALL: + if (len != 0) + goto bad_len; + uni_msg_destroy(msg); + err = cc_user_sig(user, USER_SIG_PREPARE_OUTGOING, NULL, 0); + break; + + case ATMOP_QUERY_CONNECTION_ATTRIBUTES: + if (len != sizeof(struct atm_query_connection_attributes)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_QUERY_ATTR, msg); + break; + + case ATMOP_REJECT_INCOMING_CALL: + if (len != sizeof(struct atm_reject_incoming_call)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_REJECT_INCOMING, msg); + break; + + case ATMOP_SET_CONNECTION_ATTRIBUTES: + if (len < sizeof(struct atm_set_connection_attributes)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_SET_ATTR, msg); + break; + + case ATMOP_WAIT_ON_INCOMING_CALL: + if (len != 0) + goto bad_len; + uni_msg_destroy(msg); + err = cc_user_sig(user, USER_SIG_WAIT_ON_INCOMING, NULL, 0); + break; + + case ATMOP_QUERY_CONNECTION_ATTRIBUTES_X: + if (len < sizeof(struct atm_set_connection_attributes_x) || + len != offsetof(struct atm_set_connection_attributes_x, + attr) + uni_msg_rptr(msg, + struct atm_set_connection_attributes_x *)->count * + sizeof(uint32_t)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_QUERY_ATTR_X, msg); + break; + + case ATMOP_SET_CONNECTION_ATTRIBUTES_X: + if (len < sizeof(struct atm_set_connection_attributes_x)) + goto bad_len; + err = cc_user_sig_msg(user, USER_SIG_SET_ATTR_X, msg); + break; + + case ATMOP_QUERY_STATE: + if (len != 0) + goto bad_len; + uni_msg_destroy(msg); + err = cc_user_sig(user, USER_SIG_QUERY_STATE, NULL, 0); + break; + + case ATMOP_RESP: + case ATMOP_ADD_PARTY_REJECT: + case ATMOP_ADD_PARTY_SUCCESS: + case ATMOP_ARRIVAL_OF_INCOMING_CALL: + case ATMOP_P2MP_CALL_ACTIVE: + case ATMOP_P2P_CALL_ACTIVE: + bad_signal: + /* bad signal */ + if (user->cc->log & CCLOG_USER_SIG) + cc_user_log(user, "bad signal %u", sig); + cc_user_err(user, ATMERR_BAD_OP); + uni_msg_destroy(msg); + break; + } + return (err); + + bad_len: + /* bad argument length */ + if (user->cc->log & CCLOG_USER_SIG) + cc_user_log(user, "signal %s had bad len=%zu", + op_names[sig], len); + cc_user_err(user, ATMERR_BAD_ARGS); + uni_msg_destroy(msg); + return (EINVAL); +} + +/* + * Send active signal to user + */ +static void +cc_user_active(struct ccuser *user) +{ + struct ccconn *conn = TAILQ_FIRST(&user->connq); + + set_state(user, USER_ACTIVE); + if (conn->bearer.cfg == UNI_BEARER_P2P) { + struct atm_p2p_call_active *act; + + user->config = USER_P2P; + act = CCZALLOC(sizeof(*act)); + if (act == NULL) + return; + act->connid = conn->connid; + cc_user_send(user, ATMOP_P2P_CALL_ACTIVE, act, sizeof(*act)); + CCFREE(act); + } else { + struct atm_p2mp_call_active *act; + + user->config = USER_ROOT; + act = CCZALLOC(sizeof(*act)); + if (act == NULL) + return; + act->connid = conn->connid; + cc_user_send(user, ATMOP_P2MP_CALL_ACTIVE, act, sizeof(*act)); + CCFREE(act); + } +} + +/* +* Handle a signal to this user +*/ +void +cc_user_sig_handle(struct ccuser *user, enum user_sig sig, + void *arg, u_int arg2) +{ + + if (user->cc->log & CCLOG_USER_SIG) + cc_user_log(user, "signal %s to user state %s", + cc_user_sigtab[sig], stab[user->state]); + + switch (sig) { + + + case USER_SIG_PREPARE_OUTGOING: + { + /* + * Here we create a connection for the call we soon will make. + * We put this call on the list of orphaned connections, + * because we don't know yet, which port will get the + * connection. It is assigned, when the user issues the call + * to connect. + */ + struct ccconn *conn; + + if (user->state != USER_NULL) { + cc_user_err(user, ATMERR_BAD_STATE); + goto bad_state; + } + conn = cc_conn_create(user->cc); + if (conn == NULL) { + cc_user_err(user, ATMERR_NOMEM); + return; + } + set_state(user, USER_OUT_PREPARING); + cc_conn_set_state(conn, CONN_OUT_PREPARING); + conn->blli_selector = 1; + cc_connect_to_user(conn, user); + + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + return; + } + + + case USER_SIG_CONNECT_OUTGOING: + { + /* + * Request to connect that call + * + * Here we assign the connection to a port. + */ + struct uni_msg *msg = arg; + struct atm_connect_outgoing_call *req = uni_msg_rptr(msg, + struct atm_connect_outgoing_call *); + struct ccdata *priv = user->cc; + struct ccport *port; + struct ccaddr *addr; + struct ccconn *conn = TAILQ_FIRST(&user->connq); + + if (user->state != USER_OUT_PREPARING) { + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_BAD_STATE); + goto bad_state; + } + if (!IE_ISPRESENT(req->called)) { + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_BAD_ARGS); + return; + } + CCASSERT(conn->port == NULL, ("connection still on port")); + + if (TAILQ_EMPTY(&priv->port_list)) { + /* + * We have no ports - reject + */ + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_BAD_PORT); + return; + } + + /* + * Find the correct port + * Routing of outgoing calls goes to the lowest numbered port + * with a matching address or, if no address match is found to + * the lowest numbered port. + */ + TAILQ_FOREACH(port, &priv->port_list, node_link) + TAILQ_FOREACH(addr, &port->addr_list, port_link) + if (addr_matches(addr, conn)) + break; + + if (port == NULL) + port = TAILQ_FIRST(&priv->port_list); + + cc_conn_ins_port(conn, port); + conn->called = req->called; + uni_msg_destroy(msg); + + /* + * Now move the state + */ + set_state(user, USER_OUT_WAIT_OK); + cc_conn_sig(conn, CONN_SIG_CONNECT_OUTGOING, NULL); + + return; + } + + + case USER_SIG_CONNECT_OUTGOING_ERR: + switch (user->state) { + + case USER_OUT_WAIT_OK: + set_state(user, USER_OUT_PREPARING); + cc_user_err(user, arg2); + break; + + case USER_REL_WAIT_CONN: + { + struct ccconn *conn; + + conn = TAILQ_FIRST(&user->connq); + if (conn != NULL) { + cc_disconnect_from_user(conn); + cc_conn_destroy(conn); + } + + cc_user_reset(user); + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + break; + } + + default: + goto bad_state; + } + return; + + + case USER_SIG_CONNECT_OUTGOING_OK: + switch (user->state) { + + case USER_OUT_WAIT_OK: + set_state(user, USER_OUT_WAIT_CONF); + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + break; + + case USER_REL_WAIT_CONN: + set_state(user, USER_REL_WAIT_SCONF); + break; + + default: + goto bad_state; + } + return; + + + case USER_SIG_SETUP_CONFIRM: + /* + * SETUP.confirm from UNI stack. + */ + switch (user->state) { + + case USER_OUT_WAIT_CONF: + cc_user_active(user); + break; + + case USER_REL_WAIT_SCONF: + /* now try to release */ + set_state(user, USER_REL_WAIT_CONF); + cc_conn_sig(TAILQ_FIRST(&user->connq), + CONN_SIG_RELEASE, NULL); + break; + + default: + goto bad_state; + } + return; + + + case USER_SIG_PREPARE_INCOMING: + { + struct uni_msg *msg = arg; + struct ccuser *ptr; + struct atm_prepare_incoming_call *prep = uni_msg_rptr(msg, + struct atm_prepare_incoming_call *); + + if (user->state != USER_NULL) { + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_BAD_STATE); + goto bad_state; + } + + /* + * Check the SAP + */ + if (unisve_check_sap(&prep->sap) != UNISVE_OK) { + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_BAD_SAP); + return; + } + + /* + * Loop through all incoming calls and check whether there + * is an overlap in SAP space. + */ + LIST_FOREACH(ptr, &user->cc->user_list, node_link) { + if (check_overlap(ptr, &prep->sap)) { + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_OVERLAP); + return; + } + } + + /* + * Save info and set state + */ + user->sap = CCZALLOC(sizeof(struct uni_sap)); + if (user->sap == NULL) { + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_NOMEM); + return; + } + *user->sap = prep->sap; + user->queue_max = prep->queue_size; + user->queue_act = 0; + uni_msg_destroy(msg); + + set_state(user, USER_IN_PREPARING); + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + + return; + } + + + case USER_SIG_WAIT_ON_INCOMING: + if (user->state != USER_IN_PREPARING) { + cc_user_err(user, ATMERR_BAD_STATE); + goto bad_state; + } + + set_state(user, USER_IN_WAITING); + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + return; + + + case USER_SIG_SETUP_IND: + /* + * New connection queued up in the queue. If this is the + * first one, inform the application of the arrival. + */ + switch (user->state) { + + case USER_IN_WAITING: + do_arrival(user); + break; + + case USER_IN_ARRIVED: + case USER_IN_WAIT_REJ: + case USER_IN_WAIT_ACC: + break; + + default: + goto bad_state; + } + return; + + + case USER_SIG_REJECT_INCOMING: + { + /* + * User rejects call. This is done on the OLD user + * (i.e. the one sending the arrival). + */ + struct uni_msg *msg = arg; + struct atm_reject_incoming_call *rej = uni_msg_rptr(msg, + struct atm_reject_incoming_call *); + struct ccconn *conn = TAILQ_FIRST(&user->connq); + + if (user->state != USER_IN_ARRIVED) { + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_BAD_STATE); + goto bad_state; + } + if (user->aborted) { + /* connection has disappeared. Send an ok + * to the user and lock whether there is another + * connection at this endpoint */ + uni_msg_destroy(msg); + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + + set_state(user, USER_IN_WAITING); + do_arrival(user); + return; + } + conn->cause[0] = rej->cause; + memset(&conn->cause[1], 0, sizeof(conn->cause[1])); + uni_msg_destroy(msg); + + set_state(user, USER_IN_WAIT_REJ); + cc_conn_sig(conn, CONN_SIG_REJECT, NULL); + + return; + } + + + case USER_SIG_REJECT_OK: + if (user->state != USER_IN_WAIT_REJ) + goto bad_state; + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + + set_state(user, USER_IN_WAITING); + do_arrival(user); + return; + + + case USER_SIG_REJECT_ERR: + if (user->state != USER_IN_WAIT_REJ) + goto bad_state; + cc_user_err(user, arg2); + + if (arg == NULL) + set_state(user, USER_IN_ARRIVED); + else { + set_state(user, USER_IN_WAITING); + do_arrival(user); + } + return; + + + case USER_SIG_ACCEPT_INCOMING: + { + /* + * User accepts call. This is done on the OLD user (i.e. the one + * sending the arrival), the message contains a pointer to the + * new endpoint. + */ + struct uni_msg *msg = arg; + struct atm_accept_incoming_call *acc = + uni_msg_rptr(msg, struct atm_accept_incoming_call *); + struct ccuser *newep; + + if (user->state != USER_IN_ARRIVED) { + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_BAD_STATE); + return; + } + if (user->aborted) { + /* connection has disappeared. Send an error + * to the user and lock whether there is another + * connection at this endpoint */ + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_PREVIOUSLY_ABORTED); + + set_state(user, USER_IN_WAITING); + do_arrival(user); + return; + } + acc->newep[sizeof(acc->newep) - 1] = '\0'; + + LIST_FOREACH(newep, &user->cc->user_list, node_link) + if (strcmp(acc->newep, newep->name) == 0) + break; + uni_msg_destroy(msg); + + if (newep == NULL) { + cc_user_err(user, ATMERR_BAD_ENDPOINT); + return; + } + + if (newep->state != USER_NULL || newep->accepted != NULL) { + cc_user_err(user, ATMERR_BAD_STATE); + return; + } + + set_state(user, USER_IN_WAIT_ACC); + cc_conn_sig(TAILQ_FIRST(&user->connq), CONN_SIG_ACCEPT, newep); + + return; + } + + + case USER_SIG_ACCEPT_OK: + if (user->state != USER_IN_WAIT_ACC) + goto bad_state; + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + + set_state(user, USER_IN_WAITING); + do_arrival(user); + return; + + + case USER_SIG_ACCEPT_ERR: + if (user->state != USER_IN_WAIT_ACC) + goto bad_state; + cc_user_err(user, arg2); + + if (arg == NULL) { + /* arg used as flag! */ + set_state(user, USER_IN_ARRIVED); + } else { + set_state(user, USER_IN_WAITING); + do_arrival(user); + } + return; + + + case USER_SIG_ACCEPTING: + if (user->state != USER_NULL) + goto bad_state; + set_state(user, USER_IN_ACCEPTING); + return; + + + case USER_SIG_SETUP_COMPL: + { + struct ccconn *conn = TAILQ_FIRST(&user->connq); + + if (user->state != USER_IN_ACCEPTING) + goto bad_state; + + user->state = USER_ACTIVE; + if (conn->bearer.cfg == UNI_BEARER_P2P) { + struct atm_p2p_call_active *act; + + user->config = USER_P2P; + act = CCZALLOC(sizeof(*act)); + if (act == NULL) + return; + act->connid = conn->connid; + cc_user_send(user, ATMOP_P2P_CALL_ACTIVE, + act, sizeof(*act)); + CCFREE(act); + } else { + struct atm_p2mp_call_active *act; + + user->config = USER_LEAF; + act = CCZALLOC(sizeof(*act)); + if (act == NULL) + return; + act->connid = conn->connid; + cc_user_send(user, ATMOP_P2MP_CALL_ACTIVE, + act, sizeof(*act)); + CCFREE(act); + } + return; + } + + + case USER_SIG_CALL_RELEASE: + { + struct uni_msg *msg = arg; + struct atm_call_release *api = uni_msg_rptr(msg, + struct atm_call_release *); + struct ccconn *conn; + + conn = TAILQ_FIRST(&user->connq); + switch (user->state) { + + case USER_OUT_WAIT_OK: /* U2/A3 */ + /* wait for CONN_OK first */ + conn->cause[0] = api->cause[0]; + conn->cause[1] = api->cause[1]; + set_state(user, USER_REL_WAIT_CONN); + break; + + case USER_OUT_WAIT_CONF: /* U3/A3 */ + /* wait for SETUP.confirm first */ + conn->cause[0] = api->cause[0]; + conn->cause[1] = api->cause[1]; + set_state(user, USER_REL_WAIT_SCONF); + break; + + case USER_IN_ACCEPTING: /* U11/A7 */ + conn->cause[0] = api->cause[0]; + conn->cause[1] = api->cause[1]; + set_state(user, USER_REL_WAIT_SCOMP); + cc_conn_sig(conn, CONN_SIG_RELEASE, NULL); + break; + + case USER_ACTIVE: /* U4/A8,A9,A10 */ + conn->cause[0] = api->cause[0]; + conn->cause[1] = api->cause[1]; + set_state(user, USER_REL_WAIT); + cc_conn_sig(conn, CONN_SIG_RELEASE, NULL); + break; + + default: + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_BAD_STATE); + goto bad_state; + } + uni_msg_destroy(msg); + return; + } + + + case USER_SIG_RELEASE_CONFIRM: + { + struct atm_call_release *ind; + + switch (user->state) { + + case USER_OUT_WAIT_CONF: /* U3/A3 */ + case USER_ACTIVE: /* U4/A8,A9,A10 */ + cc_user_reset(user); + break; + + case USER_REL_WAIT: /* U5 /A8,A9,A10 */ + case USER_REL_WAIT_SCOMP: /* U12/A7 */ + case USER_REL_WAIT_SCONF: /* U13/A3 */ + case USER_REL_WAIT_CONF: /* U14/A3 */ + cc_user_reset(user); + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + return; + + case USER_IN_ACCEPTING: /* U11/A7 */ + cc_user_reset(user); + break; + + default: + goto bad_state; + } + + ind = CCZALLOC(sizeof(*ind)); + if (ind == NULL) + return; + memcpy(ind->cause, user->cause, sizeof(ind->cause)); + cc_user_send(user, ATMOP_CALL_RELEASE, ind, sizeof(*ind)); + CCFREE(ind); + return; + } + + + case USER_SIG_RELEASE_ERR: + switch (user->state) { + + case USER_REL_WAIT: /* U5/A8,A9,A10 */ + set_state(user, USER_ACTIVE); + cc_user_err(user, ATM_MKUNIERR(arg2)); + break; + + case USER_REL_WAIT_CONF: /* U14/A3 */ + cc_user_err(user, ATM_MKUNIERR(arg2)); + cc_user_active(user); + break; + + case USER_REL_WAIT_SCOMP: /* U12/A7 */ + set_state(user, USER_IN_ACCEPTING); + cc_user_err(user, ATM_MKUNIERR(arg2)); + break; + + default: + goto bad_state; + } + return; + + + case USER_SIG_ADD_PARTY: + { + struct uni_msg *msg = arg; + struct atm_add_party *add = uni_msg_rptr(msg, + struct atm_add_party *); + struct ccconn *conn; + + if (user->state != USER_ACTIVE || user->config != USER_ROOT) { + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_BAD_STATE); + return; + } + + if (add->leaf_ident == 0 || add->leaf_ident >= 32786) { + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_BAD_LEAF_IDENT); + return; + } + + conn = TAILQ_FIRST(&user->connq); + conn->called = add->called; + + cc_conn_sig(conn, CONN_SIG_ADD_PARTY, + (void *)(uintptr_t)add->leaf_ident); + + uni_msg_destroy(msg); + return; + } + + + case USER_SIG_ADD_PARTY_ERR: + if (user->state != USER_ACTIVE) + goto bad_state; + cc_user_err(user, arg2); + return; + + + case USER_SIG_ADD_PARTY_OK: + if (user->state != USER_ACTIVE) + goto bad_state; + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + return; + + + case USER_SIG_ADD_PARTY_ACK: + { + u_int leaf_ident = arg2; + struct atm_add_party_success *succ; + + if (user->state != USER_ACTIVE) + goto bad_state; + + succ = CCZALLOC(sizeof(*succ)); + if (succ == NULL) + return; + + succ->leaf_ident = leaf_ident; + cc_user_send(user, ATMOP_ADD_PARTY_SUCCESS, + succ, sizeof(*succ)); + + CCFREE(succ); + return; + } + + + case USER_SIG_ADD_PARTY_REJ: + { + u_int leaf_ident = arg2; + struct atm_add_party_reject *reject; + + if (user->state != USER_ACTIVE) + goto bad_state; + + reject = CCZALLOC(sizeof(*reject)); + if (reject == NULL) + return; + + reject->leaf_ident = leaf_ident; + reject->cause = user->cause[0]; + cc_user_send(user, ATMOP_ADD_PARTY_REJECT, + reject, sizeof(*reject)); + + CCFREE(reject); + return; + } + + + case USER_SIG_DROP_PARTY: + { + struct uni_msg *msg = arg; + struct atm_drop_party *drop = uni_msg_rptr(msg, + struct atm_drop_party *); + struct ccconn *conn; + + if (user->state != USER_ACTIVE || user->config != USER_ROOT) { + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_BAD_STATE); + return; + } + + if (drop->leaf_ident >= 32786) { + uni_msg_destroy(msg); + cc_user_err(user, ATMERR_BAD_LEAF_IDENT); + return; + } + + conn = TAILQ_FIRST(&user->connq); + conn->cause[0] = drop->cause; + memset(&conn->cause[1], 0, sizeof(conn->cause[1])); + + cc_conn_sig(conn, CONN_SIG_DROP_PARTY, + (void *)(uintptr_t)drop->leaf_ident); + + uni_msg_destroy(msg); + return; + } + + + case USER_SIG_DROP_PARTY_ERR: + if (user->state != USER_ACTIVE) + goto bad_state; + cc_user_err(user, arg2); + return; + + + case USER_SIG_DROP_PARTY_OK: + if (user->state != USER_ACTIVE) + goto bad_state; + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + return; + + + case USER_SIG_DROP_PARTY_IND: + { + u_int leaf_ident = arg2; + struct atm_drop_party *drop; + + if (user->state != USER_ACTIVE) + goto bad_state; + + drop = CCZALLOC(sizeof(*drop)); + if (drop == NULL) + return; + + drop->leaf_ident = leaf_ident; + drop->cause = user->cause[0]; + cc_user_send(user, ATMOP_DROP_PARTY, drop, sizeof(*drop)); + + CCFREE(drop); + return; + } + + + case USER_SIG_QUERY_ATTR: + { + struct uni_msg *msg = arg; + struct atm_query_connection_attributes *req; + struct ccconn *conn; + + if (user->aborted) { + cc_user_err(user, ATMERR_PREVIOUSLY_ABORTED); + uni_msg_destroy(msg); + return; + } + conn = cc_query_check(user); + if (conn == NULL) { + cc_user_err(user, ATMERR_BAD_STATE); + uni_msg_destroy(msg); + return; + } + req = uni_msg_rptr(msg, + struct atm_query_connection_attributes *); + cc_attr_query(user, conn, &req->attr, 1); + uni_msg_destroy(msg); + return; + } + + case USER_SIG_QUERY_ATTR_X: + { + struct uni_msg *msg = arg; + struct atm_query_connection_attributes_x *req; + struct ccconn *conn; + + conn = cc_query_check(user); + if (conn == NULL) { + cc_user_err(user, ATMERR_BAD_STATE); + uni_msg_destroy(msg); + return; + } + req = uni_msg_rptr(msg, + struct atm_query_connection_attributes_x *); + cc_attr_query(user, conn, req->attr, req->count); + uni_msg_destroy(msg); + return; + } + + case USER_SIG_SET_ATTR: + { + struct uni_msg *msg = arg; + struct atm_set_connection_attributes *req; + struct ccconn *conn; + + if (user->aborted) { + cc_user_err(user, ATMERR_PREVIOUSLY_ABORTED); + uni_msg_destroy(msg); + return; + } + conn = cc_set_check(user); + if (conn == NULL) { + cc_user_err(user, ATMERR_BAD_STATE); + uni_msg_destroy(msg); + return; + } + req = uni_msg_rptr(msg, struct atm_set_connection_attributes *); + cc_attr_set(user, conn, &req->attr, 1, (u_char *)(req + 1), + uni_msg_len(msg) - sizeof(*req)); + uni_msg_destroy(msg); + return; + } + + case USER_SIG_SET_ATTR_X: + { + struct uni_msg *msg = arg; + struct atm_set_connection_attributes_x *req; + struct ccconn *conn; + + conn = cc_set_check(user); + if (conn == NULL) { + cc_user_err(user, ATMERR_BAD_STATE); + uni_msg_destroy(msg); + return; + } + req = uni_msg_rptr(msg, + struct atm_set_connection_attributes_x *); + cc_attr_set(user, conn, req->attr, req->count, + (u_char *)req->attr + req->count * sizeof(req->attr[0]), + uni_msg_len(msg) - + offsetof(struct atm_set_connection_attributes_x, attr) - + req->count * sizeof(req->attr[0])); + uni_msg_destroy(msg); + return; + } + + case USER_SIG_QUERY_STATE: + { + struct atm_epstate state; + + strcpy(state.name, user->name); + switch (user->state) { + + case USER_NULL: + if (user->accepted != NULL) + state.state = ATM_A7; + else + state.state = ATM_A1; + break; + + case USER_OUT_PREPARING: + state.state = ATM_A2; + break; + + case USER_OUT_WAIT_OK: + case USER_OUT_WAIT_CONF: + case USER_REL_WAIT_SCONF: + case USER_REL_WAIT_CONF: + case USER_REL_WAIT_CONN: + state.state = ATM_A3; + break; + + case USER_ACTIVE: + case USER_REL_WAIT: + switch (user->config) { + + case USER_P2P: + state.state = ATM_A8; + break; + + case USER_ROOT: + state.state = ATM_A9; + break; + + case USER_LEAF: + state.state = ATM_A10; + break; + } + break; + + case USER_IN_PREPARING: + state.state = ATM_A4; + break; + + case USER_IN_WAITING: + state.state = ATM_A5; + break; + + case USER_IN_ARRIVED: + case USER_IN_WAIT_REJ: + case USER_IN_WAIT_ACC: + state.state = ATM_A6; + break; + + case USER_IN_ACCEPTING: + case USER_REL_WAIT_SCOMP: + state.state = ATM_A7; + break; + } + cc_user_ok(user, ATMRESP_STATE, &state, sizeof(state)); + return; + } + + case USER_SIG_GET_LOCAL_PORT_INFO: + { + struct uni_msg *msg = arg; + struct atm_port_list *list; + size_t list_len; + + list = cc_get_local_port_info(user->cc, + uni_msg_rptr(msg, struct atm_get_local_port_info *)->port, + &list_len); + uni_msg_destroy(msg); + if (list == NULL) { + cc_user_err(user, ATMERR_NOMEM); + return; + } + cc_user_ok(user, ATMRESP_PORTS, list, list_len); + CCFREE(list); + return; + } + + case USER_SIG_ABORT_CONNECTION: + { + struct uni_msg *msg = arg; + struct atm_abort_connection *abo = uni_msg_rptr(msg, + struct atm_abort_connection *); + + cc_user_abort(user, &abo->cause); + uni_msg_destroy(msg); + cc_user_ok(user, ATMRESP_NONE, NULL, 0); + return; + } + + } + if (user->cc->log & CCLOG_USER_SIG) + cc_user_log(user, "bad signal=%u in state=%u", + sig, user->state); + return; + + bad_state: + if (user->cc->log & CCLOG_USER_SIG) + cc_user_log(user, "bad state=%u for signal=%u", + user->state, sig); + return; +} |