Hi,
I am facing problem with handling CANCEL/487 Request Terminated in a case of a call hunting. The problem is that the call terminating carrier doesn't process the ACK properly and keeps sending 487 messages.
Here is an example.
Let's say UAC1 has the following rules of call hunting : 1. call +33123456 for 10s 2. call +33789456 for 10s
Let's say that an incoming call to UAC1 triggers the call hunting.
Here is a diagram :
openSER -> Terminating Carrier (TC) : INVITE +33123456
... (time out : +33123456 didn't answer in 10s, so openSER sends an INVITE to +33789456 - and let's say to an other Terminating carrier TC2 - and CANCEL the current INVITE to TC)
openSER -> TC2 : INVITE : +33789456 openSER -> TC : CANCEL TC -> openSER : 487 Request Terminated openSER -> TC : ACK
The problem is now TC doesn't process the ACK correctly and keeps sending 487. So, in the case of +33789456 answering the call (a 200 OK is sent to openSER), openSER will keep relaying the 487 to TC2 and TC2 will then send a BYE a terminate the call :
TC -> openSER : 487 openSER -> TC : ACK
TC -> openSER : 487 openSER -> TC : ACK
TC2 -> openSER : OK openSER -> TC2 : ACK <-- The call is taking place -->
TC -> openSER : 487 openSER -> TC : ACK openser -> TC2 : 487 (openSER relays the 487 once the call has been established to TC2) TC2 -> openSER : ACK TC2 -> openSER : BYE <-- Call is ended but should not -->
According to the RFC, once a call has been OKed and a 487 is received, TC2 may go on with the call or send a BYE (up to it). So it behaves the right way (chapter 15. end of 3rd paragraph : "If the INVITE results in 2xx final response(s) to the INVITE, this means that a UAS accepted the invitation while the CANCEL was in progress. The UAC MAY continue with the sessions established by any 2xx responses, or MAY terminate them with BYE.").
My question is then : is there a way to prevent this behavior when a terminating carrier doesn't behave correctly, either by preventing relaying of the 487 once it has been ACKed or once the call has been OKed (but I guess we are not RFC compliant then) ?
Thanks
Bye, Guillaume
2009/3/17 Guillaume Lacroix gl@worldline.fr:
openSER -> Terminating Carrier (TC) : INVITE +33123456
... (time out : +33123456 didn't answer in 10s, so openSER sends an INVITE to +33789456 - and let's say to an other Terminating carrier TC2
- and CANCEL the current INVITE to TC)
openSER -> TC2 : INVITE : +33789456 openSER -> TC : CANCEL TC -> openSER : 487 Request Terminated openSER -> TC : ACK
The problem is now TC doesn't process the ACK correctly and keeps sending 487. So, in the case of +33789456 answering the call (a 200 OK is sent to openSER), openSER will keep relaying the 487 to TC2 and TC2 will then send a BYE a terminate the call :
TC -> openSER : 487 openSER -> TC : ACK
TC -> openSER : 487 openSER -> TC : ACK
TC2 -> openSER : OK openSER -> TC2 : ACK <-- The call is taking place -->
TC -> openSER : 487 openSER -> TC : ACK openser -> TC2 : 487 (openSER relays the 487 once the call has been established to TC2) TC2 -> openSER : ACK TC2 -> openSER : BYE <-- Call is ended but should not -->
According to the RFC, once a call has been OKed and a 487 is received, TC2 may go on with the call or send a BYE (up to it). So it behaves the right way (chapter 15. end of 3rd paragraph : "If the INVITE results in 2xx final response(s) to the INVITE, this means that a UAS accepted the invitation while the CANCEL was in progress. The UAC MAY continue with the sessions established by any 2xx responses, or MAY terminate them with BYE.").
Really interesting issue.
When Kamailio receives the 200 Ok in the second branch, it terminates the transaction (according to RFC 3261), but the first branch remains receiving 487 responses. Then, following RFC 3261, Kamailio forwards *stateless* these responses with no transaction, so the 487 is received by the client (and it decides to terminate de call).
There is a draft handling those issues: http://tools.ietf.org/html/draft-sparks-sip-invfix-03
---------------------- 7.3. Proxy Considerations
A direct consequence of the change to the UAC state machine is that a transaction-stateful proxy will not foward any stray INVITE responses. When receiving any SIP response, a transaction-stateful proxy MUST compare the transaction identifier in that response against its existing transaction state machines. The proxy MUST NOT forward the response if there is no matching transaction state machine. ----------------------
Kamailio should implement this specification, I don't know if there is some way now to imitate this behaviour.
My question is then : is there a way to prevent this behavior when a terminating carrier doesn't behave correctly, either by preventing relaying of the 487 once it has been ACKed or once the call has been OKed (but I guess we are not RFC compliant then) ?
The behaviour you desire is already "draft"-compliant :)
Interesting issue however, could you please open a feature request on the tracker?
Thanks for the quick answer and explanation Iñaki :) As you advised, I opened a feature request in the tracker and put in it the elements you mentionned. If anyone has yet a way to trick openSER to not forward 487 answer with no transaction attached, I would be glad to know it :)
Bye, Guillaume
Iñaki Baz Castillo a écrit :
2009/3/17 Guillaume Lacroix gl@worldline.fr:
openSER -> Terminating Carrier (TC) : INVITE +33123456
... (time out : +33123456 didn't answer in 10s, so openSER sends an INVITE to +33789456 - and let's say to an other Terminating carrier TC2
- and CANCEL the current INVITE to TC)
openSER -> TC2 : INVITE : +33789456 openSER -> TC : CANCEL TC -> openSER : 487 Request Terminated openSER -> TC : ACK
The problem is now TC doesn't process the ACK correctly and keeps sending 487. So, in the case of +33789456 answering the call (a 200 OK is sent to openSER), openSER will keep relaying the 487 to TC2 and TC2 will then send a BYE a terminate the call :
TC -> openSER : 487 openSER -> TC : ACK
TC -> openSER : 487 openSER -> TC : ACK
TC2 -> openSER : OK openSER -> TC2 : ACK <-- The call is taking place -->
TC -> openSER : 487 openSER -> TC : ACK openser -> TC2 : 487 (openSER relays the 487 once the call has been established to TC2) TC2 -> openSER : ACK TC2 -> openSER : BYE <-- Call is ended but should not -->
According to the RFC, once a call has been OKed and a 487 is received, TC2 may go on with the call or send a BYE (up to it). So it behaves the right way (chapter 15. end of 3rd paragraph : "If the INVITE results in 2xx final response(s) to the INVITE, this means that a UAS accepted the invitation while the CANCEL was in progress. The UAC MAY continue with the sessions established by any 2xx responses, or MAY terminate them with BYE.").
Really interesting issue.
When Kamailio receives the 200 Ok in the second branch, it terminates the transaction (according to RFC 3261), but the first branch remains receiving 487 responses. Then, following RFC 3261, Kamailio forwards *stateless* these responses with no transaction, so the 487 is received by the client (and it decides to terminate de call).
There is a draft handling those issues: http://tools.ietf.org/html/draft-sparks-sip-invfix-03
7.3. Proxy Considerations
A direct consequence of the change to the UAC state machine is that a transaction-stateful proxy will not foward any stray INVITE responses. When receiving any SIP response, a transaction-stateful proxy MUST compare the transaction identifier in that response against its existing transaction state machines. The proxy MUST NOT forward the response if there is no matching transaction state machine.
Kamailio should implement this specification, I don't know if there is some way now to imitate this behaviour.
My question is then : is there a way to prevent this behavior when a terminating carrier doesn't behave correctly, either by preventing relaying of the 487 once it has been ACKed or once the call has been OKed (but I guess we are not RFC compliant then) ?
The behaviour you desire is already "draft"-compliant :)
Interesting issue however, could you please open a feature request on the tracker?
El Martes, 17 de Marzo de 2009, Guillaume Lacroix escribió:
Thanks for the quick answer and explanation Iñaki :) As you advised, I opened a feature request in the tracker and put in it the elements you mentionned. If anyone has yet a way to trick openSER to not forward 487 answer with no transaction attached, I would be glad to know it :)
Thanks, hope we could find a solution for that.
Iñaki Baz Castillo wrote:
El Martes, 17 de Marzo de 2009, Guillaume Lacroix escribió:
Thanks for the quick answer and explanation Iñaki :) As you advised, I opened a feature request in the tracker and put in it the elements you mentionned. If anyone has yet a way to trick openSER to not forward 487 answer with no transaction attached, I would be glad to know it :)
Thanks, hope we could find a solution for that.
Can't you check if a transaction exists and "drop" the reply in the reply route?
If reply-routes can be used to modify replies (i.e. SDP and Contact fixups for NAT traversal), surely they can be used to suppress them as well?
El Martes, 17 de Marzo de 2009, Alex Balashov escribió:
Iñaki Baz Castillo wrote:
El Martes, 17 de Marzo de 2009, Guillaume Lacroix escribió:
Thanks for the quick answer and explanation Iñaki :) As you advised, I opened a feature request in the tracker and put in it the elements you mentionned. If anyone has yet a way to trick openSER to not forward 487 answer with no transaction attached, I would be glad to know it :)
Thanks, hope we could find a solution for that.
Can't you check if a transaction exists and "drop" the reply in the reply route?
Not so easy. I've never tryed the only way to process a *stateless* reply is in "onreply_route[0]". Theorically "check_trans()" works also in ONREPLY_ROUTE, but even if it detects the reply, how to drop it? do "drop()" or "exit()" eliminate a response?
If reply-routes can be used to modify replies (i.e. SDP and Contact fixups for NAT traversal), surely they can be used to suppress them as well?
Sure? (I don't know).
PD: I will try it right now :)
Iñaki Baz Castillo wrote:
Not so easy. I've never tryed the only way to process a *stateless* reply is in "onreply_route[0]". Theorically "check_trans()" works also in ONREPLY_ROUTE, but even if it detects the reply, how to drop it? do "drop()" or "exit()" eliminate a response?
I would think drop() would do it.
Why is the response stateless? When a 487 is relayed by a proxy it is statefully associated with an INVITE transaction that is being closed in response to a CANCEL, right? Or did I not carefully read this thread and miss something?
If reply-routes can be used to modify replies (i.e. SDP and Contact fixups for NAT traversal), surely they can be used to suppress them as well?
Sure? (I don't know).
PD: I will try it right now :)
Will be interested to know the results!
El Martes, 17 de Marzo de 2009, Alex Balashov escribió:
Iñaki Baz Castillo wrote:
Not so easy. I've never tryed the only way to process a *stateless* reply is in "onreply_route[0]". Theorically "check_trans()" works also in ONREPLY_ROUTE, but even if it detects the reply, how to drop it? do "drop()" or "exit()" eliminate a response?
I would think drop() would do it.
Why is the response stateless? When a 487 is relayed by a proxy it is statefully associated with an INVITE transaction that is being closed in response to a CANCEL, right? Or did I not carefully read this thread and miss something?
The transaction was terminated when a branch replied a 200 (as RFC 3261 states).
Iñaki Baz Castillo wrote:
El Martes, 17 de Marzo de 2009, Alex Balashov escribió:
Iñaki Baz Castillo wrote:
Not so easy. I've never tryed the only way to process a *stateless* reply is in "onreply_route[0]". Theorically "check_trans()" works also in ONREPLY_ROUTE, but even if it detects the reply, how to drop it? do "drop()" or "exit()" eliminate a response?
I would think drop() would do it.
Why is the response stateless? When a 487 is relayed by a proxy it is statefully associated with an INVITE transaction that is being closed in response to a CANCEL, right? Or did I not carefully read this thread and miss something?
The transaction was terminated when a branch replied a 200 (as RFC 3261 states).
Ahh. So, then why does the "reason" code for 487 say something like "transaction terminated," if the transaction has not existed for a long time?
El Martes, 17 de Marzo de 2009, Alex Balashov escribió:
The transaction was terminated when a branch replied a 200 (as RFC 3261 states).
Ahh. So, then why does the "reason" code for 487 say something like "transaction terminated," if the transaction has not existed for a long time?
487 comes from the branch_1, it's not generated by Kamailio!
The problem is: - Kamailio sends INVITE to branch_1. - After some seconds sends a CANCEL. - And sends the INVITE to branch_2. - branch_1 replies 200 for the CANCEL and 487 for the INVITE, Kamailio sends the ACK but the server ignores it and remains sending the same 487 for a while. - During that time, branch_2 replies 200 so Kamailio (accoprding to RFC 3261) terminates the transaction and forwards the 200 upstream. - After some seconds branch_1 sends a new 487 (due to the problem with the ACK). - This 487 has now no transaction in Kamailio so it forwards it stateless (by inspecting the Via header) to the client. - The client receives a 487 and decides to terminate the call.
Hope it's clear now :)
El Martes, 17 de Marzo de 2009, Alex Balashov escribió:
PD: I will try it right now :)
Will be interested to know the results!
It works !!!
onreply_route { if ! t_check_trans() { xlog("L_CRIT", "--- Response with no transaction received --> drop()\n"); drop(); } }