<?xml version="1.0" encoding='ISO-8859-1'?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [

<!-- Include general documentation entities -->
<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
%docentities;

]>
<!-- Module's Admin Guide -->

<chapter>

  <title>&adminguide;</title>

  <section>
  <title>Overview</title>
    <para>
      The nat_traversal module provides support for handling far-end NAT
      traversal for SIP signaling. The module includes functionality to
      detect user agents behind NAT, to modify SIP headers to allow user
      agents to work transparently behind NAT and to send keepalive messages
      to user agents behind NAT in order to preserve their visibility in the
      network. The module can handle user agents behind multiple cascaded
      NAT boxes as easily as user agents behind a single level of NAT.
    </para>
    <para>
      The module is designed to work in complex environments where multiple
      SIP proxies may be involved in handling registration and routing and
      where the incoming and outgoing paths may not necessarily be the same,
      or where the routing path may even change between consecutive dialogs.
      The nat_traversal functionality is built primarily for IPv4 NAT handling
      and hasn't been adapted to support IPv6 session keepalives.
    </para>
  </section>

  <section>
  <title>Keepalive functionality</title>
    <section>
    <title>Overview</title>
      <para>
        The nat_traversal module implements a very sophisticated keepalive
        mechanism, that is able to handle the most complex environments and
        use cases, including distributed environments with multiple proxies.
        Unlike existing keepalive solutions that only send keepalive messages
        to user agents that have registered (during their registration), the
        nat_traversal module can keepalive a user agent based on multiple
        conditions, making it not only more flexible and more efficient, but
        also able to work in environments and with use cases where a simple
        keepalive implementation based on keeping alive registrations alone
        cannot work.
      </para>
      <para>
        The keepalive mechanism works by sending a SIP request to a user agent
        behind NAT to make that user agent send back a reply. The purpose is
        to have packets sent from inside the NAT to the proxy often enough to
        prevent the NAT box from timing out the connection. Many NAT boxes do
        not consider packets that travel from the outside to the inside of the
        NAT to reset the connection expiration timer, thus to keepalive a user
        agent we need to trigger an answer from it.
      </para>
    </section>
    <section>
    <title>Background</title>
      <para>
        One of the major limitations of an implementation that only sends
        keepalive messages to registered user agents, is that it creates an
        artificial association between the concept of network visibility with
        the concept of user registration. The registration process only creates
        network visibility for incoming INVITE requests, in other words for
        incoming calls. However, there are other cases where a user agent needs
        to preserve its network visibility when behind NAT, that have nothing
        to do with receiving incoming calls. One of them is the ability of the
        user agent to keep receiving NOTIFY requests for a presence subscription
        it has made. Another situation is where the user agent should be able
        to receive all messages within a dialog it has initiated, even if it is
        not registered. In the first case, a presence agent is required to
        register to be able to receive notifications for its subscriptions and
        it has to keep the registration active the whole time. In the second
        case a user agent that wants to make an outgoing call has to register
        and keep the registration active during the call, otherwise it may
        not be able to receive future in-dialog messages, including the BYE
        that closes the dialog.
      </para>
      <para>
        Not only we have this forced association shown above, that requires a
        user agent to register to be able to do anything, but a simple keepalive
        implementation based on sending keepalive messages only to registered
        user agents, will also fail to work in common cases, exactly because of
        this artificial association. For example lets assume that we have a
        user agent that is registered. If during an outgoing call initiated by
        this user agent, the agent stops registering, then it will not be able
        to receive further in-dialog messages after the NAT binding expires.
        The same is true for a presence agent, receiving notifications for its
        subscriptions.
      </para>
      <para>
        In environments with multiple proxies handling the same domains, the
        problem gets even more acute. In this case the incoming and outgoing
        paths for a call may be completely different: the user agent may
        register using one proxy as an entry point to the network, but may
        make an outgoing call using a different proxy as the network entry
        point. Even more a registration may use a different proxy as the entry
        point to the network with each renewal of the registration, making it
        volatile and unreliable for anything else except incoming calls.
        A keepalive implementation that only sends keepalive messages to
        registered user agents will not be able to guarantee the delivery of
        in-dialog messages for outgoing calls even if it requires the user
        agent to register before making a call. In this case, even if we
        assume that the user agent would pick the same proxy for an outgoing
        call as the one it has used for the last registration, at the next
        registration it may pick another one (as returned by DNS), and will
        dissociate the incoming and outgoing paths rendering the outgoing
        path unusable (assuming the outgoing call takes longer than the
        registration period).
      </para>
      <para>
        All this leads to the conclusion that a keepalive implementation based
        solely on sending keepalive messages to registered user agents can only
        work in single proxy environments and then only work reliably if it
        requires the user agent to register before doing anything else, even
        though some actions would not require a user agent to register.
      </para>
    </section>
    <section>
    <title>Implementation</title>
      <para>
        To avoid the above mentioned issues, this implementation introduces
        the concept of network visibility for a given condition. This way we
        can keepalive a user agent for multiple independent conditions, thus
        avoiding all the problems presented above.
      </para>
      <para>
        The conditions for which the module will send keepalive messages are:
        <itemizedlist>
          <listitem>
            <para>
              <emphasis>Registration</emphasis> - for user agents that have
              registered to preserve their visibility for incoming calls. This
              is the result of triggering keepalive for a REGISTER request.
            </para>
          </listitem>
          <listitem>
            <para>
              <emphasis>Subscription</emphasis> - for presence agents that
              have subscribed to some events to preserve their visibility for
              receiving back notifications. This is the result of triggering
              keepalive for a SUBSCRIBE request.
            </para>
          </listitem>
          <listitem>
            <para>
              <emphasis>Dialogs</emphasis> - for user agents that have
              initiated an outgoing call to preserve their visibility for
              receiving further in-dialog messages. This is the result of
              triggering keepalive for an outgoing INVITE request.
            </para>
          </listitem>
        </itemizedlist>
      </para>
      <para>
        A user agent's NAT entry point may be kept alive for one or multiple
        of the conditions listed above. Even when a NAT endpoint is kept alive
        for more than one condition, only one keepalive message is sent to
        that NAT endpoint. The presence of multiple conditions for a NAT
        endpoint, only guarantees that the network visibility for a user agent
        based on a certain condition will be available while that condition is
        true, independently of the other conditions. When all the conditions
        to keepalive a NAT endpoint will disappear, that endpoint will be
        removed from the list with the NAT endpoints that need to be kept
        alive.
      </para>
      <para>
        The user interface for the keepalive functionality is very simple. It
        consists of a single function called nat_keepalive() that needs to be
        called only once for the requests that trigger the need for network
        visibility. These requests are: REGISTER, SUBSCRIBE and outgoing
        INVITEs. After such a request arrives it makes the user agent visible
        for the purpose of receiving back other messages. Thus, after a
        REGISTER the user agent may receive back incoming calls, after a
        SUBSCRIBE it may receive back notifications and after an outgoing
        INVITE it may receive back further in-dialog messages including the
        BYE that ends the dialog. The nat_keepalive() function needs to be
        called on the proxy that directly receives the request from the user
        agent, if it determines that the user agent making the request is
        behind NAT. The function needs to be called before the request gets
        either a stateless reply or it is relayed with t_relay(). Calling the
        nat_keepalive() function has no effect if the request gets no stateless
        reply or it is not relayed.
      </para>
      <para>
        For environments with multiple proxies, where the proxy that acts as
        an entry point to the network for a given request is not the one that
        actually handles the request, then the nat_keepalive() function needs
        to be called on the proxy that is the entry point and after that the
        request must be sent to the proxy that actually handles the request
        using t_relay(). This is needed because the keepalive functionality
        detects from the stateless replies or the TM relayed replies if the
        NAT endpoint needs to be kept alive for the condition triggered by
        the request for which the nat_keepalive() function was called.
        For example assume a network where a proxy P1 receives a REGISTER
        from a user agent behind NAT. P1 will determine that the user agent
        is behind NAT so it needs keepalive functionality, but another proxy
        called P2 is actually handling the subscriber registrations. In this
        case P1 has to call nat_keepalive() even though it doesn't yet know
        the answer P2 will give to the REGISTER request (which may even be a
        negative reply) or if P2 will restrict the proposed expiration time
        in any way. Thus P1 calls nat_keepalive() after which it calls
        t_relay(). When the reply from P2 arrives, a callback is triggered
        which will determine if the request did get a positive reply, and if
        so it will extract the registration expiration time and enable the
        keepalive functionality for that endpoint for the registration
        condition for the time given by the registration expiration.
        For single proxy environments, or if P1 is the same as P2, then
        t_relay() is not called, instead save_location() is called if the
        registration is accepted. Then the same process described above
        happens only this time triggered by a stateless reply callback.
        In both cases, calling nat_keepalive() when the REGISTER is received
        has no other effect that to trigger some callbacks that will determine
        from the reply if the caller endpoint should be kept alive or not.
      </para>
      <para>
        Below is described how nat_keepalive() should be called and what it
        does for each of the requests that need keepalive functionality (the
        function should only be called if it is determined that the user agent
        that generated the request is behind NAT):
        <itemizedlist>
          <listitem>
            <para>
              <emphasis>REGISTER</emphasis> - called before save_location() or
              t_relay() (depending on whether the proxy that received the
              REGISTER is also handling registration for that subscriber or
              not). It will determine from either the stateless reply
              generated by save_location() or the TM relayed reply if the
              registration was successful and what is its expiration time. If
              the registration was successful it will mark the given NAT
              endpoint for keepalive for the registration condition using the
              detected expiration time. If the REGISTER request is discarded
              after nat_keepalive() was called or if it intercepts a negative
              reply it will have no effect and the registration condition will
              not be activated for that endpoint.
            </para>
          </listitem>
          <listitem>
            <para>
              <emphasis>SUBSCRIBE</emphasis> - called before handle_subscribe()
              or t_relay() (depending on whether the proxy that received the
              SUBSCRIBE is also handling subscriptions for that subscriber or
              not). It will determine from either the stateless reply
              generated by handle_subscribe() or the TM relayed reply if the
              subscription was successful and what is its expiration time. If
              the subscription was successful it will mark the given NAT
              endpoint for keepalive for the subscription condition using the
              detected expiration time. If the SUBSCRIBE request is discarded
              after nat_keepalive() was called or if it intercepts a negative
              reply it will have no effect and the subscription condition will
              not be activated for that endpoint. It should be called for
              every SUBSCRIBE received, not only the ones that start a
              subscription (do not have a to tag), because it needs to update
              (extend) the expiration time for the subscription.
            </para>
          </listitem>
          <listitem>
            <para>
              <emphasis>INVITE</emphasis> - called before t_relay() for the
              first INVITE in a dialog. It will automatically trigger dialog
              tracing for that dialog and will use the dialog callbacks to
              detect changes in the dialog state. It will add a keepalive
              entry with the dialog condition for the caller NAT endpoint as
              soon as the dialog is created (this happens when t_relay() is
              called). It will then keep that condition for the given endpoint
              until the dialog is destroyed (either terminated, failed or
              expired). If the INVITE request cannot be relayed after
              nat_keepalive() was called it will have no effect and the
              dialog condition will not be activated for that endpoint.
            </para>
            <para>
              In addition an INVITE that starts a dialog will automatically
              trigger keepalive functionality for the destination endpoints
              if they are behind NAT. This is done by detecting if any of the
              destination endpoints already has a keepalive entry for the
              register condition. If so, a dialog condition will be added to
              that entry thus preserving that endpoint visibility even if the
              registration expires during the dialog or is moved to another
              proxy. During the call setup stage, multiple entries for the
              callee may be added with the dialog condition if parallel
              forking is used, however only the destination endpoints behind
              NAT will have the extra dialog condition set. Later when the
              dialog is confirmed, only the endpoint that answered the call
              will keep the dialog condition activated (if present), while all
              the endpoints from the unanswered branches will have it removed.
              This is done automatically without any need to call any function.
            </para>
          </listitem>
        </itemizedlist>
      </para>
      <para>
        Considering the elements presented in this section, we can say that
        the nat_traversal module provides a flexible and efficient keepalive
        functionality that is very easy to use. Because only the border
        proxies send keepalive messages, the network traffic is minimized.
        For the same reason, message processing in the proxies is also
        minimized, as border proxies generate keepalive messages themselves
        and send them statelessly, instead of having to relay messages
        generated by the registrars. Network traffic is also minimized by only
        sending a single keepalive message for an endpoint no matter for how
        many reasons the endpoint is kept alive. Keepalive messages are also
        distributed over the keepalive interval to avoid overloading the
        proxy by generating too many messages at a time. The nat_traversal
        module keeps its internal state about endpoints that need keepalive,
        state that is built while messages are processed by the proxy and
        thus it doesn't need to transfer any information from the usrloc
        module, which should also improve its efficiency.
      </para>
    </section>
  </section>

  <section>
  <title>Dependencies</title>
    <section>
    <title>&kamailio; Modules</title>
      <para>
        The following modules must be loaded before this module:
        <itemizedlist>
          <listitem>
            <para>
              <emphasis>sl</emphasis> module - if keepalive is enabled.
            </para>
          </listitem>
          <listitem>
            <para>
              <emphasis>tm</emphasis> module - if keepalive is enabled.
            </para>
          </listitem>
          <listitem>
            <para>
              <emphasis>dialog</emphasis> module - if keepalive is enabled
              and keeping alive INVITE dialogs is needed.
            </para>
          </listitem>
        </itemizedlist>
      </para>
    </section>
    <section>
    <title>External Libraries or Applications</title>
      <para>
        The following libraries or applications must be installed before
        running &kamailio; with this module loaded:
        <itemizedlist>
          <listitem><para>
            <emphasis>None</emphasis>.
          </para></listitem>
        </itemizedlist>
      </para>
    </section>
  </section>

  <section id="nat_traversal.p.keepalive_interval">
  <title>Exported parameters</title>
    <section>
    <title><varname>keepalive_interval</varname> (integer)</title>
      <para>
        The time interval (in seconds) required to send a keepalive message to
        all the endpoints that need being kept alive. During this interval,
        each endpoint will receive exactly one keepalive message. A negative
        value or zero will disable the keepalive functionality.
      </para>
      <para>
        <emphasis>
          Default value is <quote>60</quote>.
        </emphasis>
      </para>
      <example>
      <title>Setting the <varname>keepalive_interval</varname> parameter</title>
        <programlisting format="linespecific">
...
modparam("nat_traversal", "keepalive_interval", 90)
...
        </programlisting>
      </example>
    </section>

    <section id="nat_traversal.p.keepalive_method">
    <title><varname>keepalive_method</varname> (string)</title>
      <para>
        What SIP method to use to send keepalive messages. Typical methods
        used for this purpose are NOTIFY and OPTIONS. NOTIFY generates smaller
        replies from user agents, but they are almost entirely negative replies.
        Apparently almost none of the user agents understand that the purpose
        of the NOTIFY with a <quote>keep-alive</quote> event is to keep NAT
        open, even though many user agents send such NOTIFY requests themselves.
        However this does not affect the result at all, since the purpose is
        to trigger a response from the user agent behind NAT, positive or
        negative replies having little relevance as they are discarded anyway.
        The OPTIONS method on the other hand has a much higher rate of positive
        replies, but at the same time those positive replies are much bigger,
        mostly because the OPTIONS method is used to inform about the user
        agent capabilities and thus it includes a lot of extra headers to
        indicate those capabilities. Many user agents also include a SDP body
        with a bogus media session, probably to indicate media capabilities.
        All of this makes that positive replies to OPTIONS requests are 2 to
        3 times bigger than negative replies or replies to NOTIFY requests.
        For this reason the default value for the used method is NOTIFY.
      </para>
      <para>
        <emphasis>
          Default value is <quote>NOTIFY</quote>.
        </emphasis>
      </para>
      <example>
      <title>Setting the <varname>keepalive_method</varname> parameter</title>
        <programlisting format="linespecific">
...
modparam("nat_traversal", "keepalive_method", "OPTIONS")
...
        </programlisting>
      </example>
    </section>

    <section id="nat_traversal.p.keepalive_from">
    <title><varname>keepalive_from</varname> (string)</title>
      <para>
        Indicates what SIP URI to use in the From header of the keepalive
        requests. If not specified it will use sip:keepalive@proxy_ip, where
        proxy_ip is the IP address of the outgoing interface used to send the
        keepalive message, which is the same interface on which the request
        that triggered keepalive functionality arrived.
      </para>
      <para>
        <emphasis>
          Default value is <quote>sip:keepalive@proxy_ip</quote> with proxy_ip
          being the actual IP of the outgoing interface.
        </emphasis>
      </para>
      <example>
      <title>Setting the <varname>keepalive_from</varname> parameter</title>
        <programlisting format="linespecific">
...
modparam("nat_traversal", "keepalive_from", "sip:keepalive@my-domain.com")
...
        </programlisting>
      </example>
    </section>

    <section id="nat_traversal.p.keepalive_extra_headers">
    <title><varname>keepalive_extra_headers</varname> (string)</title>
      <para>
        Specifies extra headers that should be added to the keepalive messages
        that are sent by the proxy. The header specification must also include
        the CRLF (\r\n) line separator. Multiple headers can be specified by
        concatenating them and each of them must include the \r\n separator.
      </para>
      <para>
        <emphasis>
          Default value is undefined (send no extra headers).
        </emphasis>
      </para>
      <example>
      <title>Setting the <varname>keepalive_extra_headers</varname> parameter</title>
        <programlisting format="linespecific">
...
modparam("nat_traversal", "keepalive_extra_headers", "User-Agent: &kamailio;\r\nX-MyHeader: some_value\r\n")
...
        </programlisting>
      </example>
    </section>

    <section id="nat_traversal.p.keepalive_state_file">
    <title><varname>keepalive_state_file</varname> (string)</title>
      <para>
        Specifies a filename where information about the NAT endpoints and the
        conditions for which they are being kept alive is saved when &kamailio;
        exits. The information in this file is then used when &kamailio; starts
        to restore its internal state and continue to send keepalive messages
        to the NAT endpoints that have not expired in the meantime. This is
        useful when restarting &kamailio; to avoid losing keepalive state
        information about the NAT endpoints. The internal keepalive state is
        guaranteed to be saved in this file on exit, even when &kamailio;
        crashes.
      </para>
      <para>
        The value of this parameter can be either a relative path, in which
        case it will store it in the &kamailio; working directory, or an
        absolute path.
      </para>
      <para>
        <emphasis>
          Default value is undefined <quote>keepalive_state</quote>.
        </emphasis>
      </para>
      <example>
      <title>Setting the <varname>keepalive_state_file</varname> parameter</title>
        <programlisting format="linespecific">
...
modparam("nat_traversal", "keepalive_state_file", "/run/kamailio/keepalive_state")
...
        </programlisting>
      </example>
    </section>

    <section>
    <title><varname>contact_match</varname> (integer)</title>
      <para>
		  Control how to match the contact URIs between requests and replies.
	  </para>
      <para>
		  If set to 0, do string comparison. Use this mode when the contact URI
		  is not changed by registrar or proxy servers.
	  </para>
      <para>
		  If set to 1, do light URI comparison (match URI type, user, host,
		  port and proto). Use this mode when the contact URI can be changed
		  by registrar or proxy servers (e.g., new parameters added, or the
		  order of parameters changed).
      </para>
      <para>
        <emphasis>
          Default value is <quote>0</quote>.
        </emphasis>
      </para>
      <example>
      <title>Setting the <varname>contact_match</varname> parameter</title>
        <programlisting format="linespecific">
...
modparam("nat_traversal", "contact_match", 1)
...
        </programlisting>
      </example>
    </section>

  </section>

  <section id="nat_traversal.f.client_nat_test">
  <title>Exported functions</title>
    <section>
    <title>
      <function moreinfo="none">client_nat_test(type)</function>
    </title>
      <para>
        Check if the client is behind NAT. What tests are performed is
        specified by the type parameter which is an integer given by the sum
        of the numbers corresponding to the tests that one wishes to perform.
        The numbers corresponding to individual tests are shown below:
      </para>

      <para>
        <itemizedlist>
        <listitem><para>
          1 - tests if client has a private IP address (as defined by RFC1918)
              or one from shared address space (RFC6598) in the Contact field
              of the SIP message.
        </para></listitem>
        <listitem><para>
          2 - tests if client has contacted &kamailio; from an address that
              is different from the one in the Via field. Both the IP and
              port are compared by this test.
        </para></listitem>
        <listitem><para>
          4 - tests if client has a private IP address (as defined by RFC1918)
              or one from shared address space (RFC6598) in the top Via field
              of the SIP message.
        </para></listitem>
        </itemizedlist>
      </para>

      <para>
        For example calling client_nat_test("3") will perform test 1 and
        test 2 and return true if at least one succeeds, otherwise false.
      </para>

      <para>
        This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE.
      </para>

      <example>
      <title>Using the <function>client_nat_test</function> function</title>
        <programlisting format="linespecific">
...
if (client_nat_test("3")) {
    .....
}
...
        </programlisting>
      </example>
    </section>

    <section id="nat_traversal.f.fix_contact">
    <title>
    <function moreinfo="none">fix_contact()</function>
    </title>
      <para>
        Will replace the IP and port in the Contact header with the
        IP and port the SIP message was received from. Usually called
        after a successful call to client_nat_test(type)
      </para>

      <para>
        This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE.
      </para>

      <example>
      <title>Using the <function>fix_contact</function> function</title>
        <programlisting format="linespecific">
...
if (client_nat_test("3")) {
    fix_contact();
}
...
        </programlisting>
      </example>
    </section>

    <section id="nat_traversal.f.nat_keepalive">
    <title>
    <function moreinfo="none">nat_keepalive()</function>
    </title>
      <para>
        Trigger keepalive functionality for the source address of the request.
        When called it only sets some internal flags, which will trigger
        later the addition of the endpoint to the keepalive list if a
        positive reply is generated/received (for REGISTER and SUBSCRIBE)
        or when the dialog is started/replied (for INVITEs).
        For this reason, it can be called early or late in the script. The
        only condition is to call it before replying to the request or before
        sending it to another proxy. If the request needs to be sent to
        another proxy, t_relay() must be used to be able to intercept replies
        via TM or dialog callbacks. If stateless forwarding is used, the
        keepalive functionality will not work. Also for outgoing INVITEs,
        record_route() should also be used to make sure the proxy that keeps
        the caller endpoint alive stays in the path. For multi-proxy setups,
        this function should always be called on the border proxies (the ones
        that received the request directly from the user agent). For more
        details about this function, see the <emphasis>Implementation</emphasis>
        subsection from the <emphasis>Keepalive functionality</emphasis> section.
      </para>
      <para>
        This function can be used from REQUEST_ROUTE.
      </para>
      <example>
      <title>Using the <function>nat_keepalive</function> function</title>
        <programlisting format="linespecific">
...
if ((method=="REGISTER" || method=="SUBSCRIBE" ||
    (method=="INVITE" &amp;&amp; !has_totag())) &amp;&amp; client_nat_test("3"))
{
    nat_keepalive();
}
...
        </programlisting>
      </example>
    </section>
  </section>

  <section>
  <title>Statistics</title>
    <section>
    <title><varname>keepalive_endpoints</varname></title>
      <para>
        Indicates the total number of NAT endpoints that are being kept alive.
      </para>
    </section>
    <section>
    <title><varname>registered_endpoints</varname></title>
      <para>
        Indicates how many of the NAT endpoints are kept alive for registrations.
      </para>
    </section>
    <section>
    <title><varname>subscribed_endpoints</varname></title>
      <para>
        Indicates how many of the NAT endpoints are kept alive for subscriptions.
      </para>
    </section>
    <section>
    <title><varname>dialog_endpoints</varname></title>
      <para>
        Indicates how many of the NAT endpoints are kept alive for taking part
        in an INVITE dialog.
      </para>
    </section>
  </section>

  <section>
  <title>Pseudo Variables</title>
    <section>
    <title><varname>$keepalive.socket(nat_endpoint)</varname></title>
      <para>
        Returns the local socket used to send messages to the given NAT
        endpoint URI. The socket has the form proto:ip:port. The NAT endpoint
        URI is in the form: sip:ip:port[;transport=xxx] with transport missing
        if UDP. If the requested NAT endpoint URI is present in the internal
        keepalive table for any condition, it will return its associated local
        socket, else it will return null. The nat_endpoint can be a string or
        another pseudo-variable.
      </para>
      <para>
        This can be useful to restore the sending socket when relaying messages
        to a given user agent in multi-proxy environments. Consider an example
        where 2 proxies are involved, P1 and P2. A user agent registers by
        sending a REGISTER request to P1. P1 will call nat_keepalive() but
        because it determines that P2 should actually handle the user
        registration will forward the request to P2. Now assume P2 receives an
        incoming INVITE for this user. It will determine that the registration
        came through P1 and will forward the request to P1. P2 should also
        include the NAT endpoint URI where this request is to be relayed.
        This information should have been provided by P1 when it relayed the
        REGISTER request to P2. The means to do this is out of the scope of
        this example, but one can either use the path extension or custom
        headers to do this. When P1 receives the INVITE it will use the NAT
        endpoint URI it has received along with the request to determine the
        socket to send out the request, which should be the same as the one
        where the registration request was originally received. In the example
        below lets assume that P2 provided the original NAT endpoint address
        in a custom header called X-NAT-URI and that it also provides a custom
        header called X-Scope to indicate that the message is sent to P1 for
        being relayed back to the user agent by P1 which has the NAT open
        with it.
      </para>
      <example>
      <title>Using <varname>$keepalive.socket</varname> in multi-proxy environments</title>
        <programlisting format="linespecific">
...
# This code runs on P1 which has received an INVITE from P2 to forward
# it to the user agent behind NAT (because P1 has the NAT open with it).
if (method=="INVITE" &amp;&amp; $hdr(X-Scope)=="nat-relay") {
    $du = $hdr(X-NAT-URI);
    $fs = $keepalive.socket($du);
    t_relay();
    exit;
}
...
        </programlisting>
      </example>
    </section>

    <section>
    <title><varname>$source_uri</varname></title>
      <para>
        Returns the URI specification from where a request was received in the
        form sip:ip:port[;transport=xxx] with transport missing if UDP.
      </para>
      <para>
        This pseudo-variable can be used to set the received AVP for the
        registrar module to indicate that a user agent is behind NAT. This is
        meant as a more flexible replacement for the fix_nated_register()
        function, because it allows one to modify the source uri by appending
        some extra parameters before saving it to the received AVP.
      </para>
      <para>
        Another use for this pseudo-variable is in multi-proxy environments to
        indicate the NAT endpoint URI to the next proxy (if needed). Consider
        the previous example with two proxies P1 and P2. P1 receives the
        REGISTER request from a user agent and forwards it to P2 which does
        the actual registration. P1 needs to indicate the NAT endpoint URI to
        P2, so that P2 can include it later for incoming INVITE requests to
        this user agent.
      </para>

      <example>
      <title>Using <varname>$source_uri</varname> to set the received AVP on registrars</title>
        <programlisting format="linespecific">
...
modparam("registrar", "received_avp", "$avp(s:received_uri)")
modparam("registrar", "tcp_persistent_flag", 10)
...
# This code runs on the registrar, assuming it has received the
# REGISTER request directly from the user agent.
if (method=="REGISTER") {
    if (client_nat_test("3")) {
        if (proto==UDP) {
            nat_keepalive();
        } else {
            # Keep TCP/TLS connections open until the registration
            # expires, by setting the tcp_persistent_flag
            setflag(10);
        }
        force_rport();
        $avp(s:received_uri) = $source_uri;
        # or we could add some extra parameters to it if needed
        # $avp(s:received_uri) = $source_uri + ";relayed=false"
    }
    if (!www_authorize("", "subscriber")) {
        www_challenge("", "0");
        return;
    } else if (!check_to()) {
        sl_send_reply("403", "Username!=To not allowed ($au!=$tU)");
        return;
    }

    if (!save("location")) {
        sl_reply_error();
    }
    exit;
}
...
        </programlisting>
      </example>

      <example>
      <title>Using <varname>$source_uri</varname> in multi-proxy environments</title>
        <programlisting format="linespecific">
...
# This code runs on P1 which received the REGISTER request and has to
# forward it to the registrar P2.
if (method=="REGISTER") {
    if (client_nat_test("3")) {
        force_rport();
        nat_keepalive();
        append_hf("X-NAT-URI: $source_uri\r\n");
    }
    $du = "sip:P2_ip:P2_port";
    t_relay();
    exit;
}
...
        </programlisting>
      </example>

    </section>

  </section>

  <section>
  <title>Keepalive use cases</title>
    <section>
    <title>Single proxy environments</title>
      <para>
        In this case the usage is straight forward. The nat_keepalive() function
        needs to be called before save_location() for REGISTER requests, before
        handle_subscribe() for SUBSCRIBE requests and before t_relay() for the
        first INVITE of a dialog.
      </para>
    </section>

    <section>
    <title>Registration in multi-proxy environments</title>
      <para>
        If the proxy receiving the REGISTER request is the same as the proxy
        handling it, then the case is reduced to the single proxy case. For
        this example, lets assume they are different. We have a user agent UA1
        for which the registration is handled by the proxy P1. However UA1
        sends the REGISTER to P0 which in turn forwards it to P1 like this:
        UA1 --> P0 --> P1. In this case P0 calls nat_keepalive(), adds the NAT
        endpoint URI to the request (for example using a custom header) and
        forwards the request to P1. P1 will save the user in the user location
        together with the NAT endpoint URI.
      </para>
      <para>
        When an incoming INVITE request arrives on P1 for UA1, P1, will lookup
        the location and determine that it has to relay it to P0 because P0
        has the NAT open with UA1. P1 will include the original NAT endpoint
        URI in the request and an indication that the only role P0 has in this
        transaction is to relay it to UA1. P0 will receive this request and
        determine that is has to act as a relay for it. It will extract the
        NAT endpoint URI, then based on it the corresponding local socket
        using $keepalive.socket(endpoint_uri). It will then set both $du and
        $fs to the values it has found, call record_route() to stay in the
        path and call t_relay() to send it to UA1.
      </para>
      <para>
        Handling other type of requests (like for example SUBSCRIBE or
        MESSAGE) that arrive on P1 for UA1 is done the same way as with the
        first INVITE, on both P1 and P0.
      </para>
    </section>

    <section>
    <title>Subscription in multi-proxy environments</title>
      <para>
        If the proxy receiving the SUBSCRIBE request is the same as the proxy
        handling it, then the case is reduced to the single proxy case. For
        this example, lets assume they are different. We have a user agent UA1
        for which subscriptions are handled by the proxy P1. However UA1
        sends the SUBSCRIBE to P0 which in turn forwards it to P1 like this:
        UA1 --> P0 --> P1. In this case P0 calls nat_keepalive(), then calls
        record_route() to stay in the path and forwards the request to P1
        using t_relay(). Further SUBSCRIBE and NOTIFY requests will follow
        the record route and use P0 as a NAT entry point to have access to UA1.
        Further in-dialog SUBSCRIBE requests should also call record_route().
      </para>
    </section>

    <section>
    <title>Outgoing INVITEs in multi-proxy environments</title>
      <para>
        If the proxy receiving the INVITE request is the same as the proxy
        handling it, then the case is reduced to the single proxy case. For
        this example, lets assume they are different. We have a user agent UA1
        which is handled by the proxy P1 and UA2 which is handled by P2. UA2
        has registered with P2 going through P3, while UA1 calls UA2 by sending
        the first INVITE to P0. The call flow for the first INVITE looks like
        this: UA1 --> P0 --> P1 --> P2 --> P3 --> UA2.
        In this case P0 calls nat_keepalive(), then calls record_route() to
        stay in the path and forwards the request to P1. P1 authenticates UA1
        then forwards the request to P2, which is the home proxy for UA2. P1
        doesn't have to use record_route to stay in the path, but it can do
        that if needed for other purposes. P2 will lookup UA2 and find out
        that it is reachable through P3. It will take the original NAT
        endpoint URI that is has saved in the user location when UA2 has
        registered and include it in the message along with an indication that
        P3 only has to relay the message to UA2. If P2 does accounting or
        starts a media relay, it should also call record_route() to stay in
        the path. Then it forwards the request to P3 using t_relay(). P3 will
        detect that it only has to relay the request to UA2 because it has the
        NAT open with it. It will extract the NAT endpoint URI from the message
        and the local sending socket using $keepalive.socket(endpoint_uri) and
        will set both $du and $fs. After that it will call record_route() to
        stay in the path, and forward the request to UA2 using t_relay().
        Further in-dialog requests will follow the recorded route and use
        P0 and P3 as access points to UA1 respectively UA2. All the proxies
        that have used record_route() during the first INVITE should also
        call record_route() during further in-dialog requests to keep staying
        in the path.
      </para>
    </section>

  </section>

</chapter>

