This document describes how to deploy, configure, and manage the OpenCloud SIP Resource Adaptor.

Topics

Quickstart

getting up and running with the RA and example services

Configuration

properties you can adjust to configure the RA

Example Applications

links to descriptions and instructions for the example services

Features

details of the many features of the SIP RA

Session Timers

how session timers work with the SIP RA

Migrating from Older Versions

changes required to applications written for earlier OCSIP resource adaptor types

Other documentation for the SIP Resource Adaptor including the changelog and links to the API JavaDoc and downloads, can be found on the SIP Resource Adaptor product page.

 

Note The SIP Resource Adaptor requires Java 7 or later.

Quickstart

To quickly get up and running with the SIP Resource Adaptor and example services:

1

Start Rhino:

$RHINO_HOME/start-rhino.sh

2

Edit the domain name property in sip.properties. For correct operation of the SIP proxy, this property needs to be modified:

PROXY_DOMAINS=opencloud.com,opencloud.co.nz

Note

PROXY_DOMAINS is the list of domains for which the proxy will be "authoritative". This means for any requests received for URIs in these domains, including subdomains, the proxy will attempt to map the URI to a local, registered contact address using the location service SBB.

If no user has registered at that address, the proxy will respond with a 4xx error code.

If the request is for a domain that the proxy does not know about, it will attempt to forward the request using standard SIP routing rules.

The properties in sip.properties are substituted into SBB deployment descriptors when the example applications are built.

3

Deploy the examples: using Ant, execute the deployexamples target. This will deploy the SIP Resource Adaptor and the SIP Location Service, Registrar, and Proxy services.

$ ant deployexamples

4

Use a SIP user agent such as Linphone to send SIP requests to the server.

Tip See the Sample SIP Applications guide in the Rhino Documentation for more details on using Linphone and the example services.

Example Applications

See the Sample SIP Applications guide in the Rhino Documentation, for a complete description of the example applications.

To get up and running with the example proxy and registrar applications, see the Quickstart section of this document.

Tip

Source code for all the example applications can be found in the src directory.
This code may be freely altered and reused. It is made available under a BSD-style license.

Configuration

The SIP RA is configured using SLEE configuration properties. These properties may be specified at deploy time or updated later using management commands.

The file build.properties contains the default set of configuration properties that will be used when deploying the RA with the provided Ant build.xml script:

build.properties
sip.ra.properties=IPAddress=AUTO,Transports="udp,tcp",Port=5060,SecurePort=5061

Network address properties

These properties define the IP address, ports and transports that the SIP RA entity will be using. The default configuration is to listen on all interfaces, using UDP and TCP, on port 5060.

Property name Description Values Default
IPAddress

The IP address the SIP RA will listen on. The value AUTO means the RA will listen on all interfaces and use the host’s primary IP address in outgoing headers.

An IP address range may also be used, using address/mask notation; for example, 192.168.0.0/24. This will select the matching IP address on the node, if one exists. This may be useful in a cluster where nodes have IP addresses on the same subnet.

a hostname,
IP address,
IP address range,
or AUTO

AUTO
VirtualAddresses

A list of hostnames and/or IP addresses that are local to the cluster. Required when a load balancer is providing a virtual IP address (VIP) for the Rhino cluster. Addresses may also include a port number (for example, host:5090 in case the load balancer is using a different port.

The RA treats incoming messages routed to these addresses as if they were local. For example, isLocalSipURI() will return true if the URI’s host field matches one of these virtual addresses.

If this property is set, and the UseVirtualAddressInURIs property is set to true, the first name in the list will be used in SIP URIs generated by the RA, so that requests are routed back to the VIP, and not a specific node. The first name should be the fully-qualified hostname corresponding to the VIP.

By default this property is empty, meaning the SIP RA will use the node’s IP address (as determined by the IPAddress property) in SIP URIs.

list of hostnames or IP addresses

(none)

UseVirtualAddressInURIs

Specifies whether to use the configured virtual address in SIP URIs generated by the SIP RA.

If true (the default), the first address in the VirtualAddresses property will be used for the host part in SIP URIs. Otherwise, the node’s IP address (as determined by the IPAddress property) will be used.

true, false

true
ViaSentByAddress

Specifies an address (IP address or hostname) that will be used in the "sent-by" field of Via headers added by the SIP RA.

By default this property is empty, and the SIP RA will use the local IP address of the node. In some scenarios it may be necessary to override the default and use a virtual server address.

a hostname or IP address

(none)

Port

Port that will be used for unencrypted SIP transports (UDP, TCP, SCTP).

an integer port number

5060
SecurePort

Port that will be used for encrypted SIP transports (TLS).

an integer port number

5061
Transports

Supported SIP transports. The RA will open listen sockets for these transports.

a comma-separated list containing one or more of UDP, TCP, SCTP or TLS

UDP,TCP
OffsetPorts

Required when running multiple cluster nodes on the same host. If true, the RA will automatically add an offset to the SIP port number so that each SIP RA instance gets a different port number.

See PortOffset below.

true, false

false
PortOffset

Required when running multiple cluster nodes on the same host. If OffsetPorts is enabled, the SIP RA’s port will be calculated as Port + (NodeID - PortOffset). Typically the PortOffset value used is the lowest NodeID in the cluster. So if Port is 5060, and the lowest NodeID is 101, and the cluster has nodes 101, 102 and 103, the SIP ports used will be 5060, 5061 and 5062 respectively.

an integer NodeID, usually the value of the lowest NodeID in the cluster

(none)

AllowLoopbackSend

Determines whether the RA is allowed to send SIP messages to itself. Disabled by default to prevent accidental loops.

true, false

false
SCTP:additional_addresses

By default, SCTP listen sockets will bind to the configured IPAddress value. Additional IP addresses may be specified here; they will be advertised to peers during SCTP association setup. This property accepts a comma- or whitespace-delimited list of hostnames or addresses.

Like IPAddress, each address may be an address range, such as 192.168.0.0/24. The RA selects the matching interface on the host.

If IPAddress is AUTO or a wildcard address, then these additional addresses are ignored. Additional addresses that are wildcards are also ignored.

list of hostnames or IP addresses

(none)

SIP RA Features

Property Name Description Values Default
Automatic100TryingSupport

If enabled, the RA will automatically generate 100 Trying responses for INVITEs.

true, false

true
ReplicatedDialogSupport

Enables support for replicating SIP dialog state, so that dialog activities can continue on other cluster nodes after a node failure. Requires a clustered production Rhino installation.

true, false

false
ExtensionMethods

SIP methods that can initiate dialogs, in addition to the standard INVITE and SUBSCRIBE methods.

a comma-separated list of SIP method names

(none)

WorkerPoolSize

Number of worker threads the SIP RA will use to process incoming messages. If zero, the RA will receive and process messages in the same I/O thread. Otherwise, the pool will be used so that incoming messages can be processed concurrently.

integer, 0 or more

4
WorkerQueueSize

Queue size for the worker thread pool. If the queue fills then the RA will drop packets (UDP) or temporarily suspend reads (TCP, TLS, SCTP).

integer, 1 or more

50
IOThreads

Number of threads for handling network I/O. SIP sockets are divided between the I/O threads. A value of zero or less will create 2N threads, where N is the number of CPU cores.

integer, 0 or more

1
ListenBacklog

Size of the listen backlog queue for server sockets. Increase this to allow the server to accept more concurrent connection requests. A value of zero or less means use the Java platform’s default.

integer

0
UDPMaxRequestSize

The maximum size (in bytes) of SIP requests that may be sent sent using UDP. Requests larger than this will automatically be sent using TCP instead, as per RFC3261 §18.1.1. Values of zero or less disable this feature; the RA will try to send with UDP anyway. This is not recommended as fragmentation and packet loss are more likely with large UDP datagrams.

integer

1300
UDPInputBufferSize

Size of UDP input buffer, in bytes. UDP messages larger than this will be rejected. Clients should send large messages using TCP, as per RFC3261 §18.1.1; this option is provided to handle exceptional cases. Values must be at least 2048 bytes to ensure the stack can handle typical cases. Relying on large UDP datagrams is not recommended as fragmentation and packet loss are more likely.

integer, 2048 or more

2048
AutomaticOptionsResponses

If enabled, OPTIONS requests that are routed directly to this RA’s address (no Route header and the Request-URI has the RA’s address) will be answered immediately by the RA. Otherwise, the RA passes all OPTIONS requests up to SLEE applications to be processed. This is enabled by default so that OPTIONS health checks from load balancers or gateways are handled efficiently.

true, false

true
InviteTransactionLifetime

The maximum time (in ms) that INVITE transaction activities may be active for. Activities older than this value will be removed on Rhino’s next query liveness call. The default value of 0 means that activities will be removed on their first query liveness call, which is usually after 5 minutes with Rhino’s query liveness configuration.

long value in milliseconds, 0 or more

0
RetryAfterInterval

When the RA rejects a request due to overload, it will send a "503 Service Unavailable" response with this Retry-After interval (in seconds This instructs the client to back off so the server can recover. An interval of 0 or less means the RA will not automatically insert the Retry-After header.

integer value in seconds

5
RFC3263:failover_enabled

Specifies whether to use RFC 3236: Failover and Load Balancing. By default this is disabled, to match existing behaviour. If enabled, the RA uses RFC 3263 DNS procedures to find possible servers and automatically fails over to backup servers where available. If multiple servers are found in DNS they are sorted and tried according to their SRV priority and weight as per RFC 2782. If disabled, the DNS procedures are still used to find server addresses, but only the first address is used.

true or false

false
RFC3263:failover_timer

Specifies the duration of the failover timer. If RFC 3263 failover is enabled and this timer expires before any responses were received, the RA treats this as a transport error and tries sending the request to the next available server. This timer should be set to a value smaller than the default Timer B and Timer F timers (32s) so that failures can be detected promptly. A value of zero or less disables this timer.

failover time in milliseconds

10000
RFC3263:blacklist_timer

The duration for which a server will be blacklisted after a failure is detected. This avoids the RA trying to use the server immediately after a failure when it is most likely just going to fail again. After this time has passed the failed server may be tried again on subsequent client transactions. If a server specifies a Retry-After duration in a 503 response, that value will be used instead.

blacklist time in milliseconds

300000
SessionTimer:default_refresher

The default refresher value that will be inserted when a UAS responds to an initial session refresh request that did not specify a refresher. Must be uac or uas (case-insensitive).

uac or uas

uas
ErrorResponseProfileTable

The profile table containing per-request-method configuration of RA generated error responses. Profiles named to match request methods contain a response code and, optionally, a Reason header value to send instead of the default RA generated error. This configuration only overrides 5xx responses from the RA. Profiles are named for the request method they define responses to, for example INVITE, OPTIONS.

Null or a profile table name.
An example configuration is ErrorResponse
ProfileTable
="sip-ra1-error-responses" referring to a profile table called "sip-ra1-error-responses" containing a profile named "INVITE" with attributes ErrorResponseCode=500 and ReasonHeader="Q.850; cause=16; text=System down"


          
RefusedResponseProfileTable

The profile table containing per-request-method configuration of RA generated request-refused responses. Profiles named to match request methods contain a response code and, optionally, a Reason header value to send instead of the default RA generated error. Overrides ErrorResponseProfileTable if configured. This configuration overrides 5xx responses from the RA when the SLEE rejects creation of an activity (new dialog or non-dialog) e.g. due to rate-limiting. Profiles are named for the request method they define responses to, for example "INVITE", "OPTIONS".

Null or a profile table name. An example configuration is RefusedResponseProfileTable="sip-ra1-refused-responses" referring to a profile table called "sip-ra1-refused-responses" containing a profile named "INVITE" with attributes ErrorResponseCode=603 and ReasonHeader="Q.850; cause=21; text=Rate limited"


          
ResponseConfigurationPollTime

The interval (ms) between scans of the response configuration profile tables.

300,000 (5 minutes)
Invite:TimerC

Configures automatic "Timer C" behaviour for INVITE transactions.

Timer C (from RFC 3261) cleans up INVITE transactions that have not had a final response, and have not seen any provisional responses for at least 3 minutes.

Timer C resets on each 101-199 response. If Timer C fires before a 101-199 or final response arrives, then the transaction is terminated. Timer C is enabled for INVITE server transactions only if ServerTransactionTimeoutEnabled is true.

INVITE server transactions that time out will send a 408 (Request Timeout) response and fire a transaction timeout event. INVITE client transactions will first send a CANCEL request. If the CANCEL request does not trigger a final response for the INVITE, then a transaction timeout event is fired, and the INVITE client transaction is terminated.

A value of 0 or less disables Timer C. Otherwise it must be set to at least 180000ms (3 minutes).

duration in milliseconds

0
ServerTransactionTimeoutEnabled

Specifies whether server transactions should automatically timeout if the service sent no final response.

If enabled, INVITE and non-INVITE server transactions will automatically be removed after 64*T1 (32) seconds, and a transaction timeout event will be fired.

INVITE server transactions will send a 408 (Request Timeout) response if a 100 Trying was sent. If no 100 Trying was sent, no 408 response is needed as the client transaction will be ended by its Timer B.

Non-INVITE server transactions send no response (as per RFC 4320), since the client transaction will have already been ended by its Timer F.

If the INVITE server transaction sends a 101-199 response then the timer is cancelled, and Timer C behaviour (if enabled) takes over. If the transaction is part of a Dialog activity then that activity will be removed as well, as the service is in an unusable state.

true or false

false

SSL/TLS Properties

These properties must be specified when the Transports property contains "TLS".

Property Name Description Values Default
Keystore

The name of a keystore file used to store private key material.

a file name

sip-ra-ssl.keystore
Truststore

The name of a truststore file containing trusted CA certificates.

a file name

sip-ra-ssl.truststore
KeystorePassword

Password to unlock the keystore.

a string

(none)

TruststorePassword

Password to unlock the truststore.

a string

(none)

KeystoreType

The type of keystore implementation.

a keystore type name

jks
TruststoreType

The type of truststore implementation.

a truststore type name

jks
CRLURL

The location of a certificate revocation list.

a URL

(none)

CRLRefreshTimeout

The certificate revocation list refresh timeout.

a duration in seconds

86400
CRLLoadFailureRetryTimeout

The certificate revocation list load failure timeout.

a duration in seconds

900
CRLNoCRLLoadFailureRetryTimeout

The certificate revocation list load failure retry timeout.

a duration in seconds

60
ClientAuthentication

Indicate whether clients need to be authenticated against certificates in the truststore.

NEED, WANT, or NONE

NEED
EnabledCipherSuites

Specifies the cipher suites to enable on all TLS sockets. If empty, The JVM’s default cipher suites will be used. For the list of available cipher suites, see SunJSSE Provider.

a comma-separated list of cipher suite names

empty

BigGroup Properties

These properties apply when using the BigGroup 2.5 RA Type API.

Property Name Description Values Default
BigGroup.core-pool-size

The core number of threads in the Big Group thread pool.

integer, between 0 and BigGroup.max-pool-size

4
BigGroup.max-pool-size

The maximum number of threads in the Big Group thread pool.

integer, 0 or more

50
BigGroup.queue-size

The maximum number of waiting threads in the Big Group thread pool.

integer, 0 or more. 0 indicates no queuing, tasks are scheduled immediately if the pool has capacity.

0
BigGroup.fork-timeout

The default maximum time to wait for a success response for a Big Group Fork Activity.

timeout in milliseconds

10000
BigGroup.fork-batch-size

Specify the maximum number in a batch for a big group fork thread to process. Tune the batch size by observing the SIP RA’s big group stats in "rhino-stats".

integer, 1 or more

250
BigGroup.fork-keep-callid

Specifies whether to keep the Call-ID from the original request when forking the request to its targets. Set to False when using a sipp UAS or other simulator.

true, false

false
BigGroup.notify-batch-size

Specify the maximum number in a batch for a big group notify thread to process. Tune the batch size by observing the SIP RA’s big group stats in "rhino-stats".

integer, 1 or more

250

F5 BIG-IP Properties

The SIP RA can optionally use the F5 iControl API to dynamically update a SIP load balancer pool on the BIG-IP platform. These properties specify how the RA connects to BIG-IP.

Property Name Description Values Default
BigIP.enable-notify

Enable or disable F5 BIG-IP notify. On RA activation, if enabled F5 BIG-IP will be notified to enable the node’s pool member and start sending SIP Messages to Node. On RA deactivation, if enabled F5 BIG-IP will be notified to disable the node’s pool member and stop sending SIP Messages to Node. If disabled, F5 BIGIP will be not be notified on activation or deactivation of RA.

true, false

false
BigIP.ipaddress

BIG-IP load balancer’s address

an IP address or hostname

(none)

BigIP.port

BIG-IP load balancer’s iControl API port

an integer port number

443
BigIP.username

The user name to authenticate with the BIG-IP load balancer.

a string

(none)

BigIP.password

The password to authenticate with the BIG-IP load balancer.

a string

(none)

BigIP.poolname

The BIG-IP Pool that the SIP RA nodes are members of.

a string

(none)

Features

This section describes features of the SIP resource adaptor:

Feature What it does

deploys SIP RA in a clustered Rhino configuration

lets a downstream proxy "fork" a request, meaning it forwards a request to several contacts in parallel

parses the headers in incoming SIP messages

lets a SIP client initiate a persistent SIP connection to a SIP proxy server on the other side of a firewall/NAT

allows dialog activities to be accessed on any node in the cluster, so that SBBs can attach to dialog activity contexts and use the dialogs on any node

lets a SIP client use DNS procedures to resolve a SIP URI into the address, port, and transport protocol of the next server to contact

lets a SIP client initiate a persistent SIP connection to a SIP proxy server on the other side of a firewall/NAT

supports SCTP (RFC 4960) as a SIP transport, in addition to UDP, TCP, and TLS

Cluster Support

This feature deploys SIP RA in a clustered Rhino configuration

Below is an overview of how the RA behaves in a cluster, and any issues the Rhino administrator or developer needs to be aware of.

Overview

When a SIP RA entity is created in a Rhino cluster, each cluster node creates an instance of a SIP RA object. Each SIP RA instance will bind to the local IP interfaces on its node.

Each node has its own unique IP address. Rhino does not provide any form of IP address failover or load balancing. An external load balancer must be used if it is necessary to present a single virtual IP address for the cluster.

Activities, such as transactions and dialogs, created by the SIP RA instances on each node are local to each node. If a node fails, all SIP activities on the node will be lost, but the SIP RAs and services on other nodes can continue running, providing high availability of services.

It is possible to use replicated dialog activities. These are disabled by default. If enabled, replicated dialog activities, and service instances that are attached to them, can continue on other nodes after a failure, providing some level of fault tolerance.

Multiple nodes on one host

In the default configuration, the SIP RA cannot be deployed in a cluster where multiple nodes are running on the same host. This is because each RA instance will try to bind to the same SIP port, and the OS will not allow this if they are on the same host.

This can be resolved by specifying two additional configuration properties when deploying the RA: OffsetPorts=true and PortOffset=<LowestNodeID>.

These properties tell the RA to add an offset to the SIP port used on each node, so that each node will use a unique port number. For example, if the RA is configured with Port=5060, and the cluster has nodes with node IDs 101, 102, and 103, and PortOffset=101, the SIP ports used by the three nodes will be 5060, 5061, and 5062 respectively.

Virtual addresses

If the SIP RA is deployed in a cluster behind an IP load balancer, the load balancer typically provides a virtual IP address (VIP) that external hosts use to connect to the cluster. The SIP RA has an optional VirtualAddresses configuration property, which specifies a list of hostnames or VIPs that the cluster is known by. This allows the SIP RA to detect when a SIP URI or hostname should be treated as a local address, if it matches a virtual address for the cluster. See network address properties.

SBB programming

SBB programming considerations include location independence and helper methods.

Location independence

In the SLEE programming model, SBBs are location-independent, meaning they can run on any node that is part of the SLEE. SBBs do not need to know where they are running, and the SLEE does not provide this information. This allows SBBs to be portable between different SLEE implementations that may or may not support clustering.

However, in SIP applications it is often necessary to know something about the platform, such as the IP address and port that is being used by the SIP stack. For example, a proxy application must check incoming route headers, and remove the top route header if it is addressed to the proxy. To make this decision, the proxy must know the IP address and port that it is running on. In a clustered environment, the local IP address and port information may not be known in advance, so it cannot be provisioned in a profile or SBB env-entry.

Helper methods

To help solve this problem, the JAIN SIP RA Type provides some helper methods that SBBs can use when they need to know the local SIP network configuration. These methods are defined on the SleeSipProvider interface, and are summarized below.

Method What it does
public boolean isLocalSipURI(SipURI uri);

Determines if a SIP URI is addressed to the SIP RA on this node or one of its virtual addresses. This is useful in the above proxy scenario — the proxy can easily check the URI in the Route header.

public boolean isLocalHostname(String name);

Determines if a hostname corresponds to the IP address of the SIP RA, or a virtual address.

public SipURI getLocalSipURI(String transport);

Returns a SIP URI that is addressed to this node. This is useful for generating Contact or Record-Route headers. When the SIP RA is configured with virtual addresses, the first virtual address in the list will be used as the host part of the SIP URI.

public ViaHeader getLocalVia(String transport, String branch) throws TransportNotSupportedException;

Creates a Via header containing the correct address and port for this node.

Forking

SIP forking is feature that lets a downstream proxy "fork" a request, meaning it forwards a request to several contacts in parallel

The proxy will forward all 1xx and 2xx responses back to the caller (UAC), so it is possible for the caller to receive multiple 1xx or 2xx responses for the initial request. Each response with a different To-tag represents a new dialog.

The JAIN SIP 1.2 RA Type API specifies some events to handle forking cases, so that the multiple dialogs resulting from a forked request can be managed and cleaned up easily.

Basic model

The SIP RA’s forking support follows this basic model, and only applies if the application is using dialog activities.

  1. The original dialog activity is created when the initial dialog-creating request is sent, and the application creates a new outgoing dialog using SipProvider.getNewDialog(Transaction), SleeSipProvider.getNewDialog(Address,Address) or SleeSipProvider.getNewDialog(DialogActivity,boolean).

  2. Each 1xx response that arrives, containing a To-tag that has not been seen before in this transaction, will create a new early dialog. The exception is the first 1xx response with a To-tag; this is taken to be part of the original dialog.

  3. When a 2xx response arrives, the dialog that it matches is retained and goes to the Confirmed state. All other early dialogs are ended.

  4. When any other final response arrives (3xx-6xx), all early dialog activities are ended.

UAC procedures

When sending an initial dialog-creating request, the UAC SBB should attach to the original dialog activity. If a forked response arrives that creates a new dialog, the SIP RA will fire an event on the original dialog activity, so that the SBB can know that there is a new dialog activity. The event object is a DialogForkedEvent.

The SBB can get a reference to the new dialog activity by calling DialogForkedEvent.getNewDialog(). The SBB can attach to the new activity and send requests on it.

If a 2xx response arrives on any early dialog, the RA will fire the normal response event. If a 2xx response arrives with a different To-tag to any early dialog, the RA will fire another DialogForkedEvent. All other early dialogs will be ended.

Any late 2xx responses with different To-tags will be dropped. In this case the SIP RA will send an ACK then a BYE to tear down this forked dialog at the server. Late 2xx responses are not passed up to the SBB.

UAS procedures

UAS applications typically do not fork; however if the application is a back-to-back user agent (B2BUA), then it may need to create multiple server dialogs to correspond to the client dialogs created in the UAC procedures above. This is essential if the application needs to receive mid-dialog requests while the dialogs are in the Early state.

The JAIN SIP resource adaptor type defines the method SleeSipProvider.forwardForkedResponse(). This method should be called when the UAS forwards a dialog-creating response with a different To-tag to the original dialog. This method will forward the response upstream, and return a new dialog activity. The application can attach to the forked dialog activity in order to receive any mid-dialog requests on the dialog.

The dialog activity contains a reference to the original server transaction. When a final response is sent on the transaction, or by passing a 2xx response to SleeSipProvider.forwardForkedResponse(), all other early dialog activities will end automatically.

Example

The B2BUA example application handles forking events and the multiple client and server dialogs that may be created.

Lazy Parsing

The SIP RA uses a lazy parser, which parses the headers in incoming SIP messages

This means that headers are not parsed until they are needed, either internally within the RA or by the application accessing a header.

Headers parsed as needed

Critical headers such as Call-ID, CSeq, and Via will always be parsed by the RA, as these are essential to correct operation of the protocol. However most other headers need not be parsed at all until accessed by an application. This is a performance benefit, and can also aid interoperability with user agents that may not strictly follow SIP syntax.

Lazy parsing failures

If an application calls Message.getHeader(), and the RA is unable to parse the header, a LazyParsingException will be thrown. This is an unchecked exception. In this case, an application might want to get the unparsed header value so that it can parse the header itself. This might be required when communicating with a user agent that is known to have bugs in SIP syntax.

An application can retrieve the unparsed value of a header by casting the Message object to a LazyParsedMessage, and calling getUnparsedHeaderValue(). This returns the string value of the header, which the application can parse itself. If the header is a multi-value header, the getUnparsedHeaderValues() method should be used, which returns an array of strings.

Persistent Outbound Connections

The draft RFC draft-ietf-sip-outbound-03 describes a feature that lets a SIP client initiate a persistent SIP connection to a SIP proxy server on the other side of a firewall/NAT

Using the procedures in the RFC, the connection is kept open, and the proxy is able to route incoming SIP messages over the connection created by the client. In this way, clients that are behind a firewall/NAT can receive calls, even though their IP address is not externally visible.

Maintaining persistent connections through a firewall

The SIP RA has been enhanced to support this draft RFC. This page describe the procedures that must be used on the client and server sides to setup and maintain persistent connections through a firewall.

The SIP RA implements the JAIN SIP 1.2 API. The JAIN SIP API does not have any notion of "connections", so applications cannot manipulate a connection object. The support for the draft RFC has been implemented so that applications still use the JAIN SIP API, and connections are setup and torn down using specially-formed REGISTER requests, as described below.

Procedures at the client

Below is a summary of persistent outbound connection procedures at the client.

Initiating a persistent connection

The client initiates a persistent connection to a server by registering as described in the draft RFC, passing +sip.instance and reg-id parameters in the Contact header of the REGISTER request.

The instance parameter must uniquely identify this user agent, and must be persistent across reboots. It is up to the user agent to derive this instance identifier. The draft RFC recommends using a UUID.

The reg-id parameter is an integer, and identifies the connection to a particular endpoint. If the client wishes to initiate more than one persistent connection to the same host, each REGISTER request must specify a different reg-id. An example REGISTER message is shown below:

REGISTER sip:ext-proxy.example.com:5060;transport=tcp SIP/2.0
Via: SIP/2.0/TCP 192.168.0.100:5060;branch=z9hG4bKf-EmiaQUlzbxheKMdLiiaA
From: <sip:client@example.com>
To: <sip:client@example.com>
Call-ID: oNSYps1sGnhIHM2wwt329A
CSeq: 1 REGISTER
Max-Forwards: 70
Contact: <sip:192.168.0.100:5060;transport=tcp>;+sip.instance="<urn:uuid:00000000-0000-0000-0000-000000000001>";reg-id=1;expires=3600
Content-Length: 0

When the client attempts to send a REGISTER in this way, the SIP RA detects that a new persistent connection is required, and opens a connection to the server. If a successful response is received, then the stack marks the connection as persistent, and will automatically perform the heartbeat and reconnection procedures specified by the draft RFC. If the registration response is not successful, or times out, an alarm is raised and the RA will initiate the reconnection procedures, and will try to reconnect and register again.

Sending requests on a persistent connection

To send requests on the connection, the client only has to ensure that the request is routed to the same server that handled the REGISTER. This can be achieved by inserting a Route header in any request. For example, if the client had registered as above, it would use the Route header:

Route: <sip:ext-proxy.example.com:5060;transport=tcp;lr>

If the RA sees that a request is destined for a server that it already has a persistent connection for, then it will send the request on that connection. If there are several persistent connections open to the server, then the most recently created one is used, as specified by the draft RFC.

If a persistent connection has gone down, the RA will automatically attempt to reconnect at intervals defined in the RFC (normally starting at 30 seconds). If a client tries to send a request on the connection and it is not currently available, then a SipException will be thrown.

Re-registering

The RA will automatically re-register in the case of a connection failure, but the application is still responsible for re-registering before the previous registration expires. When the initial registration response is received, the Contact header will contain an expires parameter specifying the lifetime of the registration, in seconds. The application must set the appropriate timers so that it re-registers before the registration is due to expire.

Connection events

After establishing a persistent connection as above, an SBB client may attach to a PersistentOutboundConnection activity to be notified when the underlying connection goes up and down.

Tip See the example PersistentOutboundConnectionSbb.

Heartbeats

The RA uses the STUN heartbeat mechanism to check that the server is still responding, and also to check if the NAT mapping has changed. If a persistent connection is idle for 90 seconds, a STUN bind request is sent. If no response is received, or if the response indicated that the NAT mapping has changed, then the RA will automatically close the connection and begin reconnection procedures. No application involvement is required for handling the STUN messages, this is done entirely by the RA.

Tearing down a persistent connection

Tearing down a persistent connection is done by sending an unregister request. This is a REGISTER request as above, but with an expires value of zero. When this request is sent on a persistent connection, the RA knows it can close the connection, and it does not begin reconnection procedures, and no alarm is raised.

If the unregister request is sent when the connection is already down, the RA will treat this as a signal that the connection should be closed permanently and no more reconnection attempts will be made. Any alarms associated with the connection are cleared. The RA will pass an OK response back up to the application.

Example

An example service is included which creates and tears down a persistent connection, using the REGISTER procedures above.

Tip See PersistentOutboundConnectionSbb.java in src/com/opencloud/slee/services/sip/persistent. The Ant target deploy-persist-conn will deploy and activate the service.

The service will attempt to register when it is activated. The server it tries to register with is specified in the sip.properties variable PERSISTENT_REGISTRAR_URI. By default this is localhost:5080, but should be changed to the URI of another SLEE running the example registrar service.

Alarm types

The alarm types are:

  1. sip.persistent.connectionDown

    Description

    Raised when a single connection to an endpoint fails. Cleared automatically when the connection is restored, or if the connection is closed permanently using an unregister request.

    Source

    sip.transport.persistent.outgoing.<endpoint>.<reg-id>

    Level

    MINOR

    Message

    Connection to <endpoint> down, reg-id=<reg-id>

  2. sip.persistent.allConnectionsDown

    Description

    Raised when the SIP RA has no persistent outbound connections to an endpoint. Cleared automatically when at least one connection is restored, or all connections have been unregistered.

    Source

    sip.transport.persistent.outgoing.<endpoint>

    Level

    MAJOR

    Message

    No persistent connections available to <endpoint>

Procedures at the server

Below is a summary of persistent outbound connection procedures at the server.

X-Flow-ID header

The SIP RA uses a proprietary header, X-Flow-ID, to indicate to the server the applications whose incoming "flow" (connection) a request was received on. The header contains a string token which uniquely identifies an incoming flow on this server. The header is only set on incoming requests when the request is received on an existing persistent incoming flow, or it is an initial REGISTER request which is creating a new incoming flow.

Similarly, if an application wants to send a request on a particular incoming flow, it can set the X-Flow-ID header in the request before sending it. The RA will see the header and attempt to send on the flow indicated by the Flow-ID, throwing an exception if the flow is not present.

The RA will always remove the X-Flow-ID header before the request is sent on the network.

Registrar

Registrar applications must check incoming REGISTERs to see if the X-Flow-ID header is present, and if so, save the Flow-ID along with the other registration details. This is so that a proxy application can look up the registration and route requests to the correct flow.

The example RegistrarSbb has been updated to save the flow, along with the other registration details.

Tip See RegistrarSbb.java in src/com/opencloud/slee/services/sip/registrar.

Proxy

Proxy applications must be prepared to route requests to particular flows. When a proxy looks up a user’s registration details, it can get the Flow-ID (if present), and insert an X-Flow-ID header so the RA will send the request on the correct flow.

If the proxy is a record-routing proxy, meaning that it will see all requests in a dialog, then the proxy must take care to record-route correctly so that subsequent requests in the dialog will be sent on the correct flow. The draft RFC is not specific about how this is done, but one procedure that works is the "double record-route".

When processing an initial request, the proxy must check if the request was received on an incoming flow, or is destined for an incoming flow. If either is true, the proxy inserts two Record-Route headers. Each header may contain a Flow-ID. Later, when one of the parties in the dialog sends a subsequent request, it will arrive at the proxy with two Route headers, denoting the incoming and/or outgoing flows. The proxy removes both Route headers, and sends the request out on the flow specified in the second Route header.

Tip The example ProxySbb has been updated to retrieve flow information and use the double record-routing procedure described above. See ProxyRouter.java in src/com/opencloud/slee/services/sip/proxy.

Server-initiated connection close

The server may forcibly close a persistent incoming connection at any time, by using the proprietary API call OCSleeSipProvider.closeInboundFlow(). This method takes a string parameter, which is the value of the X-Flow-ID header, received with all requests on the connection.

Abnormal or client-initiated connection close

If the connection is closed by the client or is dropped for any reason, other than a call to closeInboundFlow() as above, the server’s RA will generate an un-REGISTER request and pass this up to the SLEE, so that registrar applications can remove the registration associated with that connection. The request will contain the correct address-of-record, contact address, instance-id, and reg-id for the connection, and will have an "expires" value of zero. The response to this request is handled internally, and will not be transmitted over the network.

Note that if the client unregisters normally and closes the connection, the stack will still generate this request, but this will have no effect since the registration would have already been removed.

If the connection is closed by the server using closeInboundFlow(), the un-REGISTER request is not generated, it is assumed that the application will be responsible for cleaning up registration state for that connection.

System properties

The following JVM system properties may be set to alter the default persistent outbound connection behaviour.

Property What it specifies Values
Default

opencloud.sip.outbound.reconnect.base-time

The base wait time between client reconnection attempts, if there are other flows still active to the same server. If a reconnection attempt fails, the wait time between subsequent reconnection attempts backs off exponentially, up to a limit of max-time.

time in seconds
90

opencloud.sip.outbound.reconnect.base-time-all-failed

The base wait time between client reconnection attempts, if there are no flows active to the same server. If a reconnection attempt fails, the wait time between subsequent reconnection attempts backs off exponentially, up to a limit of max-time.

time in seconds
30

opencloud.sip.outbound.reconnect.max-time

The maximum wait time between reconnection attempts, after backing off due to earlier failed attempts.

time in seconds
1800

opencloud.sip.persistent.connectTimeout

The maximum time to wait for a connection attempt to succeed.

time in milliseconds
15000

opencloud.sip.persistent.registrationTimeout

The maximum time to wait for a registration attempt to succeed, after a new connection was established.

time in milliseconds
15000

opencloud.sip.persistent.heartbeatTimeout

The maximum time to wait for a response to a STUN heartbeat request.

time in milliseconds
15000

opencloud.sip.persistent.idleTimeout

The time between sending STUN heartbeat requests, if there has been no activity on a flow.

time in milliseconds
90000

opencloud.sip.rfc5626Variant

Switch connection behaviour between RFC 5626 and draft-ietf-sip-outbound-03.

draft03
rfc5626

RFC 3263: Failover and Load Balancing

RFC 3263 — Locating SIP Servers specifies this feature, that lets a SIP client use DNS procedures to resolve a SIP URI into the address, port, and transport protocol of the next server to contact

In addition, these DNS procedures can result in multiple addresses that clients can try sequentially if earlier addresses failed.

Note The SIP RA now supports RFC 3263 to provide failover and load balancing of outgoing requests. Previously the SIP RA used RFC 3263 only for determining the first address to contact; it did not automatically try backup addresses if the first address failed. Now the SIP RA has been updated to automatically failover to backup addresses as well, with no intervention required from the application.

About the RFC 3263 process

When a SIP client sends a request, it must select either the first Route header’s URI, if present, or the Request-URI. This URI determines where the request is sent to (the next-hop address). This URI might only contain a domain name, such as sip:bob@example.com. RFC 3263 DNS procedures are required to convert the URI into the address, port, and transport protocol of an actual SIP server (or servers).

At a high level there are three parts to the RFC 3263 DNS process. Some of these may be skipped depending on what information is already given in the URI, such as transport, IP address, or port numbers. Here is a simplified description of the process that applies to any SIP client using RFC 3263.

  1. Determine the transport protocol. If not already specified in the URI, the client will do a DNS NAPTR lookup on the domain. This may return some NAPTR records which specify in order of preference the transport protocols that shall be used. The NAPTR records will contain SRV addresses for each supported transport protocol.

  2. Next, determine the port. This can be found by looking up the SRV address in the NAPTR record. Or, if there were no NAPTR records, the SIP client will try the default SRV addresses for its preferred transport protocols, such as _sip._tcp.example.com. The SRV query may return one or more SRV records. Each record contains the hostname and port of a SIP server. Multiple SRV records are sorted according to their priority and weight, and ordered randomly as per RFC 2782. This means that the results will be ordered slightly differently every time, providing a form of load balancing.

  3. Finally the hostnames provided in SRV records must be looked up to obtain their IP addresses. Sometimes this information is already included in the SRV response. If there were no SRV records, the SIP client will default to looking up the IP address of the hostname in the URI.

At the end of this process the SIP client has a list of (address, port, transport) tuples to try. If the list is empty then the request cannot be routed and will fail. Otherwise, the client picks the first address in the list and starts a client transaction. The next backup address will be tried if the transaction fails for any of these reasons:

  • the server responds with 503 (Service Unavailable)

  • the transaction times out

  • the transaction fails with a transport error.

When such a failure occurs, the client selects the next address and tries again with a new client transaction. If all addresses failed, then the client must fail the request.

SIP RA usage of RFC 3263

The SIP RA closely follows the RFC 3263 procedures but with some adaptations, described below.

Use of client transactions

RFC 3263 states that a new client transaction is used for each backup address. The SIP RA does this; however these additional client transactions are hidden from the application. The application just creates a single JAIN SIP ClientTransaction to send a request, as before. If a failure occurs and there are backup addresses available, the RA automatically creates a new client transaction and sends the request to the backup address. The RA takes care of routing the responses and other events up to the application’s ClientTransaction activity so that it looks like a single client transaction was used.

Each new client transaction created for contacting backup servers will have a new Via branch ID (as per the RFC), but these are derived from the original branch ID with a different suffix for each new transaction. For example if the original branch was z9hG4bK776asdhds, any subsequent transactions will have branches z9hG4bK776asdhds%1, z9hG4bK776asdhds%2 so on. In this way the branches for each transaction are still unique in the RA and the network, but can be correlated when looking at logs or packet captures. The application will only see the original branch ID in responses.

The first final response to be received will end the transaction, and no more backup addresses will be tried. If all of the available addresses failed, the application will see the last error response received from a server (a 503 or 408 response), or the RA will generate a 503 response and pass it up to the application.

Failover timer

Failover to a backup server is triggered by transaction timeouts, transport errors, or 503 responses. If a server has failed completely, the resulting transport error (such as ICMP Port Unreachable) may not be directly visible to the RA, especially when using UDP. Or there may be no ICMP rejects at all in the event of a network partition. This means failover will not occur until the transaction timeout (Timers B or F in RFC 3261) occurs, which will usually be 32 seconds!

For many applications it is desirable to try and detect failures a bit faster than this. The SIP RA defines an additional failover timer, which can expire before the default transaction timeout to trigger a failover faster. The default value of this timer is 10 seconds.

The failover timer is only started when there are multiple addresses to try. If there is just a single address, the normal transaction timeout behaviour applies. The timer is stopped as soon as any response (including 100 Trying) is received, as this indicates that the next-hop server is functioning. If this timer expires and no responses were received, the RA moves on to the next address.

Server blacklisting and recovery

By itself, DNS does not tell you if a server is available. When using the DNS process above, the same set of servers will always be returned, regardless of whether they are currently available or not. To avoid contacting servers that are likely to be down, the SIP RA maintains a "blacklist".

Whenever a server failure is detected, either by timeout, transport error, or 503 response, the RA adds the failed address to a blacklist so that the address is not tried again for some period of time. This avoids the RA needlessly contacting servers that are most likely going to be down. By default, servers are blacklisted for 5 minutes. After that time the RA will try using the address again.

Servers that respond with a 503 response that includes a Retry-After header will be blacklisted for the duration specified in the Retry-After header.

Configuring the SIP RA for RFC 3263

Tip See the RFC3263 configuration properties in SIP RA Features.

RFC 4028: Session Timers

Tip For details on session timer support, see Session Timers

RFC 5626: Outbound Connections

RFC 5626 describes a feature that lets a SIP client initiate a persistent SIP connection to a SIP proxy server on the other side of a firewall/NAT

Using Keep-Alives and Detecting Flow Failure, the connection is kept open and the proxy is able to route incoming SIP messages over the connection created by the client.

The SIP RA has been enhanced to support this RFC (reg-id, instance-id. and CRLF keep-alive). This page describes how to use and configure this behaviour.

Procedures at the client (outgoing connections)

At the client, this feature performs these procedures:

  • On receipt of REGISTER response, check for Flow-Timer header and Require: outbound header.

  • If Flow-Timer is set, send PINGs every 0.8-1.0*Flow-Timer seconds.

  • If Flow-Timer is not set, send PINGs at the client’s discretion.

  • If Require: outbound is set, expect to receive a PONG within 10s of each PING. If this PONG is not received, close the connection.

  • If Require: outbound is not set, do not expect to receive a PONG within 10s of each PING; and do not close connections if the PONG is not received.

Procedure at the server (incoming connections)

At the server, this feature performs these procedures:

Note

The SBB processing the REGISTER request is responsible for setting the Flow-Timer and `Require: outbound `headers in the REGISTER response.

For example:

FlowTimerHeader flowTimer =
        ((OCHeaderFactory)getSipHeaderFactory()).createFlowTimerHeader(20);
response.addHeader(flowTimer);
response.addHeader(getSipHeaderFactory().createRequireHeader("outbound"));
response.addHeader(getSipHeaderFactory().createSupportedHeader("outbound"));
response.addHeader(getSipHeaderFactory().createSupportedHeader("path"));
  • In the RA, if a Flow-Timer header is set, start a timer for Flow-Timer + 1s network delay allowance; and close the connection if a PING is not received before the timer expires.

  • If the Flow-Timer header is not set, don’t start a timer to receive PING; and don’t close the connection.

  • Receive PING from the client.

  • Send PONG back to the client.

  • If Flow-Timer is set, start a new timer for Flow-Timer + network delay allowance on receipt of each PING from the client; and close the connection if another PING is not received before the timer expires.

SIP properties

The following SIP properties (sip.properties) must be set to alter the default persistent outbound connection behaviour.

Property What it specifies Values
default

REGISTRATION_FLOW_TIMER

The wait time between client expected PING attempts. If there is no response (PING) within the specified time (in seconds), then the connection will be closed. A value of 0 means that no connections will be closed.

time in seconds
10

System Properties

The following JVM system properties must be set to alter the behaviour between RFC 5626 and draft-ietf-sip-outbound-03.

Property What it specifies Values
default

opencloud.sip.rfc5626Variant

Switch connection behaviour between RFC 5626 and draft-ietf-sip-outbound-03.

draft03
rfc5626

Replicated Dialogs

The OpenCloud SIP RA supports replicated dialog activities. This allows dialog activities to be accessed on any node in the cluster, so that SBBs can attach to dialog activity contexts and use the dialogs on any node

Dialog activity state

When a dialog activity is created, the dialog activity state is initially local to the calling node. When the dialog transitions to the "confirmed" state (that is, a 2xx response is sent or received on the initial transaction), the SIP RA replicates the dialog state to all nodes, using Rhino’s built-in memory database. From this point on, the dialog activity state is available to all nodes.

If a node fails, any dialogs that it created can continue on other nodes. If another node receives a request for a dialog it has not seen before, it will check the replicated state and continue processing the request as an in-dialog request. Any SBB entities that were attached to the dialog activity context will be able to receive the request. This assumes that the SIP client is able to detect the node failure and send future requests to a different node. This can be achieved using a load balancer or DNS SRV records, for example.

Performance

The performance of FT dialogs will be slower due to the replication and transaction cost. Updates to replicated dialog activity state are performed transactionally, and require a distributed lock to be acquired by the node updating dialog state. For this reason the latency will be larger than when using non-replicated dialogs.

Example

The example B2BUA service supports the use of replicated dialogs. To deploy a fully replicated B2BUA, ensure that the RA is deployed with the ReplicatedDialogSupport=true property (see Configuration), and also set the B2BUA_REPLICATED property in sip.properties to True. The latter ensures that Rhino deploys the B2BUA service as a replicated service, so that SBB CMP state is replicated as well.

SCTP Support

The SIP RA supports SCTP (RFC 4960) as a SIP transport, in addition to UDP, TCP, and TLS

SIP’s usage of SCTP is specified by RFC 4168, which the SIP RA adheres to. This page covers how to configure and use SCTP in your SIP RA applications.

Background

Stream Control Transmission Protocol (SCTP)) is a transport protocol designed for call-control signaling . Like TCP, it provides reliable, ordered, end-to-end delivery of messages, but has many features that make it better than TCP for some applications. The main benefits of using SCTP for SIP are:

  • No "head of line" blocking: SIP SCTP messages are sent and received independently over the same association. A delay in routing or processing one message does not affect other messages. In contrast, TCP has to ensure that all bytes on a connection are processed sequentially, so delays processing earlier messages will hold up later messages.

  • Multi-homing: An SCTP association (connection) can be bound to multiple interfaces on a host. This means that associations can survive interface or network failures with no application involvement.

The SIP RA follows RFC 4168 when sending and receiving messages:

  • All SIP messages are expected to use a Payload Protocol Identifier of 0. The SIP RA ignores messages with a different PPID.

  • By default, the SIP RA sends messages on stream number 0 using unordered delivery. This avoids the "head of line" blocking issue.

  • If a request arrives on another stream, the SIP RA will accept it and send any responses on the same stream.

Supported platforms

The SIP RA’s SCTP implementation requires Java Standard Edition 7, running on Linux 2.6 or Solaris 10 (or later versions of the same).

In addition, on Linux the lksctp-tools package must be installed.

Tip See SCTP - Getting Started for more information.

The SIP RA’s SCTP support uses the Java SCTP API, via the Netty library.

Using SCTP in your SIP applications

Below are instructions to configure the SIP RA, and send requests, and responses over SCTP.

Configure the SIP RA

Before using SCTP, it must be enabled in the SIP RA. To enable it, just add SCTP to the RA’s "Transports" configuration property, along with TCP, UDP, or TLS. For example, in rhino-console:

[Rhino@localhost (#0)] updateraentityconfigproperties sipra Transports TCP,UDP,SCTP
[Rhino@localhost (#1)] deactivateraentity sipra
[Rhino@localhost (#2)] activateraentity sipra

When the SIP RA is activated, it will start an SCTP server listening on the address given by the IPAddress and Port properties.

The SIP RA is now ready to send and receive SIP messages using SCTP.

Multi-homing

SCTP multi-homing is automatically used if IPAddress is "AUTO" or a wildcard address such as "0.0.0.0". In these cases the RA binds to all of the host’s interfaces, and these addresses are advertised to SCTP peers during association setup. SCTP peers automatically probe these addresses to discover which ones are reachable.

SCTP multi-homing can also be restricted to use specific addresses. These addresses must be configured in the RA’s IPAddress and SCTP:additional_addresses configuration properties.

  • The IPAddress property defines the "primary" address. The RA will bind to this address first.

  • The SCTP:additional_addresses property may contain one or more addresses, separated by commas or whitespace. The RA will attempt to bind to these addresses, after first binding to IPAddress.

  • If unable to bind to any one of these addresses, RA activation will fail, and a SLEE alarm will be raised.

Note If IPAddress is a wildcard address then all additional addresses will be ignored. Wildcard addresses in SCTP:additional_addresses are also ignored.

Send requests over SCTP

When sending a request, the SIP RA determines the transport protocol using the RFC3263 process. There are several ways to ensure that this process selects the SCTP transport for a request. These include setting the target URI’s "transport" parameter and using NAPTR and SRV DNS records as per RFC 3263.

Set the target URI’s "transport" parameter

The target URI is the URI from the first Route header, if present, or the Request-URI. Setting the URI’s transport parameter to "sctp" will force the RA to use SCTP to send the request.

In the JAIN SIP API this would be done as follows:

Request invite = messageFactory.createRequest(...);
SipURI requestURI = (SipURI) invite.getRequestURI();
requestURI.setTransportParam("sctp");
Use NAPTR and SRV DNS records as per RFC3263

If the request’s target URI does not specify a transport, then the RA must lookup the NAPTR record for the URI’s host. For example, if the URI was sip:bob@example.com, the RA would query the "example.com" domain for any NAPTR records. These specify the preferred SIP transports for the domain. For example:

;        order pref flags service    regexp replacement
IN NAPTR 50    50   "s"   "SIP+D2S"  ""     _sip._sctp.example.com.
IN NAPTR 90    50   "s"   "SIP+D2T"  ""     _sip._tcp.example.com.
IN NAPTR 100   50   "s"   "SIP+D2U"  ""     _sip._udp.example.com.

Here the preferred record (lowest order value) specifies the SCTP transport (with the SIP+D2S service key), and says the client should then lookup the SRV record _sip._sctp.example.com to get the port and IP addresses to use. The SIP RA does this automatically.

By configuring DNS appropriately for your domain as per RFC 3263, you can ensure that requests sent to public addresses like sip:host.yourdomain.com will automatically use SCTP or any other protocol.

Send responses over SCTP

Tip Nothing special needs to be done for responses; they are always sent on the same transport that the request arrived on.

Session Timers

RFC 4028 — Session Timers in the Session Initiation Protocol specifies how SIP applications can make use of session timers to detect failed dialogs.

The SIP RA can optionally use session timers for any dialog activity.

How session timers work

Session timers are disabled by default. An application that wants to use session timers must request them when creating dialog activities.

The session timer for a new dialog is configured using a SessionTimerOptions value. This value contains the mandatory session interval parameter, which is the time in seconds after which the dialog (session) is considered to have expired, if there were no session refresh requests sent or received in that time. RFC 4028 defines how the two parties on a dialog can negotiate the session interval, but it must always be at least 90 seconds.

A session refresh request is any re-INVITE or UPDATE sent on the dialog. If a session refresh request is successful, this means that both parties on the dialog are still alive, so the session timer is reset and the dialog can continue. If a session refresh request times out, or fails with 481 (dialog does not exist), then the application should terminate the dialog.

During dialog setup, one party on the dialog is selected to be the refresher, who must send a session refresh request periodically. An application can indicate its preferred refresher in its SessionTimerOptions, but this may not be applied, for example if the other party does not support session timers.

Note RFC 4028 is designed so that session timers can still be used even if only one party on the dialog supports them.

If a dialog activity in the SIP RA was selected to be the refresher, then periodically the SIP RA will fire a SessionRefreshRequiredEvent on the dialog, which tells the application that it should initiate a session refresh request. If this request fails with a timeout or 481 response then the application should terminate the dialog with a BYE.

Note Dialogs are deliberately not terminated automatically by the RA when a session refresh fails or a session expires. This is so the application has full control of how it cleans up its resources.

Regardless of who is selected to be the refresher, any successful re-INVITE or UPDATE on the dialog is implicitly a session refresh request, and resets the session timer automatically so the dialog may continue.

If there were no successful session refreshes within the session interval, then the SIP RA considers the dialog to be expired, and fires a SessionExpiredEvent. The application must then terminate the dialog by sending a BYE request.

Creating dialogs with session timers

To request the use of a session timer for a dialog, the application must pass in a SessionTimerOptions value when creating the dialog, before sending or responding to the initial request.

The dialog must be created using a dialog builder from OCSleeSipProvider.newDialogBuilder(). This lets you specify SessionTimerOptions in addition to other dialog options. For example:

// Create SessionTimerOptions value with 5 minute session interval.
SessionTimerOptions timerOptions = SessionTimerOptions.builder(300).build();

// Create new outgoing dialog activity using our timer options.
DialogActivity d = sleeSipProvider.newDialogBuilder()
                                  .outgoing(from, to)
                                  .withSessionTimer(timerOptions)
                                  .newDialog();

The session timer is activated when a successful response to the initial INVITE is sent or received. If the initial INVITE is not successful, then the dialog is not created and no session timer is started.

UAC behaviour

When a UAC application creates an outgoing dialog with a session timer, the initial outgoing INVITE is updated automatically by the SIP RA using the dialog’s SessionTimerOptions:

  • the Supported: timer header is added

  • the Session-Expires header is added, using the requested session interval

  • if the SessionTimerOptions value specifies a refresher, this is used for the "refresher" parameter in the Session-Expires header

  • if the SessionTimerOptions value specifies a minimum session interval, then a Min-SE header with this value is added to the request.

UAC behaviour includes handling 2xx, 422, and other error responses.

Handling 2xx responses

When a 2xx response arrives for the initial INVITE, the session timer is started, taking into account the session timer headers from the response:

  • If no session timer headers are present in the response, this means the UAS does not support session timers. In this case the UAC will start the session timer using its original settings, and act as the refresher.

  • The UAS may lower the Session-Expires value in its response, but not below the value of Min-SE, if it was present in the request. If the UAS wants a larger session interval it must send a 422 (Session interval too small) response.

  • If the UAC did not specify a refresher, then the UAS must decide who is the refresher, and add the "refresher" parameter to the Session-Expires header in its response. In case the UAS does not specify a refresher either, the UAC will be the refresher.

Handling 422 responses

A 422 response indicates that the UAS wants a larger session interval. The UAS specifies this value in the Min-SE header of the 422 response.

The UAC should retry the initial request using the larger session interval. This is not done automatically by the SIP RA, so that the application has a chance to decide whether it wants to retry or just abort the call.

The application can ask the RA to create a new dialog with the original parameters but with an updated session interval using OCSleeSipProvider.retryInitialSessionRefresh():

private void on422Response(ClientTransaction ct, Response response) {
    // create new dialog with SessionTimerOptions updated from 422 response
    DialogActivity newDialog = getSleeSipProvider().retryInitialSessionRefresh(ct, response);

    // attach SBB to new dialog
    ActivityContextInterface newACI = getSipACIFactory().getActivityContextInterface(newDialog);
    newACI.attach(getSbbLocalObject());

    // Send INVITE on the new dialog
    Request newInvite = newDialog.createRequest(Request.INVITE);
    newDialog.sendRequest(newInvite);
}
Handling other error responses

No special session timer handling is required for other error responses to the initial INVITE. No session timer is started and the dialog activity is terminated.

UAS behaviour

Enforcing a minimum session interval

When a UAS application receives an initial INVITE, it should first check that the request’s Session-Expires value (if present) agrees with the UAS application’s desired minimum session interval. The application should inspect the request and send a 422 response if the request’s session interval was too small.

This check can be done simply by using the convenience method rejectIfBelowMinSE():

public void onInitialInvite(RequestEvent event, ActivityContextInterface aci) {
    ServerTransaction st = event.getServerTransaction();

    boolean rejected = getSleeSipProvider().rejectIfBelowMinSE(st, 600);

    if (rejected) {
        return;  // 422 has been sent, nothing more to do
    }

    // continue processing initial INVITE...
}

This method checks the Session-Expires value in the incoming request. If it is too small, a 422 response will be sent automatically, containing the desired session interval in the Min-SE header. The method returns true so the application knows it has responded. No more processing is required; it is up to the UAC to retry if it wants.

Otherwise the application can continue processing the request, knowing that the INVITE’s Session-Expires was large enough.

Sending the 2xx response

Once the minimum session interval has been checked, the UAS application can create the dialog, passing in its desired SessionTimerOptions.

When the UAS application sends a 2xx response, it will be updated according to the session timer headers in the initial request, and the dialog’s SessionTimerOptions:

  • The Require: timer header will be added, if the request contained Supported: timer.

  • The Session-Expires header from the request will be used, unless the UAS’s timer options requested a lower value (but not lower than Min-SE).

    Note

    The UAS cannot increase the session expiry, or lower it below Min-SE. If the UAS wanted to increase the session expiry it should have rejected the INVITE with 422 (see Enforcing a minimum session interval).

  • The "refresher" parameter in the Session-Expires header will be set.

    • If the UAC dos not support session timers, then the UAS is always the refresher.

    • If the UAC specified a refresher, then this value will be used.

    • Otherwise, the refresher specified by the UAS’s SessionTimerOptions is used.

    • If the UAS did not specify a refresher either, then the RA’s SessionTimer:default-refresher config property is used (see Configuration).

ForkActivity support

Dialogs created by a ForkActivity "big fork" operation can also make use of session timers.

The application can pass its desired SessionTimerOptions value to the ForkActivity.forkTo() method.

When the INVITE is forked to its targets, each copy of the INVITE will contain the session timer headers corresponding to the SessionTimerOptions value that was supplied.

If there is a winning 2xx response, this creates a dialog and activates the session timer. The negotiated session timer options are derived as described in handling 2xx responses.

Subsequent invocations of ForkActivity.forkTo() on the same ForkActivity may pass in different SessionTimerOptions values. The winning 2xx cancels all other branches, and the resulting dialog’s session timer options are based on those that were passed to the forkTo() call that sent the winning INVITE.

Accessing session timer state

The session timer state of a dialog activity can be queried using SessionTimerDialog.getSessionTimer(). SessionTimerDialog is a subclass of Dialog, and any dialog activity object may be safely type cast to this type.

The SessionTimer interface shows whether the timer is active or expired, and also shows the dialog’s currently applied SessionTimerOptions.

Session refreshes

Once the dialog has been setup successfully, a session expiry timer is started using the session interval negotiated by the initial INVITE transaction. Session refresh requests must now be sent periodically to reset the expiry timer and keep the session alive. The party selected to be the refresher will do this at regular intervals if there has been no other activity, but either party may send a session refresh request at any time.

Refresher (UAC) behaviour

The dialog of the refresher starts another timer that fires halfway through the session expiry interval (as per RFC 4028). If no other session refresh requests are seen on the dialog, this timer causes a SessionRefreshRequiredEvent to be fired on the dialog’s ACI. When the application receives this event, it should send a re-INVITE or UPDATE request on the dialog to perform the session refresh.

Sending the session refresh request

The session refresh request can be created as a normal mid-dialog request, for example using Dialog.createRequest(). The SessionTimer interface also provides methods for creating the session refresh request, which can also take a SessionTimerOptions value, so that the session timer can be re-negotiated if necessary.

Note

When sending a re-INVITE, the application must set the message body to contain the currently active SDP, from the last successful offer-answer exchange.

Using UPDATE is recommended by RFC 4028, if the other party allows it.

The application sends the request by the usual means, for example DialogActivity.sendRequest(). When the session refresh request is sent, the SIP RA will update the request using the current SessionTimerOptions, or any new options that were passed to generateSessionRefreshRequest():

Receiving the session refresh response

If a 2xx response is received for the session refresh request, the session timer is reset, and any new session timer options are applied, such as changing the session interval or the refresher.

If the session refresh request fails with an error response or timeout, then the session timer is not reset. The session timer will eventually expire unless a new session refresh request is sent and succeeds.

If the request fails with a timeout, 408, or 481 response, the application should terminate the dialog with a BYE.

For all other error responses, the application should retry the session refresh as appropriate based on the type of error.

SIP ResponseEvents or TimeoutEvents for a failed session refresh request are decorated with the SessionRefreshFailedEvent marker interface. This can be used to check if the event should be processed as a failed session refresh, as in the example below.

private void processResponse(ResponseEvent event, ActivityContextInterface aci) {
    if (event instanceof SessionRefreshFailedEvent) {
        if (((SessionRefreshFailedEvent)event).shouldTerminate()) {
            // Must have been 408 or 481, I need to send BYE now
        }
    }
}

Refreshee (UAS) behaviour

When a session refresh request is received on a dialog activity, it will be fired as a normal mid-dialog request event.

The UAS application can just respond to the request as it would normally. The SIP RA will automatically add session timer headers to the response.

If the UAS wants to change the session timer options, it can use the method generateSessionRefreshResponse(), passing in a new SessionTimerOptions value. Note that the new session interval must fall within the refresh request’s Session-Expires and Min-SE range — values outside this range will be automatically restricted to the nearest upper or lower bound.

If the response is a 2xx (success) response, the dialog’s session expiry timer will be reset using the currently agreed session interval. If either party requested that the UAS becomes the refresher, then the dialog remembers that it is now the refresher, and starts a session refresh timer that will expire halfway through the session interval to fire a SessionRefreshRequiredEvent on this dialog activity.

If the response is an error response, the session expiry timer is not reset, so the session will expire eventually unless a subsequent session refresh request (from either party) is successful.

Note

If responding to a re-INVITE, the application must set the message body to contain the currently active SDP, from the last successful offer-answer exchange.

Session expiration

If there were no successful session refresh requests sent or received within the session interval, the dialog’s session expiry timer will fire, causing a SessionExpiredEvent to be fired on the dialog activity.

Session expiry time

As per RFC 4028 §10, the session expiry timer fires slightly before the end of the session interval.

If the session interval is E seconds, then the timer fires at E - min(32, E/3) seconds.

In practice this means E - 32 seconds for most values of E, greater than 96 seconds.

The application processing the SessionExpiredEvent should terminate the dialog by sending a BYE. Again this is not done automatically by the RA, so that the application can control how it cleans up its resources.

If no SBB processed the SessionExpiredEvent, the RA will automatically send a BYE. This avoids dialogs leaking if no service was active or able to process the event.

Warning

Once a dialog’s session timer has expired, there is no going back. The application cannot reset the timer and carry on. The dialog activity is still operational, but the only sensible course of action is to terminate the dialog as soon as possible.

Proxy mode

Applications that need to forward messages between dialogs, such as B2BUAs, might just want to pass all messages through without directly taking part in session timer negotiation and refreshing. But it would still be useful if the application could be notified when a session expired, if the endpoints were using session timers.

This is what proxy mode is used for. In proxy mode, a dialog does not need to configure any session timer options. Instead it observes the session interval negotiated by the endpoints on the dialog, and starts a session expiry timer.

The dialog does not initiate any session refresh requests on its own. It observes all session refresh requests that pass through, and resets the expiry timer when a refresh succeeds.

If neither endpoint sends a successful session refresh within the session interval, a SessionExpiredEvent is fired so the application can clean up the expired dialog.

Enabling proxy mode on a dialog

All that is required to enable proxy mode for a dialog is to use the special SessionTimerOptions constant value, SessionTimerOptions.PROXY, when creating the dialog. No further configuration is needed.

Now if either endpoint uses a session timer, the dialog will observe the Session-Expires header in the initial request and/or response, and use this value to start its own session expiry timer.

If a successful session refresh request re-negotiates the session interval, this will be observed as well, and the session expiry timer will be reset with the new value.

Replication

When SIP dialog replication is enabled (using the ReplicatedDialogSupport=true config property), session timers are replicated as well so they can recover from a node failure.

In a Rhino cluster, session timer refresh and expiry events will be fired on the same node that created the dialog activity.

If a node fails, its dialogs are automatically taken over by surviving nodes in the cluster. When a dialog is recovered on a surviving node, any session timers for that dialog are recovered as well, and will now fire on the surviving node at the expected time.

Subsequent session timer refresh and expiry events for the dialog activity will now fire on the node that took over that dialog.

Configuration properties

The SIP RA’s session timer support adds one new configuration property: SessionTimer:default_refresher.

This is only required when a UAS has to select the refresher for a dialog, because the UAC did not specify one, and the UAS application did not set a refresher in its SessionTimerOptions for the dialog. In this case the SessionTimer:default_refresher property is used so the UAS can decide.

All other session timer parameters are configured by the application for each dialog, using the appropriate SessionTimerOptions values.

Application changes to use session timers

To use session timers, an application must:

  1. Create dialogs using OCSleeSipProvider.newDialogBuilder() and passing in a SessionTimerOptions value.

  2. When using ForkActivity, pass SessionTimerOptions to ForkActivity.forkTo() when initiating a fork operation.

  3. UAS applications may use rejectIfBelowMinSE() to enforce a minimum session interval.

  4. UAC applications may use OCSleeSipProvider.retryInitialSessionRefresh() to retry the initial request when handling a 422 response.

  5. Handle SessionRefreshRequiredEvents and send re-INVITE or UPDATE requests when needed.

  6. Handle failed session refreshes and terminate the dialog if necessary.

  7. Handle session refresh requests.

  8. Handle SessionExpiredEvents and terminate the dialog.

Migrating from Older Versions

Applications written for earlier OCSIP resource adaptor types (1.4 and earlier) will need some minor changes to use the new JAIN SIP or OCSIP resource adaptor types, in particular for the SBB resource adaptor interface and event type identifiers.

SBB resource adaptor interface

In the previous resource adaptor type, the SBB RA interface was OCSipResourceAdaptorSbbInterface. This interface has been replaced by SleeSipProvider in the standard JAIN SIP 1.2 resource adaptor types.

This extends axref:jain-sip-api/javax.sip.SipProvider, and provides methods for accessing the JAIN SIP factory objects.

To access OpenCloud-specific features and headers, the OCSIP resource adaptor types can be used. This provides the OCSleeSipProvider interface, which extends SleeSipProvider.

Applications written using the previous resource adaptor type must replace all references to OCSipResourceAdaptorSbbInterface with SleeSipProvider or OCSleeSipProvider.

Event type identifiers

All event type IDs have been changed in the new resource adaptor type. All events in the JAIN SIP resource adaptor types have the vendor net.java.slee (denoting the JAIN SLEE community) and version 1.2.

Consult the table in the JAIN SIP 1.2 RA Type API to see the complete list of events.

Users of OpenCloud-specific events should also check the event table in the OCSIP 2.5 RA Type API.