Archive for the ‘SIP’ Category

SEMS and KAMAILIO combo as a PBX – concept

2012 f 3

This is a record of how to make a workable PBX using SEMS and KAMAILIO on CENTOS 6.3. This procedure will be applicable to most OSs that can run CENTOS and KAMAILIO.

This is not a description of how to do it. It’s a log of my attempts to make it happen. The last post will be a recipe to follow.

First, what is a PBX AKA PABX AKA EPABX ?

Using Wikipedia we see that PBXs have the following list of capabilities

  •     Auto attendant
  •     Auto dialing
  •     Automatic call distributor
  •     Automated directory services (where callers can be routed to a given employee)
  •     Automatic ring back
  •     Call accounting
  •     Call blocking
  •     Call forwarding on busy or absence
  •     Call park
  •     Call pick-up
  •     Call transfer
  •     Call waiting
  •     Camp-on (?wtf?)
  •     Conference call
  •     Custom greetings
  •     Customised Abbreviated dialing (Speed Dialing)
  •     Busy Override
  •     Direct Inward Dialing
  •     Direct Inward System Access (DISA) (the ability to access internal features)
  •     Do not disturb (DND)
  •     Follow-me, also known as find-me: Determines the routing of incoming calls.
  •     Interactive voice response
  •     Music on hold
  •     Night service
  •     Public address voice paging
  •     Shared message boxes (where a department can have a shared voicemail box)
  •     Voice mail
  •     Voice message broadcasting
  •     Welcome Message

Strewth! That’s a lot!. Maybe I’ll just try and do a subset for now.

Luckily a lot of them look like standard functions that SEMS can natively implement. Or at least reasonably simple code can make it happen.

By way of background I’ve already written SEMS applications that handle multiple simultaneous calls – this in conjunction with heavily tricked out networks of kamailio servers.

My plan now I guess is to look very closely at the SEMS SBC and see what can be done with it.

The basic model will be a Kamailio proxy handling PSTN upstream trunking (SIP trunking) and local and remote UA registrations.

Incoming trunked calls will be landed on the SEMS instance which will then make a new outgoing call to registered handsets. Local PBX calls will similarly be routed by Kamailio and hosted on the SEMS instance. This is pretty much the same model as described in the freeswitch implementation at

http://kb.asipto.com/freeswitch:kamailio-3.1.x-freeswitch-1.0.6d-sbc

The difference is that SEMS will act as the SBC and media server. In addition I plan to implement MWI natively in SEMS, plus busy-lamp functionality using the kamailio resource list server and publish commands from SEMS.

More to follow when I learn all about SEMS sbc.

SER aliases dbtext file format and example values

2007 f 19

Today I needed to get Sip Express Router to work with a preconfigured set of aliases. I have a project where there is a very small wafer PC running SER out of flash. The configuration is completely static and I have no need or wish to use MySQL to record this stuff.

I have chosen to use 0.9.6 SER as that seems to be the latest stable.

First port of call was the SER site where I checked out the module documentation for dbtext http://www.iptel.org/ser/doc/modules/dbtext . It was kind of minimal. In particular it didn’t tell me what I needed to know – which fields in the dbtext aliases example were needed and what values should be in them.

I also rapidly discovered that the example configuration file had problems such as wrong module paths.

To cut a long story short (and a horror experience getting mysql going to capture the table contents from ser aliases generated by serctl ) I ended up with the following files in /tmp/serdb. Of particular note is the need to \ the : character in the sip contact field as that is also the dbtext field separator.

In the aliases file username is the text of the alias and contact is where it is routed to – with sip: preceding the actual contact. All other fields should be as listed below – the 2000000000 field entries represent times and dates in the hopefully distant future

/tmp/serdb/aliases

username(str) domain(str,null) contact(str,null) expires(int,null) q(double,null) callid(str,null) cseq(int,null) last_modified(str) replicate(int,null) state(int,null) flags(int) user_agent(str) received(str,null)
1002::sip\\:1009@example.org:2000000000:1.0:FourtyTwo:42:2000000000:0:0:128:SIP Express Router FIFO::
1003::sip\\:1009@example.org:2000000000:1.0:FourtyTwo:42:2000000000:0:0:128:SIP Express Router FIFO::
1004::sip\\:1009@example.org:2000000000:1.0:FourtyTwo:42:2000000000:0:0:128:SIP Express Router FIFO::

/tmp/serdb/subscriber

username(str) password(str) ha1(str) domain(str) ha1b(str) rpid(str)
2000:plain_password:xxx:example.org:xxx:ZZZZ
1009:plan_pass2:xxx:example.org:xxx:ZZZ

/tmp/serdb/version

table_name(str) table_version(int)
subscriber:3
location:7
aliases:7

/tmp/serdb/location

username(str) domain(str,null) contact(str,null) expires(int,null) q(double,null) callid(str,null) cseq(int,null) last_modified(str) replicate(int,null) state(int,null) flags(int) user_agent(str) received(str)

To complement these I had a sercfg set up with correct paths as follows

/usr/local/etc/ser/ser,cfg

# simple quick-start config script with dbtext and correct paths for ser-0.9.6
#

# ----------- global configuration parameters ------------------------

#debug=9         # debug level (cmd line: -dddddddddd)
#forki=yes
#log_stderror=no        # (cmd line: -E)

check_via=no    # (cmd. line: -v)
dns=no          # (cmd. line: -r)
rev_dns=no      # (cmd. line: -R)
children=4

listen=10.0.0.1
port=5060

fifo="/tmp/ser_fifo"

alias=example.org

# ------------------ module loading ----------------------------------

# use dbtext database
loadmodule "/usr/local/lib/ser/modules/dbtext.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/textops.so"

# modules for digest authentication
loadmodule "/usr/local/lib/ser/modules/auth.so"
loadmodule "/usr/local/lib/ser/modules/auth_db.so"

# ----------------- setting module-specific parameters ---------------

# -- usrloc params --

# use dbtext database for persistent storage
modparam("usrloc", "db_mode", 1)
modparam("usrloc|auth_db", "db_url", "dbtext:///tmp/serdb")

# -- auth params --
#
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "user_column", "username")
modparam("auth_db", "domain_column", "domain")

# -- rr params --
# add value to ;lr param to make some broken UAs happy
modparam("rr", "enable_full_lr", 1)

# -------------------------  request routing logic -------------------

# main routing logic

route{
    # initial sanity checks -- messages with
    # max_forwards==0, or excessively long requests
    if (!mf_process_maxfwd_header("10")) {
        sl_send_reply("483","Too Many Hops");
        break;
    };
    if (msg:len >=  max_len ) {
        sl_send_reply("513", "Message too big");
        break;
    };

    # we record-route all messages -- to make sure that
    # subsequent messages will go through our proxy; that's
    # particularly good if upstreami and downstreami entities
    # use different transport protocol
    if (!method=="REGISTER") record_route();

    # subsequent messages withing a dialogi should take the
    # path determined by record-routing
    if (loose_route()) {
        # mark routing logic in request
        append_hf("P-hint: rr-enforcedrn");
        route(1);
        break;
    };

    if (!uri==myself) {
        # mark routing logic in request
        append_hf("P-hint: outboundrn");
        route(1);
        break;
    };

    # if the request is for other domain use UsrLoc
    # (in case, it does not work, use the following command
    # with proper names and addresses in it)
    if (uri==myself) {
        if (method=="REGISTER") {
            # digest authentication
            if (!www_authorize("", "subscriber")) {
                www_challenge("", "0");
                break;
            };

            save("location");
            break;
        };

        lookup("aliases");
        if (!uri==myself) {
            append_hf("P-hint: outbound aliasrn");
            route(1);
            break;
        };

        # native SIP destinations are handled using our USRLOC DB
        if (!lookup("location")) {
            sl_send_reply("404", "Not Found");
            break;
        };
    };
    append_hf("P-hint: usrloc appliedrn");
    route(1);
}

route[1]
{
    # send it out now; use stateful forwarding as it works reliably
    # even for UDP2TCP
    if (!t_relay()) {
        sl_reply_error();
    };
}