From e23ea9568cb206676174d7b1d17dcf08dc503919 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Tue, 10 Nov 2009 19:27:34 +0000 Subject: [PATCH] Add dummy call reference support. Fixes problem where PTMP NT mode responds erroneously to a FACILITY message from a phone on the dummy call reference. LibPRI behaved as if the dummy call reference were an invalid call reference and proceeded to respond on the global call reference. git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1275 2fbb986a-6c06-0410-b554-c9c1f0a7f128 --- libpri.h | 8 ++ pri.c | 63 ++++++++++++-- pri_internal.h | 30 +++++++ q931.c | 219 ++++++++++++++++++++++++++++++++----------------- 4 files changed, 239 insertions(+), 81 deletions(-) diff --git a/libpri.h b/libpri.h index 181d293..7f774aa 100644 --- a/libpri.h +++ b/libpri.h @@ -929,6 +929,14 @@ extern int pri_maintenance_service(struct pri *pri, int span, int channel, int c /* Create a new call */ q931_call *pri_new_call(struct pri *pri); +/*! + * \brief Deterimine if the given call control pointer is a dummy call. + * + * \retval TRUE if given call is a dummy call. + * \retval FALSE otherwise. + */ +int pri_is_dummy_call(q931_call *call); + /* Retrieve CRV reference for GR-303 calls. Returns >0 on success. */ int pri_get_crv(struct pri *pri, q931_call *call, int *callmode); diff --git a/pri.c b/pri.c index 17ee0a8..964da7a 100644 --- a/pri.c +++ b/pri.c @@ -226,18 +226,44 @@ static int __pri_write(struct pri *pri, void *buf, int buflen) return res; } -/* Pass in the master for this function */ void __pri_free_tei(struct pri * p) { - free(p); + if (p) { + struct q931_call *call; + + call = p->dummy_call; + if (call) { + pri_schedule_del(call->pri, call->retranstimer); + pri_call_apdu_queue_cleanup(call); + } + free(p); + } } struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master, pri_io_cb rd, pri_io_cb wr, void *userdata, int tei, int bri) { + struct d_ctrl_dummy *dummy_ctrl; struct pri *p; - if (!(p = calloc(1, sizeof(*p)))) - return NULL; + switch (switchtype) { + case PRI_SWITCH_GR303_EOC: + case PRI_SWITCH_GR303_TMC: + case PRI_SWITCH_GR303_TMC_SWITCHING: + case PRI_SWITCH_GR303_EOC_PATH: + p = calloc(1, sizeof(*p)); + if (!p) { + return NULL; + } + dummy_ctrl = NULL; + break; + default: + dummy_ctrl = calloc(1, sizeof(*dummy_ctrl)); + if (!dummy_ctrl) { + return NULL; + } + p = &dummy_ctrl->ctrl; + break; + } p->bri = bri; p->fd = fd; @@ -265,7 +291,14 @@ struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master, p->q931_rxcount = 0; p->q931_txcount = 0; #endif - if (switchtype == PRI_SWITCH_GR303_EOC) { + if (dummy_ctrl) { + /* Initialize the dummy call reference call record. */ + dummy_ctrl->ctrl.dummy_call = &dummy_ctrl->dummy_call; + q931_init_call_record(&dummy_ctrl->ctrl, dummy_ctrl->ctrl.dummy_call, + Q931_DUMMY_CALL_REFERENCE); + } + switch (switchtype) { + case PRI_SWITCH_GR303_EOC: p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->sapi = Q921_SAPI_GR303_EOC; p->tei = Q921_TEI_GR303_EOC_OPS; @@ -274,7 +307,8 @@ struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master, free(p); p = NULL; } - } else if (switchtype == PRI_SWITCH_GR303_TMC) { + break; + case PRI_SWITCH_GR303_TMC: p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->sapi = Q921_SAPI_GR303_TMC_CALLPROC; p->tei = Q921_TEI_GR303_TMC_CALLPROC; @@ -283,14 +317,19 @@ struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master, free(p); p = NULL; } - } else if (switchtype == PRI_SWITCH_GR303_TMC_SWITCHING) { + break; + case PRI_SWITCH_GR303_TMC_SWITCHING: p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->sapi = Q921_SAPI_GR303_TMC_SWITCHING; p->tei = Q921_TEI_GR303_TMC_SWITCHING; - } else if (switchtype == PRI_SWITCH_GR303_EOC_PATH) { + break; + case PRI_SWITCH_GR303_EOC_PATH: p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->sapi = Q921_SAPI_GR303_EOC; p->tei = Q921_TEI_GR303_EOC_PATH; + break; + default: + break; } /* Start Q.921 layer, Wait if we're the network */ if (p) @@ -935,6 +974,14 @@ q931_call *pri_new_call(struct pri *pri) return q931_new_call(pri); } +int pri_is_dummy_call(q931_call *call) +{ + if (!call) { + return 0; + } + return q931_is_dummy_call(call); +} + void pri_dump_event(struct pri *pri, pri_event *e) { if (!pri || !e) diff --git a/pri_internal.h b/pri_internal.h index bbb6498..e997f1a 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -114,6 +114,13 @@ struct pri { q931_call **callpool; q931_call *localpool; + /*! + * \brief Q.931 Dummy call reference call associated with this TEI. + * \note If present then this call is allocated as part of the + * D channel control structure. + */ + q931_call *dummy_call; + /* do we do overlap dialing */ int overlapdial; @@ -506,6 +513,14 @@ struct q931_call { int pri_winner; }; +/*! D channel control structure with associated dummy call reference record. */ +struct d_ctrl_dummy { + /*! D channel control structure. */ + struct pri ctrl; + /*! Dummy call reference call record. */ + struct q931_call dummy_call; +}; + extern int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data); extern pri_event *pri_schedule_run(struct pri *pri); @@ -523,6 +538,8 @@ struct pri *__pri_new_tei(int fd, int node, int switchtype, struct pri *master, void __pri_free_tei(struct pri *p); +void q931_init_call_record(struct pri *ctrl, struct q931_call *call, int cr); + void q931_party_name_init(struct q931_party_name *name); void q931_party_number_init(struct q931_party_number *number); void q931_party_subaddress_init(struct q931_party_subaddress *subaddr); @@ -589,4 +606,17 @@ static inline int BRI_TE_PTMP(struct pri *mypri) return pri->bri && (((pri)->localtype == PRI_CPE) && ((pri)->tei == Q921_TEI_GROUP)); } +#define Q931_DUMMY_CALL_REFERENCE -1 + +/*! + * \brief Deterimine if the given call control pointer is a dummy call. + * + * \retval TRUE if given call is a dummy call. + * \retval FALSE otherwise. + */ +static inline int q931_is_dummy_call(const q931_call *call) +{ + return (call->cr == Q931_DUMMY_CALL_REFERENCE) ? 1 : 0; +} + #endif diff --git a/q931.c b/q931.c index 11f2c26..53f6133 100644 --- a/q931.c +++ b/q931.c @@ -3307,30 +3307,38 @@ static char *maintenance_msg2str(int msg, int pd) return "Unknown Message Type"; } +/* Decode the call reference */ static inline int q931_cr(q931_h *h) { - int cr = 0; + int cr; int x; + if (h->crlen > 3) { pri_error(NULL, "Call Reference Length Too long: %d\n", h->crlen); - return -1; + return Q931_DUMMY_CALL_REFERENCE; } switch (h->crlen) { - case 2: - for (x=0;xcrlen;x++) { - cr <<= 8; - cr |= h->crv[x]; - } - break; - case 1: - cr = h->crv[0]; - if (cr & 0x80) { - cr &= ~0x80; - cr |= 0x8000; - } - break; - default: - pri_error(NULL, "Call Reference Length not supported: %d\n", h->crlen); + case 2: + cr = 0; + for (x = 0; x < h->crlen; ++x) { + cr <<= 8; + cr |= h->crv[x]; + } + break; + case 1: + cr = h->crv[0]; + if (cr & 0x80) { + cr &= ~0x80; + cr |= 0x8000; + } + break; + case 0: + cr = Q931_DUMMY_CALL_REFERENCE; + break; + default: + pri_error(NULL, "Call Reference Length not supported: %d\n", h->crlen); + cr = Q931_DUMMY_CALL_REFERENCE; + break; } return cr; } @@ -3370,12 +3378,73 @@ static inline void q931_dumpie(struct pri *ctrl, int codeset, q931_ie *ie, char pri_error(ctrl, "!! %c Unknown IE %d (cs%d, len = %d)\n", prefix, Q931_IE_IE(base_ie), Q931_IE_CODESET(base_ie), ielen(ie)); } +/*! + * \brief Initialize the call record. + * + * \param ctrl D channel controller. + * \param call Q.931 call leg. + * + * \note The call record is assumed to already be memset() to zero. + * + * \return Nothing + */ +void q931_init_call_record(struct pri *ctrl, struct q931_call *call, int cr) +{ + call->cr = cr; + call->slotmap = -1; + call->channelno = -1; + if (cr != Q931_DUMMY_CALL_REFERENCE) { + call->newcall = 1; + } + call->ourcallstate = Q931_CALL_STATE_NULL; + call->peercallstate = Q931_CALL_STATE_NULL; + call->sugcallstate = Q931_CALL_STATE_NOT_SET; + call->ri = -1; + call->transcapability = -1; + call->transmoderate = -1; + call->transmultiple = -1; + call->userl1 = -1; + call->userl2 = -1; + call->userl3 = -1; + call->rateadaption = -1; + call->progress = -1; + call->causecode = -1; + call->causeloc = -1; + call->cause = -1; + call->useruserprotocoldisc = -1; + call->aoc_units = -1; + call->changestatus = -1; + call->reversecharge = -1; + call->pri_winner = -1; + call->master_call = call; + q931_party_number_init(&call->redirection_number); + q931_party_address_init(&call->called); + q931_party_id_init(&call->local_id); + q931_party_id_init(&call->remote_id); + q931_party_redirecting_init(&call->redirecting); + + /* PRI is set to whoever called us */ + if (BRI_TE_PTMP(ctrl)) { + /* + * Point to the master to avoid stale pointer problems if + * the TEI is removed later. + */ + call->pri = PRI_MASTER(ctrl); + } else { + call->pri = ctrl; + } +} + static q931_call *q931_getcall(struct pri *ctrl, int cr) { q931_call *cur; q931_call *prev; struct pri *master; + if (cr == Q931_DUMMY_CALL_REFERENCE) { + return ctrl->dummy_call; + } + /* Find the master - He has the call pool */ master = PRI_MASTER(ctrl); @@ -3414,47 +3483,7 @@ static q931_call *q931_getcall(struct pri *ctrl, int cr) } /* Initialize call structure. */ - cur->cr = cr; - cur->slotmap = -1; - cur->channelno = -1; - cur->newcall = 1; - cur->ourcallstate = Q931_CALL_STATE_NULL; - cur->peercallstate = Q931_CALL_STATE_NULL; - cur->sugcallstate = Q931_CALL_STATE_NOT_SET; - cur->ri = -1; - cur->transcapability = -1; - cur->transmoderate = -1; - cur->transmultiple = -1; - cur->userl1 = -1; - cur->userl2 = -1; - cur->userl3 = -1; - cur->rateadaption = -1; - cur->progress = -1; - cur->causecode = -1; - cur->causeloc = -1; - cur->cause = -1; - cur->useruserprotocoldisc = -1; - cur->aoc_units = -1; - cur->changestatus = -1; - cur->reversecharge = -1; - cur->pri_winner = -1; - cur->master_call = cur; - q931_party_number_init(&cur->redirection_number); - q931_party_address_init(&cur->called); - q931_party_id_init(&cur->local_id); - q931_party_id_init(&cur->remote_id); - q931_party_redirecting_init(&cur->redirecting); - - /* PRI is set to whoever called us */ - if (BRI_TE_PTMP(ctrl)) { - /* - * Point to the master to avoid stale pointer problems if - * the TEI is removed later. - */ - cur->pri = master; - } else { - cur->pri = ctrl; - } + q931_init_call_record(ctrl, cur, cr); /* Append to end of list */ if (prev) { @@ -3511,6 +3540,10 @@ void q931_destroycall(struct pri *ctrl, q931_call *c) int slavesleft; int slaveidx; + if (q931_is_dummy_call(c)) { + /* Cannot destroy the dummy call. */ + return; + } if (c->master_call != c) { slave = c; c = slave->master_call; @@ -3711,7 +3744,9 @@ void q931_dump(struct pri *ctrl, q931_h *h, int len, int txrx) cref = q931_cr(h); pri_message(ctrl, "%c Call Ref: len=%2d (reference %d/0x%X) (%s)\n", c, h->crlen, cref & 0x7FFF, cref & 0x7FFF, - (cref & 0x8000) ? "Terminator" : "Originator"); + (cref == Q931_DUMMY_CALL_REFERENCE) + ? "Dummy" + : (cref & 0x8000) ? "Terminator" : "Originator"); /* Message header begins at the end of the call reference number */ mh = (q931_mh *)(h->contents + h->crlen); @@ -3778,7 +3813,9 @@ static void init_header(struct pri *ctrl, q931_call *call, unsigned char *buf, q h->pd = ctrl->protodisc; } h->x0 = 0; /* Reserved 0 */ - if (!ctrl->bri) { + if (q931_is_dummy_call(call)) { + h->crlen = 0; + } else if (!ctrl->bri) { /* Two bytes of Call Reference. */ h->crlen = 2; /* Invert the top bit to make it from our sense */ @@ -3807,18 +3844,11 @@ static void init_header(struct pri *ctrl, q931_call *call, unsigned char *buf, q static int q931_xmit(struct pri *ctrl, q931_h *h, int len, int cr, int uiframe) { - /* - * For NT-PTMP mode, we need to check the following: - * MODE = NT-PTMP - * MESSAGE = SETUP - * - * If those are true, we need to send the SETUP in a UI frame - * instead of an I-frame. - */ - if (BRI_NT_PTMP(ctrl) && uiframe) + if (uiframe) { q921_transmit_uiframe(ctrl, h, len); - else + } else { q921_transmit_iframe(ctrl, h, len, cr); + } /* The transmit operation might dump the q921 header, so logging the q931 message body after the transmit puts the sections of the message in the right order in the log */ @@ -3893,6 +3923,43 @@ static int send_message(struct pri *ctrl, q931_call *call, int msgtype, int ies[ ctrl = ctrl->subchannel; } if (ctrl) { + int uiframe; + + switch (msgtype) { + case Q931_SETUP: + /* + * For NT-PTMP mode, we need to check the following: + * MODE = NT-PTMP + * MESSAGE = SETUP + * + * If those are true, we need to send the SETUP in a UI frame + * instead of an I-frame. + */ + uiframe = call->outboundbroadcast; + break; + case Q931_FACILITY: + if (ctrl->tei == Q921_TEI_GROUP) { + /* Broadcast TEI. */ + if (q931_is_dummy_call(call)) { + /* + * This is a FACILITY message on the dummy call reference + * for the broadcast TEI. + */ + uiframe = 1; + } else { + pri_error(ctrl, + "Attempting to broadcast %s on cref %d\n", + msg2str(msgtype), call->cr); + return -1; + } + } else { + uiframe = 0; + } + break; + default: + uiframe = 0; + break; + } if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Sending message for call %p on %p TEI/SAPI %d/%d, call->pri is %p, TEI/SAPI %d/%d\n", @@ -3900,7 +3967,7 @@ static int send_message(struct pri *ctrl, q931_call *call, int msgtype, int ies[ ctrl, ctrl->tei, ctrl->sapi, call->pri, call->pri->tei, call->pri->sapi); } - q931_xmit(ctrl, h, len, 1, (msgtype == Q931_SETUP) ? 1 : 0); + q931_xmit(ctrl, h, len, 1, uiframe); } call->acked = 1; return 0; @@ -5375,6 +5442,9 @@ static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_ca c->ri = -1; break; case Q931_FACILITY: + if (q931_is_dummy_call(c)) { + q931_party_address_init(&c->called); + } break; case Q931_SETUP: if (ctrl->debug & PRI_DEBUG_Q931_STATE) @@ -7288,7 +7358,10 @@ int q931_call_getcrv(struct pri *ctrl, q931_call *call, int *callmode) int q931_call_setcrv(struct pri *ctrl, q931_call *call, int crv, int callmode) { - call->cr = (crv << 3) & 0x7fff; - call->cr |= (callmode & 0x7); + /* Do not allow changing the dummy call reference */ + if (!q931_is_dummy_call(call)) { + call->cr = (crv << 3) & 0x7fff; + call->cr |= (callmode & 0x7); + } return 0; }