I have an INVITE timeout problem.
My SIP layout includes:
+---------------+ | EXTERNAL PSTN | | SIP SERVERS | +---------------+ | | +---------------+ | MAIN INCOMING | | SIP SERVER | +---------------+ | | | | +------------+ +-----------------+ | SIP SERVER | | SIP SERVER | | DOMAINA.com| | OtherDom.com | +------------+ +-----------------+ | | | | +----------+ +---------+ | customerA| |otherCust| +----------+ +---------+
I hope the ASCII drawing worked!
Calls come in from the top down. The MAIN INCOMING server uses internal ENUM to deliver the call to the correct domain server. Once delivered to that server, if the customer does not answer his phone before an INVITE timeout occurs the call is forwarded to voicemail.
This works great when the call is from an external PSTN server. It also works great if a user in DOMAINA.com calls another user in DOMAINA.com.
I am experiencing difficulty when a user in DOMAINA.com calls a user in OtherDom.com. The SER at DOMAINA.com recognizes that INVITE is going to a foreign domain, and it t_relays() the INVITE to the MAIN INCOMING SER. It then gets t_relayed() to OtherDom.com where the call is delivered.
The problem is both use the same INVITE timer, so the TIMEOUT on DOMAINA.com occurs *before* the TIMEOUT on OtherDom.com, which means that the call is not delivered to voicemail.
Any ideas?
---greg
I'm thinking about a timer variable. Something like:
in h_table.h, add this:
typedef struct cell { /* linking data */ struct cell* next_cell; struct cell* prev_cell;
....omitted.....
unsigned int inv_timer; }
Then, in tm.c, something like (with the appropriate hooks):
inline static int w_t_inv_timeout( struct sip_msg* msg, char *go_to, char *foo ) { struct cell *t; t = get_t(); if (!t || t==T_UNDEFINED) { LOG(L_CRIT, "BUG: w_t_on_negative entered without t\n"); return -1; } t->inv_timer = (unsigned int)(long)go_to; return 1; }
Then, in timer.c, create a new function set_timer_val() which is exactly like set_timer() except the timeout value is passed to it:
void set_timer_val( struct timer_link *new_tl, enum lists list_id, unsigned int timeout ) { struct timer* list;
if (list_id<FR_TIMER_LIST || list_id>=NR_OF_TIMER_LISTS) { LOG(L_CRIT, "ERROR: set_timer: unkown list: %d\n", list_id); #ifdef EXTRA_DEBUG abort(); #endif return; }
list= &(timertable->timers[ list_id ]);
lock(list->mutex); /* check first if we are on the "detached" timer_routine list, * if so do nothing, the timer is not valid anymore * (sideffect: reset_timer ; set_timer is not safe, a reseted timer * might be lost, depending on this race condition ) */ if (new_tl->timer_list==DETACHED_LIST){ LOG(L_CRIT, "WARNING: set_timer called on a "detached" timer" " -- ignoring: %p\n", new_tl); goto end; } /* make sure I'm not already on a list */ remove_timer_unsafe( new_tl ); add_timer_unsafe( list, new_tl, get_ticks()+timeout); end: unlock(list->mutex); }
Then, in t_reply.c, something like:
int reply_received( struct sip_msg *p_msg ) { ....omitted..... /* update FR/RETR timers on provisional replies */ if (msg_status<200) { /* provisional now */ if (t->is_invite) { /* invite: change FR to longer FR_INV, do not attempt to restart retransmission any more */ set_timer_val( & uac->request.fr_timer, FR_INV_TIMER_LIST, t->inv_timer ); } .....
Then, in your ser.cfg file you would do:
t_inv_timeout(120); t_relay();
this would override the global, ie:
modparam("tm","fr_inv_timer",25)
What do you think? I think I can get it to work. It is in the tm module. Is this desirable? I think it would solve my problem. Also, I think it would allow for some other neat things, like user controllable timeout before voicemail is called.
---greg
Greg Fausak wrote:
I have an INVITE timeout problem.
My SIP layout includes:
+---------------+ | EXTERNAL PSTN | | SIP SERVERS | +---------------+ | | +---------------+ | MAIN INCOMING | | SIP SERVER | +---------------+ | | | | +------------+ +-----------------+ | SIP SERVER | | SIP SERVER | | DOMAINA.com| | OtherDom.com | +------------+ +-----------------+ | | | | +----------+ +---------+ | customerA| |otherCust| +----------+ +---------+
I hope the ASCII drawing worked!
Calls come in from the top down. The MAIN INCOMING server uses internal ENUM to deliver the call to the correct domain server. Once delivered to that server, if the customer does not answer his phone before an INVITE timeout occurs the call is forwarded to voicemail.
This works great when the call is from an external PSTN server. It also works great if a user in DOMAINA.com calls another user in DOMAINA.com.
I am experiencing difficulty when a user in DOMAINA.com calls a user in OtherDom.com. The SER at DOMAINA.com recognizes that INVITE is going to a foreign domain, and it t_relays() the INVITE to the MAIN INCOMING SER. It then gets t_relayed() to OtherDom.com where the call is delivered.
The problem is both use the same INVITE timer, so the TIMEOUT on DOMAINA.com occurs *before* the TIMEOUT on OtherDom.com, which means that the call is not delivered to voicemail.
Any ideas?
---greg
I agree variable Final-reply timer is a good thing for the scenario you mentioned as well as for the scenario of user-defined "number of rings" before voicemail hits.
We actually used to have it, it just turned out to be a performance concern and I removed it. The problem is the variable length may escalate CPU consumption. With fixed timer, we have fixed cost of appending a new element to timer list. With variable timer, we need to insert, i.e., seek proper position first. Needless to say, if CPU consumption escalates with load you would make server more vulnerable to stressful situations such as DoS.
So I think we need to reimplement the timer in a way which is both fast and variable. A way is to maintain two timer lists, one for fixed-length fast timers, another for those who use variable timer no matter what it costs. This would get the variablity without penalty to those who don't need it. Other way would be some kind of hash table.
Also other alternative would be just to give up on performance and do it the way you did.
I need to digest this first.
-jiri
At 05:34 PM 2/8/2004, Greg Fausak wrote:
I'm thinking about a timer variable. Something like:
in h_table.h, add this:
typedef struct cell { /* linking data */ struct cell* next_cell; struct cell* prev_cell;
....omitted..... unsigned int inv_timer;
}
Then, in tm.c, something like (with the appropriate hooks):
inline static int w_t_inv_timeout( struct sip_msg* msg, char *go_to, char *foo ) { struct cell *t; t = get_t(); if (!t || t==T_UNDEFINED) { LOG(L_CRIT, "BUG: w_t_on_negative entered without t\n"); return -1; } t->inv_timer = (unsigned int)(long)go_to; return 1; }
Then, in timer.c, create a new function set_timer_val() which is exactly like set_timer() except the timeout value is passed to it:
void set_timer_val( struct timer_link *new_tl, enum lists list_id, unsigned int timeout ) { struct timer* list;
if (list_id<FR_TIMER_LIST || list_id>=NR_OF_TIMER_LISTS) { LOG(L_CRIT, "ERROR: set_timer: unkown list: %d\n", list_id);
#ifdef EXTRA_DEBUG abort(); #endif return; }
list= &(timertable->timers[ list_id ]); lock(list->mutex); /* check first if we are on the "detached" timer_routine list, * if so do nothing, the timer is not valid anymore * (sideffect: reset_timer ; set_timer is not safe, a reseted timer * might be lost, depending on this race condition ) */ if (new_tl->timer_list==DETACHED_LIST){ LOG(L_CRIT, "WARNING: set_timer called on a \"detached\" timer" " -- ignoring: %p\n", new_tl); goto end; } /* make sure I'm not already on a list */ remove_timer_unsafe( new_tl ); add_timer_unsafe( list, new_tl, get_ticks()+timeout);
end: unlock(list->mutex); }
Then, in t_reply.c, something like:
int reply_received( struct sip_msg *p_msg ) { ....omitted..... /* update FR/RETR timers on provisional replies */ if (msg_status<200) { /* provisional now */ if (t->is_invite) { /* invite: change FR to longer FR_INV, do not attempt to restart retransmission any more */
set_timer_val( & uac->request.fr_timer, FR_INV_TIMER_LIST, t->inv_timer ); }
.....
Then, in your ser.cfg file you would do:
t_inv_timeout(120); t_relay();
this would override the global, ie:
modparam("tm","fr_inv_timer",25)
What do you think? I think I can get it to work. It is in the tm module. Is this desirable? I think it would solve my problem. Also, I think it would allow for some other neat things, like user controllable timeout before voicemail is called.
---greg
Greg Fausak wrote:
I have an INVITE timeout problem. My SIP layout includes:
+---------------+ | EXTERNAL PSTN | | SIP SERVERS | +---------------+ | | +---------------+ | MAIN INCOMING | | SIP SERVER | +---------------+ | | | | +------------+ +-----------------+ | SIP SERVER | | SIP SERVER | | DOMAINA.com| | OtherDom.com | +------------+ +-----------------+ | | | | +----------+ +---------+ | customerA| |otherCust| +----------+ +---------+
I hope the ASCII drawing worked! Calls come in from the top down. The MAIN INCOMING server uses internal ENUM to deliver the call to the correct domain server. Once delivered to that server, if the customer does not answer his phone before an INVITE timeout occurs the call is forwarded to voicemail. This works great when the call is from an external PSTN server. It also works great if a user in DOMAINA.com calls another user in DOMAINA.com. I am experiencing difficulty when a user in DOMAINA.com calls a user in OtherDom.com. The SER at DOMAINA.com recognizes that INVITE is going to a foreign domain, and it t_relays() the INVITE to the MAIN INCOMING SER. It then gets t_relayed() to OtherDom.com where the call is delivered. The problem is both use the same INVITE timer, so the TIMEOUT on DOMAINA.com occurs *before* the TIMEOUT on OtherDom.com, which means that the call is not delivered to voicemail. Any ideas? ---greg
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/
On Feb 08, 2004 at 10:34, Greg Fausak greg@august.net wrote:
I'm thinking about a timer variable. Something like:
[...]
void set_timer_val( struct timer_link *new_tl, enum lists list_id, unsigned int timeout ) { struct timer* list;
if (list_id<FR_TIMER_LIST || list_id>=NR_OF_TIMER_LISTS) { LOG(L_CRIT, "ERROR: set_timer: unkown list: %d\n",
list_id); #ifdef EXTRA_DEBUG abort(); #endif return; }
list= &(timertable->timers[ list_id ]); lock(list->mutex); /* check first if we are on the "detached" timer_routine list, * if so do nothing, the timer is not valid anymore * (sideffect: reset_timer ; set_timer is not safe, a reseted timer * might be lost, depending on this race condition ) */ if (new_tl->timer_list==DETACHED_LIST){ LOG(L_CRIT, "WARNING: set_timer called on a
"detached" timer" " -- ignoring: %p\n", new_tl); goto end; } /* make sure I'm not already on a list */ remove_timer_unsafe( new_tl ); add_timer_unsafe( list, new_tl, get_ticks()+timeout);
The problem is add_timer_unsafe always adds at the end of the corresponding timer list which must be sorted. This is a speed improvement, because we have only fixed timer values per list and if we always add at the end the list will be always sorted (and we don't need to walk through it). If you add a variable timeout (not a fixed one) you won't have this property anymore. The problem with walking the list and comparing timeouts (to keep it sorted even if timeout is variable) is this list can get pretty big. 10000 entries is not unusual and it could get to 600000 entries in the worst case (on 5000cps capable machine under extreme stress conditions). It gets even worse: you have to lock the list when you walk it so you would prevent other ser processes from accessing the list during this time. It would have a pretty big performance impact under stress conditions.
Andrei
Andrei Pelinescu-Onciul wrote:
On Feb 08, 2004 at 10:34, Greg Fausak greg@august.net wrote:
I'm thinking about a timer variable. Something like:
[...]
void set_timer_val( struct timer_link *new_tl, enum lists list_id, unsigned int timeout ) { struct timer* list;
if (list_id<FR_TIMER_LIST || list_id>=NR_OF_TIMER_LISTS) { LOG(L_CRIT, "ERROR: set_timer: unkown list: %d\n",
list_id); #ifdef EXTRA_DEBUG abort(); #endif return; }
list= &(timertable->timers[ list_id ]); lock(list->mutex); /* check first if we are on the "detached" timer_routine list, * if so do nothing, the timer is not valid anymore * (sideffect: reset_timer ; set_timer is not safe, a reseted timer * might be lost, depending on this race condition ) */ if (new_tl->timer_list==DETACHED_LIST){ LOG(L_CRIT, "WARNING: set_timer called on a
"detached" timer" " -- ignoring: %p\n", new_tl); goto end; } /* make sure I'm not already on a list */ remove_timer_unsafe( new_tl ); add_timer_unsafe( list, new_tl, get_ticks()+timeout);
The problem is add_timer_unsafe always adds at the end of the corresponding timer list which must be sorted. This is a speed improvement, because we have only fixed timer values per list and if we always add at the end the list will be always sorted (and we don't need to walk through it). If you add a variable timeout (not a fixed one) you won't have this property anymore. The problem with walking the list and comparing timeouts (to keep it sorted even if timeout is variable) is this list can get pretty big. 10000 entries is not unusual and it could get to 600000 entries in the worst case (on 5000cps capable machine under extreme stress conditions). It gets even worse: you have to lock the list when you walk it so you would prevent other ser processes from accessing the list during this time. It would have a pretty big performance impact under stress conditions.
I've got to figure out how to not timeout an outbound call. Perhaps simply a different queue for outbound INVITES?
---greg
Andrei
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
At 06:12 PM 2/17/2004, Greg Fausak wrote:
I've got to figure out how to not timeout an outbound call. Perhaps simply a different queue for outbound INVITES?
If it is about short-term happyniess, ingoring performance and/or having two queues, fast/fixed and slow/variable would make it as long as you are not exposed to heavy traffic. In terms of long-term robust design, we need to work on it more,
-jiri
Jiri Kuthan wrote:
At 06:12 PM 2/17/2004, Greg Fausak wrote:
I've got to figure out how to not timeout an outbound call. Perhaps simply a different queue for outbound INVITES?
If it is about short-term happyniess, ingoring performance and/or having two queues, fast/fixed and slow/variable would make it as long as you are not exposed to heavy traffic. In terms of long-term robust design, we need to work on it more,
-jiri
This will work.
I can keep my higher volume with no variables. It is just in the local domain based SER instances that I need the variable length queues.
I like happy :-)
---greg
summary: I think you need to special case timers for outbound calls not to interfere with timer logic in the other domain for inbound calls. Way a) modifiable timers (takes a piece of work on code, see a separate email), way b) stateless forwarding for outbound calls without the possibility of accounting.
as for b) the idea is simple, you keep no state for request (use forward as opposed to t_relay), you just forward back and forth like an IP router, and that's it. SER will not need and/or be able to to wipe the state from memory and cancel a transaction prematurely. The limitation is that stateless processing is mutualy exclusive with accounting. If you wish to account calls to other domains, you need to keep requests and replies in memory to correlate them to generate accounting records. If you keep something in memory, you need to set up timer to clean it if it is there for too long and takes too much place.
-jiri
At 03:56 PM 2/8/2004, Greg Fausak wrote:
I have an INVITE timeout problem.
My SIP layout includes:
+---------------+ | EXTERNAL PSTN | | SIP SERVERS | +---------------+ | | +---------------+ | MAIN INCOMING | | SIP SERVER | +---------------+ | | | | +------------+ +-----------------+ | SIP SERVER | | SIP SERVER | | DOMAINA.com| | OtherDom.com | +------------+ +-----------------+ | | | | +----------+ +---------+ | customerA| |otherCust| +----------+ +---------+
I hope the ASCII drawing worked!
Calls come in from the top down. The MAIN INCOMING server uses internal ENUM to deliver the call to the correct domain server. Once delivered to that server, if the customer does not answer his phone before an INVITE timeout occurs the call is forwarded to voicemail.
This works great when the call is from an external PSTN server. It also works great if a user in DOMAINA.com calls another user in DOMAINA.com.
I am experiencing difficulty when a user in DOMAINA.com calls a user in OtherDom.com. The SER at DOMAINA.com recognizes that INVITE is going to a foreign domain, and it t_relays() the INVITE to the MAIN INCOMING SER. It then gets t_relayed() to OtherDom.com where the call is delivered.
The problem is both use the same INVITE timer, so the TIMEOUT on DOMAINA.com occurs *before* the TIMEOUT on OtherDom.com, which means that the call is not delivered to voicemail.
Any ideas?
---greg
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
-- Jiri Kuthan http://iptel.org/~jiri/