问题描述:
FreeSWITCH发起外呼(通过OpenSIPS forward到运营商),在receive_message收到SWITCH_MESSAGE_INDICATE_BRIDGE时(SIP receive 183 Session Progress SDP),再发起RE-INVITE,这时其实还没有收到运营商返回的200OK。FS会保证在收到200OK后,将ACK和RE-INVITE依次发送出去。
OpenSIPS上先收到ACK再收到RE-INVITE,但是OpenSIPS在处理ACK和RE-INVITE时,很有可能会先将RE-INVITE发送出去,这时运营商还没有收到上次的ACK,会返回500 Overlapping Requests。
这种case可以参考 https://datatracker.ietf.org/doc/html/rfc5407#page-17 (Call Flows of Race Conditions) 中的3.1.4章节。
示例流程图:
深入分析:
后面有空的话会写下OpenSIPS tm模块的处理流程。
解决方法:
1. 使用tcp,一通通话的connection中消息是保证顺序的。
2. 运营商返回200OK,并启动定时器等待ACK,一段时间等不到再发送BYE。
3. 运营商不处理RE-INVITE,这样OpenSIPS会重发,直到运营商收到ACK,进入正常逻辑。
4. OpenSIPS tm模块保证INVITE发送在ACK之前。
5. FS发送RE-INVITE要delay一些。
6. OpenSIPS路由脚本中,在判断RE-INVITE后,添加 'usleep(400000);'。
7. OpenSIPS可以在自己的模块中注册回调函数到tm模块,监听TMCB_PRE_SEND_BUFFER事件,并在回调函数中判断是否为RE-INVITE,然后sleep任意时间。
首先排除了2、3,运营商那边的处理策略我们不好影响;1的话因为没有基于tcp使用sip的经验,担心引入其它问题;4需要改动大量OpenSIPS代码,而且即使保证INVITE发送在ACK之前,也不能保证运营商是这个接收顺序,不如选择5、6、7。
其中6最简单,这里sleep 400ms是因为transaction里消息的超时时间默认是500ms,如果sleep太长时间的话FS会因为收不到 '100 Giving it a try' 而重发RE-INVITE(当然OpenSIPS是能hold住这种case的)。效果如下:
其中10.18.2.18为OpenSIPS,10.18.2.206为FS。可以看到OpenSIPS收到INVITE后delay了400ms(18:57:12.978724 -> 18:57:13.379533)。
第7个方法比6要好,因为是在发送 '100 Giving it a try' 之后才delay的,不像6中 send 100也delay。效果如下:
可以看到,send 100之后delay了600ms,不会导致6中sleep超过500ms而导致的重发问题。
附部分代码如下:
void sendcb(struct cell* t, int type, struct tmcb_params* param)
{
struct dlg_cell* dlg;
int tp = param->req->REQ_METHOD;
if (tp == TMCB_REQUEST_IN) {
usleep(600000);
}
}
void incb(struct cell* t, int type, struct tmcb_params* param)
{
struct dlg_cell* dlg;
if (type == TMCB_REQUEST_IN && has_to_tag(¶m->req->to->body)) {
if (tmb.register_tmcb(NULL, t, TMCB_PRE_SEND_BUFFER, sendcb, 0, 0) <= 0) {
LM_ERR("cannot register TMCB_REQUEST_IN callback\n");
return -1;
}
}
}
static int mod_init(void)
{
/*****************/
if (tmb.register_tmcb(0, 0, TMCB_REQUEST_IN, incb, 0, 0) <= 0) {
LM_ERR("cannot register TMCB_REQUEST_IN callback\n");
return -1;
}
/*****************/
return 0;
}