10.1 Two paths: responses follow Via, requests follow Route
Every SIP element that handles a message answers one question: where does it go next? The answer comes from different headers for requests and for responses.
| A request | A response | |
|---|---|---|
| Next hop | The first Route value; with no Route, the Request-URI | The top Via |
| What each proxy changes | Adds a Via; removes its own Route value; may replace the Request-URI; may add Record-Route | Removes its own Via |
| Path | Can be different for each request | Always the path of its request, in reverse |
“The proxy will forward the request to the resource indicated by the URI in the topmost Route header field value or in the Request-URI if no Route header field is present.”Read the section ↗
“The Via header field indicates the transport used for the transaction and identifies the location where the response is to be sent.”Read the section ↗
The first INVITE finds Bob with DNS and the location service (Modules 3 and 11). Its responses retrace that path exactly. But the requests that come later in the dialog — the ACK for the 2xx, the BYE, a re-INVITE — do not follow the INVITE. They follow the route set, which comes from Record-Route (10.4). Mixing up the two paths is the most common routing mistake.
10.2 The Via stack
Each element that sends a request puts its own Via on top of the Vias already there. Each element that forwards a response takes its own Via off the top. The Vias are a stack: the response pops them in the reverse order of the request.
“The proxy MUST insert a Via header field value into the copy before the existing Via header field values.”Read the section ↗
“The proxy removes the topmost Via header field value from the response. If no Via header field values remain in the response, the response was meant for this element and MUST NOT be forwarded.”Read the section ↗
One Via value holds:
- Transport and sent-by:
SIP/2.0/UDP 198.51.100.10:5060— the transport and the address where the sender wants the response. - branch: the transaction ID, starting with
z9hG4bK(Module 8). Each hop has its own transaction, so each Via has its own branch. - received and rport: what the next hop really saw — added by the next hop, not by the sender (10.3).
A Via belongs to one transaction. A new request — the ACK for a 2xx, a BYE — starts again with one Via, and gets a new stack along its own path. Step through the call below. Watch the stack grow on the INVITE and shrink on the 200 OK, and see where each message goes.
Via stack
Via headers stack up and come off
- +Added at this hop
- −Removed at this hop
Alice's phone adds the first Via: its private address, and an empty rport. A NAT router changes the source to 192.0.2.10:40112.
INVITEAlice → Proxy A · request
Alice creates the request, so it has one Via: its own.
- +
Alice
UDP 192.168.1.20:5060branch=z9hG4bKv1a7rport (empty)
Sent to
sip:proxy.atlanta.example;lr
from the first Route header
10.3 received and rport: the response path behind NAT
The sender writes its own address into sent-by. Behind a NAT router, that is a private address, such as 192.168.1.20:5060. A response sent there never arrives. So the next hop writes what it actually saw into the Via:
“If the host portion of the "sent-by" parameter contains a domain name, or if it contains an IP address that differs from the packet source address, the server MUST add a "received" parameter to that Via header field value.”Read the section ↗
received fixes the address, but not the port. A NAT router also changes the UDP source port. RFC 3581 adds rport: the sender adds it with no value, and the next hop fills in the source port.
“If this Via header field value contains an "rport" parameter with no value, it MUST set the value of the parameter to the source port of the request.”Read the section ↗
“In fact, the server MUST insert a "received" parameter containing the source IP address that the request came from, even if it is identical to the value of the "sent-by" component.”Read the section ↗
The server then picks the response address from the top Via, in this order:
| The top Via has… | The response goes to… |
|---|---|
| TCP or TLS transport | The same connection the request came on |
UDP, received and rport |
received address : rport port |
UDP, received only |
received address : sent-by port (5060 if none) |
| UDP, neither | The sent-by address and port |
“If the "sent-protocol" is a reliable transport protocol such as TCP or SCTP, or TLS over those, the response MUST be sent using the existing connection to the source of the original request that created the transaction, if that connection is still open.”Read the section ↗
“Otherwise (for unreliable unicast transports), if the top Via has a "received" parameter, the response MUST be sent to the address in the "received" parameter, using the port indicated in the "sent-by" value, or using port 5060 if none is specified explicitly.”Read the section ↗
“If the "sent-protocol" component indicates an unreliable unicast transport protocol, such as UDP, and there is no "maddr" parameter, but there is both a "received" parameter and an "rport" parameter, the response MUST be sent to the IP address listed in the "received" parameter, and the port in the "rport" parameter. The response MUST be sent from the same address and port that the corresponding request was received on.”Read the section ↗
In the Via stack above, step 2 shows this. Proxy A sends the 100 Trying to 192.0.2.10:40112 — the public side of Alice’s NAT router — and not to the private address that Alice wrote. A proxy keeps the received and rport of the Vias below its own; it removes only its own Via. Module 20 covers the rest of NAT: keepalives, Contact rewriting, and SIP Outbound.
10.4 Record-Route: staying in the path
After the first INVITE, a proxy is out of the call — unless it asks to stay. Some proxies must see every request in the dialog: a billing proxy needs the BYE to stop counting, a proxy that handles NAT must relay requests to a phone behind a NAT router, and an SBC at the network edge must see everything. Such a proxy adds Record-Route to the request that creates the dialog:
“If this proxy wishes to remain on the path of future requests in a dialog created by this request (assuming the request creates a dialog), it MUST insert a Record-Route header field value into the copy before any existing Record-Route header field values, even if a Route header field is already present.”Read the section ↗
“Each proxy in the path of a request chooses whether to add a Record-Route header field value independently - the presence of a Record-Route header field in a request does not obligate this proxy to add a value.”Read the section ↗
The UAS copies the Record-Route values into its 2xx (and its 101–199 responses), in the same order, so both sides learn the list:
“When a UAS responds to a request with a response that establishes a dialog (such as a 2xx to INVITE), the UAS MUST copy all Record-Route header field values from the request into the response (including the URIs, URI parameters, and any Record-Route header field parameters, whether they are known or unknown to the UAS) and MUST maintain the order of those values.”Read the section ↗
Record-Route has a cost. Every request in the dialog passes through the proxy, and a dialog-stateful proxy keeps state for every call. So a proxy record-routes only when a service needs it:
“Record-routing may be required by certain services where the proxy needs to observe all messages in a dialog. However, it slows down processing and impairs scalability and thus proxies should only record-route if required for a particular service.”Read the section ↗
Turn Record-Route on and off for each proxy below, and choose a later request. The INVITE and its 200 OK always pass both proxies. Only the later request changes its path.
Routing visualiser
Who adds Record-Route decides who sees the rest of the call
Alice's route setRecord-Route, reversed
sip:proxy.atlanta.example;lrsip:proxy.biloxi.example;lr
Bob's route setRecord-Route, in order
sip:proxy.biloxi.example;lrsip:proxy.atlanta.example;lr
| Hop | Request-URI | Route |
|---|---|---|
bob@203.0.113.20:5060 | proxy.atlanta.exampleproxy.biloxi.example | |
bob@203.0.113.20:5060 | proxy.biloxi.example | |
bob@203.0.113.20:5060 | none |
Alice hangs up. The BYE follows her route set: Proxy A, then Proxy B, then Bob.
Try these:
- Both off. The BYE goes straight from Alice to Bob. Neither proxy knows that the call ended.
- Only Proxy B on. Alice’s ACK and BYE skip Proxy A — her own outbound proxy — and go straight to Proxy B.
- Both on, re-INVITE from Bob. Bob’s request visits Proxy B first, then Proxy A: his route set is in the other order.
10.5 Building the route set
Each proxy puts its Record-Route value on top, so the last proxy before Bob is first in the list. When the INVITE reaches Bob, it has:
Record-Route: <sip:proxy.biloxi.example;lr>
Record-Route: <sip:proxy.atlanta.example;lr>
Each side wants its nearest proxy first. Bob’s nearest proxy is Proxy B, already on top, so he keeps the order. Alice’s nearest proxy is Proxy A, at the bottom, so she reverses the list:
| Takes Record-Route from | Order | Route set in the example | |
|---|---|---|---|
| Bob (UAS) | The request | As received | proxy.biloxi.example, proxy.atlanta.example |
| Alice (UAC) | The 2xx response | Reversed | proxy.atlanta.example, proxy.biloxi.example |
“The route set MUST be set to the list of URIs in the Record-Route header field from the request, taken in order and preserving all URI parameters.”Read the section ↗
“The route set MUST be set to the list of URIs in the Record-Route header field from the response, taken in reverse order and preserving all URI parameters.”Read the section ↗
The route set is fixed once the dialog is confirmed. A re-INVITE can change the remote target, but not the route set:
“Requests within a dialog MAY contain Record-Route and Contact header fields. However, these requests do not cause the dialog's route set to be modified, although they may modify the remote target URI.”Read the section ↗
To send a request in the dialog, the UA puts the remote target in the Request-URI and the route set in Route:
“If the route set is not empty, and the first URI in the route set contains the lr parameter (see Section 19.1.1), the UAC MUST place the remote target URI into the Request-URI and MUST include a Route header field containing the route set values in order, including all parameters.”Read the section ↗
Each proxy on the way finds itself in the top Route value and removes it. When no Route value is left, the request goes to the Request-URI: the remote target.
“If the first value in the Route header field indicates this proxy, the proxy MUST remove that value from the request.”Read the section ↗
10.6 Loose routing (;lr) and strict routing
RFC 3261 separates two things: where the request is going (the Request-URI) and which proxies it must visit on the way (Route). This is loose routing. The lr parameter in a URI says “this element is a loose router”:
“A proxy is said to be loose routing if it follows the procedures defined in this specification for processing of the Route header field. These procedures separate the destination of the request (present in the Request-URI) from the set of proxies that need to be visited along the way (present in the Route header field).”Read the section ↗
“The lr parameter, when present, indicates that the element responsible for this resource implements the routing mechanisms specified in this document.”Read the section ↗
The older RFC 2543 used strict routing: the next hop always went into the Request-URI, and the real target moved to the end of Route.
“That rule caused proxies to destroy the contents of the Request-URI when a Route header field was present. Strict routing behavior is not used in this specification, in favor of a loose routing behavior.”Read the section ↗
A UA still supports strict routers. If the first URI of the route set has no lr, it builds the request the old way. The same BYE, with a route set of one proxy:
First route has ;lr (loose) |
First route has no lr (strict) |
|
|---|---|---|
| Request-URI | sip:bob@203.0.113.20:5060 — the remote target |
sip:proxy.biloxi.example — the first route |
| Route | <sip:proxy.biloxi.example;lr> |
<sip:bob@203.0.113.20:5060> — the remote target, last |
“If the route set is not empty, and its first URI does not contain the lr parameter, the UAC MUST place the first URI from the route set into the Request-URI, stripping any parameters that are not allowed in a Request-URI. The UAC MUST add a Route header field containing the remainder of the route set values in order, including all parameters. The UAC MUST then place the remote target URI into the Route header field as the last value.”Read the section ↗
An RFC 3261 proxy that receives a request with its own Record-Route URI in the Request-URI repairs it. It moves the last Route value back into the Request-URI:
“If the Request-URI of the request contains a value this proxy previously placed into a Record-Route header field (see Section 16.6 item 4), the proxy MUST replace the Request-URI in the request with the last value from the Route header field, and remove that value from the Route header field.”Read the section ↗
So every modern proxy must put lr in its Record-Route URI. A missing lr makes every UA use strict routing for the whole dialog (see Common mistakes).
“The URI placed in the Record-Route header field value MUST be a SIP or SIPS URI. This URI MUST contain an lr parameter (see Section 19.1.1).”Read the section ↗
10.7 Outbound proxy and pre-loaded Route
A phone usually sends its requests to a proxy of its provider, its outbound proxy, whatever the destination. RFC 3261 recommends that the phone holds the outbound proxy as a pre-loaded Route — a route set of one URI that it uses for requests outside a dialog:
“When a provider wishes to configure a UA with an outbound proxy, it is RECOMMENDED that this be done by providing it with a pre-existing route set with a single URI, that of the outbound proxy.”Read the section ↗
The Request-URI stays the real target, sip:bob@biloxi.example. The outbound proxy finds its own URI in Route, removes it, and routes on the Request-URI. Inside the dialog, the phone uses the route set from Record-Route, not its outbound proxy. An outbound proxy that does not record-route drops out of the call:
“In particular, a UAC configured with an outbound proxy SHOULD attempt to send the request to the location indicated in the first Route header field value instead of adopting the policy of sending all messages to the outbound proxy. This ensures that outbound proxies that do not add Record-Route header field values will drop out of the path of subsequent requests.”Read the section ↗
Call flow · outbound proxy with a pre-loaded Route
Outbound proxy and pre-loaded Route
- SIP
Alice's phone has one pre-loaded Route: its outbound proxy. The Request-URI stays Bob's AOR.
All steps as text
- Alice → Proxy A: INVITE. Alice's phone has one pre-loaded Route: its outbound proxy. The Request-URI stays Bob's AOR.
- Proxy A → Proxy B: INVITE. Proxy A removes its own Route entry. No Route is left, so it routes on the Request-URI: biloxi.example.
- Proxy B → Bob: INVITE. Proxy B adds Record-Route and replaces the Request-URI with Bob's Contact.
- Bob → Proxy B: 200 OK. Bob answers. The Record-Route names only Proxy B.
- Proxy B → Proxy A: 200 OK. Proxy B removes its Via and sends the 200 OK to Proxy A.
- Proxy A → Alice: 200 OK. Responses follow Via, so Proxy A still sees the 200 OK. Alice's route set is Proxy B only.
- Alice → Proxy B: ACK. Alice sends the ACK to the first Route, Proxy B, not to her outbound proxy.
- Proxy B → Bob: ACK. Proxy B removes its Route entry and forwards the ACK to Bob.
Many phones have an “outbound proxy” setting that instead sends every request to one address, with no pre-loaded Route. RFC 3261 allows this, but does not recommend it. Then the requests inside the dialog also go to the outbound proxy, even when it is not in the route set, and it must forward them by their Route headers.
10.8 Request-URI and To: why they can be different
The first INVITE starts with the same URI in the Request-URI and in To:
“The initial Request-URI of the message SHOULD be set to the value of the URI in the To field.”Read the section ↗
After that, they separate. To names the user that the caller wanted, and no proxy changes it. The Request-URI names where the request goes now, and each proxy can replace it:
“The To header field first and foremost specifies the desired "logical" recipient of the request, or the address-of-record of the user or resource that is the target of this request. This may or may not be the ultimate recipient of the request.”Read the section ↗
“The Request-URI in the copy's start line MUST be replaced with the URI for this target.”Read the section ↗
| Where | Request-URI | To |
|---|---|---|
| Alice’s INVITE | sip:bob@biloxi.example |
Bob <sip:bob@biloxi.example> |
| After Proxy B (location service) | sip:bob@203.0.113.20:5060 — Bob’s Contact |
unchanged |
| After call forwarding to Carol | sip:carol@biloxi.example |
unchanged: still Bob |
| A BYE inside the dialog | The remote target (Bob’s Contact) | Bob, with his tag |
| A request to a strict router | The strict router’s URI (10.6) | unchanged |
Route on the Request-URI, never on To. A proxy or B2BUA that routes on To sends a forwarded call back to the original user, and a UAS that rejects a request because To is not its own address breaks call forwarding and support lines.
“If no strict-routing elements are encountered on the path of the request, the Request-URI will always indicate the target of the request.”Read the section ↗
10.9 Loops, spirals, Max-Forwards, 482, 483
A request can reach the same proxy twice. Whether that is an error depends on what changed:
“A request that arrives at a proxy, is forwarded, and later arrives back at the same proxy. When it arrives the second time, its Request-URI is identical to the first time, and other header fields that affect proxy operation are unchanged, so that the proxy would make the same processing decision on the request it made the first time.”Read the section ↗
“A spiral is a SIP request that is routed to a proxy, forwarded onwards, and arrives once again at that proxy, but this time differs in a way that will result in a different processing decision than the original request. Typically, this means that the request's Request-URI differs from its previous arrival. A spiral is not an error condition, unlike a loop.”Read the section ↗
A spiral is normal. Bob’s phone forwards the call to Carol: it sends the INVITE back to Proxy B with the Request-URI sip:carol@biloxi.example. Proxy B sees the request a second time, but with a new target. A loop is a fault, usually two routing tables that point at each other. A proxy that checks for loops looks for its own address in the Via headers:
“If the request contains a Via header field with a sent-by value that equals a value placed into previous requests by the proxy, the request has been forwarded by this element before. The request has either looped or is legitimately spiraling through the element.”Read the section ↗
“If a loop is detected, the element MAY return a 482 (Loop Detected) response.”Read the section ↗
Loop detection is optional. The safety net that always works is Max-Forwards: the UA starts at 70, every proxy decrements it, and the element that receives 0 stops the request.
“If the copy contains a Max-Forwards header field, the proxy MUST decrement its value by one (1).”Read the section ↗
“If the request contains a Max-Forwards header field with a field value of zero (0), the element MUST NOT forward the request. If the request was for OPTIONS, the element MAY act as the final recipient and respond per Section 11. Otherwise, the element MUST return a 483 (Too many hops) response.”Read the section ↗
A low Max-Forwards also works like traceroute: a request with Max-Forwards 1 is stopped at the second hop. A B2BUA starts new requests with only its own Via, so Via-based loop detection cannot see through it. It should carry Max-Forwards across, one lower (RFC 7332). If it resets Max-Forwards to 70, a loop through two B2BUAs does not stop (Module 25).
Loop: 482
A loop between two proxies: 482
- SIP
- ⚠Problem
Alice calls a phone number. Her phone sends the INVITE to Proxy A.
All steps as text
- Alice → Proxy A: INVITE. Alice calls a phone number. Her phone sends the INVITE to Proxy A.
- Proxy A → Alice: 100 Trying. Proxy A answers 100 Trying on this hop.
- Proxy A → Proxy B: INVITE. Proxy A sends calls to +1 202 numbers to Proxy B. It adds its Via.
- Proxy B → Proxy A: 100 Trying. Proxy B answers 100 Trying on this hop.
- Proxy B → Proxy A: INVITE. Proxy B has a wrong route for this number: Proxy A. The Request-URI does not change. Problem: The same request comes back to Proxy A. Without a check, it would go round until Max-Forwards reaches 0.
- Proxy A → Proxy B: 482 Loop Detected. Proxy A finds its own address in a Via, and nothing that affects routing has changed. That is a loop.
- Proxy B → Proxy A: ACK. Proxy B ACKs the 482 on this hop, with the branch of its INVITE.
- Proxy B → Proxy A: 482 Loop Detected. Proxy B forwards the 482 back along the Via headers.
- Proxy A → Proxy B: ACK. Proxy A ACKs the 482 on this hop.
- Proxy A → Alice: 482 Loop Detected. The 482 reaches Alice. The call fails, and the misrouted number shows in the logs.
- Alice → Proxy A: ACK. Alice's phone ACKs the 482.
10.10 Double Record-Route on multi-homed proxies
A multi-homed proxy has interfaces in two networks — for example, 10.1.0.1 on an office LAN and 198.51.100.10 on the Internet. One Record-Route URI can hold only one address, and each side can reach only one of them. RFC 3261 lets the proxy rewrite its Record-Route value in the response, so each side sees a different URI:
“If the selected response contains a Record-Route header field value originally provided by this proxy, the proxy MAY choose to rewrite the value before forwarding the response. This allows the proxy to provide different URIs for itself to the next upstream and downstream elements. A proxy may choose to use this mechanism for any reason. For instance, it is useful for multi-homed hosts.”Read the section ↗
RFC 5658 recommends a simpler way. The proxy adds two Record-Route values, one for each interface. Nothing changes in the response, and both sides keep the same list:
“Therefore, this document recommends using the double Record-Route approach to avoid rewriting the Record-Route.”Read the section ↗
“This technique consists of inserting before any existing Record-Route header, a Record-Route header with the URI reflecting to the input interface, including schemes and/or URI parameters, and secondly, a Record-Route header with the URI reflecting to the output interface.”Read the section ↗
Each side’s route set now has both URIs, and the reachable one comes first: Bob gets the outside address first, Alice the inside address. When a request arrives, the proxy finds itself in both top Route values and removes both:
“So, this means, in Section 16.4, "Route Information Preprocessing" [RFC3261], implementors can choose that a proxy MAY remove two Route headers instead of one when using the double Record-Routing.”Read the section ↗
Call flow · double Record-Route
Fixed: double Record-Route
- SIP
Alice's outbound proxy is Proxy A, on its inside address 10.1.0.1.
All steps as text
- Alice → Proxy A: INVITE. Alice's outbound proxy is Proxy A, on its inside address 10.1.0.1.
- Proxy A → Proxy B: INVITE. Proxy A adds two Record-Routes: the outside address on top, then the inside address.
- Proxy B → Bob: INVITE. Proxy B forwards the INVITE to Bob's Contact. It does not record-route.
- Bob → Proxy B: 200 OK. Bob answers. His route set is the Record-Route list in order: outside address first.
- Proxy B → Proxy A: 200 OK. Proxy B removes its Via and forwards the 200 OK.
- Proxy A → Alice: 200 OK. Alice's route set is the list reversed: inside address first. Both sides reach Proxy A.
- Alice → Proxy A: ACK. Alice sends the ACK to the inside address of Proxy A.
- Proxy A → Bob: ACK. Proxy A removes both of its Route entries and sends the ACK to Bob.
- Bob → Proxy A: BYE. Bob hangs up. The BYE goes to the first Route: the outside address, 198.51.100.10.
- Proxy A → Alice: BYE. Proxy A removes both of its Route entries and sends the BYE out of its inside interface.
- Alice → Proxy A: 200 OK. Alice ends the call.
- Proxy A → Bob: 200 OK. Proxy A forwards the 200 OK to Bob.
Open-source proxies such as Kamailio and OpenSIPS add the second Record-Route value by themselves when a request leaves on a different interface or transport (the enable_double_rr setting of their rr modules).
Common mistakes
Confusing Via with Record-Route
Both headers list proxies, so they are easy to mix up. They answer different questions:
| Via | Record-Route | |
|---|---|---|
| Answers | Where does the response to this request go? | Which proxies see the later requests in the dialog? |
| Who adds it | Every element that sends the request, always | Only proxies that want to stay, only on the request that creates the dialog |
| Lifetime | One transaction | The whole dialog |
| Used by | Responses, hop by hop | The route set, then Route in each later request |
Remember: a 200 OK passes every proxy, because it follows Via. That does not mean the BYE will. To predict the BYE, read the Record-Route of the 200 OK, not its Vias.
A proxy that does not Record-Route never sees the BYE
The proxy sees the INVITE and the 200 OK, starts a call record, and never hears of the call again. The usual symptoms: billing records with no end time or with a fixed maximum duration, calls that a dialog-stateful proxy counts as active long after they ended, and requests to a phone behind NAT that never reach it.
Broken
Broken: no Record-Route, so the BYE skips Proxy B
- SIP
- ⚠Problem
Alice calls Bob. Proxy B, the billing proxy of biloxi.example, gets the INVITE.
All steps as text
- Alice → Proxy B: INVITE. Alice calls Bob. Proxy B, the billing proxy of biloxi.example, gets the INVITE.
- Proxy B → Bob: INVITE. Proxy B starts a call record and forwards the INVITE. It does not add Record-Route.
- Bob → Proxy B: 200 OK. Bob answers.
- Proxy B → Alice: 200 OK. Proxy B forwards the 200 OK. Alice's route set is empty.
- Alice → Bob: ACK. Alice sends the ACK straight to Bob's Contact. Proxy B does not see it.
- Alice → Bob: BYE. After 3 minutes, Alice hangs up. The BYE goes straight to Bob. Problem: Proxy B never sees the BYE. Its call record stays open, and the bill shows the wrong duration.
- Bob → Alice: 200 OK. Bob ends the call. Proxy B still counts minutes.
A Record-Route without ;lr
The proxy works for the first INVITE. But every UA now uses strict routing for the whole dialog: the proxy’s URI goes into the Request-URI, and the remote target goes to the end of Route. An RFC 3261 proxy that recognises its own URI repairs the request. A proxy that does not — for example, one behind a load balancer whose address it writes into Record-Route — routes the BYE by its Request-URI: back to itself, or to the load balancer. The call never ends cleanly.
Broken
Broken: Record-Route without lr
- SIP
- ⚠Problem
Alice calls Bob. Proxy B, the billing proxy of biloxi.example, gets the INVITE.
All steps as text
- Alice → Proxy B: INVITE. Alice calls Bob. Proxy B, the billing proxy of biloxi.example, gets the INVITE.
- Proxy B → Bob: INVITE. Proxy B starts a call record and adds Record-Route, but the URI has no lr parameter. Problem: RFC 3261 requires lr in every Record-Route URI. Without it, in-dialog requests use strict routing.
- Bob → Proxy B: 200 OK. Bob answers. The 200 OK copies the Record-Route.
- Proxy B → Alice: 200 OK. Alice's route set is Proxy B, and its URI has no lr. Alice must use strict routing.
- Alice → Proxy B: ACK. Strict routing: Proxy B goes in the Request-URI, and Bob's Contact goes last in Route.
- Proxy B → Bob: ACK. Proxy B finds its own URI in the Request-URI. It moves the last Route value back into the Request-URI.
- Alice → Proxy B: BYE. After 3 minutes, Alice hangs up. The BYE follows the route set to Proxy B.
- Proxy B → Bob: BYE. Proxy B repairs the BYE again and closes the call record. A proxy that does not recognise its URI fails here. Problem: This works only if Proxy B recognises its own Record-Route URI. Many elements and B2BUAs do not.
- Bob → Proxy B: 200 OK. Bob ends the call.
- Proxy B → Alice: 200 OK. Proxy B forwards the 200 OK to Alice.
Remember: look at the Request-URI of the in-dialog requests. A proxy URI there, and the Contact at the end of Route, means strict routing — find the Record-Route URI without lr.
One Record-Route on a proxy with two interfaces
The proxy writes the address of one interface into Record-Route. The UA on the other side cannot reach that address, so its in-dialog requests are lost. Calls set up fine, then the BYE from one side never arrives — often only when that side hangs up.
Broken
Broken: one Record-Route on two interfaces
- SIP
- ⚠Problem
Alice's outbound proxy is Proxy A, on its inside address 10.1.0.1.
All steps as text
- Alice → Proxy A: INVITE. Alice's outbound proxy is Proxy A, on its inside address 10.1.0.1.
- Proxy A → Proxy B: INVITE. Proxy A adds one Record-Route with its inside address, 10.1.0.1. The INVITE leaves on the outside interface.
- Proxy B → Bob: INVITE. Proxy B forwards the INVITE to Bob's Contact. It does not record-route.
- Bob → Proxy B: 200 OK. Bob answers. His route set is one URI: 10.1.0.1, a private address.
- Proxy B → Proxy A: 200 OK. Proxy B removes its Via and forwards the 200 OK.
- Proxy A → Alice: 200 OK. Alice's route set is 10.1.0.1. That works for her: she is on the inside.
- Alice → Proxy A: ACK. Alice sends the ACK to the inside address of Proxy A.
- Proxy A → Bob: ACK. Proxy A removes its Route entry and sends the ACK to Bob.
- Bob → Proxy A: BYE. Bob hangs up. His route set says 10.1.0.1: Proxy A's inside address. Problem: 10.1.0.1 is a private address. No router on the Internet can deliver the BYE.
- Bob → Proxy A: BYE (again). Bob's phone sends the BYE again for 32 seconds, then ends the dialog. Problem: Alice's phone still shows the call. Proxy A never sees the BYE.
Summary
- Responses follow Via. Each element that sends a request adds a Via on top; each element that forwards the response removes its own.
- Requests follow Route, then the Request-URI. Each proxy removes its own Route value and may replace the Request-URI.
- Behind NAT, the next hop adds received and fills in rport. A UDP response goes to
received:rport. - A proxy that adds Record-Route to the request that creates the dialog stays in the path of every later request. Without it, the ACK, BYE, and re-INVITE skip the proxy.
- The callee takes the route set from Record-Route in order; the caller reverses it. Each side has its nearest proxy first.
- Every Record-Route URI needs
;lr. Without it, the UAs use strict routing, and the request works only if the proxy repairs it. - An outbound proxy is best set as a pre-loaded Route. It stays in the dialog only if it record-routes.
- To keeps the user the caller wanted; the Request-URI is where the request goes now. Route on the Request-URI.
- A spiral is normal; a loop is a fault. Loop detection gives 482; Max-Forwards at 0 gives 483.
- A multi-homed proxy adds two Record-Route values, one for each interface, so each side can reach it.