Skip to content

Commit

Permalink
b2b_logic: do not abort bridging if re-INVITE is rejected with 491
Browse files Browse the repository at this point in the history
If the SDP negociation gets rejected with 491, we should not abort
negociation, since most likely it is an indication that the remote UAS
is trying to lock its codecs, hence do the same thing we are trying to
do, but in the other direction.
If however, the other participant does not send itself a re-INVITE, we
arm a timer to re-try the bridging according to the RFC 3261, section
14.1:
   If a UAC receives a 491 response to a re-INVITE, it SHOULD start a
   timer with a value T chosen as follows:

      1. If the UAC is the owner of the Call-ID of the dialog ID
         (meaning it generated the value), T has a randomly chosen value
         between 2.1 and 4 seconds in units of 10 ms.

Fix #3137

(cherry picked from commit 66493ce)
  • Loading branch information
razvancrainea committed Sep 15, 2023
1 parent 400fb34 commit 999fc72
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 1 deletion.
8 changes: 8 additions & 0 deletions modules/b2b_logic/b2b_logic.c
Original file line number Diff line number Diff line change
Expand Up @@ -654,11 +654,18 @@ static int mod_init(void)
if(init_callid_hdr.s)
init_callid_hdr.len = strlen(init_callid_hdr.s);

if (b2bl_init_bridge_retry() < 0) {
LM_ERR("cannot initiate bridge retry!\n");
return -1;
}

register_timer("b2bl-clean", b2bl_clean, 0, b2b_clean_period,
TIMER_FLAG_DELAY_ON_DELAY);
if(b2bl_db_mode == WRITE_BACK)
register_timer("b2bl-dbupdate", b2bl_db_timer_update, 0,
b2b_update_period, TIMER_FLAG_SKIP_ON_DELAY);
register_timer("b2bl-bridge-retry", b2bl_timer_bridge_retry, 0, 1,
TIMER_FLAG_SKIP_ON_DELAY);

if (b2b_api.register_cb(entity_event_trigger,
B2BCB_TRIGGER_EVENT, &b2bl_mod_name) < 0) {
Expand Down Expand Up @@ -789,6 +796,7 @@ static void mod_destroy(void)
pv_elem_free_all(server_address_pve);

destroy_b2bl_htable();
b2bl_free_bridge_retry();
}

static int child_init(int rank)
Expand Down
2 changes: 1 addition & 1 deletion modules/b2b_logic/b2b_logic.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ enum b2b_tuple_state {
#define B2BL_BR_FLAG_DONT_DELETE_BRIDGE_INITIATOR (1<<4)
#define B2BL_BR_FLAG_PROV_MEDIA (1<<5)
#define B2BL_BR_FLAG_NO_OLD_ENT (1<<6)

#define B2BL_BR_FLAG_PENDING_SDP (1<<7)

/* modes to write in db */
#define NO_DB 0
Expand Down
141 changes: 141 additions & 0 deletions modules/b2b_logic/bridging.c
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,8 @@ int process_bridge_200OK(struct sip_msg* msg, str* extra_headers,
LM_ERR("Failed to send second ACK in bridging scenario\n");
return -1;
}
if (shm_str_sync(&bentity1->in_sdp, body) < 0)
LM_ERR("Failed to save SDP\n");

if (bridging_start_old_ent(tuple, bentity0, bentity1,
NULL, NULL) < 0) {
Expand Down Expand Up @@ -844,6 +846,7 @@ int process_bridge_200OK(struct sip_msg* msg, str* extra_headers,
/* bridging scenario should be done */
tuple->state = B2B_BRIDGED_STATE;
LM_DBG("Finished the bridging\n");
tuple->bridge_flags &= ~B2BL_BR_FLAG_PENDING_SDP;
}
else
{
Expand Down Expand Up @@ -2275,3 +2278,141 @@ int b2bl_bridge_msg(struct sip_msg* msg, str* key, int entity_no, str *adv_ct)
local_ctx_tuple = NULL;
return -1;
}

/* RFC 3261 says we can retry anytime between 2.1s and 4s
* so we are trying as soon as possible, provided that our timer
* ticked and the 2.1s have elapsed */
#define B2BL_BRIDGE_RETRY_TIMEOUT 2100000 /* ms */
#define B2BL_BRIDGE_RETRY_MAX 4000000 /* ms */

struct b2bl_bridge_retry_t {
utime_t time;
unsigned int hash_index, local_index;
struct b2bl_bridge_retry_t *next;
} **b2bl_bridge_retry_head, **b2bl_bridge_retry_last;
gen_lock_t *b2bl_bridge_retry_lock;

int b2bl_init_bridge_retry(void)
{
b2bl_bridge_retry_lock = lock_alloc();
if (!b2bl_bridge_retry_lock) {
LM_ERR("cannot allocate bridge retry lock\n");
return -1;
}
if (!lock_init(b2bl_bridge_retry_lock)) {
LM_ERR("cannot initialize bridge retry lock\n");
return -1;
}
b2bl_bridge_retry_head = shm_malloc(sizeof(struct b2bl_bridge_retry_t *));
if (!b2bl_bridge_retry_head) {
LM_ERR("cannot allocate bridge retry head\n");
return -1;
}
*b2bl_bridge_retry_head = NULL;
b2bl_bridge_retry_last = shm_malloc(sizeof(struct b2bl_bridge_retry_t *));
if (!b2bl_bridge_retry_last) {
LM_ERR("cannot allocate bridge retry last\n");
return -1;
}
*b2bl_bridge_retry_last = NULL;
return 0;
}

void b2bl_free_bridge_retry(void)
{
struct b2bl_bridge_retry_t *it, *next;
for (it = *b2bl_bridge_retry_head; it; it = next) {
next = it->next;
shm_free(it);
}
lock_destroy(b2bl_bridge_retry_lock);
lock_dealloc(b2bl_bridge_retry_lock);
shm_free(b2bl_bridge_retry_head);
shm_free(b2bl_bridge_retry_last);
}

int b2bl_push_bridge_retry(b2bl_tuple_t *tuple)
{
struct b2bl_bridge_retry_t *retry = shm_malloc(sizeof *retry);
if (!retry)
return -1;
memset(retry, 0, sizeof *retry);
retry->hash_index = tuple->hash_index;
retry->local_index = tuple->id;

/* always adding at the end, because we know they will be added in the
* order they appear */
lock_get(b2bl_bridge_retry_lock);
retry->time = get_uticks();
retry->next = *b2bl_bridge_retry_head;
if (*b2bl_bridge_retry_last)
(*b2bl_bridge_retry_last)->next = retry;
else
*b2bl_bridge_retry_head = retry;
*b2bl_bridge_retry_last = retry;
lock_release(b2bl_bridge_retry_lock);
return 0;
}

void b2bl_timer_bridge_retry(unsigned int ticks, void* param)
{
b2bl_tuple_t *tuple;
struct b2bl_bridge_retry_t *it, *last, *next;
/* we only evaluate the list under lock, and detach it */
lock_get(b2bl_bridge_retry_lock);
it = *b2bl_bridge_retry_head;
last = it;
if (it) {
LM_DBG("going through list %p->%p\n", it, *b2bl_bridge_retry_last);
for (last = it; last; last = last->next) {
LM_DBG("detaching %p(%u.%u) after %.2fs\n", it, it->hash_index, it->local_index,
((float)(get_uticks() - it->time))/1000000);
if (get_uticks() - last->time < B2BL_BRIDGE_RETRY_TIMEOUT) {
LM_DBG("stopping %p(%u.%u) after %.2fs\n", it, it->hash_index, it->local_index,
((float)(get_uticks() - it->time))/1000000);
break;
}
}
if (it != last) {
LM_DBG("detaching from %p->%p\n", it, last);
/* detach the list */
*b2bl_bridge_retry_head = last;
if (!last)
*b2bl_bridge_retry_last = NULL;
}
}
lock_release(b2bl_bridge_retry_lock);

while (it != last) {

B2BL_LOCK_GET(it->hash_index);
tuple = b2bl_search_tuple_safe(it->hash_index, it->local_index);
if (tuple) {
if (tuple->bridge_flags & B2BL_BR_FLAG_PENDING_SDP) {
if (get_uticks() - it->time > B2BL_BRIDGE_RETRY_MAX)
LM_WARN("bridge retrying for %.*s after > %ds\n",
tuple->key->len, tuple->key->s,
B2BL_BRIDGE_RETRY_MAX/1000000);
else
LM_DBG("bridge retrying for %.*s after %.2fs\n",
tuple->key->len, tuple->key->s,
((float)(get_uticks() - it->time))/1000000);
tuple->bridge_entities[1]->state = B2BL_ENT_CONFIRMED;
if (bridging_start_old_ent(tuple, tuple->bridge_entities[0],
tuple->bridge_entities[1], NULL, NULL) < 0)
LM_ERR("Failed to start bridging with old entity\n");
else
tuple->state = B2B_BRIDGING_STATE;
} else {
LM_DBG("bridge retrying for %.*s aborted after %.2fs\n",
tuple->key->len, tuple->key->s,
((float)(get_uticks() - it->time))/1000000);
}
}
B2BL_LOCK_RELEASE(it->hash_index);

next = it->next;
shm_free(it);
it = next;
}
}
4 changes: 4 additions & 0 deletions modules/b2b_logic/bridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ int process_bridge_200OK(struct sip_msg* msg, str* extra_headers,
int b2bl_bridge(struct sip_msg* msg, b2bl_tuple_t* tuple,
unsigned hash_index, b2bl_entity_id_t *old_entity,
struct b2bl_new_entity *new_br_ent[2], str *provmedia_uri, int lifetime);
void b2bl_timer_bridge_retry(unsigned int ticks, void* param);
int b2bl_push_bridge_retry(b2bl_tuple_t *tuple);
int b2bl_init_bridge_retry(void);
void b2bl_free_bridge_retry(void);
22 changes: 22 additions & 0 deletions modules/b2b_logic/logic.c
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,7 @@ int _b2b_handle_reply(struct sip_msg *msg, b2bl_tuple_t *tuple,
b2b_req_data_t req_data;
b2b_dlginfo_t dlginfo;
int do_unlock = 0;
static str method_ack = {ACK, ACK_LEN};

if (!tuple) {
B2BL_LOCK_GET(cur_route_ctx.hash_index);
Expand Down Expand Up @@ -917,6 +918,26 @@ int _b2b_handle_reply(struct sip_msg *msg, b2bl_tuple_t *tuple,
/* if a negative reply */
if(statuscode >= 300)
{
if ((tuple->bridge_flags & B2BL_BR_FLAG_RENEW_SDP) && statuscode == 491) {
/* it is very likely that the new entity is trying to send itself a re-INVITE
* to lock down the codecs, therefore we no longer need this step - thus, for now,
* we simply ACK the ongoing bridging entity, and arm a re-negociation attempt
*/
memset(&req_data, 0, sizeof(b2b_req_data_t));
req_data.et = tuple->bridge_entities[0]->type;
req_data.b2b_key = &tuple->bridge_entities[0]->key;
req_data.method = &method_ack;
req_data.body = &tuple->bridge_entities[1]->in_sdp;
req_data.dlginfo = tuple->bridge_entities[0]->dlginfo;
b2b_api.send_request(&req_data);

if (b2bl_push_bridge_retry(tuple) == 0) {
tuple->bridge_flags |= B2BL_BR_FLAG_PENDING_SDP;
tuple->state = B2B_BRIDGED_STATE;
goto done;
}
/* else, fallback to rejecting the call */
}
entity->rejected = 1;
ret = process_bridge_negreply(tuple, tuple->hash_index, entity, msg);

Expand Down Expand Up @@ -1172,6 +1193,7 @@ int _b2b_handle_reply(struct sip_msg *msg, b2bl_tuple_t *tuple,
LM_ERR("Failed to save SDP\n");
goto error;
}
tuple->bridge_flags &= ~B2BL_BR_FLAG_PENDING_SDP;
}

/* if reINVITE and 481 or 408 reply */
Expand Down

0 comments on commit 999fc72

Please sign in to comment.