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 |