Brilliantly, and quite eloquently detailed Jan. Thanx!
FYI, I spent a *looong* time flailing around with Quintium hardware
trying to get it to do the *correct* thing (and it was for a three
person account too!)
Finally gave up in disgust, depression and despair.
Mind you, this was just before I discovered that one of our major
providers was switching to Sonus.
Sonus, I ask you.
Yikes....
Jan Janak wrote:
OK, I think I know where the confusion comes from, it
is like this:
When a user agent sends a SIP request, the request will traverse one ore
more SIP proxies until it reaches the target user agent. Each proxy and
user agent along the path of the request will add its Via header field.
The purpose of Via header fields is to make sure that all replies will
traverse exactly the same set of proxies (but in reverse order) as the
request. In other words Via header fields are used to route _replies_.
They record the path of the request so that replies can follow it.
Route and Record-Route header fields have a slightly different purpose
-- they are used to route _requests_. Let's assume this scenario:
INV INV
UA A --------> proxy ----------> UA B
a(a)1.2.3.4 b(a)5.6.7.8
User agent A with IP address 1.2.3.4 sends an INVITE request to the
proxy which forwards the request to user agent B. UA A adds the Contact
header field in the request. The Contact header field tells UA B that it
can reach UA A on IP 1.2.3.4. UA B adds a Contact header field to 200 OK
reply, telling UA A that it can reach B on IP address 5.6.7.8.
This way both user agents exchange their IP addresses and they do not
need the proxy anymore. They can easily send all further SIP messages
directly to each other, because they remember the IP address of the
remote party from Contact header field. This way all further requests
would bypass the proxy server.
There are many cases where the proxy server needs to see _all_ future
SIP messages exchanged between the user agent (for example when
performing accounting). In this case the proxy server needs tell the
user agents that they should not exchange future SIP messages directly,
but they should relay them through the proxy again.
The proxy server can do this by inserting Record-Route header field in
the INVITE message. The Record-Route header field contains the IP
address of the proxy and once the INVITE message reaches UA B, it extracts
the IP address of the proxy server from Record-Route header field and
store it in memory along with the IP address of the remote party (UA A).
So UA B knows the IP address of UA A and it also knows that it should
send all future requests to UA A through the proxy server.
UA A should also send all future SIP requests to UA B through the proxy
server, but it does not know it yet, because the proxy server added
Record-Route header field to the INVITE message (which will only reach
UA B). UA B has to tell UA A that all future SIP requests should be sent
through the proxy, and it does so by copying all Record-Route header
fields (in our example there will be only one) from the INVITE message to
200 OK (which is sent from UA B to UA A). UA A will then extract
Record-Route header fields along with Contact from 200 OK and store it
in memory. At this point both user agents know the IP address of the
remote party and that they should relay all SIP messages through the
proxy.
Now what happens when UA A wants to send an ACK to UA B. It will lookup
the IP address of the remote party (Contact from 200 OK) from memory and put
it in the Request-URI of ACK. The reason why ACK does not have the same
Request-URI as the original INVITE is that the ACK should be sent to the
user agent instance that generated 200 OK -- the Request-URI from the
INVITE would fork if user B had several user agents and this is not
desirable for ACKs. The URI based on Contact header field never forks,
it is delivered only to the UA instance that generated 200 OK.
UA A will also find the URI from the Record-Route header field in memory
(stored along with the contact of the remote party). It will create a
Route header field, put the URI in it and append it to the ACK.
RFC3261 says that if there is a Route header field in a SIP message,
then the message should not be sent to the URI in the Request-URI, but
to the URI in Route header field and thus the ACK would be sent to the
proxy. The proxy will then remove the Route header field and because
there is no other Route header field in our message, it will forward the
request to the Request-URI which will take the message to UA B.
Note the difference between Record-Route and Route header fields.
Record-Route header fields tell SIP user agent and proxies where
_future_ SIP requests should be sent. Route header field are constructed
from Record-Route header fields and they tell user agent and proxies
where the request that contains the Route header fields should be sent.
Two header field names are needed to avoid confusion, because a proxy
server can add Record-Route header field to _any_ SIP request, including
requests that already contain Route header fields (using the same name
would mix them).
The relationship between the two user agents (when they remember the IP
address of the remote party and IPs of proxies) is called dialog. SIP
requests that are forwarded using the information extracted from
Contacts and Record-Route header fields are called "SIP requests within
dialog" (typically ACK and BYE).
When a user agent sends a SIP request within a dialog, it will always
put the Contact of the remote party in the Request-URI and copy all URIs
extracted from Record-Route header fields in Route header fields (in
either forward or reverse order, based on the direction):
ACK sip:b@5.6.7.8 SIP/2.0
Route: sip:proxy@proxy.ip;lr
...
This is how all recent SIP implementations should behave. Always put the
Contact of the remote party into the Request-URI and list all proxies that
inserted Record-Route in Route header fields appended to the message.
Plain and simple.
Unfortunately there are also older SIP implementations (from pre-RFC3261
era) that need special care, because record-routing worked differently
then. In this case the Record-Route does not contain the contact of
the remote party but the topmost Route header field, and the value of
the remote contact is preserved in the last Route header field in the
message. This "compatibility mode" is what makes record routing complex
and hard to understand, but you can ignore it (you can always find
details in RFC3261).
In conclusion:
1) Via is used to route responses
2) Route and Record-Route headers are used to route requests
3) Proxies use Record-Route to signal that they want to stay on the path
of future requests.
4) User agents use Record-Route headers to build Route headers.
5) Contact of the remote party is always put in the Request-URI of
requests in dialogs.
6) The list of Route header field is created from the list of
Record-Route header fields.
7) The URI in the topmost Route header field overrides the URI in the
Request-URI, so the Request-URI will only be used if there are no
more Route header fields.
Jan.
PS: When talking to Quintum guys, you should ask them to implement what
I just described:
1) Take the Contact header field from 200 OK and put it in the
Request-URI of ACK.
2) Take all Record-Route header fields from 200 OK, reverse their
order, and append them as Route header fields to the ACK.
3) Send the ACK to the topmost Route, if any, otherwise send to the
Request-URI.