From f5bfad916ca2ebf9ca2f22414993141a91c88afa Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 31 Aug 2010 15:46:04 +0200 Subject: [PATCH] support for RFC 3263 improvement - added timer M with default = B_TIMER/4. - when timer M triggers, next ip is tried. - timer M is used only when there is another address to try. --- core/sip/cstring.h | 4 +- core/sip/resolver.h | 2 + core/sip/sip_timers.h | 56 ++++++++++++++++-- core/sip/sip_trans.cpp | 6 +- core/sip/sip_trans.h | 30 ---------- core/sip/trans_layer.cpp | 119 ++++++++++++++++++++++++++++----------- core/sip/trans_layer.h | 17 ++++-- core/sip/trans_table.cpp | 7 +-- core/sip/trans_table.h | 19 ++----- 9 files changed, 166 insertions(+), 94 deletions(-) diff --git a/core/sip/cstring.h b/core/sip/cstring.h index 87ac1f14..ea9d8f8f 100644 --- a/core/sip/cstring.h +++ b/core/sip/cstring.h @@ -64,9 +64,9 @@ struct cstring }; #define c2stlstr(str) \ - string(str.s,str.len) + string((str).s,(str).len) #define stl2cstr(str) \ - cstring((char*)str.c_str(),str.length()) + cstring((char*)(str).c_str(),(str).length()) #endif diff --git a/core/sip/resolver.h b/core/sip/resolver.h index b91ec418..e60d7d25 100644 --- a/core/sip/resolver.h +++ b/core/sip/resolver.h @@ -106,6 +106,8 @@ struct dns_handle int next_ip(sockaddr_storage* sa); + //void operator = (const dns_handle& h); + private: friend class _resolver; friend class dns_entry; diff --git a/core/sip/sip_timers.h b/core/sip/sip_timers.h index 5f64315e..30ca78a9 100644 --- a/core/sip/sip_timers.h +++ b/core/sip/sip_timers.h @@ -27,6 +27,49 @@ #ifndef _sip_timers_h_ #define _sip_timers_h_ +/** + * SIP transaction timer type definition + */ +enum sip_timer_type { + + STIMER_INVALID=0, + + // INVITE client transaction + STIMER_A, // Calling: (re-)send INV + STIMER_B, // Calling -> Terminated + STIMER_D, // Completed -> Terminated + + // non-INVITE client transaction + STIMER_E, // Trying/Proceeding: (re-)send request + STIMER_F, // Trying/Proceeding -> Terminated + STIMER_K, // Completed -> Terminated + + // INVITE server transaction + STIMER_G, // Completed: (re-)send response + STIMER_H, // Completed -> Terminated + STIMER_I, // Confirmed -> Terminated + + // non-INVITE server transaction + STIMER_J, // Completed -> Terminated + + // These timers are not defined by + // RFC 3261. + + // Used to handle 200 ACKs automatically + // in INVITE client transactions. + STIMER_L, // Terminated_200 -> Terminated + + // Transport address failover timer: + // - used to cycle throught multiple addresses + // in case the R-URI resolves to multiple addresses + STIMER_M +}; + + +/** + * SIP transaction timer default values + */ + #define T1_TIMER 500 /* 500 ms */ #define T2_TIMER 4000 /* 4 s */ #define T4_TIMER 5000 /* 5 s */ @@ -60,14 +103,19 @@ #define J_TIMER 64*T1_TIMER -// This timer is not defined by -// RFC 3261. But it is needed -// to handle 200 ACKs automatically -// in UAC transactions. +// Following timer values are not defined by +// RFC 3261. +// Used to handle 200 ACKs automatically +// in INVITE client transactions. //type 0x0b #define L_TIMER 64*T1_TIMER +// Transport address failover timer: +// - used to cycle throught multiple addresses +// in case the R-URI resolves to multiple addresses +#define M_TIMER (B_TIMER/4) + #endif diff --git a/core/sip/sip_trans.cpp b/core/sip/sip_trans.cpp index bf6a4119..e8522b90 100644 --- a/core/sip/sip_trans.cpp +++ b/core/sip/sip_trans.cpp @@ -41,7 +41,8 @@ int _timer_type_lookup[] = { 0,1,2, // STIMER_E, STIMER_F, STIMER_K 0,1,2, // STIMER_G, STIMER_H, STIMER_I 0, // STIMER_J - 2 // STIMER_L; shares the same slot as STIMER_D + 2, // STIMER_L; shares the same slot as STIMER_D + 2 // STIMER_M; shares the same slot as STIMER_D/STIMER_K }; inline timer** fetch_timer(unsigned int timer_type, timer** base) @@ -98,7 +99,7 @@ timer* sip_trans::get_timer(unsigned int timer_type) } -char _timer_name_lookup[] = {'0','A','B','D','E','F','K','G','H','I','J','L'}; +char _timer_name_lookup[] = {'0','A','B','D','E','F','K','G','H','I','J','L','M'}; #define timer_name(type) \ (_timer_name_lookup[(type) & 0xFFFF]) @@ -137,6 +138,7 @@ void trans_timer_cb(timer* t, unsigned int bucket_id, sip_trans* tr) else { WARN("Transaction %p does not exist anymore\n",tr); WARN("Timer type=%c will be deleted without further processing\n",timer_name(t->type)); + wheeltimer::instance()->remove_timer(t); } bucket->unlock(); } diff --git a/core/sip/sip_trans.h b/core/sip/sip_trans.h index f47ea9e9..1a28c704 100644 --- a/core/sip/sip_trans.h +++ b/core/sip/sip_trans.h @@ -61,36 +61,6 @@ enum { }; -enum sip_timer_type { - - STIMER_INVALID=0, - - // INVITE client transaction - STIMER_A, // Calling: (re-)send INV - STIMER_B, // Calling: -> Terminated - STIMER_D, // Completed: -> Terminated - - // non-INVITE client transaction - STIMER_E, // Trying/Proceeding: (re-)send request - STIMER_F, // Trying/Proceeding: terminate transaction - STIMER_K, // Completed: terminate transaction - - // INVITE server transaction - STIMER_G, // Completed: (re-)send response - STIMER_H, // Completed: -> Terminated - STIMER_I, // Confirmed: -> Terminated - - // non-INVITE server transaction - STIMER_J, // Completed: -> Terminated - - // This timer is not defined by - // RFC 3261. But it is needed - // to handle 200 ACKs automatically - // in INVITE client transactions. - STIMER_L // Terminated_200 -> Terminated -}; - - /** * We support at most 3 timer per transaction, * which is okay according to the standard diff --git a/core/sip/trans_layer.cpp b/core/sip/trans_layer.cpp index 7fc34433..4abaab57 100644 --- a/core/sip/trans_layer.cpp +++ b/core/sip/trans_layer.cpp @@ -325,7 +325,7 @@ int _trans_layer::send_reply(trans_ticket* tt, } assert(transport); - // TODO: inspect topmost 'Via' and select proper addr/port (w/ received, rport) + // TODO: inspect topmost 'Via' and select proper addr (+resolve DNS names) // refs: RFC3261 18.2.2; RFC3581 sockaddr_storage remote_ip; memcpy(&remote_ip,&req->remote_ip,sizeof(sockaddr_storage)); @@ -517,7 +517,7 @@ int _trans_layer::send_sl_reply(sip_msg* req, int reply_code, } assert(transport); - //TODO: select proper address depending on Via (w/ received, rport), remote_ip + int err = transport->send(&req->remote_ip,reply_buf,reply_len); delete [] reply_buf; @@ -527,15 +527,15 @@ int _trans_layer::send_sl_reply(sip_msg* req, int reply_code, // // Ref. RFC 3261 "12.2.1.1 Generating the Request" // -int _trans_layer::set_next_hop(sip_msg* msg) +int _trans_layer::set_next_hop(sip_msg* msg, + cstring* next_hop, + unsigned short* next_port) { assert(msg); list& route_hdrs = msg->route; cstring& r_uri = msg->u.request->ruri_str; - cstring next_hop; - unsigned short next_port = 0; int err=0; if(!route_hdrs.empty()){ @@ -572,9 +572,9 @@ int _trans_layer::set_next_hop(sip_msg* msg) } - if (next_hop.len == 0) { - next_hop = na.uri.host; - next_port = na.uri.port; + if (next_hop->len == 0) { + *next_hop = na.uri.host; + *next_port = na.uri.port; } if(!is_lr){ @@ -675,22 +675,31 @@ int _trans_layer::set_next_hop(sip_msg* msg) ERROR("Invalid Request URI\n"); return -1; } - next_hop = parsed_r_uri.host; - next_port = parsed_r_uri.port; + *next_hop = parsed_r_uri.host; + *next_port = parsed_r_uri.port; } - DBG("next_hop:next_port is <%.*s:%u>\n", next_hop.len, next_hop.s, next_port); + DBG("next_hop:next_port is <%.*s:%u>\n", next_hop->len, next_hop->s, *next_port); - err = resolver::instance()->resolve_name(c2stlstr(next_hop).c_str(), - &(msg->h_dns), - &(msg->remote_ip),IPv4); + return 0; +} + + +int _trans_layer::set_destination_ip(sip_msg* msg, cstring* next_hop, unsigned short next_port) +{ + memset(&(msg->remote_ip),0,sizeof(sockaddr_storage)); + int err = resolver::instance()->resolve_name(c2stlstr(*next_hop).c_str(), + &(msg->h_dns), + &(msg->remote_ip),IPv4); if(err < 0){ ERROR("Unresolvable Request URI\n"); return -1; } - ((sockaddr_in*)&(msg->remote_ip))->sin_port = htons(next_port); - + if(!((sockaddr_in*)&(msg->remote_ip))->sin_port) { + ((sockaddr_in*)&(msg->remote_ip))->sin_port = htons(next_port); + } + return 0; } @@ -739,12 +748,6 @@ int _trans_layer::send_request(sip_msg* msg, trans_ticket* tt) tt->_bucket = 0; tt->_t = 0; - if(trans_layer::instance()->set_next_hop(msg) < 0){ - - DBG("set_next_hop failed\n"); - return -1; - } - int request_len = request_line_len(msg->u.request->method_str, msg->u.request->ruri_str); @@ -804,8 +807,20 @@ int _trans_layer::send_request(sip_msg* msg, trans_ticket* tt) return MALFORMED_SIP_MSG; } - memcpy(&p_msg->remote_ip,&msg->remote_ip,sizeof(sockaddr_storage)); - memcpy(&p_msg->h_dns,&msg->h_dns,sizeof(dns_handle)); + cstring next_hop; + unsigned short next_port=0; + + if(set_next_hop(p_msg,&next_hop,&next_port) < 0){ + DBG("set_next_hop failed\n"); + delete p_msg; + return -1; + } + + if(set_destination_ip(p_msg,&next_hop,next_port) < 0){ + DBG("set_destination_ip failed\n"); + delete p_msg; + return -1; + } DBG("Sending to %s:%i <%.*s...>\n", get_addr_str(((sockaddr_in*)&p_msg->remote_ip)->sin_addr).c_str(), @@ -1164,6 +1179,7 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg* case TS_CALLING: t->clear_timer(STIMER_A); + t->clear_timer(STIMER_M); t->clear_timer(STIMER_B); // fall through trap @@ -1197,6 +1213,8 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg* case TS_CALLING: t->clear_timer(STIMER_A); + t->clear_timer(STIMER_M); + t->clear_timer(STIMER_B); case TS_PROCEEDING: @@ -1236,6 +1254,7 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg* t->state = TS_TERMINATED_200; t->clear_timer(STIMER_A); + t->clear_timer(STIMER_M); t->clear_timer(STIMER_B); t->reset_timer(STIMER_L, L_TIMER, bucket->get_id()); @@ -1285,6 +1304,7 @@ int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg* t->state = TS_COMPLETED; t->clear_timer(STIMER_E); + t->clear_timer(STIMER_M); t->clear_timer(STIMER_F); t->reset_timer(STIMER_K, K_TIMER, bucket->get_id()); @@ -1322,12 +1342,23 @@ int _trans_layer::update_uac_request(trans_bucket* bucket, sip_trans*& t, sip_ms return -1; } + // clear old retransmission buffer delete [] t->retr_buf; + + // transfer the message buffer + // to the transaction (incl. ownership) t->retr_buf = msg->buf; t->retr_len = msg->len; msg->buf = NULL; msg->len = 0; + + // copy destination address memcpy(&t->retr_addr,&msg->remote_ip,sizeof(sockaddr_storage)); + + // remove the message; + delete msg; + + return 0; } switch(msg->u.request->method){ @@ -1339,13 +1370,6 @@ int _trans_layer::update_uac_request(trans_bucket* bucket, sip_trans*& t, sip_ms t->reset_timer(STIMER_B,B_TIMER,bucket->get_id()); break; - case sip_request::ACK: - // we do not need any timer here: - // -> TIMER L is already started. - - delete msg; - break; - default: // if transport == UDP t->reset_timer(STIMER_E,E_TIMER,bucket->get_id()); @@ -1353,7 +1377,11 @@ int _trans_layer::update_uac_request(trans_bucket* bucket, sip_trans*& t, sip_ms t->reset_timer(STIMER_F,F_TIMER,bucket->get_id()); break; } - + + if(!msg->h_dns.eoip()){ // if transport == UDP + t->reset_timer(STIMER_M,M_TIMER,bucket->get_id()); + } + return 0; } @@ -1653,6 +1681,33 @@ void _trans_layer::timer_expired(timer* t, trans_bucket* bucket, sip_trans* tr) } break; + case STIMER_M: + { + sockaddr_storage sa; + memset(&sa,0,sizeof(sockaddr_storage)); + + // get the next ip + if(tr->msg->h_dns.next_ip(&sa) < 0){ + tr->clear_timer(STIMER_M); + return; + } + memcpy(&tr->msg->remote_ip,&sa,sizeof(sockaddr_storage)); + + // create new branch tag + compute_branch((char*)(tr->msg->via_p1->branch.s+MAGIC_BRANCH_LEN), + tr->msg->callid->value,tr->msg->cseq->value); + + // reset counter for timer A & E + timer* A_E_timer = tr->get_timer(STIMER_A); + tr->reset_timer(A_E_timer->type,A_TIMER,bucket->get_id()); + + if(!tr->msg->h_dns.eoip()) + tr->reset_timer(STIMER_M,M_TIMER,bucket->get_id()); + else + tr->clear_timer(STIMER_M); + } + break; + default: ERROR("Invalid timer type %i\n",t->type); break; diff --git a/core/sip/trans_layer.h b/core/sip/trans_layer.h index aa70f3d7..4f5f2cf4 100644 --- a/core/sip/trans_layer.h +++ b/core/sip/trans_layer.h @@ -153,19 +153,26 @@ public: int send_sl_reply(sip_msg* req, int reply_code, const cstring& reason, const cstring& hdrs, const cstring& body); + + /** + * Transaction timeout + */ + void timeout(trans_bucket* bucket, sip_trans* t); + +protected: /** * Fills the address structure passed and modifies * R-URI and Route headers as needed. */ - int set_next_hop(sip_msg* req); - + int set_next_hop(sip_msg* msg, cstring* next_hop, + unsigned short* next_port); + /** - * Transaction timeout + * Fills msg->remote_ip according to next_hop and next_port. */ - void timeout(trans_bucket* bucket, sip_trans* t); + int set_destination_ip(sip_msg* msg, cstring* next_hop, unsigned short next_port); -protected: /** Avoid external instantiation. @see singleton. */ _trans_layer(); ~_trans_layer(); diff --git a/core/sip/trans_table.cpp b/core/sip/trans_table.cpp index 2216e806..8eb3afc7 100644 --- a/core/sip/trans_table.cpp +++ b/core/sip/trans_table.cpp @@ -43,7 +43,7 @@ // // Global transaction table // -//trans_bucket _trans_table[H_TABLE_ENTRIES]; + hash_table _trans_table(H_TABLE_ENTRIES); trans_bucket::trans_bucket(unsigned long id) @@ -51,11 +51,6 @@ trans_bucket::trans_bucket(unsigned long id) { } -// trans_bucket::trans_bucket() -// : ht_bucket::ht_bucket((unsigned long)this - (unsigned long)_trans_table) -// { -// } - trans_bucket::~trans_bucket() { } diff --git a/core/sip/trans_table.h b/core/sip/trans_table.h index c934b943..08b1643f 100644 --- a/core/sip/trans_table.h +++ b/core/sip/trans_table.h @@ -10,16 +10,15 @@ class trans_bucket: public ht_bucket { + trans_bucket(unsigned long id); + ~trans_bucket(); + + friend class hash_table; + public: + typedef ht_bucket::value_list trans_list; - /** - * Kept public to allow for static construction. - * !!! DO CREATE ANY BUCKETS ON YOUR OWN !!! - */ - // trans_bucket(); - trans_bucket(unsigned long id); - ~trans_bucket(); // Match a request to UAS transactions // in this bucket @@ -31,12 +30,6 @@ public: sip_trans* add_trans(sip_msg* msg, int ttype); - // /** - // * Remove a transaction from this bucket, - // * if it was still present. - // */ - // void remove_trans(sip_trans* t); - private: sip_trans* match_200_ack(sip_trans* t,sip_msg* msg); sip_trans* match_1xx_prack(sip_trans* t,sip_msg* msg);