mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-25 11:37:56 +00:00
828 lines
20 KiB
C
828 lines
20 KiB
C
/*
|
|
* Copyright (c) 1996-2003
|
|
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
|
|
* All rights reserved.
|
|
*
|
|
* Author: Hartmut Brandt <harti@freebsd.org>
|
|
*
|
|
* Redistribution 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 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 IS PROVIDED BY THE AUTHOR AND 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 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/sig/sig_reset.c,v 1.11 2004/08/05 07:11:03 brandt Exp $
|
|
*
|
|
* Reset-start and reset-respond
|
|
*/
|
|
|
|
#include <netnatm/unimsg.h>
|
|
#include <netnatm/saal/sscfudef.h>
|
|
#include <netnatm/msg/unistruct.h>
|
|
#include <netnatm/msg/unimsglib.h>
|
|
#include <netnatm/sig/uni.h>
|
|
|
|
#include <netnatm/sig/unipriv.h>
|
|
#include <netnatm/sig/unimkmsg.h>
|
|
|
|
static void response_restart(struct uni *, struct uni_msg *, struct uni_all *);
|
|
static void response_status(struct uni *, struct uni_msg *, struct uni_all *);
|
|
|
|
static void response_t317(struct uni *);
|
|
|
|
static void response_error(struct uni *, struct uniapi_reset_error_response *,
|
|
uint32_t cookie);
|
|
static void response_response(struct uni *, struct uniapi_reset_response *,
|
|
uint32_t);
|
|
|
|
static void start_request(struct uni *, struct uniapi_reset_request *,
|
|
uint32_t);
|
|
|
|
static void start_t316(struct uni *);
|
|
|
|
static void start_restart_ack(struct uni *, struct uni_msg *, struct uni_all *);
|
|
static void start_status(struct uni *, struct uni_msg *, struct uni_all *);
|
|
|
|
static int restart_forward(struct uni *, const struct uni_all *);
|
|
|
|
#define DEF_PRIV_SIG(NAME, FROM) [SIG##NAME] = "SIG"#NAME,
|
|
static const char *const start_sigs[] = {
|
|
DEF_START_SIGS
|
|
};
|
|
#undef DEF_PRIV_SIG
|
|
|
|
#define DEF_PRIV_SIG(NAME, FROM) [SIG##NAME] = "SIG"#NAME,
|
|
static const char *const respond_sigs[] = {
|
|
DEF_RESPOND_SIGS
|
|
};
|
|
#undef DEF_PRIV_SIG
|
|
|
|
TIMER_FUNC_UNI(t317, t317_func)
|
|
TIMER_FUNC_UNI(t316, t316_func)
|
|
|
|
/*
|
|
* Reset-Start process.
|
|
*/
|
|
void
|
|
uni_sig_start(struct uni *uni, u_int sig, uint32_t cookie,
|
|
struct uni_msg *m, struct uni_all *u)
|
|
{
|
|
if (sig >= SIGS_END) {
|
|
VERBOSE(uni, UNI_FAC_ERR, 1, "Signal %d outside of range to "
|
|
"Reset-Start", sig);
|
|
if (m)
|
|
uni_msg_destroy(m);
|
|
if (u)
|
|
UNI_FREE(u);
|
|
return;
|
|
}
|
|
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1,
|
|
"Signal %s in state %u of Reset-Start; cookie %u",
|
|
start_sigs[sig], uni->glob_start, cookie);
|
|
|
|
switch (sig) {
|
|
|
|
/*
|
|
* User requests
|
|
*/
|
|
case SIGS_RESET_request:
|
|
start_request(uni,
|
|
uni_msg_rptr(m, struct uniapi_reset_request *), cookie);
|
|
uni_msg_destroy(m);
|
|
break;
|
|
|
|
/*
|
|
* Timers
|
|
*/
|
|
case SIGS_T316:
|
|
start_t316(uni);
|
|
break;
|
|
|
|
/*
|
|
* SAAL
|
|
*/
|
|
case SIGS_RESTART_ACK:
|
|
start_restart_ack(uni, m, u);
|
|
uni_msg_destroy(m);
|
|
UNI_FREE(u);
|
|
break;
|
|
|
|
case SIGS_STATUS:
|
|
start_status(uni, m, u);
|
|
uni_msg_destroy(m);
|
|
UNI_FREE(u);
|
|
break;
|
|
|
|
case SIGS_END:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reset-request from USER.
|
|
*
|
|
* Q.2931:Reset-Start 1/2
|
|
*/
|
|
static void
|
|
start_request(struct uni *uni, struct uniapi_reset_request *req, uint32_t cookie)
|
|
{
|
|
struct uni_all *resp;
|
|
int err;
|
|
|
|
if (uni->glob_start != UNI_CALLSTATE_REST0) {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALLSTATE, cookie, 0);
|
|
return;
|
|
}
|
|
|
|
if ((resp = UNI_ALLOC()) == NULL) {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_NOMEM, cookie, 0);
|
|
return;
|
|
}
|
|
|
|
MK_MSG_ORIG(resp, UNI_RESTART, 0, 0);
|
|
resp->u.restart.restart = req->restart;
|
|
resp->u.restart.connid = req->connid;
|
|
|
|
if (restart_forward(uni, resp))
|
|
return;
|
|
|
|
uni->connid_start = req->connid;
|
|
uni->restart_start = req->restart;
|
|
|
|
if ((err = uni_send_output(resp, uni)) != 0)
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_ENCODING, cookie, 0);
|
|
UNI_FREE(resp);
|
|
if (err)
|
|
return;
|
|
|
|
uni->cnt316 = 0;
|
|
TIMER_START_UNI(uni, t316, uni->timer316);
|
|
uni->glob_start = UNI_CALLSTATE_REST1;
|
|
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Start state := 1");
|
|
|
|
|
|
uniapi_uni_error(uni, UNIAPI_OK, cookie, 0);
|
|
}
|
|
|
|
/*
|
|
* T316 timeout function
|
|
*/
|
|
static void
|
|
t316_func(struct uni *uni)
|
|
{
|
|
uni_enq_start(uni, SIGS_T316, 0, NULL, NULL);
|
|
}
|
|
|
|
/*
|
|
* Q.2931:Reset-Start 1/2
|
|
*/
|
|
static void
|
|
start_t316(struct uni *uni)
|
|
{
|
|
if (uni->glob_start != UNI_CALLSTATE_REST1) {
|
|
VERBOSE0(uni, UNI_FAC_ERR, "T316 in state %d",
|
|
uni->glob_start);
|
|
return;
|
|
}
|
|
|
|
if (++uni->cnt316 == uni->init316) {
|
|
struct uni_msg *app;
|
|
struct uniapi_reset_error_indication *resp;
|
|
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Start error");
|
|
|
|
resp = ALLOC_API(struct uniapi_reset_error_indication, app);
|
|
if (resp != NULL) {
|
|
resp->source = 0;
|
|
resp->reason = UNIAPI_RESET_ERROR_NO_RESPONSE,
|
|
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_RESET_ERROR_indication, 0, app);
|
|
}
|
|
|
|
uni->glob_start = UNI_CALLSTATE_REST0;
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Start state := 0");
|
|
} else {
|
|
struct uni_all *resp;
|
|
|
|
if ((resp = UNI_ALLOC()) == NULL)
|
|
return;
|
|
|
|
MK_MSG_ORIG(resp, UNI_RESTART, 0, 0);
|
|
resp->u.restart.restart = uni->restart_start;
|
|
resp->u.restart.connid = uni->connid_start;
|
|
|
|
(void)uni_send_output(resp, uni);
|
|
|
|
UNI_FREE(resp);
|
|
|
|
TIMER_START_UNI(uni, t316, uni->timer316);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Got RESTART_ACK.
|
|
*/
|
|
static void
|
|
start_restart_ack(struct uni *uni, struct uni_msg *m, struct uni_all *u)
|
|
{
|
|
enum uni_callstate new_state;
|
|
struct uniapi_reset_confirm *conf;
|
|
struct uni_msg *app;
|
|
|
|
if (uni->glob_start == UNI_CALLSTATE_REST0) {
|
|
uni_respond_status_mtype(uni, &u->u.hdr.cref, uni->glob_start,
|
|
UNI_CAUSE_MSG_INCOMP, UNI_RESTART_ACK);
|
|
return;
|
|
}
|
|
|
|
if (uni->glob_start != UNI_CALLSTATE_REST1) {
|
|
ASSERT(0, ("bad global call state in Reset-Start"));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If body decoding fails, this is because IEs are wrong.
|
|
*/
|
|
(void)uni_decode_body(m, u, &uni->cx);
|
|
MANDATE_IE(uni, u->u.restart_ack.restart, UNI_IE_RESTART);
|
|
|
|
if (IE_ISGOOD(u->u.restart_ack.restart)) {
|
|
/*
|
|
* Q.2931: 5.5.2.2
|
|
*/
|
|
if (u->u.restart_ack.restart.rclass == UNI_RESTART_ALL &&
|
|
IE_ISGOOD(u->u.restart_ack.connid)) {
|
|
UNI_SAVE_IERR(&uni->cx, UNI_IE_CONNID,
|
|
u->u.restart_ack.connid.h.act,
|
|
UNI_IERR_UNK);
|
|
} else if ((u->u.restart_ack.restart.rclass == UNI_RESTART_PATH ||
|
|
u->u.restart_ack.restart.rclass == UNI_RESTART_CHANNEL)) {
|
|
MANDATE_IE(uni, u->u.restart_ack.connid, UNI_IE_CONNID);
|
|
}
|
|
}
|
|
/*
|
|
* Compare the information elements now, because
|
|
* we may need the new callstate for the status message
|
|
* below.
|
|
*/
|
|
new_state = UNI_CALLSTATE_REST1;
|
|
|
|
if (IE_ISGOOD(u->u.restart_ack.restart) &&
|
|
IE_ISGOOD(uni->restart_start) &&
|
|
u->u.restart_ack.restart.rclass == uni->restart_start.rclass &&
|
|
!IE_ISGOOD(u->u.restart_ack.connid) == !IE_ISGOOD(uni->connid_start) &&
|
|
(!IE_ISGOOD(uni->connid_start) ||
|
|
(u->u.restart_ack.connid.vpci == uni->connid_start.vpci &&
|
|
u->u.restart_ack.connid.vci == uni->connid_start.vci)))
|
|
new_state = UNI_CALLSTATE_REST0;
|
|
|
|
switch (uni_verify(uni, u->u.hdr.act)) {
|
|
case VFY_RAIM:
|
|
case VFY_RAI:
|
|
uni_respond_status_verify(uni, &u->u.hdr.cref,
|
|
UNI_CALLSTATE_REST1, NULL, 0);
|
|
case VFY_I:
|
|
return;
|
|
|
|
case VFY_CLR:
|
|
uni->glob_start = UNI_CALLSTATE_REST0;
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1,
|
|
"Reset-Start state := 0");
|
|
return;
|
|
|
|
case VFY_RAP:
|
|
case VFY_RAPU:
|
|
uni_respond_status_verify(uni, &u->u.hdr.cref,
|
|
new_state, NULL, 0);
|
|
case VFY_OK:
|
|
break;
|
|
}
|
|
|
|
if (new_state == UNI_CALLSTATE_REST1)
|
|
/*
|
|
* Q.2931: 5.5.1.2/2
|
|
*/
|
|
return;
|
|
|
|
/*
|
|
* Build restart.confirm signal for application
|
|
*/
|
|
if (!IE_ISGOOD(u->u.restart_ack.connid))
|
|
u->u.restart.connid.h.present = 0;
|
|
|
|
|
|
if ((conf = ALLOC_API(struct uniapi_reset_confirm, app)) == NULL)
|
|
return;
|
|
conf->restart = u->u.restart.restart;
|
|
conf->connid = u->u.restart.connid;
|
|
|
|
TIMER_STOP_UNI(uni, t316);
|
|
|
|
uni->funcs->uni_output(uni, uni->arg, UNIAPI_RESET_confirm, 0, app);
|
|
|
|
uni->glob_start = UNI_CALLSTATE_REST0;
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Start state := 0");
|
|
}
|
|
|
|
/*
|
|
* Reset-Start got a STATUS message.
|
|
*
|
|
* Q.2931: Reset-Start 2/2
|
|
*
|
|
* In Q.2931 only CALLSTATE_REST1 is allowed, this seems silly and to contradict
|
|
* 5.6.12. So allow it in any state.
|
|
*
|
|
* The following states are considered compatible:
|
|
*
|
|
* Sender Receiver(we)
|
|
* ------ --------
|
|
* Rest0 Rest0 this is the normal state OK!
|
|
* Rest2 Rest0 this may be the result of no answer from the API
|
|
* on the remote end and the us finally timing out. ERROR!
|
|
* Rest2 Rest1 this is normal. OK!
|
|
* Rest0 Rest1 RESTART_ACK was probably lost. OK!
|
|
*
|
|
* All others are wrong.
|
|
*/
|
|
static void
|
|
start_status(struct uni *uni, struct uni_msg *m, struct uni_all *u)
|
|
{
|
|
(void)uni_decode_body(m, u, &uni->cx);
|
|
MANDATE_IE(uni, u->u.status.callstate, UNI_IE_CALLSTATE);
|
|
MANDATE_IE(uni, u->u.status.cause, UNI_IE_CAUSE);
|
|
switch (uni_verify(uni, u->u.hdr.act)) {
|
|
case VFY_CLR:
|
|
uni->glob_start = UNI_CALLSTATE_REST0;
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Start state := 0");
|
|
return;
|
|
|
|
case VFY_RAIM:
|
|
case VFY_RAI:
|
|
case VFY_RAP:
|
|
case VFY_RAPU:
|
|
uni_respond_status_verify(uni, &u->u.hdr.cref, uni->glob_start,
|
|
NULL, 0);
|
|
case VFY_I:
|
|
case VFY_OK:
|
|
break;
|
|
}
|
|
if (!IE_ISGOOD(u->u.status.callstate)) {
|
|
/*
|
|
* As a result of the strange handling above, we must
|
|
* process a STATUS with an invalid or missing callstate!
|
|
*/
|
|
return;
|
|
}
|
|
if ((u->u.status.callstate.state == UNI_CALLSTATE_REST0 &&
|
|
uni->glob_start == UNI_CALLSTATE_REST0) ||
|
|
(u->u.status.callstate.state == UNI_CALLSTATE_REST0 &&
|
|
uni->glob_start == UNI_CALLSTATE_REST1) ||
|
|
(u->u.status.callstate.state == UNI_CALLSTATE_REST2 &&
|
|
uni->glob_start == UNI_CALLSTATE_REST1)) {
|
|
/*
|
|
* Implementation dependend procedure:
|
|
* Inform the API
|
|
*/
|
|
struct uniapi_reset_status_indication *resp;
|
|
struct uni_msg *app;
|
|
|
|
resp = ALLOC_API(struct uniapi_reset_status_indication, app);
|
|
if (resp == NULL)
|
|
return;
|
|
resp->cref = u->u.hdr.cref;
|
|
resp->callstate = u->u.status.callstate;
|
|
if (IE_ISGOOD(u->u.status.cause))
|
|
resp->cause = u->u.status.cause;
|
|
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_RESET_STATUS_indication, 0, app);
|
|
|
|
} else {
|
|
struct uniapi_reset_error_indication *resp;
|
|
struct uni_msg *app;
|
|
|
|
resp = ALLOC_API(struct uniapi_reset_error_indication, app);
|
|
if (resp != NULL) {
|
|
resp->source = 0;
|
|
resp->reason = UNIAPI_RESET_ERROR_PEER_INCOMP_STATE,
|
|
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_RESET_ERROR_indication, 0, app);
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************/
|
|
/*
|
|
* Reset-Respond process.
|
|
*/
|
|
void
|
|
uni_sig_respond(struct uni *uni, u_int sig, uint32_t cookie,
|
|
struct uni_msg *m, struct uni_all *u)
|
|
{
|
|
if (sig >= SIGR_END) {
|
|
VERBOSE(uni, UNI_FAC_ERR, 1, "Signal %d outside of range to "
|
|
"Reset-Respond", sig);
|
|
if (m)
|
|
uni_msg_destroy(m);
|
|
if (u)
|
|
UNI_FREE(u);
|
|
return;
|
|
}
|
|
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1,
|
|
"Signal %s in state %u of Reset-Respond; cookie %u",
|
|
respond_sigs[sig], uni->glob_respond, cookie);
|
|
|
|
switch (sig) {
|
|
|
|
/*
|
|
* SAAL
|
|
*/
|
|
case SIGR_RESTART:
|
|
response_restart(uni, m, u);
|
|
uni_msg_destroy(m);
|
|
UNI_FREE(u);
|
|
break;
|
|
|
|
case SIGR_STATUS:
|
|
response_status(uni, m, u);
|
|
uni_msg_destroy(m);
|
|
UNI_FREE(u);
|
|
break;
|
|
|
|
/*
|
|
* User
|
|
*/
|
|
case SIGR_RESET_ERROR_response:
|
|
response_error(uni,
|
|
uni_msg_rptr(m, struct uniapi_reset_error_response *),
|
|
cookie);
|
|
uni_msg_destroy(m);
|
|
break;
|
|
|
|
case SIGR_RESET_response:
|
|
response_response(uni,
|
|
uni_msg_rptr(m, struct uniapi_reset_response *), cookie);
|
|
uni_msg_destroy(m);
|
|
break;
|
|
|
|
/*
|
|
* Timers
|
|
*/
|
|
case SIGR_T317:
|
|
response_t317(uni);
|
|
return;
|
|
|
|
case SIGR_END:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send a RELEASE_COMPLETE to all affected calls as per
|
|
* F.2.3(3)
|
|
*/
|
|
static int
|
|
restart_forward(struct uni *uni, const struct uni_all *u)
|
|
{
|
|
struct call *c;
|
|
struct uni_all *resp;
|
|
|
|
if ((resp = UNI_ALLOC()) == NULL)
|
|
return (-1);
|
|
|
|
TAILQ_FOREACH(c, &uni->calls, link) {
|
|
if (u->u.restart.restart.rclass == UNI_RESTART_ALL ||
|
|
(IE_ISPRESENT(c->connid) &&
|
|
u->u.restart.connid.vpci == c->connid.vpci &&
|
|
(u->u.restart.restart.rclass == UNI_RESTART_PATH ||
|
|
u->u.restart.connid.vci == c->connid.vci))) {
|
|
MK_MSG_ORIG(resp, UNI_RELEASE_COMPL, c->cref, c->mine);
|
|
uni_release_compl(c, resp);
|
|
}
|
|
}
|
|
|
|
UNI_FREE(resp);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Respond process got a restart message.
|
|
* Doesn't free the messages.
|
|
*/
|
|
static void
|
|
response_restart(struct uni *uni, struct uni_msg *m, struct uni_all *u)
|
|
{
|
|
struct uni_msg *app;
|
|
struct uniapi_reset_indication *ind;
|
|
|
|
if (uni->glob_respond == UNI_CALLSTATE_REST0) {
|
|
/*
|
|
* If body decoding fails, this is because IEs are wrong.
|
|
*/
|
|
(void)uni_decode_body(m, u, &uni->cx);
|
|
MANDATE_IE(uni, u->u.restart.restart, UNI_IE_RESTART);
|
|
if (IE_ISGOOD(u->u.restart.restart)) {
|
|
/*
|
|
* Q.2931: 5.5.2.2
|
|
*/
|
|
if (u->u.restart.restart.rclass == UNI_RESTART_ALL &&
|
|
IE_ISGOOD(u->u.restart.connid)) {
|
|
UNI_SAVE_IERR(&uni->cx, UNI_IE_CONNID,
|
|
u->u.restart.connid.h.act,
|
|
UNI_IERR_UNK);
|
|
} else if ((u->u.restart.restart.rclass == UNI_RESTART_PATH ||
|
|
u->u.restart.restart.rclass == UNI_RESTART_CHANNEL)) {
|
|
MANDATE_IE(uni, u->u.restart.connid, UNI_IE_CONNID);
|
|
}
|
|
}
|
|
switch (uni_verify(uni, u->u.hdr.act)) {
|
|
case VFY_RAIM:
|
|
case VFY_RAI:
|
|
uni_respond_status_verify(uni, &u->u.hdr.cref,
|
|
UNI_CALLSTATE_REST0, NULL, 0);
|
|
case VFY_CLR:
|
|
case VFY_I:
|
|
return;
|
|
|
|
case VFY_RAP:
|
|
case VFY_RAPU:
|
|
uni_respond_status_verify(uni, &u->u.hdr.cref,
|
|
UNI_CALLSTATE_REST2, NULL, 0);
|
|
case VFY_OK:
|
|
break;
|
|
}
|
|
if (!IE_ISGOOD(u->u.restart.connid))
|
|
u->u.restart.connid.h.present = 0;
|
|
|
|
/*
|
|
* Send a RELEASE_COMPLETE to all affected calls as per
|
|
* F.2.3(3)
|
|
*/
|
|
if (restart_forward(uni, u))
|
|
return;
|
|
|
|
/*
|
|
* Build restart signal for application
|
|
*/
|
|
if ((ind = ALLOC_API(struct uniapi_reset_indication, app)) == NULL)
|
|
return;
|
|
|
|
ind->restart = u->u.restart.restart;
|
|
ind->connid = u->u.restart.connid;
|
|
|
|
uni_enq_coord(uni, SIGO_RESET_indication, 0, app);
|
|
|
|
TIMER_START_UNI(uni, t317, uni->timer317);
|
|
uni->glob_respond = UNI_CALLSTATE_REST2;
|
|
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Respond state := 2");
|
|
|
|
|
|
} else if (uni->glob_respond == UNI_CALLSTATE_REST2) {
|
|
/*
|
|
* No need to decode the message. It is unexpected in this
|
|
* state so return a status.
|
|
*/
|
|
uni_respond_status_mtype(uni, &u->u.hdr.cref, uni->glob_respond,
|
|
UNI_CAUSE_MSG_INCOMP, UNI_RESTART);
|
|
|
|
|
|
} else
|
|
ASSERT(0, ("bad global call state in responder"));
|
|
}
|
|
|
|
static void
|
|
response_t317(struct uni *uni)
|
|
{
|
|
struct uniapi_reset_error_indication *resp;
|
|
struct uni_msg *app;
|
|
|
|
if (uni->glob_respond != UNI_CALLSTATE_REST2) {
|
|
VERBOSE0(uni, UNI_FAC_ERR, "T317 in state %d",
|
|
uni->glob_respond);
|
|
return;
|
|
}
|
|
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Respond error");
|
|
|
|
if ((resp = ALLOC_API(struct uniapi_reset_error_indication, app)) != NULL) {
|
|
resp->source = 1;
|
|
resp->reason = UNIAPI_RESET_ERROR_NO_CONFIRM;
|
|
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_RESET_ERROR_indication, 0, app);
|
|
}
|
|
|
|
uni->glob_respond = UNI_CALLSTATE_REST0;
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Respond state := 0");
|
|
}
|
|
|
|
/*
|
|
* Error response from USER
|
|
*/
|
|
static void
|
|
response_error(struct uni *uni, struct uniapi_reset_error_response *c,
|
|
uint32_t cookie)
|
|
{
|
|
struct uni_all *resp;
|
|
|
|
if (uni->glob_respond != UNI_CALLSTATE_REST2) {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALLSTATE, cookie, 0);
|
|
return;
|
|
}
|
|
|
|
if ((resp = UNI_ALLOC()) == NULL) {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_NOMEM, cookie, 0);
|
|
return;
|
|
}
|
|
|
|
MK_MSG_ORIG(resp, UNI_STATUS, 0, 1);
|
|
MK_IE_CALLSTATE(resp->u.status.callstate, UNI_CALLSTATE_REST2);
|
|
|
|
if (IE_ISGOOD(c->cause))
|
|
resp->u.status.cause = c->cause;
|
|
else {
|
|
MK_IE_CAUSE(resp->u.status.cause, UNI_CAUSE_LOC_USER,
|
|
UNI_CAUSE_CHANNEL_NEX);
|
|
if (IE_ISGOOD(uni->connid_respond))
|
|
ADD_CAUSE_CHANNID(resp->u.status.cause,
|
|
uni->connid_respond.vpci,
|
|
uni->connid_respond.vci);
|
|
}
|
|
|
|
if (uni_send_output(resp, uni) != 0) {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_ENCODING, cookie, 0);
|
|
UNI_FREE(resp);
|
|
return;
|
|
}
|
|
|
|
uniapi_uni_error(uni, UNIAPI_OK, cookie, 0);
|
|
}
|
|
|
|
/*
|
|
* Reset-response from user.
|
|
*/
|
|
static void
|
|
response_response(struct uni *uni, struct uniapi_reset_response *arg,
|
|
uint32_t cookie)
|
|
{
|
|
struct uni_all *resp;
|
|
|
|
if (uni->glob_respond != UNI_CALLSTATE_REST2) {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_BAD_CALLSTATE, cookie, 0);
|
|
return;
|
|
}
|
|
|
|
if (!IE_ISGOOD(arg->restart)) {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_MISSING_IE, cookie, 0);
|
|
return;
|
|
}
|
|
|
|
if ((resp = UNI_ALLOC()) == NULL) {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_NOMEM, cookie, 0);
|
|
return;
|
|
}
|
|
|
|
TIMER_STOP_UNI(uni, t317);
|
|
|
|
MK_MSG_ORIG(resp, UNI_RESTART_ACK, 0, 1);
|
|
resp->u.restart.restart = arg->restart;
|
|
if (IE_ISGOOD(arg->connid))
|
|
resp->u.restart.connid = arg->connid;
|
|
|
|
if (uni_send_output(resp, uni) != 0) {
|
|
uniapi_uni_error(uni, UNIAPI_ERROR_ENCODING, cookie, 0);
|
|
UNI_FREE(resp);
|
|
return;
|
|
}
|
|
|
|
UNI_FREE(resp);
|
|
|
|
uni->glob_respond = UNI_CALLSTATE_REST0;
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1, "Reset-Respond state := 0");
|
|
|
|
uniapi_uni_error(uni, UNIAPI_OK, cookie, 0);
|
|
}
|
|
|
|
/*
|
|
* Reset-Response got a STATUS message.
|
|
*
|
|
* Q.2931: Reset-Response 2/2
|
|
*
|
|
* In Q.2931 only CALLSTATE_REST2 is allowed, this seems silly and to contradict
|
|
* 5.6.12. So allow it in any state.
|
|
*
|
|
* The following states are considered compatible:
|
|
*
|
|
* Sender Receiver
|
|
* ------ --------
|
|
* Rest0 Rest0 this is the normal state OK!
|
|
* Rest0 Rest2 this may be the result of no answer from the API
|
|
* and the Sender finally timing out. ERROR!
|
|
* Rest1 Rest2 this is normal. OK!
|
|
* Rest1 Rest0 RESTART_ACK was probably lost. OK!
|
|
*
|
|
* All others are wrong.
|
|
*/
|
|
static void
|
|
response_status(struct uni *uni, struct uni_msg *m, struct uni_all *u)
|
|
{
|
|
(void)uni_decode_body(m, u, &uni->cx);
|
|
MANDATE_IE(uni, u->u.status.callstate, UNI_IE_CALLSTATE);
|
|
MANDATE_IE(uni, u->u.status.cause, UNI_IE_CAUSE);
|
|
switch (uni_verify(uni, u->u.hdr.act)) {
|
|
case VFY_CLR:
|
|
if (uni->proto == UNIPROTO_UNI40U) {
|
|
uni->glob_respond = UNI_CALLSTATE_REST0;
|
|
VERBOSE(uni, UNI_FAC_RESTART, 1,
|
|
"Reset-Respond state := 0");
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case VFY_RAIM:
|
|
case VFY_RAI:
|
|
case VFY_RAP:
|
|
case VFY_RAPU:
|
|
uni_respond_status_verify(uni, &u->u.hdr.cref,
|
|
uni->glob_respond, NULL, 0);
|
|
case VFY_I:
|
|
case VFY_OK:
|
|
break;
|
|
}
|
|
if (!IE_ISGOOD(u->u.status.callstate)) {
|
|
/*
|
|
* As a result of the strange handling above, we must
|
|
* process a STATUS with an invalid or missing callstate!
|
|
*/
|
|
return;
|
|
}
|
|
if ((u->u.status.callstate.state == UNI_CALLSTATE_REST0 &&
|
|
uni->glob_respond == UNI_CALLSTATE_REST0) ||
|
|
(u->u.status.callstate.state == UNI_CALLSTATE_REST1 &&
|
|
uni->glob_respond == UNI_CALLSTATE_REST0) ||
|
|
(u->u.status.callstate.state == UNI_CALLSTATE_REST1 &&
|
|
uni->glob_respond == UNI_CALLSTATE_REST2)) {
|
|
/*
|
|
* Implementation dependend procedure:
|
|
* Inform the API
|
|
*/
|
|
struct uniapi_reset_status_indication *resp;
|
|
struct uni_msg *app;
|
|
|
|
resp = ALLOC_API(struct uniapi_reset_status_indication, app);
|
|
if (resp == NULL)
|
|
return;
|
|
|
|
resp->cref = u->u.hdr.cref;
|
|
resp->callstate = u->u.status.callstate;
|
|
if (IE_ISGOOD(u->u.status.cause))
|
|
resp->cause = u->u.status.cause;
|
|
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_RESET_STATUS_indication, 0, app);
|
|
|
|
} else {
|
|
struct uniapi_reset_error_indication *resp;
|
|
struct uni_msg *app;
|
|
|
|
resp = ALLOC_API(struct uniapi_reset_error_indication, app);
|
|
if (resp != NULL) {
|
|
resp->source = 1;
|
|
resp->reason = UNIAPI_RESET_ERROR_PEER_INCOMP_STATE,
|
|
|
|
uni->funcs->uni_output(uni, uni->arg,
|
|
UNIAPI_RESET_ERROR_indication, 0, app);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* T317 timeout function
|
|
*/
|
|
static void
|
|
t317_func(struct uni *uni)
|
|
{
|
|
uni_enq_resp(uni, SIGR_T317, 0, NULL, NULL);
|
|
}
|