Kamailio: Basic SIP Proxy (all requests) Setup

In this example, I will share how to setup Kamailio to proxy SIP requests to a SIP switch (such as FreeSWITCH or Asterisk).
192.168.1.101 is the IP of Kamailio
192.168.1.102 is the IP of FreeSWITCH or Asterisk
Here are snippets from the main config script, kamailio.cfg:

...
 

#!define IPADDRESS "192.168.1.101"

#!define SWITCH_IPADDRESS "192.168.1.102"


#!define FLAG_FROM_SWITCH 1
#!define FLAG_FROM_USER 2

# ------------------ module loading ----------------------------------
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "sl.so"
loadmodule "maxfwd.so"
loadmodule "nathelper.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "path.so"

# ----------------- setting module-specific parameters ---------------
modparam("nathelper|registrar", "received_avp", "$avp(s:rcv)")
# -------------------------  request routing logic -------------------
# main routing logic

route {

        # per request initial checks
        route(SANITY_CHECK);

        # CANCEL processing
        if (is_method("CANCEL")) {
                if (t_check_trans()) {
                        t_relay();
                }
                exit;
        }

        route(CHECK_SOURCE_IP);

        ##################################
        ### HANDLE SEQUENTIAL REQUESTS ###
        route(WITHINDLG);

        ###############################
        ### HANDLE INITIAL REQUESTS ###
        t_check_trans();

        if (is_method("INVITE|REFER")) {
                record_route();
        }
       
        if (is_method("REGISTER")) {
            add_path();
        }

        if (isflagset(FLAG_FROM_SWITCH)) {
                # don't send INVITE from SWITCH back to SWITCH, set reply route to handle NAT and forward them along
                t_on_reply("EXTERNAL_REPLY");
        } else {
                # set destination to your SWITCH
                $du = "sip:192.168.1.102:5060";
        }

        route(RELAY);


}


route[SANITY_CHECK]
{
        if (!sanity_check()) {
                #xlog("L_WARN", "$ci|end|message is insane");
                exit;
        }

        if (!mf_process_maxfwd_header("10")) {
                #xlog("L_WARN", "$ci|end|too much hops, not enough barley");
                send_reply("483", "Too Many Hops");
                exit;
        }

        if ($ua == "friendly-scanner" ||
                $ua == "sundayddr" ||
                $ua =~ "sipcli" ) {
                #xlog("L_WARN", "$ci|end|dropping message with user-agent $ua");
                exit;
        }

        if ($si == IPADDRESS) {
                #xlog("L_WARN", "$ci|end|dropping message");
                exit;
        }

}


route[CHECK_SOURCE_IP]
{
        if ($si == SWITCH_IPADDRESS) {
                setflag(FLAG_FROM_SWITCH);
        } else {
                setflag(FLAG_FROM_USER);
        }
}

# Handle requests within SIP dialogs
route[WITHINDLG]
{
        if (has_totag()) {
                # sequential request withing a dialog should
                # take the path determined by record-routing
                if (loose_route()) {
                        route(RELAY);
                } else {
                        if (is_method("NOTIFY")) {
                                route(RELAY);
                        }
                        if (is_method("SUBSCRIBE") && uri == myself) {
                                # in-dialog subscribe requests
                                exit;
                        }
                        if (is_method("ACK")) {
                                if (t_check_trans()) {
                                        # no loose-route, but stateful ACK;
                                        # must be an ACK after a 487
                                        # or e.g. 404 from upstream server
                                        t_relay();
                                        exit;
                                } else {
                                        # ACK without matching transaction ... ignore and discard
                                        #xlog("ACK without matching transaction ... ignore and discard");
                                        exit;
                                }
                        }
                        sl_send_reply("404","Not here");
                }
                exit;
        }
}

onreply_route[EXTERNAL_REPLY]
{
        route(NAT_TEST_AND_CORRECT);
}


route[NAT_TEST_AND_CORRECT]
{
        if (nat_uac_test("3")) {
                if (is_method("REGISTER")) {
                        fix_nated_register();
                } else {
                        fix_nated_contact();
                }
                force_rport();
        }
        if (has_body("application/sdp") && nat_uac_test("8")) {
                fix_nated_sdp("10");
        }
}

route[RELAY]
{
        if (!t_relay()) {
                sl_reply_error();
        }
        exit;
}

This example will proxy all SIP requests to the switch. Typically, this is not recommended as Kamailio is able to handle the SIP REGISTERs but I’ll save that for another tutorial.

9 thoughts on “Kamailio: Basic SIP Proxy (all requests) Setup”

  1. Hello,

    Is there a way to implement failover to a different upstream register if 192.168.1.102 fails?

    1. Hello,
      Yes, you can use a module such as dispatcher to check health of the B2BUA and route accordingly.

    1. You generally have to enable multihome mode with
      mhomed=1
      It also requires a bit more tweaking. You may need to use force_send_socket() in places as well. I’d be happy to assist more if you can provide a bit more detail on your setup. Feel free to email me direct, eschmidbauer@gmail.com

  2. I just came across this post from a few years ago, although I see it is still getting a bit of traction.

    Thanks for posting this! This is the only configuration I’ve been able to find online of someone with a similar setup of what I am looking to achieve. Unfortunately, I don’t have any existing Kamailio knowledge, so this is completely foreign to me.

    However, I installed Kamilio and the required modules, and used your script, replacing the IP addresses with my own setup.

    A little background: I’m attempting to use Kamailio as an outbound SIP proxy for my IP phones to a 3CX server in the cloud. The IP phones are obviously behind NAT, so I was hoping to use Kamailio as a very basic SIP proxy with NAT traversal.

    Using your script as is and saving it as kamailio.cfg does indeed proxy everything. However, I’m a little confused because it does not seem to be doing any NAT traversal. Under route[NAT_TEST_AND_CORRECT] you are indeed performing fix_nated_register(); and fix_nated_contact(); but I’m not sure if the script is running those.

    As an experiment, I did a very sloppy hack: I modified route[CHECK_SOURCE_IP] as follows to include route(NAT_TEST_AND_CORRECT);

    route[CHECK_SOURCE_IP]
    {
    if ($si == SWITCH_IPADDRESS) {
    setflag(FLAG_FROM_SWITCH);
    } else {
    setflag(FLAG_FROM_USER);
    route(NAT_TEST_AND_CORRECT);
    }
    }

    This does in fact result in REGISTER and the resulting 407 Authentication Required response being properly translated with the correct ports. However, the issue I ran into with that bootleg fix is that that INVITE and some other stuff from the 3CX server sent to the proxy are not being properly translated (they are attempting to be sent to the private IP and port).

    1. Hi Brian,
      Yes, this was meant as a broad example and definitely requires some tweaking based on your needs. You are on the right track for fixing NAT from users, but to be even more precise you can add something like this:
      route[CHECK_SOURCE_IP] {
      if ($si == SWITCH_IPADDRESS) {
      setflag(FLAG_FROM_SWITCH);
      } else if (allow_routing(“user”)) { # make sure request matches .userdomain.com
      setflag(FLAG_FROM_USER);
      route(NAT_TEST_AND_CORRECT);
      } else { # reject everything else
      setflag(FLAG_UNAUTHORIZED);
      }
      }

      and use permissions module with something like (assuming you are using SIP domains) – `/etc/kamailio/user.allow`:
      # only allow .userdomain.com
      “^sip:[^@]+@[^.]+\.userdomain\.com(:[0-9]+)?$” : ALL

      this will seriously cut down on “noise” from script kiddie bots too.
      Feel free to email me with any more questions: eschmidbauer@gmail.com

      1. Thanks, that’s a pretty good idea. So I’m glad that I was on the right path, but I seem to be stuck and fairly confused about one issue with the NAT behavior.

        “REGISTER” and “STATUS” messages are translated properly. However, if an INVITE message comes in from my PBX server (3CX), Kamailio is not NATing it.

        Here is a link to a screenshot illustrating what I mean: https://i.imgur.com/ENctyPq.png
        For reference, 40.76.x.x is the IP of the 3CX server (static public, no NAT). 10.111.222.4 is the eth0 interface of Kamailio (has a static public IP with 1:1 NAT which is not an issue). 108.24.x.x is the public IP of my home network (with NAT). 10.0.0.125 is the local IP of my Polycom IP phone on my home network (NAT).

        The “contact” address (4245@10.0.0.125:5060) in the 200 OK message (which is working properly) is the same as the “contact” in the INVITE, so I would expect that Kamailio should be able to NAT INVITE messages and any other messages properly based on the existing session of the same contact.

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.

This site uses Akismet to reduce spam. Learn how your comment data is processed.