From 230e2d90135bfa04741f4e564f4a511a87cc0173 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Fri, 5 Nov 2010 20:05:25 +0000 Subject: [PATCH] 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 --- pri.c | 17 ++-- pri_internal.h | 4 + pri_q921.h | 17 ++++ q921.c | 263 ++++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 259 insertions(+), 42 deletions(-) diff --git a/pri.c b/pri.c index 5ff84fd..a0bfe07 100644 --- a/pri.c +++ b/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. */ diff --git a/pri_internal.h b/pri_internal.h index bf0ffb7..ae71ae9 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -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 */ diff --git a/pri_q921.h b/pri_q921.h index 1cc8112..4a37003 100644 --- a/pri_q921.h +++ b/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 */ diff --git a/q921.c b/q921.c index c316277..da0a5de 100644 --- a/q921.c +++ b/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); } }