- Why Kamailio?
- Kamailio SIP Signaling Proxy
- System Architecture
- TLS module Configuration Guide
- Websocket module
- RTPengine
- JSSIP
- SIP over WEBSOCKET messages and Kamailio processing
- References and Resources
Kamailio is an open-source SIP (Session Initiation Protocol) server that provides advanced routing, protocol support, and media handling capabilities. This article demonstrates how to configure Kamailio with RTP Engine to enable WebRTC-to-SIP interoperability, allowing web-based real-time communication clients to seamlessly connect with traditional SIP User Agents (UAs).
The purpose of this article is to demo the process of using Kamailio + RTP Engine to enable SIP-based WebRTC call to a traditional SIP UA like Xlite. Kamailio Will thus provide not only call routing but also NATing, TLS and WebSocket support for webrtc endpoints. For this bridging of SRTP from WebRTC endpoint like JSSIP to RTP for SIP UA like Xlite, we will use the RTP engine.
- Protocol Bridging: Convert between WebRTC’s secure SRTP (Secure RTP) and traditional SIP’s RTP
- NAT Traversal: Provide address/port translation for endpoints behind firewalls/NATs
- Security: Enable TLS/WSS encryption for WebRTC endpoints
- Cross-Protocol Communication: Allow WebRTC clients (e.g., JSSIP) to communicate with legacy SIP endpoints (e.g., X-Lite, Linphone)
Why Kamailio?
Kamailio (formerly OpenSER) is widely adopted in carrier-grade VoIP infrastructure because it:
- Scales horizontally to handle millions of concurrent sessions
- Provides modular architecture with 100+ loadable modules
- Supports advanced SIP features (Presence, SUBSCRIBE/NOTIFY, GRUU, etc.)
- Offers excellent NAT traversal and media relay capabilities
- Has active community support and comprehensive documentation
Official Website: kamailio.org
Kamailio SIP Signaling Proxy
Kamailio functions as the central SIP routing engine, handling:
- User registration and location management
- SIP message routing and forwarding
- NAT detection and traversal
- Session state management
- Media negotiation
Essential modules for WebRTC support:
loadmodule "tm.so" # Transaction Manager - handles request/response matching
loadmodule "sl.so" # Stateless Reply - quick responses
loadmodule "rr.so" # Record-Route - tracks SIP hop path
loadmodule "pv.so" # Pseudo-Variables - $ru, $Rp, $si, etc.
loadmodule "maxfwd.so" # Max-Forwards header processing
loadmodule "usrloc.so" # User Location - registration database
loadmodule "textops.so" # Text operations on message bodies
loadmodule "siputils.so" # SIP utility functions
loadmodule "xlog.so" # Logging module
loadmodule "sanity.so" # SIP message sanity validation
loadmodule "ctl.so" # Control socket for management
loadmodule "kex.so" # Kamailio extensions
loadmodule "corex.so" # Core extensions
loadmodule "tls.so" # TLS/SSL support for secure connections
loadmodule "xhttp.so" # HTTP request handler
loadmodule "websocket.so" # WebSocket transport (RFC 7118)
loadmodule "nathelper.so" # NAT detection and manipulation
A complete working configuration is available at: kamailioexamples/webrtc_to_webrtc_ws
System Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Web Browser │
│ (JsSIP WebRTC Client) │
│ getUserMedia, RTCPeerConnection │
└────────────────────────────┬────────────────────────────────────┘
│
WSS (WebSocket Secure)
TLS 1.2+ Encrypted
│
┌────────────────────────────▼────────────────────────────────────┐
│ │
│ KAMAILIO SIP SERVER │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Modules: TLS, WebSocket, NATHelper, Registrar, Routing │ │
│ │ Listens: WSS:443 (WebSocket Secure) │ │
│ │ TCP:5060 (SIP) │ │
│ │ Functions: Registration, Call Routing, NAT Detection │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└────────────────────┬─────────────────────────┬──────────────────┘
│ │
│ │ TCP/UDP
│ SIP Signaling
│ │
UDP:2222 (NG Control Protocol) │
UDP/TCP (RTP Media) │
│ │
┌────────────────────▼──────────────────┐ ┌──▼──────────────────────┐
│ │ │ │
│ RTP ENGINE (Media Relay) │ │ Legacy SIP Endpoint │
│ • Codec Transcoding (OPUS→PCMU, etc) │ │ (X-Lite, Polycom, │
│ • SRTP↔RTP Conversion │ │ Cisco, Linphone, etc) │
│ • NAT Traversal │ │ │
│ • ICE Handling │ │ Traditional Phone PBX │
│ │ │ │
└───────────────────────────────────────┘ └─────────────────────────┘
Media Flow Example:
- Media streams: Browser → RTP Engine (SRTP/Opus) → SIP Phone (RTP/PCMU)
- Browser user initiates call with Opus codec (48kHz)
- Kamailio routes INVITE to RTP Engine
- RTP Engine negotiates codec with legacy endpoint (accepts PCMU/G.711)
Kamailio’s modules on WebSocket, TLS, NATHelper help it to support WebSocket based SIP which the default Kamailio configuration doesn’t. Snippets from Kamailio config to support webrtc endpoints is below. A full kamailio config is present here https://github.com/altanai/kamailioexamples/blob/master/webrtc_to_webrtc_ws/websocket_tls_webrtc_kamailio.cfg
Configuration of the important modules
TLS module Configuration Guide
WebRTC endpoints require secure connections (WSS – WebSocket Secure), necessitating proper SSL/TLS certificates and keys.
Step 1: Create Certificate Directory Structure
mkdir -p certs/{private,newcerts}
touch certs/index.txt
echo 01 > certs/serial
echo 01 > certs/crlnumber
ls -la certs/
# Output:
# -rw-r--r-- 1 root root 0 Dec 29 10:00 index.txt
# -rw-r--r-- 1 root root 3 Dec 29 10:00 serial
# -rw-r--r-- 1 root root 3 Dec 29 10:00 crlnumber
# drwxr-xr-x 2 root root 4096 Dec 29 10:00 newcerts/
# drwx------ 2 root root 4096 Dec 29 10:00 private/
Step 2: Generate Certificate Authority (CA)
Create CA private key (2048-bit RSA):
openssl genrsa -out certs/private/cakey.pem 2048
chmod 600 certs/private/cakey.pem
Output:
Generating RSA private key, 2048 bit long modulus (2 primes)
.................................................................................+++++
........................................+++++
e is 65537 (0x010001)
Create self-signed CA certificate (valid for 365 days):
openssl req -out certs/cacert.pem -x509 -new -key certs/private/cakey.pem \
-days 365 -subj "/C=US/ST=California/L=San Francisco/O=MyCompany/CN=kamailio-ca"
Verify CA certificate:
openssl x509 -in certs/cacert.pem -text -noout | head -20
Step 3: Generate Server Certificate
Create Certificate Signing Request (CSR):
openssl req -out kamailio1_cert_req.pem -new -nodes \
-keyout privkey.pem \
-subj "/C=US/ST=California/L=San Francisco/O=MyCompany/CN=kamailio.example.com"
Sign with CA:
openssl ca -in kamailio1_cert_req.pem -out kamailio1_cert.pem \
-config /etc/ssl/openssl.cnf -days 365
Interactive output:
Using configuration from /etc/ssl/openssl.cnf
Check that the request matched the signing rules
Signature ok
Certificate Details:
Serial Number: 1 (0x1)
Not Before: Dec 29 10:00:00 2024 GMT
Not After : Dec 29 10:00:00 2025 GMT
...
Sign the certificate? [y/n]: y
1 out of 1 certificate requests certified, commit? [y/n]: y
Write out database with 1 new entries
Data Base Updated
Step 4: Install Certificates
# Create system certificate directory
sudo mkdir -p /etc/pki/CA/
sudo mkdir -p /etc/pki/tls/private
# Copy certificates
sudo cp kamailio1_cert.pem /etc/pki/CA/
sudo cp privkey.pem /etc/pki/CA/
sudo cp certs/cacert.pem /etc/pki/CA/
# Set proper permissions
sudo chmod 644 /etc/pki/CA/kamailio1_cert.pem
sudo chmod 644 /etc/pki/CA/cacert.pem
sudo chmod 600 /etc/pki/CA/privkey.pem
# Verify installation
ls -la /etc/pki/CA/
# -rw-r--r-- 1 root root 1200 Dec 29 10:00 cacert.pem
# -rw-r--r-- 1 root root 1456 Dec 29 10:00 kamailio1_cert.pem
# -r-------- 1 root root 1708 Dec 29 10:00 privkey.pem
Step 5: Create CA Certificate List
For client certificate verification:
# Compile system CA certificates
cat /etc/ssl/certs/ca-certificates.crt > /etc/pki/CA/calist.pem
# Add your custom CA
cat certs/cacert.pem >> /etc/pki/CA/calist.pem
# Verify
openssl crl2pkcs7 -nocrl -certfile /etc/pki/CA/calist.pem | \
openssl pkcs7 -print_certs -noout | grep Subject
Step 6: Configure TLS in kamailio.cfg
#!ifdef WITH_TLS
enable_tls=1
#!endif
# TLS module parameters
modparam("tls", "tls_method", "TLSv1_2+")
modparam("tls", "certificate", "/etc/pki/CA/kamailio1_cert.pem")
modparam("tls", "private_key", "/etc/pki/CA/privkey.pem")
modparam("tls", "ca_list", "/etc/pki/CA/calist.pem")
# Security options
modparam("tls", "verify_certificate", 0)
modparam("tls", "require_certificate", 0)
modparam("tls", "cipher_list", "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK")
# SSL/TLS session cache
modparam("tls", "session_cache", 1)
modparam("tls", "session_id", "kamailio-webrtc")
Testing TLS Configuration
# Test certificate validity
openssl s_client -connect localhost:443 -CAfile certs/cacert.pem
# Expected output:
# Verification: OK
# Verify return code: 0 (ok)
# Check certificate details
echo | openssl s_client -connect localhost:443 2>/dev/null | \
openssl x509 -noout -dates
Websocket module
WebSocket (RFC 6455) enables full-duplex communication over HTTP/HTTPS, ideal for browsers. In Kamailio, WebSocket is treated as a transport protocol like TCP/UDP. WebSocket is considered a transport option just as TCP or UDP in Kamailio config , hence just as one defines IP addr and ports for TCP, UDP protocol , we need to define the same for WS or WSS
Configuration
Define WebSocket address constants:
#!substdef "!MY_IP_ADDR!192.168.1.100!g"
#!substdef "!MY_WS_PORT!80!g"
#!substdef "!MY_WSS_PORT!443!g"
#!substdef "!MY_WS_ADDR!tcp:MY_IP_ADDR:MY_WS_PORT!g"
#!substdef "!MY_WSS_ADDR!tls:MY_IP_ADDR:MY_WSS_PORT!g"
# Listen on all available addresses
listen=MY_IP_ADDR
#!ifdef WITH_WEBSOCKETS
listen=MY_WS_ADDR # ws://192.168.1.100:80
#!ifdef WITH_TLS
listen=MY_WSS_ADDR # wss://192.168.1.100:443
#!endif
#!endif
#!substdef "!MY_WS_ADDR!tcp:MY_IP_ADDR:MY_WS_PORT!g"
#!substdef "!MY_WSS_ADDR!tls:MY_IP_ADDR:MY_WSS_PORT!g"
...
listen=MY_IP_ADDR
#!ifdef WITH_WEBSOCKETS
listen=MY_WS_ADDR
#!ifdef WITH_TLS
listen=MY_WSS_ADDR
#!endif
#!endif
Port validation (reject non-WebSocket traffic on WS ports):
check if port in R-URI meant for ws or wss, did not receive websocket or secure websocket
if (($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT) && !(proto == WS || proto == WSS)) {
xlog("L_WARN", "SIP request received on $Rp\n");
sl_send_reply("403", "Forbidden");
exit;
}
request_route for websocket , included checking is client is behind NAT using nat_uac_test methods from NAThelper. If it is then for REGISTER methods do fix_nated_register and for other add_contact_alias .
NAT Detection and Handling
WebSocket clients typically operate behind NATs and firewalls. Kamailio uses nat_uac_test() to detect this:
NAT detection flags:
1: Check Via/Source IP mismatch2: Check Via/Source port mismatch4: Check Received header presence8: Check proto!=udp64: Check Contact header against source IP:port (most comprehensive)
WebSocket NAT handling implementation:
#!ifdef WITH_WEBSOCKETS
if (nat_uac_test(64)) {
# Force symmetric RTP using source address/port
force_rport();
force_local_rport();
if (is_method("REGISTER")) {
# Fix Contact header for REGISTER messages
fix_nated_register();
xlog("L_INFO", "Fixed NAT registration for $fu\n");
} else {
# Add contact alias for INVITE/MESSAGE/etc.
if (!add_contact_alias()) {
xlog("L_ERR", "Error aliasing contact <$ct> from $si:$sp\n");
sl_send_reply("400", "Bad Request");
exit;
}
xlog("L_INFO", "Added contact alias from $si:$sp\n");
}
}
#!endif
How contact aliasing works:
Original Contact in REGISTER:
Contact: <sip:john@192.168.1.50:5060>;expires=3600
Received from public IP 203.0.113.10:19035
Kamailio modifies it:
Contact: <sip:john@203.0.113.10:19035>;alias=203.0.113.10~19035~6;expires=3600
^────────────────────────────^
Public IP:Port for routing
Then when sending messages back to John:
Route via: sip:john@203.0.113.10:19035 (public address)
handle_ruri_alias() translates back to 192.168.1.50:5060 (private address)
Complete WebSocket routing block:
request_route {
# 1. Port validation
if (($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT) && !(proto == WS || proto == WSS)) {
xlog("L_WARN", "Non-WS message on WS port\n");
sl_send_reply("403", "WebSocket Required");
exit;
}
# 2. NAT detection and handling
#!ifdef WITH_WEBSOCKETS
if (nat_uac_test(64)) {
force_rport();
force_local_rport();
if (is_method("REGISTER")) {
fix_nated_register();
} else {
if (!add_contact_alias()) {
xlog("L_ERR", "Bad contact alias\n");
sl_send_reply("400", "Bad Request");
exit;
}
}
}
#!endif
# 3. Route to appropriate handler
if (is_method("REGISTER")) {
route(REGISTRAR);
} else {
route(INVOKE);
}
}
RTPengine
RTP relay and NAT helps with RTP packets. For detailed steps goto https://telecom.altanai.com/2018/04/03/rtp-engine-on-kamailio-sip-server/
RTP Engine is critical for WebRTC-to-SIP bridging because it:
- Converts SRTP to RTP: WebRTC uses Secure RTP; legacy phones use plaintext RTP
- Transcodes Codecs: Bridges Opus (WebRTC) ↔ PCMU/G.711 (SIP phones)
- Handles ICE Candidates: Manages WebRTC’s ICE protocol for NAT traversal
- Provides Media Relay: Acts as SDP answer/offer intermediary
- Records Calls: Saves media streams for compliance/audit
Installation
Install dependencies (Ubuntu/Debian):
sudo apt-get update
sudo apt-get remove rtpproxy -y # Remove if conflicts
sudo apt-get install -y \
debhelper iptables-dev \
libcurl4-openssl-dev libglib2.0-dev \
libxmlrpc-core-c3-dev libhiredis-dev \
markdown build-essential git \
libpcre3-dev libssl-dev
# Optional: for transcoding support
sudo apt-get install -y libavformat-dev libavcodec-dev libavdevice-dev
Build from source:
git clone https://github.com/sipwise/rtpengine.git
cd rtpengine
# Check if no_ngcp flavor is available
ls -la debian/flavors/
# Build
./debian/flavors/no_ngcp
# This creates debs in parent directory
cd ..
ls -la ngcp-rtpengine*.deb
# Install
sudo dpkg -i ngcp-rtpengine-daemon_*.deb
sudo dpkg -i ngcp-rtpengine-ctl_*.deb
sudo dpkg -i ngcp-rtpengine_*.deb
Running RTP Engine
Basic startup:
rtpengine --interface=192.168.1.100 \
--listen-ng=127.0.0.1:2223 \
--listen-cli=127.0.0.1:2224 \
--foreground --log-stderr
Advanced configuration with NG (Next Generation) protocol:
rtpengine \
--interface=192.168.1.100:203.0.113.100 \
--listen-ng=127.0.0.1:2223 \
--listen-cli=127.0.0.1:2224 \
--listen-udp=2222 \
--listen-tcp=25060 \
--max-sessions=10000 \
--max-load=800 \
--timeout=60 \
--silent-timeout=900 \
--foreground --log-stderr --log-level=5
Parameters explained:
--interface: IP to use for RTP media (can have public:private pair)--listen-ng: Control protocol port (localhost port 2223)--listen-cli: CLI management port (localhost port 2224)--max-sessions: Maximum concurrent RTP sessions--max-load: CPU load threshold before rejecting new sessions--timeout: Timeout for idle sessions (seconds)--silent-timeout: Timeout when no media detected (seconds)
As systemd service (/etc/systemd/system/rtpengine.service):
[Unit]
Description=RTP Engine
After=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/rtpengine \
--interface=192.168.1.100 \
--listen-ng=127.0.0.1:2223 \
--listen-cli=127.0.0.1:2224 \
--max-sessions=5000 \
--foreground --log-stderr
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Kamailio RTP Engine Integration
Load module:
loadmodule "rtpengine.so"
Configure module parameters:
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
modparam("rtpengine", "rtpengine_allow_op", 1)
modparam("rtpengine", "log_level", 5)
modparam("nathelper", "received_avp", "$avp(s:rcv)")
Implement media relay in routing logic:
route[RELAY] {
# Enable RTP relaying with options
# trust-address: trust INVITE source over From header
# replace-origin: fix SDP o= line with media source
# replace-session-connection: fix c= line
# ICE=force: force ICE for all calls
if (!rtpengine_manage("trust-address replace-origin replace-session-connection ICE=force")) {
xlog("L_ERR", "RTP Engine management failed\n");
sl_send_reply("488", "Not Acceptable Here");
exit;
}
if (!t_relay()) {
xlog("L_ERR", "Relay failed\n");
sl_send_reply("500", "Server Internal Error");
}
exit;
}
Common RTP Engine flags:
| Flag | Purpose |
|---|---|
trust-address | Use INVITE source instead of From header |
replace-origin | Fix SDP origin (o=) line |
replace-session-connection | Fix SDP connection (c=) line |
symmetric | Use symmetric RTP (outgoing = incoming addresses) |
ICE=force | Force ICE for all calls |
ICE=remove | Remove ICE from all calls |
DTLS=offer | Offer DTLS if peer supports |
DTLS=require | Require DTLS |
Example: WebRTC-to-SIP bridging:
# For WebRTC→SIP calls (RTC client calling legacy phone)
rtpengine_manage("trust-address replace-origin replace-session-connection");
# For SIP→WebRTC calls (legacy phone calling RTC client)
rtpengine_manage("trust-address replace-origin replace-session-connection ICE=force");
Testing RTP Engine
# Check if RTP Engine is responding
echo -n "ping\n" | nc -u 127.0.0.1 2223
# Expected: "pong\n"
# View RTP Engine statistics
rtpengine-ctl status
# View active sessions
rtpengine-ctl list
# Clear all sessions
rtpengine-ctl purge
JSSIP
Like most other WebRTC libraries , JSSIP is event driven and provides provide core WebRTC API like getUserMedia and RTP PeerConnection providing STUN,ICE,DTLS, SRTP features. It also integrated with rtcninja to provide cross browser accessibility. The differentiators with JSSIP lies in the fact that it supports SIP stack over WebSocket
JSSIP WebRTC client for Kamailio
For the JSSIP default cllient UI and library one can use CDN based https://cdnjs.cloudflare.com/ajax/libs/jssip/3.1.2/jssip.min.js or can take a pull from JSSIP repo and build oneself using gulp https://github.com/versatica/JsSIP.
Instantiate JSSIP websocket interface with Kamailio IP
var socket = new JsSIP.WebSocketInterface('wss://<kamailio_ip>:443');
Add configuration for registration . Note if not using Kamailio as proxy to SBC, it is recommended to add registration features to provide user reachability for incoming calls and NAT pings
var configuration = {
sockets : socket,
uri : 'sip:username@example.com',
password : 'password'
};
create UA and start
var ua = new JsSIP.UA(configuration); ua.start();
SIP over WEBSOCKET messages and Kamailio processing
Registration Flow
JsSIP Client Kamailio
│ │
│─────────────REGISTER────────────────→ │
│ Via: SIP/2.0/WSS client.invalid │
│ From: sip:user@example.com;tag=xxx │
│ To: sip:user@example.com │
│ Contact: sip:user@203.x.x.x:19035 │
│ Expires: 600 │
│ │ [Save location]
│← ────────────200 OK────────────────── │
│ Contact: sip:user@203.x.x.x:19035 │
│ ;received="sip:203.x.x.x..."│
│ ;expires=600 │
│ │
REGISTER sip JSSIP UA with user altanai, domina voiptelcom.com
REGISTER sip:voiptelco.com SIP/2.0 Via: SIP/2.0/WSS 830p2l39g8bg.invalid;branch=z9hG4bK242397 Max-Forwards: 69 To: From: ;tag=3jaad0q8l8 Call-ID: fvn2cd1b9gqh6kd7nqdpj5 CSeq: 1 REGISTER Contact: ;+sip.ice;reg-id=1;+sip.instance="";expires=600 Expires: 600 Allow: INVITE,ACK,CANCEL,BYE,UPDATE,MESSAGE,OPTIONS,REFER,INFO Supported: path,gruu,outbound User-Agent: JsSIP 3.1.2 Content-Length: 0
Processed by Kamailio using REGISTRAR route block
route(REGISTRAR);
..
route[REGISTRAR] {
if (is_method("REGISTER")) {
if (!save("location")) {
sl_reply_error();
}
exit;
}
}
SIP/2.0 200 OK Via: SIP/2.0/WSS 830p2l39g8bg.invalid;branch=z9hG4bK242397;rport=19035;received=x.x.x.x To: ;tag=4dad943d40a0a309c33d64467664aa30.f6d3 From: ;tag=3jaad0q8l8 Call-ID: fvn2cd1b9gqh6kd7nqdpj5 CSeq: 1 REGISTER Contact: ;expires=600;received="sip:x.x.x.x:19035;transport=ws";pub-gruu="sip:altanai@voiptelco.com;gr=urn:uuid:80fa65e7-1cd7-4e40-bbee-c07f7a1ae9a5";temp-gruu="sip:uloc-5d260578-4b58-c-eeac6bd6@voiptelco.com;gr";+sip.instance="";reg-id=1 Server: kamailio (5.2.3 (x86_64/linux)) Content-Length: 0
INVITE (Call Setup) Flow
JsSIP (WebRTC) Kamailio X-Lite (SIP)
│ │ │
│─────────────INVITE─────────────────→│ │
│ + SDP (Opus/SRTP) │ │
│ │ [Pass to RTP Engine] │
│ │ [RTP Relay]│
│ │──────────INVITE──────────────→
│ │ + SDP (PCMU/RTP) │
│ │ │
│← ────────100 Trying──────────────── │← ────────100 Trying─────────│
│ │ [RTP Engine handles] │
│← ────────180 Ringing─────────────── │← ────────180 Ringing────────│
│ (with early media SDP) │ │
│ │ │
│ [User picks up phone] │ │
│← ────────200 OK──────────────────── │← ────────200 OK────────────│
│ + SDP (Answer) │ + SDP (Answer) │
│ [RTP Engine updates connection] │ │
│ │ │
│─────────────ACK───────────────────→ │──────────ACK──────────────→ │
│ │ │
│ [MEDIA FLOWS] │
│ Opus SRTP ←→ RTP Engine ←→ PCMU RTP │
│ │ │
│─────────────BYE───────────────────→ │──────────BYE──────────────→ │
│ │ │
│← ────────200 OK──────────────────── │← ────────200 OK────────────│
INVITE from user1 altanai to john, notice that “To” header doesnt have tag. This will be handy for recognizing whether it is first message of dialog offer and in-dialog message such as ACK , RE-INVITE , BYE etc
INVITE sip:john@voiptelco.com SIP/2.0 Via: SIP/2.0/WSS ipoct61ao12v.invalid;branch=z9hG4bK4220209 Max-Forwards: 69 To: sip:john@voiptelco.com From: sip:altanai@voiptelco.com;tag=2q0lecmbsn Call-ID: s8bnv5869fp68d1ju8c1 CSeq: 1799 INVITE Contact: Content-Type: application/sdp Session-Expires: 90 Allow: INVITE,ACK,CANCEL,BYE,UPDATE,MESSAGE,OPTIONS,REFER,INFO Supported: timer,gruu,ice,replaces,outbound User-Agent: JsSIP 3.1.2 Content-Length: 1823
with SDP containing codecs and ICE details. Supporting audio over UDP/ TLS/RTL /SAVPF . Codecs beings
- 111 OPUS
- 103 ISAC/16000
- 104 ISAC/32000
- 9 G722
- 0 PCMU / G.711u narrowband
- 8 PCMA / G.711
- 106 , 105 , 13 – CN / comfort noise
- 110 , 112 , 113 , 126 – telephone-event / DTMF
v=0 o=- 4779000713447952953 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE 0 a=msid-semantic: WMS SFIXFrpsOUskJ5JQhp1mIARlDk6S3hVFTOBb m=audio 55839 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126 c=IN IP4 192.168.0.3 a=rtcp:9 IN IP4 0.0.0.0 a=candidate:3802297132 1 udp 2122260223 192.168.0.3 55839 typ host generation 0 network-id 1 network-cost 10 a=candidate:2887880668 1 tcp 1518280447 192.168.0.3 9 typ host tcptype active generation 0 network-id 1 network-cost 10 a=ice-ufrag:0PIq a=ice-pwd:i7ccvGPXLDO5JqMwbCUqMcyN a=ice-options:trickle a=fingerprint:sha-256 AB:50:70:E3:57:E3:0C:7B:61:3B:03:5B:0F:54:14:14:9C:49:50:16:07:DC:E7:09:3E:4D:B5:A0:2B:EC:84:A1 a=setup:actpass a=mid:0 a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level a=extmap:2 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id a=sendrecv a=msid:SFIXFrpsOUskJ5JQhp1mIARlDk6S3hVFTOBb e803836d-249a-4b81-b73f-17e0f08dde5a a=rtcp-mux a=rtpmap:111 opus/48000/2 a=rtcp-fb:111 transport-cc a=fmtp:111 minptime=10;useinbandfec=1 a=rtpmap:103 ISAC/16000 a=rtpmap:104 ISAC/32000 a=rtpmap:9 G722/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=rtpmap:106 CN/32000 a=rtpmap:105 CN/16000 a=rtpmap:13 CN/8000 a=rtpmap:110 telephone-event/48000 a=rtpmap:112 telephone-event/32000 a=rtpmap:113 telephone-event/16000 a=rtpmap:126 telephone-event/8000 a=ssrc:2800821831 cname:kLWViBLDLVfCXY8x a=ssrc:2800821831 msid:SFIXFrpsOUskJ5JQhp1mIARlDk6S3hVFTOBb e803836d-249a-4b81-b73f-17e0f08dde5a a=ssrc:2800821831 mslabel:SFIXFrpsOUskJ5JQhp1mIARlDk6S3hVFTOBb a=ssrc:2800821831 label:e803836d-249a-4b81-b73f-17e0f08dde5a
100 trying from callee, note the to and from headers remain same for request or responses. This is send automatically by kamailio for INVITE.
SIP/2.0 100 trying -- your call is important to us Via: SIP/2.0/WSS ipoct61ao12v.invalid;branch=z9hG4bK4220209;rport=17502;received=x.x.x.x To: sip:john@voiptelco.com From: sip:altanai@voiptelco.com;tag=2q0lecmbsn Call-ID: s8bnv5869fp68d1ju8c1 CSeq: 1799 INVITE Server: kamailio (5.2.3 (x86_64/linux)) Content-Length: 0
180 ringing from Callee, note the addition of contact header
SIP/2.0 180 Ringing Record-Route: Via: SIP/2.0/WSS ipoct61ao12v.invalid;rport=17502;received=x.x.x.x;branch=z9hG4bK4220209 To: sip:john@voiptelco.com;tag=pvm73e3t89 From: sip:altanai@voiptelco.com;tag=2q0lecmbsn Call-ID: s8bnv5869fp68d1ju8c1 CSeq: 1799 INVITE Contact: sip:john@voiptelco.com;alias=x.x.x.x~17510~6;gr=urn:uuid:2e560b36-3ea8-41fd-80e3-ede66babb8a7 Supported: timer,gruu,ice,replaces,outbound Content-Length: 0
200 OK with SDP
SIP/2.0 200 OK Record-Route: Via: SIP/2.0/WSS ipoct61ao12v.invalid;rport=17502;received=x.x.x.x;branch=z9hG4bK4220209 To: sip:altanai@voiptelco.com;tag=pvm73e3t89 From: sip:john@voiptelco.com ;tag=2q0lecmbsn Call-ID: s8bnv5869fp68d1ju8c1 CSeq: 1799 INVITE Contact: sip:john@voiptelco.com;alias=x.x.x.x~17510~6;gr=urn:uuid:2e560b36-3ea8-41fd-80e3-ede66babb8a7 Session-Expires: 90;refresher=uas Supported: timer,gruu,ice,replaces,outbound Content-Type: application/sdp Content-Length: 1477
v=0 o=- 4562215268128860297 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE 0 a=msid-semantic: WMS oxU77z9z9RfNL4CayvM1cMJKI0r7u6ZdqLBd m=audio 55380 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126 c=IN IP4 192.168.0.3 a=rtcp:9 IN IP4 0.0.0.0 a=candidate:3802297132 1 udp 2122260223 192.168.0.3 55380 typ host generation 0 network-id 1 network-cost 10 a=ice-ufrag:40h+ a=ice-pwd:6zJo50N7Bb2mqnrHq+jniukk a=ice-options:trickle a=fingerprint:sha-256 8A:F9:BE:8D:8A:80:FF:8C:89:3D:3A:D2:A1:36:B2:EC:11:53:81:7E:F4:53:E7:40:1E:B9:1E:A2:0F:D4:EA:2E a=setup:active a=mid:0 a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level a=extmap:2 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id a=sendrecv a=msid:oxU77z9z9RfNL4CayvM1cMJKI0r7u6ZdqLBd cb6da6b5-d5b8-460e-88bc-1458ebc718e6 a=rtcp-mux a=rtpmap:111 opus/48000/2 a=rtcp-fb:111 transport-cc a=fmtp:111 minptime=10;useinbandfec=1 a=rtpmap:103 ISAC/16000 a=rtpmap:104 ISAC/32000 a=rtpmap:9 G722/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=rtpmap:106 CN/32000 a=rtpmap:105 CN/16000 a=rtpmap:13 CN/8000 a=rtpmap:110 telephone-event/48000 a=rtpmap:112 telephone-event/32000 a=rtpmap:113 telephone-event/16000 a=rtpmap:126 telephone-event/8000 a=ssrc:3489110087 cname:oZ2LjwJD385qPHHH
Kamailio handling replies using reply_route
onreply_route {
if (nat_uac_test(64)) {
add_contact_alias();
}
}
Sending ACK.
ACK sip:john@voiptelco.com;alias=x.x.x.x~17510~6;gr=urn:uuid:2e560b36-3ea8-41fd-80e3-ede66babb8a7 SIP/2.0 Route: Via: SIP/2.0/WSS ipoct61ao12v.invalid;branch=z9hG4bK1876245 Max-Forwards: 69 To: sip:altanai@voiptelco.com ;tag=pvm73e3t89 From: sip:john@voiptelco.com;tag=2q0lecmbsn Call-ID: s8bnv5869fp68d1ju8c1 CSeq: 1799 ACK Allow: INVITE,ACK,CANCEL,BYE,UPDATE,MESSAGE,OPTIONS,REFER,INFO Supported: outbound User-Agent: JsSIP 3.1.2 Content-Length: 0
since ACK is a Within dialog message and sequential request withing a dialog should take the path determined by record-routing, we first check if it has to tag. Having a to tag validates that it is a in-dialog request .
After this validate if is loose_route() and has no destination URI $du , then try to add rui alias using handle_ruri_alias( ), if that fails, reject the request.
If it is not loose_route() and method is ACK then check if the ACK matches a transaction t_check_trans() ie is stateful. If it is then relay otherwise reject.
route(WITHINDLG);
...
route[WITHINDLG] {
if (has_totag()) {
if (loose_route()) {
if ($du == "") {
if (!handle_ruri_alias()) {
xlog("L_ERR", "Bad alias <$ru>\n");
sl_send_reply("400", "Bad Request");
exit;
}
}
route(RELAY);
} else {
if ( is_method("ACK") ) {
if ( t_check_trans() ) {
t_relay();
exit;
} else {
exit;
}
}
sl_send_reply("404", "Not Found");
}
exit;
}
}
Also required to convert ICE packet fromWebRTC to non ICE for Xlite.
References and Resources
Official Documentation
- Kamailio: https://www.kamailio.org/w/documentation
- JsSIP: https://jssip.net/documentation/3.8.0/
- RTP Engine: https://github.com/sipwise/rtpengine/tree/master/doc
- RFC 3261: SIP (Session Initiation Protocol)
- RFC 6455: WebSocket Protocol
- RFC 7118: The WebSocket Protocol as a Transport for the Session Initiation Protocol (SIP)
Example Configurations
- Kamailio Scripts for Webrtc sessions GitHub Examples: https://github.com/altanai/kamailioexamples
Related Projects
- JsSIP WebRTC Examples: https://github.com/versatica/JsSIP/tree/master/demo
- rtcninja: https://github.com/versatica/rtcninja
- WebRTC Samples: https://github.com/webrtc/samples
- Kamailio SIP Server: https://github.com/kamailio/kamailio
WebRTC Clients
- JsSIP: Browser SIP client library jssipwebrtc
- Unified Communicator: Full WebRTC application unifiedCommunicator
- Android SIP/IMS/RCS/WebRTC: Mobile implementation Android-SIP-IMS-RCS-WebRTC
- Linphone Web: Web-based Linphone client
Learning Resources
- Telecom Architecture Blog: https://telecom.altanai.com/
- Kamailio Tutorial: https://www.kamailio.org/w/documentation/tutorials/
- WebRTC Courses: Udemy, Coursera, Linux Academy
- SIP Protocol Guides: RFC 3261 (core), RFC 3311 (UPDATE), RFC 6665 (SUBSCRIBE)
