This document describes how to deploy, configure, and manage the OpenCloud SIP Resource Adaptor.
Topics
getting up and running with the RA and example services |
|
properties you can adjust to configure the RA |
|
links to descriptions and instructions for the example services |
|
details of the many features of the SIP RA |
|
how session timers work with the SIP RA |
|
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.
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 PROXY_DOMAINS=opencloud.com,opencloud.co.nz
|
||
3 |
Deploy the examples: using Ant, execute the $ ant deployexamples |
||
4 |
Use a SIP user agent such as Linphone to send SIP requests to the server.
|
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.
Source code for all the example applications can be found in the |
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:
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 An IP address range may also be used, using address/mask notation; for example, |
a hostname, |
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, The RA treats incoming messages routed to these addresses as if they were local. For example, If this property is set, and the By default this property is empty, meaning the SIP RA will use the node’s IP address (as determined by the |
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 |
|
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 |
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 |
|
false |
PortOffset |
Required when running multiple cluster nodes on the same host. If |
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. |
|
false |
SCTP:additional_addresses |
By default, SCTP listen sockets will bind to the configured Like If |
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 |
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. |
|
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, |
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, |
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 |
integer, |
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 |
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 |
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 |
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 |
RFC3263:fallback_to_blacklist_address |
Specifies whether to fallback to sending to a blacklisted address, when there are no more alternatives. If false, a SipException will be thrown if no addresses are available for the request. If true (default), the last address found for the target will be tried, even if it is blacklisted, in case the address is now back up. If the address was not available then the request may timeout or generate a 503 response. |
true or false |
true |
RFC3263:blacklist_on_transaction_timeout |
Specifies whether to blacklist addresses on SIP transaction timeout (Timer F). |
true or false |
true |
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 |
|
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 |
Null or a profile table name. |
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
|
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. |
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 |
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.7.0 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 |
4 |
BigGroup.max-pool-size |
The maximum number of threads in the Big Group thread pool. |
integer, |
50 |
BigGroup.queue-size |
The maximum number of waiting threads in the Big Group thread pool. |
integer, |
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, |
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 |
|
|
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, |
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. |
|
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 |
|
see Session Timers |
|
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 |
---|---|
|
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. |
|
Determines if a hostname corresponds to the IP address of the SIP RA, or a virtual address. |
|
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. |
|
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.
-
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)
orSleeSipProvider.getNewDialog(DialogActivity,boolean)
. -
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.
-
When a 2xx response arrives, the dialog that it matches is retained and goes to the Confirmed state. All other early dialogs are ended.
-
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.
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.
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.
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:
-
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>
-
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.
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.
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 | ValuesDefault |
---|---|---|
|
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 |
time in seconds |
|
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 |
time in seconds |
|
The maximum wait time between reconnection attempts, after backing off due to earlier failed attempts. |
time in seconds |
|
The maximum time to wait for a connection attempt to succeed. |
time in milliseconds |
|
The maximum time to wait for a registration attempt to succeed, after a new connection was established. |
time in milliseconds |
|
The maximum time to wait for a response to a STUN heartbeat request. |
time in milliseconds |
|
The time between sending STUN heartbeat requests, if there has been no activity on a flow. |
time in milliseconds |
|
Switch connection behaviour between RFC 5626 and draft-ietf-sip-outbound-03. |
draft03 |
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.
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.
-
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.
-
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. -
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 a timeout or transport error, 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.
503 response handling
Servers that respond with a 503 (Service Unavailable) response are not blacklisted. If a 503 response is received, the SIP RA will then try a number of backup servers. The number of servers to try is either 2, or 10% of the number of candidate servers, whichever is greater. If all retries fail, a 504 (Server Timeout) response is returned to the application. This is intentional, to stop upstream nodes performing additional RFC3263 retries of their own, which they might have done had they received a 503 response.
Configuring the SIP RA for RFC 3263
See the RFC3263 configuration properties in SIP RA Features. |
RFC 4028: Session Timers
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 andRequire: 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:
The SBB processing the REGISTER request is responsible for setting the For example:
|
-
In the RA, if a
Flow-Timer
header is set, start a timer forFlow-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 forFlow-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 | Valuesdefault |
---|---|---|
|
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 |
time in seconds |
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 | Valuesdefault |
---|---|---|
|
Switch connection behaviour between RFC 5626 and draft-ietf-sip-outbound-03. |
draft03 |
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.
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
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 toIPAddress
. -
If unable to bind to any one of these addresses, RA activation will fail, and a SLEE alarm will be raised.
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.
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.
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.
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 arefresher
, this is used for the "refresher" parameter in theSession-Expires
header -
if the
SessionTimerOptions
value specifies aminimum session interval
, then aMin-SE
header with this value is added to the request.
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 ofMin-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);
}
UAS behaviour
UAS behaviour includes enforcing a minimum session interval and sending the 2xx response.
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 containedSupported: 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).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.
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.
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.
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.
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:
-
Create dialogs using
OCSleeSipProvider.newDialogBuilder()
and passing in aSessionTimerOptions
value. -
When using ForkActivity, pass
SessionTimerOptions
toForkActivity.forkTo()
when initiating a fork operation. -
UAS applications may use
rejectIfBelowMinSE()
to enforce a minimum session interval. -
UAC applications may use
OCSleeSipProvider.retryInitialSessionRefresh()
to retry the initial request when handling a 422 response. -
Handle SessionRefreshRequiredEvents and send re-INVITE or UPDATE requests when needed.
-
Handle failed session refreshes and terminate the dialog if necessary.
-
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.7.0 RA Type API.