0. เมื่อไหร่ใช้ OpenSIPS? เมื่อไหร่ใช้ Asterisk/IssabelPBX/FreePBX?

มี site ที่เคยทำให้ลูกค้าประมาณ 300 extensions ก็สามารถใช้ IssabelPBX run บน server ตัวเดียวได้

อาจจะช้าบ้างในกรณีเปลี่ยน config แล้ว reload server บ่อยๆ สำหรับ site ที่มี extensions > 200 ก็น่าจะใช้

OpenSIPS ได้แล้ว อย่างไรก็ตาม OpenSIPS รองรับเฉพาะ SIP protocol (signaling) ต้องมี media server

(IVR, voicemail, conf. room) มาเสริม ซึ่งก็ต้องใช้ Asterisk หรือ FreeSWITCH หรือ YATE มาทำหน้าที่นี้

ในกรณีที่เป็นบริการสาธารณะ (เช่น โทรศัพท์บ้านของ TOT) ก็สามารถนำ OpenSIPS + media server มา

ทดแทนได้   Asterisk/IssabelPBX/FreePBX ไม่เหมาะกับงานแบบนี้อย่างแน่นอน

 

1. OpenSIPS - Open SIP Server

OpenSIPS (www.opensips.org) เป็น open source SIP server ที่พัฒนาต่อเนื่องมาจาก SER project ->

OpenSER project มีความเสถียรและมี features ที่ค่อนข้างสมบูรณ์ (แต่การใช้งานไม่ได้ง่ายสำหรับ user

ทั่วไป) OpenSIPS มี features หลักๆ ดังนี้

  • SIP registrar server
  • SIP router / proxy (lcr, dynamic routing, dialplan features)
  • SIP redirect server
  • SIP presence agent
  • SIP back-to-back User Agent
  • SIP IM server (chat and end-2-end IM)
  • SIP load-balancer or dispatcher
  • SIP front end for gateways/asterisk

บทความของเราในตอนนี้จะเป็นการติดตั้ง OpenSIPS 2.4.11 บน Debian 9.13 หลังจากนั้นบทความใน

ตอนต่อๆ ไป จะกล่าวถึง OpenSIPS GUI, การใช้งานในลักษณะ SIP trunking และ VoIP Service Platform

 

2. ติดตั้ง OpenSIPS

ขั้นแแรกติดตั้ง Debian 9.13 เลือก options - web server, ssh server, standard system utilities

หลังจากติดตั้ง OS เสร็จ ให้ login ด้วย root แล้ว run คำสั่ง ดังนี้

#cd /usr/src

#apt install gcc bison flex make openssl libssl-dev libncurses5-dev

#apt install libxml2-dev mariadb-server mariadb-client libmariadb2

#apt install libxmlrpc-core-c3-dev libdbi-perl libdbd-mysql-perl libmicrohttpd-dev

#apt install libfrontier-rpc-perl libterm-readline-gnu-perl libpcre3-dev unzip

#apt install default-libmysqlclient-dev

download opensips-2.4.11.tgz จาก download.opensips.org ไปเก็บที่โฟลเดอร์ /usr/src

#cd /usr/src

#tar zxvf opensips-2.4.11.tgz

#cd opensips-2.4.11

#make prefix=/ all include_modules="db_mysql"

#make prefix=/ install include_modules="db_mysql"

#cd packaging/debian

#cp opensips.default /etc/default/opensips

#cp opensips.init /etc/init.d/opensips

#chmod 755 /etc/init.d/opensips

#useradd opensips

 

3. คอนฟิก OpenSIPS

คอนฟิก log file

#touch /var/log/opensips.log

แก้ไข file /etc/rsyslog.conf โดยการเพิ่มบรรทัดต่อไปนี้เข้าไป

local0.*          -/var/log/opensips.log

แก้ไข file /etc/default/opensips

RUN_OPENSIPS=yes

S_MEMORY=128

แก้ไข file /etc/init.d/opensips

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin

DAEMON=/sbin/opensips

ระบุ config file = /etc/opensips/opensips.conf

แก้ไข file /etc/opensips/opensipsctlrc

DBENGINE=MYSQL

DBHOST=localhost

DBNAME=opensips

DBRWUSER=opensips

DBRWPW=”opensipsrw”

ALIASES_TYPE=”DB”

OSIPS_FIFO=”/tmp/opensips_fifo”

 สร้าง  database โดยการ run คำสั่ง

/sbin/opensipsdbctl create

ถ้าไม่มีอะไรผิดพลาดก็ทำขั้นต่อไป

แก้ไข file /etc/opensips/opensips.cfg

ให้ได้ตามนี้

###################################
####### Global Parameters ################
###################################

log_level=3
log_stderror=no
log_facility=LOG_LOCAL0

children=4

/* uncomment the following lines to enable debugging */
#debug_mode=yes

/* uncomment the next line to enable the auto temporary blacklisting of
   not available destinations (default disabled) */
#disable_dns_blacklist=no

/* uncomment the next line to enable IPv6 lookup after IPv4 dns
   lookup failures (default disabled) */
#dns_try_ipv6=yes

/* comment the next line to enable the auto discovery of local aliases
   based on reverse DNS on IPs */
auto_aliases=no

listen=udp:192.168.10.222:5060

################################
####### Modules Section ##############
################################

#set module path
mpath="/lib64/opensips/modules/"

#### SIGNALING module
loadmodule "signaling.so"

#### StateLess module
loadmodule "sl.so"

#### Transaction Module
loadmodule "tm.so"
modparam("tm", "fr_timeout", 5)
modparam("tm", "fr_inv_timeout", 30)
modparam("tm", "restart_fr_on_each_reply", 0)
modparam("tm", "onreply_avp_mode", 1)

#### Record Route Module
loadmodule "rr.so"
/* do not append from tag to the RR (no need for this script) */
modparam("rr", "append_fromtag", 0)

#### MAX ForWarD module
loadmodule "maxfwd.so"

#### SIP MSG OPerationS module
loadmodule "sipmsgops.so"

#### FIFO Management Interface
loadmodule "mi_fifo.so"
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
modparam("mi_fifo", "fifo_mode", 0666)

#### URI module
loadmodule "uri.so"
modparam("uri", "use_uri_table", 0)

#### MYSQL module
loadmodule "db_mysql.so"

#### USeR LOCation module
loadmodule "usrloc.so"
modparam("usrloc", "nat_bflag", "NAT")
modparam("usrloc", "db_mode",   2)
modparam("usrloc", "db_url",
        "mysql://opensips:opensipsrw@localhost/opensips")

#### REGISTRAR module
loadmodule "registrar.so"
modparam("registrar", "tcp_persistent_flag", "TCP_PERSISTENT")
/* uncomment the next line not to allow more than 10 contacts per AOR */
#modparam("registrar", "max_contacts", 10)

#### ACCounting module
loadmodule "acc.so"
/* what special events should be accounted ? */
modparam("acc", "early_media", 0)
modparam("acc", "report_cancels", 0)
/* by default we do not adjust the direct of the sequential requests.
   if you enable this parameter, be sure the enable "append_fromtag"
   in "rr" module */
modparam("acc", "detect_direction", 0)
modparam("acc", "db_url",
        "mysql://opensips:opensipsrw@localhost/opensips")

#### AUTHentication modules
loadmodule "auth.so"
loadmodule "auth_db.so"
modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")
modparam("auth_db|uri", "db_url",

        "mysql://opensips:opensipsrw@localhost/opensips")
modparam("auth_db", "load_credentials", "")

#### ALIAS module
loadmodule "alias_db.so"
modparam("alias_db", "db_url",
        "mysql://opensips:opensipsrw@localhost/opensips")

#### DOMAIN module
loadmodule "domain.so"
modparam("domain", "db_url",
        "mysql://opensips:opensipsrw@localhost/opensips")
modparam("domain", "db_mode", 1)   # Use caching
modparam("auth_db|usrloc|uri", "use_domain", 1)

#### DIALOG module
loadmodule "dialog.so"
modparam("dialog", "dlg_match_mode", 1)
modparam("dialog", "default_timeout", 21600)  # 6 hours timeout
modparam("dialog", "db_mode", 2)
modparam("dialog", "db_url",
        "mysql://opensips:opensipsrw@localhost/opensips")

loadmodule "proto_udp.so"

###########################
####### Routing Logic ###########
###########################

# main request routing logic

route {

        if (!mf_process_maxfwd_header("10")) {

                send_reply("483","Too Many Hops");
                exit;
        }

        if (has_totag()) {

                # handle hop-by-hop ACK (no routing required)
                if ( is_method("ACK") && t_check_trans() ) {
                        t_relay();
                        exit;
                }

                # sequential request within a dialog should
                # take the path determined by record-routing
                if ( !loose_route() ) {
                        # we do record-routing for all our traffic, so we should not
                        # receive any sequential requests without Route hdr.

                        send_reply("404","Not here");
                        exit;
                }

                # validate the sequential request against dialog
                if ( $DLG_status!=NULL && !validate_dialog() ) {
                        xlog("In-Dialog $rm from $si (callid=$ci) is not valid according to dialog\n");
                        ## exit;
                }

                if (is_method("BYE")) {
                        # do accounting even if the transaction fails
                        do_accounting("db","failed");

                }

                # route it out to whatever destination was set by loose_route()

                # in $du (destination URI).
                route(relay);
                exit;
        }

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

        # absorb retransmissions, but do not create transaction
        t_check_trans();

        if ( !(is_method("REGISTER")  ) ) {

                        if (is_from_local()) {
                        # authenticate if from local subscriber
                        # authenticate all initial non-REGISTER request that pretend to be
                        # generated by local subscriber (domain from FROM URI is local)
                        if (!proxy_authorize("", "subscriber")) {
                                proxy_challenge("", "0");
                                exit;
                        }
                        if (!db_check_from()) {
                                send_reply("403","Forbidden auth ID");
                                exit;
                        }

                        consume_credentials();
                        # caller authenticated

                } else {
                        # if caller is not local, then called number must be local

                        if (!is_uri_host_local()) {
                                send_reply("403","Relay Forbidden");
                                exit;
                        }
                }

        }

        # preloaded route checking
        if (loose_route()) {
                xlog("L_ERR",
                        "Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]");
                if (!is_method("ACK"))
                        send_reply("403","Preload Route denied");
                exit;

        }

        # record routing
        if (!is_method("REGISTER|MESSAGE"))
                record_route();

        # account only INVITEs
        if (is_method("INVITE")) {

                # create dialog with timeout
                if ( !create_dialog("B") ) {
                        send_reply("500","Internal Server Error");
                        exit;
                }

                do_accounting("db");
        }


        if (!is_uri_host_local()) {
                append_hf("P-hint: outbound\r\n");

                route(relay);
        }

        # requests for my domain

        if (is_method("PUBLISH|SUBSCRIBE")) {
                send_reply("503", "Service Unavailable");
                exit;
        }

        if (is_method("REGISTER")) {
               # authenticate the REGISTER requests
                if (!www_authorize("", "subscriber")) {
                        www_challenge("", "0");
                        exit;
                }

                if (!db_check_to()) {
                        send_reply("403","Forbidden auth ID");
                        exit;
                }
                # store the registration and generate a SIP reply
                if (!save("location"))
                        xlog("failed to register AoR $tu\n");

                exit;
        }


       if ($rU==NULL) {
                # request with no Username in RURI
                send_reply("484","Address Incomplete");
                exit;
        }

        # apply DB based aliases
        alias_db_lookup("dbaliases");

        # do lookup with method filtering
        if (!lookup("location","m")) {
                if (!db_does_uri_exist()) {

                        send_reply("420","Bad Extension");
                        exit;
                }

                t_reply("404", "Not Found");
                exit;
        }

        # when routing via usrloc, log the missed calls also
        do_accounting("db","missed");

        route(relay);
}

route[relay] {

       # for INVITEs enable some additional helper routes
        if (is_method("INVITE")) {



                t_on_branch("per_branch_ops");
                t_on_reply("handle_nat");
                t_on_failure("missed_call");
        }



        if (!t_relay()) {
                send_reply("500","Internal Error");
        }
        exit;
}

branch_route[per_branch_ops] {
        xlog("new branch at $ru\n");
}

onreply_route[handle_nat] {

        xlog("incoming reply\n");
}

failure_route[missed_call] {
        if (t_was_cancelled()) {
                exit;
        }

        # uncomment the following lines if you want to block client
        # redirect based on 3xx replies.

         ##if (t_check_status("3[0-9][0-9]")) {
         ##t_reply("404","Not found");
         ##      exit;
         ##}


}

local_route {
        if (is_method("BYE") && $DLG_dir=="UPSTREAM") {

                acc_db_request("200 Dialog Timeout", "acc");

        }

}

4. Start OpenSIPS

#/etc/init.d/opensips start

add domain

#opensipsctl domain add 192.168.10.222

#opensipsctl domain reload 192.168.10.222

(ip address ของ server)

แล้ว add subsciber (เบอร์โทรศัพท์)

#opensipsctl add This email address is being protected from spambots. You need JavaScript enabled to view it. 1000     ### user = 1000 password = 1000

#opensipsctl add This email address is being protected from spambots. You need JavaScript enabled to view it. 1001     

 

5. ทดสอบการใช้งาน

ใช้ ip phone หรือ soft phone 2 ตัว register มาที่ OpenSIPS server โดยใช้ user 1000 password 1000 และ

user 1001 password 1001 เมื่อ register เรียบร้อย ลองโทรหากัน ถ้าโทรหากันได้

ก็ถือว่าการติดตั้งคอนฟิกในเบื้องต้นเสร็จเรียบร้อยแล้ว

 

6. คำสั่งอื่นๆ

#/etc/init.d/opensips start

#/etc/init.d/opensips stop

#/etc/init.d/opensips restart

#ps -ef | grep opensips