diff options
Diffstat (limited to 'sys/contrib/ngatm/netnatm/api/cc_conn.c')
-rw-r--r-- | sys/contrib/ngatm/netnatm/api/cc_conn.c | 2096 |
1 files changed, 2096 insertions, 0 deletions
diff --git a/sys/contrib/ngatm/netnatm/api/cc_conn.c b/sys/contrib/ngatm/netnatm/api/cc_conn.c new file mode 100644 index 0000000..a8e1b38 --- /dev/null +++ b/sys/contrib/ngatm/netnatm/api/cc_conn.c @@ -0,0 +1,2096 @@ +/* + * 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_conn.c,v 1.2 2004/07/16 18:45:11 brandt Exp $ + * + * ATM API as defined per af-saa-0108 + * + * Lower half - connection handling + */ +#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> + +static const char *stab[] = { +#define DEF(N) [N] = #N, + CONN_STATES +#undef DEF +}; + +static const char *ptab[] = { +#define DEF(N) [PARTY_##N] = #N, + PARTY_STATES +#undef DEF +}; + +const char * +cc_conn_state2str(u_int s) +{ + if (s >= sizeof(stab) / sizeof(stab[0]) || stab[s] == NULL) + return ("?"); + return (stab[s]); +} + +__inline void +cc_conn_set_state(struct ccconn *conn, enum conn_state ns) +{ + if (conn->state != ns) { + if (conn->cc->log & CCLOG_CONN_STATE) + cc_conn_log(conn, "%s -> %s", + stab[conn->state], stab[ns]); + conn->state = ns; + } +} + +const char * +cc_party_state2str(u_int s) +{ + if (s >= sizeof(ptab) / sizeof(ptab[0]) || ptab[s] == NULL) + return ("?"); + return (ptab[s]); +} + +__inline void +cc_party_set_state(struct ccparty *party, enum party_state ns) +{ + + if (party->state != ns) { + if (party->conn->cc->log & CCLOG_PARTY_STATE) + cc_party_log(party, "%s -> %s", + ptab[party->state], ptab[ns]); + party->state = ns; + } +} + +/* + * Remove connection from its user's queue + */ +__inline void +cc_disconnect_from_user(struct ccconn *conn) +{ + + if (conn->user == NULL) + cc_conn_log(conn, "no %s", "user"); + else { + TAILQ_REMOVE(&conn->user->connq, conn, connq_link); + conn->user->queue_act--; + conn->user = NULL; + } +} + +/* + * Put connection on user queue + */ +__inline void +cc_connect_to_user(struct ccconn *conn, struct ccuser *user) +{ + + if (conn->user != NULL) + cc_conn_log(conn, "still connected to %p", conn->user); + conn->user = user; + TAILQ_INSERT_TAIL(&user->connq, conn, connq_link); + conn->user->queue_act++; +} + +/* + * Send a signal to the UNI stack for this connection + */ +static void +cc_send_uni(struct ccconn *conn, u_int op, struct uni_msg *msg) +{ + struct ccreq *r; + + r = CCZALLOC(sizeof(*r)); + if (r == NULL) { + if (msg != NULL) + uni_msg_destroy(msg); + cc_conn_log(conn, "no memory for cookie op=%u", op); + return; + } + + if ((r->cookie = ++conn->port->cc->cookie) == 0) + r->cookie = ++conn->port->cc->cookie; + r->req = op; + r->conn = conn; + + TAILQ_INSERT_TAIL(&conn->port->cookies, r, link); + + conn->port->cc->funcs->send_uni(conn, conn->port->uarg, op, + r->cookie, msg); +} + +/* + * Send a RELEASE.request for this connection. + */ +static void +do_release_request(struct ccconn *conn, const struct uni_ie_cause cause[2]) +{ + struct uni_msg *u; + struct uniapi_release_request *req; + + if ((u = uni_msg_alloc(sizeof(*req))) == NULL) + return; + req = uni_msg_wptr(u, struct uniapi_release_request *); + memset(req, 0, sizeof(*req)); + u->b_wptr += sizeof(struct uniapi_release_request); + + req->release.hdr.cref = conn->cref; + req->release.hdr.act = UNI_MSGACT_DEFAULT; + + if (cause == NULL) { + IE_SETPRESENT(req->release.cause[0]); + req->release.cause[0].h.act = UNI_IEACT_DEFAULT; + req->release.cause[0].loc = UNI_CAUSE_LOC_USER; + req->release.cause[0].cause = UNI_CAUSE_UNSPEC; + } else { + req->release.cause[0] = cause[0]; + req->release.cause[1] = cause[1]; + } + + cc_send_uni(conn, UNIAPI_RELEASE_request, u); +} + +/* + * Make a RELEASE.response for this connection + */ +static void +do_release_response(struct ccconn *conn, uint8_t cause, struct uni_ie_cause *ie) +{ + struct uni_msg *u; + struct uniapi_release_response *resp; + + if ((u = uni_msg_alloc(sizeof(*resp))) == NULL) + return; + resp = uni_msg_wptr(u, struct uniapi_release_response *); + memset(resp, 0, sizeof(*resp)); + u->b_wptr += sizeof(struct uniapi_release_response); + + resp->release_compl.hdr.cref = conn->cref; + resp->release_compl.hdr.act = UNI_MSGACT_DEFAULT; + + if (ie != NULL) + resp->release_compl.cause[0] = *ie; + + if (cause != 0) { + IE_SETPRESENT(resp->release_compl.cause[0]); + resp->release_compl.cause[0].h.act = UNI_IEACT_DEFAULT; + resp->release_compl.cause[0].loc = UNI_CAUSE_LOC_USER; + resp->release_compl.cause[0].cause = cause; + } + + cc_send_uni(conn, UNIAPI_RELEASE_response, u); +} + +/********************************************************************** + * + * INSTANCE handling + */ +struct ccconn * +cc_conn_create(struct ccdata *cc) +{ + struct ccconn *conn; + + conn = CCZALLOC(sizeof(*conn)); + if (conn == NULL) + return (NULL); + + conn->state = CONN_NULL; + conn->port = NULL; + conn->cc = cc; + LIST_INIT(&conn->parties); + + LIST_INSERT_HEAD(&cc->orphaned_conns, conn, port_link); + + if (conn->cc->log & CCLOG_CONN_INST) + cc_conn_log(conn, "created %s", "orphaned"); + + return (conn); +} + +/* + * assign to port + */ +void +cc_conn_ins_port(struct ccconn *conn, struct ccport *port) +{ + + if (conn->port != NULL) { + cc_conn_log(conn, "conn is already on port %u", + conn->port->param.port); + cc_conn_rem_port(conn); + } + LIST_REMOVE(conn, port_link); + + conn->port = port; + LIST_INSERT_HEAD(&port->conn_list, conn, port_link); + +} + +/* + * remove from port + */ +void +cc_conn_rem_port(struct ccconn *conn) +{ + + if (conn->port == NULL) { + cc_conn_log(conn, "conn not on any %s", "port"); + return; + } + LIST_REMOVE(conn, port_link); + conn->port = NULL; + LIST_INSERT_HEAD(&conn->cc->orphaned_conns, conn, port_link); +} + +static void +cc_conn_flush_cookies(struct ccconn *conn) +{ + struct ccreq *r, *r1; + + if (conn->port == NULL) + return; + TAILQ_FOREACH_SAFE(r, &conn->port->cookies, link, r1) { + if (r->conn == conn) { + TAILQ_REMOVE(&conn->port->cookies, r, link); + CCFREE(r); + } + } +} + +__inline void +cc_conn_reset_acceptor(struct ccconn *conn) +{ + if (conn->acceptor != NULL) { + conn->acceptor->accepted = NULL; + conn->acceptor = NULL; + } +} + +/* + * Destroy a connection + */ +void +cc_conn_destroy(struct ccconn *conn) +{ + struct ccparty *p; + + if (conn->cc->log & CCLOG_CONN_INST) + cc_conn_log(conn, "destroy%s", ""); + + if (conn->user != NULL) { + cc_conn_log(conn, "still connected to user %p\n", conn->user); + conn->user->queue_act--; + TAILQ_REMOVE(&conn->user->connq, conn, connq_link); + } + + if (conn->acceptor != NULL) + conn->acceptor->accepted = NULL; + + cc_conn_flush_cookies(conn); + cc_conn_sig_flush(conn); + + LIST_REMOVE(conn, port_link); + while ((p = LIST_FIRST(&conn->parties)) != NULL) { + LIST_REMOVE(p, link); + CCFREE(p); + } + + CCFREE(conn); +} + +struct ccparty * +cc_party_create(struct ccconn *conn, u_int ident, u_int flag) +{ + struct ccparty *party; + + party = CCZALLOC(sizeof(*party)); + if (party == NULL) + return (NULL); + + party->conn = conn; + party->state = PARTY_NULL; + IE_SETPRESENT(party->epref); + party->epref.flag = flag; + party->epref.epref = ident; + LIST_INSERT_HEAD(&conn->parties, party, link); + + if (party->conn->cc->log & CCLOG_PARTY_INST) + cc_party_log(party, "created %u.%u", flag, ident); + + return (party); +} + +static void +cc_party_destroy(struct ccparty *party) +{ + + if (party->conn->cc->log & CCLOG_PARTY_INST) + cc_party_log(party, "destroyed %u.%u", party->epref.flag, + party->epref.epref); + + LIST_REMOVE(party, link); + CCFREE(party); +} + +static struct ccparty * +cc_party_find(struct ccconn *conn, u_int ident) +{ + struct ccparty *party; + + LIST_FOREACH(party, &conn->parties, link) + if (party->epref.epref == ident) + return (party); + return (NULL); +} +/* + * Abort connection from down stream (because of the UNI hook beeing + * disconnected). This is called from two places: + * 1) the shutdown code. + * In this case the connections should be already dissociated from + * users and be only in states waiting for the UNI stack. + * 2) from the disconnect code. + */ +void +cc_conn_abort(struct ccconn *conn, int shutdown) +{ + struct ccuser *u = conn->user; + struct ccparty *p, *p1; + + if (shutdown) { + CCASSERT(u == NULL, ("still in use")); + CCASSERT(conn->acceptor == NULL, ("still in use")); + cc_conn_destroy(conn); + return; + } + + /* + * Look whether any parties are blocked waiting for a response + * from the stack. We don't use extra party states to handle + * user aborts, so check that there is a user before using it. + */ + if (u == NULL) { + while ((p = LIST_FIRST(&conn->parties)) != NULL) + cc_party_destroy(p); + } else { + LIST_FOREACH_SAFE(p, &conn->parties, link, p1) { + switch (p->state) { + + case PARTY_NULL: /* P0 */ + /* should not happen */ + goto dpty; + + case PARTY_ACTIVE: /* P1 */ + /* don't send a drop - user'll get a rel */ + goto dpty; + + case PARTY_ADD_WAIT_CREATE: /* P2 */ + case PARTY_ADD_WAIT_OK: /* P3 */ + /* we're adding - synthesise an error */ + cc_user_sig(u, USER_SIG_ADD_PARTY_ERR, + NULL, ATMERR_BAD_PORT); + goto dpty; + + case PARTY_ADD_WAIT_ACK: /* P4 */ + /* don't send a drop - user'll get a rel */ + goto dpty; + + case PARTY_DROP_WAIT_OK: /* P5 */ + case PARTY_DROP_WAIT_ACK: /* P6 */ + case PARTY_ADD_DROP_WAIT_OK: /* P11 */ + /* we're dropping - synthesis an ok */ + cc_user_sig(u, USER_SIG_DROP_PARTY_OK, + NULL, p->epref.epref); + goto dpty; + + case PARTY_WAIT_DESTROY: /* P7 */ + goto dpty; + + case PARTY_WAIT_SETUP_COMPL: /* P8 */ + case PARTY_WAIT_SETUP_CONF: /* P10 */ + /* first party - nothing to do */ + goto dpty; + + case PARTY_WAIT_DROP_ACK_OK: /* P9 */ + case PARTY_ADD_DROPACK_WAIT_OK:/* P12 */ + /* we're dropping - nothing to do */ + goto dpty; + } + cc_party_log(p, "bad uabort for party in state %s", + ptab[p->state]); + dpty: + cc_party_destroy(p); + } + } + + /* + * Now do what the connection needs + */ + switch (conn->state) { + + case CONN_NULL: /* 0 */ + case CONN_OUT_PREPARING: /* 1 */ + /* may not happen because we're not associated with + * aport yet */ + break; + + case CONN_OUT_WAIT_CREATE: /* 2 */ + case CONN_OUT_WAIT_OK: /* 3 */ + case CONN_OUT_WAIT_DESTROY: /* 37 */ + /* return an error to the user, go back to C1/U1 + * reset cref (for C37, C3) and cookie */ + conn->cref.flag = 0; + conn->cref.cref = 0; + cc_conn_flush_cookies(conn); + cc_conn_set_state(conn, CONN_OUT_PREPARING); + cc_conn_rem_port(conn); + cc_user_sig(u, USER_SIG_CONNECT_OUTGOING_ERR, + NULL, ATMERR_BAD_PORT); + return; + + case CONN_OUT_WAIT_CONF: /* 4 */ + case CONN_ACTIVE: /* 5 */ + case CONN_IN_WAIT_COMPL: /* 13 */ + /* emulate a RELEASE.confirm */ + memset(&u->cause, 0, sizeof(u->cause)); + cc_user_sig(u, USER_SIG_RELEASE_CONFIRM, NULL, 0); + cc_disconnect_from_user(conn); + cc_conn_destroy(conn); + return; + + case CONN_IN_PREPARING: /* 10 */ + case CONN_AB_WAIT_REQ_OK: /* 33 */ + case CONN_AB_WAIT_RESP_OK: /* 34 */ + case CONN_AB_FLUSH_IND: /* 35 */ + /* no user - destroy */ + cc_conn_destroy(conn); + return; + + case CONN_IN_ARRIVED: /* 11 */ + u->aborted = 1; + cc_disconnect_from_user(conn); + cc_conn_destroy(conn); + return; + + case CONN_IN_WAIT_ACCEPT_OK: /* 12 */ + /* return ACCEPT error */ + cc_disconnect_from_user(conn); + cc_conn_reset_acceptor(conn); + cc_user_sig(u, USER_SIG_ACCEPT_ERR, + u, ATMERR_PREVIOUSLY_ABORTED); + cc_conn_destroy(conn); + return; + + case CONN_REJ_WAIT_OK: /* 14 */ + /* return REJECT ok */ + cc_disconnect_from_user(conn); + cc_conn_destroy(conn); + cc_user_sig(u, USER_SIG_REJECT_OK, NULL, 0); + return; + + case CONN_REL_IN_WAIT_OK: /* 15 */ + case CONN_REL_WAIT_OK: /* 20 */ + /* confirm destroy */ + if (u != NULL) { + /* connection not aborted */ + memset(&u->cause, 0, sizeof(u->cause)); + cc_user_sig(u, USER_SIG_RELEASE_CONFIRM, NULL, 0); + cc_disconnect_from_user(conn); + } + cc_conn_destroy(conn); + return; + + case CONN_IN_WAITING: /* 21 */ + /* user has not seen the connection - destroy */ + cc_disconnect_from_user(conn); + cc_conn_destroy(conn); + return; + } + cc_conn_log(conn, "bad state %s", stab[conn->state]); +} + +#ifdef DEBUG_MATCH +static void +print_sap(const struct uni_sap *sap) +{ + static const char *const tags[] = { + [UNISVE_ABSENT] "absent", + [UNISVE_PRESENT]"present", + [UNISVE_ANY] "any", + }; + u_int i; + + printf("addr={%s", tags[sap->addr.tag]); + if (sap->addr.tag == UNISVE_PRESENT) { + printf(",%d-%d", sap->addr.type, sap->addr.plan); + for (i = 0; i < sap->addr.len; i++) + printf("%c%02x", ",:"[i!=0], sap->addr.addr[i]); + } + printf("}\n"); + + printf("selector={%s", tags[sap->selector.tag]); + if (sap->selector.tag == UNISVE_PRESENT) + printf(",%02x", sap->selector.selector); + printf("}\n"); + + printf("blli_id2={%s", tags[sap->blli_id2.tag]); + if (sap->blli_id2.tag == UNISVE_PRESENT) + printf(",%02x,%02x", sap->blli_id2.proto, sap->blli_id2.user); + printf("}\n"); + + printf("blli_id3={%s", tags[sap->blli_id3.tag]); + if (sap->blli_id3.tag == UNISVE_PRESENT) + printf(",%02x,%02x,%02x,%06x,%04x,%d", + sap->blli_id3.proto, sap->blli_id3.user, + sap->blli_id3.ipi, sap->blli_id3.oui, + sap->blli_id3.pid, sap->blli_id3.noipi); + printf("}\n"); + + printf("bhli={%s", tags[sap->bhli.tag]); + if (sap->bhli.tag == UNISVE_PRESENT) { + printf(",%d", sap->bhli.type); + for (i = 0; i < sap->bhli.len; i++) + printf("%c%02x", ",:"[i!=0], sap->bhli.info[i]); + } + printf("}\n"); +} +#endif + +/********************************************************************* + * + * DISPATCH incoming call + */ +void +cc_conn_dispatch(struct ccconn *conn) +{ + struct ccdata *priv = conn->port->cc; + struct ccuser *user; + u_int blli_index; + +#ifdef DEBUG_MATCH + static char buf[1000]; + static struct unicx cx; + static int init = 1; + + if (init) { + uni_initcx(&cx); + init = 0; + } +#endif + + /* + * Do call dispatching according to 4.6 + */ +#ifdef DEBUG_MATCH + printf("+++++ DISPATCH++++++\n"); +#endif + for (blli_index = 0; blli_index < UNI_NUM_IE_BLLI; blli_index++) { + if (blli_index > 0 && !IE_ISGOOD(conn->blli[blli_index])) + break; +#ifdef DEBUG_MATCH + if (IE_ISPRESENT(conn->called)) { + uni_print_ie(buf, sizeof(buf), UNI_IE_CALLED, + (union uni_ieall *)&conn->called, &cx); + printf("called=%s\n", buf); + } + if (IE_ISPRESENT(conn->bhli)) { + uni_print_ie(buf, sizeof(buf), UNI_IE_BHLI, + (union uni_ieall *)&conn->bhli, &cx); + printf("bhli=%s\n", buf); + } + if (IE_ISPRESENT(conn->blli[blli_index])) { + uni_print_ie(buf, sizeof(buf), UNI_IE_BLLI, + (union uni_ieall *)&conn->blli[blli_index], &cx); + printf("%s\n", buf); + } +#endif + LIST_FOREACH(user, &priv->user_list, node_link) { + if ((user->state == USER_IN_WAITING || + user->state == USER_IN_ARRIVED || + user->state == USER_IN_WAIT_ACC || + user->state == USER_IN_WAIT_REJ) && + !unisve_is_catchall(user->sap)) { +#ifdef DEBUG_MATCH + printf("TRYING user=%p\n", user); + print_sap(user->sap); +#endif + if (unisve_match(user->sap, &conn->called, + &conn->blli[blli_index], &conn->bhli)) + goto found; + } + } + } +#ifdef DEBUG_MATCH + printf("TRYING CATCHALL\n"); +#endif + blli_index = 0; + LIST_FOREACH(user, &priv->user_list, node_link) { + if ((user->state == USER_IN_WAITING || + user->state == USER_IN_ARRIVED || + user->state == USER_IN_WAIT_ACC || + user->state == USER_IN_WAIT_REJ) && + unisve_is_catchall(user->sap)) + goto found; + } +#ifdef DEBUG_MATCH + printf("SORRY\n"); +#endif + + /* + * No application found - reject call. + */ + do_release_response(conn, UNI_CAUSE_INCOMP, NULL); + cc_conn_set_state(conn, CONN_AB_WAIT_RESP_OK); + return; + + found: +#ifdef DEBUG_MATCH + printf("MATCH\n"); +#endif + if (user->queue_max == user->queue_act) { + do_release_response(conn, UNI_CAUSE_BUSY, NULL); + cc_conn_set_state(conn, CONN_AB_WAIT_RESP_OK); + return; + } + + if (blli_index == 0 && !IE_ISGOOD(conn->blli[blli_index])) + conn->blli_selector = 0; + else + conn->blli_selector = blli_index + 1; + + cc_conn_set_state(conn, CONN_IN_WAITING); + cc_connect_to_user(conn, user); + + cc_user_sig(user, USER_SIG_SETUP_IND, NULL, 0); +} + +static void +cc_party_setup_conf(struct ccconn *conn) +{ + struct ccparty *party; + + party = cc_party_find(conn, conn->epref.epref); + if (party == NULL) { + cc_party_log(party, "no party for %s", + cc_conn_sigtab[CONN_SIG_SETUP_CONFIRM]); + return; + } + if (party->state != PARTY_WAIT_SETUP_CONF) { + cc_party_log(party, "bad state=%s for signal=%s", + ptab[party->state], cc_conn_sigtab[CONN_SIG_SETUP_CONFIRM]); + return; + } + cc_party_set_state(party, PARTY_ACTIVE); +} + +static void +cc_party_add_ack_ind(struct ccconn *conn, const struct uni_ie_epref *epref) +{ + struct ccparty *party; + + party = cc_party_find(conn, epref->epref); + if (party == NULL) { + cc_party_log(party, "no party for %s", + cc_conn_sigtab[CONN_SIG_PARTY_ADD_ACK_IND]); + } + if (party->state != PARTY_ADD_WAIT_ACK) { + cc_party_log(party, "bad state=%s for signal=%s", + ptab[party->state], + cc_conn_sigtab[CONN_SIG_PARTY_ADD_ACK_IND]); + return; + } + cc_party_set_state(party, PARTY_ACTIVE); + cc_user_sig(conn->user, USER_SIG_ADD_PARTY_ACK, + NULL, epref->epref); +} + +static void +cc_party_add_rej_ind(struct ccconn *conn, const struct uni_ie_epref *epref) +{ + struct ccparty *party; + + party = cc_party_find(conn, epref->epref); + if (party == NULL) { + cc_party_log(party, "no party for %s", + cc_conn_sigtab[CONN_SIG_PARTY_ADD_REJ_IND]); + return; + } + if (party->state != PARTY_ADD_WAIT_ACK) { + cc_party_log(party, "bad state=%s for signal=%s", + ptab[party->state], + cc_conn_sigtab[CONN_SIG_PARTY_ADD_REJ_IND]); + return; + } + cc_party_set_state(party, PARTY_WAIT_DESTROY); + cc_user_sig(conn->user, USER_SIG_ADD_PARTY_REJ, NULL, epref->epref); +} + +static void +cc_party_drop_ack_ind(struct ccconn *conn, + const struct uni_drop_party *drop) +{ + struct ccparty *party; + + party = cc_party_find(conn, drop->epref.epref); + if (party == NULL) { + cc_party_log(party, "no party for %s", + ptab[CONN_SIG_DROP_PARTY_ACK_IND]); + return; + } + switch (party->state) { + + case PARTY_ACTIVE: /* P1 */ + memset(&conn->user->cause[1], 0, sizeof(conn->user->cause[1])); + conn->user->cause[0] = drop->cause; + cc_party_set_state(party, PARTY_WAIT_DESTROY); + cc_user_sig(conn->user, USER_SIG_DROP_PARTY_IND, + NULL, party->epref.epref); + break; + + case PARTY_ADD_WAIT_ACK: /* P4 */ + memset(&conn->user->cause[1], 0, sizeof(conn->user->cause[1])); + conn->user->cause[0] = drop->cause; + cc_party_set_state(party, PARTY_WAIT_DESTROY); + cc_user_sig(conn->user, USER_SIG_ADD_PARTY_REJ, + NULL, party->epref.epref); + break; + + case PARTY_DROP_WAIT_ACK: /* P6 */ + cc_party_set_state(party, PARTY_WAIT_DESTROY); + cc_user_sig(conn->user, USER_SIG_DROP_PARTY_OK, NULL, 0); + break; + + case PARTY_WAIT_SETUP_COMPL: /* P8 */ + case PARTY_WAIT_SETUP_CONF: /* P10 */ + cc_party_set_state(party, PARTY_WAIT_DESTROY); + break; + + default: + cc_party_log(party, "bad state=%s for signal=%s", + ptab[party->state], + cc_conn_sigtab[CONN_SIG_DROP_PARTY_ACK_IND]); + break; + } +} + +/* + * Handle a signal to this connection + */ +void +cc_conn_sig_handle(struct ccconn *conn, enum conn_sig sig, + void *arg, u_int iarg) +{ + struct ccparty *party; + + if (conn->cc->log & CCLOG_CONN_SIG) + cc_conn_log(conn, "signal %s in state %s", cc_conn_sigtab[sig], + stab[conn->state]); + + switch (sig) { + + case CONN_SIG_CONNECT_OUTGOING: + /* Do SETUP */ + { + struct uni_msg *u; + struct uniapi_setup_request *setup; + + if (conn->state != CONN_OUT_PREPARING) + goto bad_state; + + if (IE_ISGOOD(conn->bearer) && + conn->bearer.cfg == UNI_BEARER_MP) { + IE_SETPRESENT(conn->epref); + conn->epref.flag = 0; + conn->epref.epref = 0; + } + + /* + * Construct message to UNI. + */ + u = uni_msg_alloc(sizeof(struct uniapi_setup_request)); + if (u == NULL) { + cc_user_sig(conn->user, USER_SIG_CONNECT_OUTGOING_ERR, + NULL, ATMERR_NOMEM); + return; + } + setup = uni_msg_wptr(u, struct uniapi_setup_request *); + memset(setup, 0, sizeof(*setup)); + u->b_wptr += sizeof(struct uniapi_setup_request); + + setup->setup.hdr.act = UNI_MSGACT_DEFAULT; + memcpy(setup->setup.blli, conn->blli, sizeof(conn->blli)); + setup->setup.bearer = conn->bearer; + setup->setup.traffic = conn->traffic; + setup->setup.qos = conn->qos; + setup->setup.exqos = conn->exqos; + setup->setup.called = conn->called; + setup->setup.calledsub[0] = conn->calledsub; + setup->setup.aal = conn->aal; + setup->setup.epref = conn->epref; + setup->setup.eetd = conn->eetd; + setup->setup.abrsetup = conn->abrsetup; + setup->setup.abradd = conn->abradd; + setup->setup.calling = conn->calling; + setup->setup.callingsub[0] = conn->callingsub; + setup->setup.connid = conn->connid; + memcpy(setup->setup.tns, conn->tns, sizeof(conn->tns)); + setup->setup.atraffic = conn->atraffic; + setup->setup.mintraffic = conn->mintraffic; + setup->setup.cscope = conn->cscope; + setup->setup.bhli = conn->bhli; + setup->setup.mdcr = conn->mdcr; + + cc_conn_set_state(conn, CONN_OUT_WAIT_CREATE); + cc_send_uni(conn, UNIAPI_SETUP_request, u); + + break; + } + + + case CONN_SIG_ARRIVAL: + /* user informed of arrival of this call */ + if (conn->state != CONN_IN_WAITING) + goto bad_state; + cc_conn_set_state(conn, CONN_IN_ARRIVED); + break; + + + case CONN_SIG_RELEASE: + { + /* Release this call */ + struct uni_msg *u; + struct uniapi_release_request *req; + + if (conn->state != CONN_ACTIVE && + conn->state != CONN_IN_WAIT_COMPL) + goto bad_state; + + if ((u = uni_msg_alloc(sizeof(*req))) == NULL) + return; + + req = uni_msg_wptr(u, struct uniapi_release_request *); + memset(req, 0, sizeof(*req)); + u->b_wptr += sizeof(struct uniapi_release_request); + + req->release.hdr.cref = conn->cref; + req->release.hdr.act = UNI_MSGACT_DEFAULT; + + req->release.cause[0] = conn->cause[0]; + req->release.cause[1] = conn->cause[1]; + + if (conn->state == CONN_ACTIVE) + cc_conn_set_state(conn, CONN_REL_WAIT_OK); + else + cc_conn_set_state(conn, CONN_REL_IN_WAIT_OK); + + cc_send_uni(conn, UNIAPI_RELEASE_request, u); + break; + } + + case CONN_SIG_REJECT: + { + /* reject from user */ + struct ccuser *user = conn->user; + + if (conn->state != CONN_IN_ARRIVED) { + cc_user_sig(user, USER_SIG_REJECT_ERR, + NULL, ATMERR_BAD_STATE); + break; + } + cc_conn_set_state(conn, CONN_REJ_WAIT_OK); + do_release_response(conn, 0, conn->cause); + break; + } + + + case CONN_SIG_ACCEPT: + { + /* User accepts. */ + struct ccuser *newep = arg; + struct uni_msg *u; + struct uniapi_setup_response *resp; + struct ccuser *user = conn->user; + + if (conn->state != CONN_IN_ARRIVED) { + cc_user_sig(user, USER_SIG_ACCEPT_ERR, + NULL, ATMERR_PREVIOUSLY_ABORTED); + break; + } + + u = uni_msg_alloc(sizeof(struct uniapi_setup_response)); + if (u == NULL) { + cc_user_sig(user, USER_SIG_ACCEPT_ERR, + NULL, ATMERR_NOMEM); + return; + } + + /* + * Link to the new endpoint + */ + conn->acceptor = newep; + newep->accepted = conn; + + /* + * Construct connect message + */ + resp = uni_msg_wptr(u, struct uniapi_setup_response *); + memset(resp, 0, sizeof(*resp)); + u->b_wptr += sizeof(*resp); + + resp->connect.hdr.act = UNI_MSGACT_DEFAULT; + resp->connect.hdr.cref = conn->cref; + + /* + * attributes + */ + if (conn->dirty_attr && CCDIRTY_AAL) + resp->connect.aal = conn->aal; + if (conn->dirty_attr && CCDIRTY_BLLI) + resp->connect.blli = + conn->blli[conn->blli_selector - 1]; + if (conn->dirty_attr && CCDIRTY_CONNID) + resp->connect.connid = conn->connid; + /* XXX NOTIFY */ + if (conn->dirty_attr && CCDIRTY_EETD) + resp->connect.eetd = conn->eetd; + /* XXX GIT */ + /* XXX UU */ + if (conn->dirty_attr && CCDIRTY_TRAFFIC) + resp->connect.traffic = conn->traffic; + if (conn->dirty_attr && CCDIRTY_EXQOS) + resp->connect.exqos = conn->exqos; + if (conn->dirty_attr && CCDIRTY_ABRSETUP) + resp->connect.abrsetup = conn->abrsetup; + if (conn->dirty_attr && CCDIRTY_ABRADD) + resp->connect.abradd = conn->abradd; + + /* + * If the SETUP had an endpoint reference - echo it back + */ + if (IE_ISPRESENT(conn->epref)) { + resp->connect.epref = conn->epref; + resp->connect.epref.flag = !resp->connect.epref.flag; + } + + cc_conn_set_state(conn, CONN_IN_WAIT_ACCEPT_OK); + cc_send_uni(conn, UNIAPI_SETUP_response, u); + break; + } + + + case CONN_SIG_ADD_PARTY: + { + /* request to add party from user */ + struct uni_msg *u; + struct uniapi_add_party_request *req; + + if (conn->state != CONN_ACTIVE) + goto bad_state; + + /* create the party */ + party = cc_party_create(conn, (u_int)(uintptr_t)arg, 0); + if (party == NULL) { + cc_user_sig(conn->user, USER_SIG_ADD_PARTY_ERR, + NULL, ATMERR_NOMEM); + return; + } + party->called = conn->called; + + /* Construct message to UNI. */ + u = uni_msg_alloc(sizeof(struct uniapi_setup_request)); + if (u == NULL) { + cc_party_destroy(party); + cc_user_sig(conn->user, USER_SIG_ADD_PARTY_ERR, + NULL, ATMERR_NOMEM); + return; + } + + req = uni_msg_wptr(u, struct uniapi_add_party_request *); + memset(req, 0, sizeof(*req)); + u->b_wptr += sizeof(struct uniapi_add_party_request); + + req->add.hdr.act = UNI_MSGACT_DEFAULT; + req->add.hdr.cref = conn->cref; + req->add.epref = party->epref; + req->add.called = party->called; + + cc_party_set_state(party, PARTY_ADD_WAIT_CREATE); + cc_send_uni(conn, UNIAPI_ADD_PARTY_request, u); + break; + } + + + case CONN_SIG_DROP_PARTY: + { + /* user request to drop a party */ + struct uni_msg *u; + struct uniapi_drop_party_request *req; + + if (conn->state != CONN_ACTIVE) + goto bad_state; + + party = cc_party_find(conn, (u_int)(uintptr_t)arg); + if (party == NULL) { + cc_user_sig(conn->user, USER_SIG_DROP_PARTY_ERR, + NULL, ATMERR_BAD_PARTY); + return; + } + + switch (party->state) { + + case PARTY_ACTIVE: + case PARTY_ADD_WAIT_ACK: + break; + + default: + cc_user_sig(conn->user, USER_SIG_DROP_PARTY_ERR, + NULL, ATMERR_BAD_STATE); + return; + + } + /* + * Construct message to UNI. + */ + u = uni_msg_alloc(sizeof(*req)); + if (u == NULL) { + cc_user_sig(conn->user, USER_SIG_DROP_PARTY_ERR, + NULL, ATMERR_NOMEM); + return; + } + + req = uni_msg_wptr(u, struct uniapi_drop_party_request *); + memset(req, 0, sizeof(*req)); + u->b_wptr += sizeof(struct uniapi_drop_party_request); + + req->drop.hdr.act = UNI_MSGACT_DEFAULT; + req->drop.hdr.cref = conn->cref; + req->drop.epref = party->epref; + req->drop.cause = conn->cause[0]; + + if (party->state == PARTY_ACTIVE) + cc_party_set_state(party, PARTY_DROP_WAIT_OK); + else + cc_party_set_state(party, PARTY_ADD_DROP_WAIT_OK); + cc_send_uni(conn, UNIAPI_DROP_PARTY_request, u); + break; + } + + case CONN_SIG_DROP_PARTY_ACK_IND: + { + struct uni_msg *msg = arg; + struct uniapi_drop_party_ack_indication *ind = uni_msg_rptr(msg, + struct uniapi_drop_party_ack_indication *); + + cc_party_drop_ack_ind(conn, &ind->drop); + break; + } + + + case CONN_SIG_USER_ABORT: + /* + * Aborting a connection. This is callable in all states. + * The connection is already disconnected from the user. + * The cause is in cause[]. + */ + switch (conn->state) { + + case CONN_NULL: /* C0 */ + case CONN_OUT_PREPARING: /* C1 */ + cc_conn_destroy(conn); + break; + + case CONN_OUT_WAIT_CONF: /* C4 */ + case CONN_ACTIVE: /* C5 */ + do_release_request(conn, conn->cause); + cc_conn_set_state(conn, CONN_AB_WAIT_REQ_OK); + break; + + case CONN_IN_WAITING: /* C21 */ + /* that should not happen */ + goto bad_state; + break; + + case CONN_IN_ARRIVED: /* C11 */ + /* + * This is called only for the first connection + * of the user - the others are re-dispatched. + */ + do_release_response(conn, 0, conn->cause); + cc_conn_set_state(conn, CONN_AB_WAIT_RESP_OK); + break; + + case CONN_IN_WAIT_COMPL: /* C13 */ + do_release_request(conn, conn->cause); + cc_conn_set_state(conn, CONN_AB_WAIT_REQ_OK); + break; + + case CONN_OUT_WAIT_DESTROY: /* C20 */ + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + break; + + case CONN_IN_WAIT_ACCEPT_OK: /* C12 */ + case CONN_AB_WAIT_REQ_OK: /* C33 */ + case CONN_AB_WAIT_RESP_OK: /* C34 */ + case CONN_AB_FLUSH_IND: /* C35 */ + /* just ignore */ + break; + + /* + * The following states may not happen, because + * we're waiting for a response from the UNI stack. + * As soon as the response comes the ABORT is undefered + * and will hit us (but in another state). + */ + case CONN_OUT_WAIT_CREATE: /* C2 */ + case CONN_OUT_WAIT_OK: /* C3 */ + case CONN_IN_PREPARING: /* C10 */ + case CONN_REJ_WAIT_OK: /* C14 */ + case CONN_REL_IN_WAIT_OK: /* C15 */ + case CONN_REL_WAIT_OK: /* C20 */ + goto bad_state; + } + break; + + + case CONN_SIG_CREATED: + { + /* + * CALL_CREATED message from UNI. This can happen for either + * incoming or outgoing connections. + */ + struct uni_msg *msg = arg; + struct uniapi_call_created *cr = uni_msg_rptr(msg, + struct uniapi_call_created *); + + switch (conn->state) { + + case CONN_OUT_WAIT_CREATE: + conn->cref = cr->cref; + cc_conn_set_state(conn, CONN_OUT_WAIT_OK); + break; + + case CONN_NULL: + conn->cref = cr->cref; + cc_conn_set_state(conn, CONN_IN_PREPARING); + break; + + default: + goto bad_state; + } + break; + } + + case CONN_SIG_DESTROYED: + /* + * CALL_DESTROYED message from UNI. + */ + switch (conn->state) { + + case CONN_OUT_WAIT_DESTROY: + cc_conn_rem_port(conn); + cc_conn_set_state(conn, CONN_OUT_PREPARING); + if (conn->user != NULL) + cc_user_sig(conn->user, + USER_SIG_CONNECT_OUTGOING_ERR, + NULL, ATM_MKUNIERR(conn->reason)); + break; + + case CONN_AB_FLUSH_IND: + cc_conn_destroy(conn); + break; + + case CONN_IN_PREPARING: + cc_conn_destroy(conn); + break; + + default: + goto bad_state; + } + break; + + + case CONN_SIG_SETUP_CONFIRM: + /* Setup confirm from the UNI. */ + { + struct uni_msg *msg = arg; + struct uniapi_setup_confirm *conf = uni_msg_rptr(msg, + struct uniapi_setup_confirm *); + + switch (conn->state) { + + case CONN_OUT_WAIT_CONF: + /* + * Shuffle attributes and inform the user. + * Negotiable attributes are condititionally shuffled, + * because not returning it means accepting it + * (in case of blli the first instance of it). + * All others are shuffled unconditionally. + * Here we should also open the VCI in the driver. (XXX) + */ +#define SHUFFLE(ATTR) conn->ATTR = conf->connect.ATTR +#define COND_SHUFFLE(ATTR) if (IE_ISPRESENT(conf->connect.ATTR)) SHUFFLE(ATTR) + + COND_SHUFFLE(aal); + (void)memset(conn->blli + 1, 0, + sizeof(conn->blli) - sizeof(conn->blli[0])); + if (IE_ISPRESENT(conf->connect.blli)) + conn->blli[0] = conf->connect.blli; + conn->blli_selector = 1; + COND_SHUFFLE(epref); + SHUFFLE(conned); + SHUFFLE(connedsub); + SHUFFLE(eetd); + COND_SHUFFLE(traffic); + COND_SHUFFLE(exqos); + COND_SHUFFLE(abrsetup); + COND_SHUFFLE(abradd); + COND_SHUFFLE(connid); +#undef SHUFFLE +#undef COND_SHUFFLE + if (IE_ISGOOD(conn->epref)) + cc_party_setup_conf(conn); + + cc_conn_set_state(conn, CONN_ACTIVE); + cc_user_sig(conn->user, USER_SIG_SETUP_CONFIRM, + NULL, 0); + break; + + case CONN_AB_FLUSH_IND: + case CONN_AB_WAIT_RESP_OK: + break; + + default: + goto bad_state; + } + break; + } + + case CONN_SIG_SETUP_IND: + { + /* SETUP indication */ + struct uni_msg *msg = arg; + struct uniapi_setup_indication *ind = uni_msg_rptr(msg, + struct uniapi_setup_indication *); + u_int i; + + if (conn->state != CONN_IN_PREPARING) + goto bad_state; + + /* + * Shuffle information elements. + */ + for (i = 0; i < UNI_NUM_IE_BLLI; i++) + conn->blli[i] = ind->setup.blli[i]; + conn->bearer = ind->setup.bearer; + conn->traffic = ind->setup.traffic; + conn->qos = ind->setup.qos; + conn->exqos = ind->setup.exqos; + conn->called = ind->setup.called; + conn->calledsub = ind->setup.calledsub[0]; + conn->aal = ind->setup.aal; + conn->epref = ind->setup.epref; + conn->eetd = ind->setup.eetd; + conn->abrsetup = ind->setup.abrsetup; + conn->abradd = ind->setup.abradd; + conn->calling = ind->setup.calling; + conn->callingsub = ind->setup.callingsub[0]; + conn->connid = ind->setup.connid; + for (i = 0; i < UNI_NUM_IE_TNS; i++) + conn->tns[i] = ind->setup.tns[i]; + conn->atraffic = ind->setup.atraffic; + conn->mintraffic = ind->setup.mintraffic; + conn->cscope = ind->setup.cscope; + conn->bhli = ind->setup.bhli; + conn->mdcr = ind->setup.mdcr; + + cc_conn_dispatch(conn); + break; + } + + + case CONN_SIG_SETUP_COMPL: + { + struct uni_msg *msg = arg; + struct uniapi_setup_indication *ind __unused = + uni_msg_rptr(msg, struct uniapi_setup_indication *); + + /* SETUP_COMPLETE.indication from UNI */ + if (conn->state == CONN_AB_FLUSH_IND || + conn->state == CONN_AB_WAIT_RESP_OK) + break; + + if (conn->state != CONN_IN_WAIT_COMPL) + goto bad_state; + + cc_conn_set_state(conn, CONN_ACTIVE); + + LIST_FOREACH(party, &conn->parties, link) { + if (party->state == PARTY_WAIT_SETUP_COMPL) + cc_party_set_state(party, PARTY_ACTIVE); + else + cc_party_log(party, "bad state=%s for sig=%s", + ptab[party->state], + cc_conn_sigtab[CONN_SIG_SETUP_COMPL]); + } + + cc_user_sig(conn->user, USER_SIG_SETUP_COMPL, NULL, 0); + break; + } + + + case CONN_SIG_PROC_IND: + { + /* + * ALERTING.indication and PROCEEDING.indication are entirly + * ignored by the specification. We need to at least save the + * connid information element. + */ + struct uni_msg *msg = arg; + struct uniapi_proceeding_indication *ind = uni_msg_rptr(msg, + struct uniapi_proceeding_indication *); + + switch (conn->state) { + + case CONN_OUT_WAIT_CONF: + if (IE_ISGOOD(ind->call_proc.connid)) + conn->connid = ind->call_proc.connid; + break; + + case CONN_AB_FLUSH_IND: + case CONN_AB_WAIT_RESP_OK: + break; + + default: + goto bad_state; + } + break; + } + + case CONN_SIG_ALERTING_IND: + { + struct uni_msg *msg = arg; + struct uniapi_alerting_indication *ind = uni_msg_rptr(msg, + struct uniapi_alerting_indication *); + + switch (conn->state) { + + case CONN_OUT_WAIT_CONF: + if (IE_ISGOOD(ind->alerting.connid)) + conn->connid = ind->alerting.connid; + break; + + case CONN_AB_FLUSH_IND: + case CONN_AB_WAIT_RESP_OK: + break; + + default: + goto bad_state; + } + break; + } + + case CONN_SIG_REL_CONF: + { + /* RELEASE.confirm from UNI */ + struct uni_msg *msg = arg; + struct uniapi_release_confirm *conf = uni_msg_rptr(msg, + struct uniapi_release_confirm *); + + switch (conn->state) { + + case CONN_OUT_WAIT_CONF: + case CONN_ACTIVE: + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + memcpy(conn->user->cause, conf->release.cause, + sizeof(conn->user->cause)); + /* + * If any party is in P6, ok the user + */ + LIST_FOREACH(party, &conn->parties, link) { + if (party->state == PARTY_DROP_WAIT_ACK) { + cc_party_set_state(party, + PARTY_WAIT_DESTROY); + cc_user_sig(conn->user, + USER_SIG_DROP_PARTY_OK, + NULL, party->epref.epref); + } + } + cc_user_sig(conn->user, USER_SIG_RELEASE_CONFIRM, + NULL, 0); + cc_disconnect_from_user(conn); + break; + + case CONN_AB_FLUSH_IND: + case CONN_AB_WAIT_RESP_OK: + break; + + case CONN_IN_WAITING: + cc_disconnect_from_user(conn); + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + break; + + case CONN_IN_ARRIVED: + conn->user->aborted = 1; + memcpy(conn->user->cause, conf->release.cause, + sizeof(conn->user->cause)); + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + cc_disconnect_from_user(conn); + break; + + case CONN_IN_WAIT_COMPL: + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + memcpy(conn->user->cause, conf->release.cause, + sizeof(conn->user->cause)); + cc_user_sig(conn->user, USER_SIG_RELEASE_CONFIRM, + NULL, 0); + cc_disconnect_from_user(conn); + break; + + default: + goto bad_state; + } + break; + } + + case CONN_SIG_REL_IND: + { + /* RELEASE.ind from UNI */ + struct uni_msg *msg = arg; + struct uniapi_release_indication *conf = uni_msg_rptr(msg, + struct uniapi_release_indication *); + + switch (conn->state) { + + case CONN_OUT_WAIT_CONF: + case CONN_ACTIVE: + do_release_response(conn, 0, NULL); + cc_conn_set_state(conn, CONN_AB_WAIT_RESP_OK); + memcpy(conn->user->cause, conf->release.cause, + sizeof(conn->user->cause)); + /* + * If any party is in P6, ok the user + */ + LIST_FOREACH(party, &conn->parties, link) { + if (party->state == PARTY_DROP_WAIT_ACK) { + cc_party_set_state(party, + PARTY_WAIT_DESTROY); + cc_user_sig(conn->user, + USER_SIG_DROP_PARTY_OK, + NULL, party->epref.epref); + } + } + cc_user_sig(conn->user, USER_SIG_RELEASE_CONFIRM, + NULL, 0); + cc_disconnect_from_user(conn); + break; + + case CONN_AB_FLUSH_IND: + case CONN_AB_WAIT_RESP_OK: + break; + + case CONN_IN_WAITING: + cc_disconnect_from_user(conn); + do_release_response(conn, 0, NULL); + cc_conn_set_state(conn, CONN_AB_WAIT_RESP_OK); + break; + + case CONN_IN_ARRIVED: + conn->user->aborted = 1; + cc_disconnect_from_user(conn); + do_release_response(conn, 0, NULL); + cc_conn_set_state(conn, CONN_AB_WAIT_RESP_OK); + break; + + case CONN_IN_WAIT_COMPL: + do_release_response(conn, 0, NULL); + cc_conn_set_state(conn, CONN_AB_WAIT_RESP_OK); + memcpy(conn->user->cause, conf->release.cause, + sizeof(conn->user->cause)); + cc_user_sig(conn->user, USER_SIG_RELEASE_CONFIRM, + NULL, 0); + cc_disconnect_from_user(conn); + break; + default: + goto bad_state; + break; + } + break; + } + + case CONN_SIG_PARTY_ALERTING_IND: + /* party alerting from UNI */ + if (conn->state == CONN_AB_FLUSH_IND) + break; + if (conn->state != CONN_ACTIVE) + goto bad_state; + /* ignore */ + break; + + case CONN_SIG_PARTY_ADD_ACK_IND: + { + /* ADD PARTY ACKNOWLEDGE from UNI */ + struct uni_msg *msg = arg; + struct uniapi_add_party_ack_indication *ind = uni_msg_rptr(msg, + struct uniapi_add_party_ack_indication *); + + if (conn->state == CONN_AB_FLUSH_IND) + break; + if (conn->state != CONN_ACTIVE) + goto bad_state; + + cc_party_add_ack_ind(conn, &ind->ack.epref); + break; + } + + + case CONN_SIG_PARTY_ADD_REJ_IND: + { + /* ADD PARTY REJECT indication */ + struct uni_msg *msg = arg; + struct uniapi_add_party_rej_indication *ind = uni_msg_rptr(msg, + struct uniapi_add_party_rej_indication *); + + if (conn->state == CONN_AB_FLUSH_IND) + break; + if (conn->state != CONN_ACTIVE) + goto bad_state; + + memset(&conn->user->cause[1], 0, sizeof(conn->user->cause[1])); + conn->user->cause[0] = ind->rej.cause; + + cc_party_add_rej_ind(conn, &ind->rej.epref); + break; + } + + + case CONN_SIG_DROP_PARTY_IND: + { + /* DROP_PARTY.indication from UNI */ + struct uni_msg *msg = arg; + struct uniapi_drop_party_indication *ind = uni_msg_rptr(msg, + struct uniapi_drop_party_indication *); + struct uniapi_drop_party_ack_request *req; + struct uni_msg *u; + + if (conn->state == CONN_AB_FLUSH_IND) + break; + if (conn->state != CONN_ACTIVE) + goto bad_state; + + party = cc_party_find(conn, ind->drop.epref.epref); + if (party == NULL) { + cc_party_log(party, "no party for %s", + cc_conn_sigtab[sig]); + break; + } + + u = uni_msg_alloc(sizeof(*req)); + if (u == NULL) + return; + + memset(&conn->user->cause[1], 0, sizeof(conn->user->cause[1])); + conn->user->cause[0] = ind->drop.cause; + + switch (party->state) { + + default: + cc_party_log(party, "bad state %s for DROP.ind", + ptab[party->state]); + /* FALLTHRU */ + + case PARTY_ACTIVE: /* P1 -> P9 */ + cc_party_set_state(party, PARTY_WAIT_DROP_ACK_OK); + break; + + case PARTY_ADD_WAIT_ACK: /* P4 -> P12 */ + cc_party_set_state(party, PARTY_ADD_DROPACK_WAIT_OK); + break; + } + + /* + * Construct message to UNI. + */ + req = uni_msg_wptr(u, struct uniapi_drop_party_ack_request *); + memset(req, 0, sizeof(*req)); + u->b_wptr += sizeof(*req); + + IE_SETPRESENT(req->ack.epref); + req->ack.hdr.act = UNI_MSGACT_DEFAULT; + req->ack.hdr.cref = conn->cref; + + req->ack.epref.flag = 0; + req->ack.epref.epref = ind->drop.epref.epref; + + cc_send_uni(conn, UNIAPI_DROP_PARTY_ACK_request, u); + break; + } + + case CONN_SIG_OK: + { + /* OK response from UNI */ + struct ccuser *user = conn->user; + + switch (conn->state) { + + case CONN_OUT_WAIT_OK: /* C3 */ + cc_conn_set_state(conn, CONN_OUT_WAIT_CONF); + if (conn->user != NULL) + cc_user_sig(conn->user, + USER_SIG_CONNECT_OUTGOING_OK, NULL, 0); + break; + + case CONN_AB_WAIT_RESP_OK: /* C33 */ + case CONN_AB_WAIT_REQ_OK: /* C34 */ + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + break; + + case CONN_REL_WAIT_OK: /* C20 */ + case CONN_REL_IN_WAIT_OK: /* C15 */ + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + if (conn->user != NULL) { + /* connection has not been aborted */ + memset(&conn->user->cause, 0, + sizeof(conn->user->cause)); + cc_user_sig(conn->user, + USER_SIG_RELEASE_CONFIRM, NULL, 0); + cc_disconnect_from_user(conn); + } + break; + + case CONN_IN_WAIT_ACCEPT_OK: /* C12 */ + if (user == NULL) { + /* has been aborted */ + do_release_request(conn, NULL); + cc_conn_set_state(conn, CONN_AB_WAIT_REQ_OK); + break; + } + cc_conn_set_state(conn, CONN_IN_WAIT_COMPL); + cc_disconnect_from_user(conn); + cc_user_sig(user, USER_SIG_ACCEPT_OK, NULL, 0); + if (conn->acceptor == NULL) { + do_release_request(conn, NULL); + cc_conn_set_state(conn, CONN_AB_WAIT_REQ_OK); + break; + } + cc_connect_to_user(conn, conn->acceptor); + cc_conn_reset_acceptor(conn); + cc_user_sig(conn->user, USER_SIG_ACCEPTING, NULL, 0); + break; + + case CONN_REJ_WAIT_OK: /* C14 */ + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + if (user != NULL) { + cc_disconnect_from_user(conn); + cc_user_sig(user, USER_SIG_REJECT_OK, NULL, 0); + } + break; + + default: + /* maybe it's for a party */ + LIST_FOREACH(party, &conn->parties, link) { + switch (party->state) { + + case PARTY_ADD_WAIT_OK: /* P3 */ + if (user != NULL) + cc_user_sig(user, + USER_SIG_ADD_PARTY_OK, + NULL, 0); + cc_party_set_state(party, + PARTY_ADD_WAIT_ACK); + goto ex_party_ok; + + case PARTY_DROP_WAIT_OK: /* P5 */ + cc_party_set_state(party, + PARTY_DROP_WAIT_ACK); + goto ex_party_ok; + + case PARTY_WAIT_DROP_ACK_OK: /* P9 */ + case PARTY_ADD_DROPACK_WAIT_OK:/* P12 */ + { + struct ccparty *p1; + + cc_party_set_state(party, + PARTY_WAIT_DESTROY); + /* signal to user only if there are any other parties */ + LIST_FOREACH(p1, &conn->parties, link) + if (p1 != party) + break; + if (p1 != NULL && user != NULL) + cc_user_sig(user, + USER_SIG_DROP_PARTY_IND, + NULL, + party->epref.epref); + + goto ex_party_ok; + } + + case PARTY_ADD_DROP_WAIT_OK: /* P11 */ + cc_party_set_state(party, + PARTY_DROP_WAIT_ACK); + goto ex_party_ok; + + default: + break; + } + } + goto bad_state; + ex_party_ok: + break; + } + break; + } + + case CONN_SIG_ERROR: + { + /* error response from UNI */ + u_int reason = (iarg >> 16) & 0xffff; + u_int state = iarg & 0xffff; + struct ccuser *user = conn->user; + + switch (conn->state) { + + case CONN_OUT_WAIT_CREATE: /* C2 */ + cc_conn_rem_port(conn); + cc_conn_set_state(conn, CONN_OUT_PREPARING); + if (conn->user != NULL) + cc_user_sig(conn->user, + USER_SIG_CONNECT_OUTGOING_ERR, + NULL, ATM_MKUNIERR(reason)); + break; + + case CONN_OUT_WAIT_OK: /* C3 */ + cc_conn_set_state(conn, CONN_OUT_WAIT_DESTROY); + conn->reason = reason; + break; + + case CONN_AB_WAIT_REQ_OK: /* C33 */ + if (state == UNI_CALLSTATE_U12) { + do_release_response(conn, 0, conn->cause); + cc_conn_set_state(conn, CONN_AB_WAIT_RESP_OK); + break; + } + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + break; + + case CONN_AB_WAIT_RESP_OK: /* C34 */ + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + break; + + case CONN_REL_WAIT_OK: /* C20 */ + if (user == NULL) { + /* connection has been aborted. */ + if (state == UNI_CALLSTATE_U10) { + /* do what we can */ + do_release_request(conn, conn->cause); + cc_conn_set_state(conn, + CONN_AB_WAIT_REQ_OK); + } else if (state == UNI_CALLSTATE_U12) { + do_release_response(conn, 0, NULL); + cc_conn_set_state(conn, + CONN_AB_WAIT_RESP_OK); + } else { + cc_conn_set_state(conn, + CONN_AB_FLUSH_IND); + } + break; + } + if (state == UNI_CALLSTATE_U10) { + cc_conn_set_state(conn, CONN_ACTIVE); + cc_user_sig(conn->user, USER_SIG_RELEASE_ERR, + NULL, reason); + } else if (state == UNI_CALLSTATE_U12) { + do_release_response(conn, 0, NULL); + cc_conn_set_state(conn, CONN_AB_WAIT_RESP_OK); + memset(&conn->user->cause, 0, + sizeof(conn->user->cause)); + cc_user_sig(conn->user, + USER_SIG_RELEASE_CONFIRM, NULL, 0); + cc_disconnect_from_user(conn); + } else { + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + memset(&conn->user->cause, 0, + sizeof(conn->user->cause)); + cc_user_sig(conn->user, + USER_SIG_RELEASE_CONFIRM, NULL, 0); + cc_disconnect_from_user(conn); + } + break; + + case CONN_IN_WAIT_ACCEPT_OK: /* C12 */ + if (user == NULL) { + /* connection was aborted */ + if (state == UNI_CALLSTATE_U6 || + state == UNI_CALLSTATE_U7 || + state == UNI_CALLSTATE_U9 || + state == UNI_CALLSTATE_U12) { + do_release_response(conn, 0, NULL); + cc_conn_set_state(conn, + CONN_AB_WAIT_RESP_OK); + } else { + cc_conn_set_state(conn, + CONN_AB_FLUSH_IND); + } + break; + } + cc_conn_reset_acceptor(conn); + if (state == UNI_CALLSTATE_U6 || + state == UNI_CALLSTATE_U9 || + state == UNI_CALLSTATE_U7) { + cc_user_sig(user, USER_SIG_ACCEPT_ERR, + NULL, ATM_MKUNIERR(reason)); + cc_conn_set_state(conn, CONN_IN_ARRIVED); + } else if (state == UNI_CALLSTATE_U12) { + do_release_response(conn, 0, NULL); + cc_disconnect_from_user(conn); + cc_user_sig(user, USER_SIG_ACCEPT_ERR, + user, ATMERR_PREVIOUSLY_ABORTED); + cc_conn_set_state(conn, CONN_AB_WAIT_RESP_OK); + } else { + cc_disconnect_from_user(conn); + cc_user_sig(user, USER_SIG_ACCEPT_ERR, + user, ATMERR_PREVIOUSLY_ABORTED); + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + } + break; + + case CONN_REJ_WAIT_OK: /* C14 */ + if (user == NULL) { + /* connection has been aborted. */ + if (state == UNI_CALLSTATE_U6 || + state == UNI_CALLSTATE_U7 || + state == UNI_CALLSTATE_U9 || + state == UNI_CALLSTATE_U12) { + /* do what we can */ + do_release_response(conn, 0, NULL); + cc_conn_set_state(conn, + CONN_AB_WAIT_RESP_OK); + } else { + cc_conn_set_state(conn, + CONN_AB_FLUSH_IND); + } + break; + } + if (state == UNI_CALLSTATE_U6 || + state == UNI_CALLSTATE_U9 || + state == UNI_CALLSTATE_U7) { + cc_user_sig(user, USER_SIG_REJECT_ERR, + NULL, ATM_MKUNIERR(reason)); + cc_conn_set_state(conn, CONN_IN_ARRIVED); + } else { + cc_disconnect_from_user(conn); + cc_user_sig(user, USER_SIG_REJECT_OK, NULL, 0); + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + } + break; + + case CONN_REL_IN_WAIT_OK: /* C15 */ + if (user == NULL) { + /* connection has been aborted. */ + if (state == UNI_CALLSTATE_U8) { + /* do what we can */ + do_release_request(conn, conn->cause); + cc_conn_set_state(conn, + CONN_AB_WAIT_REQ_OK); + } else if (state == UNI_CALLSTATE_U12) { + do_release_response(conn, 0, NULL); + cc_conn_set_state(conn, + CONN_AB_WAIT_RESP_OK); + } else { + cc_conn_set_state(conn, + CONN_AB_FLUSH_IND); + } + break; + } + if (state == UNI_CALLSTATE_U8) { + cc_conn_set_state(conn, CONN_IN_WAIT_COMPL); + cc_user_sig(conn->user, USER_SIG_RELEASE_ERR, + NULL, reason); + } else if (state == UNI_CALLSTATE_U12) { + do_release_response(conn, 0, NULL); + cc_conn_set_state(conn, CONN_AB_WAIT_RESP_OK); + memset(&conn->user->cause, 0, + sizeof(conn->user->cause)); + cc_user_sig(conn->user, + USER_SIG_RELEASE_CONFIRM, NULL, 0); + cc_disconnect_from_user(conn); + } else { + cc_conn_set_state(conn, CONN_AB_FLUSH_IND); + memset(&conn->user->cause, 0, + sizeof(conn->user->cause)); + cc_user_sig(conn->user, + USER_SIG_RELEASE_CONFIRM, NULL, 0); + cc_disconnect_from_user(conn); + } + break; + + default: + /* maybe it's for a party */ + LIST_FOREACH(party, &conn->parties, link) { + switch (party->state) { + + case PARTY_ADD_WAIT_CREATE: /* P2 */ + cc_party_destroy(party); + if (user != NULL) + cc_user_sig(user, + USER_SIG_ADD_PARTY_ERR, + NULL, ATM_MKUNIERR(reason)); + goto ex_party_err; + + case PARTY_ADD_WAIT_OK: /* P3 */ + cc_party_set_state(party, + PARTY_WAIT_DESTROY); + if (user != NULL) + cc_user_sig(user, + USER_SIG_ADD_PARTY_ERR, + NULL, ATM_MKUNIERR(reason)); + goto ex_party_err; + + case PARTY_DROP_WAIT_OK: /* P5 */ + cc_party_set_state(party, + PARTY_ACTIVE); + if (user != NULL) + cc_user_sig(user, + USER_SIG_DROP_PARTY_ERR, + NULL, ATM_MKUNIERR(reason)); + goto ex_party_err; + + case PARTY_WAIT_DROP_ACK_OK: /* P9 */ + cc_party_set_state(party, + PARTY_ACTIVE); + goto ex_party_err; + + case PARTY_ADD_DROP_WAIT_OK: /* P11 */ + cc_party_set_state(party, + PARTY_ADD_WAIT_ACK); + if (user != NULL) + cc_user_sig(user, + USER_SIG_DROP_PARTY_ERR, + NULL, ATM_MKUNIERR(reason)); + goto ex_party_err; + + case PARTY_ADD_DROPACK_WAIT_OK:/* P12 */ + cc_party_set_state(party, + PARTY_ADD_WAIT_ACK); + goto ex_party_err; + + default: + break; + } + } + cc_conn_log(conn, "unexpected reason=%u ustate=%u " + "state=%s\n", reason, state, stab[conn->state]); + ex_party_err: + break; + } + break; + } + + case CONN_SIG_PARTY_CREATED: + { + struct uni_msg *msg = arg; + struct uniapi_party_created *pcr = uni_msg_rptr(msg, + struct uniapi_party_created *); + + party = cc_party_find(conn, pcr->epref.epref); + if (party == NULL) { + /* for incoming connections we see the party-created + * immediately after the call-create so that we + * must be in C10 */ + switch (conn->state) { + + case CONN_IN_PREPARING: + party = cc_party_create(conn, + pcr->epref.epref, 1); + if (party == NULL) + break; + cc_party_set_state(party, + PARTY_WAIT_SETUP_COMPL); + break; + + case CONN_OUT_WAIT_OK: + party = cc_party_create(conn, + pcr->epref.epref, 0); + if (party == NULL) + break; + cc_party_set_state(party, + PARTY_WAIT_SETUP_CONF); + break; + + default: + goto bad_state; + } + break; + } + /* this is for an ADD-PARTY */ + if (conn->state != CONN_ACTIVE) + goto bad_state; + if (party->state != PARTY_ADD_WAIT_CREATE) + goto bad_party_state; + cc_party_set_state(party, PARTY_ADD_WAIT_OK); + break; + } + + case CONN_SIG_PARTY_DESTROYED: + { + struct uni_msg *msg = arg; + struct uniapi_party_destroyed *pcr = uni_msg_rptr(msg, + struct uniapi_party_destroyed *); + + party = cc_party_find(conn, pcr->epref.epref); + if (party == NULL) { + cc_conn_log(conn, "no party to destroy %u/%u", + pcr->epref.flag, pcr->epref.epref); + break; + } + cc_party_destroy(party); + break; + } + + } + + return; + + bad_state: + cc_conn_log(conn, "bad state=%s for signal=%s", + stab[conn->state], cc_conn_sigtab[sig]); + return; + + bad_party_state: + cc_conn_log(conn, "bad party state=%s for signal=%s", + ptab[party->state], cc_conn_sigtab[sig]); + return; +} |