Added TEI identity check feature to reclaim dead TEIs.

This is the new feature portion of JIRA LIBPRI-51/SWP-2453.


git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@2105 2fbb986a-6c06-0410-b554-c9c1f0a7f128
This commit is contained in:
Richard Mudgett 2010-11-05 20:05:25 +00:00
parent de0bf0b50e
commit 230e2d9013
4 changed files with 259 additions and 42 deletions

17
pri.c
View File

@ -63,6 +63,7 @@ static const struct pri_timer_table pri_timer[] = {
{ "N202", PRI_TIMER_N202, PRI_ALL_SWITCHES },
{ "K", PRI_TIMER_K, PRI_ALL_SWITCHES },
{ "T200", PRI_TIMER_T200, PRI_ALL_SWITCHES },
{ "T201", PRI_TIMER_T201, PRI_ALL_SWITCHES },
{ "T202", PRI_TIMER_T202, PRI_ALL_SWITCHES },
{ "T203", PRI_TIMER_T203, PRI_ALL_SWITCHES },
{ "T300", PRI_TIMER_T300, PRI_ALL_SWITCHES },
@ -165,21 +166,25 @@ static void pri_default_timers(struct pri *ctrl, int switchtype)
ctrl->timers[PRI_TIMER_N200] = 3; /* Max numer of Q.921 retransmissions */
ctrl->timers[PRI_TIMER_N202] = 3; /* Max numer of transmissions of the TEI identity request message */
if (ctrl->bri == 1)
ctrl->timers[PRI_TIMER_K] = 1; /* Max number of outstanding I-frames */
else
ctrl->timers[PRI_TIMER_K] = 7; /* Max number of outstanding I-frames */
if (ctrl->bri) {
ctrl->timers[PRI_TIMER_K] = 1; /* Max number of outstanding I-frames */
} else {
ctrl->timers[PRI_TIMER_K] = 7; /* Max number of outstanding I-frames */
}
ctrl->timers[PRI_TIMER_T200] = 1000; /* Time between SABME's */
ctrl->timers[PRI_TIMER_T201] = ctrl->timers[PRI_TIMER_T200];/* Time between TEI Identity Checks (Default same as T200) */
ctrl->timers[PRI_TIMER_T202] = 10 * 1000; /* Min time between transmission of TEI Identity request messages */
ctrl->timers[PRI_TIMER_T203] = 10 * 1000; /* Max time without exchanging packets */
ctrl->timers[PRI_TIMER_T303] = 4 * 1000; /* Length between SETUP retransmissions and timeout */
ctrl->timers[PRI_TIMER_T305] = 30 * 1000; /* Wait for DISCONNECT acknowledge */
ctrl->timers[PRI_TIMER_T308] = 4 * 1000; /* Wait for RELEASE acknowledge */
ctrl->timers[PRI_TIMER_T309] = 6 * 1000; /* Time to wait before clearing calls in case of D-channel transient event. Q.931 specifies 6-90 seconds */
ctrl->timers[PRI_TIMER_T313] = 4 * 1000; /* Wait for CONNECT acknowledge, CPE side only */
ctrl->timers[PRI_TIMER_TM20] = 2500; /* Max time awaiting XID response - Q.921 Appendix IV */
ctrl->timers[PRI_TIMER_NM20] = 3; /* Number of XID retransmits - Q.921 Appendix IV */
ctrl->timers[PRI_TIMER_T303] = 4 * 1000; /* Length between SETUP retransmissions and timeout */
ctrl->timers[PRI_TIMER_T309] = 6000; /* Time to wait before clearing calls in case of D-channel transient event. Q.931 specifies 6-90 seconds */
ctrl->timers[PRI_TIMER_T_HOLD] = 4 * 1000; /* Wait for HOLD request response. */
ctrl->timers[PRI_TIMER_T_RETRIEVE] = 4 * 1000;/* Wait for RETRIEVE request response. */

View File

@ -115,6 +115,10 @@ struct pri {
/*! Layer 2 link control for D channel. */
struct q921_link link;
/*! T201 TEI Identity Check timer. */
int t201_timer;
/*! Number of times T201 has expired. */
int t201_expirycnt;
int cref; /* Next call reference value */

View File

@ -57,6 +57,8 @@
#define Q921_TEI_GR303_EOC_OPS 4
#define Q921_TEI_GR303_TMC_SWITCHING 0
#define Q921_TEI_GR303_TMC_CALLPROC 0
#define Q921_TEI_AUTO_FIRST 64
#define Q921_TEI_AUTO_LAST 126
#define Q921_SAPI_CALL_CTRL 0
#define Q921_SAPI_GR303_EOC 1
@ -183,6 +185,18 @@ typedef enum q921_state {
Q921_TIMER_RECOVERY = 8,
} q921_state;
/*! TEI identity check procedure states. */
enum q921_tei_check_state {
/*! Not participating in the TEI check procedure. */
Q921_TEI_CHECK_NONE,
/*! No reply to TEI check received. */
Q921_TEI_CHECK_DEAD,
/*! Reply to TEI check received in current poll. */
Q921_TEI_CHECK_REPLY,
/*! No reply to current TEI check poll received. A previous poll got a reply. */
Q921_TEI_CHECK_DEAD_REPLY,
};
/*! \brief Q.921 link controller structure */
struct q921_link {
/*! Next Q.921 link in the chain. */
@ -205,6 +219,9 @@ struct q921_link {
/*! Q.921 State */
enum q921_state state;
/*! TEI identity check procedure state. */
enum q921_tei_check_state tei_check;
/*! Service Access Profile Identifier (SAPI) of this link */
int sapi;
/*! Terminal Endpoint Identifier (TEI) of this link */

263
q921.c
View File

@ -57,6 +57,7 @@ static void q921_dump_pri(struct q921_link *link, char direction_tag);
static void q921_establish_data_link(struct q921_link *link);
static void q921_mdl_error(struct q921_link *link, char error);
static void q921_mdl_remove(struct q921_link *link);
static void q921_mdl_destroy(struct q921_link *link);
static void q921_restart_ptp_link_if_needed(struct q921_link *link);
/*!
@ -1138,15 +1139,18 @@ void q921_dump(struct pri *ctrl, q921_h *h, int len, int showraw, int txrx)
if ((h->u.ft == 3) && (h->u.m3 == 0) && (h->u.m2 == 0) && (h->u.data[0] == 0x0f)) {
int ri;
int tei;
u_int8_t *action;
ri = (h->u.data[1] << 8) | h->u.data[2];
tei = (h->u.data[4] >> 1);
/* TEI assignment related */
/* TEI management related */
type = q921_tei_mgmt2str(h->u.data[3]);
pri_message(ctrl, "%c MDL Message: %d(%s)\n", direction_tag, h->u.data[3], type);
pri_message(ctrl, "%c RI: %d\n", direction_tag, ri);
pri_message(ctrl, "%c Ai: %d E:%d\n", direction_tag, (h->u.data[4] >> 1) & 0x7f, h->u.data[4] & 1);
ri = (h->u.data[1] << 8) | h->u.data[2];
pri_message(ctrl, "%c Ri: %d\n", direction_tag, ri);
action = &h->u.data[4];
for (x = len - (action - (u_int8_t *) h); 0 < x; --x, ++action) {
pri_message(ctrl, "%c Ai: %d E:%d\n",
direction_tag, (*action >> 1) & 0x7f, *action & 0x01);
}
}
}
@ -1191,12 +1195,103 @@ static void q921_dump_pri_by_h(struct pri *ctrl, char direction_tag, q921_h *h)
}
}
#define Q921_TEI_CHECK_MAX_POLLS 2
static void t201_expire(void *vctrl)
{
struct pri *ctrl;
struct q921_link *link;
struct q921_link *link_next;
ctrl = vctrl;
if (!ctrl->link.next) {
/* No TEI links remain. */
ctrl->t201_timer = 0;
return;
}
/* Start the TEI check timer. */
ctrl->t201_timer =
pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T201], t201_expire, ctrl);
++ctrl->t201_expirycnt;
if (Q921_TEI_CHECK_MAX_POLLS < ctrl->t201_expirycnt) {
pri_schedule_del(ctrl, ctrl->t201_timer);
ctrl->t201_timer = 0;
/* Reclaim any dead TEI links. */
for (link = ctrl->link.next; link; link = link_next) {
link_next = link->next;
switch (link->tei_check) {
case Q921_TEI_CHECK_DEAD:
link->tei_check = Q921_TEI_CHECK_NONE;
q921_tei_remove(ctrl, link->tei);
q921_mdl_destroy(link);
break;
default:
link->tei_check = Q921_TEI_CHECK_NONE;
break;
}
}
return;
}
if (!ctrl->t201_timer) {
pri_error(ctrl, "Could not start T201 timer.");
/* Abort the remaining TEI check. */
for (link = ctrl->link.next; link; link = link->next) {
link->tei_check = Q921_TEI_CHECK_NONE;
}
return;
}
if (ctrl->t201_expirycnt == 1) {
/* First poll. Setup TEI check state. */
for (link = ctrl->link.next; link; link = link->next) {
if (link->state < Q921_TEI_ASSIGNED) {
/* We do not have a TEI. */
link->tei_check = Q921_TEI_CHECK_NONE;
} else {
/* Mark TEI as dead until proved otherwise. */
link->tei_check = Q921_TEI_CHECK_DEAD;
}
}
} else {
/* Subsequent polls. Setup for new TEI check poll. */
for (link = ctrl->link.next; link; link = link->next) {
switch (link->tei_check) {
case Q921_TEI_CHECK_REPLY:
link->tei_check = Q921_TEI_CHECK_DEAD_REPLY;
break;
default:
break;
}
}
}
q921_send_tei(ctrl, Q921_TEI_IDENTITY_CHECK_REQUEST, 0, Q921_TEI_GROUP, 1);
}
static void q921_tei_check(struct pri *ctrl)
{
if (ctrl->t201_timer) {
/* TEI check procedure already in progress. Do not disturb it. */
return;
}
ctrl->t201_expirycnt = 0;
t201_expire(ctrl);
}
static pri_event *q921_receive_MDL(struct pri *ctrl, q921_u *h, int len)
{
int ri;
struct q921_link *sub;
struct q921_link *link;
pri_event *res = NULL;
u_int8_t *action;
int count;
int tei;
if (!BRI_NT_PTMP(ctrl) && !BRI_TE_PTMP(ctrl)) {
@ -1207,12 +1302,22 @@ static pri_event *q921_receive_MDL(struct pri *ctrl, q921_u *h, int len)
if (ctrl->debug & PRI_DEBUG_Q921_STATE) {
pri_message(ctrl, "Received MDL message\n");
}
if (len <= &h->data[0] - (u_int8_t *) h) {
pri_error(ctrl, "Received short frame\n");
return NULL;
}
if (h->data[0] != 0x0f) {
pri_error(ctrl, "Received MDL with unsupported management entity %02x\n", h->data[0]);
return NULL;
}
if (!(h->data[4] & 0x01)) {
pri_error(ctrl, "Received MDL with multibyte TEI identifier\n");
if (len <= &h->data[4] - (u_int8_t *) h) {
pri_error(ctrl, "Received short MDL message\n");
return NULL;
}
if (h->data[3] != Q921_TEI_IDENTITY_CHECK_RESPONSE
&& !(h->data[4] & 0x01)) {
pri_error(ctrl, "Received %d(%s) with Ai E bit not set.\n", h->data[3],
q921_tei_mgmt2str(h->data[3]));
return NULL;
}
ri = (h->data[1] << 8) | h->data[2];
@ -1232,18 +1337,18 @@ static pri_event *q921_receive_MDL(struct pri *ctrl, q921_u *h, int len)
}
/* Find a TEI that is not allocated. */
tei = 64;
tei = Q921_TEI_AUTO_FIRST;
do {
for (sub = &ctrl->link; sub->next; sub = sub->next) {
if (sub->next->tei == tei) {
/* This TEI is already assigned, try next one. */
++tei;
if (tei < Q921_TEI_GROUP) {
if (tei <= Q921_TEI_AUTO_LAST) {
break;
}
/* XXX : TODO later sometime: Implement the TEI check procedure to reclaim some dead TEIs. */
pri_error(ctrl, "Reached maximum TEI quota, cannot assign new TEI\n");
pri_error(ctrl, "TEI pool exhausted. Reclaiming dead TEIs.\n");
q921_send_tei(ctrl, Q921_TEI_IDENTITY_DENIED, ri, Q921_TEI_GROUP, 1);
q921_tei_check(ctrl);
return NULL;
}
}
@ -1259,6 +1364,79 @@ static pri_event *q921_receive_MDL(struct pri *ctrl, q921_u *h, int len)
}
q921_setstate(sub->next, Q921_TEI_ASSIGNED);
q921_send_tei(ctrl, Q921_TEI_IDENTITY_ASSIGNED, ri, tei, 1);
count = 0;
for (sub = ctrl->link.next; sub; sub = sub->next) {
++count;
}
if (Q921_TEI_AUTO_LAST - Q921_TEI_AUTO_FIRST + 1 <= count) {
/*
* We just allocated the last TEI. Try to reclaim dead TEIs
* before another is requested.
*/
if (ctrl->debug & PRI_DEBUG_Q921_STATE) {
pri_message(ctrl, "Allocated last TEI. Reclaiming dead TEIs.\n");
}
q921_tei_check(ctrl);
}
break;
case Q921_TEI_IDENTITY_CHECK_RESPONSE:
if (!BRI_NT_PTMP(ctrl)) {
return NULL;
}
/* For each TEI listed in the message */
action = &h->data[4];
len -= (action - (u_int8_t *) h);
for (; len; --len, ++action) {
if (*action & 0x01) {
/* This is the last TEI in the list because the Ai E bit is set. */
len = 1;
}
tei = (*action >> 1);
if (tei == Q921_TEI_GROUP) {
pri_error(ctrl, "Received %s with invalid TEI %d\n",
q921_tei_mgmt2str(Q921_TEI_IDENTITY_CHECK_RESPONSE), tei);
continue;
}
for (sub = ctrl->link.next; sub; sub = sub->next) {
if (sub->tei == tei) {
/* Found the TEI. */
switch (sub->tei_check) {
case Q921_TEI_CHECK_NONE:
break;
case Q921_TEI_CHECK_DEAD:
case Q921_TEI_CHECK_DEAD_REPLY:
sub->tei_check = Q921_TEI_CHECK_REPLY;
break;
case Q921_TEI_CHECK_REPLY:
/* Duplicate TEI detected. */
sub->tei_check = Q921_TEI_CHECK_NONE;
q921_tei_remove(ctrl, tei);
q921_mdl_destroy(sub);
break;
}
break;
}
}
if (!sub) {
/* TEI not found. */
q921_tei_remove(ctrl, tei);
}
}
break;
case Q921_TEI_IDENTITY_VERIFY:
if (!BRI_NT_PTMP(ctrl)) {
return NULL;
}
if (tei == Q921_TEI_GROUP) {
pri_error(ctrl, "Received %s with invalid TEI %d\n",
q921_tei_mgmt2str(Q921_TEI_IDENTITY_VERIFY), tei);
return NULL;
}
q921_tei_check(ctrl);
break;
case Q921_TEI_IDENTITY_ASSIGNED:
if (!BRI_TE_PTMP(ctrl)) {
@ -1563,6 +1741,42 @@ static void q921_mdl_remove(struct q921_link *link)
link->mdl_free_me = mdl_free_me;
}
static void q921_mdl_link_destroy(struct q921_link *link)
{
struct pri *ctrl;
struct q921_link *freep;
struct q921_link *prev;
ctrl = link->ctrl;
freep = NULL;
for (prev = &ctrl->link; prev->next; prev = prev->next) {
if (prev->next == link) {
prev->next = link->next;
freep = link;
break;
}
}
if (freep == NULL) {
pri_error(ctrl, "Huh!? no match found in list for TEI %d\n", -link->tei);
return;
}
if (ctrl->debug & PRI_DEBUG_Q921_STATE) {
pri_message(ctrl, "Freeing TEI of %d\n", -freep->tei);
}
pri_link_destroy(freep);
}
static void q921_mdl_destroy(struct q921_link *link)
{
q921_mdl_remove(link);
if (link->mdl_free_me) {
q921_mdl_link_destroy(link);
}
}
static int q921_mdl_handle_network_error(struct q921_link *link, char error)
{
int handled = 0;
@ -1705,30 +1919,7 @@ static void q921_mdl_handle_error_callback(void *vlink)
link->mdl_timer = 0;
if (link->mdl_free_me) {
struct pri *ctrl;
struct q921_link *freep;
struct q921_link *prev;
ctrl = link->ctrl;
freep = NULL;
for (prev = &ctrl->link; prev->next; prev = prev->next) {
if (prev->next == link) {
prev->next = link->next;
freep = link;
break;
}
}
if (freep == NULL) {
pri_error(ctrl, "Huh!? no match found in list for TEI %d\n", -link->tei);
return;
}
if (ctrl->debug & PRI_DEBUG_Q921_STATE) {
pri_message(ctrl, "Freeing TEI of %d\n", -freep->tei);
}
pri_link_destroy(freep);
q921_mdl_link_destroy(link);
}
}