diff options
| author | Hartmut Brandt <harti@FreeBSD.org> | 2004-07-08 16:39:03 +0000 | 
|---|---|---|
| committer | Hartmut Brandt <harti@FreeBSD.org> | 2004-07-08 16:39:03 +0000 | 
| commit | 8339b15086533b30b0928227360a8e1752119945 (patch) | |
| tree | 55bf85f551562770045b9eb489e60cc1ecedee32 /sys/contrib/ngatm/netnatm/api/cc_user.c | |
| parent | ce3b9cfbd9190b88b516a5238a4dfd602f65985a (diff) | |
Diffstat (limited to 'sys/contrib/ngatm/netnatm/api/cc_user.c')
| -rw-r--r-- | sys/contrib/ngatm/netnatm/api/cc_user.c | 1921 | 
1 files changed, 1921 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 0000000000000..a4d5c76ffdd54 --- /dev/null +++ b/sys/contrib/ngatm/netnatm/api/cc_user.c @@ -0,0 +1,1921 @@ +/* + * 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.2 2004/07/08 09:17:18 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 */ +			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 */ +			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; + +		if (newep == NULL) { +			uni_msg_destroy(msg); +			cc_user_err(user, ATMERR_BAD_ENDPOINT); +			return; +		} + +		if (newep->state != USER_NULL || newep->accepted != NULL) { +			uni_msg_destroy(msg); +			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; +} | 
