Subject: [Sip] The right solution to sip routing

From: Robert Sparks <[email-masked]>
Date: Wed, 30 Jan 2002 01:41:54 -0500

Folks -

In the process of studying the potential problem points with
loose-routing as we've defined it so far, we've discovered
that a slight change in the way we look at the problem
has a high yeild.

Basically, we want the Route mechanism to be right. We know
we've long had an issue with the strict-routing mechanism
in that it conflates the description of the next hop and
the intended target - this is the source of all the pain
with defining restrictions on loose routing such that
reasonable things happen when strict-routers are part
of the system. It is also the source of the convoluted
proxy record-route/route processing we've had to date,
including rewriting the RR URI so that it identifies the
intended target in both directions and the odd RR/Contact
munging that existed from bis-01 to -03. If we separate
identifying the target from specifying the next hop, all 
those things go away.

The change of approach is to look at what the sip routing
mechanism would be if we had a clean slate.  Find what
the real "right thing" to do is, and then work in
as much backwards compatibility with existing strict
routing systems as is reasonable.

So what is that right thing?

What we want is for the Request-URI to represent the target
of the request and the Route header field to indicate resources
to visit on the path to that target. Instead of moving values
from the route header field into the Request-URI, proxies would use
the topmost route header field value to figure out where to send
the request (using the normal address/port/transport selection
mechanisms applied to that URI). Once the resource that is 
indicated by that topmost route header value is reached, 
then (and only then) that value is removed.

More precisely, a proxy would execute the following steps in order:

 1) Inspect the Request-URI, if it indicates a resource owned
    by this proxy, run a location service and rewrite the R-URI
    with the results. Otherwise, leave the R-URI alone.

 2) Inspect the topmost Route header field value - if it
    indicates this element, that route node as been reached
    and it is removed.

 3) Send the request to the resource in the topmost
    Route header field value using the normal address/port/transport
    resolution applied to that value (or using the Request-URI if 
    the Route header field has been exhausted).

Variations on those steps that meet the requirements in the 
looseroute proposal are valid, but its safe to say that the 
above represents the typical scenario.

If every element behaves this way, the Request-URI is never 
overwritten with a Route header value, and Route header values 
are removed when the element is reached. This last point is 
pretty important. Only the node that the URI in that value 
indicates can truly know that the node has been reached. 
That URI can carry information critical to the processing 
at that node.

Proxy record-routing becomes much simpler. The reason it's 
complex today is that the URI needs to both identify the 
proxy AND the ultimate recipient. With the right approach, 
it should just identify a service at that proxy. If there 
was only one service, the proxy could record-route with 
just its own address, which could be as simple as sip:1.2.3.4. 
In most cases, there would be no need to rewrite this in 
the response.

Also, working with a clean slate, we would have UAs behave
differently than they currently do when they have a route set.
UAs should always place the target of the request in the
Request-URI. So, given a preloaded route, the RURI of the
initial request would indicate the resource the UA was trying
to reach, and the Route header field would contain elements
to be visited on the way. When a route set is established
by receiving a response with a Record-Route header field,
the UA puts the Contact from that response into the RURI of
future request, NOT into Route header field as the last element.
This way, the RURI indicates the real target of the request,
and the Route header field represents elements that need to be
visited to get there.

So - if we could snap our fingers and have every deployed element
do the above "right thing", we'ld be in great shape - unfortunately
we have some deployed elements that will take time to change.
What is their impact and what can we reasonably do to work
with them?

Encountering a strict-router while processing a request with
preloaded routes will destroy the target information carried
in the RURI. This is a hard and fast property of the way
strict-routing is defined, and we cannot work around it without
defining a new place to carry the "real" Request-URI. (Call it
a Request-URI: header field for the sake of argument.) This
is a terrible thing to do to the semantics of the protocol
just to preserve backwards compatibility with elements doing
something we find broken. The better approach is to bite the
bullet, define the "right thing" as above, and live with
the fact that encountering legacy strict-routers in the
particular case of processing an initial request with a
preloaded route will produce unpredictable results.

However, we can assure that the most common usage of route
headers, mid-dialog routing based on URIs learned in 
record-route/contact, will remain interoperable.

Fortunately, we don't have real systems deployed that provide
preloaded routes. Anybody building such a thing needs to know
not to put strict-routing systems in it. People connecting
such systems need to know not to put strict-routers between
them. The case above is non-existant in deployed systems and
with care, we can keep its probability of ever occuring near
zero.

We can make the story better for backwards compatibility with
existing endpoints. If we add the following restriction to
loose-routing proxy behavior, requests created by current
endpoints building Route headers fields based on Record-Route
header fields and Contacts will do the right thing:

 New Restriction 1:
 * If a proxy receives a request with an RURI equal to a
   value it placed in a Record-Route header, it MUST replace
   that RURI with the first value in the Route header field.

This will cause the flow of these subsequent requests 
to effectively degenerate into the currently defined 
strict-routing behavior if a "new" proxy is in a routed path 
where upstream elements are "old" proxies or UAs. 

Now, we can make this even better by allowing elements that do
the "right thing" advertise that fact to other elements that
know what the right thing is. When such an element adds a value
to a Record-Route header field, have it include a ;lr parameter
in the value. When services create route sets to be used for
preloaded routes, have them include the ;lr parameter on each
value in that route set.

With this knowledge, when an element (especially a "right thing"
aware endpoint) is preparing to send a request to the topmost
element in the Route set, it can notice that it is about to run
into a chain of one or more strict-routing elements and can format
the request so that any information it is carrying in the RURI
is preserved. The mechanics of this are simple. If the request
to send would otherwise have contained:

  METHOD targetURI
  Route <strict1>,<strict2>,<right1;lr>,<right2;lr>

(For instance, this might have been formed by a UA that got
 the targetURI from the Contact of a 200 to an INVITE, and built
 the Route header field by flipping the values it recieved in a
 Record-Route header field).

the element can rebuild it to contain

  METHOD <strict1>
  Route <strict2>,<right1;lr>,<targetURI>,<right1;lr>,<right2;lr>

Or, in text form, the element puts the target URI into the
routeset after the first node indicating that it does
the "right thing", and follows it with another copy of that
nodes Route header value so that when that node gets the
request, it can use its normal processing (we'll see that in
action below).  If there is no element in the Routee header field
indicating that it will do the right thing, the element adds the
target URI in as the last Route header field value. 

This gives the strict router the RequestURI it is expecting 
(and is going to ignore) as well as the proper topmost Route 
header field value to cause the request to move along the right 
path. When the request gets to the element indicated by right1;lr, 
it will look like

  METHOD rightthing1;lr
  Route <targetURI>,<right1;lr>,<right2;lr>

According to New Restriction 1 above, the element at right1
would replace the RURI with the topmost Route header field,
rendering

 METHOD targetURI
 Route <right1;lr><right2;lr>

which the element at right1 can then apply its normal
processing to.

Note that this degenerates to existing UA behaviors when all elements 
are strict.  When all elements understand this right thing, it results 
in the ideal behavior above, where Contact is in the URI, and the 
record-route headers are in the route headers. When there is a mix of 
proxies and UAs, the R-URI will have meaning for each element understanding
how to do the right thing.

I think this gives us the best story we're going to get.

Here's a summary of what would change in bis:
* Add the New Restriction 1 above 
* Define the ;lr parameter and when to include it 
* Add the above description of what we expect most proxy 
  processing to be, emphasising the importance of having 
  the element indicated by the topmost route header field 
  value remove that value.
* Simplify the proxy record-routing procedures 
* Add the above behavior for new UAs constructing requests from
  Route-sets (putting the Contact element from the routeset
  into the Request-URI instead of the last Route header field
  value.
* Require all elements that know how to do this right thing to
  perform the manipulation above to get around a string of
  strict routing elements when they are encountered.

The intent will be to gradually deprecate existing strict-routing.
In the same vein, the uses of maddr that have evolved to try to
get around some of the strict routing problems will be deprecated.
Proxies implementing this new approach have no need to (MUST NOT)
use maddr in any values they supply in Record-Route header fields.

Here's an example flow that Jonathan worked out while we were
discussing this:

The scenario is the sip trapezoid, U1 -> P1 -> P2 -> U2, with both 
proxies record-routing. Here is the flow.

U1 sends:

INVITE sip:[email-masked] SIP/2.0
Contact: sip:[email-masked]

to P1. P1 is an outbound proxy. Following above rules, it isn't
domain.com, and there are no Route headers, so it looks it up in DNS and
sends there. It also record-routes:

INVITE sip:[email-masked] SIP/2.0
Contact: sip:[email-masked]
Record-Route: <sip:p1.example.com;lr>

P2 gets this. It owns domain.com. It runs a location service and
rewrites the URI. There are no route headers, so it sends to the result
of the location lookup. It also record-routes with its addres:

INVITE sip:[email-masked] SIP/2.0
Contact: sip:[email-masked]
Record-Route: <sip:p2.domain.com;lr>
Record-Route: <sip:p1.example.com;lr>

The callee at u2 gets this. It responds with a 200 OK. That 200 OK looks
like:

SIP/2.0 200 OK
Contact: sip:[email-masked]
Record-Route: <sip:p2.domain.com;lr>
Record-Route: <sip:p1.example.com;lr>

This is forwarded by P2 to P1 to U1 as normal. Now, U1 will construct
the target, which is sip:[email-masked], and a route set, which is
sip:p1.example.com;lr, sip:p2.example.com;lr. Since all route hops are
new, it constructs the following for the BYE:

BYE sip:[email-masked] SIP/2.0
Route: <sip:p1.example.com;lr>
Route: <sip:p2.example.com;lr>

As any other element (including proxies) would do, it sends this request
by looking up the top route in DNS, and sending there. This goes to P1.
P1 follows same processing as before. The domain in the R-URI is not its
own. So, it doesn't run location service, leaves r-uri alone. There is a
route, which is its own, so its popped. THe next route is there, and its
looked up in dns and the resulting address is used to forward the
request to P2:

BYE sip:[email-masked] SIP/2.0
Route: <sip:p2.example.com;lr>

Now, P2 looks at this. The R-URI is NOT ITS DOMAIN, since u2.domain.com
is a specific host. So, it does not run location service. Like P1, it
notices the top route is its own, so its popped. There is no further
route, so the r-uri is looekd up in dns, and that is used to forward.
The following is then sent to u2:

BYE sip:[email-masked] SIP/2.0

Voila! Everything works very nicely.

_______________________________________________
Sip mailing list  https://www1.ietf.org/mailman/listinfo/sip
This list is for NEW development of the core SIP Protocol
Use [email-masked] for questions on current sip
Use [email-masked] for new developments on the application of sip

---

From: Robert Sparks <[email-masked]>
Date: Thu, 31 Jan 2002 12:04:13 -0500

Vijay Gurbani pointed out an error in the example at the end
of this message - the correction is below.

> -----Original Message-----
> From: Robert Sparks 
> Sent: Wednesday, January 30, 2002 12:42 AM
> To: [email-masked]'
> Subject: The right solution to sip routing
> 
> 
> Folks -
> 

Of course, this should have been

 BYE sip:[email-masked] SIP/2.0
 Route: <sip:p1.example.com;lr>
 Route: <sip:p2.domain.com;lr>

 BYE sip:[email-masked] SIP/2.0
 Route: <sip:p2.domain.com;lr>