ซื้อสินค้า VoIP คลิกไปที่ https://www.lazada.co.th/shop/adventek/
1. เมื่อไหร่ใช้ 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 ไม่เหมาะกับงานแบบนี้อย่างแน่นอน
2. 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 3.4.5 บน Debian 11.9 หลังจากนั้นบทความใน
ตอนต่อๆ ไป จะกล่าวถึง OpenSIPS GUI, การใช้งานในลักษณะ SIP trunking และ VoIP Service Platform
3. ติดตั้ง OpenSIPS
ขั้นแแรกติดตั้ง Debian 11.9 เลือก options - web server, ssh server, standard system utilities ตามลิ้งค์
หลังจากติดตั้ง OS เสร็จ ให้ login ด้วย root แล้ว ติดตั้ง software ที่จำเป็น
#apt install curl gnupg2 m4 wget
ติดตั้ง rtpproxy
#cd /usr/src
#wget http://archive.ubuntu.com/ubuntu/pool/universe/r/rtpproxy/
#dpkg -i rtpproxy_1.2.1-2.2_amd64.deb
edit /etc/default/rtpproxy
EXTRA_OPTS="-l 192.168.100.200 -s udp:127.0.0.1:12221 -u rtpproxy:rtpproxy"
edit /etc/init.d/rtpproxy
DAEMON_OPTS="-p $PIDFILE $EXTRA_OPTS"
#systemctl restart rtpproxy
#systemctl enable rtpproxy
ติดตั้ง mariadb-server
#apt install mariadb-server
#mysql_secure_installation
ติดตั้ง OpenSIPS 3.4.5 from repos
#curl https://apt.opensips.org/opensips-org.gpg -o /usr/share/keyrings/opensips-org.gpg
#echo "deb [signed-by=/usr/share/keyrings/opensips-org.gpg] https://apt.opensips.org bullseye 3.4-releases" >/etc/apt/sources.list.d/opensips.list
#echo "deb [signed-by=/usr/share/keyrings/opensips-org.gpg] https://apt.opensips.org bullseye cli-nightly" >/etc/apt/sources.list.d/opensips-cli.list
#apt update && apt
#apt install opensips-*
คอนฟิก OpenSIPS-CLI
edit file /root/.opensips-cli.cfg ดังนี้
[default]
log_level: WARNING
prompt_name: cli
prompt_intro: Welcome to my CLI!
prompt_emptyline_repeat_cmd: False
history_file: ~/.opensips-cli.history
history_file_size: 1000
output_type: pretty-print
communication_type: http
domain: 192.168.100.200
plain_text_passwords: true
ใช้ opensips-cli สร้าง database ตาม commands ดังนี้
#opensips-cli -x database create opensips
#opensips-cli -x database add presence
4. คอนฟิก OpenSIPS
คอนฟิก log file
#touch /var/log/opensips.log
แก้ไข file /etc/rsyslog.conf โดยการเพิ่มบรรทัดต่อไปนี้เข้าไป
local0.* -/var/log/opensips.log
#systemctl restart rsyslog
แก้ไข file /etc/opensips/opensips.cfg ดังนี้
(***** download file ได้ที่ OpenSIPS-3.4.5 *****)
###################################
####### Global Parameters #########
###################################
/* uncomment the following lines to enable debugging */
#debug_mode=yes
log_level=3
xlog_level=3
stderror_enabled=no
syslog_enabled=yes
syslog_facility=LOG_LOCAL0
udp_workers=4
/* 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
socket=udp:192.168.100.200:5060
################################
####### Modules Section ########
################################
#set module path
mpath="/usr/lib/x86_64-linux-gnu/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)
#### 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", "working_mode_preset", "single-instance-sql-write-back")
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 to 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", "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)
modparam("auth_db|usrloc", "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)
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")
modparam("nathelper", "received_avp", "$avp(received_nh)")
loadmodule "rtpproxy.so"
modparam("rtpproxy", "rtpproxy_sock", "udp:localhost:12221")
#### 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_HTTP module
loadmodule "mi_http.so"
loadmodule "mi_html.so"
modparam("mi_html", "root", "opensips_mi")
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("private-contact,diff-ip-src-via,private-via,diff-port-src-via")) {
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;
}
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;
}
# 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") || is_from_gw() ) ) {
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("", "auth");
exit;
}
if ($au!=$fU) {
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"))
route(handle_presence);
if (is_method("REGISTER")) {
# authenticate the REGISTER requests
if (!www_authorize("", "subscriber")) {
www_challenge("", "auth");
exit;
}
if ($au!=$tU) {
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]+$") {
strip(1);
if (!do_routing(0)) {
send_reply(500,"No PSTN Route found");
exit;
}
route(relay);
exit;
}
# do lookup with method filtering
if (!lookup("location", "method-filtering")) {
if (!db_does_uri_exist("$ru","subscriber")) {
send_reply(420,"Bad Extension");
exit;
}
# redirect to a different VM system
$du = "sip:192.168.100.201:5062";
xlog("method filtering");
route(relay);
}
if (isbflagset("NAT")) setflag("NAT");
# 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")) {
if (isflagset("NAT") && has_body("application/sdp")) {
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("private-contact"))
fix_nated_contact();
if ( isflagset("NAT") && has_body("application/sdp") )
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";
# do not set the missed call flag again
# route(relay);
route(vm);
}
}
local_route {
if (is_method("BYE") && $DLG_dir=="UPSTREAM") {
acc_db_request("200 Dialog Timeout", "acc");
}
}
route[vm] {
# check if one calling himself
xlog("i am here");
if ($fU==$rU) {
exit;
}
sethostport("192.168.100.201:5062");
t_relay();
exit;
}
5. Start OpenSIPS
#systemctl start opensips
#systemctl status opensips
ขั้นตอนถัดไป add domain โดย access mariadb
#mysql -uroot -pXXXXX
MariaDB [(none)]> use opensips;
MariaDB [opensips]> insert into domain values(1,'192.168.100.200','first domain',NOW());
MariaDB [opensips]> exit
ใช้ opensips-cli reload domain และ และเพิ่ม users
#opensips-cli
Welcome to my CLI!
(cli)
(cli): mi domain_reload
"OK"
(cli): mi domain_dump
{
"Domains": [
{
"name": "192.168.100.200"
}
]
}
แสดงว่าเราเพิ่ม domain สำเร็จแล้ว ขั้นตอนต่อไป คือ การเพิ่ม users
(cli): user add This email address is being protected from spambots. You need JavaScript enabled to view it. pass123456
(cli): user add This email address is being protected from spambots. You need JavaScript enabled to view it. pass123456
6. ทดสอบการใช้งาน
ใช้ ip phone หรือ soft phone 2 ตัว register มาที่ OpenSIPS server โดยใช้ user 3000 password pass123456 และ
user 3001 password pass123456 เมื่อ register เรียบร้อย ลองโทรหากัน ถ้าโทรหากันได้
ก็ถือว่าการติดตั้งคอนฟิกในเบื้องต้นเสร็จเรียบร้อยแล้ว
7. คำสั่งอื่นๆ
#systemctl start opensips
#systemctl stop opensips
#systemctl restart opensips
#ps -ef | grep opensips