RFC 4321

Problems Identified Associated with the Session Initiation Protocol's (SIP) Non-INVITE Transaction

πŸ“… published January 2006, from ietf

Problems Identified Associated with the Session Initiation Protocol's (SIP) Non-INVITE Transaction ✨

πŸŽ€ okay bestie, this is RFC 4321, from the Network Working Group, and it's written by R. Sparks of Estacado Systems. It's Informational, so it's sharing knowledge and it's not a standard πŸ’…. The date is January 2006. The title block doesn't list anything it obsoletes or updates.

Status of This Memo 🌸

This memo gives info to the Internet community. It is not an Internet standard of any kind, and it can be shared freely with no limits on distribution.

Copyright (C) The Internet Society (2006).

Abstract πŸ¦‹

This doc goes over a few problems that have popped up with the non-INVITE transaction in the Session Initiation Protocol (SIP). Main character energy for sure πŸ‘‘.

Table of Contents πŸ“š

1. Problems under the Current Specifications .......................2
   1.1. NITs must complete immediately or risk losing a race .......2
   1.2. Provisional responses can delay recovery from lost
        final responses ............................................3
   1.3. Delayed responses will temporarily blacklist an element ....4
   1.4. 408 for non-INVITE is not useful ...........................6
   1.5. Non-INVITE timeouts doom forking proxies ...................7
   1.6. Mismatched timer values make winning the race harder .......7
2. Security Considerations .........................................8
3. Acknowledgements ................................................8
4. Informative References ..........................................9

1. Problems under the Current Specifications πŸ”₯

Okay so the SIP non-INVITE transaction (NIT) model has a fixed duration, and that creates a bunch of messy edge cases 😭. The yikes-y parts get worse because provisional responses mess with the non-INVITE transaction state machines the way they're currently defined.

1.1. NITs must complete immediately or risk losing a race πŸƒβ€β™€οΈ

The non-INVITE transaction from RFC 3261 [1] is designed to have a fixed, finite duration (it depends on T1). So participants have to hurry and finish the transaction as fast as they can. Check out the race condition in Figure 1 πŸ‘€.

                     UAC           UAS
                      |   request   |
                 ---  |---.         |
                  ^   |    `---.    |
                  |   |         `-->|  ---
                  |   |             |   ^
                  |   |             |   |
                64*T1 |             |   |
                  |   |             |   |
                  |   |             | 64*T1
                  |   |             |   |
                  |   |             |   |
                  v   |             |   |
    timeout <=== ---  |   200 OK    |   |
                      |         .---|   v
                      |    .---'    |  ---
                      |<--'         |

            Figure 1: Non-Invite Race Condition

The User Agent Server (UAS) in this figure thinks it responded in time and the request worked. The User Agent Client (UAC), though, thinks the request timed out and failed. Since it no longer has a matching client transaction, the UAC core just ignores what it believes is a spurious response. As far as the UAC is concerned, it got no response at all. The net result is that the UAS and UAC disagree about how the transaction turned out πŸ’€.

So a UAS can't wait until the last possible moment to send its final response within a NIT. It has to send it so it arrives at the UAC before that UAC times out. The problem is that the UAS can't accurately measure how long the request took to travel, or predict how long the response will take. Every proxy in the path adds more uncertainty πŸŒ€. That leaves the UAS with one real option: send its final response as soon as it possibly can and hope for the best πŸ™.

This limits which problems a single NIT can solve. Any delay while processing a request makes losing the race more likely. If the timing of that processing isn't predictable and controllable, a single NIT is the wrong model for handling the request. One option that works is to accept the request with a 202 and send the ultimate results in a new request going the other way πŸ¦‹.

In specialized networks, a UAS might reliably know the inter-hop latency and use that to decide whether it has time to hold its final response while doing something like a database lookup, all while keeping its risk of losing the race in Figure 1 low. Establishing that knowledge across arbitrary networks (maybe with resource reservation techniques and deterministic transports) isn't currently feasible πŸ™ƒ.

1.2. Provisional responses can delay recovery from lost final responses ⏳

The non-INVITE client transaction state machine gives NITs reliability over unreliable transports (UDP) by retransmitting the request message. Timer E is set to T1 when the request is first sent. While the machine stays in the Trying state, each time Timer E fires it gets reset to twice its previous value (capping at T2), and the request is sent again.

If the non-INVITE client transaction state machine sees a provisional response, it moves to the Proceeding state. Retransmission keeps going there, but the Timer E reset algorithm is just to use T2 instead of doubling every time. (Timer E doesn't change during the move into Proceeding.)

Moving into Proceeding before Timer E has reset to T2 can make recovery from a lost final response take extra time. Figure 2 shows recovery from a lost final response with and without a provisional message during that window. Recovery happens within 2*T1 without the provisional. With the provisional, recovery gets delayed until T2, which by default is 8*T1 🐒.

             UAC       UAS               UAC        UAS
              |         |                 |          |
        ---   |----.    |            ---  |----.     |
         ^    |     `-->|             ^   |     `--->|
     E = T1   |         |         E = T1  |    .-----|(provisional)
         v    |         |             v   |<--'      |
        ---   |----.    |            ---  |----.     |
         ^    |     `-->|             ^   |     `--->|
         |    |   X<----|(lost final) |   |   X<-----|(lost final)
         |    |         |             |   |          |
     E = 2*T1 |         |             |   |          |
         |    |         |             |   |          |
         |    |         |             |   |          |
         v    |         |             |   |          |
        ---   |----.    |             |   |          |
              |     `-->|             |   |          |
              |   .-----|(final)      |   |          |
              |<-'      |             |   |          |
              |         |             |   |          |
             \/\       /\/           /\/ /\/        /\/
                                  E = T2
             \/\       /\/           /\/ /\/        /\/
              |         |             |   |          |
              |         |             v   |          |
              |         |            ---  |----.     |
              |         |                 |     `--->|
              |         |                 |    .-----|(final)
              |         |                 |<--'      |
              |         |                 |          |

               Figure 2: Provisionals Can Harm Recovery

In practical terms, a provisional response to a NIT in currently deployed networks can hold up transaction completion by up to 3.5 seconds πŸ₯Ά.

No additional delay happens if the first provisional response arrives after Timer E has already hit its max reset interval of T2 ✨.

1.3. Delayed responses will temporarily blacklist an element 🚫

A SIP element's use of DNS Service Record Resource Records [3] is specified in RFC 3263 [2]. That spec explains how SIP keeps things highly available by having upstream elements detect when downstream elements fail. It then defines several kinds of failure detection and instructions for failover. Two of the behaviors it describes matter for this doc:

  • Inside a transaction, transport failure gets detected either by an explicit report from the transport layer or by a timeout. Timeout counts as a transport failure no matter which transport is in use. When that happens, the request is retried at the next element in the sorted results of the SRV query.
  • Between transactions, locations that report a temporary failure (like with 503/Retry-After, for example) don't get used until their requested black-out period is over.

The spec notes the benefit of caching locations that were successfully contacted, but it doesn't say how that cache gets maintained. It's unclear whether an element should stop using (temporarily blacklist) a location returned in the SRV query that results in a transport error. And if it does, when should that location come off the blacklist? πŸ€”

Without a blacklist (or some equivalent mechanism), the intended availability mechanism fails miserably πŸ’€. Picture traffic between two domains. Proxy pA in domain A needs to forward a sequence of non-INVITE requests to domain B. Through DNS SRV, pA discovers pB1 and pB2, and the ordering rules of [2] and [3] say it should use pB1 first. The first request to pB1 times out. Since pA is a proxy and a NIT has a fixed duration, pA has no opportunity to retry the request at pB2. If pA doesn't remember pB1's failure, the second request (and every non-INVITE request after it until pB1 recovers) is doomed to the same failure. Caching would let those later requests get tried at pB2 πŸ’….

Since miserable failure isn't acceptable in deployed networks, we should expect that elements will actually cache timeout failures between transactions. Then the race in Figure 1 becomes important πŸ‘‘. If an element fails to respond "soon enough", it has effectively not responded at all, and it'll be blacklisted by its peer for some period of time πŸ™ˆ.

(Heads up: even with caching, the first request timeout still results in a timeout failure all the way back to the original submitter. The failover mechanisms in [2] work well to boost the resiliency of a given INVITE transaction, but they do nothing for a given non-INVITE transaction πŸ₯².)

1.4. 408 for non-INVITE is not useful ✨

okay so check out the race condition in Figure 1 when the final response is a 408 instead of a 200 πŸ™ˆ under the current spec, that race is guaranteed to be lost πŸ’€ most existing endpoints send a 408 for a non-INVITE request 64*T1 after they get the request, as long as they haven't already sent an earlier final response. but that 408 is guaranteed to show up at the next upstream element way too late to matter 😭 and with proxies it's even worse, it's actually harmful πŸ₯€ by the time the 408 arrives, each proxy has already killed its client transaction because of timeout. so each proxy has to forward the 408 upstream statelessly, and guess what, that's also guaranteed to arrive too late πŸ’… Figure 3 shows how this can end up bombarding the original requester with spurious 408s. (fun fact: the proxy's client transaction state machine never enters the Completed state, so Timer K doesn't come into play 🧚)

                 UAC        P1         P2         P3         UAS
                  |          |          |          |          |
            ---  ===---.     |          |          |          |
             ^    |     `-->===---.     |          |          |
             |    |          |     `-->===---.     |          |
             |    |          |          |     `-->===---.     |
           64*T1  |          |          |          |     `-->===
             |    |          |          |          |          |
             |    |          |          |          |          |
             v    |          |          |          |          |
  (timeout) ---  ===         |          |          |          |
                  |    .-408===         |          |          |
                  |<--'      |    .-408===         |          |
                  |    .-408-|<--'      |    .-408===         |
                  |<--'      |    .-408-|<--'      |    .-408===
                  |    .-408-|<--'      |    .-408-|<--'      |
                  |<--'      |    .-408-|<--'      |          |
                  |    .-408-|<--'      |          |          |
                  |<--'      |          |          |          |
                  |          |          |          |          |

Figure 3: Late 408s to Non-INVITEs

this response bombardment isn't only about 408s 🌸 it only happens when the participating client transaction state machines are timing out. Figure 4 takes Figure 1 and adds multiple hops. lowkey the UAS does respond "in time" to P3, but that response is still too late for P2, P1, and the UAC 😀

                 UAC        P1         P2         P3         UAS
                  |          |          |          |          |
            ---  ===---.     |          |          |          |
             ^    |     `-->===---.     |          |          |
             |    |          |     `-->===---.     |          |
             |    |          |          |     `-->===---.     |
           64*T1  |          |          |          |     `-->===
             |    |          |          |          |          |
             |    |          |          |          |          |
             v    |          |          |          |          |
  (timeout) ---  ===         |          |          |          |
                  |    .-408===         |          |    .-200-|
                  |<--'      |    .-408===   .-200-|<--'      |
                  |    .-408-|<--'.-200-|<--'     ===         |
                  |<--'.-200-|<--'      |          |         ===
                  |<--'      |          |          |          |
                  |          |          |          |          |

Figure 4: Additional Timeout-Related Error

1.5. Non-INVITE timeouts doom forking proxies πŸ’€

ngl this one is rough 😭 a single branch with a delayed or missing final response takes over the processing at proxy that gets no 2xx responses to a forked non-INVITE request. this proxy is required to let all of its client transactions finish before it picks a "best response". that forces the proxy's server transaction to lose the race in Figure 1 πŸƒβ€β™€οΈ anything it finally forwards (a 401, for example) shows up at the upstream elements too late to be used. so if none of the branches would return a 2xx response, one element failing (or its transport) dooms the proxy to failure πŸ˜”

1.6. Mismatched timer values make winning the race harder πŸŽ€

there are a ton of failure scenarios from misconfiguration or misbehavior that the SIP spec just doesn't talk about πŸ™ƒ one of them is putting two elements with different configured values for T1 and T2 on the same network. looking at Figure 1, the race failure only gets more likely in this misconfigured state (you might think shortening T1 on the element acting as a UAS helps here, but remember these elements can swap roles on the next request, bestie πŸ’…). the protocol has no way to discover or negotiate a peer's timer values, so if you deploy systems with non-defaults, you have to be super careful that they never directly talk to elements with default values 🫢

2. Security Considerations πŸ”

this doc describes some problems in the core SIP spec [1] related to SIP non-INVITE requests, which are the messages other than INVITE that begin transactions. a few of these problems lead to flooding or forgery risk, and an adversary could possibly exploit them in a denial of service attack 😱 the fixes live in the companion document [4].

one fix there prohibits proxies and User Agents from sending 408 responses to non-INVITE transactions. without that change, proxies automatically generate a storm of useless responses πŸŒͺ️ an attacker could take advantage of this by tricking User Agents into sending non-INVITE requests to a black hole (through social engineering or DNS poisoning), or by selectively dropping responses.

⚠️ this one is serious, so don't skip it: the other fix prohibits proxies from forwarding late responses. without it, an attacker could easily forge messages that look like late responses. every proxy compliant with RFC 3261 is required to forward these responses, which wastes bandwidth and CPU and could potentially overwhelm target User Agents (especially ones on low speed connections) 🚨

3. Acknowledgements 🫢

this doc captures lots of conversations about non-INVITE issues ✨ significant contributers include Ben Campbell, Gonzalo Camarillo, Steve Donovan, Rohan Mahy, Dan Petrie, Adam Roach, Jonathan Rosenberg, and Dean Willis. literal legends πŸ‘‘

4. Informative References πŸ“š

[1] Rosenberg, J., Schulzrinne, H., Camarillo, G., Johnston, A., Peterson, J., Sparks, R., Handley, M., and E. Schooler, "SIP: Session Initiation Protocol", RFC 3261, June 2002.

[2] Rosenberg, J. and H. Schulzrinne, "Session Initiation Protocol (SIP): Locating SIP Servers", RFC 3263, June 2002.

[3] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for specifying the location of services (DNS SRV)", RFC 2782, February 2000.

[4] Sparks, R., "Actions Addressing Identified Issues with the Session Initiation Protocol's (SIP) Non-INVITE Transaction", RFC 4320, January 2006.

Author's Address πŸŽ€

Robert J. Sparks Estacado Systems 17210 Campbell Road Suite 250 Dallas, TX 75252-4203

EMail: rjsparks@estacado.net

Copyright (C) The Internet Society (2006).

This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights.

This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Intellectual Property πŸ”’

The IETF takes no position regarding the validity or scope of any Intellectual Property Rights or other rights that might be claimed to pertain to the implementation or use of the technology described in this document or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights. Information on the procedures with respect to rights in RFC documents can be found in BCP 78 and BCP 79.

Copies of IPR disclosures made to the IETF Secretariat and any assurances of licenses to be made available, or the result of an attempt made to obtain a general license or permission for the use of such proprietary rights by implementers or users of this specification can be obtained from the IETF on-line IPR repository at http://www.ietf.org/ipr.

The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that may cover technology that may be required to implement this standard. Please address the information to the IETF at ietf-ipr@ietf.org.

Acknowledgement 🌈

Funding for the RFC Editor function is provided by the IETF Administrative Support Activity (IASA). πŸ’–

πŸ“¦ where this page comes from