Chapter 8. Handling of NAT

Table of Contents

Handling of NAT using Mediaproxy
Mediaproxy Transparent NAT Traversal ser.cfg Analysis
Using the Mediaproxy Transparent NAT Traversal ser.cfg
Handling of NAT using RTPproxy
RTPproxy Transparent NAT Traversal ser.cfg Analysis

There are two implementations for handling of NAT, use of mediaproxy and the use of rtpproxy. The following sections describe each of these methods.

There is considerable debate on the serusers mailing forum about which is better! Both are described in this document, but due to the simple fact that the authors of this document have deployed mediaproxy in their own installations means that the rest of this Getting Started document builds upon the mediaproxy solution. If there are users who would like to contribute material supporting the rtpproxy solution then this will be considered for future issues.

Figure 8.1. Reference Design Plus NAT

Reference Design Plus NAT

Handling of NAT using Mediaproxy

What this ser.cfg will do:

  1. Introduce specialized processing which is necessary when dealing with SIP clients that are behind a NAT device such as a DSL router or corporate firewall

Mediaproxy is one of two NAT traversal solutions for SER, the other being rtpproxy. Mediaproxy is known as a far-end NAT traversal solution which means that it handles all aspects of NAT at the SIP proxy location rather than at the SIP client location.

There are many advantages to handling NAT issues at the SIP proxy server, the primary benefit being that SIP client configuration is much simpler. Some of the important features of mediaproxy are:

  1. DNS SRV record capabilities for load distribution

  2. Mediaproxy can be installed on remote servers which are not running SER. By doing this your SIP proxy load is lightened

  3. Web monitoring is included with the mediaproxy distribution

Mediaproxy is a separate software product that is not distributed with SER. The SER distribution only includes the glue which gives SER the ability to communicate with a running instance of mediaproxy. This glue is known as the mediaproxy module. It is really a dispatcher module that can control one or more actual mediaproxy servers.

NOTE: In order for mediaproxy to function properly it must be configured to listen on a public IP address. Also, in most real world configurations, mediaproxy will not be installed on the SER server, but on a remote machine. Refer to the appendix for information on installing mediaproxy. Also, the mediaproxy version used for testing these configuration files can be downloaded from http://ONsip.org/

debug=3
fork=yes1
log_stderror=no2

listen=192.0.2.13   # INSERT YOUR IP ADDRESS HERE
port=5060
children=4

dns=no
rev_dns=no
fifo="/tmp/ser_fifo"
fifo_db_url="mysql://ser:heslo@localhost/ser"

loadmodule "/usr/local/lib/ser/modules/mysql.so"
loadmodule "/usr/local/lib/ser/modules/sl.so"
loadmodule "/usr/local/lib/ser/modules/tm.so"
loadmodule "/usr/local/lib/ser/modules/rr.so"
loadmodule "/usr/local/lib/ser/modules/maxfwd.so"
loadmodule "/usr/local/lib/ser/modules/usrloc.so"
loadmodule "/usr/local/lib/ser/modules/registrar.so"
loadmodule "/usr/local/lib/ser/modules/auth.so"
loadmodule "/usr/local/lib/ser/modules/auth_db.so"
loadmodule "/usr/local/lib/ser/modules/uri.so"
loadmodule "/usr/local/lib/ser/modules/uri_db.so"3
loadmodule "/usr/local/lib/ser/modules/domain.so"4
loadmodule "/usr/local/lib/ser/modules/mediaproxy.so"5
loadmodule "/usr/local/lib/ser/modules/nathelper.so"6
loadmodule "/usr/local/lib/ser/modules/textops.so"7

modparam("auth_db|domain|uri_db|usrloc","db_url","mysql://ser:heslo@localhost/ser")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")

modparam("nathelper", "rtpproxy_disable", 1)8
modparam("nathelper", "natping_interval", 0)9

modparam("mediaproxy","natping_interval", 30)10
modparam("mediaproxy","mediaproxy_socket", "/var/run/mediaproxy.sock")(11)
modparam("mediaproxy","sip_asymmetrics","/usr/local/etc/ser/sip-clients")(12)
modparam("mediaproxy","rtp_asymmetrics","/usr/local/etc/ser/rtp-clients")(13)

modparam("usrloc", "db_mode", 2)

modparam("registrar", "nat_flag", 6)(14)

modparam("rr", "enable_full_lr", 1)

route {

  # -----------------------------------------------------------------
  # Sanity Check Section
  # -----------------------------------------------------------------
  if (!mf_process_maxfwd_header("10")) {
    sl_send_reply("483", "Too Many Hops");
    break;
  };

  if (msg:len > max_len) {
    sl_send_reply("513", "Message Overflow");
    break;
  };

  # -----------------------------------------------------------------
  # Record Route Section
  # -----------------------------------------------------------------
  if (method=="INVITE" && client_nat_test("3")) { (15)
    # INSERT YOUR IP ADDRESS HERE
    record_route_preset("192.0.2.13:5060;nat=yes");(16)
  } else if (method!="REGISTER") {  
    record_route();(17)
  };

  # -----------------------------------------------------------------
  # Call Tear Down Section
  # -----------------------------------------------------------------
  if (method=="BYE" || method=="CANCEL") { (18)
    end_media_session();(19)
  };

  # -----------------------------------------------------------------
  # Loose Route Section
  # -----------------------------------------------------------------
  if (loose_route()) {

    if ((method=="INVITE" || method=="REFER") && !has_totag()) {(20)
      sl_send_reply("403", "Forbidden");
      break;
    };

    if (method=="INVITE") {

      (21)if (!proxy_authorize("","subscriber")) {
        proxy_challenge("","0");
        break;
      } else if (!check_from()) {
        sl_send_reply("403", "Use From=ID");
        break;
      };

      consume_credentials();

      if (client_nat_test("3") || search("^Route:.*;nat=yes")) { (22)
        setflag(6);(23)
        use_media_proxy();(24)
      };
    };

    route(1);
    break;
  };

  # -----------------------------------------------------------------
  # Call Type Processing Section
  # -----------------------------------------------------------------
  if (uri!=myself) {
    route(4);(25)
    route(1);
    break;
  };

  if (method=="ACK") {
    route(1);
    break;
  (26)} else if (method=="CANCEL") { 
    route(1);
    break;
  } else if (method=="INVITE") {
    route(3);
    break;
  } else  if (method=="REGISTER") {
    route(2);
    break;
  };

  lookup("aliases");
  if (uri!=myself) {
    route(4);(27)
    route(1);
    break;
  };

  if (!lookup("location")) {
    sl_send_reply("404", "User Not Found");
    break;
  };

  route(1);
}

route[1] {

  # -----------------------------------------------------------------
  # Default Message Handler
  # -----------------------------------------------------------------

  t_on_reply("1");(28)

  if (!t_relay()) {

    (29)if (method="INVITE" || method=="ACK") {
      end_media_session();
    };

    sl_reply_error();
  };
}

route[2] {

  # -----------------------------------------------------------------
  # REGISTER Message Handler
  # ----------------------------------------------------------------

  sl_send_reply("100", "Trying");

  if (!search("^Contact:[ ]*\*") && client_nat_test("7")) { (30)
    setflag(6);(31)
    fix_nated_register();(32)
    force_rport();(33)
  };

  if (!www_authorize("","subscriber")) {
    www_challenge("","0");
    break;
  };

  if (!check_to()) {
    sl_send_reply("401", "Unauthorized");
    break;
  };

  consume_credentials();

  if (!save("location")) {
    sl_reply_error();
  };
}

route[3] {

  # -----------------------------------------------------------------
  # INVITE Message Handler
  # -----------------------------------------------------------------

  if (client_nat_test("3")) { (34)
    setflag(7);(35)
    force_rport();(36)
    fix_nated_contact();(37)
  };

  if (!proxy_authorize("","subscriber")) {
    proxy_challenge("","0");
    break;
  } else if (!check_from()) {
    sl_send_reply("403", "Use From=ID");
    break;
  };

  consume_credentials();

  lookup("aliases");
  if (uri!=myself) {
    route(4);(38)
    route(1);
    break;
  };

  if (!lookup("location")) { (39)
    sl_send_reply("404", "User Not Found");
    break;
  };

  route(4);(40)
  route(1);
}

(41)route[4] {

  # -----------------------------------------------------------------
  # NAT Traversal Section
  # -----------------------------------------------------------------

  if (isflagset(6) || isflagset(7)) {
    if (!isflagset(8)) {(42)
      setflag(8);
      use_media_proxy();
    };
  };
}

(43)onreply_route[1] {

  if ((isflagset(6) || isflagset(7)) && (status=~"(180)|(183)|2[0-9][0-9]")) { (44)

    (45)if (!search("^Content-Length:[ ]*0")) {
      use_media_proxy();
    };
  };

  (46)if (client_nat_test("1")) {
    fix_nated_contact();
  };
}

Mediaproxy Transparent NAT Traversal ser.cfg Analysis

Our NAT traversal implementation is referred to as transparent NAT traversal because there are no differences between SIP clients with public IP addresses and SIP clients sitting behind a NAT device.

This SER configuration handles all NAT related issues invisibly so that configuring SIP phones is a breeze. We do not use STUN because STUN generally adds another layer of complexity that can be avoided.

To handle NATed devices we have introduced the use of mediaproxy. Mediaproxy is an RTP proxy application which is not part of SER. Mediaproxy installation and start script are shown in the appendix.

The issue of NAT is complicated because there are several aspects of NATed SIP clients that all must be addressed properly in order to effectively perform transparent RTP proxying. Please refer to NAT, STUN, and RTP proxy, section 3.5, for an in-depth review of these aspects.

A brief re-cap of the main NAT issues that must be addressed are:

  1. NATed clients must maintain an open SIP port, usually 5060 at all times. If this port closes on the clients NAT device then incoming calls will not be successful because the SIP proxy will be unable to notify the NATed SIP client that an INVITE message is being sent to it. In this case it is possible for the NATed SIP client to still make outbound calls.

  2. RTP ports can, and usually do use a random port. This means that users cannot just open a port on their NAT device for RTP.

  3. re-INVITE messages must be handled in a special manner in order to prevent RTP media streams from being lost mid-call. If this were to happen you usually end up with one-way audio or no audio after a re-INVITE message is processed. NOTE: The reason for this is that SIP RFC3261 specifies that a SIP UA may change CODECs, RTP ports, etc during mid-call. To compound this issue, it is difficult to determine if either SIP party in a call is NATed when processing re-INVITE messages.

So how do we handle these NAT issues? The solutions to these problems are:

  1. The SER configuration presented in this section takes advantage of the fact that we can use the SER proxy to keep NAT bindings open with the use of the natping_interval setting in mediaproxy. By doing so we can be certain that port 5060 for NATed clients will be accessible to our SIP proxy and therefore we will be able to send INVITE messages to those clients at any time.

  2. Mediaproxy gives SER the ability to be oblivious to the specific RTP port that a SIP client uses. The way it does this is that the two SIP clients think they are talking directly to each other, when in reality they are talking to the media proxy, which then forwards RTP data to the other SIP client. This is all handled automatically when the original SIP INVITE message is being processed and the call is set up.NOTE: In this ser.cfg example, we only proxy RTP media when one or more SIP clients are behind a NAT device. In cases where both SIP clients are on the public Internet, then we do not proxy RTP streams since both SIP clients can directly contact each other. This is a key to building a scaleable VoIP platform.

  3. We take advantage of the fact that we can embed our own NAT indicator in the Record-Route header of the original INVITE message. By doing so we can look for this special NAT tag when processing re-INVITE messages. If we find this tag then we know we must proxy the RTP media.NOTE: This technique of embedding a NAT flag in the Record-Route header has been suggested by Jan Janak of IPtel. He is one of the projects core developers and is an authoritative person on the subject.NOTE: This technique of embedding a NAT flag in the INVITE message is not required when using rtpproxy because the rtpproxy server has the ability to handle this situation without additional help.

1

Up until now we have run SER as a foreground process. From this point forward we will run SER as a daemon. The fork directive tells the SER daemon to run in the background. This is a requirement for using the init.d start script shown in the appendix.

2

Since we are running SER as a background process we must set the log_stderror directive equal to no in order to not keep SER in the foreground.

3

The uri module is introduced here to access the has_totag() function. This function is necessary for processing re-INVITEs and is describe below.

4

The domain module is needed because the mediaproxy module has dependencies to it for the is_uri_host_local() and is_from_local() functions.

5

Here we introduce the use of mediaproxy. This line has confused many SER novices because it implies that mediaproxy is included with SER, however, mediaproxy is an external application. This line only invokes the mediaproxy module, which is the glue between SER and the actual mediaproxy application.

6

We use some of the nathelper library functions to determine whether or not a SIP UA is NATed. An important item to understand is that just because nathelper is included here does not indicate that we are using rtpproxy (another RTP proxy application comparable to mediaproxy). Also note here that nathelper is generally referred to in the same context as rtpproxy because most people that use rtpproxy also use nathelper.

7

The textops module provide utility functions for manipulating text, searching for substrings, and checking for the existence of specific header fields.

8

Since we are using mediaproxy rather than rtpproxy, we need to tell the nathelper module to not attempt to bind to a running instance of rtpproxy. If we were to omit this line then syslog would contain many errors stating that rtpproxy could not be found.

9

Our NAT traversal is using mediaproxy, and therefore we have decided to use the mediaproxy keep-alive mechanism, which will ping NATed SIP clients on a regular basis. Because of this we need to disable the rtpproxy ping mechanism since we do not need it.

10

The mediaproxy natping_interval is a very crucial setting, which controls how often our SER proxy will ping registered SIP clients. Most NAT devices will only hold port bindings open for a minute or two, so we specify 30 seconds here.

This causes SER to send a 4-byte UDP packet to each SIP client every 30 seconds. This is all that is required to keep NATed clients alive.

NOTE: Some NAT devices have been reported to not allow incoming keep-alives. Thus, many user clients have their own implementations of keep-alive. If you experience one-way audio problems after a while, you may have run into this problem. The only solution is to turn on user client keep-alives. Also, keep in mind that it is perfectly acceptable to have the SER proxy send keep-alive traffic as well as the SIP traffic to the client.

(11)

SER and the mediaproxy dispatcher communicate via a standard Unix socket, which is specified here.

(12)

Mediaproxy may need to know when a SIP UA is asymmetric with respect to its SIP messaging port. Most SIP UAs are symmetric, meaning that they listen for incoming SIP messages on the same port as they use for sending their own SIP messages. However, if you find asymmetric clients that are not handled correctly, you can specify their User-Agent headers in this file.

A default example of this file can be found in the SER distribution under <ser-sources>/modules/mediaproxy/config/sip-asymmetric-clients. This example file has been copied and renamed to the path specified on this line.

(13)

Mediaproxy may need to know when a SIP UA is asymmetric with respect to its RTP port. If you find asymmetric clients that not handled correctly you can specify their User-Agent headers in this file.

A default example of this file can be found in the SER distribution under <ser-sources>/modules/mediaproxy/config/rtp-asymmetric-clients. This example file has been copied and renamed to the path specified on this line.

(14)

When SIP clients attempt to REGISTER with our SIP proxy we need a way to tell the registrar module to store NAT information for any particular UA. We do this by using flag 6, which has been arbitrarily chosen (but defined earlier in the loadmodule parameter section). We could have specified another integer here, but flag 6 seems to be the accepted standard for nat_flag.

If the nat_flag is set before calling the save() function to store contact information, SER will preserve the NAT contact information as well as set the flags column in the MySQL location table. By doing so, we can call lookup(location) when processing messages and flag 6 will be set for NATed clients.

(15)

If the received message is an INVITE and it is from a SIP client that is behind a NAT we need to embed a special NAT flag, which will be returned to our SIP proxy in the case of a re-INVITE. This embedded flag can then be located in our loose route processing logic to determine if the message originator is NATed or not.

(16)

If the message originator is NATed we specify the Record-Route header explicitly by using the record_route_preset() function. We pass to this function the IP address of our SIP proxy. If your SIP proxy sits behind a router that has Application Level Gateway (ALG) support, such as a Cisco 3600 series, then you will use the physical RFC1918 address of your SIP proxy here because the ALG-enabled router will rewrite private IP addresses.

NOTE: For those that are familiar with rtpproxy you may be wondering why this step is necessary since rtpproxy doesnt require the embedded ;nat=yes tag. The reason mediaproxy requires this and rtpproxy does not is that the use_media_proxy() function, which is introduced later in this example, will set up a new RTP proxy channel if you call the function and the <Call-ID> header is not found it mediaproxys list of active sessions.

The corresponding function in rtpproxy has the ability to instruct rtpproxy to only take action if a previous <Call-ID> header is found in the list of active rtpproxy sessions.

This difference also affects the loose_route() code block because mediaproxy users must look for the ;nat=yes tag when processing re-INVITE messages and rtpproxy users do not. So from this aspect, rtpproxy is somewhat easier to use.

(17)

If the message is not an INVITE from a NATed SIP client and it is not a REGISTER then we just call record_route() as normal to ensure that messages return to our SIP proxy from upstream or downstream SIP proxy servers or PSTN gateways.

(18)

Anytime we receive a BYE or CANCEL message we should assume that it is for a call that has been set up with mediaproxy. So here we just attempt to tear down the RTP proxy session. It is perfectly safe to call end_media_session(), even for calls that were not RTP proxied.

(19)

Tell mediaproxy to end the session for the current call.

(20)

Our NAT traversal requirements must handle re-INVITE messages in a special manner to prevent RTP media streams from dropping off during a re-INVITE. So we do special re-INVITE NAT processing here.

In order to ensure that we are dealing only with an actual re-INVITE, we must make sure the has_totag() function returns TRUE and loose_route() is also TRUE. The reason for this is that it is possible for an original INVITE message to include predefined route headers, which would cause loose_route() to return TRUE. Therefore the has_totag() is checked because only connected calls will have a tag= entry in the <To> header (ie, calls where a 200 OK as been generated by the callee).

In other words this new security check is based on the fact that established SIP dialogs will have a "totag" whereas calls in the process of being established will not. To ensure that our loose routing logic is not exposed to malicious users we make sure that INVITE and REFER messages are only accepted for established dialogs.

(21)

If the message is an INVITE then we need to challenge the message sender for credentials. If the message sender cannot provide valid credentials then SER will reply with a 403 error message.

NOTE: This current example requires that the caller and callee are registered with the SIP router. Future examples will expand on this section to allow "trusted" SIP devices, such as a PSTN gateway.

(22)

Now we check the NAT status of the re-INVITE to see if the message originator is NATed or not by calling client_nat_test("3"). We also search for a <Route> header that contains the ";nat=yes" embedded tag which would have been included by our record_route_preset() discussed earlier. If the ;nat=yes tag is found, then the caller is NATed.

(23)

If the message sender is NATed or the re-INVITE contains the ;nat=yes flag, we set flag 6 for later reference. This flag can then be checked in the reply_route.

(24)

In order to proxy RTP streams we just call use_media_proxy(). This will communicate to the external mediaproxy server causing it to open UDP ports for both clients, or maintain an existing RTP proxy session for an existing call, based on the <Call-ID> header. Calling use_media_proxy() causes the SDP payload to be rewritten with the IP address of the mediaproxy server and the allocated ports.

(25)

In the event that our message is no longer to be handled by our SIP router, we call our NAT handling route block to enable mediaproxy if needed before sending the message to its destination.

(26)

We now explicitly handle CANCEL messages. CANCEL messages can be safely processed with a simple call to t_relay() because SER will automatically match the CANCEL message to the original INVITE message. So here we just route the message to the default message handler.

(27)

Enable mediaproxy if needed before sending the message to its destination.

(28)

When dealing with NATed clients we must correctly handle response messages that may be heading back to the client. These response messages are accessible in SER by using a reply_route block.

SER allows you to specify multiple reply_route blocks which can perform many tasks. Here we specify that any reply messages must be passed to reply_route[1] which is defined at the end of the ser.cfg file.

In order to invoke a reply_route, you simply need to set the handler prior to calling t_relay(), as we have done here.

(29)

If the message could not be sent and it is an INVITE or ACK then we should attempt to release any previously established mediaproxy session.

(30)

If client_nat_test() returns true then we must set flag 6 to inform the registrar module that the client is NATed.

Also note that we must only invoke client_nat_test() if the SIP message being processed contains an actual <Contact:> header. Otherwise an error will be written to syslog.

NOTE: An interesting side note here is that the regular expression for the <Contact:> header is ^Contact:[ ]*\* which reads like this;

If the line starts with the text Contact: and is followed by any number of spaces (ie: [ ]*) and then followed by an asterisk (ie: \*). The reason for this is that a SIP client can include the header <Contact: *> just the same as <Contact:'space'*> and both are valid. By testing for one or more white space characters we catch all formatting styles.

NOTE: The Contact: header will contain an asterisk "*" character when a SIP client is requesting to be "unregistered" from the SIP proxy. In fact, many SIP clients can and/or will send a REGISTER message with a "Contact: *" header when they are rebooted.

(31)

If a client is indeed NATed, then we must inform the SER registrar module that it needs to store the actual IP address and port that the SIP message came from. This information is then used by subsequent calls to lookup(location) in order to find the public IP address of a SIP client that is behind a NAT device.

(32)

Fix_nated_register() is used specifically for processing REGISTER messages from NATed clients. This is very different from fix_nated_contact() because the former will not alter the URI in the <Contact:> header whereas the latter will.

Fix_nated_register() will only append parameters to the <Contact:> header URI, which follows RFC3261 Section 10.3 on handling REGISTER messages. If we had used fix_nated_contact() here then you will likely have compatibility problems where SIP UAs do not honor the 200 OK response that SER replies with upon successful REGISTRATION. This would then cause the SIP client to loose its registration.

(33)

Force_rport() adds the received IP port to the top most via header in the SIP message. This enables subsequent SIP messages to return to the proper port later on in a SIP transaction.

(34)

Invite messages have slightly different NAT testing requirements than REGISTER messages. Here we only test to see if the SIP message has an RFC1918 IP address in the <Contact:> header and whether or not the SIP UA contacted SER on a different IP address or port from what is specified in the via header.

(35)

If it is determined that the SIP client is NATed, then we set flag 7 for later reference.

NOTE: This client_nat_test() only determines if the message sender is behind a NAT device. At this point in the ser.cfg file we have not determined if the message recipient is NATed or not.

(36)

Add the received port to the VIA header.

(37)

Now we rewrite the messages <Contact:> header to contain the IP address and port of the public side of the NATed SIP client.

(38)

Enable mediaproxy if needed before sending the message to its destination.

(39)

Now we find the contact record for the party being called.

NOTE: A subtle, but very important, detail here is that flag 6 will be set by the call to lookup(location) in route[2] if the party being called is found in the MySQL location table and is NATed. The reason flag 6 will be set is because we specified this as the registrar modules nat_flag parameter.

(40)

Enable mediaproxy if needed before sending the message to its destination.

Now that we have taken care of all the NAT related items, we can safely send the INVITE message to its destination.

(41)

Route[4] is a convenience route block which enabled mediaproxy if either the message sender (flag 7) or the message recipient (flag 6) are NATed.

(42)

Flag 8 is used to make sure the mediaproxy doesn't get invoked more than once for our call. If mediaproxy were to erroneously be called more than once then the SIP message would end up with a corrupted SDP payload because the call to use_media_proxy() would alter the message incorrectly.

If flag 8 is not set then set it to prevent calling route[4] again.

(43)

Here we introduce a reply_route handler. A reply_route is defined just like any other block in SER. The only difference is that it is called onreply_route.

Any message that is passed to this block will be returning to the original sender. You can think of these messages as the response to the original request that the caller made. The types of messages that will appear here will have an integer response code, much like HTTP response codes. Examples here would be 200, 401, 403, and 404.

(44)

In this ser.cfg we are only interested in response codes of 180, 183, and all 2xx messages for NATed clients. We can check the status as shown with a regular expression. If any of these response codes are found then this statement will be TRUE.

An important thing to note is that we can check flags set in the other route blocks because their scope is still valid. So our caller and callee NAT flags are still accessible. If flag 6 is set then the caller is NATed, and if flag 7 is set then the callee is NATed.

(45)

We can only call use_media_proxy() for SIP messages that have a valid <Contact> parameter in the SDP payload. So here we test to make sure the c= parameter is valid by simply checking the SDP payload length. We assume that if we have an SDP payload then we will have a c= parameter and can call use_media_proxy().

If we were to simply call use_media_proxy() then we would likely see errors in syslog.

(46)

Finally we rewrite the messages <Contact:> header to contain the IP address and port of the public side of the NATed SIP client.

Using the Mediaproxy Transparent NAT Traversal ser.cfg

We have now made SER 100% NAT aware while keeping all of our SIP clients unaware of NAT. To test this new NAT functionality make sure you have an instance of mediaproxy running on the same physical server as SER.

Once mediaproxy is running you can start SER and register your SIP clients as normal. It should not matter whether the SIP client is behind a NAT device or not.

To see if a calls RTP streams have been routed to mediaproxy you can execute the Python script located at /usr/local/mediaproxy/sessions.py (assuming you installed mediaproxy at that location.)

You can also install the web monitoring tool located at /usr/local/mediaproxy/web/ if you have an Apache web server running on your SIP proxy. A screen shot of this web monitoring tool is available at http://www.ag-projects.com/docs/MediaSessions.pdf