/* * 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 * * 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 #include #include #include #include #include #include #include /* * 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; }