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:
parent
de0bf0b50e
commit
230e2d9013
17
pri.c
17
pri.c
@ -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. */
|
||||
|
@ -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 */
|
||||
|
||||
|
17
pri_q921.h
17
pri_q921.h
@ -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
263
q921.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user