BlogKamailio Giai đoạn K0-K1 chi tiết: So sánh OpenSIPS, KEMI, topos và kamailio.cfg đầy đủ
Bố cục bài viết
Trang này
Ngày tạo
16/09/2026
Cập nhật
Lượt xem
7
Đọc
11m
Kamailio Giai đoạn K0-K1 — Định vị khác biệt và Cấu hình nền tảng (Chi tiết đầy đủ)
Điều kiện tiên quyết: đã hoàn thành Giai đoạn 0-1 của OpenSIPS (transaction/dialog, cấu trúc route, module lõi). Tài liệu này tận dụng tối đa kiến thức đó — không lặp lại lý thuyết SIP.
1. Gốc gác chung — vì sao học nhanh được
Cả Kamailio và OpenSIPS đều bắt nguồn từ OpenSER — dự án tách làm hai nhánh phát triển độc lập từ năm 2008 do bất đồng trong đội ngũ phát triển. Vì cùng gốc, phần lõi xử lý SIP (transaction, dialog, cấu trúc route script) gần như song song — đây là lý do bạn học Kamailio nhanh hơn hẳn người học từ số 0.
2. Bảng ánh xạ module — dùng lại được gần như nguyên vẹn
| Module | OpenSIPS | Kamailio | Ghi chú khác biệt |
|---|---|---|---|
| Transaction | tm | tm | Giống hệt |
| Stateless reply | sl | sl | Giống hệt |
| Record-route | rr | rr | Giống hệt |
| Max-Forwards | maxfwd | maxfwd | Giống hệt |
| Vị trí đăng ký | usrloc + registrar | usrloc + registrar | Giống hệt |
| Xác thực | auth + auth_db | auth + auth_db | Giống hệt |
| Dialog tracking | dialog | dialog | Giống hệt |
| CDR/Accounting | acc (dùng do_accounting() tường minh) | acc (vẫn dùng cơ chế flag-based truyền thống) | Khác biệt thật sự — xem mục 4.3 |
| NAT | nathelper | nathelper | Giống hệt |
| Load balancing | dispatcher | dispatcher | Giống hệt |
| LCR nâng cao | drouting | carrierroute | Tên khác, carrierroute phong phú hơn |
| B2BUA | b2b_entities + b2b_logic | b2b_logic (kiến trúc gộp hơn) | Cách tiếp cận khác |
| Topology hiding | Thủ công qua B2BUA | topos (tự động hoá) | Khác biệt triết lý lớn — xem mục 6 |
| Media relay | rtpengine | rtpengine | Cùng giao thức ng-control |
| Scripting nâng cao | Không có | KEMI (Lua/Python/JS) | Khác biệt lớn nhất — xem mục 5 |
| Clustering | clusterer | dmq | Thiết kế khác |
3. Khác biệt cấu trúc file cấu hình — quan trọng nhất khi bắt đầu gõ
3.1 Tên khối route chính đã đổi
Đây là điểm gây bối rối nhiều nhất cho người quen OpenSIPS: OpenSIPS dùng khối route { ... } trần, còn Kamailio bắt buộc đặt tên rõ ràng là request_route { ... }:
# OpenSIPS
route {
...
}
# Kamailio — tên khối khác, hành vi tương đương
request_route {
...
}
Các khối phụ vẫn giữ nguyên cú pháp và cách gọi:
failure_route[TÊN] { ... }
branch_route[TÊN] { ... }
onreply_route[TÊN] { ... }
event_route[TÊN] { ... }
route[TÊN] { ... } # named route, gọi lại bằng route(TÊN) — giống hệt OpenSIPS
Kamailio còn có thêm reply_route { ... } (không đánh số) — đây là route lõi xử lý stateless reply ở tầng thấp nhất, khác với onreply_route[] (chạy theo từng transaction cụ thể). Với cấu hình mức cơ bản/trung cấp, bạn hiếm khi cần đụng tới reply_route trần này.
3.2 Global parameters — tên một số tham số đổi khác
| OpenSIPS | Kamailio | Ý nghĩa |
|---|---|---|
log_level=3 | debug=2 | Mức độ chi tiết log — tên tham số khác hẳn, đừng gõ nhầm log_level vào Kamailio |
log_stderror=yes | log_stderror=yes | Giống hệt |
children=4 | children=4 | Giống hệt |
listen=udp:eth0:5060 | listen=udp:eth0:5060 | Giống hệt |
| (thường không cần) | mpath="/usr/lib/aarch64-linux-gnu/kamailio/modules/" | Kamailio thường bắt buộc khai báo rõ đường dẫn thư mục chứa file .so của module — khác biệt hay khiến người mới bị lỗi “module not found” |
3.3 Preprocessor directive — giống hệt, dùng để đặt tên flag
Cả hai đều hỗ trợ #!define, #!ifdef, #!else, #!endif. Kamailio có truyền thống định nghĩa sẵn một bộ hằng số flag ngay đầu file (đây gần như là quy ước chuẩn mực trong cộng đồng, xuất hiện trong hầu hết mọi tutorial và cả cfg mẫu chính thức):
#!define FLT_ACC 1
#!define FLT_ACCMISSED 2
#!define FLT_ACCFAILED 3
#!define FLT_NATS 5
#!define FLB_NATB 6
#!define FLB_NATSIPPING 7
4. Module lõi — điểm khác biệt cụ thể cần lưu ý
4.1 auth_db — gần như giống hệt
loadmodule "auth.so"
loadmodule "auth_db.so"
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "db_url", "mysql://kamailio:kamailiorw@localhost/kamailio")
Hàm dùng trong script — www_authorize("", "subscriber") / www_challenge("", "0") — cú pháp giữ nguyên so với OpenSIPS.
4.2 usrloc/registrar — giống hệt
loadmodule "usrloc.so"
modparam("usrloc", "db_mode", 0)
loadmodule "registrar.so"
save("location") / lookup("location") — giống hệt.
4.3 acc — khác biệt thật sự, phải để ý
Đây là chỗ Kamailio chưa theo xu hướng chuyển sang gọi hàm tường minh như OpenSIPS — vẫn dùng cơ chế đặt flag rồi để module tự động ghi log khi transaction hoàn tất:
loadmodule "acc.so"
modparam("acc", "early_media", 0)
modparam("acc", "report_ack", 0)
modparam("acc", "report_cancels", 0)
modparam("acc", "detect_direction", 0)
modparam("acc", "log_flag", "FLT_ACC")
modparam("acc", "log_missed_flag", "FLT_ACCMISSED")
Trong script chỉ cần setflag(FLT_ACC); tại đúng thời điểm muốn ghi — module tự lo phần còn lại, không cần gọi hàm do_accounting() như OpenSIPS.
5. KEMI — giới thiệu và ví dụ thực tế
5.1 KEMI hoạt động như thế nào
KEMI (Kamailio Embedded Management Interface) cho phép thay thế toàn bộ nội dung request_route{}/failure_route[]… bằng một file mã nguồn Lua/Python/JavaScript thật. Kamailio core, khi phát hiện dùng KEMI, sẽ gọi vào các hàm có tên quy ước (ksr_request_route(), ksr_reply_route()…) trong file script đó thay vì parse cfg DSL.
Kích hoạt bằng module tương ứng:
loadmodule "app_lua.so"
modparam("app_lua", "load", "/etc/kamailio/kamailio.lua")
5.2 Ví dụ đối chiếu cfg thuần vs Lua (KEMI)
Cfg thuần (giống OpenSIPS):
request_route {
if (is_method("CANCEL")) {
if (t_check_trans()) {
t_relay();
}
exit;
}
}
Tương đương bằng Lua qua KEMI:
function ksr_request_route()
if KSR.pv.get("$rm") == "CANCEL" then
if KSR.tm.t_check_trans() > 0 then
KSR.tm.t_relay()
end
return 1
end
end
Nhận xét quan trọng: KSR là đối tượng động export toàn bộ hàm module ra namespace theo dạng KSR.<tên_module>.<tên_hàm>() — ví dụ KSR.sl.sl_send_reply(...), KSR.registrar.save(...), KSR.tm.t_relay(). Đây chính là bảng tra cứu bạn sẽ dùng liên tục khi viết KEMI thật.
5.3 Khi nào nên chuyển sang KEMI
Không phải lúc nào cũng cần — cfg thuần vẫn đủ tốt cho logic đơn giản. Chuyển sang KEMI khi: cần gọi API HTTP/REST ngoài (kiểm tra số dư tài khoản trước khi cho gọi, ví dụ), cần cấu trúc dữ liệu phức tạp (bảng, JSON) khó biểu diễn bằng AVP, hoặc muốn viết unit test cho logic routing (dễ hơn hẳn với một file Lua/Python thật).
6. topos — Topology Hiding tự động
6.1 Vấn đề nó giải quyết
Ở OpenSIPS Giai đoạn 2, để ẩn danh tính/thông tin nội bộ, bạn phải tự dựng cả một kịch bản B2BUA (hai Call-ID độc lập). Kamailio có module topos giải quyết một lớp vấn đề hẹp hơn nhưng tự động hoàn toàn: ẩn toàn bộ header lộ thông tin nội bộ (Via, Record-Route, Contact chứa IP thật) mà không cần tách dialog — nó lưu ánh xạ giữa header thật và header “mặt nạ” vào một bảng lưu trữ (DB hoặc bộ nhớ), rồi tự động dịch qua lại hai chiều.
loadmodule "topos.so"
modparam("topos", "db_url", "mysql://kamailio:kamailiorw@localhost/kamailio")
modparam("topos", "sanity_checks", 1)
Sau khi load, chỉ cần gọi một hàm duy nhất ngay đầu request_route cho mọi request initial:
route[TOPOS]{
topos_enable();
}
6.2 So sánh triết lý — chọn đúng công cụ
| Tiêu chí | topos (Kamailio) | B2BUA thủ công (OpenSIPS) |
|---|---|---|
| Mức độ ẩn | Ẩn header lộ thông tin hạ tầng (IP, hostname nội bộ) | Ẩn hoàn toàn, tách hẳn hai dialog độc lập (Call-ID khác nhau) |
| Độ phức tạp cấu hình | Thấp — bật một hàm là xong | Cao — phải tự viết toàn bộ logic tạo/nối hai leg |
| Dùng khi nào | Chỉ cần che giấu hạ tầng khỏi đối tác/khách hàng bên ngoài | Cần kiểm soát hoàn toàn từng leg (transfer, anonymize identity thật, transcoding logic riêng) |
7. HEP + Homer/sngrep — hệ sinh thái giám sát đặc trưng của Kamailio
7.1 Vấn đề Wireshark không giải quyết được ở quy mô lớn
Wireshark chỉ bắt gói được trên một máy tại một thời điểm. Với hệ thống nhiều node Kamailio phân tán, bạn cần một nơi tập trung toàn bộ bản tin SIP từ mọi node lại để trace theo Call-ID xuyên suốt nhiều server — đây là vai trò của HEP (Homer Encapsulation Protocol).
loadmodule "siptrace.so"
modparam("siptrace", "trace_flag", 21)
modparam("siptrace", "duplicate_uri", "sip:127.0.0.1:9060") # gửi bản sao bản tin tới Homer capture agent
Kiến trúc: mỗi node Kamailio gửi một bản sao mọi bản tin SIP (đóng gói theo giao thức HEP) tới Homer server (lưu trữ tập trung) — sau đó dùng giao diện web Homer hoặc công cụ dòng lệnh sngrep để tìm kiếm/trace theo Call-ID, xem toàn bộ hành trình một cuộc gọi qua nhiều node cùng lúc, kể cả khi cuộc gọi đã kết thúc từ lâu (khác hẳn Wireshark chỉ xem được real-time).
7.2 Vì sao đáng học ngay từ giai đoạn nền tảng
Kỹ năng debug bằng Homer/sngrep là thứ phân biệt rõ “người từng vận hành hệ thống thật quy mô carrier” — nên hình thành thói quen bật siptrace ngay từ lab đầu tiên, thay vì chỉ dùng khi gặp sự cố production.
8. File cấu hình đầy đủ — port lại bài lab Giai đoạn 1 OpenSIPS sang Kamailio
#!KAMAILIO
#!define FLT_ACC 1
#!define FLT_ACCMISSED 2
####### Global Parameters #########
debug=2
log_stderror=yes
children=4
mpath="/usr/lib/x86_64-linux-gnu/kamailio/modules/"
listen=udp:eth0:5060
####### Modules Section ########
loadmodule "tm.so"
loadmodule "sl.so"
loadmodule "rr.so"
modparam("rr", "append_fromtag", 1)
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
modparam("usrloc", "db_mode", 0)
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "db_url", "mysql://kamailio:kamailiorw@localhost/kamailio")
loadmodule "dialog.so"
modparam("dialog", "dlg_flag", 4)
loadmodule "acc.so"
modparam("acc", "log_flag", "FLT_ACC")
modparam("acc", "log_missed_flag", "FLT_ACCMISSED")
####### Routing Logic ########
request_route {
if (!mf_process_maxfwd_header(10)) {
sl_send_reply("483", "Too Many Hops");
exit;
}
if (has_totag()) {
if (loose_route()) {
if (is_method("BYE")) {
setflag(FLT_ACC);
}
route(RELAY);
exit;
} else {
if (is_method("ACK")) {
if (t_check_trans()) {
t_relay();
}
exit;
}
sl_send_reply("404", "Not Here");
exit;
}
}
if (is_method("CANCEL")) {
if (t_check_trans()) {
t_relay();
}
exit;
}
if (is_method("OPTIONS") && $rU==$null) {
sl_send_reply("200", "OK");
exit;
}
if (is_method("REGISTER")) {
if (!www_authorize("", "subscriber")) {
www_challenge("", "0");
exit;
}
if (!save("location")) {
sl_reply_error();
}
exit;
}
if (is_method("INVITE")) {
record_route();
if (!t_newtran()) {
sl_reply_error();
exit;
}
}
if (!lookup("location")) {
sl_send_reply("404", "Not Found");
exit;
}
if (is_method("INVITE")) {
setflag(FLT_ACC);
}
route(RELAY);
}
route[RELAY] {
if (!t_relay()) {
sl_reply_error();
}
exit;
}
Khác biệt nhỏ đáng chú ý so với bản OpenSIPS: dòng t_newtran() xuất hiện tường minh trong nhánh INVITE — ở OpenSIPS transaction thường được tạo ngầm khi gọi t_relay() lần đầu, nhưng nhiều cfg mẫu Kamailio khuyến khích tạo transaction tường minh sớm hơn trong luồng INVITE để kiểm soát tốt hơn các bước xử lý ở giữa (ví dụ chèn thêm topos_enable() hoặc rtpengine_offer() trước khi relay).
9. Kiểm thử
- Kiểm tra cú pháp:
kamailio -c -f /etc/kamailio/kamailio.cfg - Chạy foreground debug:
kamailio -f /etc/kamailio/kamailio.cfg -D -E - Test REGISTER bằng
sipsak— giống hệt lệnh đã dùng ở OpenSIPS, không cần học lại. - Bật
siptrace+ Homer/sngrep ngay từ lần test đầu tiên để làm quen công cụ.
10. Lỗi thường gặp khi chuyển từ OpenSIPS sang Kamailio
| Triệu chứng | Nguyên nhân hay gặp |
|---|---|
Lỗi “module not found” dù đã loadmodule đúng tên | Thiếu khai báo mpath trỏ đúng thư mục chứa file .so — vị trí mặc định khác nhau tuỳ bản distro |
Log không hiện dù đã set log_level=3 | Nhầm tên tham số — Kamailio dùng debug, không phải log_level |
CDR không bao giờ được ghi dù module acc đã load | Quên setflag(FLT_ACC) đúng chỗ — Kamailio không tự ghi nếu không có flag, khác OpenSIPS gọi do_accounting() tường minh |
| Cấu hình chạy được bằng cfg thuần nhưng không chạy khi chuyển sang KEMI | Quên đối tượng KSR là namespace động — gõ sai tên module con (ví dụ KSR.registrar.save chứ không phải KSR.save) |
11. Tự kiểm tra trước khi qua Giai đoạn K2
- Giải thích được vì sao Kamailio đổi tên khối
route{}thànhrequest_route{}, cònfailure_route[]/branch_route[]/onreply_route[]thì giữ nguyên. - Chỉ ra đúng 3 tham số global parameter tên bị đổi khác so với OpenSIPS.
- Giải thích được sự khác nhau giữa cơ chế accounting của Kamailio (flag-based) và OpenSIPS (
do_accounting()tường minh). - Mô tả được luồng dữ liệu khi bật
siptrace— bản tin SIP đi từ Kamailio tới đâu, dùng công cụ gì để xem lại.
Nếu trả lời trôi chảy cả 4 câu, bạn sẵn sàng cho Giai đoạn K2 (KEMI chuyên sâu — viết lại toàn bộ logic trên bằng Lua thật).