Skip to content
LIVE FEED

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

Sunex 16/09/2026 · 11 phút đọc

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

ModuleOpenSIPSKamailioGhi chú khác biệt
TransactiontmtmGiống hệt
Stateless replyslslGiống hệt
Record-routerrrrGiống hệt
Max-ForwardsmaxfwdmaxfwdGiống hệt
Vị trí đăng kýusrloc + registrarusrloc + registrarGiống hệt
Xác thựcauth + auth_dbauth + auth_dbGiống hệt
Dialog trackingdialogdialogGiống hệt
CDR/Accountingacc (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
NATnathelpernathelperGiống hệt
Load balancingdispatcherdispatcherGiống hệt
LCR nâng caodroutingcarrierrouteTên khác, carrierroute phong phú hơn
B2BUAb2b_entities + b2b_logicb2b_logic (kiến trúc gộp hơn)Cách tiếp cận khác
Topology hidingThủ công qua B2BUAtopos (tự động hoá)Khác biệt triết lý lớn — xem mục 6
Media relayrtpenginertpengineCùng giao thức ng-control
Scripting nâng caoKhông cóKEMI (Lua/Python/JS)Khác biệt lớn nhất — xem mục 5
ClusteringclustererdmqThiế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

OpenSIPSKamailioÝ nghĩa
log_level=3debug=2Mức độ chi tiết log — tên tham số khác hẳn, đừng gõ nhầm log_level vào Kamailio
log_stderror=yeslog_stderror=yesGiống hệt
children=4children=4Giống hệt
listen=udp:eth0:5060listen=udp:eth0:5060Giố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ìnhThấp — bật một hàm là xongCao — phải tự viết toàn bộ logic tạo/nối hai leg
Dùng khi nàoChỉ cần che giấu hạ tầng khỏi đối tác/khách hàng bên ngoàiCầ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ử

  1. Kiểm tra cú pháp: kamailio -c -f /etc/kamailio/kamailio.cfg
  2. Chạy foreground debug: kamailio -f /etc/kamailio/kamailio.cfg -D -E
  3. Test REGISTER bằng sipsak — giống hệt lệnh đã dùng ở OpenSIPS, không cần học lại.
  4. 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ứngNguyên nhân hay gặp
Lỗi “module not found” dù đã loadmodule đúng tênThiế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=3Nhầ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 đã loadQuê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 KEMIQuê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ành request_route{}, còn failure_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).

Contact
Facebook
TOP