Article Index

ซื้อสินค้า VoIP คลิกไปที่ https://www.lazada.co.th/shop/adventek/

1. ในสองตอนที่ผ่านมาระบบยังไม่ support NAT ในตอนนี้เราติดตั้ง rtpproxy เพื่อให้ระบบสามารถ support NAT

(ip phones อยู่หลัง NAT)

#apt install rtpproxy

edit /etc/init.d/rtpproxy แก้ไข  CONTROL_SOCK และ DAEMON_OPTS ดังนี้

ส่วนอื่นๆ เหมือนเดิม 

================================================================

...

CONTROL_SOCK="udp:127.0.0.1:12221"

...

DAEMON_OPTS="-l 192.168.10.222 -s $CONTROL_SOCK -u $USER:$GROUP -p $PIDFILE $EXTRA_OPTS"

...

================================================================

 #systemctl restart rtpproxy

หลังจากนั้น compile modules ต่างๆให้ครบ (ยังไม่ได้ใช้งาน เตรียมให้พร้อมไว้ก่อน) ดังนี้

#cd /usr/src/opensips-2.4.11

#make modules=modules/dialplan modules

#cp modules/dialplan/dialplan.so /lib64/opensips/module

#make modules=modules/xcap modules

#cp modules/xcap/xcap.so /lib64/opensips/module

#make modules=modules/presence modules

#cp modules/presence/presence.so /lib64/opensips/module

#make modules=modules/presence_xml modules

#cp modules/presence_xml/presence_xml.so /lib64/opensips/module

 

2. ในตอนนี้เราจะโทรเข้าออกผ่าน PSTN gateway ดังรูป

 

 


 

PSTN gateway แปลง SIP (IP) <--> E-1 (TDM) หรือ FXO (analog)

opensips server จะใช้ permissions module เพื่อกำหนดให้ call จาก ip address ที่กำหนดเท่านั้นเข้ามาได้

ส่วนการโทรออกจะใช้ group module เพื่อกำหนดว่า user ที่อยู่ใน group ไหนโทรออกไป PSTN ได้บ้าง

 

3. คอนฟิกจาก OpenSIPS Control Panel ดังนี้

 

 

เพิ่ม user เข้าระบบ จะใช้ username ตัวนี้ในการ register โทรศัพท์เข้ากับ opensips server เมื่อต้องการโทรหากันจะใช้ username โทรหากันก็ได้ แต่อาจจะไม่สะดวกจำเป็นต้องมี alias username ดังรูปถัดไป   alias username จำเป็นต้องมีเมื่อเราจะรับ call จาก PSTN

 

  

หลังจากนั้น กำหนด acl ให้ user แต่ละคน   กำหนดให้ grp_two เท่านั้น   ที่สามารถโทรออกไป PSTN ได้   ซึ่งต้องกำหนดในไฟล์ /etc/opensips/opensips.cfg ซึ่งเป็นวิธีที่ไม่ดีเพราะเมื่อมีการเปลี่ยนแปลง ต้องแก้ไขไฟล์ /etc/opensips/opensips.cfg แล้วต้อง restart opensips วิธีที่ดีกว่าต้องใช้ module dialplan และ module dynamic routing ซึ่งจะเขียนในตอนถัดๆ ไป

 

 

 กำหนดค่าให้ permissions module ให้ call จาก ip address 192.168.10.3 เท่านั้นที่เข้ามาหา opensips server ได้

 

 

4. แก้ไขคอนฟิกไฟล์ /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"

#### HTTPD module
loadmodule "httpd.so"
modparam("httpd", "port", 8888)

#### 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")
modparam("registrar", "received_avp", "$avp(received_nh)")/* 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)

#### PRESENCE modules
loadmodule "xcap.so"
loadmodule "presence.so"
loadmodule "presence_xml.so"
modparam("xcap|presence", "db_url",
        "mysql://opensips:opensipsrw@localhost/opensips")
modparam("presence_xml", "force_active", 1)
modparam("presence", "fallback2db", 0)

#### 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")

####  NAT modules
loadmodule "nathelper.so"
modparam("nathelper", "natping_interval", 10)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping_bflag", "SIP_PING_FLAG")
modparam("nathelper", "sipping_from", "sip:pinger@127.0.0.1") # CUSTOMIZE ME
modparam("nathelper", "received_avp", "$avp(received_nh)")

loadmodule "rtpproxy.so"
modparam("rtpproxy", "rtpproxy_sock", "udp:localhost:12221") # CUSTOMIZE ME

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

####  DYNAMMIC ROUTING module
loadmodule "drouting.so"
modparam("drouting", "db_url",
        "mysql://opensips:opensipsrw@localhost/opensips")

#### MI_JSON module
loadmodule "mi_json.so"
modparam("mi_json", "mi_json_root", "json")

loadmodule "permissions.so"
modparam("permissions", "db_url", "mysql://opensips:opensipsrw@localhost/opensips")

loadmodule "group.so"
modparam("group", "db_url", "mysql://opensips:opensipsrw@localhost/opensips")

loadmodule "proto_udp.so"

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

# main request routing logic

route {

        # initial NAT handling; detect if the request comes from behind a NAT
        # and apply contact fixing
        force_rport();
        if (nat_uac_test("23")) {
                if (is_method("REGISTER")) {
                        fix_nated_register();
                        setbflag(NAT);
                } else {
                        fix_nated_contact();
                        setflag(NAT);
                }
        }

        if (!mf_process_maxfwd_header("10")) {
                send_reply("483","Too Many Hops");
                exit;
        }

        # sequential request

        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() ) {
                        if (is_method("SUBSCRIBE") && is_myself("$rd")) {
                                # in-dialog subscribe requests
                                route(handle_presence);
                                exit;
                        }
                        # 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");

                }


                if (check_route_param("nat=yes"))
                        setflag(NAT);
                # route it out to whatever destination was set by loose_route()
                # in $du (destination URI).
                route(relay);
                exit;
        }

        # initial request

        # 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 ( !check_source_address("0") ) {
                        # 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

                }

        }

        # 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"))
                        route(handle_presence);

        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;
                }               if (isflagset(NAT)) {
                        setbflag(SIP_PING_FLAG);
                }
                # 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");

        # apply transformations from dialplan table
        # dp_translate("0","$rU/$rU");
        #
        # if ($rU=~"^\+[1-9][0-9]+$") {
        #
        #               if (!do_routing("0")) {
        #               send_reply("500","No PSTN Route found");
        #               exit;
        #       }
        #
        #       route(relay);
        #       exit;
        # }

        #### routing to PSTN gateway ####

        if ( $rU=~"^0[2-9][0-9]{7}" ) {
           if ( db_is_user_in("credentials","grp_two") ) {
               route(pstn);
               exit;
           } else {
               sl_send_reply("403","No permission for long distance");
           }
        }

        # do lookup with method filtering
        if (!lookup("location","m")) {
                if (!db_does_uri_exist()) {
                        send_reply("420","Bad Extension");
                        exit;
                }

                # redirect to a different VM system
                $du = "sip:127.0.0.2:5060"; # CUSTOMIZE ME
                route(relay);

        }

        if (isbflagset(NAT)) setflag(NAT);

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

        route(relay);
}

route[pstn] {
     rewritehostport("192.168.10.3");
     route(relay);
}

route[relay] {
        # for INVITEs enable some additional helper routes
        if (is_method("INVITE")) {

                if (isflagset(NAT)) {
                        rtpproxy_offer("ro");
                }

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

        if (isflagset(NAT)) {
                add_rr_param(";nat=yes");
        }

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

# Presence route
route[handle_presence]
{
        if (!t_newtran()) {
                sl_reply_error();
                exit;
        }

        if(is_method("PUBLISH")) {
                handle_publish();
        } else
        if( is_method("SUBSCRIBE")) {
                handle_subscribe();
        }

        exit;
}

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


onreply_route[handle_nat] {
        if (nat_uac_test("1"))
                fix_nated_contact();
        if ( isflagset(NAT) )
                rtpproxy_answer("ro");
        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;
        ##}


        # redirect the failed to a different VM system
        if (t_check_status("486|408")) {
                $du = "sip:127.0.0.2:5060"; # CUSTOMIZE ME
                # do not set the missed call flag again
                route(relay);
        }
}

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

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

        }
}

 

5. ทดสอบ

ลองใช้ softphone บนมือถือ register เข้า opensips server จะคุยกันได้ผ่าน voice, video, message แต่ถ้าโทรผ่าน PSTN ก็จะได้เฉพาะ voice เท่านั้น