RFC 5627

Obtaining and Using Globally Routable User Agent URIs (GRUUs) in the Session Initiation Protocol (SIP)

๐Ÿ“… published October 2009, from ietf

Obtaining and Using Globally Routable User Agent URIs (GRUUs) in the Session Initiation Protocol (SIP) โœจ

Okay bestie, welcome to RFC 5627 ๐ŸŽ€ (Part 1 of 9!). This one was written by J. Rosenberg from Cisco Systems, and it's a Standards Track document, so it's an official Internet standards spec ๐Ÿ‘‘. It's dated October 2009, and it's part of the IETF's Network Working Group ๐Ÿฆ‹.

Abstract ๐Ÿ’…

Okay so, a bunch of SIP apps need a user agent (UA) to build a URI that literally anyone on the Internet can use to route a call to that one specific UA instance ๐Ÿงš. That kind of URI is called a Globally Routable UA URI (GRUU), and it's giving main character energy โœจ. This doc describes a SIP extension for getting a GRUU from a registrar, and for passing a GRUU to a peer inside a dialog ๐Ÿ’–.

Status of This Memo ๐Ÿ“œ

This document specifies an Internet standards track protocol for the Internet community, and requests discussion and suggestions for improvements. Please refer to the current edition of the "Internet Official Protocol Standards" (STD 1) for the standardization state and status of this protocol. Distribution of this memo is unlimited.

Copyright (c) 2009 IETF Trust and the persons identified as the document authors. All rights reserved.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the BSD License.

This document may contain material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. The person(s) controlling the copyright in some of this material may not have granted the IETF Trust the right to allow modifications of such material outside the IETF Standards Process. Without obtaining an adequate license from the person(s) controlling the copyright in such materials, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process, except to format it for publication as an RFC or to translate it into languages other than English.

Table of Contents ๐Ÿงš

1.  Introduction . . . . . . . . . . . . . . . . . . . . . . . . .  4
2.  Terminology  . . . . . . . . . . . . . . . . . . . . . . . . .  5
3.  Overview of Operation  . . . . . . . . . . . . . . . . . . . .  5
  3.1.  Structure of GRUUs . . . . . . . . . . . . . . . . . . . .  5
    3.1.1.  GRUUs That Expose the Underlying AOR . . . . . . . . .  6
    3.1.2.  GRUUs That Hide the Underlying AOR . . . . . . . . . .  6
  3.2.  Obtaining a GRUU . . . . . . . . . . . . . . . . . . . . .  7
  3.3.  Using a GRUU . . . . . . . . . . . . . . . . . . . . . . .  8
  3.4.  Dereferencing a GRUU . . . . . . . . . . . . . . . . . . .  8
4.  User Agent Behavior  . . . . . . . . . . . . . . . . . . . . .  9
  4.1.  Generating a REGISTER Request  . . . . . . . . . . . . . .  9
  4.2.  Learning GRUUs from REGISTER Responses . . . . . . . . . . 10
  4.3.  Constructing a Self-Made GRUU  . . . . . . . . . . . . . . 11
  4.4.  Using One's Own GRUUs  . . . . . . . . . . . . . . . . . . 12
    4.4.1.  Considerations for Multiple AORs . . . . . . . . . . . 13
  4.5.  Dereferencing a GRUU . . . . . . . . . . . . . . . . . . . 14
  4.6.  Rendering GRUUs on a User Interface  . . . . . . . . . . . 14
5.  Registrar Behavior . . . . . . . . . . . . . . . . . . . . . . 14
  5.1.  Processing a REGISTER Request  . . . . . . . . . . . . . . 14
  5.2.  Generating a REGISTER Response . . . . . . . . . . . . . . 16
  5.3.  Timing Out a Registration  . . . . . . . . . . . . . . . . 16
  5.4.  Creation of a GRUU . . . . . . . . . . . . . . . . . . . . 17
  5.5.  Registration Event Support . . . . . . . . . . . . . . . . 19
6.  Proxy Behavior . . . . . . . . . . . . . . . . . . . . . . . . 19
  6.1.  Request Targeting  . . . . . . . . . . . . . . . . . . . . 19
  6.2.  Record-Routing . . . . . . . . . . . . . . . . . . . . . . 21
7.  Grammar  . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
8.  Requirements . . . . . . . . . . . . . . . . . . . . . . . . . 23
9.  Example Call Flow  . . . . . . . . . . . . . . . . . . . . . . 24
10. Security Considerations  . . . . . . . . . . . . . . . . . . . 29
  10.1. Outside Attacks  . . . . . . . . . . . . . . . . . . . . . 29
  10.2. Inside Attacks . . . . . . . . . . . . . . . . . . . . . . 30
  10.3. Privacy Considerations . . . . . . . . . . . . . . . . . . 31
11. IANA Considerations  . . . . . . . . . . . . . . . . . . . . . 33
  11.1. Header Field Parameter . . . . . . . . . . . . . . . . . . 33
  11.2. URI Parameter  . . . . . . . . . . . . . . . . . . . . . . 33
  11.3. SIP Option Tag . . . . . . . . . . . . . . . . . . . . . . 33
12. Acknowledgments  . . . . . . . . . . . . . . . . . . . . . . . 34
13. References . . . . . . . . . . . . . . . . . . . . . . . . . . 34
  13.1. Normative References . . . . . . . . . . . . . . . . . . . 34
  13.2. Informative References . . . . . . . . . . . . . . . . . . 35
Appendix A.  Example GRUU Construction Algorithms  . . . . . . . . 37
  A.1.  Public GRUU  . . . . . . . . . . . . . . . . . . . . . . . 37
  A.2.  Temporary GRUU . . . . . . . . . . . . . . . . . . . . . . 37
Appendix B.  Network Design Considerations . . . . . . . . . . . . 39

1. Introduction ๐Ÿฆ‹

Okay so in the Session Initiation Protocol (SIP), defined in RFC 3261 [1], the core thing you reference is the Address of Record (AOR). Here's the tea though: one user can have a whole bunch of user agents (handsets, softphones, voicemail accounts, and more) that all hang off the same AOR ๐Ÿ’…. There are plenty of situations where you really want an identifier that points at one user agent instead of the whole group an AOR covers ๐ŸŽ€.

Let's look at a blind transfer (see RFC 5589 [19]) ๐Ÿ“ž. User A is talking to user B, and User A wants to move the call over to user C. So user A sends a REFER to user C. In part, that REFER looks like this:

REFER sip:C@example.com SIP/2.0
From: sip:A@example.com;tag=99asd
To: sip:C@example.com
Refer-To: (URI that identifies B's UA)

The Refer-To header field has to contain a URI that user C can use to call user B. But bestie, that call needs to land on the specific UA instance user B is using to talk to user A ๐Ÿ˜ญ. If it doesn't, the transfer service won't run properly. For example, if A hands C B's AOR, the call might end up in B's voicemail instead of on B's current handset ๐Ÿ’€.

So to make this work, user B gives user A an instance-specific URI in the Contact header of their SIP exchange ๐Ÿซถ. That URI points at the UA B is currently using, and user C's user agent can dereference it. Here's the thing: user B has no idea ahead of time who user A is going to transfer the call to, so the URI has to be usable by anyone ๐ŸŒˆ.

A lot of current clients try to solve the instance-specific identifier problem by putting explicit IP addresses in the values they provide in the Contact header field. Honestly though, that clashes with NATs and firewalls, and in practice these URIs can't be used by arbitrary outside clients ๐Ÿ˜ฉ. Using hostnames has run into similar trouble. Plus, lots of SIP clients don't have a hostname for themselves at all, or can't get one ๐Ÿ™ˆ.

This specification describes a mechanism for providing a unique user-agent identifier that is still globally routable. That identifier is called a Globally Routable User Agent (UA) URI (GRUU) โœจ.

2. Terminology ๐Ÿ“

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 [4].

This spec defines a few extra terms too, so let's get them down ๐Ÿ“.

contact: The term "contact", when used in all lowercase, refers to a URI that is bound to an AOR and GRUU by means of a registration. A contact is usually a SIP URI, and is bound to the AOR and GRUU through a REGISTER request by appearing as a value of the Contact header field. The contact URI identifies a specific UA ๐Ÿงš.

remote target: The term "remote target" refers to a URI that a user agent uses to identify itself for receipt of both mid-dialog and out-of-dialog requests. A remote target is established by placing a URI in the Contact header field of a dialog-forming request or response, and it is updated by target refresh requests or responses ๐Ÿฆ‹.

Contact header field: The term "Contact header field", with a capitalized C, refers to the header field that can appear in REGISTER requests and responses, redirects, or dialog-creating requests and responses. Depending on the semantics, the Contact header field sometimes conveys a contact, and sometimes conveys a remote target ๐Ÿ’œ.

3. Overview of Operation ๐ŸŒธ

The basic idea behind a GRUU is honestly simple ๐Ÿ˜Œ. GRUUs are issued by SIP domains, and they always route back to a proxy in that domain. The domain keeps track of the binding between the GRUU and the particular UA instance. When a GRUU gets dereferenced while a SIP request is being sent, that request arrives at the proxy. The proxy maps the GRUU to the contact for that particular UA instance, and sends the request there ๐ŸŽ€.

3.1. Structure of GRUUs ๐Ÿงฉ

A GRUU is a SIP URI with two properties:

  • It routes to a specific UA instance.
  • It can be successfully dereferenced by any user agent on the Internet, not just the ones in the same domain or IP network as the UA instance the GRUU points to.

In principle, a GRUU can be built any way the domain likes, as long as it meets the criteria above. But here's the vibe: all GRUUs contain the "gr" URI parameter (with or without a value), so that a recipient of a GRUU can tell it has these two properties ๐Ÿซถ.

In practice, there are two different types of GRUUs:

  1. GRUUs that expose the underlying AOR
  2. GRUUs that hide the underlying AOR

3.1.1. GRUUs That Expose the Underlying AOR ๐Ÿ”

In lots of cases, it's really useful to build the GRUU so the mapping to the AOR is obvious ๐Ÿ‘€. For example, many user agents keep call logs that track incoming and outgoing call attempts. If the UA made a call to a GRUU (maybe because of a transfer request), the call log will contain that GRUU. Since the call log gets rendered to the user, it would be handy to show the user the AOR instead, because the AOR actually means something to people ๐Ÿ’–.

This type of GRUU is called a public GRUU. It's built by taking the AOR and adding the "gr" URI parameter with a value chosen by the registrar in the domain. The value of the "gr" URI parameter contains a representation of the UA instance. For instance, if the AOR was sip:alice@example.com, the GRUU might be:

sip:alice@example.com;gr=kjh29x97us97d

If a UA removes the "gr" URI parameter, the result is the AOR. Since lots of systems ignore unknown parameters anyway, a public GRUU will "look" like the AOR to those systems ๐Ÿ™ˆโœจ.

Part 1 of 9 complete, bestie ๐ŸŒˆ๐Ÿ’– Part 2 is coming up next!

3.1.2. GRUUs That Hide the Underlying AOR โœจ

Okay so sometimes you want a GRUU that totally hides the AOR so nobody who gets it can figure out the AOR from it ๐Ÿ™ˆ That's called a temporary GRUU ๐Ÿฆ‹ The biggest reason is privacy, bestie. The GRUU can have basically any content, as long as it meets the requirements in Sections 3.1 and 5.4, and the AOR can't be easily determined from it. It gets the "gr" URI parameter, with or without a value. To avoid creating a pile of state in the registrar ๐Ÿ’€, people usually build cryptographically protected "stateless" GRUUs using an algorithm like the one in Appendix A ๐Ÿ”

An example of a temporary GRUU built with a stateful algorithm would be:

sip:asd887f9dfkk76690@example.com;gr

3.2. Obtaining a GRUU ๐Ÿงš

A user agent can get a GRUU in a few different ways:

  • As part of its REGISTER transaction.
  • By building one locally, using the IP address or hostname of the user agent instance as the domain part of the URI. These are called self-made GRUUs, and they're only really GRUUs when the UA building them knows it's globally reachable using that IP address or hostname.
  • Via some locally specified administrative mechanism.

A UA that wants to get a GRUU through its REGISTER request does that by providing an instance ID in the "+sip.instance" Contact header field parameter, which is defined in RFC 5626 [14]. For example:

Contact: <sip:callee@192.0.2.2>
;+sip.instance="<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"

The registrar spots this header field parameter and gives back two GRUUs in the REGISTER response, which is so iconic ๐Ÿ’… One is a temporary GRUU, and the other is the public GRUU. They come back in the "temp-gruu" and "pub-gruu" Contact header field parameters in the response, respectively. For example:

<allOneLine>
Contact: <sip:callee@192.0.2.2>
;pub-gruu="sip:callee@example.com;gr=urn:
uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
;temp-gruu="sip:tgruu.7hs==
jd7vnzga5w7fajsc7-ajd6fabz0f8g5@example.com;gr"
;+sip.instance="<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"
;expires=3600
</allOneLine>

Note that the <allOneLine> tag is used as defined in [17].

When a user agent refreshes this registration before it expires, the registrar gives back the same public GRUU but creates a new temporary GRUU ๐ŸŒธ Even though every refresh gives the UA a brand-new temporary GRUU, all of the temporary GRUUs learned from earlier REGISTER responses during the lifetime of a contact stay valid as long as (1) a contact with that instance ID is still registered, and (2) the UA doesn't change the Call-ID in its REGISTER request compared to previous ones for the same reg-id [14]. When the last contact for the instance expires, whether through explicit de-registration or timeout, all of the temporary GRUUs get invalidated ๐Ÿ˜ญ Similarly, if a register refresh for a contact (or, if RFC 5626 is being used, for a reg-id) changes the Call-ID compared to previous register refreshes, all of the older temporary GRUUs are invalidated. When the user agent later makes a new registration with the same instance ID, the public GRUU is the same. The temporary GRUU will be new (just like with refreshes), and it'll be the only valid temporary GRUU for the instance until the next refresh, at which point a second one becomes valid too. So, lowkey, temporary GRUUs "accumulate" during the lifetime of a registration ๐ŸŽ€

3.3. Using a GRUU ๐Ÿ’–

Once a user agent gets GRUUs from the registrar, it uses them in a few ways. First, it puts them in the Contact header field of non-REGISTER requests and responses it sends (for example, an INVITE request and a 200 OK response). According to RFC 3261 [1], the Contact header field is supposed to contain a URI that routes to that user agent. Before this specification, there wasn't really a way to meet that requirement, so the user agent would use one of its temporary GRUUs for anonymous calls and its public GRUU otherwise ๐Ÿซถ

Second, the UA can use the GRUU anywhere else it needs a URI that resolves back to itself, such as a webpage ๐ŸŒˆ

3.4. Dereferencing a GRUU ๐Ÿฆ‹

Because a GRUU is simply a URI, a UA dereferences it in exactly the same way it would any other URI. However, once the request has been routed to the appropriate proxy, things work a little differently โœจ The proxy maps the GRUU to the AOR and figures out the set of contacts that this particular UA instance has registered. The GRUU is then mapped to those contacts, and the request is routed towards the UA ๐Ÿš€


4. User Agent Behavior ๐Ÿ’…

This section defines the normative behavior for user agents.

4.1. Generating a REGISTER Request ๐Ÿ”ฅ

When a UA compliant to this specification generates a REGISTER request (initial or refresh), it MUST include the Supported header field in the request. The value of that header field MUST include "gruu" as one of the option tags. This alerts the registrar for the domain that the UA supports the GRUU mechanism ๐Ÿ‘‘

Furthermore, for each contact for which the UA wants to get a GRUU, the UA MUST include a "sip.instance" media feature tag (see RFC 5626 [14]) as a UA characteristic (see [7]), and its value MUST be the instance ID that identifies the UA instance being registered. Each such Contact header field SHOULD NOT contain a "pub-gruu" or "temp-gruu" header field. The contact URI MUST NOT be equivalent, based on the URI equality rules in RFC 3261 [1], to the AOR in the To header field. If the contact URI is a GRUU, it MUST NOT be a GRUU for the AOR in the To header field ๐Ÿ™…โ€โ™€๏ธ

As in RFC 3261 [1], the Call-ID in a REGISTER refresh SHOULD be identical to the Call-ID that was used to register a contact before. With GRUU, there's an extra thing to consider ๐Ÿ‘€ If the Call-ID changes in a register refresh, the server will invalidate all temporary GRUUs associated with that UA instance, and the only valid one will be the new one returned in that REGISTER response. When RFC 5626 is in use, this rule applies to the reg-ids instead: if the Call-ID changes for the registration refresh of a particular reg-id, the server will invalidate all temporary GRUUs associated with that UA instance as a whole. So, if a UA wants its previously obtained temporary GRUUs to stay valid, it MUST utilize the same Call-ID in REGISTER refreshes. However, it MAY change the Call-ID in a refresh if invalidation is the goal ๐Ÿงน

Note that if any dialogs are in progress that use a temporary GRUU as a remote target, and a UA does a registration refresh with a change in Call-ID, those temporary GRUUs become invalid, and the UA won't be reachable for subsequent mid-dialog messages. Yikes ๐Ÿ˜ญ

If a UA instance is trying to register multiple contacts for the same instance for redundancy, it MUST use the procedures defined in RFC 5626 [14] ๐Ÿซง

A UA utilizing GRUUs can still perform third-party registrations and can include contacts that omit the "+sip.instance" Contact header field parameter. Mix and match, bestie ๐Ÿ’œ

If a UA wants to guarantee that the REGISTER request isn't processed unless the domain supports and uses this extension, it MAY include a Require header field in the request with a value that contains the "gruu" option tag. This is in addition to the Supported header field, which also has to contain the "gruu" option tag. Using Proxy-Require isn't necessary and is NOT RECOMMENDED ๐Ÿ™ˆ

4.2. Learning GRUUs from REGISTER Responses ๐ŸŽ€

okay so if the REGISTER response is a 2xx, each Contact header field that has the +sip.instance Contact header field parameter can also have a pub-gruu and a temp-gruu Contact header field parameter ๐Ÿ’…. These params hand over the public GRUU and a temporary GRUU for the UA instance, respectively. A UA MUST be prepared for a Contact header field to contain just a pub-gruu, just a temp-gruu, neither, or both ๐Ÿ™ˆ. The temporary GRUU stays valid for the whole duration of the registration (that means through refreshes), while the public GRUU persists across registrations โœจ. The UA gets a new temporary GRUU in each successful REGISTER response, and the public GRUU will typically be the same one. BUT a UA MUST be prepared for the public GRUU to change from a previous one, because persistence isn't guaranteed with complete certainty ๐Ÿ˜ญ. If a UA changed its Call-ID in this REGISTER request compared to a previous REGISTER request for the same contact or reg-id, the UA MUST discard all temporary GRUUs it learned through prior REGISTER responses ๐Ÿ’€. A UA MAY keep zero, one, some, or all of the temporary GRUUs it gets while at least one contact or reg-id stays continuously registered ๐Ÿฆ‹. If a UA stores any temporary GRUUs to use during its registration, it needs to be certain the registration doesn't accidentally lapse because of clock skew between the UA and registrar ๐Ÿ•ฐ๏ธ. So the UA MUST refresh its registration in a way that the REGISTER refresh transaction either completes or times out before the registration expires. For default transaction timers, that means at least 32 seconds before expiration, assuming the registration expiration is larger than 64 seconds. If the registration expiration is less than 64 seconds, the UA SHOULD refresh its registration halfway through the expiration period ๐ŸŒธ.

Note that when [14] is in use, and the UA is using multiple flows for redundancy, the temporary GRUUs stay valid as long as at least one flow is registered. So even if one flow's registration expires, the temporary GRUUs learned earlier are still valid ๐Ÿซถ.

In cases where registrars forcefully shorten registration intervals, user agents use the registration event package, RFC 3680 [24], to learn about these changes ๐Ÿ‘€. A user agent implementing both RFC 3680 [24] and GRUU MUST also implement the extensions to RFC 3680 [24] for conveying information on GRUU, as defined in RFC 5628 [28], because those are necessary to keep the set of temporary GRUUs synchronized between the UA and the registrar ๐Ÿ”. More generally, the usefulness of temporary GRUUs depends on the UA and registrar being in sync about the set of valid temporary GRUUs at any given time โญ. Without support for RFC 3680 [24] and its GRUU extension, the client only stays in sync as long as it always re-registers well before the registration expires. Besides forceful de-registrations, other events (like network outages, connection failures, and short refresh intervals) can lead to potential inconsistencies in the set of valid temporary GRUUs ๐Ÿ˜ตโ€๐Ÿ’ซ. For this reason, it is RECOMMENDED that a UA that uses temporary GRUUs implement RFC 3680 [24] and RFC 5628 [28] ๐Ÿ’œ.

A non-2xx response to the REGISTER request has no impact on any existing GRUUs previously provided to the UA. Specifically, if a previously successful REGISTER request gave the UA a GRUU, a later failed request does not remove, delete, or otherwise invalidate that GRUU ๐Ÿ™Œ.

The user and host parts of the GRUU the UA learns in the REGISTER response MUST be treated opaquely by the UA. That means the UA MUST NOT modify them in any way ๐Ÿ”’. A UA MUST NOT modify or remove URI parameters it does not recognize. Furthermore, the UA MUST NOT add, remove, or modify URI parameters that are relevant for receipt and processing of the request at the proxy, including the transport, lr, maddr, ttl, user, and comp URI parameters (see RFC 3486 [25]). The other URI parameter defined in RFC 3261 [1], method, would not typically be present in a GRUU delivered from a registrar, and a UA MAY add a method URI parameter to the GRUU before handing it out to another entity ๐Ÿ’…. Similarly, the URI parameters defined in RFC 4240 [26] and RFC 4458 [27] are meant for the UA to consume. These wouldn't be included in the GRUU returned by a registrar, and a UA that wants to provide services tied to those URI parameters MAY add them ๐Ÿงš.

Note, however, that if another UA dereferences the GRUU, the parameters will be lost at the proxy when the Request-URI is translated into the registered contact, unless some other means is provided for the attributes to be delivered to the UA ๐Ÿ’€. Mechanisms for such delivery are currently the subject of future standardization activity (see "Delivery of Request-URI Targets to User Agents" [29]).

4.3. Constructing a Self-Made GRUU ๐Ÿ› ๏ธ

Lots of user agents (like gateways to the Public Switched Telephone Network (PSTN), conferencing servers, and media servers) don't do registrations, so they can't get GRUUs that way ๐Ÿฅฒ. These kinds of user agents can be publicly reachable. That would mean the domain's policy is that requests can come from anywhere on the public Internet and get delivered to the user agent without any processing by intervening proxies inside the domain. Firewall and NAT policies administered by the domain would also have to let those requests into the network ๐ŸŒ. When a user agent is certain these conditions are met, a UA MAY construct a self-made GRUU. Of course, a user agent that does REGISTER, but for whom these conditions are met anyway, MAY also construct a self-made GRUU. However, using GRUUs obtained from the registrar is RECOMMENDED instead ๐Ÿ‘‘.

A self-made GRUU is one whose domain part equals the IP address or hostname of the user agent. The user part of the SIP URI is chosen arbitrarily by the user agent ๐ŸŽฒ. Like all other GRUUs, the URI MUST contain the gr URI parameter, with or without a value, to indicate that it is a GRUU โœจ.

If a user agent doesn't register but also isn't publicly reachable, it would need to get a GRUU some other way ๐Ÿซ . Typically, the UA would be configured with a GRUU, the GRUU would be configured into the proxy, and the proxy would be configured with a mapping from the GRUU to the IP address (or hostname) and port of the UA ๐Ÿ—บ๏ธ.


4.4. Using One's Own GRUUs ๐Ÿ’–

A UA SHOULD use a GRUU when populating the Contact header field of dialog-forming and target refresh requests and responses. In other words, a UA compliant with this specification SHOULD use one of its GRUUs as its remote target ๐ŸŽฏ. This includes:

  • the INVITE request
  • a 2xx or 18x response to an INVITE which contains a To tag
  • the SUBSCRIBE request (see [5])
  • a 2xx response to a SUBSCRIBE which contains a To tag
  • the NOTIFY request
  • the REFER request (see [6])
  • a 2xx response to NOTIFY
  • the UPDATE request
  • a 2xx response to NOTIFY

The only reason not to use a GRUU would be privacy considerations; see Section 10.3 ๐Ÿ™ˆ.

When using a GRUU obtained through registrations, a UA MUST have an active registration before using a GRUU, and MUST use a GRUU learned through that registration ๐Ÿ“. It MUST NOT reuse a GRUU learned through a previous registration that has lapsed (in other words, one obtained when registering a contact that has since expired) ๐Ÿšซ. The UA MAY use either the public GRUU or one of its temporary GRUUs provided by its registrar ๐Ÿ’œ. A UA MUST NOT use a temporary GRUU learned in a REGISTER response whose Call-ID differs from the one in the most recent REGISTER request the UA generated for the same AOR and instance ID (and, if RFC 5626 [14] is in use, reg-id) ๐Ÿ˜ค. When a UA wants to construct an anonymous request as described in RFC 3323 [15], it SHOULD use a temporary GRUU. See Section 10.3 for a more complete discussion of the level of privacy afforded by temporary GRUUs ๐Ÿ•ต๏ธโ€โ™€๏ธ.

As per RFC 3261 [1], a UA SHOULD include a Supported header with the option tag gruu in requests and responses it generates ๐Ÿซถ.

4.4.1. Considerations for Multiple AORs ๐Ÿฆ‹

In some SIP networks, a user agent can have multiple AORs, either in different domains or within the same domain. In those cases, additional considerations apply ๐Ÿงฉ.

When a UA sends a request, the request will be sent "using" one of its AORs. That AOR will typically show up in the From header field of the request, and credentials unique to that AOR will be used to authenticate the request ๐Ÿ”. The GRUU placed into the Contact header field of such a request SHOULD be one associated with the AOR used to send the request. In cases where the UA uses a tel URI (as defined in [11]) to populate the From header field, the UA typically has a SIP AOR that is treated as an alias for the tel URI. The GRUU associated with that SIP AOR SHOULD be used in the Contact header field ๐Ÿ“ž.

When a UA receives a request, the GRUU placed into the Contact header field of a 2xx response SHOULD be the one associated with the AOR or GRUU that the request was most recently targeted at ๐ŸŽฏ. There are several ways to figure out which AOR or GRUU a request was sent to. For example, if a UA registered a different contact to each AOR (by using a different user part of the URI), the Request-URI (which contains that contact) will tell you the AOR ๐Ÿ”.


4.5. Dereferencing a GRUU ๐Ÿช„

A GRUU is identified by the presence of the gr URI parameter, and that URI parameter might or might not have a value. A UA that wants to send a request to a URI containing a GRUU knows the request will be delivered to a specific UA instance without any further action from the requestor ๐Ÿ’Œ.

Some UAs implement non-standard URI-handling mechanisms that make up for the fact that, until now, many contact URIs haven't been globally routable ๐Ÿ› ๏ธ. Since any URI containing the gr URI parameter is known to be globally routable, a UA SHOULD NOT apply such mechanisms when a contact URI contains the gr URI parameter ๐Ÿšซ.

Because the instance ID is a callee capabilities parameter, a UA might be tempted to send a request to the AOR of a user and include an Accept-Contact header field (defined in [12]) that indicates a preference for routing the request to a UA with a specific instance ID. Although this would appear to have the same effect as sending a request to the GRUU, it does not. The caller preferences expressed in the Accept-Contact header field are just preferences. Their efficacy depends on a UA constructing an Accept-Contact header field that interacts with the domain-processing logic for an AOR, to cause a request to route to a particular instance. Given the variability in routing logic in a domain (for example, time-based routing to only selected contacts), this doesn't work for many domain-routing policies. However, this specification does not forbid a client from attempting such a request, as there can be cases where the desired operation truly is a preferential routing request ๐Ÿคทโ€โ™€๏ธ.

4.6. Rendering GRUUs on a User Interface ๐Ÿซถ

okay bestie, when you're showing a GRUU to a user on screen, it is RECOMMENDED that you strip out the gr URI parameter first ๐Ÿ’… For public GRUUs, that leaves you with the AOR, which is exactly what we want โœจ For temporary GRUUs though, the result is gonna look seemingly random ๐ŸŽฒ Future work might come up with better mechanisms so an automaton can tell that a URI is anonymized and therefore shouldn't be rendered ๐Ÿค–

5. Registrar Behavior ๐Ÿ‘‘

5.1. Processing a REGISTER Request ๐Ÿ“

okay so a REGISTER request might include a Require header field with the "gruu" option tag ๐ŸŽ€ That tells the registrar it has to understand this extension to process the request, but it does not require the registrar to create GRUUs, however ๐Ÿ’–

As the registrar is processing the contacts in the REGISTER request, following step 7 in Section 10.3 of RFC 3261 [1], it checks whether each Contact header field has a "+sip.instance" header field parameter ๐Ÿ‘€ If it's there with a non-zero expiration, the contact gets processed further using the rules in the rest of this section. Otherwise, the contact is processed using the normal RFC 3261 [1] rules ๐Ÿฆ‹

Note that handling a REGISTER request with a Contact header field value of * and an expiration of zero still keeps the meaning defined in RFC 3261 [1]: all contacts, not just the ones with a specific instance ID, get deleted ๐Ÿงน As described in Section 5.4, this removes the binding of each contact to the AOR and the binding of each contact to its GRUUs.

If the contact URI is equivalent (based on URI equivalence in RFC 3261 [1]) to the AOR, the registrar MUST reject the request with a 403, since this would cause a routing loop ๐Ÿ™…โ€โ™€๏ธ If the contact URI is a GRUU for the AOR in the To header field of the REGISTER request, the registrar MUST reject the request with a 403, for the same reason. If the contact is not a SIP URI, the REGISTER request MUST be rejected with a 403 ๐Ÿšซ

Next, the registrar checks whether there's already a valid public GRUU for the AOR (in the To header field of the REGISTER request) and the instance ID (the content of the "+sip.instance" Contact header field parameter) ๐Ÿ” If there's no valid public GRUU, the registrar SHOULD construct one at this point, following the procedures in Section 5.4. The public GRUU MUST be constructed by adding the gr URI parameter, with a value, to the AOR. If the contact had a "pub-gruu" Contact header field parameter, the registrar MUST ignore that parameter ๐Ÿ™ˆ A UA cannot suggest or otherwise provide a public GRUU to the registrar, so no sneaking it in, bestie ๐Ÿ™…โ€โ™€๏ธ

Next, the registrar checks for any existing contacts registered to the same AOR and instance ID and, if the contact in the REGISTER request is registering a flow [14], the reg-id ๐Ÿง If there's at least one, the registrar finds the one that was most recently registered and looks at the Call-ID value associated with that registered contact. If it differs from the one in the REGISTER request, the registrar MUST invalidate all previously generated temporary GRUUs for the AOR and instance ID ๐Ÿ’€ A consequence of this invalidation is that requests addressed to those GRUUs will be rejected by the domain with a 404 from this point forward ๐Ÿ˜ญ

Next, the registrar SHOULD create a new temporary GRUU for the AOR and instance ID with the characteristics described in Section 5.4 โœจ The temporary GRUU construction algorithm MUST have the following two properties:

  1. The likelihood that the temporary GRUU is equal to another GRUU that the registrar has created MUST be vanishingly small ๐ŸŽฒ
  2. Given a pair of GRUUs, it MUST be computationally infeasible to determine whether they were issued for the same AOR or instance ID or for different AORs and instance IDs ๐Ÿ”

If the contact had a "temp-gruu" Contact header field parameter, the registrar MUST ignore that parameter. A UA cannot suggest or otherwise provide a temporary GRUU to the registrar ๐Ÿซฃ

5.2. Generating a REGISTER Response ๐Ÿ“ฌ

When generating the 200 (OK) response to the REGISTER request, the procedures of step 8 of Section 10.3 of RFC 3261 [1] are followed ๐Ÿ’– Furthermore, for each Contact header field value placed in the response, if the registrar has stored an instance ID associated with that contact, that instance ID is returned as a Contact header field parameter ๐ŸŽ€ If the REGISTER request had a Supported header field that included the "gruu" option tag, and the registrar has at least one temporary GRUU assigned to the instance ID and AOR, the registrar MUST add a "temp-gruu" Contact header field parameter to that Contact header field. The value of the "temp-gruu" parameter is a quoted string, and MUST contain the most recently created temporary GRUU for that AOR and instance ID ๐ŸŒธ In addition, if the registrar has a public GRUU assigned to the instance ID and AOR (and the client supports GRUUs), the registrar MUST add a "pub-gruu" Contact header field parameter to that Contact header field. The value of the "pub-gruu" Contact header field parameter is the public GRUU ๐Ÿฆ‹

The registrar SHOULD NOT include the "gruu" option tag in the Require or Supported header field of the response ๐Ÿ™…โ€โ™€๏ธ

5.3. Timing Out a Registration โณ

When a registered contact expires (whether from a timeout or an explicit de-registration), its binding to the AOR is removed as usual ๐Ÿงน On top of that, its bindings to its GRUUs get removed at the same time, as a consequence of the relationships described in Section 5.4. If, because the contact expired, a particular GRUU no longer has any registered contacts bound to it, and that GRUU is a temporary GRUU, the GRUU MUST be invalidated ๐Ÿ’€ This means all of the accumulated temporary GRUUs get invalidated once the last contact for a given instance ID expires ๐Ÿ˜ญ

If the GRUU was a public GRUU, though, the registrar SHOULD continue to treat it as valid ๐Ÿ’… So subsequent requests targeted at the GRUU, before a contact gets re-registered to it, SHOULD return a 480 (Temporarily Unavailable) response ๐ŸŒ™ Since the GRUU stays valid, the rules in Section 5.1 will keep it when a contact with that instance ID gets registered to the AOR again ๐Ÿ”

These rules give a public GRUU a semi-permanent vibe โœจ The intent is for the registrar to make every attempt to keep the GRUU valid for as long as the AOR itself is known within the domain. The requirements for this are at SHOULD strength and not MUST strength, because a MUST strength requirement is really hard to meet ๐Ÿ˜ฎโ€๐Ÿ’จ Registrar failures could cause the set of valid GRUUs to get lost, and this specification requires the UA to be robust against those cases ๐Ÿ›ก๏ธ That said, it's possible to construct a public GRUU so the registrar doesn't need to keep any extra state for it, and the GRUU still meets the requirements described here ๐Ÿงš

5.4. Creation of a GRUU ๐ŸŽ€

okay so this section is all about what the registrar has to keep track of when it makes a GRUU ๐Ÿ’… these rules are only for GRUUs the registrar creates, not self-made ones or ones that didn't come from a registration bestie ๐Ÿงš

When a registrar makes a GRUU, it's required to keep some info tied to that GRUU, whether it's a public one or a temporary one โœจ every GRUU is linked to one AOR and one instance ID. A registrar MUST be able to figure out the instance ID and AOR when it's handed a GRUU ๐Ÿ” and the GRUU, like an AOR, resolves to zero or more contacts. The AOR resolves to all the registered contacts for that AOR, but a GRUU only resolves to the contacts whose instance ID matches its own ๐Ÿฆ‹ so a contact with an instance ID is always bound to both a GRUU and its AOR. never just the AOR, never just the GRUU. it's giving couple energy ๐Ÿ’œ

Here's a picture of it, because pictures are everything ๐Ÿ˜ญ this one shows three contacts on a single AOR. one has instance ID 1 and the other two have instance ID 2. there are two GRUUs for this AOR, one for instance ID 1 and one for instance ID 2. the first GRUU only resolves to the instance ID 1 contact, and the second only resolves to the instance ID 2 contacts.

      +----------+   +----------+  +----------+
      |  GRUU    |   |          |  |  GRUU    |
      |          |   |   AOR    |  |          |
      |Instance:1|   |          |  |Instance:2|
      +----------+   +----------+  +----------+
           |           /  |  \           / |
           |          /   |   \         /  |
           |         /    |    \       /   |
           |        /     |     \     /    |
           |       /      |      \   /     |
           |      /       |       \ /      |
           |     /        |        X       |
           |    /         |       / \      |
           |   /          |      /   \     |
           |  /           |     /     \    |
           V V            V    V       V   V
      +----------+   +----------+  +----------+
      | Contact  |   | Contact  |  | Contact  |
      |          |   |          |  |          |
      |Instance:1|   |Instance:2|  |Instance:2|
      +----------+   +----------+  +----------+

                             Figure 1

There can be lots of GRUUs with the same instance ID and AOR ๐ŸŒธ this spec actually wants registrars to keep many of them, one public and several temporary ones. BUT, if two GRUUs have different AORs, different instance IDs, or both, they MUST be different based on URI equality comparison. and a GRUU in a domain MUST NOT be equivalent (by URI comparison) to any AOR in that domain except the one it's associated with ๐Ÿ™…โ€โ™€๏ธ

a public GRUU will always be equivalent to its AOR based on URI equality rules, and that's not a bug, it's the vibe ๐Ÿ’– RFC 3261 [1] says URI parameters that show up in one URI but not the other get ignored for equality purposes. a public GRUU only differs from its AOR by having the "gr" URI parameter, so those two URIs count as equivalent under those rules.

Once a temporary GRUU is built, the registrar MUST consider it valid until it gets invalidated by the rules we already covered ๐Ÿ•ฐ๏ธ once a public GRUU is built, it MUST be considered valid for as long as its AOR is valid. and once an AOR stops being valid within a domain, all of its GRUUs MUST be considered invalid too ๐Ÿ’€ no survivors.

This spec doesn't force one specific way to build the GRUU ๐Ÿง there are example algorithms for public and temporary GRUUs that work well in Appendix A. BUT, on top of the properties from Section 3.1, a GRUU built by a registrar MUST have these:

  • The domain part of the URI is an IP address on the public Internet, or, if it's a hostname, the resolution procedures in RFC 3263 [2], once applied, land on an IP address on the public Internet ๐ŸŒ
  • When a request is sent to the GRUU, it gets routed to a proxy that can access the registration data the registrar made. that proxy is called an authoritative proxy, as defined in RFC 5626 [14] โœจ

5.5. Registration Event Support ๐Ÿ””

RFC 3680 [24] defines an event package that lets a client find out about registration events at the registrar ๐Ÿ‘€ this package also lets registrars change registrations forcefully, like shortening them so the client has to re-register. so if a registrar supports both RFC 3680 [24] and GRUU, it MUST also support RFC 5628 [28]. no skipping this one, bestie ๐Ÿซถ

6. Proxy Behavior ๐Ÿงš

Proxy behavior is fully defined in Section 16 of RFC 3261 [1] ๐Ÿ“– GRUU processing changes that processing in two places: request targeting at the authoritative proxy, and record-routing ๐Ÿ’…

6.1. Request Targeting ๐ŸŽฏ

When a proxy gets a request, owns the domain in the Request-URI, and needs to access a location service to compute request targets (like Section 16.5 of RFC 3261 [1] describes), the proxy looks at the Request-URI. if it has the "gr" URI parameter but isn't equivalent (by URI comparison) to a GRUU that's currently valid in the domain, it SHOULD be rejected with a 404 (Not Found) response ๐Ÿ™…โ€โ™€๏ธ that's the same thing a proxy does for any other URI in the domain that isn't valid, so nothing new here.

If the Request-URI has the "gr" URI parameter and IS equivalent (by URI comparison) to a GRUU that's currently valid in the domain, processing goes on just like it would for any other URI in the location service, as defined in Section 16.5 of RFC 3261 [1]. the one difference is the "gr" URI parameter doesn't get removed during canonicalization ๐Ÿฆ‹ this is true for both out-of-dialog requests aimed at the GRUU and mid-dialog requests aimed at the GRUU. (for mid-dialog ones, the incoming request has a Route header field value with the URI the proxy used for record-routing.)

Quick note: the "gr" URI parameter only sticks around so the GRUU can be found in the location service โœจ if there's a match, the Request-URI gets rewritten with the registered contacts, replacing the GRUU and its "gr" URI parameter. so the "gr" param never gets carried into the rewritten Request-URI ๐Ÿซง

If there are no registered contacts bound to the GRUU, the server MUST return a 480 (Temporarily Unavailable) response ๐Ÿ˜ญ if there's more than one, there are two cases:

  1. The client is using RFC 5626 [14] and registering multiple contacts for redundancy. those contacts have "reg-id" Contact header field parameters, and the rules in Section 7 of RFC 5626 [14] for picking a single registered contact apply ๐Ÿ’œ
  2. The client was NOT using RFC 5626 [14]. in that case, multiple contacts with the same instance ID only happen if the client rebooted, restarted, and re-registered. these contacts won't have the "reg-id" Contact header field parameter. the proxy MUST select the most recently refreshed contact ๐Ÿ•ฐ๏ธ just like with RFC 5626, if a request to this target fails with a 408 (Request Timeout) or 430 (Flow Failed) response, the proxy SHOULD retry with the next most recently refreshed contact. and if the request fails with any other response, the proxy MUST NOT retry on any other contacts for this instance ๐Ÿ™…โ€โ™€๏ธ

Any caller preferences in the request (as defined in RFC 3841 [12]) SHOULD be processed against the contacts bound to the GRUU ๐Ÿ’…

Basically, to pick a registered contact, the GRUU gets processed just like it was the AOR, but only with a subset of the contacts bound to the AOR ๐Ÿงš

Special considerations apply to processing any Path headers stored in the registration (see RFC 3327 [3]) ๐Ÿ“ฆ if the received request has Route header field values beyond the one pointing to the authoritative proxy itself (this happens with mid-dialog requests), the Path URI MUST be discarded. RFC 3327 [3] allows this as a matter of local policy, and GRUU usage needs this policy to avoid call spirals and likely call failures ๐ŸŒ€

A proxy MAY apply other processing to the request, like called party features, the same way it might for requests aimed at an AOR โœจ for out-of-dialog requests, it is RECOMMENDED to apply screening types of functions, both automated (like blacklist and whitelist screening) and interactive (like interactive voice response (IVR) applications that check with the user about whether to accept a call) ๐Ÿ“ž in lots of cases the new request is related to an existing dialog, and might be trying to join it (using the Join header field defined in RFC 3911 [21]) or replace it (using the Replaces header field defined in RFC 3891 [22]) ๐Ÿฆ‹ when that's the case, the UA will usually make its own authorization decisions. bypassing screening services at the authoritative proxy might make sense, but network designers need to think about it carefully, since whether you can do that depends on the specific screening service ๐Ÿง

BUT, forwarding services, like call forwarding, SHOULD NOT be provided for requests sent to a GRUU ๐Ÿšซ the whole point of a GRUU is to target one specific UA instance, and forwarding just doesn't go with that ๐Ÿ’–

If the request is mid-dialog, a proxy SHOULD only apply services that make sense for mid-dialog requests, generally speaking. that leaves out screening and forwarding functions ๐ŸŒธ

And a request sent to a GRUU SHOULD NOT be redirected ๐Ÿ™ˆ a lot of the time a UA uses a GRUU to help get through NATs and firewalls, and a redirect could stop that from working.

6.2. Record-Routing ๐Ÿ’…

okay so there are TWO separate record-routing rules, one for the originating domain and one for the terminating domain ๐ŸŽ€ they exist so requests don't end up in unnecessary, possibly messy spirals of bouncing around, which is honestly so relatable ๐Ÿ˜ญ

If all of these are true:

  • an originating authoritative proxy receives a dialog-forming request,
  • AND the Contact header field contains a GRUU in the domain of the proxy,
  • AND that GRUU is a valid one in the domain of the proxy,
  • AND that GRUU is associated with the AOR matching the authenticated identity of the requestor (assuming such authentication has been performed),
  • AND the request contains Record-Route header fields,

then the authoritative proxy MUST record-route. โœจ If all of these conditions are true except that the GRUU is associated with an AOR that did not match the authenticated identity of the requestor, then it is RECOMMENDED that the proxy reject the request with a 403 (Forbidden) response. ๐Ÿ™ˆ

If:

  • a terminating authoritative proxy receives a dialog-forming request,
  • AND the Request-URI contains a URI in the location service (either a GRUU or an AOR),
  • AND the contact selected for sending the request has an instance ID and is bound to a GRUU,
  • AND the registration contain Path URI,

then the authoritative proxy MUST record-route. ๐Ÿฆ‹

If a proxy is in either the originating or terminating domains but is not an authoritative proxy, the proxy MAY record-route. ๐ŸŒธ

If a proxy in the terminating domain requires mid-dialog requests to pass through it for whatever reason (firewall traversal, accounting, etc.), the proxy MUST still record-route, and MUST NOT assume that a UA will utilize its GRUU in the Contact header field of its response (which would cause mid-dialog requests to pass through the proxy without record-routing). ๐Ÿงš

Implementors should note that, if a UA uses a GRUU in its contact, and a proxy inserted itself into the Path header field of a registration, that proxy will be receiving mid-dialog requests regardless of whether it record-routes or not. The only distinction is what URI the proxy will see in the topmost Route header field of mid-dialog requests. If the proxy record-routes, it will see that URI. If it does not, it will see the Path URI it inserted. ๐Ÿ‘€

7. Grammar ๐Ÿ“œ

okay this part is the grammar, and it's giving very precise ๐Ÿค“ This specification defines two new Contact header field parameters ("temp-gruu" and "pub-gruu") by extending the grammar for "contact-params" as defined in RFC 3261 [1]. It also defines a new SIP URI parameter ("gr") by extending the grammar for "uri-parameter" as defined in RFC 3261 [1]. The ABNF [13] is as follows:

contact-params  =/ temp-gruu / pub-gruu
temp-gruu       =  "temp-gruu" EQUAL quoted-string
pub-gruu        =  "pub-gruu" EQUAL quoted-string

uri-parameter   =/ gr-param
gr-param        = "gr" ["=" pvalue]   ; defined in RFC 3261

The quoted strings for temp-gruu and pub-gruu MUST contain a SIP URI. โœจ However, they are encoded like all other quoted strings and can therefore contain quoted-pair escapes when represented this way. ๐ŸŽ€


8. Requirements ๐ŸŒˆ

okay this is the wishlist section, the list of things this whole spec was built to do ๐Ÿ’– This specification was created in order to meet the following requirements:

  • REQ 1: When a UA invokes a GRUU, it must cause the request to be routed to the specific UA instance to which the GRUU refers. ๐ŸŽฏ
  • REQ 2: It must be possible for a GRUU to be invoked from anywhere on the Internet, and still cause the request to be routed appropriately. That is, a GRUU must not be restricted to use within a specific addressing realm. ๐ŸŒ
  • REQ 3: It must be possible for a GRUU to be constructed without requiring the network to store additional state. ๐Ÿง 
  • REQ 4: It must be possible for a UA to obtain a multiplicity of GRUUs that each route to that UA instance. For example, this is needed to support ad hoc conferencing where a UA instance needs a different URI for each conference it is hosting. NOTE: This requirement is not met by this specification, and is being addressed in a separate specification (currently, "Delivery of Request-URI Targets to User Agents" [29]). ๐Ÿฅฒ
  • REQ 5: When a UA receives a request sent to a GRUU, it must be possible for the UA to know the GRUU that was used to invoke the request. This is necessary as a consequence of REQ 4. NOTE: This requirement is not met by this specification, and is being addressed in a separate specification (currently, "Delivery of Request-URI Targets to User Agents" [29]). ๐Ÿซ 
  • REQ 6: It must be possible for a UA to add opaque content to a GRUU. This content is not interpreted or altered by the network, and is used only by the UA instance to whom the GRUU refers. This provides a basic cookie type of functionality, allowing a UA to build a GRUU with the state embedded. NOTE: This requirement is not met by this specification, and is being addressed in a separate specification (currently, "Delivery of Request-URI Targets to User Agents" [29]). ๐Ÿ“
  • REQ 7: It must be possible for a proxy to execute services and features on behalf of a UA instance represented by a GRUU. As an example, if a user has call-blocking features, a proxy might want to apply those call-blocking features to calls made to the GRUU, in addition to calls made to the user's AOR. ๐Ÿ“ž๐Ÿšซ
  • REQ 8: It must be possible for a UA in a dialog to inform its peer of its GRUU, and for the peer to know that the URI represents a GRUU. This is needed for the conferencing and dialog reuse applications of GRUUs, where the URIs are transferred within a dialog. ๐Ÿค
  • REQ 9: When transferring a GRUU per REQ 8, it must be possible for the UA receiving the GRUU to be assured of its integrity and authenticity. ๐Ÿ”
  • REQ 10: It must be possible for a server that is authoritative for a domain to construct a GRUU that routes to a UA instance bound to an AOR in that domain. In other words, the proxy can construct a GRUU, too. This is needed for the presence application. ๐Ÿ‘‘

9. Example Call Flow โœจ

okay so this section is basically a full movie of a call, bestie ๐ŸŽฌ๐Ÿฟ it shows a basic registration and call setup, then a subscription aimed at the GRUU, then the callee totally crashing and coming back (rip ๐Ÿ’€), and then a re-registration. the RFC 4475 [17] conventions are used for showing really long message lines, so you'll see some <allOneLine> markers below. don't panic, they're just formatting ๐Ÿซถ

    Caller                 Proxy                Callee
    |                     |(1) REGISTER         |
    |                     |<--------------------|
    |                     |(2) 200 OK           |
    |                     |-------------------->|
    |(3) INVITE           |                     |
    |-------------------->|                     |
    |                     |(4) INVITE           |
    |                     |-------------------->|
    |                     |(5) 200 OK           |
    |                     |<--------------------|
    |(6) 200 OK           |                     |
    |<--------------------|                     |
    |(7) ACK              |                     |
    |-------------------->|                     |
    |                     |(8) ACK              |
    |                     |-------------------->|
    |(9) SUBSCRIBE        |                     |
    |-------------------->|                     |
    |                     |(10) SUBSCRIBE       |
    |                     |-------------------->|
    |                     |(11) 200 OK          |
    |                     |<--------------------|
    |(12) 200 OK          |                     |
    |<--------------------|                     |
    |                     |(13) NOTIFY          |
    |                     |<--------------------|
    |(14) NOTIFY          |                     |
    |<--------------------|                     |
    |(15) 200 OK          |                     |
    |-------------------->|                     |
    |                     |(16) 200 OK          |
    |                     |-------------------->|
    |                     |                     |Crashes,
    |                     |(17) REGISTER        | Reboots
    |                     |<--------------------|
    |                     |(18) 200 OK          |
    |                     |-------------------->|

Figure 2

okay so the callee supports the GRUU extension, which means its REGISTER (1) looks like this ๐Ÿ‘‘

REGISTER sip:example.com SIP/2.0
Via: SIP/2.0/UDP 192.0.2.1;branch=z9hG4bKnashds7
Max-Forwards: 70
From: Callee <sip:callee@example.com>;tag=a73kszlfl
Supported: gruu
To: Callee <sip:callee@example.com>
Call-ID: 1j9FpLxk3uxtm8tn@192.0.2.1
CSeq: 1 REGISTER
Contact: <sip:callee@192.0.2.1>
 ;+sip.instance="<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"
Content-Length: 0

the registrar hands out a temporary GRUU and a public GRUU ๐ŸŽ€ so the REGISTER response (message 2) comes back looking like this:

SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.0.2.1;branch=z9hG4bKnashds7
From: Callee <sip:callee@example.com>;tag=a73kszlfl
To: Callee <sip:callee@example.com> ;tag=b88sn
Call-ID: 1j9FpLxk3uxtm8tn@192.0.2.1
CSeq: 1 REGISTER
<allOneLine>
Contact: <sip:callee@192.0.2.1>
;pub-gruu="sip:callee@example.com
;gr=urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
;temp-gruu="sip:tgruu.7hs==
jd7vnzga5w7fajsc7-ajd6fabz0f8g5@example.com;gr"
;+sip.instance="<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"
;expires=3600
</allOneLine>
Content-Length: 0

so the Contact header field in that response has a pub-gruu parameter holding the public GRUU sip:callee@example.com;gr=urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6, plus a temp-gruu parameter holding the temporary GRUU sip:tgruu.7hs==jd7vnzga5w7fajsc7-ajd6fabz0f8g5@example.com;gr. โœจ both of them are valid GRUUs for the AOR and instance ID, and both translate to the contact sip:callee@192.0.2.1. two GRUUs, one callee, zero drama ๐Ÿ’…

the INVITE from the caller (message 3) is a totally normal SIP INVITE, nothing fancy there ๐Ÿฆ‹ but the 200 OK from the callee (message 5) now has a GRUU as the remote target. the UA picked its public GRUU, iconic choice ๐Ÿ‘‘

SIP/2.0 200 OK
Via: SIP/2.0/UDP proxy.example.com;branch=z9hG4bKnaa8
Via: SIP/2.0/UDP host.example.com;branch=z9hG4bK99a
From: Caller <sip:caller@example.com>;tag=n88ah
To: Callee <sip:callee@example.com> ;tag=a0z8
Call-ID: 1j9FpLxk3uxtma7@host.example.com
CSeq: 1 INVITE
Supported: gruu
Allow: INVITE, OPTIONS, CANCEL, BYE, ACK, SUBSCRIBE
<allOneLine>
Contact:
<sip:callee@example.com
;gr=urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>
</allOneLine>
Content-Length: --
Content-Type: application/sdp

[SDP Not shown]

later on in the call, the caller wants to subscribe to the dialog event package (defined in [16]) on that one specific UA ๐Ÿ‘€ so it sends a SUBSCRIBE request (message 9) aimed at the remote target, which is a GRUU:

<allOneLine>
SUBSCRIBE sip:callee@example.com;gr=urn:uuid:f8
1d4fae-7dec-11d0-a765-00a0c91e6bf6
 SIP/2.0
</allOneLine>
Via: SIP/2.0/UDP host.example.com;branch=z9hG4bK9zz8
From: Caller <sip:caller@example.com>;tag=kkaz-
<allOneLine>
To: <sip:callee@example.com;gr=urn:uuid:f8
1d4fae-7dec-11d0-a765-00a0c91e6bf6>
</allOneLine>
Call-ID: faif9a@host.example.com
CSeq: 2 SUBSCRIBE
Supported: gruu
Event: dialog
Allow: INVITE, OPTIONS, CANCEL, BYE, ACK, NOTIFY
Contact: <sip:caller@example.com;gr=hdg7777ad7aflzig8sf7>
Content-Length: 0

in this example the caller also supports GRUUs, so it's using its own GRUU to fill in its remote target ๐Ÿงš

this request goes to the proxy, which does a location lookup on the Request-URI. the GRUU gets translated into the contact for that instance, and then the proxy forwards it to that contact ๐Ÿš€

SUBSCRIBE sip:callee@192.0.2.1 SIP/2.0
Via: SIP/2.0/UDP proxy.example.com;branch=z9hG4bK9555
Via: SIP/2.0/UDP host.example.com;branch=z9hG4bK9zz8
From: Caller <sip:caller@example.com>;tag=kkaz-
<allOneLine>
To: <sip:callee@example.com;gr=urn:uuid:f8
1d4fae-7dec-11d0-a765-00a0c91e6bf6>
</allOneLine>
Call-ID: faif9a@host.example.com
CSeq: 2 SUBSCRIBE
Supported: gruu
Event: dialog
Allow: INVITE, OPTIONS, CANCEL, BYE, ACK, NOTIFY
Contact: <sip:caller@example.com;gr=hdg7777ad7aflzig8sf7>
Content-Length: 0

the SUBSCRIBE gets a 200 response (message 11), then a NOTIFY (messages 13 and 14) and its response (messages 15 and 16). โœจ okay now the plot twist ๐Ÿ˜ญ after message 16 comes in, the callee's machine crashes and then recovers. it gets a brand new IP address, 192.0.2.2. it has no idea it had an active registration before, so it makes a new one (message 17 below). notice that the instance ID stays the same, because it sticks around across reboots ๐Ÿ’œ

REGISTER sip:example.com SIP/2.0
Via: SIP/2.0/UDP 192.0.2.2;branch=z9hG4bKnasbba
Max-Forwards: 70
From: Callee <sip:callee@example.com>;tag=ha8d777f0
Supported: gruu
To: Callee <sip:callee@example.com>
Call-ID: hf8asxzff8s7f@192.0.2.2
CSeq: 1 REGISTER
<allOneLine>
Contact: <sip:callee@192.0.2.2>
;+sip.instance="<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"
</allOneLine>
Content-Length: 0

the registrar spots that a different contact, sip:callee@192.0.2.1, is already tied to the same instance ID ๐Ÿ•ต๏ธโ€โ™€๏ธ so it registers the new one too and sends both back in the REGISTER response. both contacts share the same public GRUUs, but the registrar made a second temporary GRUU for this AOR and instance ID combo. both contacts show up in the response, and the temporary GRUU for each one is the same, which is the most recently created one for that instance ID and AOR. the registrar then sends this response:

SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.0.2.2;branch=z9hG4bKnasbba
From: Callee <sip:callee@example.com>;tag=ha8d777f0
To: Callee <sip:callee@example.com>;tag=99f8f7
Call-ID: hf8asxzff8s7f@192.0.2.2
CSeq: 1 REGISTER
<allOneLine>
Contact: <sip:callee@192.0.2.2>
;pub-gruu="sip:callee@example.com;gr=urn:
uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
;temp-gruu="sip:tgruu.7hatz6cn-098shfyq193=
ajfux8fyg7ajqqe7@example.com;gr"
;+sip.instance="<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"
;expires=3600
</allOneLine>
<allOneLine>
Contact: <sip:callee@192.0.2.1>
;pub-gruu="sip:callee@example.com;gr=urn:
uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
;temp-gruu="sip:tgruu.7hatz6cn-098shfyq193=
ajfux8fyg7ajqqe7@example.com;gr"
;+sip.instance="<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"
;expires=400
</allOneLine>
Content-Length: 0

and the UA doesn't need to clean up the stale contact ๐Ÿงน the request targeting rules in Section 6.1 make sure the request gets delivered to the most recent one. the system handles it, periodt ๐Ÿ’…


10. Security Considerations

okay security time, and this part is SERIOUS ๐Ÿ”ฅ attacks in SIP networks that use GRUUs fall into two buckets: outside attacks (a third party trying to break in) and inside attacks (the attacker is a legit participant who's up to no good ๐Ÿ’€). on top of that, there are privacy considerations with using GRUUs too, so keep your eyes open ๐Ÿ‘€

10.1. Outside Attacks

it's really important that a UA can trust the integrity of a GRUU it gets in a REGISTER response ๐Ÿซถ if an attacker messes with the GRUU, the result could be a denial of service (DoS) hitting the UA, and nobody wants that ๐Ÿ˜ญ so it is RECOMMENDED that a UA use the SIPS URI scheme in the Request-URI when it registers. proxies and registrars MUST support the SIPS URI and MUST support TLS. this doesn't change anything from the requirements in RFC 3261 [1], so nothing new to panic about ๐Ÿ’–

the example GRUU construction algorithm in Appendix A.1 doesn't try to hide the AOR and instance ID tied to a GRUU ๐Ÿ™ˆ that's actually considered a good thing overall, because it makes it easy to track who a particular call is aimed at. learning the instance ID doesn't really help an attacker much. to register or otherwise change registrations for a user, an attacker would need that user's credentials, and knowing the instance ID is not enough ๐Ÿ”

the example GRUU construction algorithm in Appendix A.1 also doesn't try to stop people from guessing a GRUU if they know the AOR and instance ID ๐Ÿ‘€ someone who can do that can send a new request straight at a particular instance. however, this specification recommends that service treatment (in particular, screening features) be given to requests that are sent to a GRUU. that treatment makes sure a GRUU doesn't become a back door ๐Ÿšช for attackers to reach a user who has already tried to block them. blocked means blocked, bestie ๐Ÿซถ๐Ÿ’…

10.2. Inside Attacks ๐Ÿ•ต๏ธโ€โ™€๏ธ

okay so this is the sneaky stuff, bestie. Because of this spec, a UA starts using GRUUs in the dialog forming and target refresh requests and responses it sends out ๐Ÿ’Œ. Those GRUUs then get handed over to another UA (the correspondent), who uses them in the requests they send ๐Ÿ“ž.

If a shady correspondent strips out the gr URI parameter, the request goes to the authoritative proxy ๐Ÿš—. If the GRUU was temporary, taking off gr gives you a URI that isn't recognized as a GRUU and isn't equal to any AOR, so the request gets rejected โŒ. If the GRUU was public, removing gr gives you the AOR, so the request gets treated like a call to the AOR ๐Ÿซ . And since getting the AOR out of a GRUU is a goal people actually want, this is not an attack, and the call gets handled normally โœจ.

Now the drama ๐Ÿ’€. A malicious user in the system might try to use a GRUU to launch a DoS attack against another SIP UA ๐Ÿ˜ค. The attacker waits for a call from that UA and watches for its GRUU. Once they have it, they send a SIP request to an entity, like a presence server, that will generate lots of requests back toward the UA. The attacker puts the target's GRUU in the Contact header field of that SUBSCRIBE request, so all that traffic gets pointed at the target instead ๐ŸŽฏ. Since the GRUU is globally routable, that traffic is more likely to reach the target than traffic sent to its IP address ๐Ÿ˜ฌ.

This spec helps stop this by requiring proxies to check that the GRUU in the Contact of a request matches the authenticated identity of the request's sender ๐Ÿ”. That check needs an outbound proxy. SIP doesn't require outbound proxies, so this does leave a potential area of vulnerability ๐Ÿฅบ. In practice though, nearly all SIP deployments use an outbound proxy, so this vulnerability isn't likely to be a concern ๐Ÿ’….


10.3. Privacy Considerations ๐Ÿ™ˆ

RFC 3323 [15] defines mechanisms for privacy. It separates network-provided privacy from user-provided privacy ๐Ÿซง. With the network kind, the user asks the network for privacy services by putting a Privacy header field in the request. With the user kind, the UA follows a basic set of guidelines for building its request, so some level of privacy comes along for free ๐ŸŒธ.

The guidelines in Section 4.1 of RFC 3323 [15] for user-provided privacy tell a UA to build its Contact header field with a URI that leaves out the user part and uses the IP address or hostname of the UA ๐Ÿ™ƒ. That clashes with the rules in this spec, which require using a GRUU in the Contact header field ๐Ÿงฉ.

BUT, the temporary GRUUs from the registrar can be used in place of the Contact URI format described in RFC 3323 [15] โœจ. A UA gathers the temporary GRUU returned in each REGISTER response and keeps a small number of them cached ๐Ÿ—‚๏ธ. When it makes or receives a call, it uses a temporary GRUU to fill in the Contact header field ๐Ÿ“ฒ.

A UA can either use the same temporary GRUU for every call, or a different one for each call. It depends on how much privacy you want ๐Ÿฆ‹:

  • A UA using the same temporary GRUU for each call lets a correspondent, just by looking at the Contact header field, link calls as coming from the same UA ๐Ÿ”—. That's true for the user-provided privacy procedures in RFC 3323 [15] too, since the IP address or hostname in the Contact URI is a similar giveaway ๐Ÿ‘€.
  • A UA using a different temporary GRUU for each call means a correspondent can't link calls as coming from the same UA, just by looking at the Contact header field ๐Ÿซฅ.
  • In both cases, unless network-provided privacy is in place, the IP address and port information in the Session Description Protocol (SDP) (defined in [10]) lets a correspondent link calls as coming from the same UA ๐Ÿซฃ.
  • In both cases, if a user makes a call, the correspondent can call back by sending requests toward the GRUU in the Contact header field ๐Ÿ“ž. Features like transfer and digit collection by network application servers (see RFC 4730 [20]), which depend on a Contact with the GRUU property, also work ๐Ÿงš. These inbound requests stay possible until the registration for that UA lapses โณ.
  • A UA that wants to invalidate its previous temporary GRUU to limit reachability MAY do so by generating a REGISTER refresh with a Call-ID that differs from the ones used before ๐Ÿ”„. A UA SHOULD NOT forcefully expire its registration and then re-register to invalidate a temporary GRUU ๐Ÿ™…โ€โ™€๏ธ. That causes a brief period of unreachability and often piles excess load on the network ๐Ÿ“‰. Refreshing with a new Call-ID is more efficient and is meant as the technique for coarse-grained control over how long temporary GRUUs stay valid ๐ŸŽ€. A UA that doesn't want to be bugged by a specific call back will need manual or automated call-handling procedures to reject it ๐Ÿšซ. This spec doesn't give the UA a way to manually invalidate individual temporary GRUUs ๐Ÿคทโ€โ™€๏ธ.
  • If a UA insists on getting no inbound requests at all (including ones from network applications, like the ones used for collecting digits), it can put a non-GRUU in the Contact header field ๐Ÿ˜ถ. However, this is NOT RECOMMENDED ๐Ÿšจ. Using a GRUU together with automated call rejection features is far superior ๐Ÿ‘‘.
  • As long as a temporary GRUU fills the Contact header field, a correspondent can't learn anything about the AOR or instance ID of the UA by inspecting the Contact header field ๐Ÿค. But without a network-provided privacy service, the IP address in the SDP can still reveal things about the UA, like its geographic location and ISP ๐Ÿ—บ๏ธ.
  • In all cases, whether the UA uses a temporary or public GRUU in the Contact, whether it uses GRUU at all, and whether it invokes a network-provided privacy service, a correspondent can determine the SIP service provider of the UA ๐Ÿ“ก.

11. IANA Considerations ๐Ÿงš

This specification defines two new Contact header field parameters, one SIP URI parameter, and a SIP option tag ๐ŸŽ€.

11.1. Header Field Parameter

This specification defines two new header field parameters, as per the registry created by RFC 3968 [8] ๐Ÿ“‹. The required information is as follows:

Header field in which the parameter can appear:  Contact
Name of the Parameter:  pub-gruu
Predefined Values:  none
RFC Reference:  RFC 5627

Header field in which the parameter can appear:  Contact
Name of the Parameter:  temp-gruu
Predefined Values:  none
RFC Reference:  RFC 5627

11.2. URI Parameter

This specification defines one new SIP URI parameter, as per the registry created by RFC 3969 [9] ๐Ÿฆ„.

Name of the Parameter:  gr
Predefined Values:  none
RFC Reference:  RFC 5627

11.3. SIP Option Tag

This specification registers a new SIP option tag, as per the guidelines in Section 27.1 of RFC 3261 [1] ๐ŸŒˆ.

Name:  gruu

Description:  This option tag is used to identify the Globally
   Routable User Agent URI (GRUU) extension.  When used in a
   Supported header, it indicates that a User Agent understands the
   extension.  When used in a Require header field of a REGISTER
   request, it indicates that the registrar is not expected to
   process the registration unless it supports the GRUU extension.

12. Acknowledgments ๐Ÿซถ

The author would like to thank Eric Rescorla, Robert Sparks, Rohan Mahy, Paul Kyzivat, Alan Johnston, Ya-Ching Tan, Dale Worley, Jeroen van Bemmel, Vijay Gurbani, Andrew Allen, Alan Hawrylyshen, Francois Audet, Fredrik Thulin, Dean Willis, David Hancock, Keith Drage, and Cullen Jennings for their comments and contributions to this work ๐Ÿ’–. Eric Rescorla provided the text for the introduction and the GRUU construction algorithm in the appendix ๐Ÿ‘‘.


13. References ๐Ÿ“š

13.1. Normative 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]   Willis, D. and B. Hoeneisen, "Session Initiation Protocol (SIP)
      Extension Header Field for Registering Non-Adjacent Contacts",
      RFC 3327, December 2002.

[4]   Bradner, S., "Key words for use in RFCs to Indicate Requirement
      Levels", BCP 14, RFC 2119, March 1997.

[5]   Roach, A., "Session Initiation Protocol (SIP)-Specific Event
      Notification", RFC 3265, June 2002.

[6]   Sparks, R., "The Session Initiation Protocol (SIP) Refer
      Method", RFC 3515, April 2003.

[7]   Rosenberg, J., Schulzrinne, H., and P. Kyzivat, "Indicating
      User Agent Capabilities in the Session Initiation Protocol
      (SIP)", RFC 3840, August 2004.

[8]   Camarillo, G., "The Internet Assigned Number Authority (IANA)
      Header Field Parameter Registry for the Session Initiation
      Protocol (SIP)", BCP 98, RFC 3968, December 2004.

[9]   Camarillo, G., "The Internet Assigned Number Authority (IANA)
      Uniform Resource Identifier (URI) Parameter Registry for the
      Session Initiation Protocol (SIP)", BCP 99, RFC 3969,
      December 2004.

[10]  Handley, M., Jacobson, V., and C. Perkins, "SDP: Session
      Description Protocol", RFC 4566, July 2006.

[11]  Schulzrinne, H., "The tel URI for Telephone Numbers", RFC 3966,
      December 2004.

[12]  Rosenberg, J., Schulzrinne, H., and P. Kyzivat, "Caller
      Preferences for the Session Initiation Protocol (SIP)",
      RFC 3841, August 2004.

[13]  Crocker, D. and P. Overell, "Augmented BNF for Syntax
      Specifications: ABNF", STD 68, RFC 5234, January 2008.

[14]  Jennings, C., Ed. and R. Mahy, Ed., "Managing Client-Initiated
      Connections in the Session Initiation Protocol (SIP)",
      RFC 5626, October 2009.

13.2. Informative References โœจ

okay so these are the informative references, bestie ๐Ÿ“š they're not required reading to implement this, but they're the friends this spec keeps name-dropping ๐Ÿ’–

  • [15] Peterson, J., "A Privacy Mechanism for the Session Initiation Protocol (SIP)", RFC 3323, November 2002.
  • [16] Rosenberg, J., Schulzrinne, H., and R. Mahy, "An INVITE-Initiated Dialog Event Package for the Session Initiation Protocol (SIP)", RFC 4235, November 2005.
  • [17] Sparks, R., Hawrylyshen, A., Johnston, A., Rosenberg, J., and H. Schulzrinne, "Session Initiation Protocol (SIP) Torture Test Messages", RFC 4475, May 2006.
  • [18] Schulzrinne, H., "Dynamic Host Configuration Protocol (DHCP-for-IPv4) Option for Session Initiation Protocol (SIP) Servers", RFC 3361, August 2002.
  • [19] Sparks, R., Johnston, A., and D. Petrie, "Session Initiation Protocol (SIP) Call Control - Transfer", BCP 149, RFC 5589, June 2009.
  • [20] Burger, E. and M. Dolly, "A Session Initiation Protocol (SIP) Event Package for Key Press Stimulus (KPML)", RFC 4730, November 2006.
  • [21] Mahy, R. and D. Petrie, "The Session Initiation Protocol (SIP) "Join" Header", RFC 3911, October 2004.
  • [22] Mahy, R., Biggs, B., and R. Dean, "The Session Initiation Protocol (SIP) "Replaces" Header", RFC 3891, September 2004.
  • [23] Willis, D. and B. Hoeneisen, "Session Initiation Protocol (SIP) Extension Header Field for Service Route Discovery During Registration", RFC 3608, October 2003.
  • [24] Rosenberg, J., "A Session Initiation Protocol (SIP) Event Package for Registrations", RFC 3680, March 2004.
  • [25] Camarillo, G., "Compressing the Session Initiation Protocol (SIP)", RFC 3486, February 2003.
  • [26] Burger, E., Van Dyke, J., and A. Spitzer, "Basic Network Media Services with SIP", RFC 4240, December 2005.
  • [27] Jennings, C., Audet, F., and J. Elwell, "Session Initiation Protocol (SIP) URIs for Applications such as Voicemail and Interactive Voice Response (IVR)", RFC 4458, April 2006.
  • [28] Kyzivat, P., "Registration Event Package Extension for Session Initiation Protocol (SIP) Globally Routable User Agent URIs (GRUUs)", RFC 5628, October 2009.
  • [29] Rosenberg, J., van Elburg, J., Holmberg, C., Audet, F., and S. Schubert, Ed., "Delivery of Request-URI Targets to User Agents", Work in Progress, June 2009.

Appendix A. Example GRUU Construction Algorithms ๐Ÿงš

okay this appendix is the fun part, bestie ๐ŸŽ€ how a GRUU actually gets built is not something the spec locks down. this is just an example a registrar can use to make a public GRUU and a temporary GRUU. others are totally allowed too, as long as they meet the constraints defined for a GRUU ๐Ÿ’…

A.1. Public GRUU ๐ŸŒธ

the most basic way to build a public GRUU is to take the AOR and drop the actual value of the instance ID into the contents of the "gr" URI parameter. easy peasy ๐Ÿซถ

A.2. Temporary GRUU ๐Ÿฆ‹

ngl this is where it gets spicy ๐ŸŒถ๏ธ the spec says a registrar has to create a brand new temporary GRUU on every registration refresh. if a registration is super long-lived, that can quickly mean hundreds or even thousands of temporary GRUUs getting made and handed out to a UA ๐Ÿ˜ญ so it's really important to have an algorithm that does not need extra storage that keeps growing with the number of temporary GRUUs. the algorithm below hits that goal โœจ

the registrar keeps a counter, I. this counter is 48 bits, starts at zero, and is stored persistently (in a backend database or something similar). when the registrar makes the first temporary GRUU for a particular AOR and instance ID, it notes the current counter value, I_i, and then increments the counter in the database. next, the registrar maps I_i to the AOR and instance ID using the database, a persistent hashmap, or similar tech. if the registration expires so that no contacts with that instance ID are bound to the GRUU anymore, the registrar removes the mapping. similarly, if the temporary GRUUs get invalidated because the Call-ID changed, the registrar removes the current mapping from I_i to the AOR and instance ID, notes the current counter value I_j, and stores a mapping from I_j to the AOR and instance ID. follow these rules and the hashmap ends up with exactly one mapping for each AOR and instance ID that has a currently valid registration ๐Ÿ’œ

using a 48-bit space with sequential assignment gives a compact hashmap key, which matters a lot for keeping GRUUs a reasonable size ๐Ÿ“ the counter starts at zero when the system is initialized. persistent and reliable storage of the counter is required so a GRUU never gets misrouted to the wrong AOR and instance ID ๐Ÿ˜ฌ same goes for the hashmap: it has to be stored persistently, even across proxy and registrar restarts. if the hashmap gets reset, every previous temporary GRUU becomes invalid. that might make dialogs in progress fail, or make future requests to a temporary GRUU fail when they normally wouldn't ๐Ÿ’€ and the same hashmap needs to be reachable by every proxy and registrar that can field requests for a particular AOR and instance ID.

the registrar also keeps a pair of local symmetric keys, K_e and K_a. these get regenerated every time the counter is reset. when the counter rolls over or gets reset, the registrar remembers the old K_e and K_a values for a while. just like the hashmap, these keys need to be shared across all the proxies and registrars that can service requests for a particular AOR and instance ID ๐Ÿ”‘

to generate a new temporary GRUU, the registrar makes a random 80-bit distinguisher value, D. then it computes:

M = D || I_i
E = AES-ECB-Encrypt(K_e, M)
A = HMAC-SHA256-80(K_a, E)

Temp-Gruu-userpart = "tgruu." || base64(E) || base64(A)

where || means concatenation, and AES-ECB-Encrypt is AES encryption in electronic codebook mode. M ends up 128 bits long, which gives an E of 128 bits and an A of 80 bits. the resulting user part is 42 characters long ๐ŸŽ€

when a proxy gets a request whose user part begins with "tgruu.", it pulls out the rest and splits it into 22 characters (E') and the remaining 14 characters (A'). then it gets A and E by base64-decoding A' and E' respectively. next, it computes:

Ac = HMAC-SHA256-80(K_a, E)

if the counter has rolled over or been reset, this computation is done with both the current and the previous K_a. if the Ac value(s) computed don't match the A pulled out of the GRUU, the GRUU is rejected as invalid ๐Ÿšซ otherwise, the proxy computes:

M = AES-ECB-Decrypt(K_e, E)

if the counter has rolled over, this decryption uses whichever K_e goes with the K_a that gave a valid Ac in the previous HMAC check. the leading 80 bits (the distinguisher D) get thrown away, leaving an index I_i into the hashmap. that index gets looked up. if it exists, the proxy now knows the AOR and instance ID for this temporary GRUU ๐Ÿฅฐ if nothing is in the hashmap for the key I_i, the GRUU is no longer valid and the request gets rejected.

last bit of math, bestie ๐Ÿงฎ a 48-bit counter lets a registrar handle as many as a billion AORs, with 10 instances per AOR, and cycle through 10,000 Call-ID changes for each instance over the course of a single registration. those numbers are averages. the system works fine if a particular AOR has more than 10 instances, or a particular instance cycles through more than 10,000 Call-IDs in its registration, as long as the average still meets those constraints โœจ


Appendix B. Network Design Considerations ๐ŸŒˆ

the GRUU spec works properly based on logic implemented in the user agents and in the authoritative proxies on both sides of a call. which means it's totally possible to build network deployments where GRUUs won't work properly ๐Ÿ˜ฌ so let's talk about the setup that keeps things happy.

one big assumption the GRUU mechanism makes is this: if a request passes through any proxies in the originating domain before it visits the terminating domain, one of those proxies will be the authoritative proxy for the User Agent Client (UAC). admins of SIP networks need to make sure this property stays true ๐Ÿซถ there are a few ways to get there:

  1. if the user agents support the service-route mechanism [23], the registrar can implement it and return a service route that points to the authoritative proxy. this makes requests the user agent originates go through the authoritative proxy.
  2. the user agents can be configured to never use an outbound proxy, and send requests directly to the domain of the terminating party. this isn't practical in many use cases, but hey, it is a solution to this requirement ๐Ÿ’…
  3. the user agents can be configured with an outbound proxy in the same domain as the authoritative proxy, and this outbound proxy forwards requests to the authoritative proxy by default. this works really well when the clients aren't roaming. in that case, the outbound proxy in a visited network may be discovered dynamically through DHCP [18] ๐Ÿฆ‹
  4. when the client discovers a local outbound proxy via something like DHCP, and isn't implementing the service route mechanism, the UA can be configured to automatically add an additional Route header field after the outbound proxy. that Route header points to a proxy in the home network. this has the same net effect as the service route mechanism, but it's done through static configuration ๐Ÿ“

Author's Address ๐Ÿ’œ

Jonathan Rosenberg Cisco Systems Edison, NJ US

EMail: jdrosen@cisco.com URI: http://www.jdrosen.net

๐Ÿ“ฆ where this page comes from