BlogGiai đoạn 0 chi tiết: Nền tảng SIP, Transaction/Dialog, Method và P-Header trong VoLTE/IMS
Bố cục bài viết
Trang này
Ngày tạo
15/09/2026
Cập nhật
Lượt xem
6
Đọc
17m
Giai đoạn 0 — Nền tảng SIP (Chi tiết đầy đủ)
Tài liệu này là bản mở rộng chi tiết của Giai đoạn 0 trong lộ trình chuyên gia OpenSIPS. Mục tiêu: sau khi đọc và thực hành xong, bạn hiểu SIP ở mức có thể tự giải thích lại cho người khác, không chỉ “biết tên gọi”.
1. SIP là gì, và tại sao nó được thiết kế như vậy
SIP (Session Initiation Protocol, RFC 3261) là giao thức báo hiệu (signaling) — nó không truyền tải âm thanh/hình ảnh của cuộc gọi. SIP chỉ làm nhiệm vụ: thiết lập, sửa đổi, và kết thúc một phiên (session). Media thật (âm thanh) đi bằng giao thức riêng là RTP.
Điểm thiết kế quan trọng nhất cần nắm: SIP mô phỏng theo mô hình HTTP (text-based, request/response, header:value) nhưng thêm khái niệm transaction và dialog mà HTTP không có — vì một cuộc gọi điện thoại có trạng thái sống trong nhiều phút, không phải một request-response đơn lẻ như tải một trang web.
Hai vai trò cơ bản:
- UAC (User Agent Client) — bên gửi request (người gọi).
- UAS (User Agent Server) — bên nhận request và trả lời (người được gọi). Một endpoint thường đóng cả hai vai trò tại các thời điểm khác nhau trong cùng một cuộc gọi (ví dụ: gửi BYE thì làm UAC, nhận INVITE thì làm UAS).
2. Cấu trúc bản tin SIP
2.1 Request
INVITE sip:bob@example.com SIP/2.0
Via: SIP/2.0/UDP alice-pc.example.com:5060;branch=z9hG4bK776asdhds
Max-Forwards: 70
To: Bob <sip:bob@example.com>
From: Alice <sip:alice@example.com>;tag=1928301774
Call-ID: a84b4c76e66710@alice-pc.example.com
CSeq: 314159 INVITE
Contact: <sip:alice@alice-pc.example.com>
Content-Type: application/sdp
Content-Length: 142
[SDP body ở đây]
Dòng đầu (Request-Line) gồm 3 phần: method (INVITE), Request-URI (sip:bob@example.com — địa chỉ đích), phiên bản (SIP/2.0).
2.2 Response
SIP/2.0 200 OK
Via: SIP/2.0/UDP alice-pc.example.com:5060;branch=z9hG4bK776asdhds
To: Bob <sip:bob@example.com>;tag=a6c85cf
From: Alice <sip:alice@example.com>;tag=1928301774
Call-ID: a84b4c76e66710@alice-pc.example.com
CSeq: 314159 INVITE
Contact: <sip:bob@bob-pc.example.com>
Content-Type: application/sdp
Content-Length: 131
Dòng đầu (Status-Line) gồm: phiên bản, status code (200), reason phrase (OK).
2.3 Header bắt buộc — phải hiểu từng cái, không học vẹt
| Header | Ý nghĩa | Vì sao bắt buộc |
|---|---|---|
| Via | Ghi lại đường đi của request qua từng proxy — mỗi proxy chèn thêm một dòng Via | Response phải đi ngược đúng đường request đã đi qua; thiếu Via, response không biết đường về |
| From | Ai gửi request (có tag — định danh phía gửi trong dialog) | Kết hợp với To-tag và Call-ID để định danh dialog |
| To | Ai nhận request (chưa có tag ở request đầu, có tag từ response trở đi) | Cùng vai trò với From trong việc định danh dialog |
| Call-ID | Định danh duy nhất cho toàn bộ cuộc gọi/đăng ký | Tất cả bản tin cùng một dialog phải có cùng Call-ID |
| CSeq | Số thứ tự + method — tăng dần cho mỗi request mới trong cùng dialog | Phân biệt request nào là mới, request nào là retransmit |
| Contact | Địa chỉ trực tiếp để liên lạc lại (bỏ qua các proxy trung gian nếu có thể) | Bắt buộc cho các request dialog-forming (INVITE, REGISTER, SUBSCRIBE…) |
| Max-Forwards | Giới hạn số hop request được phép đi qua (giá trị khởi tạo thường 70) | Chống loop vô hạn giữa các proxy — mỗi proxy giảm 1, về 0 thì huỷ |
| Content-Length | Độ dài body (byte) | Bắt buộc khi dùng TCP để biết message kết thúc ở đâu; qua UDP có thể bỏ qua nhưng nên có |
2.4 Dialog ID — công thức nhớ lòng
Dialog ID = Call-ID + From-tag + To-tag
Một cuộc gọi có hai chiều nhìn dialog ID ngược nhau (UAC nhìn From là mình, UAS nhìn To là mình) nhưng về bản chất chỉ có một dialog. Đây là kiến thức nền để sau này hiểu tại sao OpenSIPS module dialog cần theo dõi đúng 3 giá trị này.
2.5 Message body — SDP sơ lược
SDP (Session Description Protocol, RFC 4566) mô tả: địa chỉ IP/port sẽ nhận media, codec nào được hỗ trợ (payload type), loại media (audio/video). Ví dụ rút gọn:
v=0
o=alice 2890844526 2890844526 IN IP4 alice-pc.example.com
s=-
c=IN IP4 alice-pc.example.com
t=0 0
m=audio 49170 RTP/AVP 0
a=rtpmap:0 PCMU/8000
Dòng m= quan trọng nhất: port 49170, dùng RTP, payload type 0 (PCMU/G.711 μ-law). SDP trao đổi qua cơ chế offer/answer: bên gọi gửi offer trong INVITE (hoặc sau), bên nhận trả answer trong response (hoặc ACK).
3. Transaction và Dialog — điểm khó nhất, phải hiểu chắc
3.1 Transaction là gì
Một transaction là một cặp request-response (hoặc chuỗi response) được xử lý như một đơn vị có trạng thái, tồn tại trong thời gian ngắn (giây), để đảm bảo cơ chế retransmit qua UDP hoạt động đúng. Một dialog (cuộc gọi kéo dài phút) chứa nhiều transaction bên trong.
3.2 Ba loại transaction — đây là chỗ 90% người mới hiểu sai
- INVITE client/server transaction — có state machine riêng, phức tạp hơn vì response 2xx và non-2xx được xử lý khác nhau.
- Non-INVITE transaction (REGISTER, BYE, OPTIONS…) — state machine đơn giản hơn.
- ACK transaction — ACK cho non-2xx đi cùng transaction với INVITE gốc; nhưng ACK cho 2xx là một transaction hoàn toàn riêng, tự nó định tuyến bằng thông tin trong Contact/Route, không qua lại transaction layer của tm module.
Đây chính là lý do vì sao trong OpenSIPS, bạn không thể dùng t_relay() để forward ACK của một 2xx response — ACK 2xx phải được định tuyến “thủ công” theo route set, còn ACK của non-2xx thì tự động do tm xử lý trong cùng transaction.
3.3 State machine transaction INVITE (client side, rút gọn)
INVITE gửi đi
|
v
[Calling] --- nhận 1xx ---> [Proceeding] --- nhận 2xx ---> [Terminated]
| |
nhận 2xx nhận 3xx-6xx
| |
v v
[Terminated] gửi ACK --> [Completed] --> (chờ timeout) --> [Terminated]
Ghi nhớ: nếu response là 2xx, transaction layer coi như xong ngay, ACK đi ra ngoài transaction (do UAC tự gửi lại, không qua lại tm). Nếu response là non-2xx (3xx-6xx), transaction layer tự động sinh ACK và chuyển sang trạng thái Completed chờ retransmit hết hạn.
3.4 Dialog — early vs confirmed
- Early dialog: được tạo khi nhận response tạm thời có tag (ví dụ 180 Ringing có To-tag) — dialog tồn tại nhưng chưa xác nhận.
- Confirmed dialog: được tạo/xác nhận khi nhận 200 OK — từ đây dialog chính thức tồn tại cho tới khi có BYE.
Không phải response nào cũng tạo dialog — chỉ response có To-tag mới “dialog-forming”.
4. Toàn bộ SIP Method thông dụng — giải thích sâu từng cái
REGISTER
Đăng ký vị trí hiện tại (Contact — thường là IP:port sau NAT) gắn với một AOR (Address of Record, ví dụ sip:alice@example.com). Không dialog-forming — không tạo dialog, chỉ là transaction đơn lẻ, lặp lại định kỳ (thường mỗi 3600s hoặc theo Expires header) để giữ đăng ký còn hiệu lực.
Flow điển hình có auth:
UE Registrar
|----REGISTER (không auth)------->|
|<---401 Unauthorized (nonce)-----|
|----REGISTER (có Authorization)->|
|<---200 OK (Contact, Expires)----|
INVITE
Khởi tạo một dialog + một session media. Là request phức tạp nhất vì có thể sinh ra nhiều response tạm thời (100, 180, 183) trước response cuối (final response).
ACK
Xác nhận đã nhận final response của INVITE. Không có response cho ACK. Như đã nói ở mục 3.3, ACK cho 2xx và ACK cho non-2xx đi hai đường khác nhau về mặt xử lý transaction.
BYE
Kết thúc một dialog đã confirmed (đã có 200 OK trước đó). Là request trong-dialog, đi theo route set đã thiết lập từ Record-Route lúc INVITE.
CANCEL
Huỷ một INVITE đang trong trạng thái chờ (chưa có final response) — ví dụ máy đang đổ chuông, người gọi cúp máy giữa chừng. CANCEL không hoạt động được nữa sau khi đã nhận final response (lúc đó phải dùng BYE thay vì CANCEL).
OPTIONS
Truy vấn khả năng (capabilities) của một UA hoặc dùng làm keepalive/health-check giữa các server. Không nhất thiết dialog-forming, thường được trả lời trực tiếp mà không cần forward tới đích thật.
INFO
Gửi thông tin giữa hai bên trong một dialog đã thiết lập (ví dụ truyền tín hiệu DTMF kiểu cũ trước khi RFC 4733 dùng RTP payload phổ biến hơn). Không làm thay đổi trạng thái session.
UPDATE
Cho phép sửa session (đổi SDP) trước khi dialog nhận final response — dùng trong các luồng có early media/PRACK phức tạp.
PRACK
“Provisional ACK” — xác nhận đã nhận một response tạm thời reliable (dùng khi có extension 100rel, header Require: 100rel). Không phải response tạm thời nào cũng cần PRACK, chỉ khi hai bên đồng ý dùng 100rel.
REFER
Yêu cầu bên nhận thực hiện một hành động khác (thường là gọi tới một địa chỉ mới) — nền tảng của call transfer. Bên nhận REFER sẽ tạo một dialog/subscription mới để báo cáo kết quả (qua NOTIFY) cho bên gửi REFER. Đây chính là method khiến proxy thuần không đủ — cần B2BUA để thật sự điều khiển được luồng transfer.
SUBSCRIBE / NOTIFY
Cặp method cho cơ chế event notification (RFC 6665) — dùng cho presence (trạng thái online/busy), BLF (Busy Lamp Field trên máy bàn), hay bất kỳ “sự kiện” nào một bên muốn theo dõi. SUBSCRIBE thiết lập theo dõi, NOTIFY gửi cập nhật định kỳ hoặc khi có thay đổi.
MESSAGE
Gửi tin nhắn tức thời (instant message) trong một request đơn lẻ, không cần thiết lập dialog trước — khác hẳn cách các ứng dụng chat hiện đại hoạt động (vốn dùng kênh riêng), MESSAGE gửi thẳng nội dung trong body của chính SIP request.
PUBLISH
Đẩy trạng thái (thường là presence) lên server — khác NOTIFY ở chỗ PUBLISH là hành động chủ động “tôi đang ở trạng thái X” thay vì “tôi muốn biết trạng thái của ai đó”.
5. Response code — đầy đủ theo nhóm
| Nhóm | Ý nghĩa chung | Mã hay gặp | Giải thích |
|---|---|---|---|
| 1xx | Provisional — đang xử lý, chưa xong | 100 Trying | Server đã nhận request, đang xử lý |
| 180 Ringing | Đang đổ chuông ở đầu bên kia | ||
| 183 Session Progress | Có early media (ví dụ nhạc chờ) trước khi trả lời hẳn | ||
| 2xx | Thành công | 200 OK | Request được chấp nhận và xử lý thành công |
| 3xx | Chuyển hướng | 302 Moved Temporarily | Đích đã đổi, thử địa chỉ mới trong Contact |
| 4xx | Lỗi phía client (request) | 400 Bad Request | Bản tin sai cú pháp |
| 401 Unauthorized | Cần xác thực (dùng cho REGISTER, do UAS gửi) | ||
| 403 Forbidden | Bị từ chối, không được phép | ||
| 404 Not Found | Không tìm thấy user/đích | ||
| 407 Proxy Authentication Required | Cần xác thực với proxy (khác 401 — dùng khi proxy giữa đường yêu cầu, không phải đích cuối) | ||
| 486 Busy Here | Máy bận | ||
| 487 Request Terminated | Request đã bị huỷ (thường do CANCEL) | ||
| 5xx | Lỗi phía server | 500 Server Internal Error | Lỗi nội bộ server |
| 503 Service Unavailable | Server quá tải hoặc bảo trì | ||
| 6xx | Lỗi toàn cục | 600 Busy Everywhere | Bận ở mọi thiết bị đã đăng ký |
| 603 Decline | Từ chối cuộc gọi có chủ đích |
6. Call flow mẫu — vẽ lại bằng tay trước khi đọc tiếp
Cuộc gọi thành công:
Alice Proxy Bob
|---INVITE-------------->| |
|<--100 Trying------------| |
| |----INVITE---------------->|
| |<---180 Ringing------------|
|<--180 Ringing-----------| |
| |<---200 OK------------------|
|<--200 OK-----------------| |
|---ACK------------------------------------------------>| (đi trực tiếp nếu có Contact, không qua proxy nếu proxy không record-route)
|<==========RTP media hai chiều=========================>|
|---BYE-------------------------------------------------->|
|<--200 OK------------------------------------------------|
Cuộc gọi bị từ chối (busy):
Alice Proxy Bob
|---INVITE-------------->|----INVITE---------------->|
|<--100 Trying------------| |
|<--486 Busy Here---------|<---486 Busy Here----------|
|---ACK------------------->|
Đăng ký thất bại do sai mật khẩu:
UE Registrar
|----REGISTER--------------------->|
|<---401 Unauthorized (nonce)------|
|----REGISTER (auth sai)---------->|
|<---403 Forbidden hoặc 401 lại----|
7. Các trường P- (P-Header) trong cuộc gọi VoIP trên 4G/5G — IMS/VoLTE
Đây là phần khác biệt quan trọng: RFC 3261 gốc không có các header này — chúng là private extension header do 3GPP định nghĩa riêng (chuẩn hoá qua RFC 3455/3325/7315 và chi tiết hoá trong 3GPP TS 24.229) để SIP hoạt động được trong môi trường mạng di động (IMS — IP Multimedia Subsystem, nền tảng của VoLTE/VoNR). Không nắm phần này thì không thể làm việc với core network thật của nhà mạng.
| Header | Chuẩn | Ai chèn | Chức năng | Bắt buộc? |
|---|---|---|---|---|
| P-Access-Network-Info (PANI) | RFC 7315 / TS 24.229 §7.2A.4 | UE (thiết bị đầu cuối) | Báo cho mạng biết UE đang truy cập qua công nghệ gì (ví dụ 3GPP-E-UTRAN-FDD, cell ID) — dùng cho tính cước theo vị trí, chọn codec, chính sách QoS | Bắt buộc — UE phải chèn vào mọi request khởi tạo dialog, request trong-dialog (trừ CANCEL), và response (trừ response của CANCEL); chèn vào ACK là tuỳ chọn |
| P-Asserted-Identity (PAI) | RFC 3325 / RFC 7315 | Mạng (P-CSCF/S-CSCF), không phải UE | Danh tính đã được mạng xác thực của người gửi — đây là danh tính “thật” mà mạng tin tưởng, dùng cho hiển thị số gọi đến, tính cước, chống giả mạo caller ID | Bắt buộc do mạng chèn trong network domain tin cậy; UE không tự chèn được |
| P-Preferred-Identity | RFC 3325 | UE | UE “đề nghị” mạng dùng danh tính nào (khi UE có nhiều public identity) — mạng có quyền chấp nhận hoặc thay bằng danh tính khác khi tạo P-Asserted-Identity | Tuỳ chọn — chỉ cần khi UE có nhiều identity và muốn chỉ định |
| P-Called-Party-ID | RFC 3455 | Mạng (S-CSCF) | Cho biết chính xác địa chỉ nào trong số nhiều địa chỉ công khai (shared public identity) của UE bị gọi tới — quan trọng khi một UE đăng ký nhiều số | Bắt buộc khi UE có nhiều public identity dùng chung một Contact |
| P-Visited-Network-ID | RFC 3455 | UE (dựa trên cấu hình mạng khách khi roaming) | Cho biết UE đang roaming ở mạng nào — dùng để home network áp policy roaming đúng | Bắt buộc trong kịch bản roaming, không cần khi ở mạng nhà |
| P-Charging-Vector | RFC 3455 | Mạng (P-CSCF khởi tạo, các node sau giữ nguyên) | Chứa ICID (IMS Charging Identifier) — định danh duy nhất để ghép các bản ghi tính cước (CDR) từ nhiều node khác nhau về cùng một phiên | Bắt buộc trong toàn bộ mạng lõi IMS — không có thì không thể đối soát cước |
| P-Charging-Function-Addresses | RFC 3455 | Mạng (P-CSCF) | Địa chỉ của CCF (offline charging) và ECF (online charging) — cho các node biết gửi bản ghi cước đi đâu | Bắt buộc trong network domain, không xuất hiện phía UE |
| P-Associated-URI | RFC 3455 | Mạng (S-CSCF), trả về trong 200 OK của REGISTER | Danh sách tất cả các public identity khác cùng gắn với một private identity — cho UE biết “bạn còn những số nào khác” | Trả về khi có nhiều associated identity, không phải lúc nào cũng có |
| P-Served-User | RFC 5502 | Mạng (S-CSCF) | Cho các Application Server biết đang phục vụ dịch vụ cho user nào, ở chiều nào (originating/terminating) — quan trọng khi có nhiều dịch vụ giá trị gia tăng (voicemail, call forwarding…) chạy tuần tự | Bắt buộc khi dialog đi qua Application Server thực hiện logic dịch vụ |
| P-Early-Media | RFC 5009 | Cả UE và mạng | Xác nhận rõ ràng ai được phép gửi/nhận early media (âm thanh trước khi trả lời hẳn, ví dụ nhạc chờ) — tránh nhầm lẫn khi có nhiều nguồn early media (forking) | Tuỳ chọn, dùng khi có khả năng mơ hồ về early media |
| P-Media-Authorization | RFC 3313 (cũ) | Mạng | Token uỷ quyền cho tầng media dùng để xin cấp tài nguyên QoS ở lớp mạng | Legacy — ít dùng trong triển khai IMS/VoLTE hiện đại, hầu hết thay bằng cơ chế PCC (Policy and Charging Control) riêng |
| Privacy (không có tiền tố P- nhưng luôn đi cùng nhóm này) | RFC 3323 | UE | Yêu cầu ẩn danh tính — ví dụ Privacy: id yêu cầu mạng không lộ P-Asserted-Identity ra bên ngoài network domain tin cậy | Tuỳ chọn, do người dùng chủ động bật (ví dụ bấm *67 kiểu VoLTE) |
Ghi nhớ cốt lõi để không nhầm lẫn:
- Header nào UE chèn (PANI, P-Preferred-Identity, P-Visited-Network-ID, Privacy) phản ánh thông tin/đề nghị từ thiết bị.
- Header nào mạng chèn (P-Asserted-Identity, P-Called-Party-ID, P-Charging-Vector, P-Charging-Function-Addresses, P-Associated-URI, P-Served-User) phản ánh quyết định/xác thực của core network — UE không có quyền tự đặt các giá trị này, vì chúng là nền tảng của việc tính cước và chống giả mạo.
- Nếu sau này bạn làm việc với SBC/P-CSCF thật (ví dụ tích hợp OpenSIPS làm P-CSCF/I-CSCF trong lab IMS), đây chính là nhóm header bạn sẽ phải đọc/ghi/kiểm tra thường xuyên nhất — không phải các header RFC 3261 gốc.
8. Bài tập thực hành Wireshark — làm theo đúng thứ tự
- Cài Wireshark, cài 2 softphone (Zoiper hoặc Linphone) trỏ vào một OpenSIPS default config (hoặc bất kỳ SIP server thử nghiệm nào).
- Bắt gói trên interface đang dùng, lọc bằng
sip. - Thực hiện lần lượt và quan sát:
- Đăng ký (REGISTER) — thử sai mật khẩu trước để thấy 401, rồi đăng ký đúng để thấy 200 OK.
- Gọi thành công — quan sát đủ INVITE → 100 → 180 → 200 → ACK → BYE → 200.
- Gọi rồi huỷ giữa chừng (CANCEL) trước khi bên kia bắt máy.
- Gọi vào máy đang bận (mở sẵn một cuộc gọi khác) để thấy 486.
- Với mỗi luồng, dùng Wireshark > Telephony > VoIP Calls để xem biểu đồ flow tự động — so sánh với sơ đồ bạn tự vẽ tay ở mục 6.
- Tự kiểm tra: che sơ đồ ở mục 6 lại, vẽ lại từ trí nhớ dựa trên packet capture thật của chính bạn.
9. Tự kiểm tra trước khi qua Giai đoạn 1
Bạn đã sẵn sàng nếu trả lời được không cần tra tài liệu:
- Tại sao ACK cho 2xx lại là transaction riêng, không đi cùng transaction INVITE?
- Dialog ID gồm những thành phần nào, vì sao cần cả ba?
- REFER khác gì so với việc UAC tự gửi một INVITE mới?
- Vì sao P-Asserted-Identity không thể do UE tự chèn mà phải do mạng xác thực?
- P-Access-Network-Info bắt buộc trong trường hợp nào, tuỳ chọn trong trường hợp nào?
Nếu còn vướng ở bất kỳ câu nào trong 5 câu trên, quay lại đọc phần tương ứng trước khi bắt đầu Giai đoạn 1 — nền không chắc thì phần cấu hình OpenSIPS phía sau sẽ chỉ là “làm theo mẫu” chứ không phải hiểu thật.