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.
sayer/1.4-spce2.6
Raphael Coeffic 16 years ago
parent 5120770a4e
commit f5bfad916c

@ -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

@ -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;

@ -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

@ -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();
}

@ -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

@ -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<sip_header*>& 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;

@ -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();

@ -43,7 +43,7 @@
//
// Global transaction table
//
//trans_bucket _trans_table[H_TABLE_ENTRIES];
hash_table<trans_bucket> _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<sip_trans>::ht_bucket((unsigned long)this - (unsigned long)_trans_table)
// {
// }
trans_bucket::~trans_bucket()
{
}

@ -10,16 +10,15 @@
class trans_bucket:
public ht_bucket<sip_trans>
{
trans_bucket(unsigned long id);
~trans_bucket();
friend class hash_table<trans_bucket>;
public:
typedef ht_bucket<sip_trans>::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);

Loading…
Cancel
Save