- tm per transaction retr timers: support for changing the retransmission
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Tue, 5 Jun 2007 14:12:36 +0000 (14:12 +0000)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Tue, 5 Jun 2007 14:12:36 +0000 (14:12 +0000)
  intervals on the fly, from the script, on a per transaction basis
  (it is enabled if tm is compiled with -DTM_DIFF_RT_TIMEOUT -- default):
  t_set_retr(t1, t2)
- tm transaction lifetime for inv/noninv (config. per transaction): added
  maximum transaction lifetime - a transaction is not allowed to be active
  longer then this interval. See t_set_max_lifetime() or the new
  script params. max_inv_lifetime and max_noninv_lifetime.
- fix: retr_interval was not disabled on error in  retr_buf_hanlder

12 files changed:
modules/tm/config.h
modules/tm/doc/functions.xml
modules/tm/h_table.h
modules/tm/t_cancel.c
modules/tm/t_funcs.h
modules/tm/t_lookup.c
modules/tm/t_lookup.h
modules/tm/t_reply.c
modules/tm/timer.c
modules/tm/timer.h
modules/tm/tm.c
modules/tm/uac.c

index b609740..5b4014c 100644 (file)
 
 /* DELETE timer ... tells how long should the transaction persist in memory
    after it was removed from the hash table and before it will be deleted */
-#define DEL_TIME_OUT      200 /* ms */
+#define DEL_TIME_OUT      200 /* ms, obsoleted */
  
 /* retransmission timers */
 #define RETR_T1           500 /* ms */
 #define RETR_T2          4000 /* ms */
 
+/* maximum total lifetime for an INVITE or non-INVITE transaction
+ * Note: after this time the transaction will not be deleted
+ *  immediately, but forced to go in the wait state or in wait for ack state 
+ *  and then wait state, so it will still be alive for either WT_TIME_OUT in 
+ *  the non-inv or "silent" inv. case and for FR_TIME_OUT + WT_TIME_OUT for an
+ *  invite transaction (for which  we must wait for the neg. reply ack)*/
+#define MAX_INV_LIFETIME    180000 /* ms, rfc min. C-timer value */
+#define MAX_NONINV_LIFETIME  32000 /* ms, rfc timer F value */
+
 /* when first reply is sent, this additional space is allocated so that
    one does not have to reallocate share memory when the message is
    replaced by a subsequent, longer message
index 1062d76..c682d54 100644 (file)
@@ -372,7 +372,9 @@ t_forward_nonack("1.2.3.4", "5060");
        </title>
        <para>
                Sets the fr_inv_timeout and optionally fr_timeout for the current
-               transaction. If the transaction is already created (e.g called after
+               transaction or for transactions created during the same script 
+               invocation, after calling this function.
+               If the transaction is already created (e.g called after
                 <function>t_relay()</function> or in an onreply_route) all the
                 branches will have their final response timeout updated on-the-fly.
                If one of the parameters is 0, it's value won't be changed.
@@ -412,6 +414,69 @@ branch_route[1] {
        </example>
        </section>
 
+       <section id="t_set_retr">
+       <title>
+           <function>t_set_retr(retr_t1_interval, retr_t2_interval)</function>
+       </title>
+       <para>
+               Sets the retr_t1_interval and retr_t2_interval for the current
+               transaction or for transactions created during the same script 
+               invocation, after calling this function.
+               If one of the parameters is 0, it's value won't be changed.
+               If the transaction is already created (e.g called after
+                <function>t_relay()</function> or in an onreply_route) all the
+                existing branches will have their retransmissions intervals updated 
+                on-the-fly:
+                if the retransmission interval for the branch has not yet reached T2
+                 the interval will be reset to retr_t1_interval, else to 
+                 retr_t2_interval. Note that the change will happen after the current
+                 interval expires (after the next retransmission, the next-next 
+                 retransmission will take place at retr_t1_interval or 
+                 retr_t2_interval).
+                All new branches of the same transaction will start with the new 
+                values.
+                This function will work even if it's called in the script before
+               a transaction creating function (e.g.: t_set_retr(500, 4000);
+               t_relay()). All new transaction created after this function call, 
+               during the same script invocation will use the new values.
+               Note that this function will work only if tm is compile with
+                -DTM_DIFF_RT_TIMEOUT (which increases every transaction size with
+                4 bytes).
+       </para>
+       <para>Meaning of the parameters is as follows:</para>
+       <itemizedlist>
+           <listitem>
+               <para><emphasis>retr_t1_interval</emphasis> - new T1 retransmission
+                       interval (in milliseconds). See also
+                       <varname>retr_t1_timeout</varname>.
+               </para>
+               <para><emphasis>retr_t2_interval</emphasis> - new T2 (or maximum) 
+                       retransmission interval (in milliseconds). See also
+                       <varname>retr_t2_timeout</varname>.
+               </para>
+           </listitem>
+       </itemizedlist>
+       <example>
+           <title><function>t_set_retr</function> usage</title>
+           <programlisting>
+...
+route { 
+       t_set_retr(250, 0); # set only T1 to 250 ms
+       t_on_branch("1");
+       t_relay(); 
+} 
+
+branch_route[1] {
+       # if we are calling the a remote pstn, extend T1 and decrease T2
+       # for all the branches
+       if (uri=~"sip:[0-9]+"){
+               t_set_retr(500, 2000); 
+       }
+}
+           </programlisting>
+       </example>
+       </section>
+
        <section id="t_branch_timeout">
        <title>
            <function>t_branch_timeout()</function>
index 678199c..4970263 100644 (file)
@@ -39,6 +39,8 @@
  * 2006-08-11  dns failover support (andrei)
  * 2007-05-29  switch ref_count to atomic and delete a cell automatically on
  *             UNREF if the ref_count reaches 0 (andrei)
+ * 2007-06-01  support for different retransmissions intervals per transaction;
+ *             added maximum inv. and non-inv. transaction life time (andrei)
  */
 
 #include "defs.h"
 #include "../../atomic_ops.h"
 #include "config.h"
 
+/* if TM_DIFF_RT_TIMEOUT is defined, different retransmissions timeouts
+ * can be used for each transaction, at a small memory cost
+ * (extra 4 bytes/transaction) */
+#define TM_DIFF_RT_TIMEOUT
+
+
 struct s_table;
 struct entry;
 struct cell;
@@ -211,7 +219,10 @@ struct totag_elem {
 
 #define T_DONT_FORK   (T_CANCELED|T_6xx)
 
-
+/* unsigned short should be enough for a retr. timer: max. 65535 ticks =>
+ * max  retr. = 1023 s for tick = 15 ms, which should be more then enough and
+ * saves us 2*2 bytes */
+typedef unsigned short retr_timeout_t;
 
 /* transaction context */
 
@@ -294,6 +305,11 @@ typedef struct cell
        
        ticks_t fr_timeout;     /* final response interval for retr_bufs */
        ticks_t fr_inv_timeout; /* final inv. response interval for retr_bufs */
+#ifdef TM_DIFF_RT_TIMEOUT
+       retr_timeout_t rt_t1_timeout; /* start retr. interval for retr_bufs */
+       retr_timeout_t rt_t2_timeout; /* maximum retr. interval for retr_bufs */
+#endif
+       ticks_t end_of_life; /* maximum lifetime */
 
        /* nr of replied branch; 0..MAX_BRANCHES=branch value,
         * -1 no reply, -2 local reply */
index 1c98d6f..c18483a 100644 (file)
@@ -201,8 +201,10 @@ int cancel_branch( struct cell *t, int branch, int flags )
 
        DBG("DEBUG: cancel_branch: sending cancel...\n");
 #ifdef TMCB_ONSEND
-       if (SEND_BUFFER( crb )>=0)
-               run_onsend_callbacks(TMCB_REQUEST_SENT, crb, 0, 0, TMCB_LOCAL_F);
+       if (SEND_BUFFER( crb )>=0){
+               if (unlikely (has_tran_tmcbs(t, TMCB_REQUEST_SENT)))
+                       run_onsend_callbacks(TMCB_REQUEST_SENT, crb, 0, 0, TMCB_LOCAL_F);
+       }
 #else
        SEND_BUFFER( crb );
 #endif
index 43d49aa..1686fa3 100644 (file)
@@ -152,19 +152,19 @@ int fr_inv_avp2timer(unsigned int* timer);
 #ifdef TIMER_DEBUG
 #define start_retr(rb) \
        _set_fr_retr((rb), \
-                               ((rb)->dst.proto==PROTO_UDP)?rt_t1_timeout:(ticks_t)(-1), \
+                               ((rb)->dst.proto==PROTO_UDP)?RT_T1_TIMEOUT(rb):(ticks_t)(-1), \
                                __FILE__, __FUNCTION__, __LINE__)
 
 #define force_retr(rb) \
-       _set_fr_retr((rb), rt_t1_timeout, __FILE__, __FUNCTION__, __LINE__)
+       _set_fr_retr((rb), RT_T1_TIMEOUT(rb), __FILE__, __FUNCTION__, __LINE__)
 
 #else
 #define start_retr(rb) \
        _set_fr_retr((rb), \
-                               ((rb)->dst.proto==PROTO_UDP)?rt_t1_timeout:(ticks_t)(-1))
+                               ((rb)->dst.proto==PROTO_UDP)?RT_T1_TIMEOUT(rb):(ticks_t)(-1))
 
 #define force_retr(rb) \
-       _set_fr_retr((rb), rt_t1_timeout)
+       _set_fr_retr((rb), RT_T1_TIMEOUT(rb))
 
 #endif
 
index 230bf40..ffe61d4 100644 (file)
@@ -90,6 +90,8 @@
  *               needed (andrei)
  * 2007-03-17  added callbacks for retransmitted request, ack to negative 
  *              replies and replies to local transactions (andrei)
+ * 2007-06-01  support for different retransmissions intervals per transaction;
+ *             added maximum inv. and non-inv. transaction life time (andrei)
  */
 
 #include "defs.h"
@@ -1068,6 +1070,7 @@ static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
 {
        struct sip_msg *shm_msg;
        unsigned int timeout; /* avp timeout gets stored here (in s) */
+       ticks_t lifetime;
 
        shm_msg=new_cell->uas.request;
        new_cell->from.s=shm_msg->from->name.s;
@@ -1082,23 +1085,35 @@ static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
                -shm_msg->cseq->name.s;
 
        new_cell->method=new_cell->uas.request->first_line.u.request.method;
-       if (p_msg->REQ_METHOD==METHOD_INVITE) new_cell->flags |= T_IS_INVITE_FLAG;
+       if (p_msg->REQ_METHOD==METHOD_INVITE){
+               new_cell->flags |= T_IS_INVITE_FLAG;
+               lifetime=(ticks_t)get_msgid_val(user_inv_max_lifetime,
+                                                                                               p_msg->id, int);
+               if (likely(lifetime==0))
+                       lifetime=tm_max_inv_lifetime;
+       }else{
+               lifetime=(ticks_t)get_msgid_val(user_noninv_max_lifetime, 
+                                                                                       p_msg->id, int);
+               if (likely(lifetime==0))
+                       lifetime=tm_max_noninv_lifetime;
+       }
        new_cell->on_negative=get_on_negative();
        new_cell->on_reply=get_on_reply();
+       new_cell->end_of_life=get_ticks_raw()+lifetime;;
        new_cell->fr_timeout=(ticks_t)get_msgid_val(user_fr_timeout,
                                                                                                p_msg->id, int);
        new_cell->fr_inv_timeout=(ticks_t)get_msgid_val(user_fr_inv_timeout,
                                                                                                p_msg->id, int);
-       if (new_cell->fr_timeout==0){
-               if (!fr_avp2timer(&timeout)) {
+       if (likely(new_cell->fr_timeout==0)){
+               if (unlikely(!fr_avp2timer(&timeout))) {
                        DBG("init_new_t: FR__TIMER = %d s\n", timeout);
                        new_cell->fr_timeout=S_TO_TICKS((ticks_t)timeout);
                }else{
                        new_cell->fr_timeout=fr_timeout;
                }
        }
-       if (new_cell->fr_inv_timeout==0){
-               if (!fr_inv_avp2timer(&timeout)) {
+       if (likely(new_cell->fr_inv_timeout==0)){
+               if (unlikely(!fr_inv_avp2timer(&timeout))) {
                        DBG("init_new_t: FR_INV_TIMER = %d s\n", timeout);
                        new_cell->fr_inv_timeout=S_TO_TICKS((ticks_t)timeout);
                        new_cell->flags |= T_NOISY_CTIMER_FLAG;
@@ -1106,6 +1121,16 @@ static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
                        new_cell->fr_inv_timeout=fr_inv_timeout;
                }
        }
+#ifdef TM_DIFF_RT_TIMEOUT
+       new_cell->rt_t1_timeout=(ticks_t)get_msgid_val(user_rt_t1_timeout,
+                                                                                               p_msg->id, int);
+       if (likely(new_cell->rt_t1_timeout==0))
+               new_cell->rt_t1_timeout=rt_t1_timeout;
+       new_cell->rt_t2_timeout=(ticks_t)get_msgid_val(user_rt_t2_timeout,
+                                                                                               p_msg->id, int);
+       if (likely(new_cell->rt_t2_timeout==0))
+               new_cell->rt_t2_timeout=rt_t2_timeout;
+#endif
        new_cell->on_branch=get_on_branch();
 }
 
@@ -1468,7 +1493,7 @@ int t_set_fr(struct sip_msg* msg, unsigned int fr_inv_to, unsigned int fr_to)
        t=get_t();
        /* in MODE_REPLY and MODE_ONFAILURE T will be set to current transaction;
         * in MODE_REQUEST T will be set only if the transaction was already
-        * created; if not -> use the static variable */
+        * created; if not -> use the static variables */
        if (!t || t==T_UNDEFINED ){
                set_msgid_val(user_fr_inv_timeout, msg->id, int, (int)fr_inv);
                set_msgid_val(user_fr_timeout, msg->id, int, (int)fr);
@@ -1477,3 +1502,90 @@ int t_set_fr(struct sip_msg* msg, unsigned int fr_inv_to, unsigned int fr_to)
        }
        return 1;
 }
+
+
+#ifdef TM_DIFF_RT_TIMEOUT
+
+/* params: retr. t1 & retr. t2 value in ms, 0 means "do not touch"
+ * ret: 1 on success, -1 on error (script safe)*/
+int t_set_retr(struct sip_msg* msg, unsigned int t1_to, unsigned int t2_to)
+{
+       struct cell *t;
+       ticks_t retr_t1, retr_t2;
+       
+       
+       retr_t1=MS_TO_TICKS((ticks_t)t1_to);
+       if (unlikely((retr_t1==0) && (t1_to!=0))){
+               ERR("t_set_retr: retr. t1 interval too small (%u)\n", t1_to);
+               return -1;
+       }
+       if (unlikely(MAX_UVAR_VALUE(t->rt_t1_timeout) < retr_t1)){
+               ERR("t_set_retr: retr. t1 interval too big: %d (max %lu)\n",
+                               t1_to, TICKS_TO_MS(MAX_UVAR_VALUE(t->rt_t1_timeout))); 
+               return -1;
+       } 
+       retr_t2=MS_TO_TICKS((ticks_t)t2_to);
+       if (unlikely((retr_t2==0) && (t2_to!=0))){
+               ERR("t_set_retr: retr. t2 interval too small (%d)\n", t2_to);
+               return -1;
+       }
+       if (unlikely(MAX_UVAR_VALUE(t->rt_t2_timeout) < retr_t2)){
+               ERR("t_set_retr: retr. t2 interval too big: %u (max %lu)\n",
+                               t2_to, TICKS_TO_MS(MAX_UVAR_VALUE(t->rt_t2_timeout))); 
+               return -1;
+       } 
+       
+       t=get_t();
+       /* in MODE_REPLY and MODE_ONFAILURE T will be set to current transaction;
+        * in MODE_REQUEST T will be set only if the transaction was already
+        * created; if not -> use the static variables */
+       if (!t || t==T_UNDEFINED ){
+               set_msgid_val(user_rt_t1_timeout, msg->id, int, (int)retr_t1);
+               set_msgid_val(user_rt_t2_timeout, msg->id, int, (int)retr_t2);
+       }else{
+               change_retr(t, 1, retr_t1, retr_t2); /* change running uac timers */
+       }
+       return 1;
+}
+#endif
+
+
+/* params: maximum transaction lifetime for inv and non-inv
+ *         0 means do not touch"
+ * ret: 1 on success, -1 on error (script safe)*/
+int t_set_max_lifetime(struct sip_msg* msg,
+                                               unsigned int lifetime_inv_to,
+                                               unsigned int lifetime_noninv_to)
+{
+       struct cell *t;
+       ticks_t max_inv_lifetime, max_noninv_lifetime;
+       
+       
+       max_noninv_lifetime=MS_TO_TICKS((ticks_t)lifetime_noninv_to);
+       max_inv_lifetime=MS_TO_TICKS((ticks_t)lifetime_inv_to);
+       if (unlikely((max_noninv_lifetime==0) && (lifetime_noninv_to!=0))){
+               ERR("t_set_max_lifetime: non-inv. interval too small (%d)\n",
+                               lifetime_noninv_to);
+               return -1;
+       }
+       if (unlikely((max_inv_lifetime==0) && (lifetime_inv_to!=0))){
+               ERR("t_set_max_lifetime: inv. interval too small (%d)\n",
+                               lifetime_inv_to);
+               return -1;
+       }
+       
+       t=get_t();
+       /* in MODE_REPLY and MODE_ONFAILURE T will be set to current transaction;
+        * in MODE_REQUEST T will be set only if the transaction was already
+        * created; if not -> use the static variables */
+       if (!t || t==T_UNDEFINED ){
+               set_msgid_val(user_noninv_max_lifetime, msg->id, int,
+                                               (int)max_noninv_lifetime);
+               set_msgid_val(user_inv_max_lifetime, msg->id, int,
+                                               (int)max_inv_lifetime);
+       }else{
+               change_end_of_life(t, 1, is_invite(t)?max_inv_lifetime:
+                                                                                               max_noninv_lifetime);
+       }
+       return 1;
+}
index fd1ed4e..93a9a2c 100644 (file)
@@ -99,6 +99,10 @@ int t_lookup_ident(struct cell** trans, unsigned int hash_index, unsigned int la
 int t_lookup_callid(struct cell** trans, str callid, str cseq);
 
 int t_set_fr(struct sip_msg* msg, unsigned int fr_inv_to, unsigned int fr_to );
-
+#ifdef TM_DIFF_RT_TIMEOUT
+int t_set_retr(struct sip_msg* msg, unsigned int t1_to, unsigned int t2_to);
 #endif
+int t_set_max_lifetime(struct sip_msg* msg, unsigned int eol_inv,
+                                                                                       unsigned int eol_noninv);
 
+#endif
index cdbaa0f..e5c9c7b 100644 (file)
@@ -742,7 +742,7 @@ int run_failure_handlers(struct cell *t, struct sip_msg *rpl,
        }
 
        /* don't start faking anything if we don't have to */
-       if ( !has_tran_tmcbs( t, TMCB_ON_FAILURE) && !t->on_negative ) {
+       if (unlikely(!t->on_negative && !has_tran_tmcbs( t, TMCB_ON_FAILURE))) {
                LOG(L_WARN,
                        "Warning: run_failure_handlers: no negative handler (%d, %d)\n",
                        t->on_negative,
@@ -973,8 +973,8 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
                                                                 allow new branches from the failure route */
 
                /* run ON_FAILURE handlers ( route and callbacks) */
-               if ( has_tran_tmcbs( Trans, TMCB_ON_FAILURE_RO|TMCB_ON_FAILURE)
-               || Trans->on_negative ) {
+               if (unlikely(has_tran_tmcbs( Trans, TMCB_ON_FAILURE_RO|TMCB_ON_FAILURE)
+                                               || Trans->on_negative )) {
                        extra_flags=
                                ((Trans->uac[picked_branch].request.flags & F_RB_TIMEOUT)?
                                                        FL_TIMEOUT:0) | 
@@ -1100,10 +1100,12 @@ int t_retransmit_reply( struct cell *t )
        UNLOCK_REPLIES( t );
        SEND_PR_BUFFER( & t->uas.response, b, len );
 #ifdef TMCB_ONSEND
-       /* we don't know if it's a retransmission of a local reply or a forwarded
-        * reply */
-       run_onsend_callbacks(TMCB_RESPONSE_SENT, &t->uas.response, 0, 0,
-                                                       TMCB_RETR_F);
+       if (unlikely(has_tran_tmcbs(r_buf->my_T, TMCB_RESPONSE_SENT))){ 
+               /* we don't know if it's a retransmission of a local reply or a 
+                * forwarded reply */
+               run_onsend_callbacks(TMCB_RESPONSE_SENT, &t->uas.response, 0, 0,
+                                                               TMCB_RETR_F);
+       }
 #endif
        DBG("DEBUG: reply retransmitted. buf=%p: %.9s..., shmem=%p: %.9s\n",
                b, b, t->uas.response.buffer, t->uas.response.buffer );
@@ -1797,14 +1799,12 @@ int reply_received( struct sip_msg  *p_msg )
                goto done;
 
        /* update FR/RETR timers on provisional replies */
-       if (msg_status<200 && ( restart_fr_on_each_reply ||
+       if (is_invite(t) && msg_status<200 && ( restart_fr_on_each_reply ||
                                ( (last_uac_status<msg_status) &&
                                        ((msg_status>=180) || (last_uac_status==0)) )
                        ) ) { /* provisional now */
-               if (is_invite(t)) {
-                       restart_rb_fr(& uac->request, t->fr_inv_timeout);
-                       uac->request.flags|=F_RB_FR_INV; /* mark fr_inv */
-               }
+               restart_rb_fr(& uac->request, t->fr_inv_timeout);
+               uac->request.flags|=F_RB_FR_INV; /* mark fr_inv */
        } /* provisional replies */
 
 done:
index e3288de..aff85cf 100644 (file)
  *  2007-03-15  TMCB_ONSEND callbacks support (andrei)
  *  2007-05-29  delete on transaction ref_count==0 : removed the delete timer
  *               (andrei)
+ * 2007-06-01  support for different retransmissions intervals per transaction;
+ *             added maximum inv. and non-inv. transaction life time (andrei)
  */
 
 #include "defs.h"
@@ -147,6 +149,12 @@ int noisy_ctimer=0;
 
 struct msgid_var user_fr_timeout;
 struct msgid_var user_fr_inv_timeout;
+#ifdef TM_DIFF_RT_TIMEOUT
+struct msgid_var user_rt_t1_timeout;
+struct msgid_var user_rt_t2_timeout;
+#endif
+struct msgid_var user_inv_max_lifetime;
+struct msgid_var user_noninv_max_lifetime;
 
 /* default values of timeouts for all the timer list */
 
@@ -157,6 +165,33 @@ ticks_t delete_timeout     =       DEL_TIME_OUT;
 ticks_t rt_t1_timeout  =       RETR_T1;
 ticks_t rt_t2_timeout  =       RETR_T2;
 
+/* maximum time and invite or noninv transaction will live, from
+ * the moment of creation (overrides larger fr/fr_inv timeouts,
+ * extensions due to dns failover, fr_inv restart a.s.o)
+ * Note: after this time the transaction will not be deleted
+ *  immediately, but forced to go in the wait state or in wait for ack state 
+ *  and then wait state, so it will still be alive for either wait_timeout in 
+ *  the non-inv or "silent" inv. case and for fr_timeout + wait_timeout for an
+ *  invite transaction (for which  we must wait for the neg. reply ack)
+ */
+ticks_t tm_max_inv_lifetime            =       MAX_INV_LIFETIME;
+ticks_t tm_max_noninv_lifetime =       MAX_NONINV_LIFETIME;
+
+
+/* internal use, val should be unsigned or positive */
+#define SIZE_FIT_CHECK(cell_member, val, cfg_name) \
+       if (MAX_UVAR_VALUE(((struct cell*)0)->cell_member) < (val)){ \
+               ERR("tm_init_timers: " cfg_name " too big: %lu (%lu ticks) " \
+                               "- max %lu (%lu ticks) \n", TICKS_TO_MS((unsigned long)(val)),\
+                               (unsigned long)(val), \
+                               TICKS_TO_MS(MAX_UVAR_VALUE(((struct cell*)0)->cell_member)), \
+                               MAX_UVAR_VALUE(((struct cell*)0)->cell_member)); \
+               goto error; \
+       } \
+       DBG("tm_init_timer:" cfg_name " value: %lu , max value %lu\n", \
+                       (unsigned long)(val), \
+                               MAX_UVAR_VALUE(((struct cell*)0)->cell_member)) 
+
 /* fix timer values to ticks */
 int tm_init_timers()
 {
@@ -166,6 +201,8 @@ int tm_init_timers()
        delete_timeout=MS_TO_TICKS(delete_timeout);
        rt_t1_timeout=MS_TO_TICKS(rt_t1_timeout);
        rt_t2_timeout=MS_TO_TICKS(rt_t2_timeout);
+       tm_max_inv_lifetime=MS_TO_TICKS(tm_max_inv_lifetime);
+       tm_max_noninv_lifetime=MS_TO_TICKS(tm_max_noninv_lifetime);
        /* fix 0 values to 1 tick (minimum possible wait time ) */
        if (fr_timeout==0) fr_timeout=1;
        if (fr_inv_timeout==0) fr_inv_timeout=1;
@@ -173,14 +210,36 @@ int tm_init_timers()
        if (delete_timeout==0) delete_timeout=1;
        if (rt_t2_timeout==0) rt_t2_timeout=1;
        if (rt_t1_timeout==0) rt_t1_timeout=1;
+       if (tm_max_inv_lifetime==0) tm_max_inv_lifetime=1;
+       if (tm_max_noninv_lifetime==0) tm_max_noninv_lifetime=1;
+       
+       /* size fit checks */
+       SIZE_FIT_CHECK(fr_timeout, fr_timeout, "fr_timer");
+       SIZE_FIT_CHECK(fr_inv_timeout, fr_inv_timeout, "fr_inv_timer");
+#ifdef TM_DIFF_RT_TIMEOUT
+       SIZE_FIT_CHECK(rt_t1_timeout, rt_t1_timeout, "retr_timer1");
+       SIZE_FIT_CHECK(rt_t2_timeout, rt_t2_timeout, "retr_timer2");
+#endif
+       SIZE_FIT_CHECK(end_of_life, tm_max_inv_lifetime, "max_inv_lifetime");
+       SIZE_FIT_CHECK(end_of_life, tm_max_noninv_lifetime, "max_noninv_lifetime");
        
        memset(&user_fr_timeout, 0, sizeof(user_fr_timeout));
        memset(&user_fr_inv_timeout, 0, sizeof(user_fr_inv_timeout));
+#ifdef TM_DIFF_RT_TIMEOUT
+       memset(&user_rt_t1_timeout, 0, sizeof(user_rt_t1_timeout));
+       memset(&user_rt_t2_timeout, 0, sizeof(user_rt_t2_timeout));
+#endif
+       memset(&user_inv_max_lifetime, 0, sizeof(user_inv_max_lifetime));
+       memset(&user_noninv_max_lifetime, 0, sizeof(user_noninv_max_lifetime));
        
-       DBG("tm: tm_init_timers: fr=%d fr_inv=%d wait=%d delete=%d t1=%d t2=%d\n",
+       DBG("tm: tm_init_timers: fr=%d fr_inv=%d wait=%d delete=%d t1=%d t2=%d"
+                       " max_inv_lifetime=%d max_noninv_lifetime=%d\n",
                        fr_timeout, fr_inv_timeout, wait_timeout, delete_timeout,
-                       rt_t1_timeout, rt_t2_timeout);
+                       rt_t1_timeout, rt_t2_timeout, tm_max_inv_lifetime,
+                       tm_max_noninv_lifetime);
        return 0;
+error:
+       return -1;
 }
 
 /******************** handlers ***************************/
@@ -319,6 +378,7 @@ inline static void final_response_handler(  struct retr_buf* r_buf,
        */
        int branch_ret;
        int prev_branch;
+       ticks_t now;
 #endif
 
 #      ifdef EXTRA_DEBUG
@@ -397,15 +457,17 @@ inline static void final_response_handler(        struct retr_buf* r_buf,
                /* if this is an invite, the destination resolves to more ips, and
                 *  it still hasn't passed more than fr_inv_timeout since we
                 *  started, add another branch/uac */
-               if (is_invite(t) && use_dns_failover &&
-                               ((get_ticks_raw()-(r_buf->fr_expire-t->fr_timeout)) <
-                                       t->fr_inv_timeout)){
-                       branch_ret=add_uac_dns_fallback(t, t->uas.request,
-                                                                                               &t->uac[r_buf->branch], 0);
-                       prev_branch=-1;
-                       while((branch_ret>=0) &&(branch_ret!=prev_branch)){
-                               prev_branch=branch_ret;
-                               branch_ret=t_send_branch(t, branch_ret, t->uas.request , 0, 0);
+               if (use_dns_failover){
+                       now=get_ticks_raw();
+                       if ((s_ticks_t)(t->end_of_life-now)>0){
+                               branch_ret=add_uac_dns_fallback(t, t->uas.request,
+                                                                                                       &t->uac[r_buf->branch], 0);
+                               prev_branch=-1;
+                               while((branch_ret>=0) &&(branch_ret!=prev_branch)){
+                                       prev_branch=branch_ret;
+                                       branch_ret=t_send_branch(t, branch_ret, t->uas.request , 
+                                                                                               0, 0);
+                               }
                        }
                }
 #endif
@@ -433,6 +495,7 @@ ticks_t retr_buf_handler(ticks_t ticks, struct timer_ln* tl, void *p)
        ticks_t fr_remainder;
        ticks_t retr_remainder;
        ticks_t retr_interval;
+       ticks_t new_retr_interval;
        struct cell *t;
 
        rbuf=(struct  retr_buf*)
@@ -460,7 +523,7 @@ ticks_t retr_buf_handler(ticks_t ticks, struct timer_ln* tl, void *p)
                        if ((s_ticks_t)(rbuf->retr_expire-ticks)<=0){
                                if (rbuf->flags & F_RB_RETR_DISABLED)
                                        goto disabled;
-                               /* retr_interval= min (2*ri, rt_t2) */
+                               /* retr_interval= min (2*ri, rt_t2) , *p==2*ri*/
                                /* no branch version: 
                                        #idef CC_SIGNED_RIGHT_SHIFT
                                                ri=  rt_t2+((2*ri-rt_t2) & 
@@ -472,13 +535,16 @@ ticks_t retr_buf_handler(ticks_t ticks, struct timer_ln* tl, void *p)
                                
                                /* get the  current interval from timer param. */
                                if ((rbuf->flags & F_RB_T2) || 
-                                               (((ticks_t)(unsigned long)p<<1)>rt_t2_timeout))
-                                       retr_interval=rt_t2_timeout;
-                               else
-                                       retr_interval=(ticks_t)(unsigned long)p<<1;
+                                               (((ticks_t)(unsigned long)p)>RT_T2_TIMEOUT(rbuf))){
+                                       retr_interval=RT_T2_TIMEOUT(rbuf);
+                                       new_retr_interval=RT_T2_TIMEOUT(rbuf);
+                               }else{
+                                       retr_interval=(ticks_t)(unsigned long)p;
+                                       new_retr_interval=retr_interval<<1;
+                               }
 #ifdef TIMER_DEBUG
                                DBG("tm: timer: retr: new interval %d (max %d)\n", 
-                                               retr_interval, rt_t2_timeout);
+                                               retr_interval, RT_T2_TIMEOUT(rbuf));
 #endif
                                /* we could race with the reply_received code, but the 
                                 * worst thing that can happen is to delay a reset_to_t2
@@ -486,10 +552,9 @@ ticks_t retr_buf_handler(ticks_t ticks, struct timer_ln* tl, void *p)
                                rbuf->retr_expire=ticks+retr_interval;
                                /* set new interval to -1 on error, or retr_int. on success */
                                retr_remainder=retransmission_handler(rbuf) | retr_interval;
-                               retr_remainder=retr_interval;
-                               /* store the crt. retr. interval inside the timer struct,
+                               /* store the next retr. interval inside the timer struct,
                                 * in the data member */
-                               tl->data=(void*)(unsigned long)retr_interval;
+                               tl->data=(void*)(unsigned long)(new_retr_interval);
                        }else{
                                retr_remainder= rbuf->retr_expire-ticks;
                                DBG("tm: timer: retr: nothing to do, expire in %d\n", 
@@ -507,6 +572,13 @@ ticks_t retr_buf_handler(ticks_t ticks, struct timer_ln* tl, void *p)
 #ifdef TIMER_DEBUG
        DBG("tm: timer retr_buf_handler @%d (%p ->%p->%p) exiting min (%d, %d)\n",
                        ticks, tl, rbuf, t, retr_remainder, fr_remainder);
+#endif
+#ifdef EXTRA_DEBUG
+       if  (retr_remainder==0 || fr_remainder==0){
+               BUG("tm: timer retr_buf_handler: 0 remainder => disabling timer!: "
+                               "retr_remainder=%d, fr_remainder=%d\n", retr_remainder,
+                               fr_remainder);
+       }
 #endif
        if (retr_remainder<fr_remainder)
                return retr_remainder;
index f8a8f43..909dc8a 100644 (file)
@@ -30,6 +30,8 @@
  *  2003-09-12  timer_link.tg exists only if EXTRA_DEBUG (andrei)
  *  2004-02-13  timer_link.payload removed (bogdan)
  *  2005-11-03  rewritten to use the new timers (andrei)
+ *  2007-06-01  support for different retr. intervals per transaction;
+ *              added maximum inv. and non-inv. transaction life time (andrei)
  */
 
 
 #include "../../timer.h"
 #include "h_table.h"
 
+
+
+#ifdef  TM_DIFF_RT_TIMEOUT
+#define RT_T1_TIMEOUT(rb)      ((rb)->my_T->rt_t1_timeout)
+#define RT_T2_TIMEOUT(rb)      ((rb)->my_T->rt_t2_timeout)
+#else
+#define RT_T1_TIMEOUT(rb)      (rt_t1_timeout)
+#define RT_T2_TIMEOUT(rb)      (rt_t2_timeout)
+#endif
+
+#define TM_REQ_TIMEOUT(t) \
+       (is_invite(t)?tm_max_inv_lifetime:tm_max_noninv_lifetime)
+
+
 extern struct msgid_var user_fr_timeout;
 extern struct msgid_var user_fr_inv_timeout;
+#ifdef TM_DIFF_RT_TIMEOUT
+extern struct msgid_var user_rt_t1_timeout;
+extern struct msgid_var user_rt_t2_timeout;
+#endif
+extern struct msgid_var user_inv_max_lifetime;
+extern struct msgid_var user_noninv_max_lifetime;
 
 extern ticks_t fr_timeout;
 extern ticks_t fr_inv_timeout;
@@ -53,6 +75,10 @@ extern ticks_t delete_timeout;
 extern ticks_t rt_t1_timeout;
 extern ticks_t rt_t2_timeout;
 
+extern ticks_t tm_max_inv_lifetime;
+extern ticks_t tm_max_noninv_lifetime;
+
+
 extern int tm_init_timers();
 
 ticks_t wait_handler(ticks_t t, struct timer_ln *tl, void* data);
@@ -63,7 +89,7 @@ ticks_t retr_buf_handler(ticks_t t, struct timer_ln *tl, void* data);
 
 #define init_rb_timers(rb) \
        timer_init(&(rb)->timer, retr_buf_handler, \
-                               (void*)(unsigned long)rt_t1_timeout, 0)
+                               (void*)(unsigned long)RT_T1_TIMEOUT(rb), 0)
 
 /* set fr & retr timer
  * rb  -  pointer to struct retr_buf
@@ -80,11 +106,13 @@ inline static int _set_fr_retr(struct retr_buf* rb, ticks_t retr)
 {
        ticks_t timeout;
        ticks_t ticks;
+       ticks_t eol;
        int ret;
        
        ticks=get_ticks_raw();
        timeout=rb->my_T->fr_timeout;
-       rb->timer.data=(void*)(unsigned long)retr; /* hack */
+       eol=rb->my_T->end_of_life;
+       rb->timer.data=(void*)(unsigned long)(2*retr); /* hack , next retr. int. */
        rb->retr_expire=ticks+retr;
        if (rb->t_active){
                /* we could have set_fr_retr called in the same time (acceptable 
@@ -98,6 +126,12 @@ inline static int _set_fr_retr(struct retr_buf* rb, ticks_t retr)
        }
        /* set active & if retr==-1 set disabled */
        rb->flags|= (F_RB_RETR_DISABLED & -(retr==-1)); 
+       /* adjust timeout to MIN(fr, maximum lifetime) if rb is a request
+        *  (for neg. replies we are force to wait for the ACK so use fr) */
+       if (unlikely ((rb->activ_type==TYPE_REQUEST) && 
+               ((s_ticks_t)(eol-(ticks+timeout))<0)) ){ /* fr after end of life */
+               timeout=(((s_ticks_t)(eol-ticks))>0)?(eol-ticks):1; /* expire now */ 
+       }
        rb->fr_expire=ticks+timeout;
 #ifdef TIMER_DEBUG
        ret=timer_add_safe(&(rb)->timer, (timeout<retr)?timeout:retr,
@@ -127,14 +161,25 @@ do{ \
 /* reset retr. interval to t2 and restart retr. timer */
 #define switch_rb_retr_to_t2(rb) \
        do{ \
-               (rb)->retr_expire=get_ticks_raw()+rt_t2_timeout; \
                (rb)->flags|=F_RB_T2; \
+               (rb)->retr_expire=get_ticks_raw()+RT_T2_TIMEOUT(rb); \
        }while(0)
 
 
-/* restart fr */
-#define restart_rb_fr(rb, new_val) \
-       ((rb)->fr_expire=get_ticks_raw()+(new_val))
+
+inline static void restart_rb_fr(struct retr_buf* rb, ticks_t new_val)
+{
+       ticks_t now;
+       struct cell* t;
+       
+       now=get_ticks_raw();
+       t=rb->my_T;
+       if (unlikely ((rb->activ_type==TYPE_REQUEST) &&
+                                       (((s_ticks_t)(t->end_of_life-(now+new_val)))<0)) )
+               rb->fr_expire=t->end_of_life;
+       else
+               rb->fr_expire=now+new_val;
+}
 
 
 
@@ -144,24 +189,88 @@ do{ \
 inline static void change_fr(struct cell* t, ticks_t fr_inv, ticks_t fr)
 {
        int i;
-       ticks_t fr_inv_expire, fr_expire;
+       ticks_t fr_inv_expire, fr_expire, req_fr_expire;
        
        fr_expire=get_ticks_raw();
        fr_inv_expire=fr_expire+fr_inv;
        fr_expire+=fr;
+       req_fr_expire=((s_ticks_t)(t->end_of_life-fr_expire)<0)?
+                                               t->end_of_life:fr_expire;
        if (fr_inv) t->fr_inv_timeout=fr_inv;
        if (fr) t->fr_timeout=fr;
        for (i=0; i<t->nr_of_outgoings; i++){
                if (t->uac[i].request.t_active){ 
                                if ((t->uac[i].request.flags & F_RB_FR_INV) && fr_inv)
                                        t->uac[i].request.fr_expire=fr_inv_expire;
-                               else if (fr)
-                                       t->uac[i].request.fr_expire=fr_expire;
+                               else if (fr){
+                                       if (t->uac[i].request.activ_type==TYPE_REQUEST)
+                                               t->uac[i].request.fr_expire=req_fr_expire;
+                                       else
+                                               t->uac[i].request.fr_expire=fr_expire;
+                               }
                }
        }
 }
 
 
+#ifdef TM_DIFF_RT_TIMEOUT
+/* change t1 & t2 retransmissions timers
+ * if now==1 try to change them almost on the fly 
+ *  (next retransmission either at rt_t1 or rt_t2)
+ * else only rt_t2 for running branches and both of them for new branches
+ *  if timer value==0 => leave it unchanged
+ */
+inline static void change_retr(struct cell* t, int now,
+                                                               ticks_t rt_t1, ticks_t rt_t2)
+{
+       int i;
+       ticks_t new_rt1_expire, new_rt2_expire, crt;
+
+       if (rt_t1) t->rt_t1_timeout=rt_t1;
+       if (rt_t2) t->rt_t2_timeout=rt_t2;
+       if (now){
+               crt=get_ticks_raw();
+               new_rt1_expire=crt+rt_t1;
+               new_rt2_expire=crt+rt_t2;
+               for (i=0; i<t->nr_of_outgoings; i++){
+                       if (t->uac[i].request.t_active){ 
+                                       if ((t->uac[i].request.flags & F_RB_T2) && rt_t2)
+                                               /* not really needed (?) - if F_RB_T2 is set
+                                                * t->rt_t2_timeout will be used anyway */
+                                               t->uac[i].request.timer.data=
+                                                                       (void*)(unsigned long)rt_t2;
+                                       else if (rt_t1)
+                                               t->uac[i].request.timer.data=
+                                                                       (void*)(unsigned long)rt_t1;
+                       }
+               }
+       }
+}
+#endif /* TM_DIFF_RT_TIMEOUT */
+
+
+
+/* set the maximum transaction lifetime (from the present moment)
+ * if adj is 1, adjust final response timeouts for all the req. branches such
+ * that they are all <= eol (note however that this will work only for
+ *  branches that still retransmit) */
+inline static void change_end_of_life(struct cell* t, int adj, ticks_t eol)
+{
+       int i;
+       
+       t->end_of_life=get_ticks_raw()+eol;
+       if (adj){
+               for (i=0; i<t->nr_of_outgoings; i++){
+                       if (t->uac[i].request.t_active){ 
+                                       if ((t->uac[i].request.activ_type==TYPE_REQUEST) &&
+                                                       ((s_ticks_t)(t->end_of_life - 
+                                                                               t->uac[i].request.fr_expire)<0))
+                                               t->uac[i].request.fr_expire=t->end_of_life;
+                       }
+               }
+       }
+}
+
 inline static void cleanup_localcancel_timers( struct cell *t )
 {
        int i;
index 608749a..91efd5c 100644 (file)
@@ -189,6 +189,8 @@ inline static int w_t_on_reply(struct sip_msg* msg, char *go_to, char *foo );
 inline static int t_check_status(struct sip_msg* msg, char *regexp, char *foo);
 static int t_set_fr_inv(struct sip_msg* msg, char* fr_inv, char* foo);
 static int t_set_fr_all(struct sip_msg* msg, char* fr_inv, char* fr);
+static int w_t_set_retr(struct sip_msg* msg, char* retr_t1, char* retr_t2);
+static int w_t_set_max_lifetime(struct sip_msg* msg, char* inv, char* noninv);
 static int t_branch_timeout(struct sip_msg* msg, char*, char*);
 static int t_branch_replied(struct sip_msg* msg, char*, char*);
 static int t_any_timeout(struct sip_msg* msg, char*, char*);
@@ -282,6 +284,10 @@ static cmd_export_t cmds[]={
                        REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_set_fr",          t_set_fr_all,             2, fixup_var_int_12,
                        REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+       {"t_set_retr",        w_t_set_retr,               2, fixup_var_int_12,
+                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+       {"t_set_max_lifetime", w_t_set_max_lifetime,      2, fixup_var_int_12,
+                       REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_branch_timeout",  t_branch_timeout,         0, 0,  FAILURE_ROUTE},
        {"t_branch_replied",  t_branch_replied,         0, 0,  FAILURE_ROUTE},
        {"t_any_timeout",     t_any_timeout,            0, 0, 
@@ -330,6 +336,8 @@ static param_export_t params[]={
        {"delete_timer",        PARAM_INT, &delete_timeout                       },
        {"retr_timer1",         PARAM_INT, &rt_t1_timeout                        },
        {"retr_timer2"  ,       PARAM_INT, &rt_t2_timeout                        },
+       {"max_inv_lifetime",    PARAM_INT, &tm_max_inv_lifetime                  },
+       {"max_noninv_lifetime", PARAM_INT, &tm_max_noninv_lifetime               },
        {"noisy_ctimer",        PARAM_INT, &noisy_ctimer                         },
        {"uac_from",            PARAM_STRING, &uac_from                          },
        {"unix_tx_timeout",     PARAM_INT, &tm_unix_tx_timeout                   },
@@ -1167,6 +1175,45 @@ static int t_set_fr_inv(struct sip_msg* msg, char* fr_inv, char* foo)
 
 
 
+/* set retr. intervals per transaction; 0 means: use the default value */
+static int w_t_set_retr(struct sip_msg* msg, char* p1, char* p2)
+{
+       int t1, t2;
+       
+       if (get_int_fparam(&t1, msg, (fparam_t*)p1) < 0) return -1;
+       if (p2) {
+               if (get_int_fparam(&t2, msg, (fparam_t*)p2) < 0) return -1;
+       } else {
+               t2 = 0;
+       }
+#ifdef TM_DIFF_RT_TIMEOUT
+       return t_set_retr(msg, t1, t2);
+#else
+       ERR("w_t_set_retr: support for changing retransmission intervals on "
+                       "the fly not compiled in (re-compile tm with"
+                       " -DTM_DIFF_RT_TIMEOUT)\n");
+       return -1;
+#endif
+}
+
+
+
+/* set maximum transaction lifetime for inv & noninv */
+static int w_t_set_max_lifetime(struct sip_msg* msg, char* p1, char* p2)
+{
+       int t1, t2;
+       
+       if (get_int_fparam(&t1, msg, (fparam_t*)p1) < 0) return -1;
+       if (p2) {
+               if (get_int_fparam(&t2, msg, (fparam_t*)p2) < 0) return -1;
+       } else {
+               t2 = 0;
+       }
+       return t_set_max_lifetime(msg, t1, t2);
+}
+
+
+
 /* script function, FAILURE_ROUTE only, returns true if the 
  * choosed "failure" branch failed because of a timeout, 
  * -1 otherwise */
index 9228a7c..06976d5 100644 (file)
@@ -56,6 +56,8 @@
  *  2007-03-15  TMCB_ONSEND callbacks support added (andrei)
  *  2007-03-23  TMCB_LOCAL_REQUEST_IN callbacks support (andrei)
  *  2007-04-23  per dialog callbacks support (andrei)
+ *  2007-06-01  support for per transaction different retransmissions intervals
+ *              (andrei)
  */
 
 #include <string.h>
@@ -187,6 +189,7 @@ static inline int t_uac_prepare(str* method, str* headers, str* body,
         int buf_len, ret, flags;
        unsigned int hi;
        int is_ack;
+       ticks_t lifetime;
 #ifdef USE_DNS_FAILOVER
        struct dns_srv_handle dns_h;
 #endif
@@ -249,6 +252,12 @@ static inline int t_uac_prepare(str* method, str* headers, str* body,
                LOG(L_ERR, "t_uac: short of cell shmem\n");
                goto error2;
        }
+       if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0){
+               new_cell->flags |= T_IS_INVITE_FLAG;
+               lifetime=tm_max_inv_lifetime;
+       }else
+               lifetime=tm_max_noninv_lifetime;
+       new_cell->flags |= T_IS_LOCAL_FLAG;
        /* init timers hack, new_cell->fr_timer and new_cell->fr_inv_timer
         * must be set, or else the fr will happen immediately
         * we can't call init_new_t() because we don't have a sip msg
@@ -256,6 +265,12 @@ static inline int t_uac_prepare(str* method, str* headers, str* body,
         * module params fr_inv_timer and fr_timer -- andrei */
        new_cell->fr_timeout=fr_timeout;
        new_cell->fr_inv_timeout=fr_inv_timeout;
+       new_cell->end_of_life=get_ticks_raw()+lifetime;
+#ifdef TM_DIFF_RT_TIMEOUT
+       /* same as above for retransmission intervals */
+       new_cell->rt_t1_timeout=rt_t1_timeout;
+       new_cell->rt_t2_timeout=rt_t2_timeout;
+#endif
 
        /* better reset avp list now - anyhow, it's useless from
         * this point (bogdan) */
@@ -273,9 +288,6 @@ static inline int t_uac_prepare(str* method, str* headers, str* body,
                goto error2;
        }
 
-       if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0)
-               new_cell->flags |= T_IS_INVITE_FLAG;
-       new_cell->flags |= T_IS_LOCAL_FLAG;
        set_kr(REQ_FWDED);
 
        request = &new_cell->uac[0].request;