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
This commit is contained in:
Richard Mudgett 2009-11-10 19:27:34 +00:00
parent c7c670aa85
commit e23ea9568c
4 changed files with 239 additions and 81 deletions

View File

@ -929,6 +929,14 @@ extern int pri_maintenance_service(struct pri *pri, int span, int channel, int c
/* Create a new call */ /* Create a new call */
q931_call *pri_new_call(struct pri *pri); 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. */ /* Retrieve CRV reference for GR-303 calls. Returns >0 on success. */
int pri_get_crv(struct pri *pri, q931_call *call, int *callmode); int pri_get_crv(struct pri *pri, q931_call *call, int *callmode);

63
pri.c
View File

@ -226,18 +226,44 @@ static int __pri_write(struct pri *pri, void *buf, int buflen)
return res; return res;
} }
/* Pass in the master for this function */
void __pri_free_tei(struct pri * p) 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 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; struct pri *p;
if (!(p = calloc(1, sizeof(*p)))) switch (switchtype) {
return NULL; 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->bri = bri;
p->fd = fd; 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_rxcount = 0;
p->q931_txcount = 0; p->q931_txcount = 0;
#endif #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->protodisc = GR303_PROTOCOL_DISCRIMINATOR;
p->sapi = Q921_SAPI_GR303_EOC; p->sapi = Q921_SAPI_GR303_EOC;
p->tei = Q921_TEI_GR303_EOC_OPS; 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); free(p);
p = NULL; p = NULL;
} }
} else if (switchtype == PRI_SWITCH_GR303_TMC) { break;
case PRI_SWITCH_GR303_TMC:
p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->protodisc = GR303_PROTOCOL_DISCRIMINATOR;
p->sapi = Q921_SAPI_GR303_TMC_CALLPROC; p->sapi = Q921_SAPI_GR303_TMC_CALLPROC;
p->tei = Q921_TEI_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); free(p);
p = NULL; p = NULL;
} }
} else if (switchtype == PRI_SWITCH_GR303_TMC_SWITCHING) { break;
case PRI_SWITCH_GR303_TMC_SWITCHING:
p->protodisc = GR303_PROTOCOL_DISCRIMINATOR; p->protodisc = GR303_PROTOCOL_DISCRIMINATOR;
p->sapi = Q921_SAPI_GR303_TMC_SWITCHING; p->sapi = Q921_SAPI_GR303_TMC_SWITCHING;
p->tei = Q921_TEI_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->protodisc = GR303_PROTOCOL_DISCRIMINATOR;
p->sapi = Q921_SAPI_GR303_EOC; p->sapi = Q921_SAPI_GR303_EOC;
p->tei = Q921_TEI_GR303_EOC_PATH; p->tei = Q921_TEI_GR303_EOC_PATH;
break;
default:
break;
} }
/* Start Q.921 layer, Wait if we're the network */ /* Start Q.921 layer, Wait if we're the network */
if (p) if (p)
@ -935,6 +974,14 @@ q931_call *pri_new_call(struct pri *pri)
return q931_new_call(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) void pri_dump_event(struct pri *pri, pri_event *e)
{ {
if (!pri || !e) if (!pri || !e)

View File

@ -114,6 +114,13 @@ struct pri {
q931_call **callpool; q931_call **callpool;
q931_call *localpool; 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 */ /* do we do overlap dialing */
int overlapdial; int overlapdial;
@ -506,6 +513,14 @@ struct q931_call {
int pri_winner; 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 int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data);
extern pri_event *pri_schedule_run(struct pri *pri); 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 __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_name_init(struct q931_party_name *name);
void q931_party_number_init(struct q931_party_number *number); void q931_party_number_init(struct q931_party_number *number);
void q931_party_subaddress_init(struct q931_party_subaddress *subaddr); 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)); 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 #endif

219
q931.c
View File

@ -3307,30 +3307,38 @@ static char *maintenance_msg2str(int msg, int pd)
return "Unknown Message Type"; return "Unknown Message Type";
} }
/* Decode the call reference */
static inline int q931_cr(q931_h *h) static inline int q931_cr(q931_h *h)
{ {
int cr = 0; int cr;
int x; int x;
if (h->crlen > 3) { if (h->crlen > 3) {
pri_error(NULL, "Call Reference Length Too long: %d\n", h->crlen); pri_error(NULL, "Call Reference Length Too long: %d\n", h->crlen);
return -1; return Q931_DUMMY_CALL_REFERENCE;
} }
switch (h->crlen) { switch (h->crlen) {
case 2: case 2:
for (x=0;x<h->crlen;x++) { cr = 0;
cr <<= 8; for (x = 0; x < h->crlen; ++x) {
cr |= h->crv[x]; cr <<= 8;
} cr |= h->crv[x];
break; }
case 1: break;
cr = h->crv[0]; case 1:
if (cr & 0x80) { cr = h->crv[0];
cr &= ~0x80; if (cr & 0x80) {
cr |= 0x8000; cr &= ~0x80;
} cr |= 0x8000;
break; }
default: break;
pri_error(NULL, "Call Reference Length not supported: %d\n", h->crlen); 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; 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)); 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) static q931_call *q931_getcall(struct pri *ctrl, int cr)
{ {
q931_call *cur; q931_call *cur;
q931_call *prev; q931_call *prev;
struct pri *master; struct pri *master;
if (cr == Q931_DUMMY_CALL_REFERENCE) {
return ctrl->dummy_call;
}
/* Find the master - He has the call pool */ /* Find the master - He has the call pool */
master = PRI_MASTER(ctrl); master = PRI_MASTER(ctrl);
@ -3414,47 +3483,7 @@ static q931_call *q931_getcall(struct pri *ctrl, int cr)
} }
/* Initialize call structure. */ /* Initialize call structure. */
cur->cr = cr; q931_init_call_record(ctrl, cur, 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;
}
/* Append to end of list */ /* Append to end of list */
if (prev) { if (prev) {
@ -3511,6 +3540,10 @@ void q931_destroycall(struct pri *ctrl, q931_call *c)
int slavesleft; int slavesleft;
int slaveidx; int slaveidx;
if (q931_is_dummy_call(c)) {
/* Cannot destroy the dummy call. */
return;
}
if (c->master_call != c) { if (c->master_call != c) {
slave = c; slave = c;
c = slave->master_call; 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); cref = q931_cr(h);
pri_message(ctrl, "%c Call Ref: len=%2d (reference %d/0x%X) (%s)\n", pri_message(ctrl, "%c Call Ref: len=%2d (reference %d/0x%X) (%s)\n",
c, h->crlen, cref & 0x7FFF, cref & 0x7FFF, 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 */ /* Message header begins at the end of the call reference number */
mh = (q931_mh *)(h->contents + h->crlen); 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->pd = ctrl->protodisc;
} }
h->x0 = 0; /* Reserved 0 */ 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. */ /* Two bytes of Call Reference. */
h->crlen = 2; h->crlen = 2;
/* Invert the top bit to make it from our sense */ /* 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) static int q931_xmit(struct pri *ctrl, q931_h *h, int len, int cr, int uiframe)
{ {
/* if (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)
q921_transmit_uiframe(ctrl, h, len); q921_transmit_uiframe(ctrl, h, len);
else } else {
q921_transmit_iframe(ctrl, h, len, cr); q921_transmit_iframe(ctrl, h, len, cr);
}
/* The transmit operation might dump the q921 header, so logging the q931 /* 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 message body after the transmit puts the sections of the message in the
right order in the log */ 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; ctrl = ctrl->subchannel;
} }
if (ctrl) { 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) { if (ctrl->debug & PRI_DEBUG_Q931_STATE) {
pri_message(ctrl, pri_message(ctrl,
"Sending message for call %p on %p TEI/SAPI %d/%d, call->pri is %p, TEI/SAPI %d/%d\n", "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, ctrl, ctrl->tei, ctrl->sapi,
call->pri, call->pri->tei, call->pri->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; call->acked = 1;
return 0; return 0;
@ -5375,6 +5442,9 @@ static int prepare_to_handle_q931_message(struct pri *ctrl, q931_mh *mh, q931_ca
c->ri = -1; c->ri = -1;
break; break;
case Q931_FACILITY: case Q931_FACILITY:
if (q931_is_dummy_call(c)) {
q931_party_address_init(&c->called);
}
break; break;
case Q931_SETUP: case Q931_SETUP:
if (ctrl->debug & PRI_DEBUG_Q931_STATE) 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) int q931_call_setcrv(struct pri *ctrl, q931_call *call, int crv, int callmode)
{ {
call->cr = (crv << 3) & 0x7fff; /* Do not allow changing the dummy call reference */
call->cr |= (callmode & 0x7); if (!q931_is_dummy_call(call)) {
call->cr = (crv << 3) & 0x7fff;
call->cr |= (callmode & 0x7);
}
return 0; return 0;
} }