Tools: Kamailio Fundamentals — Installation, Routing, Authentication & TLS (2026)
Tutorial 42: Kamailio Fundamentals — Installation, Routing, Authentication & TLS
Table of Contents
1. Introduction
What Is Kamailio?
Kamailio Is NOT a PBX
When You Need Kamailio
Use Cases
2. Architecture Overview
Core Engine
Module System
Configuration Language
Request Processing Flow
Media Path — Kamailio Does NOT Touch RTP
Comparison: Kamailio vs OpenSIPS vs Asterisk
3. Installation
Prerequisites
Step 1: Add the Official Kamailio Repository
Step 2: Install Kamailio and Essential Modules
Step 3: Install and Configure the Database
Step 4: Configure Kamailio Defaults
Step 5: Firewall Configuration
Step 6: Start and Verify
4. Configuration Language
Config File Structure
Global Parameters
Module Loading
Module Parameters
Route Blocks
Pseudo-Variables
Conditional Logic
Core Functions
Complete Minimal Configuration
5. SIP Routing
REGISTER Handling
Stateless vs Stateful Forwarding
Record-Route
Sequential Request Routing (In-Dialog)
Routing to External Destinations (SIP Trunks)
Complete Routing Configuration
6. User Authentication
Overview
Module Setup
Managing Users
Authentication Route
IP-Based Authentication for Trunks
Complete Authenticated Proxy Configuration
7. NAT Traversal
The NAT Problem
NAT Detection with nathelper
Fixing NAT in SIP Signaling
RTPEngine — Media NAT Fix
Installing RTPEngine
Configuring RTPEngine
Kamailio RTPEngine Integration
STUN/TURN Overview
Complete NAT-Aware Configuration
8. TLS & Security
TLS Module Configuration
Separate TLS Config File
Certificate Generation
Option A: Let's Encrypt (Production)
Option B: Self-Signed (Testing)
Client Certificate Verification
SRTP with RTPEngine
Pike Module — Flood Protection
Htable — IP Blacklisting
fail2ban Integration
Security Best Practices
9. Load Balancing with Dispatcher
What Is the Dispatcher Module?
Module Setup
Destination Sets and Algorithms
Adding Backend Servers
Via Database
Via File (Alternative to Database)
Dispatcher Routing Logic
Health Checking
Complete Dispatcher Configuration for 3 Asterisk Backends
10. Dialog & Accounting
Dialog Module — Track Active Calls
Per-User Call Limits
Dialog Statistics
Accounting Module — CDR Generation
CDR Database Schema
Custom CDR Fields
11. WebRTC Gateway
Overview
WebSocket Module Setup
HTTP/WebSocket Upgrade Handling
WebRTC Routing Logic
Nginx Reverse Proxy for WSS
JsSIP/SIP.js Browser Client Example
Complete WebRTC Gateway Configuration
12. Integration with Media Servers
Kamailio + Asterisk: SBC / Load Balancer Pattern
Kamailio + FreeSWITCH
Topology Hiding (topoh Module)
Header Manipulation for Integration
Domain-Based Routing (Multi-Tenant)
Complete Integration Config Example
13. Monitoring & Management
kamcmd — Command-Line Management
kamctl — User and Database Management
JSONRPC Interface for Remote Management
Statistics Overview
Prometheus Exporter
Grafana Dashboard
Log Analysis Patterns
sipcapture / Homer Integration
14. Troubleshooting
SIP Tracing in Kamailio
Debug Logging Levels
Using xlog for Targeted Tracing
Config Trace Mode
Common Routing Errors
481 Call Leg Does Not Exist
407 Authentication Loops
483 Too Many Hops
Registration Failures
NAT Problems (One-Way Audio)
Dispatcher Failover Not Working
Performance Tuning
Essential kamcmd Commands Reference
Quick Diagnostic Script A complete beginner-to-intermediate guide to Kamailio — the high-performance open-source SIP proxy/router. Learn installation, SIP routing logic, user authentication, NAT traversal, TLS encryption, and load balancing from scratch, with production-ready configurations for use as a Session Border Controller (SBC) in front of Asterisk or FreeSWITCH. Whether you need to scale a single-server PBX to handle thousands of concurrent registrations, add a security layer in front of your media servers, or build a multi-tenant SIP platform, Kamailio is the tool that makes it possible. Technologies: Kamailio, SIP, RTPEngine, TLS, WebRTC, MariaDB, Dispatcher, NAT, Prometheus
Difficulty: Beginner–Intermediate
Reading time: ~70 minutes Kamailio is a high-performance, open-source SIP (Session Initiation Protocol) server written in C. It functions as a SIP proxy, SIP registrar, SIP router, and application server. It does not handle media (audio/video) — it exclusively processes SIP signaling messages. Think of Kamailio as a traffic controller for phone calls. It decides where calls should go, authenticates callers, and enforces policies — but it never touches the actual voice data. That job belongs to media servers like Asterisk, FreeSWITCH, or RTPEngine. This is the single most important concept to internalize: B2BUA vs Proxy: Asterisk and FreeSWITCH act as a B2BUA (Back-to-Back User Agent) — they terminate one SIP session and create a new one, sitting in the middle of both the signaling and media paths. Kamailio acts as a proxy — it forwards SIP messages without terminating the session, and media flows directly between endpoints (or through a separate media relay like RTPEngine). You need Kamailio when: Kamailio's core is a SIP message processing engine. When a SIP message arrives, the core: The core itself is minimal — most functionality comes from modules. Kamailio has 200+ modules. Here are the essential ones: Kamailio uses its own configuration language in kamailio.cfg. The config consists of: Every SIP request follows this processing chain: Additional route types: Kamailio only processes the SIP signaling path. For media (RTP), you either: Kamailio and OpenSIPS share the same heritage (both forked from SIP Express Router). They have similar config syntax but have diverged significantly. Choose either based on your team's familiarity — both are production-proven at massive scale. Always use the official Kamailio repository for the latest stable release: Kamailio needs a database for user authentication, location storage, and call accounting. Configure the Kamailio database control tool: Create the database and tables: Expected tables include: The /etc/default/kamailio file should contain: Test with a SIP OPTIONS ping: The Kamailio configuration file (/etc/kamailio/kamailio.cfg) has four distinct sections, and they must appear in this order: Preprocessor directives (#!define) let you enable/disable features at compile time, similar to #ifdef in C. Modules are loaded with loadmodule. The order can matter — some modules depend on others: Each module is configured with modparam("module_name", "parameter", value): Route blocks contain the actual SIP processing logic: Pseudo-variables provide access to SIP message fields and internal state. They are the workhorses of Kamailio configuration: Here is a complete, working minimal kamailio.cfg that functions as a basic SIP proxy: Registration is how SIP phones tell Kamailio where they can be reached. The usrloc module stores contact-to-AOR (Address of Record) mappings, and the registrar module processes REGISTER requests. In request_route, add before the forwarding logic: Stateless (sl module): Kamailio forwards the request and immediately forgets about it. Fast, but no failure handling. Stateful (tm module): Kamailio tracks the transaction, enabling failure routes, timeout handling, and proper retransmission management. This is what you want for INVITE processing. Rule of thumb: Always use t_relay() for INVITE transactions. Use forward() only for simple pass-through scenarios where you don't need failure handling. When Kamailio proxies an INVITE, it must add a Record-Route header to stay in the signaling path for subsequent requests (BYE, re-INVITE, etc.). Without Record-Route, the BYE goes directly between endpoints — Kamailio never sees it and cannot track call state or generate CDRs. After the initial INVITE/200 OK/ACK, subsequent requests (BYE, re-INVITE, UPDATE) are "in-dialog" — they have a To tag. These must be routed using loose_route(): To route calls to an external SIP trunk or carrier: Here is a complete SIP proxy configuration with REGISTER handling, INVITE routing, and in-dialog request management: Without authentication, anyone can register phones and make calls through your proxy. Kamailio uses SIP Digest Authentication (RFC 2617) — the same challenge-response mechanism used by HTTP Basic Auth but adapted for SIP. Parameters explained: Alternatively, insert directly into the database: SIP trunks typically authenticate by IP address rather than username/password. Use the permissions module: Add trusted IPs to the address table: Or directly in the database: NAT (Network Address Translation) is the biggest source of SIP headaches. When a phone behind NAT sends a SIP message, the headers contain the phone's private IP (e.g., 192.168.1.100), but the actual packets arrive from the router's public IP (e.g., 203.0.113.50). This causes two problems: The nathelper module provides functions to detect and fix NAT: NAT detection function — nat_uac_test(flags): Common usage: nat_uac_test(63) — tests all flags combined (1+2+4+8+16+32 = 63). For REGISTER requests, store the real source IP so Kamailio can reach the phone later: Fixing SIP signaling is only half the battle. The RTP media streams also need NAT handling. RTPEngine is a high-performance media relay that sits alongside Kamailio. Usage in routing logic: RTPEngine flags explained: While RTPEngine handles media relay server-side, clients behind NAT may also need STUN/TURN: For most Kamailio deployments, RTPEngine eliminates the need for client-side STUN/TURN — the proxy handles everything. STUN/TURN becomes important primarily for WebRTC deployments. If you need a TURN server, coturn is the standard choice: This configuration handles: TLS encrypts SIP signaling, preventing eavesdropping and tampering. SIP over TLS runs on port 5061 by default. For more granular control, use a dedicated TLS config file (/etc/kamailio/tls.cfg): For mutual TLS (mTLS), require clients to present a valid certificate: In routing logic, you can check TLS peer information: TLS only encrypts SIP signaling. To encrypt the media (RTP), you need SRTP. RTPEngine handles this transparently: The pike module detects and blocks IP addresses that send too many SIP requests (brute-force attacks, SIP scanners): Usage in request_route: The htable module provides in-memory hash tables for fast lookups. Use it for blacklists, whitelists, and rate counters: Kamailio can log authentication failures in a format that fail2ban can parse: Create a fail2ban filter for Kamailio: Create a fail2ban jail: The dispatcher module distributes SIP traffic across multiple backend servers (Asterisk, FreeSWITCH, etc.) with health checking and automatic failover. This is the most common reason to deploy Kamailio in front of a PBX cluster. Destinations are organized into sets (groups). Each set has an algorithm that determines how traffic is distributed: If you prefer not to use a database, configure dispatcher to use a flat file: Create /etc/kamailio/dispatcher.list: Kamailio automatically sends SIP OPTIONS pings to all dispatcher destinations at the configured interval. The health check behavior: Monitor dispatcher status: The dialog module tracks SIP call dialogs from the initial INVITE to the final BYE. It provides call counting, duration tracking, and per-user call limits. Enable dialog tracking in the routing logic: Prevent a single user from making too many simultaneous calls: The acc module generates Call Detail Records (CDRs) and logs them to the database, syslog, or both. Enable accounting in routing: The acc table stores accounting records: Add custom data to your CDRs using AVPs: You need to add corresponding columns to the acc table: WebRTC allows browser-based voice/video calls using JavaScript. Browsers use SIP over WebSocket (WSS) for signaling and DTLS-SRTP for media. Kamailio acts as a gateway between WebRTC and traditional SIP endpoints. WebSocket connections start as HTTP requests with an Upgrade: websocket header: WebRTC requires special handling for media negotiation. The key is using RTPEngine to bridge between DTLS-SRTP (WebRTC) and plain RTP (traditional SIP): In production, you typically put Nginx in front of Kamailio for WSS, handling TLS termination: When using Nginx for TLS termination, Kamailio listens on plain WS: Here is a minimal browser client using JsSIP: The most common deployment: Kamailio sits in front of one or more Asterisk servers, handling registration, authentication, NAT, and load balancing. Asterisk handles media processing (IVR, voicemail, conferencing, recording). Asterisk configuration for Kamailio integration: On each Asterisk backend, configure it to trust the Kamailio server: The same pattern works with FreeSWITCH as the media server: The topoh module hides your internal network topology from external SIP peers. It replaces internal IP addresses in SIP headers with the Kamailio server's public address: When routing between Kamailio and backend servers, you often need to add, remove, or modify SIP headers: Route calls to different backends based on the domain in the request: For database-driven domain routing, use the domain module: kamcmd is the primary tool for interacting with a running Kamailio instance: kamctl is the higher-level management tool for user/database operations: The jsonrpcs module exposes a JSON-RPC interface for programmatic management: Example JSONRPC calls: Key statistics to monitor: Use kamailio_exporter to expose Kamailio statistics as Prometheus metrics: Prometheus scrape config: Key panels for a Kamailio Grafana dashboard: Configure rsyslog to write Kamailio logs to a dedicated file: Key log patterns to monitor: Logrotate configuration: Homer is a SIP capture and analysis tool. Kamailio can send SIP message copies to Homer for analysis: Change debug level at runtime (no restart needed): Enable route execution tracing (very verbose — development only): Or per-message in routing logic: Cause: Kamailio receives an in-dialog request (BYE, re-INVITE) but cannot find the matching transaction or dialog. Cause: Kamailio keeps challenging with 407, and the client keeps retrying with credentials, but they never match. Also verify calculate_ha1 is set correctly: Cause: SIP message is looping between servers, incrementing Max-Forwards until it reaches 0. Symptom: SIP phones cannot register. Check: Symptom: Call connects but one or both sides cannot hear audio. Symptom: Calls fail when a backend goes down instead of failing over. Save this as /usr/local/bin/kamailio-diag.sh for quick health checks: End of Tutorial 42. You now have the foundation to deploy Kamailio as a SIP proxy, SBC, load balancer, or WebRTC gateway in front of your Asterisk or FreeSWITCH infrastructure. Start with a basic proxy config (Section 4), add authentication (Section 6), layer on NAT handling (Section 7) and TLS (Section 8), then scale with dispatcher (Section 9) as your platform grows. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse
SIP Request arrives │ ▼
┌──────────────┐
│ request_route│ ── Main routing logic (MANDATORY)
└──────┬───────┘ │ t_relay() / forward() ▼
┌──────────────┐
│ branch_route │ ── Per-branch processing (before forwarding)
└──────┬───────┘ │ SIP request sent to destination │ │ Response comes back ▼
┌──────────────┐
│ onreply_route│ ── Process responses (180, 200, etc.)
└──────┬───────┘ │ If negative response (4xx, 5xx, 6xx) ▼
┌──────────────┐
│ failure_route│ ── Handle failures (try alternate route)
└──────────────┘
SIP Request arrives │ ▼
┌──────────────┐
│ request_route│ ── Main routing logic (MANDATORY)
└──────┬───────┘ │ t_relay() / forward() ▼
┌──────────────┐
│ branch_route │ ── Per-branch processing (before forwarding)
└──────┬───────┘ │ SIP request sent to destination │ │ Response comes back ▼
┌──────────────┐
│ onreply_route│ ── Process responses (180, 200, etc.)
└──────┬───────┘ │ If negative response (4xx, 5xx, 6xx) ▼
┌──────────────┐
│ failure_route│ ── Handle failures (try alternate route)
└──────────────┘
SIP Request arrives │ ▼
┌──────────────┐
│ request_route│ ── Main routing logic (MANDATORY)
└──────┬───────┘ │ t_relay() / forward() ▼
┌──────────────┐
│ branch_route │ ── Per-branch processing (before forwarding)
└──────┬───────┘ │ SIP request sent to destination │ │ Response comes back ▼
┌──────────────┐
│ onreply_route│ ── Process responses (180, 200, etc.)
└──────┬───────┘ │ If negative response (4xx, 5xx, 6xx) ▼
┌──────────────┐
│ failure_route│ ── Handle failures (try alternate route)
└──────────────┘
SIP Signaling
Phone A ◄─────────────────────────────────► Phone B │ via Kamailio proxy │ │ │ │ RTP Media (Voice) │ └──────────────────────────────────────────┘ Direct (or via RTPEngine)
SIP Signaling
Phone A ◄─────────────────────────────────► Phone B │ via Kamailio proxy │ │ │ │ RTP Media (Voice) │ └──────────────────────────────────────────┘ Direct (or via RTPEngine)
SIP Signaling
Phone A ◄─────────────────────────────────► Phone B │ via Kamailio proxy │ │ │ │ RTP Media (Voice) │ └──────────────────────────────────────────┘ Direct (or via RTPEngine)
# Install prerequisites
apt update
apt install -y gnupg2 curl apt-transport-https # --- For Debian 12 (Bookworm) ---
curl -fsSL https://deb.kamailio.org/kamailiodebkey.gpg | \ gpg --dearmor -o /usr/share/keyrings/kamailio-archive-keyring.gpg cat > /etc/apt/sources.list.d/kamailio.list << 'EOF'
deb [signed-by=/usr/share/keyrings/kamailio-archive-keyring.gpg] https://deb.kamailio.org/kamailio60 bookworm main
EOF # --- For Ubuntu 24.04 (Noble) ---
curl -fsSL https://deb.kamailio.org/kamailiodebkey.gpg | \ gpg --dearmor -o /usr/share/keyrings/kamailio-archive-keyring.gpg cat > /etc/apt/sources.list.d/kamailio.list << 'EOF'
deb [signed-by=/usr/share/keyrings/kamailio-archive-keyring.gpg] https://deb.kamailio.org/kamailio60 noble main
EOF apt update
# Install prerequisites
apt update
apt install -y gnupg2 curl apt-transport-https # --- For Debian 12 (Bookworm) ---
curl -fsSL https://deb.kamailio.org/kamailiodebkey.gpg | \ gpg --dearmor -o /usr/share/keyrings/kamailio-archive-keyring.gpg cat > /etc/apt/sources.list.d/kamailio.list << 'EOF'
deb [signed-by=/usr/share/keyrings/kamailio-archive-keyring.gpg] https://deb.kamailio.org/kamailio60 bookworm main
EOF # --- For Ubuntu 24.04 (Noble) ---
curl -fsSL https://deb.kamailio.org/kamailiodebkey.gpg | \ gpg --dearmor -o /usr/share/keyrings/kamailio-archive-keyring.gpg cat > /etc/apt/sources.list.d/kamailio.list << 'EOF'
deb [signed-by=/usr/share/keyrings/kamailio-archive-keyring.gpg] https://deb.kamailio.org/kamailio60 noble main
EOF apt update
# Install prerequisites
apt update
apt install -y gnupg2 curl apt-transport-https # --- For Debian 12 (Bookworm) ---
curl -fsSL https://deb.kamailio.org/kamailiodebkey.gpg | \ gpg --dearmor -o /usr/share/keyrings/kamailio-archive-keyring.gpg cat > /etc/apt/sources.list.d/kamailio.list << 'EOF'
deb [signed-by=/usr/share/keyrings/kamailio-archive-keyring.gpg] https://deb.kamailio.org/kamailio60 bookworm main
EOF # --- For Ubuntu 24.04 (Noble) ---
curl -fsSL https://deb.kamailio.org/kamailiodebkey.gpg | \ gpg --dearmor -o /usr/share/keyrings/kamailio-archive-keyring.gpg cat > /etc/apt/sources.list.d/kamailio.list << 'EOF'
deb [signed-by=/usr/share/keyrings/kamailio-archive-keyring.gpg] https://deb.kamailio.org/kamailio60 noble main
EOF apt update
apt install -y \ kamailio \ kamailio-mysql-modules \ kamailio-tls-modules \ kamailio-websocket-modules \ kamailio-json-modules \ kamailio-extra-modules \ kamailio-xml-modules \ kamailio-presence-modules # Verify installation
kamailio -v
# Output: version: kamailio 6.0.x ...
apt install -y \ kamailio \ kamailio-mysql-modules \ kamailio-tls-modules \ kamailio-websocket-modules \ kamailio-json-modules \ kamailio-extra-modules \ kamailio-xml-modules \ kamailio-presence-modules # Verify installation
kamailio -v
# Output: version: kamailio 6.0.x ...
apt install -y \ kamailio \ kamailio-mysql-modules \ kamailio-tls-modules \ kamailio-websocket-modules \ kamailio-json-modules \ kamailio-extra-modules \ kamailio-xml-modules \ kamailio-presence-modules # Verify installation
kamailio -v
# Output: version: kamailio 6.0.x ...
# Install MariaDB
apt install -y mariadb-server mariadb-client # Secure the installation
mysql_secure_installation
# Set root password, remove anonymous users, disable remote root, remove test DB # Start and enable MariaDB
systemctl enable --now mariadb
# Install MariaDB
apt install -y mariadb-server mariadb-client # Secure the installation
mysql_secure_installation
# Set root password, remove anonymous users, disable remote root, remove test DB # Start and enable MariaDB
systemctl enable --now mariadb
# Install MariaDB
apt install -y mariadb-server mariadb-client # Secure the installation
mysql_secure_installation
# Set root password, remove anonymous users, disable remote root, remove test DB # Start and enable MariaDB
systemctl enable --now mariadb
# Edit kamctlrc — the database configuration file
cat > /etc/kamailio/kamctlrc << 'CONF'
Database type: MYSQL, PGSQL, ORACLE, DB_BERKELEY, DBTEXT
DBENGINE=MYSQL
Database hostDBHOST=localhost
Database nameDBNAME=kamailio
Database admin user (for creating the DB)DBRWUSER="kamailio"DBRWPW="YOUR_KAMAILIO_DB_PASSWORD"
Read-only user (optional, for failover)DBROUSER="kamailioro"DBROPW="YOUR_KAMAILIO_RO_PASSWORD"
Database portDBPORT=3306
Path to mysql clientINSTALL_EXTRA_TABLES=yesINSTALL_PRESENCE_TABLES=yesINSTALL_DBUID_TABLES=yesCommandCopy# Edit kamctlrc — the database configuration file
cat > /etc/kamailio/kamctlrc << 'CONF'
Database type: MYSQL, PGSQL, ORACLE, DB_BERKELEY, DBTEXT
# Edit kamctlrc — the database configuration file
cat > /etc/kamailio/kamctlrc << 'CONF'
DBENGINE=MYSQL
Database hostDBHOST=localhost
Database nameDBNAME=kamailio
Database admin user (for creating the DB)DBRWUSER="kamailio"DBRWPW="YOUR_KAMAILIO_DB_PASSWORD"
Read-only user (optional, for failover)DBROUSER="kamailioro"DBROPW="YOUR_KAMAILIO_RO_PASSWORD"
Database portDBPORT=3306
Path to mysql clientINSTALL_EXTRA_TABLES=yesINSTALL_PRESENCE_TABLES=yesINSTALL_DBUID_TABLES=yesCommandCopy# Edit kamctlrc — the database configuration file
cat > /etc/kamailio/kamctlrc << 'CONF'
Database type: MYSQL, PGSQL, ORACLE, DB_BERKELEY, DBTEXT
# Edit kamctlrc — the database configuration file
cat > /etc/kamailio/kamctlrc << 'CONF'
DBENGINE=MYSQL
Database hostDBHOST=localhost
Database nameDBNAME=kamailio
Database admin user (for creating the DB)DBRWUSER="kamailio"DBRWPW="YOUR_KAMAILIO_DB_PASSWORD"
Read-only user (optional, for failover)DBROUSER="kamailioro"DBROPW="YOUR_KAMAILIO_RO_PASSWORD"
Database portDBPORT=3306
Path to mysql clientINSTALL_EXTRA_TABLES=yesINSTALL_PRESENCE_TABLES=yesINSTALL_DBUID_TABLES=yesCommandCopy# Create the Kamailio database (answer 'y' to all prompts)
kamdbctl create # This creates:
# - kamailio database
# - Core tables: location, subscriber, version, etc.
# - Extra tables: dialog, acc, dispatcher, etc.
# - Presence tables: presentity, watchers, etc. # Verify
mysql -u kamailio -p'YOUR_KAMAILIO_DB_PASSWORD' -e "SHOW TABLES;" kamailio
# Create the Kamailio database (answer 'y' to all prompts)
kamdbctl create # This creates:
# - kamailio database
# - Core tables: location, subscriber, version, etc.
# - Extra tables: dialog, acc, dispatcher, etc.
# - Presence tables: presentity, watchers, etc. # Verify
mysql -u kamailio -p'YOUR_KAMAILIO_DB_PASSWORD' -e "SHOW TABLES;" kamailio
# Create the Kamailio database (answer 'y' to all prompts)
kamdbctl create # This creates:
# - kamailio database
# - Core tables: location, subscriber, version, etc.
# - Extra tables: dialog, acc, dispatcher, etc.
# - Presence tables: presentity, watchers, etc. # Verify
mysql -u kamailio -p'YOUR_KAMAILIO_DB_PASSWORD' -e "SHOW TABLES;" kamailio
+--------------------+
| Tables_in_kamailio |
+--------------------+
| acc |
| address |
| aliases |
| dialog |
| dispatcher |
| domain |
| location |
| missed_calls |
| subscriber |
| trusted |
| version |
| ... |
+--------------------+
+--------------------+
| Tables_in_kamailio |
+--------------------+
| acc |
| address |
| aliases |
| dialog |
| dispatcher |
| domain |
| location |
| missed_calls |
| subscriber |
| trusted |
| version |
| ... |
+--------------------+
+--------------------+
| Tables_in_kamailio |
+--------------------+
| acc |
| address |
| aliases |
| dialog |
| dispatcher |
| domain |
| location |
| missed_calls |
| subscriber |
| trusted |
| version |
| ... |
+--------------------+
# Enable Kamailio to -weight: 500;">start via systemd
sed -i 's/^RUN_KAMAILIO=no/RUN_KAMAILIO=yes/' /etc/default/kamailio # Set memory allocation (adjust based on your server)
sed -i 's/^# SHM_MEMORY=.*/SHM_MEMORY=128/' /etc/default/kamailio
sed -i 's/^# PKG_MEMORY=.*/PKG_MEMORY=16/' /etc/default/kamailio # Review the defaults file
cat /etc/default/kamailio
# Enable Kamailio to -weight: 500;">start via systemd
sed -i 's/^RUN_KAMAILIO=no/RUN_KAMAILIO=yes/' /etc/default/kamailio # Set memory allocation (adjust based on your server)
sed -i 's/^# SHM_MEMORY=.*/SHM_MEMORY=128/' /etc/default/kamailio
sed -i 's/^# PKG_MEMORY=.*/PKG_MEMORY=16/' /etc/default/kamailio # Review the defaults file
cat /etc/default/kamailio
# Enable Kamailio to -weight: 500;">start via systemd
sed -i 's/^RUN_KAMAILIO=no/RUN_KAMAILIO=yes/' /etc/default/kamailio # Set memory allocation (adjust based on your server)
sed -i 's/^# SHM_MEMORY=.*/SHM_MEMORY=128/' /etc/default/kamailio
sed -i 's/^# PKG_MEMORY=.*/PKG_MEMORY=16/' /etc/default/kamailio # Review the defaults file
cat /etc/default/kamailio
# Set to "yes" to -weight: 500;">enable kamailio
RUN_KAMAILIO=yes # User and group to run as
USER=kamailio
GROUP=kamailio # Shared memory (MB) — total pool for all Kamailio processes
SHM_MEMORY=128 # Private memory (MB) — per-process memory
PKG_MEMORY=16 # Config file location
CFGFILE=/etc/kamailio/kamailio.cfg # PID file
PIDFILE=/run/kamailio/kamailio.pid
# Set to "yes" to -weight: 500;">enable kamailio
RUN_KAMAILIO=yes # User and group to run as
USER=kamailio
GROUP=kamailio # Shared memory (MB) — total pool for all Kamailio processes
SHM_MEMORY=128 # Private memory (MB) — per-process memory
PKG_MEMORY=16 # Config file location
CFGFILE=/etc/kamailio/kamailio.cfg # PID file
PIDFILE=/run/kamailio/kamailio.pid
# Set to "yes" to -weight: 500;">enable kamailio
RUN_KAMAILIO=yes # User and group to run as
USER=kamailio
GROUP=kamailio # Shared memory (MB) — total pool for all Kamailio processes
SHM_MEMORY=128 # Private memory (MB) — per-process memory
PKG_MEMORY=16 # Config file location
CFGFILE=/etc/kamailio/kamailio.cfg # PID file
PIDFILE=/run/kamailio/kamailio.pid
# Using ufw
ufw allow 5060/udp comment "SIP UDP"
ufw allow 5060/tcp comment "SIP TCP"
ufw allow 5061/tcp comment "SIP TLS"
ufw allow 8443/tcp comment "SIP WSS (WebRTC)"
ufw allow 10000:20000/udp comment "RTP Media (RTPEngine)" # Or using iptables directly
iptables -A INPUT -p udp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5061 -j ACCEPT
iptables -A INPUT -p tcp --dport 8443 -j ACCEPT
iptables -A INPUT -p udp --dport 10000:20000 -j ACCEPT
# Using ufw
ufw allow 5060/udp comment "SIP UDP"
ufw allow 5060/tcp comment "SIP TCP"
ufw allow 5061/tcp comment "SIP TLS"
ufw allow 8443/tcp comment "SIP WSS (WebRTC)"
ufw allow 10000:20000/udp comment "RTP Media (RTPEngine)" # Or using iptables directly
iptables -A INPUT -p udp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5061 -j ACCEPT
iptables -A INPUT -p tcp --dport 8443 -j ACCEPT
iptables -A INPUT -p udp --dport 10000:20000 -j ACCEPT
# Using ufw
ufw allow 5060/udp comment "SIP UDP"
ufw allow 5060/tcp comment "SIP TCP"
ufw allow 5061/tcp comment "SIP TLS"
ufw allow 8443/tcp comment "SIP WSS (WebRTC)"
ufw allow 10000:20000/udp comment "RTP Media (RTPEngine)" # Or using iptables directly
iptables -A INPUT -p udp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5061 -j ACCEPT
iptables -A INPUT -p tcp --dport 8443 -j ACCEPT
iptables -A INPUT -p udp --dport 10000:20000 -j ACCEPT
# Check config syntax before starting
kamailio -c -f /etc/kamailio/kamailio.cfg # Start Kamailio
-weight: 500;">systemctl -weight: 500;">start kamailio
-weight: 500;">systemctl -weight: 500;">enable kamailio # Check -weight: 500;">status
-weight: 500;">systemctl -weight: 500;">status kamailio # Verify it's listening
ss -ulnp | grep kamailio
ss -tlnp | grep kamailio # Expected output:
# udp UNCONN 0 0 0.0.0.0:5060 0.0.0.0:* users:(("kamailio",pid=...))
# tcp LISTEN 0 0 0.0.0.0:5060 0.0.0.0:* users:(("kamailio",pid=...))
# Check config syntax before starting
kamailio -c -f /etc/kamailio/kamailio.cfg # Start Kamailio
-weight: 500;">systemctl -weight: 500;">start kamailio
-weight: 500;">systemctl -weight: 500;">enable kamailio # Check -weight: 500;">status
-weight: 500;">systemctl -weight: 500;">status kamailio # Verify it's listening
ss -ulnp | grep kamailio
ss -tlnp | grep kamailio # Expected output:
# udp UNCONN 0 0 0.0.0.0:5060 0.0.0.0:* users:(("kamailio",pid=...))
# tcp LISTEN 0 0 0.0.0.0:5060 0.0.0.0:* users:(("kamailio",pid=...))
# Check config syntax before starting
kamailio -c -f /etc/kamailio/kamailio.cfg # Start Kamailio
-weight: 500;">systemctl -weight: 500;">start kamailio
-weight: 500;">systemctl -weight: 500;">enable kamailio # Check -weight: 500;">status
-weight: 500;">systemctl -weight: 500;">status kamailio # Verify it's listening
ss -ulnp | grep kamailio
ss -tlnp | grep kamailio # Expected output:
# udp UNCONN 0 0 0.0.0.0:5060 0.0.0.0:* users:(("kamailio",pid=...))
# tcp LISTEN 0 0 0.0.0.0:5060 0.0.0.0:* users:(("kamailio",pid=...))
# Install sipsak (SIP Swiss Army Knife)
-weight: 500;">apt -weight: 500;">install -y sipsak # Send OPTIONS to Kamailio
sipsak -s sip:YOUR_SERVER_IP:5060
# Expected: SIP/2.0 200 OK # Using kamctl
kamctl stats
kamctl fifo which # Using kamcmd
kamcmd core.version
kamcmd core.uptime
kamcmd ul.dump
# Install sipsak (SIP Swiss Army Knife)
-weight: 500;">apt -weight: 500;">install -y sipsak # Send OPTIONS to Kamailio
sipsak -s sip:YOUR_SERVER_IP:5060
# Expected: SIP/2.0 200 OK # Using kamctl
kamctl stats
kamctl fifo which # Using kamcmd
kamcmd core.version
kamcmd core.uptime
kamcmd ul.dump
# Install sipsak (SIP Swiss Army Knife)
-weight: 500;">apt -weight: 500;">install -y sipsak # Send OPTIONS to Kamailio
sipsak -s sip:YOUR_SERVER_IP:5060
# Expected: SIP/2.0 200 OK # Using kamctl
kamctl stats
kamctl fifo which # Using kamcmd
kamcmd core.version
kamcmd core.uptime
kamcmd ul.dump
┌─────────────────────────────┐
│ 1. Global Parameters │ ← Server-wide settings
├─────────────────────────────┤
│ 2. Module Loading │ ← loadmodule statements
├─────────────────────────────┤
│ 3. Module Parameters │ ← modparam() calls
├─────────────────────────────┤
│ 4. Route Blocks │ ← Routing logic
└─────────────────────────────┘
┌─────────────────────────────┐
│ 1. Global Parameters │ ← Server-wide settings
├─────────────────────────────┤
│ 2. Module Loading │ ← loadmodule statements
├─────────────────────────────┤
│ 3. Module Parameters │ ← modparam() calls
├─────────────────────────────┤
│ 4. Route Blocks │ ← Routing logic
└─────────────────────────────┘
┌─────────────────────────────┐
│ 1. Global Parameters │ ← Server-wide settings
├─────────────────────────────┤
│ 2. Module Loading │ ← loadmodule statements
├─────────────────────────────┤
│ 3. Module Parameters │ ← modparam() calls
├─────────────────────────────┤
│ 4. Route Blocks │ ← Routing logic
└─────────────────────────────┘
#!KAMAILIO /* ===== Global Parameters ===== */ /* Listen addresses — where Kamailio accepts SIP */
listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060 /* Server aliases — domains this server responds to */
alias="YOUR_DOMAIN"
alias="YOUR_SERVER_IP" /* Logging */
debug=2 # 0=emergency ... 4=debug (use 2 for production)
log_stderror=no # Log to syslog, not stderr
log_facility=LOG_LOCAL0 # Syslog facility /* Process configuration */
children=8 # Number of SIP worker processes (per interface)
tcp_children=4 # Number of TCP worker processes
tcp_max_connections=4096 # Max simultaneous TCP connections
tcp_connection_lifetime=3600 # TCP keepalive (seconds) /* Memory */
# Shared memory and private memory set in /etc/default/kamailio /* DNS */
dns=no # Disable internal DNS (use system resolver)
rev_dns=no # Disable reverse DNS lookups
dns_try_ipv6=no # Disable IPv6 DNS queries
use_dns_cache=on # Cache DNS results
dns_cache_flags=1 # DNS cache settings /* SIP-specific */
mhomed=0 # Multi-homed (0=disabled, 1=choose best source IP)
auto_aliases=no # Don't auto-detect aliases from DNS
server_signature=no # Hide Kamailio version in Server header
server_header="Server: SIP Proxy" # Custom Server header
user_agent_header="User-Agent: SIP Proxy" # Custom UA header /* Reply to OPTIONS with 200 OK */
#!define WITH_AUTH
#!define WITH_MYSQL
#!define WITH_NAT
#!KAMAILIO /* ===== Global Parameters ===== */ /* Listen addresses — where Kamailio accepts SIP */
listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060 /* Server aliases — domains this server responds to */
alias="YOUR_DOMAIN"
alias="YOUR_SERVER_IP" /* Logging */
debug=2 # 0=emergency ... 4=debug (use 2 for production)
log_stderror=no # Log to syslog, not stderr
log_facility=LOG_LOCAL0 # Syslog facility /* Process configuration */
children=8 # Number of SIP worker processes (per interface)
tcp_children=4 # Number of TCP worker processes
tcp_max_connections=4096 # Max simultaneous TCP connections
tcp_connection_lifetime=3600 # TCP keepalive (seconds) /* Memory */
# Shared memory and private memory set in /etc/default/kamailio /* DNS */
dns=no # Disable internal DNS (use system resolver)
rev_dns=no # Disable reverse DNS lookups
dns_try_ipv6=no # Disable IPv6 DNS queries
use_dns_cache=on # Cache DNS results
dns_cache_flags=1 # DNS cache settings /* SIP-specific */
mhomed=0 # Multi-homed (0=disabled, 1=choose best source IP)
auto_aliases=no # Don't auto-detect aliases from DNS
server_signature=no # Hide Kamailio version in Server header
server_header="Server: SIP Proxy" # Custom Server header
user_agent_header="User-Agent: SIP Proxy" # Custom UA header /* Reply to OPTIONS with 200 OK */
#!define WITH_AUTH
#!define WITH_MYSQL
#!define WITH_NAT
#!KAMAILIO /* ===== Global Parameters ===== */ /* Listen addresses — where Kamailio accepts SIP */
listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060 /* Server aliases — domains this server responds to */
alias="YOUR_DOMAIN"
alias="YOUR_SERVER_IP" /* Logging */
debug=2 # 0=emergency ... 4=debug (use 2 for production)
log_stderror=no # Log to syslog, not stderr
log_facility=LOG_LOCAL0 # Syslog facility /* Process configuration */
children=8 # Number of SIP worker processes (per interface)
tcp_children=4 # Number of TCP worker processes
tcp_max_connections=4096 # Max simultaneous TCP connections
tcp_connection_lifetime=3600 # TCP keepalive (seconds) /* Memory */
# Shared memory and private memory set in /etc/default/kamailio /* DNS */
dns=no # Disable internal DNS (use system resolver)
rev_dns=no # Disable reverse DNS lookups
dns_try_ipv6=no # Disable IPv6 DNS queries
use_dns_cache=on # Cache DNS results
dns_cache_flags=1 # DNS cache settings /* SIP-specific */
mhomed=0 # Multi-homed (0=disabled, 1=choose best source IP)
auto_aliases=no # Don't auto-detect aliases from DNS
server_signature=no # Hide Kamailio version in Server header
server_header="Server: SIP Proxy" # Custom Server header
user_agent_header="User-Agent: SIP Proxy" # Custom UA header /* Reply to OPTIONS with 200 OK */
#!define WITH_AUTH
#!define WITH_MYSQL
#!define WITH_NAT
/* ===== Module Loading ===== */ /* Core SIP modules */
loadmodule "kex.so" # Kamailio core extensions
loadmodule "corex.so" # Core extra functions
loadmodule "sl.so" # Stateless replies
loadmodule "tm.so" # Transaction management (stateful SIP)
loadmodule "tmx.so" # Transaction extensions
loadmodule "rr.so" # Record-Route
loadmodule "maxfwd.so" # Max-Forwards check
loadmodule "pv.so" # Pseudo-variables
loadmodule "textops.so" # Text operations on SIP messages
loadmodule "textopsx.so" # Extended text operations
loadmodule "siputils.so" # SIP utility functions
loadmodule "xlog.so" # Extended logging
loadmodule "sanity.so" # SIP message sanity checks /* Database */
loadmodule "db_mysql.so" # MySQL/MariaDB connector /* User management */
loadmodule "usrloc.so" # User location (registration database)
loadmodule "registrar.so" # REGISTER processing /* Authentication */
loadmodule "auth.so" # Auth framework
loadmodule "auth_db.so" # Database-backed auth /* NAT traversal */
loadmodule "nathelper.so" # NAT detection + signaling fix
loadmodule "rtpengine.so" # RTPEngine media relay /* Dialog and accounting */
loadmodule "dialog.so" # Call dialog tracking
loadmodule "acc.so" # Accounting / CDR /* Load balancing */
loadmodule "dispatcher.so" # Dispatcher (load balancer) /* Security */
loadmodule "pike.so" # Flood detection
loadmodule "htable.so" # Hash tables /* Management */
loadmodule "jsonrpcs.so" # JSON-RPC control interface
loadmodule "ctl.so" # Control interface (kamcmd)
loadmodule "cfg_rpc.so" # RPC for config
/* ===== Module Loading ===== */ /* Core SIP modules */
loadmodule "kex.so" # Kamailio core extensions
loadmodule "corex.so" # Core extra functions
loadmodule "sl.so" # Stateless replies
loadmodule "tm.so" # Transaction management (stateful SIP)
loadmodule "tmx.so" # Transaction extensions
loadmodule "rr.so" # Record-Route
loadmodule "maxfwd.so" # Max-Forwards check
loadmodule "pv.so" # Pseudo-variables
loadmodule "textops.so" # Text operations on SIP messages
loadmodule "textopsx.so" # Extended text operations
loadmodule "siputils.so" # SIP utility functions
loadmodule "xlog.so" # Extended logging
loadmodule "sanity.so" # SIP message sanity checks /* Database */
loadmodule "db_mysql.so" # MySQL/MariaDB connector /* User management */
loadmodule "usrloc.so" # User location (registration database)
loadmodule "registrar.so" # REGISTER processing /* Authentication */
loadmodule "auth.so" # Auth framework
loadmodule "auth_db.so" # Database-backed auth /* NAT traversal */
loadmodule "nathelper.so" # NAT detection + signaling fix
loadmodule "rtpengine.so" # RTPEngine media relay /* Dialog and accounting */
loadmodule "dialog.so" # Call dialog tracking
loadmodule "acc.so" # Accounting / CDR /* Load balancing */
loadmodule "dispatcher.so" # Dispatcher (load balancer) /* Security */
loadmodule "pike.so" # Flood detection
loadmodule "htable.so" # Hash tables /* Management */
loadmodule "jsonrpcs.so" # JSON-RPC control interface
loadmodule "ctl.so" # Control interface (kamcmd)
loadmodule "cfg_rpc.so" # RPC for config
/* ===== Module Loading ===== */ /* Core SIP modules */
loadmodule "kex.so" # Kamailio core extensions
loadmodule "corex.so" # Core extra functions
loadmodule "sl.so" # Stateless replies
loadmodule "tm.so" # Transaction management (stateful SIP)
loadmodule "tmx.so" # Transaction extensions
loadmodule "rr.so" # Record-Route
loadmodule "maxfwd.so" # Max-Forwards check
loadmodule "pv.so" # Pseudo-variables
loadmodule "textops.so" # Text operations on SIP messages
loadmodule "textopsx.so" # Extended text operations
loadmodule "siputils.so" # SIP utility functions
loadmodule "xlog.so" # Extended logging
loadmodule "sanity.so" # SIP message sanity checks /* Database */
loadmodule "db_mysql.so" # MySQL/MariaDB connector /* User management */
loadmodule "usrloc.so" # User location (registration database)
loadmodule "registrar.so" # REGISTER processing /* Authentication */
loadmodule "auth.so" # Auth framework
loadmodule "auth_db.so" # Database-backed auth /* NAT traversal */
loadmodule "nathelper.so" # NAT detection + signaling fix
loadmodule "rtpengine.so" # RTPEngine media relay /* Dialog and accounting */
loadmodule "dialog.so" # Call dialog tracking
loadmodule "acc.so" # Accounting / CDR /* Load balancing */
loadmodule "dispatcher.so" # Dispatcher (load balancer) /* Security */
loadmodule "pike.so" # Flood detection
loadmodule "htable.so" # Hash tables /* Management */
loadmodule "jsonrpcs.so" # JSON-RPC control interface
loadmodule "ctl.so" # Control interface (kamcmd)
loadmodule "cfg_rpc.so" # RPC for config
/* ===== Module Parameters ===== */ /* ----- tm ----- */
modparam("tm", "failure_reply_mode", 3)
modparam("tm", "fr_timer", 30000) # Final response timeout (ms)
modparam("tm", "fr_inv_timer", 120000) # INVITE final response timeout (ms)
modparam("tm", "restart_fr_on_each_reply", 1) /* ----- rr ----- */
modparam("rr", "enable_full_lr", 1) # Full loose-routing
modparam("rr", "append_fromtag", 1) # Append From-tag to Record-Route /* ----- usrloc ----- */
modparam("usrloc", "db_mode", 2) # 0=cache, 1=write-through, 2=write-back, 3=DB only
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("usrloc", "timer_interval", 60) # Cleanup interval (seconds)
modparam("usrloc", "use_domain", 0) # 0=ignore domain, 1=use domain in AOR /* ----- registrar ----- */
modparam("registrar", "method_filtering", 1)
modparam("registrar", "max_expires", 3600) # Max registration lifetime
modparam("registrar", "min_expires", 60) # Min registration lifetime
modparam("registrar", "default_expires", 600) # Default if not specified
modparam("registrar", "gruu_enabled", 0) /* ----- auth_db ----- */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1) # Calculate HA1 from plaintext password
modparam("auth_db", "password_column", "password")
modparam("auth_db", "load_credentials", "$avp(s:caller_uuid)=uuid")
modparam("auth_db", "use_domain", 0) /* ----- nathelper ----- */
modparam("nathelper", "natping_interval", 30) # NAT keepalive interval
modparam("nathelper", "ping_nated_only", 1) # Only ping NATed contacts
modparam("nathelper", "sipping", 1) # Use SIP OPTIONS as keepalive /* ----- rtpengine ----- */
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* ----- dialog ----- */
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4)
modparam("dialog", "profiles_with_value", "caller")
modparam("dialog", "profiles_no_value", "active_calls") /* ----- dispatcher ----- */
modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "ds_ping_interval", 10) # Health check interval (seconds)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3) # Failures before marking down
modparam("dispatcher", "ds_inactive_threshold", 3) # Successes before marking up
modparam("dispatcher", "ds_probing_mode", 1) # Probe all destinations /* ----- pike ----- */
modparam("pike", "sampling_time_unit", 2) # Sampling window (seconds)
modparam("pike", "reqs_density_per_unit", 30) # Max requests per window
modparam("pike", "remove_latency", 4) # Ban duration (sampling units) /* ----- jsonrpcs ----- */
modparam("jsonrpcs", "pretty_format", 1)
modparam("jsonrpcs", "transport", 1) # 0=FIFO, 1=datagram, 2=both
/* ===== Module Parameters ===== */ /* ----- tm ----- */
modparam("tm", "failure_reply_mode", 3)
modparam("tm", "fr_timer", 30000) # Final response timeout (ms)
modparam("tm", "fr_inv_timer", 120000) # INVITE final response timeout (ms)
modparam("tm", "restart_fr_on_each_reply", 1) /* ----- rr ----- */
modparam("rr", "enable_full_lr", 1) # Full loose-routing
modparam("rr", "append_fromtag", 1) # Append From-tag to Record-Route /* ----- usrloc ----- */
modparam("usrloc", "db_mode", 2) # 0=cache, 1=write-through, 2=write-back, 3=DB only
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("usrloc", "timer_interval", 60) # Cleanup interval (seconds)
modparam("usrloc", "use_domain", 0) # 0=ignore domain, 1=use domain in AOR /* ----- registrar ----- */
modparam("registrar", "method_filtering", 1)
modparam("registrar", "max_expires", 3600) # Max registration lifetime
modparam("registrar", "min_expires", 60) # Min registration lifetime
modparam("registrar", "default_expires", 600) # Default if not specified
modparam("registrar", "gruu_enabled", 0) /* ----- auth_db ----- */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1) # Calculate HA1 from plaintext password
modparam("auth_db", "password_column", "password")
modparam("auth_db", "load_credentials", "$avp(s:caller_uuid)=uuid")
modparam("auth_db", "use_domain", 0) /* ----- nathelper ----- */
modparam("nathelper", "natping_interval", 30) # NAT keepalive interval
modparam("nathelper", "ping_nated_only", 1) # Only ping NATed contacts
modparam("nathelper", "sipping", 1) # Use SIP OPTIONS as keepalive /* ----- rtpengine ----- */
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* ----- dialog ----- */
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4)
modparam("dialog", "profiles_with_value", "caller")
modparam("dialog", "profiles_no_value", "active_calls") /* ----- dispatcher ----- */
modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "ds_ping_interval", 10) # Health check interval (seconds)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3) # Failures before marking down
modparam("dispatcher", "ds_inactive_threshold", 3) # Successes before marking up
modparam("dispatcher", "ds_probing_mode", 1) # Probe all destinations /* ----- pike ----- */
modparam("pike", "sampling_time_unit", 2) # Sampling window (seconds)
modparam("pike", "reqs_density_per_unit", 30) # Max requests per window
modparam("pike", "remove_latency", 4) # Ban duration (sampling units) /* ----- jsonrpcs ----- */
modparam("jsonrpcs", "pretty_format", 1)
modparam("jsonrpcs", "transport", 1) # 0=FIFO, 1=datagram, 2=both
/* ===== Module Parameters ===== */ /* ----- tm ----- */
modparam("tm", "failure_reply_mode", 3)
modparam("tm", "fr_timer", 30000) # Final response timeout (ms)
modparam("tm", "fr_inv_timer", 120000) # INVITE final response timeout (ms)
modparam("tm", "restart_fr_on_each_reply", 1) /* ----- rr ----- */
modparam("rr", "enable_full_lr", 1) # Full loose-routing
modparam("rr", "append_fromtag", 1) # Append From-tag to Record-Route /* ----- usrloc ----- */
modparam("usrloc", "db_mode", 2) # 0=cache, 1=write-through, 2=write-back, 3=DB only
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("usrloc", "timer_interval", 60) # Cleanup interval (seconds)
modparam("usrloc", "use_domain", 0) # 0=ignore domain, 1=use domain in AOR /* ----- registrar ----- */
modparam("registrar", "method_filtering", 1)
modparam("registrar", "max_expires", 3600) # Max registration lifetime
modparam("registrar", "min_expires", 60) # Min registration lifetime
modparam("registrar", "default_expires", 600) # Default if not specified
modparam("registrar", "gruu_enabled", 0) /* ----- auth_db ----- */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1) # Calculate HA1 from plaintext password
modparam("auth_db", "password_column", "password")
modparam("auth_db", "load_credentials", "$avp(s:caller_uuid)=uuid")
modparam("auth_db", "use_domain", 0) /* ----- nathelper ----- */
modparam("nathelper", "natping_interval", 30) # NAT keepalive interval
modparam("nathelper", "ping_nated_only", 1) # Only ping NATed contacts
modparam("nathelper", "sipping", 1) # Use SIP OPTIONS as keepalive /* ----- rtpengine ----- */
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* ----- dialog ----- */
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4)
modparam("dialog", "profiles_with_value", "caller")
modparam("dialog", "profiles_no_value", "active_calls") /* ----- dispatcher ----- */
modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "ds_ping_interval", 10) # Health check interval (seconds)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3) # Failures before marking down
modparam("dispatcher", "ds_inactive_threshold", 3) # Successes before marking up
modparam("dispatcher", "ds_probing_mode", 1) # Probe all destinations /* ----- pike ----- */
modparam("pike", "sampling_time_unit", 2) # Sampling window (seconds)
modparam("pike", "reqs_density_per_unit", 30) # Max requests per window
modparam("pike", "remove_latency", 4) # Ban duration (sampling units) /* ----- jsonrpcs ----- */
modparam("jsonrpcs", "pretty_format", 1)
modparam("jsonrpcs", "transport", 1) # 0=FIFO, 1=datagram, 2=both
/* ===== Route Blocks ===== */ /* Main request routing */
request_route { # This is where every incoming SIP request is processed xlog("L_INFO", "Received $rm from $si:$sp — R-URI: $ru\n"); # ... routing logic ...
} /* Named sub-routes (called with route(NAME)) */
route[AUTH] { # Authentication logic
} route[REGISTRAR] { # Registration handling
} route[RELAY] { # Forward the request
} /* Reply processing */
reply_route { # Process all SIP responses
} onreply_route[MANAGE_REPLY] { # Per-transaction reply handling
} /* Failure handling */
failure_route[MANAGE_FAILURE] { # Called when a transaction receives a negative final response
} /* Branch processing */
branch_route[MANAGE_BRANCH] { # Called for each branch before forwarding
}
/* ===== Route Blocks ===== */ /* Main request routing */
request_route { # This is where every incoming SIP request is processed xlog("L_INFO", "Received $rm from $si:$sp — R-URI: $ru\n"); # ... routing logic ...
} /* Named sub-routes (called with route(NAME)) */
route[AUTH] { # Authentication logic
} route[REGISTRAR] { # Registration handling
} route[RELAY] { # Forward the request
} /* Reply processing */
reply_route { # Process all SIP responses
} onreply_route[MANAGE_REPLY] { # Per-transaction reply handling
} /* Failure handling */
failure_route[MANAGE_FAILURE] { # Called when a transaction receives a negative final response
} /* Branch processing */
branch_route[MANAGE_BRANCH] { # Called for each branch before forwarding
}
/* ===== Route Blocks ===== */ /* Main request routing */
request_route { # This is where every incoming SIP request is processed xlog("L_INFO", "Received $rm from $si:$sp — R-URI: $ru\n"); # ... routing logic ...
} /* Named sub-routes (called with route(NAME)) */
route[AUTH] { # Authentication logic
} route[REGISTRAR] { # Registration handling
} route[RELAY] { # Forward the request
} /* Reply processing */
reply_route { # Process all SIP responses
} onreply_route[MANAGE_REPLY] { # Per-transaction reply handling
} /* Failure handling */
failure_route[MANAGE_FAILURE] { # Called when a transaction receives a negative final response
} /* Branch processing */
branch_route[MANAGE_BRANCH] { # Called for each branch before forwarding
}
# If/else
if ($rm == "INVITE") { xlog("L_INFO", "Processing INVITE\n");
} else if ($rm == "REGISTER") { xlog("L_INFO", "Processing REGISTER\n");
} else { xlog("L_INFO", "Processing $rm\n");
} # Regex matching
if ($rU =~ "^1[0-9]{3}$") { # Request URI username matches 4-digit extension starting with 1 xlog("L_INFO", "Internal extension: $rU\n");
} # String matching
if ($ua =~ "friendly-scanner") { # Block known SIP scanners sl_send_reply(403, "Forbidden"); exit;
} # IP matching
if ($si == "10.0.0.1") { # Request from trusted IP
} # Check if header exists
if (is_present_hf("X-Custom-Header")) { xlog("L_INFO", "Custom header value: $hdr(X-Custom-Header)\n");
} # Logical operators
if ($rm == "INVITE" && !has_totag()) { # New INVITE (not a re-INVITE)
}
# If/else
if ($rm == "INVITE") { xlog("L_INFO", "Processing INVITE\n");
} else if ($rm == "REGISTER") { xlog("L_INFO", "Processing REGISTER\n");
} else { xlog("L_INFO", "Processing $rm\n");
} # Regex matching
if ($rU =~ "^1[0-9]{3}$") { # Request URI username matches 4-digit extension starting with 1 xlog("L_INFO", "Internal extension: $rU\n");
} # String matching
if ($ua =~ "friendly-scanner") { # Block known SIP scanners sl_send_reply(403, "Forbidden"); exit;
} # IP matching
if ($si == "10.0.0.1") { # Request from trusted IP
} # Check if header exists
if (is_present_hf("X-Custom-Header")) { xlog("L_INFO", "Custom header value: $hdr(X-Custom-Header)\n");
} # Logical operators
if ($rm == "INVITE" && !has_totag()) { # New INVITE (not a re-INVITE)
}
# If/else
if ($rm == "INVITE") { xlog("L_INFO", "Processing INVITE\n");
} else if ($rm == "REGISTER") { xlog("L_INFO", "Processing REGISTER\n");
} else { xlog("L_INFO", "Processing $rm\n");
} # Regex matching
if ($rU =~ "^1[0-9]{3}$") { # Request URI username matches 4-digit extension starting with 1 xlog("L_INFO", "Internal extension: $rU\n");
} # String matching
if ($ua =~ "friendly-scanner") { # Block known SIP scanners sl_send_reply(403, "Forbidden"); exit;
} # IP matching
if ($si == "10.0.0.1") { # Request from trusted IP
} # Check if header exists
if (is_present_hf("X-Custom-Header")) { xlog("L_INFO", "Custom header value: $hdr(X-Custom-Header)\n");
} # Logical operators
if ($rm == "INVITE" && !has_totag()) { # New INVITE (not a re-INVITE)
}
# Forward request statelessly (fast, no failure handling)
forward(); # Forward request statefully (enables failure_route)
t_relay(); # Send a stateless reply
sl_send_reply(200, "OK");
sl_send_reply(403, "Forbidden");
sl_send_reply(503, "Service Unavailable"); # Send a stateful reply
t_reply(200, "OK"); # Rewrite the Request-URI
$ru = "sip:[email protected]";
# Or using rewriteuri:
rewriteuri("sip:[email protected]"); # Rewrite just the host portion
$rd = "10.0.0.5"; # Rewrite just the username
$rU = "2001"; # Add a SIP header
append_hf("X-Forwarded-For: $si\r\n"); # Remove a SIP header
remove_hf("User-Agent"); # Replace a SIP header
subst_hf("User-Agent", "/.*$/SIP Proxy/", "a"); # Stop processing
exit; # Drop the message silently
drop();
# Forward request statelessly (fast, no failure handling)
forward(); # Forward request statefully (enables failure_route)
t_relay(); # Send a stateless reply
sl_send_reply(200, "OK");
sl_send_reply(403, "Forbidden");
sl_send_reply(503, "Service Unavailable"); # Send a stateful reply
t_reply(200, "OK"); # Rewrite the Request-URI
$ru = "sip:[email protected]";
# Or using rewriteuri:
rewriteuri("sip:[email protected]"); # Rewrite just the host portion
$rd = "10.0.0.5"; # Rewrite just the username
$rU = "2001"; # Add a SIP header
append_hf("X-Forwarded-For: $si\r\n"); # Remove a SIP header
remove_hf("User-Agent"); # Replace a SIP header
subst_hf("User-Agent", "/.*$/SIP Proxy/", "a"); # Stop processing
exit; # Drop the message silently
drop();
# Forward request statelessly (fast, no failure handling)
forward(); # Forward request statefully (enables failure_route)
t_relay(); # Send a stateless reply
sl_send_reply(200, "OK");
sl_send_reply(403, "Forbidden");
sl_send_reply(503, "Service Unavailable"); # Send a stateful reply
t_reply(200, "OK"); # Rewrite the Request-URI
$ru = "sip:[email protected]";
# Or using rewriteuri:
rewriteuri("sip:[email protected]"); # Rewrite just the host portion
$rd = "10.0.0.5"; # Rewrite just the username
$rU = "2001"; # Add a SIP header
append_hf("X-Forwarded-For: $si\r\n"); # Remove a SIP header
remove_hf("User-Agent"); # Replace a SIP header
subst_hf("User-Agent", "/.*$/SIP Proxy/", "a"); # Stop processing
exit; # Drop the message silently
drop();
#!KAMAILIO /* ===== Global Parameters ===== */
listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
alias="YOUR_DOMAIN" debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=4
auto_aliases=no
server_signature=no /* ===== Module Loading ===== */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so" /* ===== Module Parameters ===== */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) /* ===== Routing Logic ===== */
request_route { /* --- 1. Sanity checks --- */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { xlog("L_WARN", "Malformed SIP from $si:$sp\n"); exit; } /* --- 2. Record-Route for in-dialog requests --- */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* --- 3. Handle sequential requests (in-dialog) --- */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE from $fU to $tU\n"); } t_relay(); exit; } else { /* Sequential request but no Route header — reject */ sl_send_reply(404, "Not Found"); exit; } } /* --- 4. Handle CANCEL --- */ if (is_method("CANCEL")) { if (t_check_trans()) { t_relay(); } exit; } /* --- 5. Absorb retransmissions --- */ t_check_trans(); /* --- 6. Handle OPTIONS (keepalive) --- */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* --- 7. Route the request --- */ xlog("L_INFO", "$rm from $fU@$si to $rU — forwarding\n"); t_relay(); exit;
}
#!KAMAILIO /* ===== Global Parameters ===== */
listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
alias="YOUR_DOMAIN" debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=4
auto_aliases=no
server_signature=no /* ===== Module Loading ===== */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so" /* ===== Module Parameters ===== */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) /* ===== Routing Logic ===== */
request_route { /* --- 1. Sanity checks --- */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { xlog("L_WARN", "Malformed SIP from $si:$sp\n"); exit; } /* --- 2. Record-Route for in-dialog requests --- */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* --- 3. Handle sequential requests (in-dialog) --- */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE from $fU to $tU\n"); } t_relay(); exit; } else { /* Sequential request but no Route header — reject */ sl_send_reply(404, "Not Found"); exit; } } /* --- 4. Handle CANCEL --- */ if (is_method("CANCEL")) { if (t_check_trans()) { t_relay(); } exit; } /* --- 5. Absorb retransmissions --- */ t_check_trans(); /* --- 6. Handle OPTIONS (keepalive) --- */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* --- 7. Route the request --- */ xlog("L_INFO", "$rm from $fU@$si to $rU — forwarding\n"); t_relay(); exit;
}
#!KAMAILIO /* ===== Global Parameters ===== */
listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
alias="YOUR_DOMAIN" debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=4
auto_aliases=no
server_signature=no /* ===== Module Loading ===== */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so" /* ===== Module Parameters ===== */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) /* ===== Routing Logic ===== */
request_route { /* --- 1. Sanity checks --- */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { xlog("L_WARN", "Malformed SIP from $si:$sp\n"); exit; } /* --- 2. Record-Route for in-dialog requests --- */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* --- 3. Handle sequential requests (in-dialog) --- */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE from $fU to $tU\n"); } t_relay(); exit; } else { /* Sequential request but no Route header — reject */ sl_send_reply(404, "Not Found"); exit; } } /* --- 4. Handle CANCEL --- */ if (is_method("CANCEL")) { if (t_check_trans()) { t_relay(); } exit; } /* --- 5. Absorb retransmissions --- */ t_check_trans(); /* --- 6. Handle OPTIONS (keepalive) --- */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* --- 7. Route the request --- */ xlog("L_INFO", "$rm from $fU@$si to $rU — forwarding\n"); t_relay(); exit;
}
/* Add to module loading section */
loadmodule "usrloc.so"
loadmodule "registrar.so" /* Module parameters */
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600)
/* Add to module loading section */
loadmodule "usrloc.so"
loadmodule "registrar.so" /* Module parameters */
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600)
/* Add to module loading section */
loadmodule "usrloc.so"
loadmodule "registrar.so" /* Module parameters */
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600)
route[REGISTRAR] { /* Only handle REGISTER */ if (!is_method("REGISTER")) return; /* Check for too many contacts */ if (is_present_hf("Contact")) { if ($hdr(Contact) =~ "\*") { /* Unregister all — Contact: * */ } } /* Save the registration */ if (!save("location")) { sl_send_reply(500, "Server Error - Registration Failed"); exit; } /* save() automatically sends 200 OK with Contact + Expires */ exit;
}
route[REGISTRAR] { /* Only handle REGISTER */ if (!is_method("REGISTER")) return; /* Check for too many contacts */ if (is_present_hf("Contact")) { if ($hdr(Contact) =~ "\*") { /* Unregister all — Contact: * */ } } /* Save the registration */ if (!save("location")) { sl_send_reply(500, "Server Error - Registration Failed"); exit; } /* save() automatically sends 200 OK with Contact + Expires */ exit;
}
route[REGISTRAR] { /* Only handle REGISTER */ if (!is_method("REGISTER")) return; /* Check for too many contacts */ if (is_present_hf("Contact")) { if ($hdr(Contact) =~ "\*") { /* Unregister all — Contact: * */ } } /* Save the registration */ if (!save("location")) { sl_send_reply(500, "Server Error - Registration Failed"); exit; } /* save() automatically sends 200 OK with Contact + Expires */ exit;
}
/* Handle REGISTER */
if (is_method("REGISTER")) { route(REGISTRAR); exit;
}
/* Handle REGISTER */
if (is_method("REGISTER")) { route(REGISTRAR); exit;
}
/* Handle REGISTER */
if (is_method("REGISTER")) { route(REGISTRAR); exit;
}
/* Stateless forward — fire and forget */
forward();
/* Stateless forward — fire and forget */
forward();
/* Stateless forward — fire and forget */
forward();
/* Stateful forward — enables failure_route and onreply_route */
t_on_failure("MANAGE_FAILURE");
t_on_reply("MANAGE_REPLY");
t_on_branch("MANAGE_BRANCH");
t_relay();
/* Stateful forward — enables failure_route and onreply_route */
t_on_failure("MANAGE_FAILURE");
t_on_reply("MANAGE_REPLY");
t_on_branch("MANAGE_BRANCH");
t_relay();
/* Stateful forward — enables failure_route and onreply_route */
t_on_failure("MANAGE_FAILURE");
t_on_reply("MANAGE_REPLY");
t_on_branch("MANAGE_BRANCH");
t_relay();
/* Add Record-Route for dialog-initiating requests */
if (is_method("INVITE|SUBSCRIBE")) { record_route();
}
/* Add Record-Route for dialog-initiating requests */
if (is_method("INVITE|SUBSCRIBE")) { record_route();
}
/* Add Record-Route for dialog-initiating requests */
if (is_method("INVITE|SUBSCRIBE")) { record_route();
}
INVITE from Phone A → Kamailio → Phone B With record_route(), Kamailio adds: Record-Route: <sip:YOUR_SERVER_IP;lr> Phone B's 200 OK includes this in the Route set. Now BYE from Phone A → Kamailio → Phone B (Kamailio stays in the signaling path) Without record_route(): BYE from Phone A → Phone B (directly — Kamailio is bypassed)
INVITE from Phone A → Kamailio → Phone B With record_route(), Kamailio adds: Record-Route: <sip:YOUR_SERVER_IP;lr> Phone B's 200 OK includes this in the Route set. Now BYE from Phone A → Kamailio → Phone B (Kamailio stays in the signaling path) Without record_route(): BYE from Phone A → Phone B (directly — Kamailio is bypassed)
INVITE from Phone A → Kamailio → Phone B With record_route(), Kamailio adds: Record-Route: <sip:YOUR_SERVER_IP;lr> Phone B's 200 OK includes this in the Route set. Now BYE from Phone A → Kamailio → Phone B (Kamailio stays in the signaling path) Without record_route(): BYE from Phone A → Phone B (directly — Kamailio is bypassed)
/* Handle sequential (in-dialog) requests */
if (has_totag()) { /* Validate Route headers */ if (loose_route()) { /* In-dialog request with valid Route — forward it */ if (is_method("BYE")) { xlog("L_INFO", "Call ended: $fU → $tU (Call-ID: $ci)\n"); /* Optionally: track in CDR */ } if (is_method("ACK")) { /* ACK for 2xx — route it */ } t_relay(); exit; } else { /* In-dialog request but no valid Route header */ if (is_method("ACK")) { /* ACK without Route — might be for a local 4xx reply */ if (t_check_trans()) { exit; } exit; } /* All other in-dialog without Route — error */ sl_send_reply(404, "Not Found"); exit; }
}
/* Handle sequential (in-dialog) requests */
if (has_totag()) { /* Validate Route headers */ if (loose_route()) { /* In-dialog request with valid Route — forward it */ if (is_method("BYE")) { xlog("L_INFO", "Call ended: $fU → $tU (Call-ID: $ci)\n"); /* Optionally: track in CDR */ } if (is_method("ACK")) { /* ACK for 2xx — route it */ } t_relay(); exit; } else { /* In-dialog request but no valid Route header */ if (is_method("ACK")) { /* ACK without Route — might be for a local 4xx reply */ if (t_check_trans()) { exit; } exit; } /* All other in-dialog without Route — error */ sl_send_reply(404, "Not Found"); exit; }
}
/* Handle sequential (in-dialog) requests */
if (has_totag()) { /* Validate Route headers */ if (loose_route()) { /* In-dialog request with valid Route — forward it */ if (is_method("BYE")) { xlog("L_INFO", "Call ended: $fU → $tU (Call-ID: $ci)\n"); /* Optionally: track in CDR */ } if (is_method("ACK")) { /* ACK for 2xx — route it */ } t_relay(); exit; } else { /* In-dialog request but no valid Route header */ if (is_method("ACK")) { /* ACK without Route — might be for a local 4xx reply */ if (t_check_trans()) { exit; } exit; } /* All other in-dialog without Route — error */ sl_send_reply(404, "Not Found"); exit; }
}
route[TO_TRUNK] { /* Route to SIP trunk */ $ru = "sip:" + $rU + "@TRUNK_IP:5060"; /* Add custom headers if the trunk requires them */ append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); /* Remove internal headers */ remove_hf("X-Internal-Route"); /* Forward statefully with failure handling */ t_on_failure("TRUNK_FAILURE"); t_relay(); exit;
} failure_route[TRUNK_FAILURE] { /* If the primary trunk fails, try backup */ if (t_is_canceled()) exit; xlog("L_WARN", "Trunk failed with $T_reply_code for $rU\n"); if ($T_reply_code >= 500) { /* 5xx error — try backup trunk */ $ru = "sip:" + $rU + "@BACKUP_TRUNK_IP:5060"; t_relay(); exit; }
}
route[TO_TRUNK] { /* Route to SIP trunk */ $ru = "sip:" + $rU + "@TRUNK_IP:5060"; /* Add custom headers if the trunk requires them */ append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); /* Remove internal headers */ remove_hf("X-Internal-Route"); /* Forward statefully with failure handling */ t_on_failure("TRUNK_FAILURE"); t_relay(); exit;
} failure_route[TRUNK_FAILURE] { /* If the primary trunk fails, try backup */ if (t_is_canceled()) exit; xlog("L_WARN", "Trunk failed with $T_reply_code for $rU\n"); if ($T_reply_code >= 500) { /* 5xx error — try backup trunk */ $ru = "sip:" + $rU + "@BACKUP_TRUNK_IP:5060"; t_relay(); exit; }
}
route[TO_TRUNK] { /* Route to SIP trunk */ $ru = "sip:" + $rU + "@TRUNK_IP:5060"; /* Add custom headers if the trunk requires them */ append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); /* Remove internal headers */ remove_hf("X-Internal-Route"); /* Forward statefully with failure handling */ t_on_failure("TRUNK_FAILURE"); t_relay(); exit;
} failure_route[TRUNK_FAILURE] { /* If the primary trunk fails, try backup */ if (t_is_canceled()) exit; xlog("L_WARN", "Trunk failed with $T_reply_code for $rU\n"); if ($T_reply_code >= 500) { /* 5xx error — try backup trunk */ $ru = "sip:" + $rU + "@BACKUP_TRUNK_IP:5060"; t_relay(); exit; }
}
request_route { /* ---- Sanity ---- */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { xlog("L_WARN", "Malformed SIP from $si:$sp\n"); exit; } /* ---- Anti-flood ---- */ if (src_ip != myself) { if (!pike_check_req()) { xlog("L_ALERT", "PIKE BLOCK: $si flooding\n"); exit; } } /* ---- Record-Route ---- */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* ---- In-dialog requests ---- */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE: $fU → $tU ($ci)\n"); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) { exit; } sl_send_reply(404, "Not Found"); exit; } /* ---- CANCEL ---- */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } /* ---- Retransmission absorption ---- */ t_check_trans(); /* ---- REGISTER ---- */ if (is_method("REGISTER")) { route(REGISTRAR); exit; } /* ---- OPTIONS keepalive ---- */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* ---- INVITE routing ---- */ if (is_method("INVITE")) { /* Look up the destination in the location table */ if (!lookup("location")) { /* Not found in registrations — try external routing */ xlog("L_INFO", "User $rU not registered, sending 404\n"); sl_send_reply(404, "User Not Found"); exit; } /* Found — relay to registered contact */ route(RELAY); exit; } /* ---- Other requests ---- */ if (is_method("MESSAGE|NOTIFY|INFO|UPDATE|PRACK|REFER")) { if (lookup("location")) { route(RELAY); exit; } sl_send_reply(404, "User Not Found"); exit; } /* Default — reject */ sl_send_reply(405, "Method Not Allowed"); exit;
} route[RELAY] { t_on_failure("MANAGE_FAILURE"); t_on_reply("MANAGE_REPLY"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[REGISTRAR] { if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
} onreply_route[MANAGE_REPLY] { xlog("L_INFO", "Reply $T_reply_code for $rm ($ci)\n");
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Failure $T_reply_code for $rU ($ci)\n");
}
request_route { /* ---- Sanity ---- */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { xlog("L_WARN", "Malformed SIP from $si:$sp\n"); exit; } /* ---- Anti-flood ---- */ if (src_ip != myself) { if (!pike_check_req()) { xlog("L_ALERT", "PIKE BLOCK: $si flooding\n"); exit; } } /* ---- Record-Route ---- */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* ---- In-dialog requests ---- */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE: $fU → $tU ($ci)\n"); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) { exit; } sl_send_reply(404, "Not Found"); exit; } /* ---- CANCEL ---- */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } /* ---- Retransmission absorption ---- */ t_check_trans(); /* ---- REGISTER ---- */ if (is_method("REGISTER")) { route(REGISTRAR); exit; } /* ---- OPTIONS keepalive ---- */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* ---- INVITE routing ---- */ if (is_method("INVITE")) { /* Look up the destination in the location table */ if (!lookup("location")) { /* Not found in registrations — try external routing */ xlog("L_INFO", "User $rU not registered, sending 404\n"); sl_send_reply(404, "User Not Found"); exit; } /* Found — relay to registered contact */ route(RELAY); exit; } /* ---- Other requests ---- */ if (is_method("MESSAGE|NOTIFY|INFO|UPDATE|PRACK|REFER")) { if (lookup("location")) { route(RELAY); exit; } sl_send_reply(404, "User Not Found"); exit; } /* Default — reject */ sl_send_reply(405, "Method Not Allowed"); exit;
} route[RELAY] { t_on_failure("MANAGE_FAILURE"); t_on_reply("MANAGE_REPLY"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[REGISTRAR] { if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
} onreply_route[MANAGE_REPLY] { xlog("L_INFO", "Reply $T_reply_code for $rm ($ci)\n");
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Failure $T_reply_code for $rU ($ci)\n");
}
request_route { /* ---- Sanity ---- */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { xlog("L_WARN", "Malformed SIP from $si:$sp\n"); exit; } /* ---- Anti-flood ---- */ if (src_ip != myself) { if (!pike_check_req()) { xlog("L_ALERT", "PIKE BLOCK: $si flooding\n"); exit; } } /* ---- Record-Route ---- */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* ---- In-dialog requests ---- */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE: $fU → $tU ($ci)\n"); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) { exit; } sl_send_reply(404, "Not Found"); exit; } /* ---- CANCEL ---- */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } /* ---- Retransmission absorption ---- */ t_check_trans(); /* ---- REGISTER ---- */ if (is_method("REGISTER")) { route(REGISTRAR); exit; } /* ---- OPTIONS keepalive ---- */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* ---- INVITE routing ---- */ if (is_method("INVITE")) { /* Look up the destination in the location table */ if (!lookup("location")) { /* Not found in registrations — try external routing */ xlog("L_INFO", "User $rU not registered, sending 404\n"); sl_send_reply(404, "User Not Found"); exit; } /* Found — relay to registered contact */ route(RELAY); exit; } /* ---- Other requests ---- */ if (is_method("MESSAGE|NOTIFY|INFO|UPDATE|PRACK|REFER")) { if (lookup("location")) { route(RELAY); exit; } sl_send_reply(404, "User Not Found"); exit; } /* Default — reject */ sl_send_reply(405, "Method Not Allowed"); exit;
} route[RELAY] { t_on_failure("MANAGE_FAILURE"); t_on_reply("MANAGE_REPLY"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[REGISTRAR] { if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
} onreply_route[MANAGE_REPLY] { xlog("L_INFO", "Reply $T_reply_code for $rm ($ci)\n");
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Failure $T_reply_code for $rU ($ci)\n");
}
Phone Kamailio Database │ │ │ │─── REGISTER (no auth) ───────►│ │ │ │ │ │◄── 401 Unauthorized ──────────│ (with WWW-Authenticate │ │ (nonce + realm) │ challenge) │ │ │ │ │─── REGISTER (with auth) ─────►│ │ │ (username + response hash) │── lookup password ────────►│ │ │◄── password ───────────────│ │ │ │ │ │ verify hash(password + │ │ │ nonce + method + uri) │ │ │ │ │◄── 200 OK ────────────────────│ │
Phone Kamailio Database │ │ │ │─── REGISTER (no auth) ───────►│ │ │ │ │ │◄── 401 Unauthorized ──────────│ (with WWW-Authenticate │ │ (nonce + realm) │ challenge) │ │ │ │ │─── REGISTER (with auth) ─────►│ │ │ (username + response hash) │── lookup password ────────►│ │ │◄── password ───────────────│ │ │ │ │ │ verify hash(password + │ │ │ nonce + method + uri) │ │ │ │ │◄── 200 OK ────────────────────│ │
Phone Kamailio Database │ │ │ │─── REGISTER (no auth) ───────►│ │ │ │ │ │◄── 401 Unauthorized ──────────│ (with WWW-Authenticate │ │ (nonce + realm) │ challenge) │ │ │ │ │─── REGISTER (with auth) ─────►│ │ │ (username + response hash) │── lookup password ────────►│ │ │◄── password ───────────────│ │ │ │ │ │ verify hash(password + │ │ │ nonce + method + uri) │ │ │ │ │◄── 200 OK ────────────────────│ │
/* Module loading */
loadmodule "auth.so"
loadmodule "auth_db.so" /* Module parameters */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0)
/* Module loading */
loadmodule "auth.so"
loadmodule "auth_db.so" /* Module parameters */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0)
/* Module loading */
loadmodule "auth.so"
loadmodule "auth_db.so" /* Module parameters */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0)
# Add a user
kamctl add 1001 MySecurePass123
# This inserts into the 'subscriber' table # List users
kamctl showdb subscriber # Change password
kamctl passwd 1001 NewPassword456 # Delete a user
kamctl rm 1001 # Add with domain (if use_domain=1)
kamctl add [email protected] MySecurePass123
# Add a user
kamctl add 1001 MySecurePass123
# This inserts into the 'subscriber' table # List users
kamctl showdb subscriber # Change password
kamctl passwd 1001 NewPassword456 # Delete a user
kamctl rm 1001 # Add with domain (if use_domain=1)
kamctl add [email protected] MySecurePass123
# Add a user
kamctl add 1001 MySecurePass123
# This inserts into the 'subscriber' table # List users
kamctl showdb subscriber # Change password
kamctl passwd 1001 NewPassword456 # Delete a user
kamctl rm 1001 # Add with domain (if use_domain=1)
kamctl add [email protected] MySecurePass123
INSERT INTO subscriber (username, domain, password, ha1, ha1b)
VALUES ( '1001', 'YOUR_DOMAIN', 'MySecurePass123', MD5('1001:YOUR_DOMAIN:MySecurePass123'), MD5('1001@YOUR_DOMAIN:YOUR_DOMAIN:MySecurePass123')
);
INSERT INTO subscriber (username, domain, password, ha1, ha1b)
VALUES ( '1001', 'YOUR_DOMAIN', 'MySecurePass123', MD5('1001:YOUR_DOMAIN:MySecurePass123'), MD5('1001@YOUR_DOMAIN:YOUR_DOMAIN:MySecurePass123')
);
INSERT INTO subscriber (username, domain, password, ha1, ha1b)
VALUES ( '1001', 'YOUR_DOMAIN', 'MySecurePass123', MD5('1001:YOUR_DOMAIN:MySecurePass123'), MD5('1001@YOUR_DOMAIN:YOUR_DOMAIN:MySecurePass123')
);
route[AUTH] { /* Skip authentication for requests from trusted IPs */ if (src_ip == myself) return; /* REGISTER requires WWW-Authenticate (401) */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } /* Verify the From URI matches the authenticated user */ if ($au != $fU) { xlog("L_WARN", "Auth mismatch: auth=$au from=$fU ($si)\n"); sl_send_reply(403, "Forbidden — User Mismatch"); exit; } return; /* Auth passed */ } /* All other requests use Proxy-Authenticate (407) */ if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } /* Verify authenticated user matches From */ if ($au != $fU) { xlog("L_WARN", "Auth mismatch: auth=$au from=$fU ($si)\n"); sl_send_reply(403, "Forbidden — User Mismatch"); exit; } /* Remove the Proxy-Authorization header before forwarding */ consume_credentials(); return;
}
route[AUTH] { /* Skip authentication for requests from trusted IPs */ if (src_ip == myself) return; /* REGISTER requires WWW-Authenticate (401) */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } /* Verify the From URI matches the authenticated user */ if ($au != $fU) { xlog("L_WARN", "Auth mismatch: auth=$au from=$fU ($si)\n"); sl_send_reply(403, "Forbidden — User Mismatch"); exit; } return; /* Auth passed */ } /* All other requests use Proxy-Authenticate (407) */ if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } /* Verify authenticated user matches From */ if ($au != $fU) { xlog("L_WARN", "Auth mismatch: auth=$au from=$fU ($si)\n"); sl_send_reply(403, "Forbidden — User Mismatch"); exit; } /* Remove the Proxy-Authorization header before forwarding */ consume_credentials(); return;
}
route[AUTH] { /* Skip authentication for requests from trusted IPs */ if (src_ip == myself) return; /* REGISTER requires WWW-Authenticate (401) */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } /* Verify the From URI matches the authenticated user */ if ($au != $fU) { xlog("L_WARN", "Auth mismatch: auth=$au from=$fU ($si)\n"); sl_send_reply(403, "Forbidden — User Mismatch"); exit; } return; /* Auth passed */ } /* All other requests use Proxy-Authenticate (407) */ if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } /* Verify authenticated user matches From */ if ($au != $fU) { xlog("L_WARN", "Auth mismatch: auth=$au from=$fU ($si)\n"); sl_send_reply(403, "Forbidden — User Mismatch"); exit; } /* Remove the Proxy-Authorization header before forwarding */ consume_credentials(); return;
}
loadmodule "permissions.so"
modparam("permissions", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
loadmodule "permissions.so"
modparam("permissions", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
loadmodule "permissions.so"
modparam("permissions", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
# Add a trusted trunk IP (group 1 = trunks)
kamctl address add 1 TRUNK_IP 32 5060 "Primary SIP Trunk" # Reload the address table
kamctl address reload # Show trusted addresses
kamctl address show
# Add a trusted trunk IP (group 1 = trunks)
kamctl address add 1 TRUNK_IP 32 5060 "Primary SIP Trunk" # Reload the address table
kamctl address reload # Show trusted addresses
kamctl address show
# Add a trusted trunk IP (group 1 = trunks)
kamctl address add 1 TRUNK_IP 32 5060 "Primary SIP Trunk" # Reload the address table
kamctl address reload # Show trusted addresses
kamctl address show
INSERT INTO address (grp, ip_addr, mask, port, tag)
VALUES (1, 'TRUNK_IP', 32, 5060, 'Primary SIP Trunk'); INSERT INTO address (grp, ip_addr, mask, port, tag)
VALUES (1, 'BACKUP_TRUNK_IP', 32, 5060, 'Backup SIP Trunk');
INSERT INTO address (grp, ip_addr, mask, port, tag)
VALUES (1, 'TRUNK_IP', 32, 5060, 'Primary SIP Trunk'); INSERT INTO address (grp, ip_addr, mask, port, tag)
VALUES (1, 'BACKUP_TRUNK_IP', 32, 5060, 'Backup SIP Trunk');
INSERT INTO address (grp, ip_addr, mask, port, tag)
VALUES (1, 'TRUNK_IP', 32, 5060, 'Primary SIP Trunk'); INSERT INTO address (grp, ip_addr, mask, port, tag)
VALUES (1, 'BACKUP_TRUNK_IP', 32, 5060, 'Backup SIP Trunk');
route[AUTH] { /* Check if source IP is a trusted trunk (group 1) */ if (allow_source_address(1)) { xlog("L_INFO", "Trusted trunk: $si ($hdr(P-Asserted-Identity))\n"); return; /* Skip digest auth */ } /* Not a trunk — require digest authentication */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } } else { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } consume_credentials(); } /* Verify From matches authenticated user */ if ($au != $null && $au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return;
}
route[AUTH] { /* Check if source IP is a trusted trunk (group 1) */ if (allow_source_address(1)) { xlog("L_INFO", "Trusted trunk: $si ($hdr(P-Asserted-Identity))\n"); return; /* Skip digest auth */ } /* Not a trunk — require digest authentication */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } } else { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } consume_credentials(); } /* Verify From matches authenticated user */ if ($au != $null && $au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return;
}
route[AUTH] { /* Check if source IP is a trusted trunk (group 1) */ if (allow_source_address(1)) { xlog("L_INFO", "Trusted trunk: $si ($hdr(P-Asserted-Identity))\n"); return; /* Skip digest auth */ } /* Not a trunk — require digest authentication */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } } else { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } consume_credentials(); } /* Verify From matches authenticated user */ if ($au != $null && $au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return;
}
request_route { /* Sanity */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { exit; } /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* ---- AUTHENTICATE ---- */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* Route to registered user or reject */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } route(RELAY); exit;
} route[AUTH] { /* Trusted trunk IPs — no auth needed */ if (allow_source_address(1)) return; /* REGISTER → 401 challenge */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } /* All other methods → 407 challenge */ if (is_method("INVITE|MESSAGE|SUBSCRIBE|NOTIFY|REFER")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[RELAY] { t_on_failure("MANAGE_FAILURE"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n");
}
request_route { /* Sanity */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { exit; } /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* ---- AUTHENTICATE ---- */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* Route to registered user or reject */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } route(RELAY); exit;
} route[AUTH] { /* Trusted trunk IPs — no auth needed */ if (allow_source_address(1)) return; /* REGISTER → 401 challenge */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } /* All other methods → 407 challenge */ if (is_method("INVITE|MESSAGE|SUBSCRIBE|NOTIFY|REFER")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[RELAY] { t_on_failure("MANAGE_FAILURE"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n");
}
request_route { /* Sanity */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { exit; } /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* ---- AUTHENTICATE ---- */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* Route to registered user or reject */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } route(RELAY); exit;
} route[AUTH] { /* Trusted trunk IPs — no auth needed */ if (allow_source_address(1)) return; /* REGISTER → 401 challenge */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } /* All other methods → 407 challenge */ if (is_method("INVITE|MESSAGE|SUBSCRIBE|NOTIFY|REFER")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[RELAY] { t_on_failure("MANAGE_FAILURE"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n");
}
┌──────────────┐ ┌──────────┐ ┌──────────────┐
│ Phone A │ │ NAT │ │ Kamailio │
│ 192.168.1.100│────────►│ Router │────────►│ Public IP │
│ SIP Contact: │ │203.0.113 │ │ │
│ 192.168.1.100│ │ .50 │ │ │
│ SDP c= line: │ │ │ │ │
│ 192.168.1.100│ │ │ │ │
└──────────────┘ └──────────┘ └──────────────┘ Problem: SIP headers say 192.168.1.100 Packets actually came from 203.0.113.50 Kamailio can't reply to 192.168.1.100!
┌──────────────┐ ┌──────────┐ ┌──────────────┐
│ Phone A │ │ NAT │ │ Kamailio │
│ 192.168.1.100│────────►│ Router │────────►│ Public IP │
│ SIP Contact: │ │203.0.113 │ │ │
│ 192.168.1.100│ │ .50 │ │ │
│ SDP c= line: │ │ │ │ │
│ 192.168.1.100│ │ │ │ │
└──────────────┘ └──────────┘ └──────────────┘ Problem: SIP headers say 192.168.1.100 Packets actually came from 203.0.113.50 Kamailio can't reply to 192.168.1.100!
┌──────────────┐ ┌──────────┐ ┌──────────────┐
│ Phone A │ │ NAT │ │ Kamailio │
│ 192.168.1.100│────────►│ Router │────────►│ Public IP │
│ SIP Contact: │ │203.0.113 │ │ │
│ 192.168.1.100│ │ .50 │ │ │
│ SDP c= line: │ │ │ │ │
│ 192.168.1.100│ │ │ │ │
└──────────────┘ └──────────┘ └──────────────┘ Problem: SIP headers say 192.168.1.100 Packets actually came from 203.0.113.50 Kamailio can't reply to 192.168.1.100!
/* Module loading */
loadmodule "nathelper.so" /* Module parameters */
modparam("nathelper", "natping_interval", 30) # Send keepalive every 30s
modparam("nathelper", "ping_nated_only", 1) # Only ping NATed clients
modparam("nathelper", "sipping", 1) # Use SIP OPTIONS as keepalive
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
/* Module loading */
loadmodule "nathelper.so" /* Module parameters */
modparam("nathelper", "natping_interval", 30) # Send keepalive every 30s
modparam("nathelper", "ping_nated_only", 1) # Only ping NATed clients
modparam("nathelper", "sipping", 1) # Use SIP OPTIONS as keepalive
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
/* Module loading */
loadmodule "nathelper.so" /* Module parameters */
modparam("nathelper", "natping_interval", 30) # Send keepalive every 30s
modparam("nathelper", "ping_nated_only", 1) # Only ping NATed clients
modparam("nathelper", "sipping", 1) # Use SIP OPTIONS as keepalive
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
route[NATDETECT] { /* Detect NAT */ if (nat_uac_test(63)) { /* Mark this transaction as NATed */ setflag(5); /* Flag 5 = NATed */ /* Fix the Contact header to use the real source IP:port */ fix_nated_contact(); /* Force adding rport to Via (so replies go to real source port) */ force_rport(); xlog("L_INFO", "NAT detected for $fU from $si:$sp\n"); } return;
} route[NATMANAGE] { /* Fix Contact in replies too */ if (is_reply()) { if (nat_uac_test(63)) { fix_nated_contact(); } } return;
}
route[NATDETECT] { /* Detect NAT */ if (nat_uac_test(63)) { /* Mark this transaction as NATed */ setflag(5); /* Flag 5 = NATed */ /* Fix the Contact header to use the real source IP:port */ fix_nated_contact(); /* Force adding rport to Via (so replies go to real source port) */ force_rport(); xlog("L_INFO", "NAT detected for $fU from $si:$sp\n"); } return;
} route[NATMANAGE] { /* Fix Contact in replies too */ if (is_reply()) { if (nat_uac_test(63)) { fix_nated_contact(); } } return;
}
route[NATDETECT] { /* Detect NAT */ if (nat_uac_test(63)) { /* Mark this transaction as NATed */ setflag(5); /* Flag 5 = NATed */ /* Fix the Contact header to use the real source IP:port */ fix_nated_contact(); /* Force adding rport to Via (so replies go to real source port) */ force_rport(); xlog("L_INFO", "NAT detected for $fU from $si:$sp\n"); } return;
} route[NATMANAGE] { /* Fix Contact in replies too */ if (is_reply()) { if (nat_uac_test(63)) { fix_nated_contact(); } } return;
}
route[REGISTRAR] { /* NAT fix for REGISTER */ if (nat_uac_test(63)) { setflag(5); fix_nated_register(); /* Store real IP:port in location DB */ force_rport(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
}
route[REGISTRAR] { /* NAT fix for REGISTER */ if (nat_uac_test(63)) { setflag(5); fix_nated_register(); /* Store real IP:port in location DB */ force_rport(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
}
route[REGISTRAR] { /* NAT fix for REGISTER */ if (nat_uac_test(63)) { setflag(5); fix_nated_register(); /* Store real IP:port in location DB */ force_rport(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
}
# Debian 12 / Ubuntu 24.04
-weight: 500;">apt -weight: 500;">install -y rtpengine # Or from the official repository for the latest version:
# Add the RTPEngine repo (check https://dfx.at/rtpengine/ for current URLs)
-weight: 500;">apt -weight: 500;">install -y \ ngcp-rtpengine-daemon \ ngcp-rtpengine-iptables \ ngcp-rtpengine-recording-daemon # Optional: for call recording
# Debian 12 / Ubuntu 24.04
-weight: 500;">apt -weight: 500;">install -y rtpengine # Or from the official repository for the latest version:
# Add the RTPEngine repo (check https://dfx.at/rtpengine/ for current URLs)
-weight: 500;">apt -weight: 500;">install -y \ ngcp-rtpengine-daemon \ ngcp-rtpengine-iptables \ ngcp-rtpengine-recording-daemon # Optional: for call recording
# Debian 12 / Ubuntu 24.04
-weight: 500;">apt -weight: 500;">install -y rtpengine # Or from the official repository for the latest version:
# Add the RTPEngine repo (check https://dfx.at/rtpengine/ for current URLs)
-weight: 500;">apt -weight: 500;">install -y \ ngcp-rtpengine-daemon \ ngcp-rtpengine-iptables \ ngcp-rtpengine-recording-daemon # Optional: for call recording
cat > /etc/rtpengine/rtpengine.conf << 'EOF'
[rtpengine]
# Control socket — Kamailio connects here
listen-ng=127.0.0.1:2223 # Network interfaces
interface=public/YOUR_SERVER_IP # Port range for RTP
port-min=10000
port-max=20000 # Timeouts
timeout=60
silent-timeout=600
final-timeout=7200 # Logging
log-level=5
log-facility=daemon # Recording (optional)
# recording-dir=/var/spool/rtpengine
# recording-method=pcap
# recording-format=eth # TOS/DSCP marking for QoS
tos=184 # Pidfile
pidfile=/run/rtpengine/rtpengine.pid # Number of worker threads
num-threads=4
EOF
cat > /etc/rtpengine/rtpengine.conf << 'EOF'
[rtpengine]
# Control socket — Kamailio connects here
listen-ng=127.0.0.1:2223 # Network interfaces
interface=public/YOUR_SERVER_IP # Port range for RTP
port-min=10000
port-max=20000 # Timeouts
timeout=60
silent-timeout=600
final-timeout=7200 # Logging
log-level=5
log-facility=daemon # Recording (optional)
# recording-dir=/var/spool/rtpengine
# recording-method=pcap
# recording-format=eth # TOS/DSCP marking for QoS
tos=184 # Pidfile
pidfile=/run/rtpengine/rtpengine.pid # Number of worker threads
num-threads=4
EOF
cat > /etc/rtpengine/rtpengine.conf << 'EOF'
[rtpengine]
# Control socket — Kamailio connects here
listen-ng=127.0.0.1:2223 # Network interfaces
interface=public/YOUR_SERVER_IP # Port range for RTP
port-min=10000
port-max=20000 # Timeouts
timeout=60
silent-timeout=600
final-timeout=7200 # Logging
log-level=5
log-facility=daemon # Recording (optional)
# recording-dir=/var/spool/rtpengine
# recording-method=pcap
# recording-format=eth # TOS/DSCP marking for QoS
tos=184 # Pidfile
pidfile=/run/rtpengine/rtpengine.pid # Number of worker threads
num-threads=4
EOF
# Enable and -weight: 500;">start RTPEngine
-weight: 500;">systemctl -weight: 500;">enable --now rtpengine
-weight: 500;">systemctl -weight: 500;">status rtpengine # Verify it's listening
ss -ulnp | grep rtpengine
# Enable and -weight: 500;">start RTPEngine
-weight: 500;">systemctl -weight: 500;">enable --now rtpengine
-weight: 500;">systemctl -weight: 500;">status rtpengine # Verify it's listening
ss -ulnp | grep rtpengine
# Enable and -weight: 500;">start RTPEngine
-weight: 500;">systemctl -weight: 500;">enable --now rtpengine
-weight: 500;">systemctl -weight: 500;">status rtpengine # Verify it's listening
ss -ulnp | grep rtpengine
/* Module loading */
loadmodule "rtpengine.so" /* Module parameters */
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
/* Module loading */
loadmodule "rtpengine.so" /* Module parameters */
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
/* Module loading */
loadmodule "rtpengine.so" /* Module parameters */
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
route[NATMANAGE] { /* Determine RTPEngine flags based on NAT -weight: 500;">status and direction */ if (is_request()) { if (has_body("application/sdp")) { if (isflagset(5)) { /* Caller is NATed — replace private IPs, force relay */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { /* Caller is not NATed — just relay for consistency */ rtpengine_manage("ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if (isflagset(5)) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { rtpengine_manage("ICE=-weight: 500;">remove"); } } } return;
}
route[NATMANAGE] { /* Determine RTPEngine flags based on NAT -weight: 500;">status and direction */ if (is_request()) { if (has_body("application/sdp")) { if (isflagset(5)) { /* Caller is NATed — replace private IPs, force relay */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { /* Caller is not NATed — just relay for consistency */ rtpengine_manage("ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if (isflagset(5)) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { rtpengine_manage("ICE=-weight: 500;">remove"); } } } return;
}
route[NATMANAGE] { /* Determine RTPEngine flags based on NAT -weight: 500;">status and direction */ if (is_request()) { if (has_body("application/sdp")) { if (isflagset(5)) { /* Caller is NATed — replace private IPs, force relay */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { /* Caller is not NATed — just relay for consistency */ rtpengine_manage("ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if (isflagset(5)) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { rtpengine_manage("ICE=-weight: 500;">remove"); } } } return;
}
-weight: 500;">apt -weight: 500;">install -y coturn
# Configuration in /etc/turnserver.conf
-weight: 500;">apt -weight: 500;">install -y coturn
# Configuration in /etc/turnserver.conf
-weight: 500;">apt -weight: 500;">install -y coturn
# Configuration in /etc/turnserver.conf
#!KAMAILIO /* ===== Global Parameters ===== */
listen=udp:YOUR_SERVER_IP:5060 advertise YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060 advertise YOUR_SERVER_IP:5060 debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
server_signature=no
force_rport=yes /* Always add rport */ /* ===== Module Loading ===== */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so" /* ===== Module Parameters ===== */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio") modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600) modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)") modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* ===== Routing Logic ===== */
request_route { /* Sanity checks */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { exit; } /* NAT detection */ route(NATDETECT); /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE: $fU → $tU\n"); /* Release RTPEngine session */ rtpengine_delete(); } if (is_method("INVITE|UPDATE|ACK")) { route(NATMANAGE); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Authenticate */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { route(REGISTRAR); exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE — lookup and route */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } /* NAT manage for initial INVITE */ route(NATMANAGE); route(RELAY); exit;
} route[RELAY] { t_on_reply("MANAGE_REPLY"); t_on_failure("MANAGE_FAILURE"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } if (is_method("INVITE|MESSAGE|SUBSCRIBE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[REGISTRAR] { if (nat_uac_test(63)) { setflag(5); fix_nated_register(); force_rport(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
} route[NATDETECT] { if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } return;
} route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { if (isflagset(5)) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { rtpengine_manage("ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
} onreply_route[MANAGE_REPLY] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE); }
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n"); if ($T_reply_code == 486 || $T_reply_code == 408) { xlog("L_INFO", "User $rU busy or timeout\n"); }
}
#!KAMAILIO /* ===== Global Parameters ===== */
listen=udp:YOUR_SERVER_IP:5060 advertise YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060 advertise YOUR_SERVER_IP:5060 debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
server_signature=no
force_rport=yes /* Always add rport */ /* ===== Module Loading ===== */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so" /* ===== Module Parameters ===== */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio") modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600) modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)") modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* ===== Routing Logic ===== */
request_route { /* Sanity checks */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { exit; } /* NAT detection */ route(NATDETECT); /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE: $fU → $tU\n"); /* Release RTPEngine session */ rtpengine_delete(); } if (is_method("INVITE|UPDATE|ACK")) { route(NATMANAGE); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Authenticate */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { route(REGISTRAR); exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE — lookup and route */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } /* NAT manage for initial INVITE */ route(NATMANAGE); route(RELAY); exit;
} route[RELAY] { t_on_reply("MANAGE_REPLY"); t_on_failure("MANAGE_FAILURE"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } if (is_method("INVITE|MESSAGE|SUBSCRIBE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[REGISTRAR] { if (nat_uac_test(63)) { setflag(5); fix_nated_register(); force_rport(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
} route[NATDETECT] { if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } return;
} route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { if (isflagset(5)) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { rtpengine_manage("ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
} onreply_route[MANAGE_REPLY] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE); }
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n"); if ($T_reply_code == 486 || $T_reply_code == 408) { xlog("L_INFO", "User $rU busy or timeout\n"); }
}
#!KAMAILIO /* ===== Global Parameters ===== */
listen=udp:YOUR_SERVER_IP:5060 advertise YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060 advertise YOUR_SERVER_IP:5060 debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
server_signature=no
force_rport=yes /* Always add rport */ /* ===== Module Loading ===== */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so" /* ===== Module Parameters ===== */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio") modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600) modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)") modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* ===== Routing Logic ===== */
request_route { /* Sanity checks */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { exit; } /* NAT detection */ route(NATDETECT); /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE: $fU → $tU\n"); /* Release RTPEngine session */ rtpengine_delete(); } if (is_method("INVITE|UPDATE|ACK")) { route(NATMANAGE); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Authenticate */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { route(REGISTRAR); exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE — lookup and route */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } /* NAT manage for initial INVITE */ route(NATMANAGE); route(RELAY); exit;
} route[RELAY] { t_on_reply("MANAGE_REPLY"); t_on_failure("MANAGE_FAILURE"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } if (is_method("INVITE|MESSAGE|SUBSCRIBE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[REGISTRAR] { if (nat_uac_test(63)) { setflag(5); fix_nated_register(); force_rport(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
} route[NATDETECT] { if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } return;
} route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { if (isflagset(5)) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { rtpengine_manage("ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
} onreply_route[MANAGE_REPLY] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE); }
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n"); if ($T_reply_code == 486 || $T_reply_code == 408) { xlog("L_INFO", "User $rU busy or timeout\n"); }
}
/* Module loading */
loadmodule "tls.so" /* Global: add TLS listen address */
listen=tls:YOUR_SERVER_IP:5061 /* TLS module parameters */
modparam("tls", "config", "/etc/kamailio/tls.cfg")
modparam("tls", "tls_force_run", 1) /* Start even if cert is missing */ /* Alternative: inline TLS config (instead of separate tls.cfg) */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "ca_list", "/etc/kamailio/certs/chain.pem")
modparam("tls", "tls_method", "TLSv1.2+") /* Minimum TLS 1.2 */
modparam("tls", "verify_certificate", 0) /* 0=don't require client cert */
modparam("tls", "require_certificate", 0) /* 0=allow non-TLS clients */
modparam("tls", "connection_timeout", 60)
/* Module loading */
loadmodule "tls.so" /* Global: add TLS listen address */
listen=tls:YOUR_SERVER_IP:5061 /* TLS module parameters */
modparam("tls", "config", "/etc/kamailio/tls.cfg")
modparam("tls", "tls_force_run", 1) /* Start even if cert is missing */ /* Alternative: inline TLS config (instead of separate tls.cfg) */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "ca_list", "/etc/kamailio/certs/chain.pem")
modparam("tls", "tls_method", "TLSv1.2+") /* Minimum TLS 1.2 */
modparam("tls", "verify_certificate", 0) /* 0=don't require client cert */
modparam("tls", "require_certificate", 0) /* 0=allow non-TLS clients */
modparam("tls", "connection_timeout", 60)
/* Module loading */
loadmodule "tls.so" /* Global: add TLS listen address */
listen=tls:YOUR_SERVER_IP:5061 /* TLS module parameters */
modparam("tls", "config", "/etc/kamailio/tls.cfg")
modparam("tls", "tls_force_run", 1) /* Start even if cert is missing */ /* Alternative: inline TLS config (instead of separate tls.cfg) */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "ca_list", "/etc/kamailio/certs/chain.pem")
modparam("tls", "tls_method", "TLSv1.2+") /* Minimum TLS 1.2 */
modparam("tls", "verify_certificate", 0) /* 0=don't require client cert */
modparam("tls", "require_certificate", 0) /* 0=allow non-TLS clients */
modparam("tls", "connection_timeout", 60)
# /etc/kamailio/tls.cfg [server:default]
method = TLSv1.2+
verify_certificate = no
require_certificate = no
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/chain.pem # Specific config for a trusted peer
[server:TRUNK_IP:5061]
method = TLSv1.2+
verify_certificate = yes
require_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/trunk-ca.pem [client:default]
method = TLSv1.2+
verify_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/chain.pem
# /etc/kamailio/tls.cfg [server:default]
method = TLSv1.2+
verify_certificate = no
require_certificate = no
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/chain.pem # Specific config for a trusted peer
[server:TRUNK_IP:5061]
method = TLSv1.2+
verify_certificate = yes
require_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/trunk-ca.pem [client:default]
method = TLSv1.2+
verify_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/chain.pem
# /etc/kamailio/tls.cfg [server:default]
method = TLSv1.2+
verify_certificate = no
require_certificate = no
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/chain.pem # Specific config for a trusted peer
[server:TRUNK_IP:5061]
method = TLSv1.2+
verify_certificate = yes
require_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/trunk-ca.pem [client:default]
method = TLSv1.2+
verify_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/chain.pem
# Install certbot
-weight: 500;">apt -weight: 500;">install -y certbot # Obtain certificate (standalone mode — -weight: 500;">stop Kamailio briefly)
-weight: 500;">systemctl -weight: 500;">stop kamailio
certbot certonly --standalone -d YOUR_DOMAIN # Certificate files are at:
# /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem
# /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem
# /etc/letsencrypt/live/YOUR_DOMAIN/chain.pem # Create symlinks for Kamailio
mkdir -p /etc/kamailio/certs
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem /etc/kamailio/certs/fullchain.pem
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem /etc/kamailio/certs/privkey.pem
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/chain.pem /etc/kamailio/certs/chain.pem # Set permissions
chown -R kamailio:kamailio /etc/kamailio/certs/ # Auto-renewal with Kamailio reload
cat > /etc/letsencrypt/renewal-hooks/post/kamailio.sh << 'HOOK'
#!/bin/bash
-weight: 500;">systemctl reload kamailio
HOOK
chmod +x /etc/letsencrypt/renewal-hooks/post/kamailio.sh -weight: 500;">systemctl -weight: 500;">start kamailio
# Install certbot
-weight: 500;">apt -weight: 500;">install -y certbot # Obtain certificate (standalone mode — -weight: 500;">stop Kamailio briefly)
-weight: 500;">systemctl -weight: 500;">stop kamailio
certbot certonly --standalone -d YOUR_DOMAIN # Certificate files are at:
# /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem
# /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem
# /etc/letsencrypt/live/YOUR_DOMAIN/chain.pem # Create symlinks for Kamailio
mkdir -p /etc/kamailio/certs
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem /etc/kamailio/certs/fullchain.pem
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem /etc/kamailio/certs/privkey.pem
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/chain.pem /etc/kamailio/certs/chain.pem # Set permissions
chown -R kamailio:kamailio /etc/kamailio/certs/ # Auto-renewal with Kamailio reload
cat > /etc/letsencrypt/renewal-hooks/post/kamailio.sh << 'HOOK'
#!/bin/bash
-weight: 500;">systemctl reload kamailio
HOOK
chmod +x /etc/letsencrypt/renewal-hooks/post/kamailio.sh -weight: 500;">systemctl -weight: 500;">start kamailio
# Install certbot
-weight: 500;">apt -weight: 500;">install -y certbot # Obtain certificate (standalone mode — -weight: 500;">stop Kamailio briefly)
-weight: 500;">systemctl -weight: 500;">stop kamailio
certbot certonly --standalone -d YOUR_DOMAIN # Certificate files are at:
# /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem
# /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem
# /etc/letsencrypt/live/YOUR_DOMAIN/chain.pem # Create symlinks for Kamailio
mkdir -p /etc/kamailio/certs
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem /etc/kamailio/certs/fullchain.pem
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem /etc/kamailio/certs/privkey.pem
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/chain.pem /etc/kamailio/certs/chain.pem # Set permissions
chown -R kamailio:kamailio /etc/kamailio/certs/ # Auto-renewal with Kamailio reload
cat > /etc/letsencrypt/renewal-hooks/post/kamailio.sh << 'HOOK'
#!/bin/bash
-weight: 500;">systemctl reload kamailio
HOOK
chmod +x /etc/letsencrypt/renewal-hooks/post/kamailio.sh -weight: 500;">systemctl -weight: 500;">start kamailio
mkdir -p /etc/kamailio/certs
cd /etc/kamailio/certs # Generate CA
openssl genrsa -out ca-key.pem 4096
openssl req -x509 -new -nodes -key ca-key.pem -sha256 -days 3650 \ -out ca-cert.pem \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=SIP-CA" # Generate server certificate
openssl genrsa -out privkey.pem 2048
openssl req -new -key privkey.pem -out server.csr \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=YOUR_DOMAIN" # Sign with CA
openssl x509 -req -in server.csr -CA ca-cert.pem -CAkey ca-key.pem \ -CAcreateserial -out fullchain.pem -days 365 -sha256 # Copy CA cert as chain
cp ca-cert.pem chain.pem # Set ownership
chown -R kamailio:kamailio /etc/kamailio/certs/
chmod 600 /etc/kamailio/certs/privkey.pem
mkdir -p /etc/kamailio/certs
cd /etc/kamailio/certs # Generate CA
openssl genrsa -out ca-key.pem 4096
openssl req -x509 -new -nodes -key ca-key.pem -sha256 -days 3650 \ -out ca-cert.pem \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=SIP-CA" # Generate server certificate
openssl genrsa -out privkey.pem 2048
openssl req -new -key privkey.pem -out server.csr \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=YOUR_DOMAIN" # Sign with CA
openssl x509 -req -in server.csr -CA ca-cert.pem -CAkey ca-key.pem \ -CAcreateserial -out fullchain.pem -days 365 -sha256 # Copy CA cert as chain
cp ca-cert.pem chain.pem # Set ownership
chown -R kamailio:kamailio /etc/kamailio/certs/
chmod 600 /etc/kamailio/certs/privkey.pem
mkdir -p /etc/kamailio/certs
cd /etc/kamailio/certs # Generate CA
openssl genrsa -out ca-key.pem 4096
openssl req -x509 -new -nodes -key ca-key.pem -sha256 -days 3650 \ -out ca-cert.pem \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=SIP-CA" # Generate server certificate
openssl genrsa -out privkey.pem 2048
openssl req -new -key privkey.pem -out server.csr \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=YOUR_DOMAIN" # Sign with CA
openssl x509 -req -in server.csr -CA ca-cert.pem -CAkey ca-key.pem \ -CAcreateserial -out fullchain.pem -days 365 -sha256 # Copy CA cert as chain
cp ca-cert.pem chain.pem # Set ownership
chown -R kamailio:kamailio /etc/kamailio/certs/
chmod 600 /etc/kamailio/certs/privkey.pem
/* In tls.cfg — require client certificates */
[server:default]
method = TLSv1.2+
verify_certificate = yes
require_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/ca-cert.pem
/* In tls.cfg — require client certificates */
[server:default]
method = TLSv1.2+
verify_certificate = yes
require_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/ca-cert.pem
/* In tls.cfg — require client certificates */
[server:default]
method = TLSv1.2+
verify_certificate = yes
require_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/ca-cert.pem
if ($proto == "tls") { xlog("L_INFO", "TLS peer: $tls_peer_subject\n"); xlog("L_INFO", "TLS peer CN: $tls_peer_subject_cn\n"); xlog("L_INFO", "TLS peer verified: $tls_peer_verified\n"); if ($tls_peer_verified != 1) { sl_send_reply(403, "Certificate Verification Failed"); exit; }
}
if ($proto == "tls") { xlog("L_INFO", "TLS peer: $tls_peer_subject\n"); xlog("L_INFO", "TLS peer CN: $tls_peer_subject_cn\n"); xlog("L_INFO", "TLS peer verified: $tls_peer_verified\n"); if ($tls_peer_verified != 1) { sl_send_reply(403, "Certificate Verification Failed"); exit; }
}
if ($proto == "tls") { xlog("L_INFO", "TLS peer: $tls_peer_subject\n"); xlog("L_INFO", "TLS peer CN: $tls_peer_subject_cn\n"); xlog("L_INFO", "TLS peer verified: $tls_peer_verified\n"); if ($tls_peer_verified != 1) { sl_send_reply(403, "Certificate Verification Failed"); exit; }
}
/* In NATMANAGE route — encrypt media */
route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { if ($proto == "tls" || $proto == "wss") { /* TLS/WSS client — use SRTP on client side, plain RTP to backend */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove SDES-off RTP/SAVP"); } else { /* Plain SIP — no SRTP */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
}
/* In NATMANAGE route — encrypt media */
route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { if ($proto == "tls" || $proto == "wss") { /* TLS/WSS client — use SRTP on client side, plain RTP to backend */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove SDES-off RTP/SAVP"); } else { /* Plain SIP — no SRTP */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
}
/* In NATMANAGE route — encrypt media */
route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { if ($proto == "tls" || $proto == "wss") { /* TLS/WSS client — use SRTP on client side, plain RTP to backend */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove SDES-off RTP/SAVP"); } else { /* Plain SIP — no SRTP */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
}
loadmodule "pike.so"
modparam("pike", "sampling_time_unit", 2) /* 2-second window */
modparam("pike", "reqs_density_per_unit", 30) /* Max 30 requests per window */
modparam("pike", "remove_latency", 4) /* Block for 4 windows (8 seconds) */
loadmodule "pike.so"
modparam("pike", "sampling_time_unit", 2) /* 2-second window */
modparam("pike", "reqs_density_per_unit", 30) /* Max 30 requests per window */
modparam("pike", "remove_latency", 4) /* Block for 4 windows (8 seconds) */
loadmodule "pike.so"
modparam("pike", "sampling_time_unit", 2) /* 2-second window */
modparam("pike", "reqs_density_per_unit", 30) /* Max 30 requests per window */
modparam("pike", "remove_latency", 4) /* Block for 4 windows (8 seconds) */
request_route { /* Anti-flood — check before anything else */ if (src_ip != myself) { if (!pike_check_req()) { xlog("L_ALERT", "PIKE: Blocking flood from $si\n"); /* Silently drop — don't send reply (wastes resources) */ exit; } } /* ... rest of routing ... */
}
request_route { /* Anti-flood — check before anything else */ if (src_ip != myself) { if (!pike_check_req()) { xlog("L_ALERT", "PIKE: Blocking flood from $si\n"); /* Silently drop — don't send reply (wastes resources) */ exit; } } /* ... rest of routing ... */
}
request_route { /* Anti-flood — check before anything else */ if (src_ip != myself) { if (!pike_check_req()) { xlog("L_ALERT", "PIKE: Blocking flood from $si\n"); /* Silently drop — don't send reply (wastes resources) */ exit; } } /* ... rest of routing ... */
}
loadmodule "htable.so" /* Define a blacklist table */
modparam("htable", "htable", "ipban=>size=8;autoexpire=3600;")
/* size=8: 2^8 = 256 slots, autoexpire=3600: entries expire after 1 hour */ /* Define a counter table for rate limiting per user */
modparam("htable", "htable", "ratelimit=>size=10;autoexpire=60;")
loadmodule "htable.so" /* Define a blacklist table */
modparam("htable", "htable", "ipban=>size=8;autoexpire=3600;")
/* size=8: 2^8 = 256 slots, autoexpire=3600: entries expire after 1 hour */ /* Define a counter table for rate limiting per user */
modparam("htable", "htable", "ratelimit=>size=10;autoexpire=60;")
loadmodule "htable.so" /* Define a blacklist table */
modparam("htable", "htable", "ipban=>size=8;autoexpire=3600;")
/* size=8: 2^8 = 256 slots, autoexpire=3600: entries expire after 1 hour */ /* Define a counter table for rate limiting per user */
modparam("htable", "htable", "ratelimit=>size=10;autoexpire=60;")
request_route { /* Check blacklist */ if ($sht(ipban=>$si) != $null) { xlog("L_WARN", "Blocked blacklisted IP: $si\n"); exit; /* Silently drop */ } /* After pike blocks, add to blacklist */ if (src_ip != myself) { if (!pike_check_req()) { $sht(ipban=>$si) = 1; xlog("L_ALERT", "BLACKLISTED: $si (pike trigger)\n"); exit; } } /* Per-user rate limiting (max 10 INVITE per minute) */ if (is_method("INVITE") && !has_totag()) { $sht(ratelimit=>$fU) = $sht(ratelimit=>$fU) + 1; if ($sht(ratelimit=>$fU) > 10) { sl_send_reply(429, "Too Many Requests"); exit; } }
}
request_route { /* Check blacklist */ if ($sht(ipban=>$si) != $null) { xlog("L_WARN", "Blocked blacklisted IP: $si\n"); exit; /* Silently drop */ } /* After pike blocks, add to blacklist */ if (src_ip != myself) { if (!pike_check_req()) { $sht(ipban=>$si) = 1; xlog("L_ALERT", "BLACKLISTED: $si (pike trigger)\n"); exit; } } /* Per-user rate limiting (max 10 INVITE per minute) */ if (is_method("INVITE") && !has_totag()) { $sht(ratelimit=>$fU) = $sht(ratelimit=>$fU) + 1; if ($sht(ratelimit=>$fU) > 10) { sl_send_reply(429, "Too Many Requests"); exit; } }
}
request_route { /* Check blacklist */ if ($sht(ipban=>$si) != $null) { xlog("L_WARN", "Blocked blacklisted IP: $si\n"); exit; /* Silently drop */ } /* After pike blocks, add to blacklist */ if (src_ip != myself) { if (!pike_check_req()) { $sht(ipban=>$si) = 1; xlog("L_ALERT", "BLACKLISTED: $si (pike trigger)\n"); exit; } } /* Per-user rate limiting (max 10 INVITE per minute) */ if (is_method("INVITE") && !has_totag()) { $sht(ratelimit=>$fU) = $sht(ratelimit=>$fU) + 1; if ($sht(ratelimit=>$fU) > 10) { sl_send_reply(429, "Too Many Requests"); exit; } }
}
/* In the AUTH route — log failures */
route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { /* Log for fail2ban — note the specific format */ xlog("L_WARN", "AUTH_FAILED: method=REGISTER user=$fU from=$si\n"); www_challenge("YOUR_DOMAIN", 1); exit; } }
}
/* In the AUTH route — log failures */
route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { /* Log for fail2ban — note the specific format */ xlog("L_WARN", "AUTH_FAILED: method=REGISTER user=$fU from=$si\n"); www_challenge("YOUR_DOMAIN", 1); exit; } }
}
/* In the AUTH route — log failures */
route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { /* Log for fail2ban — note the specific format */ xlog("L_WARN", "AUTH_FAILED: method=REGISTER user=$fU from=$si\n"); www_challenge("YOUR_DOMAIN", 1); exit; } }
}
cat > /etc/fail2ban/filter.d/kamailio.conf << 'EOF'
[Definition]
failregex = AUTH_FAILED:.*from=<HOST>
ignoreregex =
EOF
cat > /etc/fail2ban/filter.d/kamailio.conf << 'EOF'
[Definition]
failregex = AUTH_FAILED:.*from=<HOST>
ignoreregex =
EOF
cat > /etc/fail2ban/filter.d/kamailio.conf << 'EOF'
[Definition]
failregex = AUTH_FAILED:.*from=<HOST>
ignoreregex =
EOF
cat > /etc/fail2ban/jail.d/kamailio.conf << 'EOF'
[kamailio]
enabled = true
filter = kamailio
logpath = /var/log/kamailio/kamailio.log
maxretry = 5
findtime = 300
bantime = 3600
action = iptables-allports[name=kamailio, protocol=all]
EOF
cat > /etc/fail2ban/jail.d/kamailio.conf << 'EOF'
[kamailio]
enabled = true
filter = kamailio
logpath = /var/log/kamailio/kamailio.log
maxretry = 5
findtime = 300
bantime = 3600
action = iptables-allports[name=kamailio, protocol=all]
EOF
cat > /etc/fail2ban/jail.d/kamailio.conf << 'EOF'
[kamailio]
enabled = true
filter = kamailio
logpath = /var/log/kamailio/kamailio.log
maxretry = 5
findtime = 300
bantime = 3600
action = iptables-allports[name=kamailio, protocol=all]
EOF
# Make sure Kamailio logs to a file (rsyslog)
cat > /etc/rsyslog.d/kamailio.conf << 'EOF'
local0.* /var/log/kamailio/kamailio.log
EOF mkdir -p /var/log/kamailio
-weight: 500;">systemctl -weight: 500;">restart rsyslog
-weight: 500;">systemctl -weight: 500;">restart fail2ban
# Make sure Kamailio logs to a file (rsyslog)
cat > /etc/rsyslog.d/kamailio.conf << 'EOF'
local0.* /var/log/kamailio/kamailio.log
EOF mkdir -p /var/log/kamailio
-weight: 500;">systemctl -weight: 500;">restart rsyslog
-weight: 500;">systemctl -weight: 500;">restart fail2ban
# Make sure Kamailio logs to a file (rsyslog)
cat > /etc/rsyslog.d/kamailio.conf << 'EOF'
local0.* /var/log/kamailio/kamailio.log
EOF mkdir -p /var/log/kamailio
-weight: 500;">systemctl -weight: 500;">restart rsyslog
-weight: 500;">systemctl -weight: 500;">restart fail2ban
┌──────────────┐ │ Kamailio │ │ (Dispatcher)│ └──────┬───────┘ │ ┌────────────┼────────────┐ │ │ │ ┌────▼───┐ ┌────▼───┐ ┌────▼───┐ │Asterisk│ │Asterisk│ │Asterisk│ │ #1 │ │ #2 │ │ #3 │ └────────┘ └────────┘ └────────┘
┌──────────────┐ │ Kamailio │ │ (Dispatcher)│ └──────┬───────┘ │ ┌────────────┼────────────┐ │ │ │ ┌────▼───┐ ┌────▼───┐ ┌────▼───┐ │Asterisk│ │Asterisk│ │Asterisk│ │ #1 │ │ #2 │ │ #3 │ └────────┘ └────────┘ └────────┘
┌──────────────┐ │ Kamailio │ │ (Dispatcher)│ └──────┬───────┘ │ ┌────────────┼────────────┐ │ │ │ ┌────▼───┐ ┌────▼───┐ ┌────▼───┐ │Asterisk│ │Asterisk│ │Asterisk│ │ #1 │ │ #2 │ │ #3 │ └────────┘ └────────┘ └────────┘
loadmodule "dispatcher.so" modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "table_name", "dispatcher")
modparam("dispatcher", "flags", 2) /* Failover support */
modparam("dispatcher", "ds_ping_interval", 10) /* Health check every 10s */
modparam("dispatcher", "ds_ping_method", "OPTIONS") /* Use SIP OPTIONS */
modparam("dispatcher", "ds_probing_threshold", 3) /* 3 failures = down */
modparam("dispatcher", "ds_inactive_threshold", 3) /* 3 successes = up */
modparam("dispatcher", "ds_probing_mode", 1) /* Probe all destinations */
modparam("dispatcher", "ds_ping_from", "sip:kamailio@YOUR_DOMAIN")
modparam("dispatcher", "ds_ping_reply_codes", "class2;class3;class4")
loadmodule "dispatcher.so" modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "table_name", "dispatcher")
modparam("dispatcher", "flags", 2) /* Failover support */
modparam("dispatcher", "ds_ping_interval", 10) /* Health check every 10s */
modparam("dispatcher", "ds_ping_method", "OPTIONS") /* Use SIP OPTIONS */
modparam("dispatcher", "ds_probing_threshold", 3) /* 3 failures = down */
modparam("dispatcher", "ds_inactive_threshold", 3) /* 3 successes = up */
modparam("dispatcher", "ds_probing_mode", 1) /* Probe all destinations */
modparam("dispatcher", "ds_ping_from", "sip:kamailio@YOUR_DOMAIN")
modparam("dispatcher", "ds_ping_reply_codes", "class2;class3;class4")
loadmodule "dispatcher.so" modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "table_name", "dispatcher")
modparam("dispatcher", "flags", 2) /* Failover support */
modparam("dispatcher", "ds_ping_interval", 10) /* Health check every 10s */
modparam("dispatcher", "ds_ping_method", "OPTIONS") /* Use SIP OPTIONS */
modparam("dispatcher", "ds_probing_threshold", 3) /* 3 failures = down */
modparam("dispatcher", "ds_inactive_threshold", 3) /* 3 successes = up */
modparam("dispatcher", "ds_probing_mode", 1) /* Probe all destinations */
modparam("dispatcher", "ds_ping_from", "sip:kamailio@YOUR_DOMAIN")
modparam("dispatcher", "ds_ping_reply_codes", "class2;class3;class4")
/* dispatcher table: setid, destination, flags, priority, attrs, description */ -- Set 1: Asterisk backends (Round-Robin)
INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.10:5060', 0, 0, 'weight=50;duid=ast1', 'Asterisk 1'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.11:5060', 0, 0, 'weight=30;duid=ast2', 'Asterisk 2'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.12:5060', 0, 0, 'weight=20;duid=ast3', 'Asterisk 3'); -- Set 2: Media servers (Priority-based failover)
INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (2, 'sip:10.0.0.20:5060', 0, 0, 'duid=media1', 'Primary Media'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (2, 'sip:10.0.0.21:5060', 0, 1, 'duid=media2', 'Backup Media');
/* dispatcher table: setid, destination, flags, priority, attrs, description */ -- Set 1: Asterisk backends (Round-Robin)
INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.10:5060', 0, 0, 'weight=50;duid=ast1', 'Asterisk 1'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.11:5060', 0, 0, 'weight=30;duid=ast2', 'Asterisk 2'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.12:5060', 0, 0, 'weight=20;duid=ast3', 'Asterisk 3'); -- Set 2: Media servers (Priority-based failover)
INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (2, 'sip:10.0.0.20:5060', 0, 0, 'duid=media1', 'Primary Media'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (2, 'sip:10.0.0.21:5060', 0, 1, 'duid=media2', 'Backup Media');
/* dispatcher table: setid, destination, flags, priority, attrs, description */ -- Set 1: Asterisk backends (Round-Robin)
INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.10:5060', 0, 0, 'weight=50;duid=ast1', 'Asterisk 1'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.11:5060', 0, 0, 'weight=30;duid=ast2', 'Asterisk 2'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.12:5060', 0, 0, 'weight=20;duid=ast3', 'Asterisk 3'); -- Set 2: Media servers (Priority-based failover)
INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (2, 'sip:10.0.0.20:5060', 0, 0, 'duid=media1', 'Primary Media'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (2, 'sip:10.0.0.21:5060', 0, 1, 'duid=media2', 'Backup Media');
# Reload dispatcher data
kamcmd dispatcher.reload
# Reload dispatcher data
kamcmd dispatcher.reload
# Reload dispatcher data
kamcmd dispatcher.reload
modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list")
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list")
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list")
modparam("dispatcher", "ds_probing_mode", 1)
# Format: setid destination flags priority attrs
# Set 1: Asterisk backends
1 sip:10.0.0.10:5060 0 0 weight=50;duid=ast1
1 sip:10.0.0.11:5060 0 0 weight=30;duid=ast2
1 sip:10.0.0.12:5060 0 0 weight=20;duid=ast3 # Set 2: Media servers (priority failover)
2 sip:10.0.0.20:5060 0 0 duid=media1
2 sip:10.0.0.21:5060 0 1 duid=media2
# Format: setid destination flags priority attrs
# Set 1: Asterisk backends
1 sip:10.0.0.10:5060 0 0 weight=50;duid=ast1
1 sip:10.0.0.11:5060 0 0 weight=30;duid=ast2
1 sip:10.0.0.12:5060 0 0 weight=20;duid=ast3 # Set 2: Media servers (priority failover)
2 sip:10.0.0.20:5060 0 0 duid=media1
2 sip:10.0.0.21:5060 0 1 duid=media2
# Format: setid destination flags priority attrs
# Set 1: Asterisk backends
1 sip:10.0.0.10:5060 0 0 weight=50;duid=ast1
1 sip:10.0.0.11:5060 0 0 weight=30;duid=ast2
1 sip:10.0.0.12:5060 0 0 weight=20;duid=ast3 # Set 2: Media servers (priority failover)
2 sip:10.0.0.20:5060 0 0 duid=media1
2 sip:10.0.0.21:5060 0 1 duid=media2
route[DISPATCH] { /* Select a backend from set 1 using round-robin (algorithm 0) */ if (!ds_select_dst(1, 0)) { /* No backends available */ xlog("L_ERR", "DISPATCHER: No backends available in set 1\n"); sl_send_reply(503, "Service Unavailable"); exit; } xlog("L_INFO", "DISPATCH: Routing $rU to $du (set 1)\n"); /* Enable failover — if this backend fails, try the next */ t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit;
} failure_route[DISPATCH_FAILURE] { if (t_is_canceled()) exit; /* Mark the failed destination as inactive */ if (t_check_status("5[0-9][0-9]") || t_check_status("408")) { xlog("L_WARN", "DISPATCH: Backend $du failed ($T_reply_code), trying next\n"); ds_mark_dst("ip"); /* Mark as inactive (probing) */ /* Try the next backend in the set */ if (ds_next_dst()) { xlog("L_INFO", "DISPATCH: Failover to $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit; } /* All backends down */ xlog("L_ERR", "DISPATCH: All backends in set 1 are down\n"); t_reply(503, "All Backends Unavailable"); exit; }
}
route[DISPATCH] { /* Select a backend from set 1 using round-robin (algorithm 0) */ if (!ds_select_dst(1, 0)) { /* No backends available */ xlog("L_ERR", "DISPATCHER: No backends available in set 1\n"); sl_send_reply(503, "Service Unavailable"); exit; } xlog("L_INFO", "DISPATCH: Routing $rU to $du (set 1)\n"); /* Enable failover — if this backend fails, try the next */ t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit;
} failure_route[DISPATCH_FAILURE] { if (t_is_canceled()) exit; /* Mark the failed destination as inactive */ if (t_check_status("5[0-9][0-9]") || t_check_status("408")) { xlog("L_WARN", "DISPATCH: Backend $du failed ($T_reply_code), trying next\n"); ds_mark_dst("ip"); /* Mark as inactive (probing) */ /* Try the next backend in the set */ if (ds_next_dst()) { xlog("L_INFO", "DISPATCH: Failover to $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit; } /* All backends down */ xlog("L_ERR", "DISPATCH: All backends in set 1 are down\n"); t_reply(503, "All Backends Unavailable"); exit; }
}
route[DISPATCH] { /* Select a backend from set 1 using round-robin (algorithm 0) */ if (!ds_select_dst(1, 0)) { /* No backends available */ xlog("L_ERR", "DISPATCHER: No backends available in set 1\n"); sl_send_reply(503, "Service Unavailable"); exit; } xlog("L_INFO", "DISPATCH: Routing $rU to $du (set 1)\n"); /* Enable failover — if this backend fails, try the next */ t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit;
} failure_route[DISPATCH_FAILURE] { if (t_is_canceled()) exit; /* Mark the failed destination as inactive */ if (t_check_status("5[0-9][0-9]") || t_check_status("408")) { xlog("L_WARN", "DISPATCH: Backend $du failed ($T_reply_code), trying next\n"); ds_mark_dst("ip"); /* Mark as inactive (probing) */ /* Try the next backend in the set */ if (ds_next_dst()) { xlog("L_INFO", "DISPATCH: Failover to $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit; } /* All backends down */ xlog("L_ERR", "DISPATCH: All backends in set 1 are down\n"); t_reply(503, "All Backends Unavailable"); exit; }
}
Backend UP: ─── OPTIONS ──► Backend ◄── 200 OK ──── Backend (Counter: successes = 0 → stays UP) Backend goes DOWN: ─── OPTIONS ──► Backend ◄── [timeout] ── Backend (Counter: failures = 1, 2, 3 → marked INACTIVE when threshold reached) Backend recovers: ─── OPTIONS ──► Backend ◄── 200 OK ──── Backend (Counter: successes = 1, 2, 3 → marked ACTIVE when threshold reached)
Backend UP: ─── OPTIONS ──► Backend ◄── 200 OK ──── Backend (Counter: successes = 0 → stays UP) Backend goes DOWN: ─── OPTIONS ──► Backend ◄── [timeout] ── Backend (Counter: failures = 1, 2, 3 → marked INACTIVE when threshold reached) Backend recovers: ─── OPTIONS ──► Backend ◄── 200 OK ──── Backend (Counter: successes = 1, 2, 3 → marked ACTIVE when threshold reached)
Backend UP: ─── OPTIONS ──► Backend ◄── 200 OK ──── Backend (Counter: successes = 0 → stays UP) Backend goes DOWN: ─── OPTIONS ──► Backend ◄── [timeout] ── Backend (Counter: failures = 1, 2, 3 → marked INACTIVE when threshold reached) Backend recovers: ─── OPTIONS ──► Backend ◄── 200 OK ──── Backend (Counter: successes = 1, 2, 3 → marked ACTIVE when threshold reached)
# Show all destinations and their -weight: 500;">status
kamcmd dispatcher.list # Output format:
# SET DEST FLAGS PRIORITY ATTRS
# 1 sip:10.0.0.10:5060 AP 0 weight=50
# 1 sip:10.0.0.11:5060 AP 0 weight=30
# 1 sip:10.0.0.12:5060 IP 0 weight=20 # Flags: A=Active, I=Inactive, P=Probing, D=Disabled, X=Deleted # Manually set destination state
kamcmd dispatcher.set_state ip 1 sip:10.0.0.12:5060 # Set to Inactive/Probing
kamcmd dispatcher.set_state ap 1 sip:10.0.0.12:5060 # Set to Active/Probing
# Show all destinations and their -weight: 500;">status
kamcmd dispatcher.list # Output format:
# SET DEST FLAGS PRIORITY ATTRS
# 1 sip:10.0.0.10:5060 AP 0 weight=50
# 1 sip:10.0.0.11:5060 AP 0 weight=30
# 1 sip:10.0.0.12:5060 IP 0 weight=20 # Flags: A=Active, I=Inactive, P=Probing, D=Disabled, X=Deleted # Manually set destination state
kamcmd dispatcher.set_state ip 1 sip:10.0.0.12:5060 # Set to Inactive/Probing
kamcmd dispatcher.set_state ap 1 sip:10.0.0.12:5060 # Set to Active/Probing
# Show all destinations and their -weight: 500;">status
kamcmd dispatcher.list # Output format:
# SET DEST FLAGS PRIORITY ATTRS
# 1 sip:10.0.0.10:5060 AP 0 weight=50
# 1 sip:10.0.0.11:5060 AP 0 weight=30
# 1 sip:10.0.0.12:5060 IP 0 weight=20 # Flags: A=Active, I=Inactive, P=Probing, D=Disabled, X=Deleted # Manually set destination state
kamcmd dispatcher.set_state ip 1 sip:10.0.0.12:5060 # Set to Inactive/Probing
kamcmd dispatcher.set_state ap 1 sip:10.0.0.12:5060 # Set to Active/Probing
#!KAMAILIO
#!define WITH_DISPATCHER listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
server_signature=no /* Modules */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "dispatcher.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "dialog.so" /* Module parameters */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio") modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600) modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) /* Dispatcher — load balancer */
modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "ds_ping_interval", 10)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3)
modparam("dispatcher", "ds_inactive_threshold", 3)
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_ping_from", "sip:kamailio@YOUR_DOMAIN")
modparam("dispatcher", "ds_ping_reply_codes", "class2;class3;class4") /* NAT */
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)") modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* Dialog — for call-load balancing */
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4) /* ===== Routing ===== */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) exit; /* NAT detection */ if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { rtpengine_delete(); } if (has_body("application/sdp")) { route(NATMANAGE); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Authenticate */ route(AUTH); /* REGISTER — save locally, don't forward to backends */ if (is_method("REGISTER")) { if (nat_uac_test(63)) { fix_nated_register(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE — dispatch to backend Asterisk servers */ if (is_method("INVITE")) { /* Track the dialog for call-load balancing */ setflag(4); dlg_manage(); /* NAT manage */ route(NATMANAGE); /* Dispatch to backend set 1 */ route(DISPATCH); exit; } /* Other requests — try location lookup first */ if (lookup("location")) { route(RELAY); exit; } sl_send_reply(404, "Not Found"); exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } if (is_method("INVITE|MESSAGE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[DISPATCH] { /* Round-robin (algorithm 0) across set 1 */ if (!ds_select_dst(1, 0)) { xlog("L_ERR", "DISPATCH: All backends down for $rU\n"); sl_send_reply(503, "Service Unavailable"); exit; } xlog("L_INFO", "DISPATCH: $rU → $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit;
} route[RELAY] { t_on_reply("MANAGE_REPLY"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
} onreply_route[MANAGE_REPLY] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE); }
} failure_route[DISPATCH_FAILURE] { if (t_is_canceled()) exit; if (t_check_status("5[0-9][0-9]") || t_check_status("408")) { xlog("L_WARN", "DISPATCH: $du failed ($T_reply_code), failover\n"); ds_mark_dst("ip"); if (ds_next_dst()) { xlog("L_INFO", "DISPATCH: Failover → $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit; } xlog("L_ERR", "DISPATCH: All backends exhausted\n"); t_reply(503, "All Backends Unavailable"); exit; }
}
#!KAMAILIO
#!define WITH_DISPATCHER listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
server_signature=no /* Modules */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "dispatcher.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "dialog.so" /* Module parameters */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio") modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600) modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) /* Dispatcher — load balancer */
modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "ds_ping_interval", 10)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3)
modparam("dispatcher", "ds_inactive_threshold", 3)
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_ping_from", "sip:kamailio@YOUR_DOMAIN")
modparam("dispatcher", "ds_ping_reply_codes", "class2;class3;class4") /* NAT */
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)") modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* Dialog — for call-load balancing */
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4) /* ===== Routing ===== */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) exit; /* NAT detection */ if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { rtpengine_delete(); } if (has_body("application/sdp")) { route(NATMANAGE); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Authenticate */ route(AUTH); /* REGISTER — save locally, don't forward to backends */ if (is_method("REGISTER")) { if (nat_uac_test(63)) { fix_nated_register(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE — dispatch to backend Asterisk servers */ if (is_method("INVITE")) { /* Track the dialog for call-load balancing */ setflag(4); dlg_manage(); /* NAT manage */ route(NATMANAGE); /* Dispatch to backend set 1 */ route(DISPATCH); exit; } /* Other requests — try location lookup first */ if (lookup("location")) { route(RELAY); exit; } sl_send_reply(404, "Not Found"); exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } if (is_method("INVITE|MESSAGE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[DISPATCH] { /* Round-robin (algorithm 0) across set 1 */ if (!ds_select_dst(1, 0)) { xlog("L_ERR", "DISPATCH: All backends down for $rU\n"); sl_send_reply(503, "Service Unavailable"); exit; } xlog("L_INFO", "DISPATCH: $rU → $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit;
} route[RELAY] { t_on_reply("MANAGE_REPLY"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
} onreply_route[MANAGE_REPLY] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE); }
} failure_route[DISPATCH_FAILURE] { if (t_is_canceled()) exit; if (t_check_status("5[0-9][0-9]") || t_check_status("408")) { xlog("L_WARN", "DISPATCH: $du failed ($T_reply_code), failover\n"); ds_mark_dst("ip"); if (ds_next_dst()) { xlog("L_INFO", "DISPATCH: Failover → $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit; } xlog("L_ERR", "DISPATCH: All backends exhausted\n"); t_reply(503, "All Backends Unavailable"); exit; }
}
#!KAMAILIO
#!define WITH_DISPATCHER listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
server_signature=no /* Modules */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "dispatcher.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "dialog.so" /* Module parameters */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio") modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600) modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) /* Dispatcher — load balancer */
modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "ds_ping_interval", 10)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3)
modparam("dispatcher", "ds_inactive_threshold", 3)
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_ping_from", "sip:kamailio@YOUR_DOMAIN")
modparam("dispatcher", "ds_ping_reply_codes", "class2;class3;class4") /* NAT */
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)") modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* Dialog — for call-load balancing */
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4) /* ===== Routing ===== */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) exit; /* NAT detection */ if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { rtpengine_delete(); } if (has_body("application/sdp")) { route(NATMANAGE); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Authenticate */ route(AUTH); /* REGISTER — save locally, don't forward to backends */ if (is_method("REGISTER")) { if (nat_uac_test(63)) { fix_nated_register(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE — dispatch to backend Asterisk servers */ if (is_method("INVITE")) { /* Track the dialog for call-load balancing */ setflag(4); dlg_manage(); /* NAT manage */ route(NATMANAGE); /* Dispatch to backend set 1 */ route(DISPATCH); exit; } /* Other requests — try location lookup first */ if (lookup("location")) { route(RELAY); exit; } sl_send_reply(404, "Not Found"); exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } if (is_method("INVITE|MESSAGE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[DISPATCH] { /* Round-robin (algorithm 0) across set 1 */ if (!ds_select_dst(1, 0)) { xlog("L_ERR", "DISPATCH: All backends down for $rU\n"); sl_send_reply(503, "Service Unavailable"); exit; } xlog("L_INFO", "DISPATCH: $rU → $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit;
} route[RELAY] { t_on_reply("MANAGE_REPLY"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
} onreply_route[MANAGE_REPLY] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE); }
} failure_route[DISPATCH_FAILURE] { if (t_is_canceled()) exit; if (t_check_status("5[0-9][0-9]") || t_check_status("408")) { xlog("L_WARN", "DISPATCH: $du failed ($T_reply_code), failover\n"); ds_mark_dst("ip"); if (ds_next_dst()) { xlog("L_INFO", "DISPATCH: Failover → $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit; } xlog("L_ERR", "DISPATCH: All backends exhausted\n"); t_reply(503, "All Backends Unavailable"); exit; }
}
loadmodule "dialog.so" modparam("dialog", "db_mode", 1) /* 1 = write to DB on changes */
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4) /* Flag to -weight: 500;">enable dialog tracking */
modparam("dialog", "timeout_avp", "$avp(dlg_timeout)")
modparam("dialog", "default_timeout", 43200) /* 12-hour max call duration */ /* Dialog profiles — for per-user call limits */
modparam("dialog", "profiles_with_value", "caller;callee")
modparam("dialog", "profiles_no_value", "active_calls")
loadmodule "dialog.so" modparam("dialog", "db_mode", 1) /* 1 = write to DB on changes */
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4) /* Flag to -weight: 500;">enable dialog tracking */
modparam("dialog", "timeout_avp", "$avp(dlg_timeout)")
modparam("dialog", "default_timeout", 43200) /* 12-hour max call duration */ /* Dialog profiles — for per-user call limits */
modparam("dialog", "profiles_with_value", "caller;callee")
modparam("dialog", "profiles_no_value", "active_calls")
loadmodule "dialog.so" modparam("dialog", "db_mode", 1) /* 1 = write to DB on changes */
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4) /* Flag to -weight: 500;">enable dialog tracking */
modparam("dialog", "timeout_avp", "$avp(dlg_timeout)")
modparam("dialog", "default_timeout", 43200) /* 12-hour max call duration */ /* Dialog profiles — for per-user call limits */
modparam("dialog", "profiles_with_value", "caller;callee")
modparam("dialog", "profiles_no_value", "active_calls")
request_route { /* ... (sanity, NAT, in-dialog handling) ... */ if (is_method("INVITE") && !has_totag()) { /* New call — -weight: 500;">enable dialog tracking */ setflag(4); dlg_manage(); /* Set dialog timeout (optional) */ $avp(dlg_timeout) = 7200; /* 2-hour limit for this call */ /* Set dialog profile for the caller */ set_dlg_profile("caller", "$fU"); set_dlg_profile("callee", "$rU"); set_dlg_profile("active_calls"); /* ... route the call ... */ }
}
request_route { /* ... (sanity, NAT, in-dialog handling) ... */ if (is_method("INVITE") && !has_totag()) { /* New call — -weight: 500;">enable dialog tracking */ setflag(4); dlg_manage(); /* Set dialog timeout (optional) */ $avp(dlg_timeout) = 7200; /* 2-hour limit for this call */ /* Set dialog profile for the caller */ set_dlg_profile("caller", "$fU"); set_dlg_profile("callee", "$rU"); set_dlg_profile("active_calls"); /* ... route the call ... */ }
}
request_route { /* ... (sanity, NAT, in-dialog handling) ... */ if (is_method("INVITE") && !has_totag()) { /* New call — -weight: 500;">enable dialog tracking */ setflag(4); dlg_manage(); /* Set dialog timeout (optional) */ $avp(dlg_timeout) = 7200; /* 2-hour limit for this call */ /* Set dialog profile for the caller */ set_dlg_profile("caller", "$fU"); set_dlg_profile("callee", "$rU"); set_dlg_profile("active_calls"); /* ... route the call ... */ }
}
route[CHECK_CALL_LIMITS] { /* Max 5 simultaneous calls per caller */ if (get_profile_size("caller", "$fU", "$avp(caller_calls)")) { if ($avp(caller_calls) >= 5) { xlog("L_WARN", "Call limit reached for $fU ($avp(caller_calls) active)\n"); sl_send_reply(486, "Busy — Call Limit Reached"); exit; } } /* Max 2 simultaneous calls per callee */ if (get_profile_size("callee", "$rU", "$avp(callee_calls)")) { if ($avp(callee_calls) >= 2) { xlog("L_WARN", "Callee $rU busy ($avp(callee_calls) active)\n"); sl_send_reply(486, "Busy Here"); exit; } } return;
}
route[CHECK_CALL_LIMITS] { /* Max 5 simultaneous calls per caller */ if (get_profile_size("caller", "$fU", "$avp(caller_calls)")) { if ($avp(caller_calls) >= 5) { xlog("L_WARN", "Call limit reached for $fU ($avp(caller_calls) active)\n"); sl_send_reply(486, "Busy — Call Limit Reached"); exit; } } /* Max 2 simultaneous calls per callee */ if (get_profile_size("callee", "$rU", "$avp(callee_calls)")) { if ($avp(callee_calls) >= 2) { xlog("L_WARN", "Callee $rU busy ($avp(callee_calls) active)\n"); sl_send_reply(486, "Busy Here"); exit; } } return;
}
route[CHECK_CALL_LIMITS] { /* Max 5 simultaneous calls per caller */ if (get_profile_size("caller", "$fU", "$avp(caller_calls)")) { if ($avp(caller_calls) >= 5) { xlog("L_WARN", "Call limit reached for $fU ($avp(caller_calls) active)\n"); sl_send_reply(486, "Busy — Call Limit Reached"); exit; } } /* Max 2 simultaneous calls per callee */ if (get_profile_size("callee", "$rU", "$avp(callee_calls)")) { if ($avp(callee_calls) >= 2) { xlog("L_WARN", "Callee $rU busy ($avp(callee_calls) active)\n"); sl_send_reply(486, "Busy Here"); exit; } } return;
}
# View active calls
kamcmd dlg.list # Count active dialogs
kamcmd dlg.stats_active # Get dialog by Call-ID
kamcmd dlg.dlg_list_ctx # View profile sizes (active calls per user)
kamcmd dlg.profile_list caller
kamcmd dlg.profile_list active_calls # Terminate a dialog
kamcmd dlg.end_dlg <h_entry> <h_id>
# View active calls
kamcmd dlg.list # Count active dialogs
kamcmd dlg.stats_active # Get dialog by Call-ID
kamcmd dlg.dlg_list_ctx # View profile sizes (active calls per user)
kamcmd dlg.profile_list caller
kamcmd dlg.profile_list active_calls # Terminate a dialog
kamcmd dlg.end_dlg <h_entry> <h_id>
# View active calls
kamcmd dlg.list # Count active dialogs
kamcmd dlg.stats_active # Get dialog by Call-ID
kamcmd dlg.dlg_list_ctx # View profile sizes (active calls per user)
kamcmd dlg.profile_list caller
kamcmd dlg.profile_list active_calls # Terminate a dialog
kamcmd dlg.end_dlg <h_entry> <h_id>
loadmodule "acc.so"
loadmodule "acc_db.so" /* Database CDR backend (included in kamailio-mysql-modules) */ /* Accounting to database */
modparam("acc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("acc", "db_flag", 1) /* Flag to trigger DB accounting */
modparam("acc", "db_missed_flag", 2) /* Flag for missed call logging */
modparam("acc", "failed_transaction_flag", 3) /* Flag for failed transactions */ /* What to log */
modparam("acc", "log_level", 1)
modparam("acc", "log_flag", 1) /* CDR generation — log duration, -weight: 500;">start/end time */
modparam("acc", "cdr_enable", 1)
modparam("acc", "cdr_start_on_confirmed", 1) /* Start CDR timer on 200 OK */
modparam("acc", "cdr_log_enable", 1) /* Extra fields to log */
modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_user=$rU;dst_domain=$rd;" "callid=$ci;ua=$ua") /* CDR extra fields */
modparam("acc", "cdr_extra", "src_user=$fU;dst_user=$rU;src_ip=$si;callid=$ci")
loadmodule "acc.so"
loadmodule "acc_db.so" /* Database CDR backend (included in kamailio-mysql-modules) */ /* Accounting to database */
modparam("acc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("acc", "db_flag", 1) /* Flag to trigger DB accounting */
modparam("acc", "db_missed_flag", 2) /* Flag for missed call logging */
modparam("acc", "failed_transaction_flag", 3) /* Flag for failed transactions */ /* What to log */
modparam("acc", "log_level", 1)
modparam("acc", "log_flag", 1) /* CDR generation — log duration, -weight: 500;">start/end time */
modparam("acc", "cdr_enable", 1)
modparam("acc", "cdr_start_on_confirmed", 1) /* Start CDR timer on 200 OK */
modparam("acc", "cdr_log_enable", 1) /* Extra fields to log */
modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_user=$rU;dst_domain=$rd;" "callid=$ci;ua=$ua") /* CDR extra fields */
modparam("acc", "cdr_extra", "src_user=$fU;dst_user=$rU;src_ip=$si;callid=$ci")
loadmodule "acc.so"
loadmodule "acc_db.so" /* Database CDR backend (included in kamailio-mysql-modules) */ /* Accounting to database */
modparam("acc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("acc", "db_flag", 1) /* Flag to trigger DB accounting */
modparam("acc", "db_missed_flag", 2) /* Flag for missed call logging */
modparam("acc", "failed_transaction_flag", 3) /* Flag for failed transactions */ /* What to log */
modparam("acc", "log_level", 1)
modparam("acc", "log_flag", 1) /* CDR generation — log duration, -weight: 500;">start/end time */
modparam("acc", "cdr_enable", 1)
modparam("acc", "cdr_start_on_confirmed", 1) /* Start CDR timer on 200 OK */
modparam("acc", "cdr_log_enable", 1) /* Extra fields to log */
modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_user=$rU;dst_domain=$rd;" "callid=$ci;ua=$ua") /* CDR extra fields */
modparam("acc", "cdr_extra", "src_user=$fU;dst_user=$rU;src_ip=$si;callid=$ci")
request_route { /* ... */ if (is_method("INVITE") && !has_totag()) { setflag(1); /* Enable DB accounting */ setflag(2); /* Log missed calls */ setflag(3); /* Log failed transactions */ setflag(4); /* Enable dialog tracking (needed for CDR duration) */ dlg_manage(); /* ... route the call ... */ }
}
request_route { /* ... */ if (is_method("INVITE") && !has_totag()) { setflag(1); /* Enable DB accounting */ setflag(2); /* Log missed calls */ setflag(3); /* Log failed transactions */ setflag(4); /* Enable dialog tracking (needed for CDR duration) */ dlg_manage(); /* ... route the call ... */ }
}
request_route { /* ... */ if (is_method("INVITE") && !has_totag()) { setflag(1); /* Enable DB accounting */ setflag(2); /* Log missed calls */ setflag(3); /* Log failed transactions */ setflag(4); /* Enable dialog tracking (needed for CDR duration) */ dlg_manage(); /* ... route the call ... */ }
}
-- View CDRs
SELECT id, method, from_tag, to_tag, callid, sip_code, sip_reason, src_user, dst_user, src_ip, time
FROM acc
ORDER BY time DESC
LIMIT 20; -- CDR table (if cdr_enable=1)
SELECT id, start_time, end_time, duration, src_user, dst_user, src_ip, callid, sip_code, sip_reason
FROM cdrs
ORDER BY start_time DESC
LIMIT 20;
-- View CDRs
SELECT id, method, from_tag, to_tag, callid, sip_code, sip_reason, src_user, dst_user, src_ip, time
FROM acc
ORDER BY time DESC
LIMIT 20; -- CDR table (if cdr_enable=1)
SELECT id, start_time, end_time, duration, src_user, dst_user, src_ip, callid, sip_code, sip_reason
FROM cdrs
ORDER BY start_time DESC
LIMIT 20;
-- View CDRs
SELECT id, method, from_tag, to_tag, callid, sip_code, sip_reason, src_user, dst_user, src_ip, time
FROM acc
ORDER BY time DESC
LIMIT 20; -- CDR table (if cdr_enable=1)
SELECT id, start_time, end_time, duration, src_user, dst_user, src_ip, callid, sip_code, sip_reason
FROM cdrs
ORDER BY start_time DESC
LIMIT 20;
/* In modparam section */
modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_user=$rU;dst_domain=$rd;" "callid=$ci;ua=$ua;" "trunk=$avp(trunk_name);" "campaign=$avp(campaign)") /* In routing logic — set custom fields before acc triggers */
if (is_method("INVITE") && !has_totag()) { $avp(trunk_name) = "protech"; $avp(campaign) = "uk_sales"; setflag(1); /* ... */
}
/* In modparam section */
modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_user=$rU;dst_domain=$rd;" "callid=$ci;ua=$ua;" "trunk=$avp(trunk_name);" "campaign=$avp(campaign)") /* In routing logic — set custom fields before acc triggers */
if (is_method("INVITE") && !has_totag()) { $avp(trunk_name) = "protech"; $avp(campaign) = "uk_sales"; setflag(1); /* ... */
}
/* In modparam section */
modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_user=$rU;dst_domain=$rd;" "callid=$ci;ua=$ua;" "trunk=$avp(trunk_name);" "campaign=$avp(campaign)") /* In routing logic — set custom fields before acc triggers */
if (is_method("INVITE") && !has_totag()) { $avp(trunk_name) = "protech"; $avp(campaign) = "uk_sales"; setflag(1); /* ... */
}
ALTER TABLE acc ADD COLUMN trunk VARCHAR(64) DEFAULT '';
ALTER TABLE acc ADD COLUMN campaign VARCHAR(64) DEFAULT '';
ALTER TABLE acc ADD COLUMN trunk VARCHAR(64) DEFAULT '';
ALTER TABLE acc ADD COLUMN campaign VARCHAR(64) DEFAULT '';
ALTER TABLE acc ADD COLUMN trunk VARCHAR(64) DEFAULT '';
ALTER TABLE acc ADD COLUMN campaign VARCHAR(64) DEFAULT '';
┌─────────┐ WSS/SRTP ┌──────────┐ SIP/RTP ┌──────────┐
│ Browser │ ◄────────────► │ Kamailio │ ◄──────────► │ Asterisk │
│ SIP.js │ │+RTPEngine│ │ (PBX) │
└─────────┘ └──────────┘ └──────────┘ Browser → Kamailio: SIP over WSS (port 8443), DTLS-SRTP media
Kamailio → Asterisk: SIP over UDP (port 5060), plain RTP media
RTPEngine: Converts DTLS-SRTP ↔ plain RTP
┌─────────┐ WSS/SRTP ┌──────────┐ SIP/RTP ┌──────────┐
│ Browser │ ◄────────────► │ Kamailio │ ◄──────────► │ Asterisk │
│ SIP.js │ │+RTPEngine│ │ (PBX) │
└─────────┘ └──────────┘ └──────────┘ Browser → Kamailio: SIP over WSS (port 8443), DTLS-SRTP media
Kamailio → Asterisk: SIP over UDP (port 5060), plain RTP media
RTPEngine: Converts DTLS-SRTP ↔ plain RTP
┌─────────┐ WSS/SRTP ┌──────────┐ SIP/RTP ┌──────────┐
│ Browser │ ◄────────────► │ Kamailio │ ◄──────────► │ Asterisk │
│ SIP.js │ │+RTPEngine│ │ (PBX) │
└─────────┘ └──────────┘ └──────────┘ Browser → Kamailio: SIP over WSS (port 8443), DTLS-SRTP media
Kamailio → Asterisk: SIP over UDP (port 5060), plain RTP media
RTPEngine: Converts DTLS-SRTP ↔ plain RTP
/* Module loading */
loadmodule "websocket.so"
loadmodule "xhttp.so" /* HTTP handling for WebSocket -weight: 500;">upgrade */
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "tls.so" /* Listen on WSS (WebSocket Secure) */
listen=tls:YOUR_SERVER_IP:8443 /* WebSocket parameters */
modparam("websocket", "keepalive_mechanism", 1) /* 1 = PING/PONG */
modparam("websocket", "keepalive_timeout", 30) /* Seconds */
modparam("websocket", "keepalive_processes", 1) /* TLS for WSS */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "tls_method", "TLSv1.2+")
/* Module loading */
loadmodule "websocket.so"
loadmodule "xhttp.so" /* HTTP handling for WebSocket -weight: 500;">upgrade */
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "tls.so" /* Listen on WSS (WebSocket Secure) */
listen=tls:YOUR_SERVER_IP:8443 /* WebSocket parameters */
modparam("websocket", "keepalive_mechanism", 1) /* 1 = PING/PONG */
modparam("websocket", "keepalive_timeout", 30) /* Seconds */
modparam("websocket", "keepalive_processes", 1) /* TLS for WSS */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "tls_method", "TLSv1.2+")
/* Module loading */
loadmodule "websocket.so"
loadmodule "xhttp.so" /* HTTP handling for WebSocket -weight: 500;">upgrade */
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "tls.so" /* Listen on WSS (WebSocket Secure) */
listen=tls:YOUR_SERVER_IP:8443 /* WebSocket parameters */
modparam("websocket", "keepalive_mechanism", 1) /* 1 = PING/PONG */
modparam("websocket", "keepalive_timeout", 30) /* Seconds */
modparam("websocket", "keepalive_processes", 1) /* TLS for WSS */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "tls_method", "TLSv1.2+")
/* Handle HTTP requests (WebSocket -weight: 500;">upgrade) */
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($hdr(Upgrade) =~ "websocket" && $hdr(Connection) =~ "Upgrade" && $rm == "GET") { /* Validate Origin header (optional but recommended) */ # if ($hdr(Origin) != "https://YOUR_DOMAIN") { # xhttp_reply(403, "Forbidden", "text/plain", "Bad Origin"); # exit; # } /* Accept WebSocket -weight: 500;">upgrade */ if (ws_handle_handshake()) { exit; } } /* Non-WebSocket HTTP request — reject */ xhttp_reply(404, "Not Found", "text/plain", "WebSocket Only"); exit;
}
/* Handle HTTP requests (WebSocket -weight: 500;">upgrade) */
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($hdr(Upgrade) =~ "websocket" && $hdr(Connection) =~ "Upgrade" && $rm == "GET") { /* Validate Origin header (optional but recommended) */ # if ($hdr(Origin) != "https://YOUR_DOMAIN") { # xhttp_reply(403, "Forbidden", "text/plain", "Bad Origin"); # exit; # } /* Accept WebSocket -weight: 500;">upgrade */ if (ws_handle_handshake()) { exit; } } /* Non-WebSocket HTTP request — reject */ xhttp_reply(404, "Not Found", "text/plain", "WebSocket Only"); exit;
}
/* Handle HTTP requests (WebSocket -weight: 500;">upgrade) */
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($hdr(Upgrade) =~ "websocket" && $hdr(Connection) =~ "Upgrade" && $rm == "GET") { /* Validate Origin header (optional but recommended) */ # if ($hdr(Origin) != "https://YOUR_DOMAIN") { # xhttp_reply(403, "Forbidden", "text/plain", "Bad Origin"); # exit; # } /* Accept WebSocket -weight: 500;">upgrade */ if (ws_handle_handshake()) { exit; } } /* Non-WebSocket HTTP request — reject */ xhttp_reply(404, "Not Found", "text/plain", "WebSocket Only"); exit;
}
route[NATMANAGE_WEBRTC] { if (is_request()) { if (has_body("application/sdp")) { if ($proto == "ws" || $proto == "wss") { /* WebRTC → SIP: convert DTLS-SRTP to plain RTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove " "RTP/AVP SDES-off"); } else if (ds_is_from_list(1)) { /* Reply from backend (SIP) → WebRTC: convert RTP to DTLS-SRTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force " "RTP/SAVPF SDES-off " "DTLS=passive"); } else { /* SIP to SIP — standard relay */ rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if ($proto == "ws" || $proto == "wss") { /* Reply going to WebRTC client */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force " "RTP/SAVPF SDES-off " "DTLS=passive"); } else { /* Reply going to SIP endpoint */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove " "RTP/AVP SDES-off"); } } } return;
}
route[NATMANAGE_WEBRTC] { if (is_request()) { if (has_body("application/sdp")) { if ($proto == "ws" || $proto == "wss") { /* WebRTC → SIP: convert DTLS-SRTP to plain RTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove " "RTP/AVP SDES-off"); } else if (ds_is_from_list(1)) { /* Reply from backend (SIP) → WebRTC: convert RTP to DTLS-SRTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force " "RTP/SAVPF SDES-off " "DTLS=passive"); } else { /* SIP to SIP — standard relay */ rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if ($proto == "ws" || $proto == "wss") { /* Reply going to WebRTC client */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force " "RTP/SAVPF SDES-off " "DTLS=passive"); } else { /* Reply going to SIP endpoint */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove " "RTP/AVP SDES-off"); } } } return;
}
route[NATMANAGE_WEBRTC] { if (is_request()) { if (has_body("application/sdp")) { if ($proto == "ws" || $proto == "wss") { /* WebRTC → SIP: convert DTLS-SRTP to plain RTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove " "RTP/AVP SDES-off"); } else if (ds_is_from_list(1)) { /* Reply from backend (SIP) → WebRTC: convert RTP to DTLS-SRTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force " "RTP/SAVPF SDES-off " "DTLS=passive"); } else { /* SIP to SIP — standard relay */ rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if ($proto == "ws" || $proto == "wss") { /* Reply going to WebRTC client */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force " "RTP/SAVPF SDES-off " "DTLS=passive"); } else { /* Reply going to SIP endpoint */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove " "RTP/AVP SDES-off"); } } } return;
}
# /etc/nginx/sites-available/kamailio-wss
upstream kamailio_wss { server 127.0.0.1:8080; # Kamailio WS (non-TLS) listener
} server { listen 443 ssl; server_name YOUR_DOMAIN; ssl_certificate /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; location /ws { proxy_pass http://kamailio_wss; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "-weight: 500;">upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 3600s; proxy_send_timeout 3600s; }
}
# /etc/nginx/sites-available/kamailio-wss
upstream kamailio_wss { server 127.0.0.1:8080; # Kamailio WS (non-TLS) listener
} server { listen 443 ssl; server_name YOUR_DOMAIN; ssl_certificate /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; location /ws { proxy_pass http://kamailio_wss; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "-weight: 500;">upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 3600s; proxy_send_timeout 3600s; }
}
# /etc/nginx/sites-available/kamailio-wss
upstream kamailio_wss { server 127.0.0.1:8080; # Kamailio WS (non-TLS) listener
} server { listen 443 ssl; server_name YOUR_DOMAIN; ssl_certificate /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; location /ws { proxy_pass http://kamailio_wss; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "-weight: 500;">upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 3600s; proxy_send_timeout 3600s; }
}
/* Plain WebSocket behind Nginx */
listen=tcp:127.0.0.1:8080
/* Plain WebSocket behind Nginx */
listen=tcp:127.0.0.1:8080
/* Plain WebSocket behind Nginx */
listen=tcp:127.0.0.1:8080
<!DOCTYPE html>
<html>
<head> <title>WebRTC Phone</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jssip/3.10.0/jssip.min.js"></script>
</head>
<body> <h2>WebRTC SIP Phone</h2> <div> <input type="text" id="target" placeholder="Extension (e.g., 1002)" /> <button onclick="makeCall()">Call</button> <button onclick="hangUp()">Hang Up</button> </div> <div id="-weight: 500;">status">Disconnected</div> <audio id="remoteAudio" autoplay></audio> <script>
// Configuration
const socket = new JsSIP.WebSocketInterface('wss://YOUR_DOMAIN:8443');
const configuration = { sockets: [socket], uri: 'sip:1001@YOUR_DOMAIN', password: 'MySecurePass123', display_name: 'Web User 1001', register: true, session_timers: false
}; // Create User Agent
const ua = new JsSIP.UA(configuration);
let currentSession = null; // Event handlers
ua.on('connected', () => { document.getElementById('-weight: 500;">status').textContent = 'Connected';
}); ua.on('registered', () => { document.getElementById('-weight: 500;">status').textContent = 'Registered';
}); ua.on('registrationFailed', (e) => { document.getElementById('-weight: 500;">status').textContent = 'Registration failed: ' + e.cause;
}); ua.on('newRTCSession', (data) => { const session = data.session; currentSession = session; if (session.direction === 'incoming') { document.getElementById('-weight: 500;">status').textContent = 'Incoming call from ' + session.remote_identity.uri; // Auto-answer (or show accept/reject buttons) session.answer({ mediaConstraints: { audio: true, video: false } }); } session.on('confirmed', () => { document.getElementById('-weight: 500;">status').textContent = 'Call active'; }); session.on('ended', () => { document.getElementById('-weight: 500;">status').textContent = 'Call ended'; currentSession = null; }); session.on('failed', (e) => { document.getElementById('-weight: 500;">status').textContent = 'Call failed: ' + e.cause; currentSession = null; }); session.on('peerconnection', (e) => { e.peerconnection.ontrack = (event) => { document.getElementById('remoteAudio').srcObject = event.streams[0]; }; });
}); // Start the User Agent
ua.-weight: 500;">start(); // Make a call
function makeCall() { const target = document.getElementById('target').value; if (!target) return; const options = { mediaConstraints: { audio: true, video: false }, pcConfig: { iceServers: [ { urls: 'stun:stun.l.google.com:19302' } ] } }; currentSession = ua.call('sip:' + target + '@YOUR_DOMAIN', options);
} // Hang up
function hangUp() { if (currentSession) { currentSession.terminate(); }
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head> <title>WebRTC Phone</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jssip/3.10.0/jssip.min.js"></script>
</head>
<body> <h2>WebRTC SIP Phone</h2> <div> <input type="text" id="target" placeholder="Extension (e.g., 1002)" /> <button onclick="makeCall()">Call</button> <button onclick="hangUp()">Hang Up</button> </div> <div id="-weight: 500;">status">Disconnected</div> <audio id="remoteAudio" autoplay></audio> <script>
// Configuration
const socket = new JsSIP.WebSocketInterface('wss://YOUR_DOMAIN:8443');
const configuration = { sockets: [socket], uri: 'sip:1001@YOUR_DOMAIN', password: 'MySecurePass123', display_name: 'Web User 1001', register: true, session_timers: false
}; // Create User Agent
const ua = new JsSIP.UA(configuration);
let currentSession = null; // Event handlers
ua.on('connected', () => { document.getElementById('-weight: 500;">status').textContent = 'Connected';
}); ua.on('registered', () => { document.getElementById('-weight: 500;">status').textContent = 'Registered';
}); ua.on('registrationFailed', (e) => { document.getElementById('-weight: 500;">status').textContent = 'Registration failed: ' + e.cause;
}); ua.on('newRTCSession', (data) => { const session = data.session; currentSession = session; if (session.direction === 'incoming') { document.getElementById('-weight: 500;">status').textContent = 'Incoming call from ' + session.remote_identity.uri; // Auto-answer (or show accept/reject buttons) session.answer({ mediaConstraints: { audio: true, video: false } }); } session.on('confirmed', () => { document.getElementById('-weight: 500;">status').textContent = 'Call active'; }); session.on('ended', () => { document.getElementById('-weight: 500;">status').textContent = 'Call ended'; currentSession = null; }); session.on('failed', (e) => { document.getElementById('-weight: 500;">status').textContent = 'Call failed: ' + e.cause; currentSession = null; }); session.on('peerconnection', (e) => { e.peerconnection.ontrack = (event) => { document.getElementById('remoteAudio').srcObject = event.streams[0]; }; });
}); // Start the User Agent
ua.-weight: 500;">start(); // Make a call
function makeCall() { const target = document.getElementById('target').value; if (!target) return; const options = { mediaConstraints: { audio: true, video: false }, pcConfig: { iceServers: [ { urls: 'stun:stun.l.google.com:19302' } ] } }; currentSession = ua.call('sip:' + target + '@YOUR_DOMAIN', options);
} // Hang up
function hangUp() { if (currentSession) { currentSession.terminate(); }
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head> <title>WebRTC Phone</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jssip/3.10.0/jssip.min.js"></script>
</head>
<body> <h2>WebRTC SIP Phone</h2> <div> <input type="text" id="target" placeholder="Extension (e.g., 1002)" /> <button onclick="makeCall()">Call</button> <button onclick="hangUp()">Hang Up</button> </div> <div id="-weight: 500;">status">Disconnected</div> <audio id="remoteAudio" autoplay></audio> <script>
// Configuration
const socket = new JsSIP.WebSocketInterface('wss://YOUR_DOMAIN:8443');
const configuration = { sockets: [socket], uri: 'sip:1001@YOUR_DOMAIN', password: 'MySecurePass123', display_name: 'Web User 1001', register: true, session_timers: false
}; // Create User Agent
const ua = new JsSIP.UA(configuration);
let currentSession = null; // Event handlers
ua.on('connected', () => { document.getElementById('-weight: 500;">status').textContent = 'Connected';
}); ua.on('registered', () => { document.getElementById('-weight: 500;">status').textContent = 'Registered';
}); ua.on('registrationFailed', (e) => { document.getElementById('-weight: 500;">status').textContent = 'Registration failed: ' + e.cause;
}); ua.on('newRTCSession', (data) => { const session = data.session; currentSession = session; if (session.direction === 'incoming') { document.getElementById('-weight: 500;">status').textContent = 'Incoming call from ' + session.remote_identity.uri; // Auto-answer (or show accept/reject buttons) session.answer({ mediaConstraints: { audio: true, video: false } }); } session.on('confirmed', () => { document.getElementById('-weight: 500;">status').textContent = 'Call active'; }); session.on('ended', () => { document.getElementById('-weight: 500;">status').textContent = 'Call ended'; currentSession = null; }); session.on('failed', (e) => { document.getElementById('-weight: 500;">status').textContent = 'Call failed: ' + e.cause; currentSession = null; }); session.on('peerconnection', (e) => { e.peerconnection.ontrack = (event) => { document.getElementById('remoteAudio').srcObject = event.streams[0]; }; });
}); // Start the User Agent
ua.-weight: 500;">start(); // Make a call
function makeCall() { const target = document.getElementById('target').value; if (!target) return; const options = { mediaConstraints: { audio: true, video: false }, pcConfig: { iceServers: [ { urls: 'stun:stun.l.google.com:19302' } ] } }; currentSession = ua.call('sip:' + target + '@YOUR_DOMAIN', options);
} // Hang up
function hangUp() { if (currentSession) { currentSession.terminate(); }
}
</script>
</body>
</html>
#!KAMAILIO listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
listen=tls:YOUR_SERVER_IP:5061
listen=tls:YOUR_SERVER_IP:8443 debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
tcp_children=8
auto_aliases=no
server_signature=no
force_rport=yes /* Modules */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "xhttp.so"
loadmodule "websocket.so"
loadmodule "tls.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so" /* TLS */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "tls_method", "TLSv1.2+") /* WebSocket */
modparam("websocket", "keepalive_mechanism", 1)
modparam("websocket", "keepalive_timeout", 30) /* Transaction */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) /* User location */
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 300) /* Auth */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) /* NAT + RTPEngine */
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* WebSocket -weight: 500;">upgrade handler */
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($hdr(Upgrade) =~ "websocket" && $hdr(Connection) =~ "Upgrade" && $rm == "GET") { if (ws_handle_handshake()) exit; } xhttp_reply(404, "Not Found", "text/plain", "WebSocket Only"); exit;
} /* Main routing */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) exit; /* NAT */ if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } /* Tag WebSocket requests */ if ($proto == "ws" || $proto == "wss") { setflag(6); /* Flag 6 = WebRTC client */ } if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) rtpengine_delete(); if (has_body("application/sdp")) route(NATMANAGE_WS); route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Auth */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { if (nat_uac_test(63)) fix_nated_register(); if (!save("location")) sl_send_reply(500, "Failed"); exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } route(NATMANAGE_WS); route(RELAY); exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } return; } if (is_method("INVITE|MESSAGE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } consume_credentials(); } return;
} route[RELAY] { t_on_reply("MANAGE_REPLY_WS"); t_on_failure("MANAGE_FAILURE"); if (!t_relay()) sl_send_reply(500, "Relay Error"); exit;
} route[NATMANAGE_WS] { if (is_request()) { if (has_body("application/sdp")) { if (isflagset(6)) { /* FROM WebRTC → TO SIP: DTLS-SRTP → plain RTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove RTP/AVP SDES-off"); } else { /* FROM SIP → TO WebRTC or SIP */ rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if (isflagset(6)) { /* Reply TO WebRTC client: plain RTP → DTLS-SRTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force RTP/SAVPF SDES-off DTLS=passive"); } else { rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } return;
} onreply_route[MANAGE_REPLY_WS] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE_WS); }
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n");
}
#!KAMAILIO listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
listen=tls:YOUR_SERVER_IP:5061
listen=tls:YOUR_SERVER_IP:8443 debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
tcp_children=8
auto_aliases=no
server_signature=no
force_rport=yes /* Modules */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "xhttp.so"
loadmodule "websocket.so"
loadmodule "tls.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so" /* TLS */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "tls_method", "TLSv1.2+") /* WebSocket */
modparam("websocket", "keepalive_mechanism", 1)
modparam("websocket", "keepalive_timeout", 30) /* Transaction */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) /* User location */
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 300) /* Auth */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) /* NAT + RTPEngine */
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* WebSocket -weight: 500;">upgrade handler */
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($hdr(Upgrade) =~ "websocket" && $hdr(Connection) =~ "Upgrade" && $rm == "GET") { if (ws_handle_handshake()) exit; } xhttp_reply(404, "Not Found", "text/plain", "WebSocket Only"); exit;
} /* Main routing */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) exit; /* NAT */ if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } /* Tag WebSocket requests */ if ($proto == "ws" || $proto == "wss") { setflag(6); /* Flag 6 = WebRTC client */ } if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) rtpengine_delete(); if (has_body("application/sdp")) route(NATMANAGE_WS); route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Auth */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { if (nat_uac_test(63)) fix_nated_register(); if (!save("location")) sl_send_reply(500, "Failed"); exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } route(NATMANAGE_WS); route(RELAY); exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } return; } if (is_method("INVITE|MESSAGE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } consume_credentials(); } return;
} route[RELAY] { t_on_reply("MANAGE_REPLY_WS"); t_on_failure("MANAGE_FAILURE"); if (!t_relay()) sl_send_reply(500, "Relay Error"); exit;
} route[NATMANAGE_WS] { if (is_request()) { if (has_body("application/sdp")) { if (isflagset(6)) { /* FROM WebRTC → TO SIP: DTLS-SRTP → plain RTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove RTP/AVP SDES-off"); } else { /* FROM SIP → TO WebRTC or SIP */ rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if (isflagset(6)) { /* Reply TO WebRTC client: plain RTP → DTLS-SRTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force RTP/SAVPF SDES-off DTLS=passive"); } else { rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } return;
} onreply_route[MANAGE_REPLY_WS] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE_WS); }
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n");
}
#!KAMAILIO listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
listen=tls:YOUR_SERVER_IP:5061
listen=tls:YOUR_SERVER_IP:8443 debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
tcp_children=8
auto_aliases=no
server_signature=no
force_rport=yes /* Modules */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "xhttp.so"
loadmodule "websocket.so"
loadmodule "tls.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so" /* TLS */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "tls_method", "TLSv1.2+") /* WebSocket */
modparam("websocket", "keepalive_mechanism", 1)
modparam("websocket", "keepalive_timeout", 30) /* Transaction */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) /* User location */
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 300) /* Auth */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) /* NAT + RTPEngine */
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* WebSocket -weight: 500;">upgrade handler */
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($hdr(Upgrade) =~ "websocket" && $hdr(Connection) =~ "Upgrade" && $rm == "GET") { if (ws_handle_handshake()) exit; } xhttp_reply(404, "Not Found", "text/plain", "WebSocket Only"); exit;
} /* Main routing */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) exit; /* NAT */ if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } /* Tag WebSocket requests */ if ($proto == "ws" || $proto == "wss") { setflag(6); /* Flag 6 = WebRTC client */ } if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) rtpengine_delete(); if (has_body("application/sdp")) route(NATMANAGE_WS); route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Auth */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { if (nat_uac_test(63)) fix_nated_register(); if (!save("location")) sl_send_reply(500, "Failed"); exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } route(NATMANAGE_WS); route(RELAY); exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } return; } if (is_method("INVITE|MESSAGE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } consume_credentials(); } return;
} route[RELAY] { t_on_reply("MANAGE_REPLY_WS"); t_on_failure("MANAGE_FAILURE"); if (!t_relay()) sl_send_reply(500, "Relay Error"); exit;
} route[NATMANAGE_WS] { if (is_request()) { if (has_body("application/sdp")) { if (isflagset(6)) { /* FROM WebRTC → TO SIP: DTLS-SRTP → plain RTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove RTP/AVP SDES-off"); } else { /* FROM SIP → TO WebRTC or SIP */ rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if (isflagset(6)) { /* Reply TO WebRTC client: plain RTP → DTLS-SRTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force RTP/SAVPF SDES-off DTLS=passive"); } else { rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } return;
} onreply_route[MANAGE_REPLY_WS] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE_WS); }
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n");
}
Internet DMZ LAN
───────────────────────────────────────────────────────────────── ┌──────────┐ ┌──────────┐
SIP Phones ────────────►│ Kamailio │──────────────►│ Asterisk │
SIP Trunks ────────────►│ (SBC) │ │ (PBX) │
WebRTC ────────────►│ │──────────────►│ Asterisk │ │+RTPEngine│ │ (PBX) │ └──────────┘ └──────────┘ Port 5060/5061 │ Port 5060 Port 8443 (WSS) │ (Internal) Port 10000-20000 (RTP via RTPEngine)
Internet DMZ LAN
───────────────────────────────────────────────────────────────── ┌──────────┐ ┌──────────┐
SIP Phones ────────────►│ Kamailio │──────────────►│ Asterisk │
SIP Trunks ────────────►│ (SBC) │ │ (PBX) │
WebRTC ────────────►│ │──────────────►│ Asterisk │ │+RTPEngine│ │ (PBX) │ └──────────┘ └──────────┘ Port 5060/5061 │ Port 5060 Port 8443 (WSS) │ (Internal) Port 10000-20000 (RTP via RTPEngine)
Internet DMZ LAN
───────────────────────────────────────────────────────────────── ┌──────────┐ ┌──────────┐
SIP Phones ────────────►│ Kamailio │──────────────►│ Asterisk │
SIP Trunks ────────────►│ (SBC) │ │ (PBX) │
WebRTC ────────────►│ │──────────────►│ Asterisk │ │+RTPEngine│ │ (PBX) │ └──────────┘ └──────────┘ Port 5060/5061 │ Port 5060 Port 8443 (WSS) │ (Internal) Port 10000-20000 (RTP via RTPEngine)
; /etc/asterisk/pjsip.conf (Asterisk 16+) [transport-udp]
type=transport
protocol=udp
bind=0.0.0.0 ; Trust Kamailio as a SIP peer
[kamailio]
type=identify
endpoint=kamailio
match=YOUR_SERVER_IP [kamailio]
type=endpoint
context=from-kamailio
disallow=all
allow=alaw
allow=ulaw
allow=opus
direct_media=no
trust_id_inbound=yes
trust_id_outbound=yes
send_pai=yes [kamailio]
type=aor
max_contacts=1
; /etc/asterisk/pjsip.conf (Asterisk 16+) [transport-udp]
type=transport
protocol=udp
bind=0.0.0.0 ; Trust Kamailio as a SIP peer
[kamailio]
type=identify
endpoint=kamailio
match=YOUR_SERVER_IP [kamailio]
type=endpoint
context=from-kamailio
disallow=all
allow=alaw
allow=ulaw
allow=opus
direct_media=no
trust_id_inbound=yes
trust_id_outbound=yes
send_pai=yes [kamailio]
type=aor
max_contacts=1
; /etc/asterisk/pjsip.conf (Asterisk 16+) [transport-udp]
type=transport
protocol=udp
bind=0.0.0.0 ; Trust Kamailio as a SIP peer
[kamailio]
type=identify
endpoint=kamailio
match=YOUR_SERVER_IP [kamailio]
type=endpoint
context=from-kamailio
disallow=all
allow=alaw
allow=ulaw
allow=opus
direct_media=no
trust_id_inbound=yes
trust_id_outbound=yes
send_pai=yes [kamailio]
type=aor
max_contacts=1
; /etc/asterisk/extensions.conf
[from-kamailio]
; Calls from Kamailio arrive here
exten => _X.,1,NoOp(Call from Kamailio: ${CALLERID(num)} → ${EXTEN}) same => n,Dial(PJSIP/${EXTEN},30) same => n,Hangup()
; /etc/asterisk/extensions.conf
[from-kamailio]
; Calls from Kamailio arrive here
exten => _X.,1,NoOp(Call from Kamailio: ${CALLERID(num)} → ${EXTEN}) same => n,Dial(PJSIP/${EXTEN},30) same => n,Hangup()
; /etc/asterisk/extensions.conf
[from-kamailio]
; Calls from Kamailio arrive here
exten => _X.,1,NoOp(Call from Kamailio: ${CALLERID(num)} → ${EXTEN}) same => n,Dial(PJSIP/${EXTEN},30) same => n,Hangup()
<!-- FreeSWITCH: /etc/freeswitch/sip_profiles/internal.xml -->
<!-- Add Kamailio as an ACL -->
<param name="apply-inbound-acl" value="kamailio"/> <!-- /etc/freeswitch/autoload_configs/acl.conf.xml -->
<list name="kamailio" default="deny"> <node type="allow" cidr="YOUR_SERVER_IP/32"/>
</list>
<!-- FreeSWITCH: /etc/freeswitch/sip_profiles/internal.xml -->
<!-- Add Kamailio as an ACL -->
<param name="apply-inbound-acl" value="kamailio"/> <!-- /etc/freeswitch/autoload_configs/acl.conf.xml -->
<list name="kamailio" default="deny"> <node type="allow" cidr="YOUR_SERVER_IP/32"/>
</list>
<!-- FreeSWITCH: /etc/freeswitch/sip_profiles/internal.xml -->
<!-- Add Kamailio as an ACL -->
<param name="apply-inbound-acl" value="kamailio"/> <!-- /etc/freeswitch/autoload_configs/acl.conf.xml -->
<list name="kamailio" default="deny"> <node type="allow" cidr="YOUR_SERVER_IP/32"/>
</list>
loadmodule "topoh.so" modparam("topoh", "mask_ip", "YOUR_SERVER_IP")
modparam("topoh", "mask_callid", 1) /* Mask Call-ID */
modparam("topoh", "sanity_checks", 1) /* Validate before unmasking */
loadmodule "topoh.so" modparam("topoh", "mask_ip", "YOUR_SERVER_IP")
modparam("topoh", "mask_callid", 1) /* Mask Call-ID */
modparam("topoh", "sanity_checks", 1) /* Validate before unmasking */
loadmodule "topoh.so" modparam("topoh", "mask_ip", "YOUR_SERVER_IP")
modparam("topoh", "mask_callid", 1) /* Mask Call-ID */
modparam("topoh", "sanity_checks", 1) /* Validate before unmasking */
BEFORE (external peer sees internal IPs): Via: SIP/2.0/UDP 10.0.0.10:5060 ← Internal Asterisk IP exposed! Contact: <sip:[email protected]> ← Internal IP exposed! Record-Route: <sip:10.0.0.10;lr> ← Internal IP exposed! AFTER (topoh masks everything): Via: SIP/2.0/UDP YOUR_SERVER_IP:5060 Contact: <sip:1001@YOUR_SERVER_IP> Record-Route: <sip:YOUR_SERVER_IP;lr>
BEFORE (external peer sees internal IPs): Via: SIP/2.0/UDP 10.0.0.10:5060 ← Internal Asterisk IP exposed! Contact: <sip:[email protected]> ← Internal IP exposed! Record-Route: <sip:10.0.0.10;lr> ← Internal IP exposed! AFTER (topoh masks everything): Via: SIP/2.0/UDP YOUR_SERVER_IP:5060 Contact: <sip:1001@YOUR_SERVER_IP> Record-Route: <sip:YOUR_SERVER_IP;lr>
BEFORE (external peer sees internal IPs): Via: SIP/2.0/UDP 10.0.0.10:5060 ← Internal Asterisk IP exposed! Contact: <sip:[email protected]> ← Internal IP exposed! Record-Route: <sip:10.0.0.10;lr> ← Internal IP exposed! AFTER (topoh masks everything): Via: SIP/2.0/UDP YOUR_SERVER_IP:5060 Contact: <sip:1001@YOUR_SERVER_IP> Record-Route: <sip:YOUR_SERVER_IP;lr>
route[TO_ASTERISK] { /* Add custom headers for Asterisk to use */ append_hf("X-Caller-IP: $si\r\n"); append_hf("X-Original-URI: $ou\r\n"); append_hf("X-Auth-User: $au\r\n"); /* Set P-Asserted-Identity for the backend */ remove_hf("P-Asserted-Identity"); append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); /* Remove headers that shouldn't reach the backend */ remove_hf("Proxy-Authorization"); /* Route to backend */ $du = "sip:10.0.0.10:5060"; route(RELAY);
}
route[TO_ASTERISK] { /* Add custom headers for Asterisk to use */ append_hf("X-Caller-IP: $si\r\n"); append_hf("X-Original-URI: $ou\r\n"); append_hf("X-Auth-User: $au\r\n"); /* Set P-Asserted-Identity for the backend */ remove_hf("P-Asserted-Identity"); append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); /* Remove headers that shouldn't reach the backend */ remove_hf("Proxy-Authorization"); /* Route to backend */ $du = "sip:10.0.0.10:5060"; route(RELAY);
}
route[TO_ASTERISK] { /* Add custom headers for Asterisk to use */ append_hf("X-Caller-IP: $si\r\n"); append_hf("X-Original-URI: $ou\r\n"); append_hf("X-Auth-User: $au\r\n"); /* Set P-Asserted-Identity for the backend */ remove_hf("P-Asserted-Identity"); append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); /* Remove headers that shouldn't reach the backend */ remove_hf("Proxy-Authorization"); /* Route to backend */ $du = "sip:10.0.0.10:5060"; route(RELAY);
}
route[DOMAIN_ROUTE] { /* Route based on destination domain */ if ($rd == "customer-a.com") { $du = "sip:10.0.0.10:5060"; /* Customer A's Asterisk */ xlog("L_INFO", "Routing to Customer A backend\n"); } else if ($rd == "customer-b.com") { $du = "sip:10.0.0.11:5060"; /* Customer B's Asterisk */ xlog("L_INFO", "Routing to Customer B backend\n"); } else if ($rd == "customer-c.com") { /* Customer C uses dispatcher set 3 */ if (!ds_select_dst(3, 0)) { sl_send_reply(503, "Service Unavailable"); exit; } } else { /* Unknown domain */ sl_send_reply(404, "Domain Not Served"); exit; } route(RELAY); exit;
}
route[DOMAIN_ROUTE] { /* Route based on destination domain */ if ($rd == "customer-a.com") { $du = "sip:10.0.0.10:5060"; /* Customer A's Asterisk */ xlog("L_INFO", "Routing to Customer A backend\n"); } else if ($rd == "customer-b.com") { $du = "sip:10.0.0.11:5060"; /* Customer B's Asterisk */ xlog("L_INFO", "Routing to Customer B backend\n"); } else if ($rd == "customer-c.com") { /* Customer C uses dispatcher set 3 */ if (!ds_select_dst(3, 0)) { sl_send_reply(503, "Service Unavailable"); exit; } } else { /* Unknown domain */ sl_send_reply(404, "Domain Not Served"); exit; } route(RELAY); exit;
}
route[DOMAIN_ROUTE] { /* Route based on destination domain */ if ($rd == "customer-a.com") { $du = "sip:10.0.0.10:5060"; /* Customer A's Asterisk */ xlog("L_INFO", "Routing to Customer A backend\n"); } else if ($rd == "customer-b.com") { $du = "sip:10.0.0.11:5060"; /* Customer B's Asterisk */ xlog("L_INFO", "Routing to Customer B backend\n"); } else if ($rd == "customer-c.com") { /* Customer C uses dispatcher set 3 */ if (!ds_select_dst(3, 0)) { sl_send_reply(503, "Service Unavailable"); exit; } } else { /* Unknown domain */ sl_send_reply(404, "Domain Not Served"); exit; } route(RELAY); exit;
}
loadmodule "domain.so"
modparam("domain", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
loadmodule "domain.so"
modparam("domain", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
loadmodule "domain.so"
modparam("domain", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
-- Add served domains
INSERT INTO domain (domain) VALUES ('customer-a.com');
INSERT INTO domain (domain) VALUES ('customer-b.com');
-- Add served domains
INSERT INTO domain (domain) VALUES ('customer-a.com');
INSERT INTO domain (domain) VALUES ('customer-b.com');
-- Add served domains
INSERT INTO domain (domain) VALUES ('customer-a.com');
INSERT INTO domain (domain) VALUES ('customer-b.com');
/* Check if we serve this domain */
if (!is_domain_local("$rd")) { sl_send_reply(403, "Domain Not Served"); exit;
}
/* Check if we serve this domain */
if (!is_domain_local("$rd")) { sl_send_reply(403, "Domain Not Served"); exit;
}
/* Check if we serve this domain */
if (!is_domain_local("$rd")) { sl_send_reply(403, "Domain Not Served"); exit;
}
/* Integrated SBC config: Kamailio in front of 2 Asterisk servers */ route[ROUTE_TO_BACKEND] { /* Determine which backend based on called number pattern */ if ($rU =~ "^1[0-9]{3}$") { /* 4-digit extensions starting with 1 → Asterisk 1 */ $du = "sip:10.0.0.10:5060"; xlog("L_INFO", "Routing ext $rU → Asterisk 1\n"); } else if ($rU =~ "^2[0-9]{3}$") { /* 4-digit extensions starting with 2 → Asterisk 2 */ $du = "sip:10.0.0.11:5060"; xlog("L_INFO", "Routing ext $rU → Asterisk 2\n"); } else if ($rU =~ "^[0-9]{10,}$") { /* External number (10+ digits) → dispatcher set for trunks */ if (!ds_select_dst(2, 4)) { /* Set 2, priority algorithm */ sl_send_reply(503, "No Trunks Available"); exit; } xlog("L_INFO", "Routing PSTN $rU → trunk $du\n"); } else { sl_send_reply(404, "Number Not Routable"); exit; } /* Add integration headers */ append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); append_hf("X-Kamailio-Server: sbc01\r\n"); /* Enable dialog tracking */ setflag(4); dlg_manage(); /* Manage media */ route(NATMANAGE); /* Forward with failover */ t_on_failure("BACKEND_FAILURE"); route(RELAY); exit;
} failure_route[BACKEND_FAILURE] { if (t_is_canceled()) exit; if (t_check_status("408|5[0-9][0-9]")) { xlog("L_WARN", "Backend $du failed ($T_reply_code)\n"); /* Try alternate backend */ if ($rU =~ "^1[0-9]{3}$") { /* Asterisk 1 failed — try Asterisk 2 */ $du = "sip:10.0.0.11:5060"; } else if ($rU =~ "^2[0-9]{3}$") { /* Asterisk 2 failed — try Asterisk 1 */ $du = "sip:10.0.0.10:5060"; } else { /* Trunk failover — use dispatcher */ if (ds_next_dst()) { t_on_failure("BACKEND_FAILURE"); route(RELAY); exit; } t_reply(503, "All Backends Down"); exit; } t_on_failure("FINAL_FAILURE"); route(RELAY); exit; }
} failure_route[FINAL_FAILURE] { if (t_is_canceled()) exit; xlog("L_ERR", "All backends failed for $rU\n");
}
/* Integrated SBC config: Kamailio in front of 2 Asterisk servers */ route[ROUTE_TO_BACKEND] { /* Determine which backend based on called number pattern */ if ($rU =~ "^1[0-9]{3}$") { /* 4-digit extensions starting with 1 → Asterisk 1 */ $du = "sip:10.0.0.10:5060"; xlog("L_INFO", "Routing ext $rU → Asterisk 1\n"); } else if ($rU =~ "^2[0-9]{3}$") { /* 4-digit extensions starting with 2 → Asterisk 2 */ $du = "sip:10.0.0.11:5060"; xlog("L_INFO", "Routing ext $rU → Asterisk 2\n"); } else if ($rU =~ "^[0-9]{10,}$") { /* External number (10+ digits) → dispatcher set for trunks */ if (!ds_select_dst(2, 4)) { /* Set 2, priority algorithm */ sl_send_reply(503, "No Trunks Available"); exit; } xlog("L_INFO", "Routing PSTN $rU → trunk $du\n"); } else { sl_send_reply(404, "Number Not Routable"); exit; } /* Add integration headers */ append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); append_hf("X-Kamailio-Server: sbc01\r\n"); /* Enable dialog tracking */ setflag(4); dlg_manage(); /* Manage media */ route(NATMANAGE); /* Forward with failover */ t_on_failure("BACKEND_FAILURE"); route(RELAY); exit;
} failure_route[BACKEND_FAILURE] { if (t_is_canceled()) exit; if (t_check_status("408|5[0-9][0-9]")) { xlog("L_WARN", "Backend $du failed ($T_reply_code)\n"); /* Try alternate backend */ if ($rU =~ "^1[0-9]{3}$") { /* Asterisk 1 failed — try Asterisk 2 */ $du = "sip:10.0.0.11:5060"; } else if ($rU =~ "^2[0-9]{3}$") { /* Asterisk 2 failed — try Asterisk 1 */ $du = "sip:10.0.0.10:5060"; } else { /* Trunk failover — use dispatcher */ if (ds_next_dst()) { t_on_failure("BACKEND_FAILURE"); route(RELAY); exit; } t_reply(503, "All Backends Down"); exit; } t_on_failure("FINAL_FAILURE"); route(RELAY); exit; }
} failure_route[FINAL_FAILURE] { if (t_is_canceled()) exit; xlog("L_ERR", "All backends failed for $rU\n");
}
/* Integrated SBC config: Kamailio in front of 2 Asterisk servers */ route[ROUTE_TO_BACKEND] { /* Determine which backend based on called number pattern */ if ($rU =~ "^1[0-9]{3}$") { /* 4-digit extensions starting with 1 → Asterisk 1 */ $du = "sip:10.0.0.10:5060"; xlog("L_INFO", "Routing ext $rU → Asterisk 1\n"); } else if ($rU =~ "^2[0-9]{3}$") { /* 4-digit extensions starting with 2 → Asterisk 2 */ $du = "sip:10.0.0.11:5060"; xlog("L_INFO", "Routing ext $rU → Asterisk 2\n"); } else if ($rU =~ "^[0-9]{10,}$") { /* External number (10+ digits) → dispatcher set for trunks */ if (!ds_select_dst(2, 4)) { /* Set 2, priority algorithm */ sl_send_reply(503, "No Trunks Available"); exit; } xlog("L_INFO", "Routing PSTN $rU → trunk $du\n"); } else { sl_send_reply(404, "Number Not Routable"); exit; } /* Add integration headers */ append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); append_hf("X-Kamailio-Server: sbc01\r\n"); /* Enable dialog tracking */ setflag(4); dlg_manage(); /* Manage media */ route(NATMANAGE); /* Forward with failover */ t_on_failure("BACKEND_FAILURE"); route(RELAY); exit;
} failure_route[BACKEND_FAILURE] { if (t_is_canceled()) exit; if (t_check_status("408|5[0-9][0-9]")) { xlog("L_WARN", "Backend $du failed ($T_reply_code)\n"); /* Try alternate backend */ if ($rU =~ "^1[0-9]{3}$") { /* Asterisk 1 failed — try Asterisk 2 */ $du = "sip:10.0.0.11:5060"; } else if ($rU =~ "^2[0-9]{3}$") { /* Asterisk 2 failed — try Asterisk 1 */ $du = "sip:10.0.0.10:5060"; } else { /* Trunk failover — use dispatcher */ if (ds_next_dst()) { t_on_failure("BACKEND_FAILURE"); route(RELAY); exit; } t_reply(503, "All Backends Down"); exit; } t_on_failure("FINAL_FAILURE"); route(RELAY); exit; }
} failure_route[FINAL_FAILURE] { if (t_is_canceled()) exit; xlog("L_ERR", "All backends failed for $rU\n");
}
# Server information
kamcmd core.version
kamcmd core.uptime
kamcmd core.info # Process management
kamcmd core.ps # List processes
kamcmd core.psx # Extended process list # Statistics
kamcmd stats.get_statistics all # All statistics
kamcmd stats.get_statistics core: # Core statistics
kamcmd stats.get_statistics tmx: # Transaction statistics
kamcmd stats.get_statistics dialog: # Dialog (call) statistics
kamcmd stats.get_statistics usrloc: # Registration statistics
kamcmd stats.get_statistics dispatcher: # Dispatcher statistics # User location (registrations)
kamcmd ul.dump # Dump all registrations
kamcmd ul.lookup location 1001 # Look up specific user
kamcmd ul.rm location 1001 # Remove a registration
kamcmd ul.flush # Flush expired contacts # Dispatcher
kamcmd dispatcher.list # List all destinations with -weight: 500;">status
kamcmd dispatcher.reload # Reload from database
kamcmd dispatcher.set_state ap 1 sip:10.0.0.10:5060 # Set Active
kamcmd dispatcher.set_state ip 1 sip:10.0.0.10:5060 # Set Inactive # Dialog
kamcmd dlg.list # List active calls
kamcmd dlg.stats_active # Active call count
kamcmd dlg.end_dlg <h_entry> <h_id> # Terminate a call # TLS
kamcmd tls.list # List TLS connections
kamcmd tls.info # TLS module info # Memory
kamcmd pkg.stats # Per-process memory
kamcmd mod.stats # Per-module memory # Configuration reload
kamcmd cfg.sets # List config variables
# Server information
kamcmd core.version
kamcmd core.uptime
kamcmd core.info # Process management
kamcmd core.ps # List processes
kamcmd core.psx # Extended process list # Statistics
kamcmd stats.get_statistics all # All statistics
kamcmd stats.get_statistics core: # Core statistics
kamcmd stats.get_statistics tmx: # Transaction statistics
kamcmd stats.get_statistics dialog: # Dialog (call) statistics
kamcmd stats.get_statistics usrloc: # Registration statistics
kamcmd stats.get_statistics dispatcher: # Dispatcher statistics # User location (registrations)
kamcmd ul.dump # Dump all registrations
kamcmd ul.lookup location 1001 # Look up specific user
kamcmd ul.rm location 1001 # Remove a registration
kamcmd ul.flush # Flush expired contacts # Dispatcher
kamcmd dispatcher.list # List all destinations with -weight: 500;">status
kamcmd dispatcher.reload # Reload from database
kamcmd dispatcher.set_state ap 1 sip:10.0.0.10:5060 # Set Active
kamcmd dispatcher.set_state ip 1 sip:10.0.0.10:5060 # Set Inactive # Dialog
kamcmd dlg.list # List active calls
kamcmd dlg.stats_active # Active call count
kamcmd dlg.end_dlg <h_entry> <h_id> # Terminate a call # TLS
kamcmd tls.list # List TLS connections
kamcmd tls.info # TLS module info # Memory
kamcmd pkg.stats # Per-process memory
kamcmd mod.stats # Per-module memory # Configuration reload
kamcmd cfg.sets # List config variables
# Server information
kamcmd core.version
kamcmd core.uptime
kamcmd core.info # Process management
kamcmd core.ps # List processes
kamcmd core.psx # Extended process list # Statistics
kamcmd stats.get_statistics all # All statistics
kamcmd stats.get_statistics core: # Core statistics
kamcmd stats.get_statistics tmx: # Transaction statistics
kamcmd stats.get_statistics dialog: # Dialog (call) statistics
kamcmd stats.get_statistics usrloc: # Registration statistics
kamcmd stats.get_statistics dispatcher: # Dispatcher statistics # User location (registrations)
kamcmd ul.dump # Dump all registrations
kamcmd ul.lookup location 1001 # Look up specific user
kamcmd ul.rm location 1001 # Remove a registration
kamcmd ul.flush # Flush expired contacts # Dispatcher
kamcmd dispatcher.list # List all destinations with -weight: 500;">status
kamcmd dispatcher.reload # Reload from database
kamcmd dispatcher.set_state ap 1 sip:10.0.0.10:5060 # Set Active
kamcmd dispatcher.set_state ip 1 sip:10.0.0.10:5060 # Set Inactive # Dialog
kamcmd dlg.list # List active calls
kamcmd dlg.stats_active # Active call count
kamcmd dlg.end_dlg <h_entry> <h_id> # Terminate a call # TLS
kamcmd tls.list # List TLS connections
kamcmd tls.info # TLS module info # Memory
kamcmd pkg.stats # Per-process memory
kamcmd mod.stats # Per-module memory # Configuration reload
kamcmd cfg.sets # List config variables
# User management
kamctl add 1001 password123 # Add user
kamctl rm 1001 # Remove user
kamctl passwd 1001 newpass # Change password
kamctl showdb subscriber # List all users # Address/ACL management
kamctl address add 1 10.0.0.5 32 5060 "Asterisk Backend"
kamctl address show
kamctl address reload # Dispatcher management
kamctl dispatcher add 1 sip:10.0.0.10:5060 0 0 "" "Backend 1"
kamctl dispatcher show
kamctl dispatcher reload # Database operations
kamctl db show version # Show table versions
kamctl db exec "SELECT * FROM location" # Raw SQL query # Online monitoring
kamctl monitor # Real-time stats display
kamctl stats # One-shot statistics # Fifo commands
kamctl fifo which # List available FIFO commands
# User management
kamctl add 1001 password123 # Add user
kamctl rm 1001 # Remove user
kamctl passwd 1001 newpass # Change password
kamctl showdb subscriber # List all users # Address/ACL management
kamctl address add 1 10.0.0.5 32 5060 "Asterisk Backend"
kamctl address show
kamctl address reload # Dispatcher management
kamctl dispatcher add 1 sip:10.0.0.10:5060 0 0 "" "Backend 1"
kamctl dispatcher show
kamctl dispatcher reload # Database operations
kamctl db show version # Show table versions
kamctl db exec "SELECT * FROM location" # Raw SQL query # Online monitoring
kamctl monitor # Real-time stats display
kamctl stats # One-shot statistics # Fifo commands
kamctl fifo which # List available FIFO commands
# User management
kamctl add 1001 password123 # Add user
kamctl rm 1001 # Remove user
kamctl passwd 1001 newpass # Change password
kamctl showdb subscriber # List all users # Address/ACL management
kamctl address add 1 10.0.0.5 32 5060 "Asterisk Backend"
kamctl address show
kamctl address reload # Dispatcher management
kamctl dispatcher add 1 sip:10.0.0.10:5060 0 0 "" "Backend 1"
kamctl dispatcher show
kamctl dispatcher reload # Database operations
kamctl db show version # Show table versions
kamctl db exec "SELECT * FROM location" # Raw SQL query # Online monitoring
kamctl monitor # Real-time stats display
kamctl stats # One-shot statistics # Fifo commands
kamctl fifo which # List available FIFO commands
loadmodule "jsonrpcs.so"
modparam("jsonrpcs", "pretty_format", 1)
modparam("jsonrpcs", "transport", 1) /* Optional: HTTP-based JSONRPC */
loadmodule "xhttp.so"
/* Add to event_route[xhttp:request] or use separate port */
loadmodule "jsonrpcs.so"
modparam("jsonrpcs", "pretty_format", 1)
modparam("jsonrpcs", "transport", 1) /* Optional: HTTP-based JSONRPC */
loadmodule "xhttp.so"
/* Add to event_route[xhttp:request] or use separate port */
loadmodule "jsonrpcs.so"
modparam("jsonrpcs", "pretty_format", 1)
modparam("jsonrpcs", "transport", 1) /* Optional: HTTP-based JSONRPC */
loadmodule "xhttp.so"
/* Add to event_route[xhttp:request] or use separate port */
# Using kamcmd (which uses JSONRPC internally)
kamcmd -s tcp:localhost:2049 core.version # Using -weight: 500;">curl (if HTTP transport is enabled)
-weight: 500;">curl -s -X POST http://localhost:5060/jsonrpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"core.version","id":1}' # Get active calls count
-weight: 500;">curl -s -X POST http://localhost:5060/jsonrpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"stats.get_statistics","params":[["dialog:"]],"id":1}'
# Using kamcmd (which uses JSONRPC internally)
kamcmd -s tcp:localhost:2049 core.version # Using -weight: 500;">curl (if HTTP transport is enabled)
-weight: 500;">curl -s -X POST http://localhost:5060/jsonrpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"core.version","id":1}' # Get active calls count
-weight: 500;">curl -s -X POST http://localhost:5060/jsonrpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"stats.get_statistics","params":[["dialog:"]],"id":1}'
# Using kamcmd (which uses JSONRPC internally)
kamcmd -s tcp:localhost:2049 core.version # Using -weight: 500;">curl (if HTTP transport is enabled)
-weight: 500;">curl -s -X POST http://localhost:5060/jsonrpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"core.version","id":1}' # Get active calls count
-weight: 500;">curl -s -X POST http://localhost:5060/jsonrpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"stats.get_statistics","params":[["dialog:"]],"id":1}'
# Install the Kamailio Prometheus exporter
# Option 1: Go binary
-weight: 500;">wget https://github.com/florentchauveau/kamailio_exporter/releases/latest/download/kamailio_exporter_linux_amd64
chmod +x kamailio_exporter_linux_amd64
mv kamailio_exporter_linux_amd64 /usr/local/bin/kamailio_exporter # Create systemd -weight: 500;">service
cat > /etc/systemd/system/kamailio-exporter.-weight: 500;">service << 'EOF'
[Unit]
Description=Kamailio Prometheus Exporter
After=kamailio.-weight: 500;">service [Service]
ExecStart=/usr/local/bin/kamailio_exporter \ --kamailio.address=unix:/var/run/kamailio/kamailio_ctl \ --web.listen-address=:9494
Restart=always
User=kamailio [Install]
WantedBy=multi-user.target
EOF -weight: 500;">systemctl daemon-reload
-weight: 500;">systemctl -weight: 500;">enable --now kamailio-exporter
# Install the Kamailio Prometheus exporter
# Option 1: Go binary
-weight: 500;">wget https://github.com/florentchauveau/kamailio_exporter/releases/latest/download/kamailio_exporter_linux_amd64
chmod +x kamailio_exporter_linux_amd64
mv kamailio_exporter_linux_amd64 /usr/local/bin/kamailio_exporter # Create systemd -weight: 500;">service
cat > /etc/systemd/system/kamailio-exporter.-weight: 500;">service << 'EOF'
[Unit]
Description=Kamailio Prometheus Exporter
After=kamailio.-weight: 500;">service [Service]
ExecStart=/usr/local/bin/kamailio_exporter \ --kamailio.address=unix:/var/run/kamailio/kamailio_ctl \ --web.listen-address=:9494
Restart=always
User=kamailio [Install]
WantedBy=multi-user.target
EOF -weight: 500;">systemctl daemon-reload
-weight: 500;">systemctl -weight: 500;">enable --now kamailio-exporter
# Install the Kamailio Prometheus exporter
# Option 1: Go binary
-weight: 500;">wget https://github.com/florentchauveau/kamailio_exporter/releases/latest/download/kamailio_exporter_linux_amd64
chmod +x kamailio_exporter_linux_amd64
mv kamailio_exporter_linux_amd64 /usr/local/bin/kamailio_exporter # Create systemd -weight: 500;">service
cat > /etc/systemd/system/kamailio-exporter.-weight: 500;">service << 'EOF'
[Unit]
Description=Kamailio Prometheus Exporter
After=kamailio.-weight: 500;">service [Service]
ExecStart=/usr/local/bin/kamailio_exporter \ --kamailio.address=unix:/var/run/kamailio/kamailio_ctl \ --web.listen-address=:9494
Restart=always
User=kamailio [Install]
WantedBy=multi-user.target
EOF -weight: 500;">systemctl daemon-reload
-weight: 500;">systemctl -weight: 500;">enable --now kamailio-exporter
# /etc/prometheus/prometheus.yml
scrape_configs: - job_name: 'kamailio' static_configs: - targets: ['localhost:9494'] scrape_interval: 15s
# /etc/prometheus/prometheus.yml
scrape_configs: - job_name: 'kamailio' static_configs: - targets: ['localhost:9494'] scrape_interval: 15s
# /etc/prometheus/prometheus.yml
scrape_configs: - job_name: 'kamailio' static_configs: - targets: ['localhost:9494'] scrape_interval: 15s
# /etc/rsyslog.d/kamailio.conf
local0.* /var/log/kamailio/kamailio.log
# /etc/rsyslog.d/kamailio.conf
local0.* /var/log/kamailio/kamailio.log
# /etc/rsyslog.d/kamailio.conf
local0.* /var/log/kamailio/kamailio.log
# Authentication failures (potential brute force)
grep "AUTH_FAILED" /var/log/kamailio/kamailio.log | tail -20 # Pike blocks (flood detection)
grep "PIKE" /var/log/kamailio/kamailio.log | tail -20 # Dispatcher failures (backend down)
grep "DISPATCH.*failed" /var/log/kamailio/kamailio.log | tail -20 # Registration events
grep "REGISTER" /var/log/kamailio/kamailio.log | tail -20 # Call failures
grep "failed.*T_reply_code" /var/log/kamailio/kamailio.log | tail -20
# Authentication failures (potential brute force)
grep "AUTH_FAILED" /var/log/kamailio/kamailio.log | tail -20 # Pike blocks (flood detection)
grep "PIKE" /var/log/kamailio/kamailio.log | tail -20 # Dispatcher failures (backend down)
grep "DISPATCH.*failed" /var/log/kamailio/kamailio.log | tail -20 # Registration events
grep "REGISTER" /var/log/kamailio/kamailio.log | tail -20 # Call failures
grep "failed.*T_reply_code" /var/log/kamailio/kamailio.log | tail -20
# Authentication failures (potential brute force)
grep "AUTH_FAILED" /var/log/kamailio/kamailio.log | tail -20 # Pike blocks (flood detection)
grep "PIKE" /var/log/kamailio/kamailio.log | tail -20 # Dispatcher failures (backend down)
grep "DISPATCH.*failed" /var/log/kamailio/kamailio.log | tail -20 # Registration events
grep "REGISTER" /var/log/kamailio/kamailio.log | tail -20 # Call failures
grep "failed.*T_reply_code" /var/log/kamailio/kamailio.log | tail -20
cat > /etc/logrotate.d/kamailio << 'EOF'
/var/log/kamailio/kamailio.log { daily rotate 14 compress delaycompress missingok notifempty postrotate /usr/bin/-weight: 500;">systemctl reload rsyslog > /dev/null 2>&1 || true endscript
}
EOF
cat > /etc/logrotate.d/kamailio << 'EOF'
/var/log/kamailio/kamailio.log { daily rotate 14 compress delaycompress missingok notifempty postrotate /usr/bin/-weight: 500;">systemctl reload rsyslog > /dev/null 2>&1 || true endscript
}
EOF
cat > /etc/logrotate.d/kamailio << 'EOF'
/var/log/kamailio/kamailio.log { daily rotate 14 compress delaycompress missingok notifempty postrotate /usr/bin/-weight: 500;">systemctl reload rsyslog > /dev/null 2>&1 || true endscript
}
EOF
loadmodule "siptrace.so" modparam("siptrace", "duplicate_uri", "sip:HOMER_IP:9060")
modparam("siptrace", "hep_mode_on", 1)
modparam("siptrace", "hep_version", 3)
modparam("siptrace", "hep_capture_id", 100)
modparam("siptrace", "trace_on", 1)
modparam("siptrace", "trace_flag", 22) /* In request_route — -weight: 500;">enable tracing for all traffic */
request_route { setflag(22); /* Enable SIP trace */ sip_trace(); /* ... */
}
loadmodule "siptrace.so" modparam("siptrace", "duplicate_uri", "sip:HOMER_IP:9060")
modparam("siptrace", "hep_mode_on", 1)
modparam("siptrace", "hep_version", 3)
modparam("siptrace", "hep_capture_id", 100)
modparam("siptrace", "trace_on", 1)
modparam("siptrace", "trace_flag", 22) /* In request_route — -weight: 500;">enable tracing for all traffic */
request_route { setflag(22); /* Enable SIP trace */ sip_trace(); /* ... */
}
loadmodule "siptrace.so" modparam("siptrace", "duplicate_uri", "sip:HOMER_IP:9060")
modparam("siptrace", "hep_mode_on", 1)
modparam("siptrace", "hep_version", 3)
modparam("siptrace", "hep_capture_id", 100)
modparam("siptrace", "trace_on", 1)
modparam("siptrace", "trace_flag", 22) /* In request_route — -weight: 500;">enable tracing for all traffic */
request_route { setflag(22); /* Enable SIP trace */ sip_trace(); /* ... */
}
# Get current debug level
kamcmd cfg.get core debug # Set debug to maximum (temporary — resets on -weight: 500;">restart)
kamcmd cfg.seti core debug 6 # Set back to production level
kamcmd cfg.seti core debug 2
# Get current debug level
kamcmd cfg.get core debug # Set debug to maximum (temporary — resets on -weight: 500;">restart)
kamcmd cfg.seti core debug 6 # Set back to production level
kamcmd cfg.seti core debug 2
# Get current debug level
kamcmd cfg.get core debug # Set debug to maximum (temporary — resets on -weight: 500;">restart)
kamcmd cfg.seti core debug 6 # Set back to production level
kamcmd cfg.seti core debug 2
/* Add strategic xlog statements */
request_route { xlog("L_INFO", ">>> $rm from $fU@$si:$sp to $rU R-URI=$ru\n"); /* Trace specific call by Call-ID */ if ($ci =~ "abc123") { xlog("L_DBG", "[TRACE] Headers:\n$mb\n"); }
} route[RELAY] { xlog("L_INFO", "RELAY: $rm → $du (branch=$T_branch_idx)\n");
} onreply_route[MANAGE_REPLY] { xlog("L_INFO", "<<< Reply $T_reply_code $T_reply_reason for $rm ($ci)\n");
} failure_route[MANAGE_FAILURE] { xlog("L_WARN", "!!! FAILURE: $T_reply_code for $rU from $du ($ci)\n");
}
/* Add strategic xlog statements */
request_route { xlog("L_INFO", ">>> $rm from $fU@$si:$sp to $rU R-URI=$ru\n"); /* Trace specific call by Call-ID */ if ($ci =~ "abc123") { xlog("L_DBG", "[TRACE] Headers:\n$mb\n"); }
} route[RELAY] { xlog("L_INFO", "RELAY: $rm → $du (branch=$T_branch_idx)\n");
} onreply_route[MANAGE_REPLY] { xlog("L_INFO", "<<< Reply $T_reply_code $T_reply_reason for $rm ($ci)\n");
} failure_route[MANAGE_FAILURE] { xlog("L_WARN", "!!! FAILURE: $T_reply_code for $rU from $du ($ci)\n");
}
/* Add strategic xlog statements */
request_route { xlog("L_INFO", ">>> $rm from $fU@$si:$sp to $rU R-URI=$ru\n"); /* Trace specific call by Call-ID */ if ($ci =~ "abc123") { xlog("L_DBG", "[TRACE] Headers:\n$mb\n"); }
} route[RELAY] { xlog("L_INFO", "RELAY: $rm → $du (branch=$T_branch_idx)\n");
} onreply_route[MANAGE_REPLY] { xlog("L_INFO", "<<< Reply $T_reply_code $T_reply_reason for $rm ($ci)\n");
} failure_route[MANAGE_FAILURE] { xlog("L_WARN", "!!! FAILURE: $T_reply_code for $rU from $du ($ci)\n");
}
/* In kamailio.cfg — global parameter */
cfgtrace=1 /* Trace all route block execution */
/* In kamailio.cfg — global parameter */
cfgtrace=1 /* Trace all route block execution */
/* In kamailio.cfg — global parameter */
cfgtrace=1 /* Trace all route block execution */
if ($ci =~ "debug-this-call") { setflag(25); /* Use cfgtrace for this specific request */
}
if ($ci =~ "debug-this-call") { setflag(25); /* Use cfgtrace for this specific request */
}
if ($ci =~ "debug-this-call") { setflag(25); /* Use cfgtrace for this specific request */
}
/* Ensure Record-Route is always added */
if (is_method("INVITE|SUBSCRIBE")) { record_route();
} /* Handle stale in-dialog requests gracefully */
if (has_totag()) { if (!loose_route()) { if (is_method("ACK")) { if (t_check_trans()) exit; exit; /* Silently absorb orphan ACKs */ } if (is_method("BYE")) { /* Reply 481 but don't log as error — normal after -weight: 500;">restart */ sl_send_reply(481, "Call Leg Does Not Exist"); exit; } }
}
/* Ensure Record-Route is always added */
if (is_method("INVITE|SUBSCRIBE")) { record_route();
} /* Handle stale in-dialog requests gracefully */
if (has_totag()) { if (!loose_route()) { if (is_method("ACK")) { if (t_check_trans()) exit; exit; /* Silently absorb orphan ACKs */ } if (is_method("BYE")) { /* Reply 481 but don't log as error — normal after -weight: 500;">restart */ sl_send_reply(481, "Call Leg Does Not Exist"); exit; } }
}
/* Ensure Record-Route is always added */
if (is_method("INVITE|SUBSCRIBE")) { record_route();
} /* Handle stale in-dialog requests gracefully */
if (has_totag()) { if (!loose_route()) { if (is_method("ACK")) { if (t_check_trans()) exit; exit; /* Silently absorb orphan ACKs */ } if (is_method("BYE")) { /* Reply 481 but don't log as error — normal after -weight: 500;">restart */ sl_send_reply(481, "Call Leg Does Not Exist"); exit; } }
}
# Check if the user exists in subscriber table
kamctl showdb subscriber | grep 1001 # Check realm mismatch
# The realm in www_authorize/proxy_authorize MUST match
# what the SIP phone uses
# Check if the user exists in subscriber table
kamctl showdb subscriber | grep 1001 # Check realm mismatch
# The realm in www_authorize/proxy_authorize MUST match
# what the SIP phone uses
# Check if the user exists in subscriber table
kamctl showdb subscriber | grep 1001 # Check realm mismatch
# The realm in www_authorize/proxy_authorize MUST match
# what the SIP phone uses
/* Ensure realm matches your domain/IP */
if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { /* Try with IP as realm if domain fails */ if (!proxy_authorize("YOUR_SERVER_IP", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; }
}
/* Ensure realm matches your domain/IP */
if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { /* Try with IP as realm if domain fails */ if (!proxy_authorize("YOUR_SERVER_IP", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; }
}
/* Ensure realm matches your domain/IP */
if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { /* Try with IP as realm if domain fails */ if (!proxy_authorize("YOUR_SERVER_IP", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; }
}
# Watch for looping messages
grep "483" /var/log/kamailio/kamailio.log # Check Via headers in the SIP trace — if you see
# the same server IP appearing multiple times, it's a loop
# Watch for looping messages
grep "483" /var/log/kamailio/kamailio.log # Check Via headers in the SIP trace — if you see
# the same server IP appearing multiple times, it's a loop
# Watch for looping messages
grep "483" /var/log/kamailio/kamailio.log # Check Via headers in the SIP trace — if you see
# the same server IP appearing multiple times, it's a loop
/* Ensure mf_process_maxfwd_header is at the TOP of request_route */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } /* Check for loop: same message arriving twice */ if (is_method("INVITE") && !has_totag()) { if (t_lookup_request()) { /* Already processing this transaction — retransmission */ exit; } }
}
/* Ensure mf_process_maxfwd_header is at the TOP of request_route */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } /* Check for loop: same message arriving twice */ if (is_method("INVITE") && !has_totag()) { if (t_lookup_request()) { /* Already processing this transaction — retransmission */ exit; } }
}
/* Ensure mf_process_maxfwd_header is at the TOP of request_route */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } /* Check for loop: same message arriving twice */ if (is_method("INVITE") && !has_totag()) { if (t_lookup_request()) { /* Already processing this transaction — retransmission */ exit; } }
}
# 1. Is Kamailio listening?
ss -ulnp | grep 5060 # 2. Check for registration attempts in logs
grep "REGISTER" /var/log/kamailio/kamailio.log | tail -20 # 3. Verify user exists
kamctl showdb subscriber # 4. Check current registrations
kamcmd ul.dump # 5. Test registration manually
sipsak -U -C sip:test@YOUR_SERVER_IP \ -s sip:1001@YOUR_SERVER_IP \ -u 1001 -a password123
# 1. Is Kamailio listening?
ss -ulnp | grep 5060 # 2. Check for registration attempts in logs
grep "REGISTER" /var/log/kamailio/kamailio.log | tail -20 # 3. Verify user exists
kamctl showdb subscriber # 4. Check current registrations
kamcmd ul.dump # 5. Test registration manually
sipsak -U -C sip:test@YOUR_SERVER_IP \ -s sip:1001@YOUR_SERVER_IP \ -u 1001 -a password123
# 1. Is Kamailio listening?
ss -ulnp | grep 5060 # 2. Check for registration attempts in logs
grep "REGISTER" /var/log/kamailio/kamailio.log | tail -20 # 3. Verify user exists
kamctl showdb subscriber # 4. Check current registrations
kamcmd ul.dump # 5. Test registration manually
sipsak -U -C sip:test@YOUR_SERVER_IP \ -s sip:1001@YOUR_SERVER_IP \ -u 1001 -a password123
# 1. Is RTPEngine running?
-weight: 500;">systemctl -weight: 500;">status rtpengine
ss -ulnp | grep rtpengine # 2. Check RTPEngine statistics
echo "list totals" | nc -u 127.0.0.1 2223 # 3. Are RTP ports open in the firewall?
# Port range 10000-20000 UDP must be open
ss -ulnp | grep -E "1[0-9]{4}|20000" # 4. Check if NAT is being detected
grep "NAT detected" /var/log/kamailio/kamailio.log | tail -10 # 5. Capture SIP to check SDP
ngrep -W byline -d any port 5060 | grep -A20 "INVITE"
# Look at c= and m= lines — do they contain private IPs?
# 1. Is RTPEngine running?
-weight: 500;">systemctl -weight: 500;">status rtpengine
ss -ulnp | grep rtpengine # 2. Check RTPEngine statistics
echo "list totals" | nc -u 127.0.0.1 2223 # 3. Are RTP ports open in the firewall?
# Port range 10000-20000 UDP must be open
ss -ulnp | grep -E "1[0-9]{4}|20000" # 4. Check if NAT is being detected
grep "NAT detected" /var/log/kamailio/kamailio.log | tail -10 # 5. Capture SIP to check SDP
ngrep -W byline -d any port 5060 | grep -A20 "INVITE"
# Look at c= and m= lines — do they contain private IPs?
# 1. Is RTPEngine running?
-weight: 500;">systemctl -weight: 500;">status rtpengine
ss -ulnp | grep rtpengine # 2. Check RTPEngine statistics
echo "list totals" | nc -u 127.0.0.1 2223 # 3. Are RTP ports open in the firewall?
# Port range 10000-20000 UDP must be open
ss -ulnp | grep -E "1[0-9]{4}|20000" # 4. Check if NAT is being detected
grep "NAT detected" /var/log/kamailio/kamailio.log | tail -10 # 5. Capture SIP to check SDP
ngrep -W byline -d any port 5060 | grep -A20 "INVITE"
# Look at c= and m= lines — do they contain private IPs?
# Check dispatcher -weight: 500;">status
kamcmd dispatcher.list
# Are backends marked as Active (AP) or Inactive (IP)? # Check if health checking is working
grep "OPTIONS" /var/log/kamailio/kamailio.log | tail -10
# Check dispatcher -weight: 500;">status
kamcmd dispatcher.list
# Are backends marked as Active (AP) or Inactive (IP)? # Check if health checking is working
grep "OPTIONS" /var/log/kamailio/kamailio.log | tail -10
# Check dispatcher -weight: 500;">status
kamcmd dispatcher.list
# Are backends marked as Active (AP) or Inactive (IP)? # Check if health checking is working
grep "OPTIONS" /var/log/kamailio/kamailio.log | tail -10
# /etc/sysctl.conf — network tuning for SIP proxy
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
net.core.netdev_max_backlog = 5000
net.ipv4.udp_mem = 8388608 12582912 16777216
net.ipv4.ip_local_port_range = 10000 65535
net.core.somaxconn = 4096
fs.file-max = 1000000 # Apply
sysctl -p # Increase open file limit for Kamailio
cat >> /etc/security/limits.d/kamailio.conf << 'EOF'
kamailio soft nofile 65536
kamailio hard nofile 65536
EOF
# /etc/sysctl.conf — network tuning for SIP proxy
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
net.core.netdev_max_backlog = 5000
net.ipv4.udp_mem = 8388608 12582912 16777216
net.ipv4.ip_local_port_range = 10000 65535
net.core.somaxconn = 4096
fs.file-max = 1000000 # Apply
sysctl -p # Increase open file limit for Kamailio
cat >> /etc/security/limits.d/kamailio.conf << 'EOF'
kamailio soft nofile 65536
kamailio hard nofile 65536
EOF
# /etc/sysctl.conf — network tuning for SIP proxy
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
net.core.netdev_max_backlog = 5000
net.ipv4.udp_mem = 8388608 12582912 16777216
net.ipv4.ip_local_port_range = 10000 65535
net.core.somaxconn = 4096
fs.file-max = 1000000 # Apply
sysctl -p # Increase open file limit for Kamailio
cat >> /etc/security/limits.d/kamailio.conf << 'EOF'
kamailio soft nofile 65536
kamailio hard nofile 65536
EOF
#!/bin/bash
# Kamailio Quick Diagnostics
echo "=== Kamailio Diagnostics ==="
echo "" echo "--- Version & Uptime ---"
kamcmd core.version 2>/dev/null || echo "Kamailio not running!"
kamcmd core.uptime 2>/dev/null echo ""
echo "--- Listening Sockets ---"
ss -ulnp 2>/dev/null | grep kamailio
ss -tlnp 2>/dev/null | grep kamailio echo ""
echo "--- Active Registrations ---"
REGS=$(kamcmd ul.dump 2>/dev/null | grep -c "AOR::" || echo "0")
echo "Registered users: $REGS" echo ""
echo "--- Active Calls ---"
kamcmd stats.get_statistics dialog: 2>/dev/null | grep active || echo "No dialog module" echo ""
echo "--- Dispatcher Status ---"
kamcmd dispatcher.list 2>/dev/null | grep -E "URI|FLAGS" || echo "No dispatcher" echo ""
echo "--- Key Statistics ---"
kamcmd stats.get_statistics core:rcv_requests 2>/dev/null
kamcmd stats.get_statistics core:err_requests 2>/dev/null
kamcmd stats.get_statistics tmx:5xx_transactions 2>/dev/null echo ""
echo "--- Memory Usage ---"
kamcmd stats.get_statistics shmem: 2>/dev/null | head -5 echo ""
echo "--- Recent Errors (last 10) ---"
grep -i "error\|critical\|alert" /var/log/kamailio/kamailio.log 2>/dev/null | tail -10 echo ""
echo "--- RTPEngine Status ---"
-weight: 500;">systemctl is-active rtpengine 2>/dev/null || echo "RTPEngine not installed" echo ""
echo "=== Done ==="
#!/bin/bash
# Kamailio Quick Diagnostics
echo "=== Kamailio Diagnostics ==="
echo "" echo "--- Version & Uptime ---"
kamcmd core.version 2>/dev/null || echo "Kamailio not running!"
kamcmd core.uptime 2>/dev/null echo ""
echo "--- Listening Sockets ---"
ss -ulnp 2>/dev/null | grep kamailio
ss -tlnp 2>/dev/null | grep kamailio echo ""
echo "--- Active Registrations ---"
REGS=$(kamcmd ul.dump 2>/dev/null | grep -c "AOR::" || echo "0")
echo "Registered users: $REGS" echo ""
echo "--- Active Calls ---"
kamcmd stats.get_statistics dialog: 2>/dev/null | grep active || echo "No dialog module" echo ""
echo "--- Dispatcher Status ---"
kamcmd dispatcher.list 2>/dev/null | grep -E "URI|FLAGS" || echo "No dispatcher" echo ""
echo "--- Key Statistics ---"
kamcmd stats.get_statistics core:rcv_requests 2>/dev/null
kamcmd stats.get_statistics core:err_requests 2>/dev/null
kamcmd stats.get_statistics tmx:5xx_transactions 2>/dev/null echo ""
echo "--- Memory Usage ---"
kamcmd stats.get_statistics shmem: 2>/dev/null | head -5 echo ""
echo "--- Recent Errors (last 10) ---"
grep -i "error\|critical\|alert" /var/log/kamailio/kamailio.log 2>/dev/null | tail -10 echo ""
echo "--- RTPEngine Status ---"
-weight: 500;">systemctl is-active rtpengine 2>/dev/null || echo "RTPEngine not installed" echo ""
echo "=== Done ==="
#!/bin/bash
# Kamailio Quick Diagnostics
echo "=== Kamailio Diagnostics ==="
echo "" echo "--- Version & Uptime ---"
kamcmd core.version 2>/dev/null || echo "Kamailio not running!"
kamcmd core.uptime 2>/dev/null echo ""
echo "--- Listening Sockets ---"
ss -ulnp 2>/dev/null | grep kamailio
ss -tlnp 2>/dev/null | grep kamailio echo ""
echo "--- Active Registrations ---"
REGS=$(kamcmd ul.dump 2>/dev/null | grep -c "AOR::" || echo "0")
echo "Registered users: $REGS" echo ""
echo "--- Active Calls ---"
kamcmd stats.get_statistics dialog: 2>/dev/null | grep active || echo "No dialog module" echo ""
echo "--- Dispatcher Status ---"
kamcmd dispatcher.list 2>/dev/null | grep -E "URI|FLAGS" || echo "No dispatcher" echo ""
echo "--- Key Statistics ---"
kamcmd stats.get_statistics core:rcv_requests 2>/dev/null
kamcmd stats.get_statistics core:err_requests 2>/dev/null
kamcmd stats.get_statistics tmx:5xx_transactions 2>/dev/null echo ""
echo "--- Memory Usage ---"
kamcmd stats.get_statistics shmem: 2>/dev/null | head -5 echo ""
echo "--- Recent Errors (last 10) ---"
grep -i "error\|critical\|alert" /var/log/kamailio/kamailio.log 2>/dev/null | tail -10 echo ""
echo "--- RTPEngine Status ---"
-weight: 500;">systemctl is-active rtpengine 2>/dev/null || echo "RTPEngine not installed" echo ""
echo "=== Done ==="
chmod +x /usr/local/bin/kamailio-diag.sh
chmod +x /usr/local/bin/kamailio-diag.sh
chmod +x /usr/local/bin/kamailio-diag.sh - Introduction
- Architecture Overview
- Installation — Debian 12 / Ubuntu 24.04
- Configuration Language
- SIP Routing
- User Authentication
- NAT Traversal
- TLS & Security
- Load Balancing with Dispatcher
- Dialog & Accounting
- WebRTC Gateway
- Integration with Media Servers
- Monitoring & Management
- Troubleshooting - Scaling: Your Asterisk server handles 200 registrations fine, but you need to support 10,000+ SIP devices
- Security (SBC): You want a hardened front-end that absorbs SIP attacks before they reach your PBX
- Load balancing: You have 3+ Asterisk servers and need to distribute calls intelligently
- Multi-tenant routing: You operate a SIP platform serving multiple customers/domains
- Geographic routing: Route calls to the nearest media server based on caller location
- WebRTC gateway: Bridge browser-based SIP.js/JsSIP clients to your traditional SIP infrastructure
- Registration proxy: Offload thousands of SIP registrations from your media server
- Topology hiding: Hide your internal network structure from external SIP peers
- Regulatory compliance: Apply call routing rules, rate limiting, and CDR collection at the edge - Parses the SIP message
- Executes the routing logic defined in kamailio.cfg
- Forwards, replies, or drops the message based on your rules - Global parameters — server settings (listen address, debug level, etc.)
- Module loading — which modules to activate
- Module parameters — settings for each module
- Route blocks — the actual SIP processing logic - reply_route — global reply processing (all responses)
- onsend_route — executed just before sending a message out
- event_route — triggered by internal events (e.g., event_route[tm:local-request]) - Let endpoints send media directly to each other (simple, but breaks with NAT)
- Use RTPEngine as a media relay (required for NAT, WebRTC, SRTP)
- Route through a B2BUA like Asterisk (which handles both signaling and media) - OS: Debian 12 (Bookworm) or Ubuntu 24.04 (Noble) — 64-bit
- RAM: 1 GB minimum, 4 GB+ recommended for production
- CPU: 1 core minimum, 4+ cores for production
- Disk: 10 GB minimum
- Network: Static public IP, ports 5060/UDP+TCP, 5061/TCP (TLS), 8443/TCP (WSS) - calculate_ha1=1: Passwords are stored in plaintext; Kamailio calculates the HA1 hash. Set to 0 if you store pre-computed HA1 hashes (more secure).
- password_column: Which column in the subscriber table contains the password.
- use_domain=0: Ignore the domain part — authenticate by username only. Set to 1 for multi-domain setups. - Signaling: Kamailio tries to send SIP replies to 192.168.1.100 — which is unreachable from the internet
- Media: The SDP body says "send RTP to 192.168.1.100:10000" — the other party cannot reach this address - STUN: Helps clients discover their public IP. Lightweight but fails with symmetric NAT.
- TURN: Full relay server. Works with all NAT types but adds latency and bandwidth cost.
- ICE: Framework that uses both STUN and TURN to find the best media path. - NAT detection on all incoming requests
- SIP signaling fix-up (Contact, Via, rport)
- RTPEngine integration for media relay
- NAT keepalive pings to registered clients
- Proper RTPEngine session cleanup on BYE
- In-dialog NAT management for re-INVITEs and replies - Kamailio restarted during an active call
- Record-Route was not added to the initial INVITE
- Dialog module lost state - calculate_ha1=1 — passwords stored in plaintext, Kamailio hashes them
- calculate_ha1=0 — passwords stored as HA1 hashes already - Verify nathelper module is loaded and nat_uac_test() is called
- Verify rtpengine module is loaded and rtpengine_manage() is called for both requests AND replies
- Verify Record-Route is added so in-dialog requests go through Kamailio
- Verify force_rport is applied for NATed clients
- Verify fix_nated_contact() is called to rewrite Contact headers
- Verify RTPEngine firewall ports are open (10000-20000 UDP)
# Create the Kamailio database (answer 'y' to all prompts)
kamdbctl create # This creates:
# - kamailio database
# - Core tables: location, subscriber, version, etc.
# - Extra tables: dialog, acc, dispatcher, etc.
# - Presence tables: presentity, watchers, etc. # Verify
mysql -u kamailio -p'YOUR_KAMAILIO_DB_PASSWORD' -e "SHOW TABLES;" kamailio
# Create the Kamailio database (answer 'y' to all prompts)
kamdbctl create # This creates:
# - kamailio database
# - Core tables: location, subscriber, version, etc.
# - Extra tables: dialog, acc, dispatcher, etc.
# - Presence tables: presentity, watchers, etc. # Verify
mysql -u kamailio -p'YOUR_KAMAILIO_DB_PASSWORD' -e "SHOW TABLES;" kamailio
# Create the Kamailio database (answer 'y' to all prompts)
kamdbctl create # This creates:
# - kamailio database
# - Core tables: location, subscriber, version, etc.
# - Extra tables: dialog, acc, dispatcher, etc.
# - Presence tables: presentity, watchers, etc. # Verify
mysql -u kamailio -p'YOUR_KAMAILIO_DB_PASSWORD' -e "SHOW TABLES;" kamailio
+--------------------+
| Tables_in_kamailio |
+--------------------+
| acc |
| address |
| aliases |
| dialog |
| dispatcher |
| domain |
| location |
| missed_calls |
| subscriber |
| trusted |
| version |
| ... |
+--------------------+
+--------------------+
| Tables_in_kamailio |
+--------------------+
| acc |
| address |
| aliases |
| dialog |
| dispatcher |
| domain |
| location |
| missed_calls |
| subscriber |
| trusted |
| version |
| ... |
+--------------------+
+--------------------+
| Tables_in_kamailio |
+--------------------+
| acc |
| address |
| aliases |
| dialog |
| dispatcher |
| domain |
| location |
| missed_calls |
| subscriber |
| trusted |
| version |
| ... |
+--------------------+
# Enable Kamailio to -weight: 500;">start via systemd
sed -i 's/^RUN_KAMAILIO=no/RUN_KAMAILIO=yes/' /etc/default/kamailio # Set memory allocation (adjust based on your server)
sed -i 's/^# SHM_MEMORY=.*/SHM_MEMORY=128/' /etc/default/kamailio
sed -i 's/^# PKG_MEMORY=.*/PKG_MEMORY=16/' /etc/default/kamailio # Review the defaults file
cat /etc/default/kamailio
# Enable Kamailio to -weight: 500;">start via systemd
sed -i 's/^RUN_KAMAILIO=no/RUN_KAMAILIO=yes/' /etc/default/kamailio # Set memory allocation (adjust based on your server)
sed -i 's/^# SHM_MEMORY=.*/SHM_MEMORY=128/' /etc/default/kamailio
sed -i 's/^# PKG_MEMORY=.*/PKG_MEMORY=16/' /etc/default/kamailio # Review the defaults file
cat /etc/default/kamailio
# Enable Kamailio to -weight: 500;">start via systemd
sed -i 's/^RUN_KAMAILIO=no/RUN_KAMAILIO=yes/' /etc/default/kamailio # Set memory allocation (adjust based on your server)
sed -i 's/^# SHM_MEMORY=.*/SHM_MEMORY=128/' /etc/default/kamailio
sed -i 's/^# PKG_MEMORY=.*/PKG_MEMORY=16/' /etc/default/kamailio # Review the defaults file
cat /etc/default/kamailio
# Set to "yes" to -weight: 500;">enable kamailio
RUN_KAMAILIO=yes # User and group to run as
USER=kamailio
GROUP=kamailio # Shared memory (MB) — total pool for all Kamailio processes
SHM_MEMORY=128 # Private memory (MB) — per-process memory
PKG_MEMORY=16 # Config file location
CFGFILE=/etc/kamailio/kamailio.cfg # PID file
PIDFILE=/run/kamailio/kamailio.pid
# Set to "yes" to -weight: 500;">enable kamailio
RUN_KAMAILIO=yes # User and group to run as
USER=kamailio
GROUP=kamailio # Shared memory (MB) — total pool for all Kamailio processes
SHM_MEMORY=128 # Private memory (MB) — per-process memory
PKG_MEMORY=16 # Config file location
CFGFILE=/etc/kamailio/kamailio.cfg # PID file
PIDFILE=/run/kamailio/kamailio.pid
# Set to "yes" to -weight: 500;">enable kamailio
RUN_KAMAILIO=yes # User and group to run as
USER=kamailio
GROUP=kamailio # Shared memory (MB) — total pool for all Kamailio processes
SHM_MEMORY=128 # Private memory (MB) — per-process memory
PKG_MEMORY=16 # Config file location
CFGFILE=/etc/kamailio/kamailio.cfg # PID file
PIDFILE=/run/kamailio/kamailio.pid
# Using ufw
ufw allow 5060/udp comment "SIP UDP"
ufw allow 5060/tcp comment "SIP TCP"
ufw allow 5061/tcp comment "SIP TLS"
ufw allow 8443/tcp comment "SIP WSS (WebRTC)"
ufw allow 10000:20000/udp comment "RTP Media (RTPEngine)" # Or using iptables directly
iptables -A INPUT -p udp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5061 -j ACCEPT
iptables -A INPUT -p tcp --dport 8443 -j ACCEPT
iptables -A INPUT -p udp --dport 10000:20000 -j ACCEPT
# Using ufw
ufw allow 5060/udp comment "SIP UDP"
ufw allow 5060/tcp comment "SIP TCP"
ufw allow 5061/tcp comment "SIP TLS"
ufw allow 8443/tcp comment "SIP WSS (WebRTC)"
ufw allow 10000:20000/udp comment "RTP Media (RTPEngine)" # Or using iptables directly
iptables -A INPUT -p udp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5061 -j ACCEPT
iptables -A INPUT -p tcp --dport 8443 -j ACCEPT
iptables -A INPUT -p udp --dport 10000:20000 -j ACCEPT
# Using ufw
ufw allow 5060/udp comment "SIP UDP"
ufw allow 5060/tcp comment "SIP TCP"
ufw allow 5061/tcp comment "SIP TLS"
ufw allow 8443/tcp comment "SIP WSS (WebRTC)"
ufw allow 10000:20000/udp comment "RTP Media (RTPEngine)" # Or using iptables directly
iptables -A INPUT -p udp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5060 -j ACCEPT
iptables -A INPUT -p tcp --dport 5061 -j ACCEPT
iptables -A INPUT -p tcp --dport 8443 -j ACCEPT
iptables -A INPUT -p udp --dport 10000:20000 -j ACCEPT
# Check config syntax before starting
kamailio -c -f /etc/kamailio/kamailio.cfg # Start Kamailio
-weight: 500;">systemctl -weight: 500;">start kamailio
-weight: 500;">systemctl -weight: 500;">enable kamailio # Check -weight: 500;">status
-weight: 500;">systemctl -weight: 500;">status kamailio # Verify it's listening
ss -ulnp | grep kamailio
ss -tlnp | grep kamailio # Expected output:
# udp UNCONN 0 0 0.0.0.0:5060 0.0.0.0:* users:(("kamailio",pid=...))
# tcp LISTEN 0 0 0.0.0.0:5060 0.0.0.0:* users:(("kamailio",pid=...))
# Check config syntax before starting
kamailio -c -f /etc/kamailio/kamailio.cfg # Start Kamailio
-weight: 500;">systemctl -weight: 500;">start kamailio
-weight: 500;">systemctl -weight: 500;">enable kamailio # Check -weight: 500;">status
-weight: 500;">systemctl -weight: 500;">status kamailio # Verify it's listening
ss -ulnp | grep kamailio
ss -tlnp | grep kamailio # Expected output:
# udp UNCONN 0 0 0.0.0.0:5060 0.0.0.0:* users:(("kamailio",pid=...))
# tcp LISTEN 0 0 0.0.0.0:5060 0.0.0.0:* users:(("kamailio",pid=...))
# Check config syntax before starting
kamailio -c -f /etc/kamailio/kamailio.cfg # Start Kamailio
-weight: 500;">systemctl -weight: 500;">start kamailio
-weight: 500;">systemctl -weight: 500;">enable kamailio # Check -weight: 500;">status
-weight: 500;">systemctl -weight: 500;">status kamailio # Verify it's listening
ss -ulnp | grep kamailio
ss -tlnp | grep kamailio # Expected output:
# udp UNCONN 0 0 0.0.0.0:5060 0.0.0.0:* users:(("kamailio",pid=...))
# tcp LISTEN 0 0 0.0.0.0:5060 0.0.0.0:* users:(("kamailio",pid=...))
# Install sipsak (SIP Swiss Army Knife)
-weight: 500;">apt -weight: 500;">install -y sipsak # Send OPTIONS to Kamailio
sipsak -s sip:YOUR_SERVER_IP:5060
# Expected: SIP/2.0 200 OK # Using kamctl
kamctl stats
kamctl fifo which # Using kamcmd
kamcmd core.version
kamcmd core.uptime
kamcmd ul.dump
# Install sipsak (SIP Swiss Army Knife)
-weight: 500;">apt -weight: 500;">install -y sipsak # Send OPTIONS to Kamailio
sipsak -s sip:YOUR_SERVER_IP:5060
# Expected: SIP/2.0 200 OK # Using kamctl
kamctl stats
kamctl fifo which # Using kamcmd
kamcmd core.version
kamcmd core.uptime
kamcmd ul.dump
# Install sipsak (SIP Swiss Army Knife)
-weight: 500;">apt -weight: 500;">install -y sipsak # Send OPTIONS to Kamailio
sipsak -s sip:YOUR_SERVER_IP:5060
# Expected: SIP/2.0 200 OK # Using kamctl
kamctl stats
kamctl fifo which # Using kamcmd
kamcmd core.version
kamcmd core.uptime
kamcmd ul.dump
┌─────────────────────────────┐
│ 1. Global Parameters │ ← Server-wide settings
├─────────────────────────────┤
│ 2. Module Loading │ ← loadmodule statements
├─────────────────────────────┤
│ 3. Module Parameters │ ← modparam() calls
├─────────────────────────────┤
│ 4. Route Blocks │ ← Routing logic
└─────────────────────────────┘
┌─────────────────────────────┐
│ 1. Global Parameters │ ← Server-wide settings
├─────────────────────────────┤
│ 2. Module Loading │ ← loadmodule statements
├─────────────────────────────┤
│ 3. Module Parameters │ ← modparam() calls
├─────────────────────────────┤
│ 4. Route Blocks │ ← Routing logic
└─────────────────────────────┘
┌─────────────────────────────┐
│ 1. Global Parameters │ ← Server-wide settings
├─────────────────────────────┤
│ 2. Module Loading │ ← loadmodule statements
├─────────────────────────────┤
│ 3. Module Parameters │ ← modparam() calls
├─────────────────────────────┤
│ 4. Route Blocks │ ← Routing logic
└─────────────────────────────┘
#!KAMAILIO /* ===== Global Parameters ===== */ /* Listen addresses — where Kamailio accepts SIP */
listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060 /* Server aliases — domains this server responds to */
alias="YOUR_DOMAIN"
alias="YOUR_SERVER_IP" /* Logging */
debug=2 # 0=emergency ... 4=debug (use 2 for production)
log_stderror=no # Log to syslog, not stderr
log_facility=LOG_LOCAL0 # Syslog facility /* Process configuration */
children=8 # Number of SIP worker processes (per interface)
tcp_children=4 # Number of TCP worker processes
tcp_max_connections=4096 # Max simultaneous TCP connections
tcp_connection_lifetime=3600 # TCP keepalive (seconds) /* Memory */
# Shared memory and private memory set in /etc/default/kamailio /* DNS */
dns=no # Disable internal DNS (use system resolver)
rev_dns=no # Disable reverse DNS lookups
dns_try_ipv6=no # Disable IPv6 DNS queries
use_dns_cache=on # Cache DNS results
dns_cache_flags=1 # DNS cache settings /* SIP-specific */
mhomed=0 # Multi-homed (0=disabled, 1=choose best source IP)
auto_aliases=no # Don't auto-detect aliases from DNS
server_signature=no # Hide Kamailio version in Server header
server_header="Server: SIP Proxy" # Custom Server header
user_agent_header="User-Agent: SIP Proxy" # Custom UA header /* Reply to OPTIONS with 200 OK */
#!define WITH_AUTH
#!define WITH_MYSQL
#!define WITH_NAT
#!KAMAILIO /* ===== Global Parameters ===== */ /* Listen addresses — where Kamailio accepts SIP */
listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060 /* Server aliases — domains this server responds to */
alias="YOUR_DOMAIN"
alias="YOUR_SERVER_IP" /* Logging */
debug=2 # 0=emergency ... 4=debug (use 2 for production)
log_stderror=no # Log to syslog, not stderr
log_facility=LOG_LOCAL0 # Syslog facility /* Process configuration */
children=8 # Number of SIP worker processes (per interface)
tcp_children=4 # Number of TCP worker processes
tcp_max_connections=4096 # Max simultaneous TCP connections
tcp_connection_lifetime=3600 # TCP keepalive (seconds) /* Memory */
# Shared memory and private memory set in /etc/default/kamailio /* DNS */
dns=no # Disable internal DNS (use system resolver)
rev_dns=no # Disable reverse DNS lookups
dns_try_ipv6=no # Disable IPv6 DNS queries
use_dns_cache=on # Cache DNS results
dns_cache_flags=1 # DNS cache settings /* SIP-specific */
mhomed=0 # Multi-homed (0=disabled, 1=choose best source IP)
auto_aliases=no # Don't auto-detect aliases from DNS
server_signature=no # Hide Kamailio version in Server header
server_header="Server: SIP Proxy" # Custom Server header
user_agent_header="User-Agent: SIP Proxy" # Custom UA header /* Reply to OPTIONS with 200 OK */
#!define WITH_AUTH
#!define WITH_MYSQL
#!define WITH_NAT
#!KAMAILIO /* ===== Global Parameters ===== */ /* Listen addresses — where Kamailio accepts SIP */
listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060 /* Server aliases — domains this server responds to */
alias="YOUR_DOMAIN"
alias="YOUR_SERVER_IP" /* Logging */
debug=2 # 0=emergency ... 4=debug (use 2 for production)
log_stderror=no # Log to syslog, not stderr
log_facility=LOG_LOCAL0 # Syslog facility /* Process configuration */
children=8 # Number of SIP worker processes (per interface)
tcp_children=4 # Number of TCP worker processes
tcp_max_connections=4096 # Max simultaneous TCP connections
tcp_connection_lifetime=3600 # TCP keepalive (seconds) /* Memory */
# Shared memory and private memory set in /etc/default/kamailio /* DNS */
dns=no # Disable internal DNS (use system resolver)
rev_dns=no # Disable reverse DNS lookups
dns_try_ipv6=no # Disable IPv6 DNS queries
use_dns_cache=on # Cache DNS results
dns_cache_flags=1 # DNS cache settings /* SIP-specific */
mhomed=0 # Multi-homed (0=disabled, 1=choose best source IP)
auto_aliases=no # Don't auto-detect aliases from DNS
server_signature=no # Hide Kamailio version in Server header
server_header="Server: SIP Proxy" # Custom Server header
user_agent_header="User-Agent: SIP Proxy" # Custom UA header /* Reply to OPTIONS with 200 OK */
#!define WITH_AUTH
#!define WITH_MYSQL
#!define WITH_NAT
/* ===== Module Loading ===== */ /* Core SIP modules */
loadmodule "kex.so" # Kamailio core extensions
loadmodule "corex.so" # Core extra functions
loadmodule "sl.so" # Stateless replies
loadmodule "tm.so" # Transaction management (stateful SIP)
loadmodule "tmx.so" # Transaction extensions
loadmodule "rr.so" # Record-Route
loadmodule "maxfwd.so" # Max-Forwards check
loadmodule "pv.so" # Pseudo-variables
loadmodule "textops.so" # Text operations on SIP messages
loadmodule "textopsx.so" # Extended text operations
loadmodule "siputils.so" # SIP utility functions
loadmodule "xlog.so" # Extended logging
loadmodule "sanity.so" # SIP message sanity checks /* Database */
loadmodule "db_mysql.so" # MySQL/MariaDB connector /* User management */
loadmodule "usrloc.so" # User location (registration database)
loadmodule "registrar.so" # REGISTER processing /* Authentication */
loadmodule "auth.so" # Auth framework
loadmodule "auth_db.so" # Database-backed auth /* NAT traversal */
loadmodule "nathelper.so" # NAT detection + signaling fix
loadmodule "rtpengine.so" # RTPEngine media relay /* Dialog and accounting */
loadmodule "dialog.so" # Call dialog tracking
loadmodule "acc.so" # Accounting / CDR /* Load balancing */
loadmodule "dispatcher.so" # Dispatcher (load balancer) /* Security */
loadmodule "pike.so" # Flood detection
loadmodule "htable.so" # Hash tables /* Management */
loadmodule "jsonrpcs.so" # JSON-RPC control interface
loadmodule "ctl.so" # Control interface (kamcmd)
loadmodule "cfg_rpc.so" # RPC for config
/* ===== Module Loading ===== */ /* Core SIP modules */
loadmodule "kex.so" # Kamailio core extensions
loadmodule "corex.so" # Core extra functions
loadmodule "sl.so" # Stateless replies
loadmodule "tm.so" # Transaction management (stateful SIP)
loadmodule "tmx.so" # Transaction extensions
loadmodule "rr.so" # Record-Route
loadmodule "maxfwd.so" # Max-Forwards check
loadmodule "pv.so" # Pseudo-variables
loadmodule "textops.so" # Text operations on SIP messages
loadmodule "textopsx.so" # Extended text operations
loadmodule "siputils.so" # SIP utility functions
loadmodule "xlog.so" # Extended logging
loadmodule "sanity.so" # SIP message sanity checks /* Database */
loadmodule "db_mysql.so" # MySQL/MariaDB connector /* User management */
loadmodule "usrloc.so" # User location (registration database)
loadmodule "registrar.so" # REGISTER processing /* Authentication */
loadmodule "auth.so" # Auth framework
loadmodule "auth_db.so" # Database-backed auth /* NAT traversal */
loadmodule "nathelper.so" # NAT detection + signaling fix
loadmodule "rtpengine.so" # RTPEngine media relay /* Dialog and accounting */
loadmodule "dialog.so" # Call dialog tracking
loadmodule "acc.so" # Accounting / CDR /* Load balancing */
loadmodule "dispatcher.so" # Dispatcher (load balancer) /* Security */
loadmodule "pike.so" # Flood detection
loadmodule "htable.so" # Hash tables /* Management */
loadmodule "jsonrpcs.so" # JSON-RPC control interface
loadmodule "ctl.so" # Control interface (kamcmd)
loadmodule "cfg_rpc.so" # RPC for config
/* ===== Module Loading ===== */ /* Core SIP modules */
loadmodule "kex.so" # Kamailio core extensions
loadmodule "corex.so" # Core extra functions
loadmodule "sl.so" # Stateless replies
loadmodule "tm.so" # Transaction management (stateful SIP)
loadmodule "tmx.so" # Transaction extensions
loadmodule "rr.so" # Record-Route
loadmodule "maxfwd.so" # Max-Forwards check
loadmodule "pv.so" # Pseudo-variables
loadmodule "textops.so" # Text operations on SIP messages
loadmodule "textopsx.so" # Extended text operations
loadmodule "siputils.so" # SIP utility functions
loadmodule "xlog.so" # Extended logging
loadmodule "sanity.so" # SIP message sanity checks /* Database */
loadmodule "db_mysql.so" # MySQL/MariaDB connector /* User management */
loadmodule "usrloc.so" # User location (registration database)
loadmodule "registrar.so" # REGISTER processing /* Authentication */
loadmodule "auth.so" # Auth framework
loadmodule "auth_db.so" # Database-backed auth /* NAT traversal */
loadmodule "nathelper.so" # NAT detection + signaling fix
loadmodule "rtpengine.so" # RTPEngine media relay /* Dialog and accounting */
loadmodule "dialog.so" # Call dialog tracking
loadmodule "acc.so" # Accounting / CDR /* Load balancing */
loadmodule "dispatcher.so" # Dispatcher (load balancer) /* Security */
loadmodule "pike.so" # Flood detection
loadmodule "htable.so" # Hash tables /* Management */
loadmodule "jsonrpcs.so" # JSON-RPC control interface
loadmodule "ctl.so" # Control interface (kamcmd)
loadmodule "cfg_rpc.so" # RPC for config
/* ===== Module Parameters ===== */ /* ----- tm ----- */
modparam("tm", "failure_reply_mode", 3)
modparam("tm", "fr_timer", 30000) # Final response timeout (ms)
modparam("tm", "fr_inv_timer", 120000) # INVITE final response timeout (ms)
modparam("tm", "restart_fr_on_each_reply", 1) /* ----- rr ----- */
modparam("rr", "enable_full_lr", 1) # Full loose-routing
modparam("rr", "append_fromtag", 1) # Append From-tag to Record-Route /* ----- usrloc ----- */
modparam("usrloc", "db_mode", 2) # 0=cache, 1=write-through, 2=write-back, 3=DB only
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("usrloc", "timer_interval", 60) # Cleanup interval (seconds)
modparam("usrloc", "use_domain", 0) # 0=ignore domain, 1=use domain in AOR /* ----- registrar ----- */
modparam("registrar", "method_filtering", 1)
modparam("registrar", "max_expires", 3600) # Max registration lifetime
modparam("registrar", "min_expires", 60) # Min registration lifetime
modparam("registrar", "default_expires", 600) # Default if not specified
modparam("registrar", "gruu_enabled", 0) /* ----- auth_db ----- */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1) # Calculate HA1 from plaintext password
modparam("auth_db", "password_column", "password")
modparam("auth_db", "load_credentials", "$avp(s:caller_uuid)=uuid")
modparam("auth_db", "use_domain", 0) /* ----- nathelper ----- */
modparam("nathelper", "natping_interval", 30) # NAT keepalive interval
modparam("nathelper", "ping_nated_only", 1) # Only ping NATed contacts
modparam("nathelper", "sipping", 1) # Use SIP OPTIONS as keepalive /* ----- rtpengine ----- */
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* ----- dialog ----- */
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4)
modparam("dialog", "profiles_with_value", "caller")
modparam("dialog", "profiles_no_value", "active_calls") /* ----- dispatcher ----- */
modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "ds_ping_interval", 10) # Health check interval (seconds)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3) # Failures before marking down
modparam("dispatcher", "ds_inactive_threshold", 3) # Successes before marking up
modparam("dispatcher", "ds_probing_mode", 1) # Probe all destinations /* ----- pike ----- */
modparam("pike", "sampling_time_unit", 2) # Sampling window (seconds)
modparam("pike", "reqs_density_per_unit", 30) # Max requests per window
modparam("pike", "remove_latency", 4) # Ban duration (sampling units) /* ----- jsonrpcs ----- */
modparam("jsonrpcs", "pretty_format", 1)
modparam("jsonrpcs", "transport", 1) # 0=FIFO, 1=datagram, 2=both
/* ===== Module Parameters ===== */ /* ----- tm ----- */
modparam("tm", "failure_reply_mode", 3)
modparam("tm", "fr_timer", 30000) # Final response timeout (ms)
modparam("tm", "fr_inv_timer", 120000) # INVITE final response timeout (ms)
modparam("tm", "restart_fr_on_each_reply", 1) /* ----- rr ----- */
modparam("rr", "enable_full_lr", 1) # Full loose-routing
modparam("rr", "append_fromtag", 1) # Append From-tag to Record-Route /* ----- usrloc ----- */
modparam("usrloc", "db_mode", 2) # 0=cache, 1=write-through, 2=write-back, 3=DB only
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("usrloc", "timer_interval", 60) # Cleanup interval (seconds)
modparam("usrloc", "use_domain", 0) # 0=ignore domain, 1=use domain in AOR /* ----- registrar ----- */
modparam("registrar", "method_filtering", 1)
modparam("registrar", "max_expires", 3600) # Max registration lifetime
modparam("registrar", "min_expires", 60) # Min registration lifetime
modparam("registrar", "default_expires", 600) # Default if not specified
modparam("registrar", "gruu_enabled", 0) /* ----- auth_db ----- */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1) # Calculate HA1 from plaintext password
modparam("auth_db", "password_column", "password")
modparam("auth_db", "load_credentials", "$avp(s:caller_uuid)=uuid")
modparam("auth_db", "use_domain", 0) /* ----- nathelper ----- */
modparam("nathelper", "natping_interval", 30) # NAT keepalive interval
modparam("nathelper", "ping_nated_only", 1) # Only ping NATed contacts
modparam("nathelper", "sipping", 1) # Use SIP OPTIONS as keepalive /* ----- rtpengine ----- */
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* ----- dialog ----- */
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4)
modparam("dialog", "profiles_with_value", "caller")
modparam("dialog", "profiles_no_value", "active_calls") /* ----- dispatcher ----- */
modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "ds_ping_interval", 10) # Health check interval (seconds)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3) # Failures before marking down
modparam("dispatcher", "ds_inactive_threshold", 3) # Successes before marking up
modparam("dispatcher", "ds_probing_mode", 1) # Probe all destinations /* ----- pike ----- */
modparam("pike", "sampling_time_unit", 2) # Sampling window (seconds)
modparam("pike", "reqs_density_per_unit", 30) # Max requests per window
modparam("pike", "remove_latency", 4) # Ban duration (sampling units) /* ----- jsonrpcs ----- */
modparam("jsonrpcs", "pretty_format", 1)
modparam("jsonrpcs", "transport", 1) # 0=FIFO, 1=datagram, 2=both
/* ===== Module Parameters ===== */ /* ----- tm ----- */
modparam("tm", "failure_reply_mode", 3)
modparam("tm", "fr_timer", 30000) # Final response timeout (ms)
modparam("tm", "fr_inv_timer", 120000) # INVITE final response timeout (ms)
modparam("tm", "restart_fr_on_each_reply", 1) /* ----- rr ----- */
modparam("rr", "enable_full_lr", 1) # Full loose-routing
modparam("rr", "append_fromtag", 1) # Append From-tag to Record-Route /* ----- usrloc ----- */
modparam("usrloc", "db_mode", 2) # 0=cache, 1=write-through, 2=write-back, 3=DB only
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("usrloc", "timer_interval", 60) # Cleanup interval (seconds)
modparam("usrloc", "use_domain", 0) # 0=ignore domain, 1=use domain in AOR /* ----- registrar ----- */
modparam("registrar", "method_filtering", 1)
modparam("registrar", "max_expires", 3600) # Max registration lifetime
modparam("registrar", "min_expires", 60) # Min registration lifetime
modparam("registrar", "default_expires", 600) # Default if not specified
modparam("registrar", "gruu_enabled", 0) /* ----- auth_db ----- */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1) # Calculate HA1 from plaintext password
modparam("auth_db", "password_column", "password")
modparam("auth_db", "load_credentials", "$avp(s:caller_uuid)=uuid")
modparam("auth_db", "use_domain", 0) /* ----- nathelper ----- */
modparam("nathelper", "natping_interval", 30) # NAT keepalive interval
modparam("nathelper", "ping_nated_only", 1) # Only ping NATed contacts
modparam("nathelper", "sipping", 1) # Use SIP OPTIONS as keepalive /* ----- rtpengine ----- */
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* ----- dialog ----- */
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4)
modparam("dialog", "profiles_with_value", "caller")
modparam("dialog", "profiles_no_value", "active_calls") /* ----- dispatcher ----- */
modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "ds_ping_interval", 10) # Health check interval (seconds)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3) # Failures before marking down
modparam("dispatcher", "ds_inactive_threshold", 3) # Successes before marking up
modparam("dispatcher", "ds_probing_mode", 1) # Probe all destinations /* ----- pike ----- */
modparam("pike", "sampling_time_unit", 2) # Sampling window (seconds)
modparam("pike", "reqs_density_per_unit", 30) # Max requests per window
modparam("pike", "remove_latency", 4) # Ban duration (sampling units) /* ----- jsonrpcs ----- */
modparam("jsonrpcs", "pretty_format", 1)
modparam("jsonrpcs", "transport", 1) # 0=FIFO, 1=datagram, 2=both
/* ===== Route Blocks ===== */ /* Main request routing */
request_route { # This is where every incoming SIP request is processed xlog("L_INFO", "Received $rm from $si:$sp — R-URI: $ru\n"); # ... routing logic ...
} /* Named sub-routes (called with route(NAME)) */
route[AUTH] { # Authentication logic
} route[REGISTRAR] { # Registration handling
} route[RELAY] { # Forward the request
} /* Reply processing */
reply_route { # Process all SIP responses
} onreply_route[MANAGE_REPLY] { # Per-transaction reply handling
} /* Failure handling */
failure_route[MANAGE_FAILURE] { # Called when a transaction receives a negative final response
} /* Branch processing */
branch_route[MANAGE_BRANCH] { # Called for each branch before forwarding
}
/* ===== Route Blocks ===== */ /* Main request routing */
request_route { # This is where every incoming SIP request is processed xlog("L_INFO", "Received $rm from $si:$sp — R-URI: $ru\n"); # ... routing logic ...
} /* Named sub-routes (called with route(NAME)) */
route[AUTH] { # Authentication logic
} route[REGISTRAR] { # Registration handling
} route[RELAY] { # Forward the request
} /* Reply processing */
reply_route { # Process all SIP responses
} onreply_route[MANAGE_REPLY] { # Per-transaction reply handling
} /* Failure handling */
failure_route[MANAGE_FAILURE] { # Called when a transaction receives a negative final response
} /* Branch processing */
branch_route[MANAGE_BRANCH] { # Called for each branch before forwarding
}
/* ===== Route Blocks ===== */ /* Main request routing */
request_route { # This is where every incoming SIP request is processed xlog("L_INFO", "Received $rm from $si:$sp — R-URI: $ru\n"); # ... routing logic ...
} /* Named sub-routes (called with route(NAME)) */
route[AUTH] { # Authentication logic
} route[REGISTRAR] { # Registration handling
} route[RELAY] { # Forward the request
} /* Reply processing */
reply_route { # Process all SIP responses
} onreply_route[MANAGE_REPLY] { # Per-transaction reply handling
} /* Failure handling */
failure_route[MANAGE_FAILURE] { # Called when a transaction receives a negative final response
} /* Branch processing */
branch_route[MANAGE_BRANCH] { # Called for each branch before forwarding
}
# If/else
if ($rm == "INVITE") { xlog("L_INFO", "Processing INVITE\n");
} else if ($rm == "REGISTER") { xlog("L_INFO", "Processing REGISTER\n");
} else { xlog("L_INFO", "Processing $rm\n");
} # Regex matching
if ($rU =~ "^1[0-9]{3}$") { # Request URI username matches 4-digit extension starting with 1 xlog("L_INFO", "Internal extension: $rU\n");
} # String matching
if ($ua =~ "friendly-scanner") { # Block known SIP scanners sl_send_reply(403, "Forbidden"); exit;
} # IP matching
if ($si == "10.0.0.1") { # Request from trusted IP
} # Check if header exists
if (is_present_hf("X-Custom-Header")) { xlog("L_INFO", "Custom header value: $hdr(X-Custom-Header)\n");
} # Logical operators
if ($rm == "INVITE" && !has_totag()) { # New INVITE (not a re-INVITE)
}
# If/else
if ($rm == "INVITE") { xlog("L_INFO", "Processing INVITE\n");
} else if ($rm == "REGISTER") { xlog("L_INFO", "Processing REGISTER\n");
} else { xlog("L_INFO", "Processing $rm\n");
} # Regex matching
if ($rU =~ "^1[0-9]{3}$") { # Request URI username matches 4-digit extension starting with 1 xlog("L_INFO", "Internal extension: $rU\n");
} # String matching
if ($ua =~ "friendly-scanner") { # Block known SIP scanners sl_send_reply(403, "Forbidden"); exit;
} # IP matching
if ($si == "10.0.0.1") { # Request from trusted IP
} # Check if header exists
if (is_present_hf("X-Custom-Header")) { xlog("L_INFO", "Custom header value: $hdr(X-Custom-Header)\n");
} # Logical operators
if ($rm == "INVITE" && !has_totag()) { # New INVITE (not a re-INVITE)
}
# If/else
if ($rm == "INVITE") { xlog("L_INFO", "Processing INVITE\n");
} else if ($rm == "REGISTER") { xlog("L_INFO", "Processing REGISTER\n");
} else { xlog("L_INFO", "Processing $rm\n");
} # Regex matching
if ($rU =~ "^1[0-9]{3}$") { # Request URI username matches 4-digit extension starting with 1 xlog("L_INFO", "Internal extension: $rU\n");
} # String matching
if ($ua =~ "friendly-scanner") { # Block known SIP scanners sl_send_reply(403, "Forbidden"); exit;
} # IP matching
if ($si == "10.0.0.1") { # Request from trusted IP
} # Check if header exists
if (is_present_hf("X-Custom-Header")) { xlog("L_INFO", "Custom header value: $hdr(X-Custom-Header)\n");
} # Logical operators
if ($rm == "INVITE" && !has_totag()) { # New INVITE (not a re-INVITE)
}
# Forward request statelessly (fast, no failure handling)
forward(); # Forward request statefully (enables failure_route)
t_relay(); # Send a stateless reply
sl_send_reply(200, "OK");
sl_send_reply(403, "Forbidden");
sl_send_reply(503, "Service Unavailable"); # Send a stateful reply
t_reply(200, "OK"); # Rewrite the Request-URI
$ru = "sip:[email protected]";
# Or using rewriteuri:
rewriteuri("sip:[email protected]"); # Rewrite just the host portion
$rd = "10.0.0.5"; # Rewrite just the username
$rU = "2001"; # Add a SIP header
append_hf("X-Forwarded-For: $si\r\n"); # Remove a SIP header
remove_hf("User-Agent"); # Replace a SIP header
subst_hf("User-Agent", "/.*$/SIP Proxy/", "a"); # Stop processing
exit; # Drop the message silently
drop();
# Forward request statelessly (fast, no failure handling)
forward(); # Forward request statefully (enables failure_route)
t_relay(); # Send a stateless reply
sl_send_reply(200, "OK");
sl_send_reply(403, "Forbidden");
sl_send_reply(503, "Service Unavailable"); # Send a stateful reply
t_reply(200, "OK"); # Rewrite the Request-URI
$ru = "sip:[email protected]";
# Or using rewriteuri:
rewriteuri("sip:[email protected]"); # Rewrite just the host portion
$rd = "10.0.0.5"; # Rewrite just the username
$rU = "2001"; # Add a SIP header
append_hf("X-Forwarded-For: $si\r\n"); # Remove a SIP header
remove_hf("User-Agent"); # Replace a SIP header
subst_hf("User-Agent", "/.*$/SIP Proxy/", "a"); # Stop processing
exit; # Drop the message silently
drop();
# Forward request statelessly (fast, no failure handling)
forward(); # Forward request statefully (enables failure_route)
t_relay(); # Send a stateless reply
sl_send_reply(200, "OK");
sl_send_reply(403, "Forbidden");
sl_send_reply(503, "Service Unavailable"); # Send a stateful reply
t_reply(200, "OK"); # Rewrite the Request-URI
$ru = "sip:[email protected]";
# Or using rewriteuri:
rewriteuri("sip:[email protected]"); # Rewrite just the host portion
$rd = "10.0.0.5"; # Rewrite just the username
$rU = "2001"; # Add a SIP header
append_hf("X-Forwarded-For: $si\r\n"); # Remove a SIP header
remove_hf("User-Agent"); # Replace a SIP header
subst_hf("User-Agent", "/.*$/SIP Proxy/", "a"); # Stop processing
exit; # Drop the message silently
drop();
#!KAMAILIO /* ===== Global Parameters ===== */
listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
alias="YOUR_DOMAIN" debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=4
auto_aliases=no
server_signature=no /* ===== Module Loading ===== */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so" /* ===== Module Parameters ===== */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) /* ===== Routing Logic ===== */
request_route { /* --- 1. Sanity checks --- */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { xlog("L_WARN", "Malformed SIP from $si:$sp\n"); exit; } /* --- 2. Record-Route for in-dialog requests --- */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* --- 3. Handle sequential requests (in-dialog) --- */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE from $fU to $tU\n"); } t_relay(); exit; } else { /* Sequential request but no Route header — reject */ sl_send_reply(404, "Not Found"); exit; } } /* --- 4. Handle CANCEL --- */ if (is_method("CANCEL")) { if (t_check_trans()) { t_relay(); } exit; } /* --- 5. Absorb retransmissions --- */ t_check_trans(); /* --- 6. Handle OPTIONS (keepalive) --- */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* --- 7. Route the request --- */ xlog("L_INFO", "$rm from $fU@$si to $rU — forwarding\n"); t_relay(); exit;
}
#!KAMAILIO /* ===== Global Parameters ===== */
listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
alias="YOUR_DOMAIN" debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=4
auto_aliases=no
server_signature=no /* ===== Module Loading ===== */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so" /* ===== Module Parameters ===== */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) /* ===== Routing Logic ===== */
request_route { /* --- 1. Sanity checks --- */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { xlog("L_WARN", "Malformed SIP from $si:$sp\n"); exit; } /* --- 2. Record-Route for in-dialog requests --- */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* --- 3. Handle sequential requests (in-dialog) --- */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE from $fU to $tU\n"); } t_relay(); exit; } else { /* Sequential request but no Route header — reject */ sl_send_reply(404, "Not Found"); exit; } } /* --- 4. Handle CANCEL --- */ if (is_method("CANCEL")) { if (t_check_trans()) { t_relay(); } exit; } /* --- 5. Absorb retransmissions --- */ t_check_trans(); /* --- 6. Handle OPTIONS (keepalive) --- */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* --- 7. Route the request --- */ xlog("L_INFO", "$rm from $fU@$si to $rU — forwarding\n"); t_relay(); exit;
}
#!KAMAILIO /* ===== Global Parameters ===== */
listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
alias="YOUR_DOMAIN" debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=4
auto_aliases=no
server_signature=no /* ===== Module Loading ===== */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so" /* ===== Module Parameters ===== */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) /* ===== Routing Logic ===== */
request_route { /* --- 1. Sanity checks --- */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { xlog("L_WARN", "Malformed SIP from $si:$sp\n"); exit; } /* --- 2. Record-Route for in-dialog requests --- */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* --- 3. Handle sequential requests (in-dialog) --- */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE from $fU to $tU\n"); } t_relay(); exit; } else { /* Sequential request but no Route header — reject */ sl_send_reply(404, "Not Found"); exit; } } /* --- 4. Handle CANCEL --- */ if (is_method("CANCEL")) { if (t_check_trans()) { t_relay(); } exit; } /* --- 5. Absorb retransmissions --- */ t_check_trans(); /* --- 6. Handle OPTIONS (keepalive) --- */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* --- 7. Route the request --- */ xlog("L_INFO", "$rm from $fU@$si to $rU — forwarding\n"); t_relay(); exit;
}
/* Add to module loading section */
loadmodule "usrloc.so"
loadmodule "registrar.so" /* Module parameters */
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600)
/* Add to module loading section */
loadmodule "usrloc.so"
loadmodule "registrar.so" /* Module parameters */
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600)
/* Add to module loading section */
loadmodule "usrloc.so"
loadmodule "registrar.so" /* Module parameters */
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600)
route[REGISTRAR] { /* Only handle REGISTER */ if (!is_method("REGISTER")) return; /* Check for too many contacts */ if (is_present_hf("Contact")) { if ($hdr(Contact) =~ "\*") { /* Unregister all — Contact: * */ } } /* Save the registration */ if (!save("location")) { sl_send_reply(500, "Server Error - Registration Failed"); exit; } /* save() automatically sends 200 OK with Contact + Expires */ exit;
}
route[REGISTRAR] { /* Only handle REGISTER */ if (!is_method("REGISTER")) return; /* Check for too many contacts */ if (is_present_hf("Contact")) { if ($hdr(Contact) =~ "\*") { /* Unregister all — Contact: * */ } } /* Save the registration */ if (!save("location")) { sl_send_reply(500, "Server Error - Registration Failed"); exit; } /* save() automatically sends 200 OK with Contact + Expires */ exit;
}
route[REGISTRAR] { /* Only handle REGISTER */ if (!is_method("REGISTER")) return; /* Check for too many contacts */ if (is_present_hf("Contact")) { if ($hdr(Contact) =~ "\*") { /* Unregister all — Contact: * */ } } /* Save the registration */ if (!save("location")) { sl_send_reply(500, "Server Error - Registration Failed"); exit; } /* save() automatically sends 200 OK with Contact + Expires */ exit;
}
/* Handle REGISTER */
if (is_method("REGISTER")) { route(REGISTRAR); exit;
}
/* Handle REGISTER */
if (is_method("REGISTER")) { route(REGISTRAR); exit;
}
/* Handle REGISTER */
if (is_method("REGISTER")) { route(REGISTRAR); exit;
}
/* Stateless forward — fire and forget */
forward();
/* Stateless forward — fire and forget */
forward();
/* Stateless forward — fire and forget */
forward();
/* Stateful forward — enables failure_route and onreply_route */
t_on_failure("MANAGE_FAILURE");
t_on_reply("MANAGE_REPLY");
t_on_branch("MANAGE_BRANCH");
t_relay();
/* Stateful forward — enables failure_route and onreply_route */
t_on_failure("MANAGE_FAILURE");
t_on_reply("MANAGE_REPLY");
t_on_branch("MANAGE_BRANCH");
t_relay();
/* Stateful forward — enables failure_route and onreply_route */
t_on_failure("MANAGE_FAILURE");
t_on_reply("MANAGE_REPLY");
t_on_branch("MANAGE_BRANCH");
t_relay();
/* Add Record-Route for dialog-initiating requests */
if (is_method("INVITE|SUBSCRIBE")) { record_route();
}
/* Add Record-Route for dialog-initiating requests */
if (is_method("INVITE|SUBSCRIBE")) { record_route();
}
/* Add Record-Route for dialog-initiating requests */
if (is_method("INVITE|SUBSCRIBE")) { record_route();
}
INVITE from Phone A → Kamailio → Phone B With record_route(), Kamailio adds: Record-Route: <sip:YOUR_SERVER_IP;lr> Phone B's 200 OK includes this in the Route set. Now BYE from Phone A → Kamailio → Phone B (Kamailio stays in the signaling path) Without record_route(): BYE from Phone A → Phone B (directly — Kamailio is bypassed)
INVITE from Phone A → Kamailio → Phone B With record_route(), Kamailio adds: Record-Route: <sip:YOUR_SERVER_IP;lr> Phone B's 200 OK includes this in the Route set. Now BYE from Phone A → Kamailio → Phone B (Kamailio stays in the signaling path) Without record_route(): BYE from Phone A → Phone B (directly — Kamailio is bypassed)
INVITE from Phone A → Kamailio → Phone B With record_route(), Kamailio adds: Record-Route: <sip:YOUR_SERVER_IP;lr> Phone B's 200 OK includes this in the Route set. Now BYE from Phone A → Kamailio → Phone B (Kamailio stays in the signaling path) Without record_route(): BYE from Phone A → Phone B (directly — Kamailio is bypassed)
/* Handle sequential (in-dialog) requests */
if (has_totag()) { /* Validate Route headers */ if (loose_route()) { /* In-dialog request with valid Route — forward it */ if (is_method("BYE")) { xlog("L_INFO", "Call ended: $fU → $tU (Call-ID: $ci)\n"); /* Optionally: track in CDR */ } if (is_method("ACK")) { /* ACK for 2xx — route it */ } t_relay(); exit; } else { /* In-dialog request but no valid Route header */ if (is_method("ACK")) { /* ACK without Route — might be for a local 4xx reply */ if (t_check_trans()) { exit; } exit; } /* All other in-dialog without Route — error */ sl_send_reply(404, "Not Found"); exit; }
}
/* Handle sequential (in-dialog) requests */
if (has_totag()) { /* Validate Route headers */ if (loose_route()) { /* In-dialog request with valid Route — forward it */ if (is_method("BYE")) { xlog("L_INFO", "Call ended: $fU → $tU (Call-ID: $ci)\n"); /* Optionally: track in CDR */ } if (is_method("ACK")) { /* ACK for 2xx — route it */ } t_relay(); exit; } else { /* In-dialog request but no valid Route header */ if (is_method("ACK")) { /* ACK without Route — might be for a local 4xx reply */ if (t_check_trans()) { exit; } exit; } /* All other in-dialog without Route — error */ sl_send_reply(404, "Not Found"); exit; }
}
/* Handle sequential (in-dialog) requests */
if (has_totag()) { /* Validate Route headers */ if (loose_route()) { /* In-dialog request with valid Route — forward it */ if (is_method("BYE")) { xlog("L_INFO", "Call ended: $fU → $tU (Call-ID: $ci)\n"); /* Optionally: track in CDR */ } if (is_method("ACK")) { /* ACK for 2xx — route it */ } t_relay(); exit; } else { /* In-dialog request but no valid Route header */ if (is_method("ACK")) { /* ACK without Route — might be for a local 4xx reply */ if (t_check_trans()) { exit; } exit; } /* All other in-dialog without Route — error */ sl_send_reply(404, "Not Found"); exit; }
}
route[TO_TRUNK] { /* Route to SIP trunk */ $ru = "sip:" + $rU + "@TRUNK_IP:5060"; /* Add custom headers if the trunk requires them */ append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); /* Remove internal headers */ remove_hf("X-Internal-Route"); /* Forward statefully with failure handling */ t_on_failure("TRUNK_FAILURE"); t_relay(); exit;
} failure_route[TRUNK_FAILURE] { /* If the primary trunk fails, try backup */ if (t_is_canceled()) exit; xlog("L_WARN", "Trunk failed with $T_reply_code for $rU\n"); if ($T_reply_code >= 500) { /* 5xx error — try backup trunk */ $ru = "sip:" + $rU + "@BACKUP_TRUNK_IP:5060"; t_relay(); exit; }
}
route[TO_TRUNK] { /* Route to SIP trunk */ $ru = "sip:" + $rU + "@TRUNK_IP:5060"; /* Add custom headers if the trunk requires them */ append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); /* Remove internal headers */ remove_hf("X-Internal-Route"); /* Forward statefully with failure handling */ t_on_failure("TRUNK_FAILURE"); t_relay(); exit;
} failure_route[TRUNK_FAILURE] { /* If the primary trunk fails, try backup */ if (t_is_canceled()) exit; xlog("L_WARN", "Trunk failed with $T_reply_code for $rU\n"); if ($T_reply_code >= 500) { /* 5xx error — try backup trunk */ $ru = "sip:" + $rU + "@BACKUP_TRUNK_IP:5060"; t_relay(); exit; }
}
route[TO_TRUNK] { /* Route to SIP trunk */ $ru = "sip:" + $rU + "@TRUNK_IP:5060"; /* Add custom headers if the trunk requires them */ append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); /* Remove internal headers */ remove_hf("X-Internal-Route"); /* Forward statefully with failure handling */ t_on_failure("TRUNK_FAILURE"); t_relay(); exit;
} failure_route[TRUNK_FAILURE] { /* If the primary trunk fails, try backup */ if (t_is_canceled()) exit; xlog("L_WARN", "Trunk failed with $T_reply_code for $rU\n"); if ($T_reply_code >= 500) { /* 5xx error — try backup trunk */ $ru = "sip:" + $rU + "@BACKUP_TRUNK_IP:5060"; t_relay(); exit; }
}
request_route { /* ---- Sanity ---- */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { xlog("L_WARN", "Malformed SIP from $si:$sp\n"); exit; } /* ---- Anti-flood ---- */ if (src_ip != myself) { if (!pike_check_req()) { xlog("L_ALERT", "PIKE BLOCK: $si flooding\n"); exit; } } /* ---- Record-Route ---- */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* ---- In-dialog requests ---- */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE: $fU → $tU ($ci)\n"); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) { exit; } sl_send_reply(404, "Not Found"); exit; } /* ---- CANCEL ---- */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } /* ---- Retransmission absorption ---- */ t_check_trans(); /* ---- REGISTER ---- */ if (is_method("REGISTER")) { route(REGISTRAR); exit; } /* ---- OPTIONS keepalive ---- */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* ---- INVITE routing ---- */ if (is_method("INVITE")) { /* Look up the destination in the location table */ if (!lookup("location")) { /* Not found in registrations — try external routing */ xlog("L_INFO", "User $rU not registered, sending 404\n"); sl_send_reply(404, "User Not Found"); exit; } /* Found — relay to registered contact */ route(RELAY); exit; } /* ---- Other requests ---- */ if (is_method("MESSAGE|NOTIFY|INFO|UPDATE|PRACK|REFER")) { if (lookup("location")) { route(RELAY); exit; } sl_send_reply(404, "User Not Found"); exit; } /* Default — reject */ sl_send_reply(405, "Method Not Allowed"); exit;
} route[RELAY] { t_on_failure("MANAGE_FAILURE"); t_on_reply("MANAGE_REPLY"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[REGISTRAR] { if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
} onreply_route[MANAGE_REPLY] { xlog("L_INFO", "Reply $T_reply_code for $rm ($ci)\n");
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Failure $T_reply_code for $rU ($ci)\n");
}
request_route { /* ---- Sanity ---- */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { xlog("L_WARN", "Malformed SIP from $si:$sp\n"); exit; } /* ---- Anti-flood ---- */ if (src_ip != myself) { if (!pike_check_req()) { xlog("L_ALERT", "PIKE BLOCK: $si flooding\n"); exit; } } /* ---- Record-Route ---- */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* ---- In-dialog requests ---- */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE: $fU → $tU ($ci)\n"); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) { exit; } sl_send_reply(404, "Not Found"); exit; } /* ---- CANCEL ---- */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } /* ---- Retransmission absorption ---- */ t_check_trans(); /* ---- REGISTER ---- */ if (is_method("REGISTER")) { route(REGISTRAR); exit; } /* ---- OPTIONS keepalive ---- */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* ---- INVITE routing ---- */ if (is_method("INVITE")) { /* Look up the destination in the location table */ if (!lookup("location")) { /* Not found in registrations — try external routing */ xlog("L_INFO", "User $rU not registered, sending 404\n"); sl_send_reply(404, "User Not Found"); exit; } /* Found — relay to registered contact */ route(RELAY); exit; } /* ---- Other requests ---- */ if (is_method("MESSAGE|NOTIFY|INFO|UPDATE|PRACK|REFER")) { if (lookup("location")) { route(RELAY); exit; } sl_send_reply(404, "User Not Found"); exit; } /* Default — reject */ sl_send_reply(405, "Method Not Allowed"); exit;
} route[RELAY] { t_on_failure("MANAGE_FAILURE"); t_on_reply("MANAGE_REPLY"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[REGISTRAR] { if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
} onreply_route[MANAGE_REPLY] { xlog("L_INFO", "Reply $T_reply_code for $rm ($ci)\n");
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Failure $T_reply_code for $rU ($ci)\n");
}
request_route { /* ---- Sanity ---- */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { xlog("L_WARN", "Malformed SIP from $si:$sp\n"); exit; } /* ---- Anti-flood ---- */ if (src_ip != myself) { if (!pike_check_req()) { xlog("L_ALERT", "PIKE BLOCK: $si flooding\n"); exit; } } /* ---- Record-Route ---- */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* ---- In-dialog requests ---- */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE: $fU → $tU ($ci)\n"); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) { exit; } sl_send_reply(404, "Not Found"); exit; } /* ---- CANCEL ---- */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } /* ---- Retransmission absorption ---- */ t_check_trans(); /* ---- REGISTER ---- */ if (is_method("REGISTER")) { route(REGISTRAR); exit; } /* ---- OPTIONS keepalive ---- */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* ---- INVITE routing ---- */ if (is_method("INVITE")) { /* Look up the destination in the location table */ if (!lookup("location")) { /* Not found in registrations — try external routing */ xlog("L_INFO", "User $rU not registered, sending 404\n"); sl_send_reply(404, "User Not Found"); exit; } /* Found — relay to registered contact */ route(RELAY); exit; } /* ---- Other requests ---- */ if (is_method("MESSAGE|NOTIFY|INFO|UPDATE|PRACK|REFER")) { if (lookup("location")) { route(RELAY); exit; } sl_send_reply(404, "User Not Found"); exit; } /* Default — reject */ sl_send_reply(405, "Method Not Allowed"); exit;
} route[RELAY] { t_on_failure("MANAGE_FAILURE"); t_on_reply("MANAGE_REPLY"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[REGISTRAR] { if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
} onreply_route[MANAGE_REPLY] { xlog("L_INFO", "Reply $T_reply_code for $rm ($ci)\n");
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Failure $T_reply_code for $rU ($ci)\n");
}
Phone Kamailio Database │ │ │ │─── REGISTER (no auth) ───────►│ │ │ │ │ │◄── 401 Unauthorized ──────────│ (with WWW-Authenticate │ │ (nonce + realm) │ challenge) │ │ │ │ │─── REGISTER (with auth) ─────►│ │ │ (username + response hash) │── lookup password ────────►│ │ │◄── password ───────────────│ │ │ │ │ │ verify hash(password + │ │ │ nonce + method + uri) │ │ │ │ │◄── 200 OK ────────────────────│ │
Phone Kamailio Database │ │ │ │─── REGISTER (no auth) ───────►│ │ │ │ │ │◄── 401 Unauthorized ──────────│ (with WWW-Authenticate │ │ (nonce + realm) │ challenge) │ │ │ │ │─── REGISTER (with auth) ─────►│ │ │ (username + response hash) │── lookup password ────────►│ │ │◄── password ───────────────│ │ │ │ │ │ verify hash(password + │ │ │ nonce + method + uri) │ │ │ │ │◄── 200 OK ────────────────────│ │
Phone Kamailio Database │ │ │ │─── REGISTER (no auth) ───────►│ │ │ │ │ │◄── 401 Unauthorized ──────────│ (with WWW-Authenticate │ │ (nonce + realm) │ challenge) │ │ │ │ │─── REGISTER (with auth) ─────►│ │ │ (username + response hash) │── lookup password ────────►│ │ │◄── password ───────────────│ │ │ │ │ │ verify hash(password + │ │ │ nonce + method + uri) │ │ │ │ │◄── 200 OK ────────────────────│ │
/* Module loading */
loadmodule "auth.so"
loadmodule "auth_db.so" /* Module parameters */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0)
/* Module loading */
loadmodule "auth.so"
loadmodule "auth_db.so" /* Module parameters */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0)
/* Module loading */
loadmodule "auth.so"
loadmodule "auth_db.so" /* Module parameters */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0)
# Add a user
kamctl add 1001 MySecurePass123
# This inserts into the 'subscriber' table # List users
kamctl showdb subscriber # Change password
kamctl passwd 1001 NewPassword456 # Delete a user
kamctl rm 1001 # Add with domain (if use_domain=1)
kamctl add [email protected] MySecurePass123
# Add a user
kamctl add 1001 MySecurePass123
# This inserts into the 'subscriber' table # List users
kamctl showdb subscriber # Change password
kamctl passwd 1001 NewPassword456 # Delete a user
kamctl rm 1001 # Add with domain (if use_domain=1)
kamctl add [email protected] MySecurePass123
# Add a user
kamctl add 1001 MySecurePass123
# This inserts into the 'subscriber' table # List users
kamctl showdb subscriber # Change password
kamctl passwd 1001 NewPassword456 # Delete a user
kamctl rm 1001 # Add with domain (if use_domain=1)
kamctl add [email protected] MySecurePass123
INSERT INTO subscriber (username, domain, password, ha1, ha1b)
VALUES ( '1001', 'YOUR_DOMAIN', 'MySecurePass123', MD5('1001:YOUR_DOMAIN:MySecurePass123'), MD5('1001@YOUR_DOMAIN:YOUR_DOMAIN:MySecurePass123')
);
INSERT INTO subscriber (username, domain, password, ha1, ha1b)
VALUES ( '1001', 'YOUR_DOMAIN', 'MySecurePass123', MD5('1001:YOUR_DOMAIN:MySecurePass123'), MD5('1001@YOUR_DOMAIN:YOUR_DOMAIN:MySecurePass123')
);
INSERT INTO subscriber (username, domain, password, ha1, ha1b)
VALUES ( '1001', 'YOUR_DOMAIN', 'MySecurePass123', MD5('1001:YOUR_DOMAIN:MySecurePass123'), MD5('1001@YOUR_DOMAIN:YOUR_DOMAIN:MySecurePass123')
);
route[AUTH] { /* Skip authentication for requests from trusted IPs */ if (src_ip == myself) return; /* REGISTER requires WWW-Authenticate (401) */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } /* Verify the From URI matches the authenticated user */ if ($au != $fU) { xlog("L_WARN", "Auth mismatch: auth=$au from=$fU ($si)\n"); sl_send_reply(403, "Forbidden — User Mismatch"); exit; } return; /* Auth passed */ } /* All other requests use Proxy-Authenticate (407) */ if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } /* Verify authenticated user matches From */ if ($au != $fU) { xlog("L_WARN", "Auth mismatch: auth=$au from=$fU ($si)\n"); sl_send_reply(403, "Forbidden — User Mismatch"); exit; } /* Remove the Proxy-Authorization header before forwarding */ consume_credentials(); return;
}
route[AUTH] { /* Skip authentication for requests from trusted IPs */ if (src_ip == myself) return; /* REGISTER requires WWW-Authenticate (401) */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } /* Verify the From URI matches the authenticated user */ if ($au != $fU) { xlog("L_WARN", "Auth mismatch: auth=$au from=$fU ($si)\n"); sl_send_reply(403, "Forbidden — User Mismatch"); exit; } return; /* Auth passed */ } /* All other requests use Proxy-Authenticate (407) */ if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } /* Verify authenticated user matches From */ if ($au != $fU) { xlog("L_WARN", "Auth mismatch: auth=$au from=$fU ($si)\n"); sl_send_reply(403, "Forbidden — User Mismatch"); exit; } /* Remove the Proxy-Authorization header before forwarding */ consume_credentials(); return;
}
route[AUTH] { /* Skip authentication for requests from trusted IPs */ if (src_ip == myself) return; /* REGISTER requires WWW-Authenticate (401) */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } /* Verify the From URI matches the authenticated user */ if ($au != $fU) { xlog("L_WARN", "Auth mismatch: auth=$au from=$fU ($si)\n"); sl_send_reply(403, "Forbidden — User Mismatch"); exit; } return; /* Auth passed */ } /* All other requests use Proxy-Authenticate (407) */ if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } /* Verify authenticated user matches From */ if ($au != $fU) { xlog("L_WARN", "Auth mismatch: auth=$au from=$fU ($si)\n"); sl_send_reply(403, "Forbidden — User Mismatch"); exit; } /* Remove the Proxy-Authorization header before forwarding */ consume_credentials(); return;
}
loadmodule "permissions.so"
modparam("permissions", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
loadmodule "permissions.so"
modparam("permissions", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
loadmodule "permissions.so"
modparam("permissions", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
# Add a trusted trunk IP (group 1 = trunks)
kamctl address add 1 TRUNK_IP 32 5060 "Primary SIP Trunk" # Reload the address table
kamctl address reload # Show trusted addresses
kamctl address show
# Add a trusted trunk IP (group 1 = trunks)
kamctl address add 1 TRUNK_IP 32 5060 "Primary SIP Trunk" # Reload the address table
kamctl address reload # Show trusted addresses
kamctl address show
# Add a trusted trunk IP (group 1 = trunks)
kamctl address add 1 TRUNK_IP 32 5060 "Primary SIP Trunk" # Reload the address table
kamctl address reload # Show trusted addresses
kamctl address show
INSERT INTO address (grp, ip_addr, mask, port, tag)
VALUES (1, 'TRUNK_IP', 32, 5060, 'Primary SIP Trunk'); INSERT INTO address (grp, ip_addr, mask, port, tag)
VALUES (1, 'BACKUP_TRUNK_IP', 32, 5060, 'Backup SIP Trunk');
INSERT INTO address (grp, ip_addr, mask, port, tag)
VALUES (1, 'TRUNK_IP', 32, 5060, 'Primary SIP Trunk'); INSERT INTO address (grp, ip_addr, mask, port, tag)
VALUES (1, 'BACKUP_TRUNK_IP', 32, 5060, 'Backup SIP Trunk');
INSERT INTO address (grp, ip_addr, mask, port, tag)
VALUES (1, 'TRUNK_IP', 32, 5060, 'Primary SIP Trunk'); INSERT INTO address (grp, ip_addr, mask, port, tag)
VALUES (1, 'BACKUP_TRUNK_IP', 32, 5060, 'Backup SIP Trunk');
route[AUTH] { /* Check if source IP is a trusted trunk (group 1) */ if (allow_source_address(1)) { xlog("L_INFO", "Trusted trunk: $si ($hdr(P-Asserted-Identity))\n"); return; /* Skip digest auth */ } /* Not a trunk — require digest authentication */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } } else { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } consume_credentials(); } /* Verify From matches authenticated user */ if ($au != $null && $au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return;
}
route[AUTH] { /* Check if source IP is a trusted trunk (group 1) */ if (allow_source_address(1)) { xlog("L_INFO", "Trusted trunk: $si ($hdr(P-Asserted-Identity))\n"); return; /* Skip digest auth */ } /* Not a trunk — require digest authentication */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } } else { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } consume_credentials(); } /* Verify From matches authenticated user */ if ($au != $null && $au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return;
}
route[AUTH] { /* Check if source IP is a trusted trunk (group 1) */ if (allow_source_address(1)) { xlog("L_INFO", "Trusted trunk: $si ($hdr(P-Asserted-Identity))\n"); return; /* Skip digest auth */ } /* Not a trunk — require digest authentication */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } } else { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } consume_credentials(); } /* Verify From matches authenticated user */ if ($au != $null && $au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return;
}
request_route { /* Sanity */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { exit; } /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* ---- AUTHENTICATE ---- */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* Route to registered user or reject */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } route(RELAY); exit;
} route[AUTH] { /* Trusted trunk IPs — no auth needed */ if (allow_source_address(1)) return; /* REGISTER → 401 challenge */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } /* All other methods → 407 challenge */ if (is_method("INVITE|MESSAGE|SUBSCRIBE|NOTIFY|REFER")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[RELAY] { t_on_failure("MANAGE_FAILURE"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n");
}
request_route { /* Sanity */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { exit; } /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* ---- AUTHENTICATE ---- */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* Route to registered user or reject */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } route(RELAY); exit;
} route[AUTH] { /* Trusted trunk IPs — no auth needed */ if (allow_source_address(1)) return; /* REGISTER → 401 challenge */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } /* All other methods → 407 challenge */ if (is_method("INVITE|MESSAGE|SUBSCRIBE|NOTIFY|REFER")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[RELAY] { t_on_failure("MANAGE_FAILURE"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n");
}
request_route { /* Sanity */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { exit; } /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* ---- AUTHENTICATE ---- */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* Route to registered user or reject */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } route(RELAY); exit;
} route[AUTH] { /* Trusted trunk IPs — no auth needed */ if (allow_source_address(1)) return; /* REGISTER → 401 challenge */ if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } /* All other methods → 407 challenge */ if (is_method("INVITE|MESSAGE|SUBSCRIBE|NOTIFY|REFER")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[RELAY] { t_on_failure("MANAGE_FAILURE"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n");
}
┌──────────────┐ ┌──────────┐ ┌──────────────┐
│ Phone A │ │ NAT │ │ Kamailio │
│ 192.168.1.100│────────►│ Router │────────►│ Public IP │
│ SIP Contact: │ │203.0.113 │ │ │
│ 192.168.1.100│ │ .50 │ │ │
│ SDP c= line: │ │ │ │ │
│ 192.168.1.100│ │ │ │ │
└──────────────┘ └──────────┘ └──────────────┘ Problem: SIP headers say 192.168.1.100 Packets actually came from 203.0.113.50 Kamailio can't reply to 192.168.1.100!
┌──────────────┐ ┌──────────┐ ┌──────────────┐
│ Phone A │ │ NAT │ │ Kamailio │
│ 192.168.1.100│────────►│ Router │────────►│ Public IP │
│ SIP Contact: │ │203.0.113 │ │ │
│ 192.168.1.100│ │ .50 │ │ │
│ SDP c= line: │ │ │ │ │
│ 192.168.1.100│ │ │ │ │
└──────────────┘ └──────────┘ └──────────────┘ Problem: SIP headers say 192.168.1.100 Packets actually came from 203.0.113.50 Kamailio can't reply to 192.168.1.100!
┌──────────────┐ ┌──────────┐ ┌──────────────┐
│ Phone A │ │ NAT │ │ Kamailio │
│ 192.168.1.100│────────►│ Router │────────►│ Public IP │
│ SIP Contact: │ │203.0.113 │ │ │
│ 192.168.1.100│ │ .50 │ │ │
│ SDP c= line: │ │ │ │ │
│ 192.168.1.100│ │ │ │ │
└──────────────┘ └──────────┘ └──────────────┘ Problem: SIP headers say 192.168.1.100 Packets actually came from 203.0.113.50 Kamailio can't reply to 192.168.1.100!
/* Module loading */
loadmodule "nathelper.so" /* Module parameters */
modparam("nathelper", "natping_interval", 30) # Send keepalive every 30s
modparam("nathelper", "ping_nated_only", 1) # Only ping NATed clients
modparam("nathelper", "sipping", 1) # Use SIP OPTIONS as keepalive
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
/* Module loading */
loadmodule "nathelper.so" /* Module parameters */
modparam("nathelper", "natping_interval", 30) # Send keepalive every 30s
modparam("nathelper", "ping_nated_only", 1) # Only ping NATed clients
modparam("nathelper", "sipping", 1) # Use SIP OPTIONS as keepalive
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
/* Module loading */
loadmodule "nathelper.so" /* Module parameters */
modparam("nathelper", "natping_interval", 30) # Send keepalive every 30s
modparam("nathelper", "ping_nated_only", 1) # Only ping NATed clients
modparam("nathelper", "sipping", 1) # Use SIP OPTIONS as keepalive
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
route[NATDETECT] { /* Detect NAT */ if (nat_uac_test(63)) { /* Mark this transaction as NATed */ setflag(5); /* Flag 5 = NATed */ /* Fix the Contact header to use the real source IP:port */ fix_nated_contact(); /* Force adding rport to Via (so replies go to real source port) */ force_rport(); xlog("L_INFO", "NAT detected for $fU from $si:$sp\n"); } return;
} route[NATMANAGE] { /* Fix Contact in replies too */ if (is_reply()) { if (nat_uac_test(63)) { fix_nated_contact(); } } return;
}
route[NATDETECT] { /* Detect NAT */ if (nat_uac_test(63)) { /* Mark this transaction as NATed */ setflag(5); /* Flag 5 = NATed */ /* Fix the Contact header to use the real source IP:port */ fix_nated_contact(); /* Force adding rport to Via (so replies go to real source port) */ force_rport(); xlog("L_INFO", "NAT detected for $fU from $si:$sp\n"); } return;
} route[NATMANAGE] { /* Fix Contact in replies too */ if (is_reply()) { if (nat_uac_test(63)) { fix_nated_contact(); } } return;
}
route[NATDETECT] { /* Detect NAT */ if (nat_uac_test(63)) { /* Mark this transaction as NATed */ setflag(5); /* Flag 5 = NATed */ /* Fix the Contact header to use the real source IP:port */ fix_nated_contact(); /* Force adding rport to Via (so replies go to real source port) */ force_rport(); xlog("L_INFO", "NAT detected for $fU from $si:$sp\n"); } return;
} route[NATMANAGE] { /* Fix Contact in replies too */ if (is_reply()) { if (nat_uac_test(63)) { fix_nated_contact(); } } return;
}
route[REGISTRAR] { /* NAT fix for REGISTER */ if (nat_uac_test(63)) { setflag(5); fix_nated_register(); /* Store real IP:port in location DB */ force_rport(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
}
route[REGISTRAR] { /* NAT fix for REGISTER */ if (nat_uac_test(63)) { setflag(5); fix_nated_register(); /* Store real IP:port in location DB */ force_rport(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
}
route[REGISTRAR] { /* NAT fix for REGISTER */ if (nat_uac_test(63)) { setflag(5); fix_nated_register(); /* Store real IP:port in location DB */ force_rport(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
}
# Debian 12 / Ubuntu 24.04
-weight: 500;">apt -weight: 500;">install -y rtpengine # Or from the official repository for the latest version:
# Add the RTPEngine repo (check https://dfx.at/rtpengine/ for current URLs)
-weight: 500;">apt -weight: 500;">install -y \ ngcp-rtpengine-daemon \ ngcp-rtpengine-iptables \ ngcp-rtpengine-recording-daemon # Optional: for call recording
# Debian 12 / Ubuntu 24.04
-weight: 500;">apt -weight: 500;">install -y rtpengine # Or from the official repository for the latest version:
# Add the RTPEngine repo (check https://dfx.at/rtpengine/ for current URLs)
-weight: 500;">apt -weight: 500;">install -y \ ngcp-rtpengine-daemon \ ngcp-rtpengine-iptables \ ngcp-rtpengine-recording-daemon # Optional: for call recording
# Debian 12 / Ubuntu 24.04
-weight: 500;">apt -weight: 500;">install -y rtpengine # Or from the official repository for the latest version:
# Add the RTPEngine repo (check https://dfx.at/rtpengine/ for current URLs)
-weight: 500;">apt -weight: 500;">install -y \ ngcp-rtpengine-daemon \ ngcp-rtpengine-iptables \ ngcp-rtpengine-recording-daemon # Optional: for call recording
cat > /etc/rtpengine/rtpengine.conf << 'EOF'
[rtpengine]
# Control socket — Kamailio connects here
listen-ng=127.0.0.1:2223 # Network interfaces
interface=public/YOUR_SERVER_IP # Port range for RTP
port-min=10000
port-max=20000 # Timeouts
timeout=60
silent-timeout=600
final-timeout=7200 # Logging
log-level=5
log-facility=daemon # Recording (optional)
# recording-dir=/var/spool/rtpengine
# recording-method=pcap
# recording-format=eth # TOS/DSCP marking for QoS
tos=184 # Pidfile
pidfile=/run/rtpengine/rtpengine.pid # Number of worker threads
num-threads=4
EOF
cat > /etc/rtpengine/rtpengine.conf << 'EOF'
[rtpengine]
# Control socket — Kamailio connects here
listen-ng=127.0.0.1:2223 # Network interfaces
interface=public/YOUR_SERVER_IP # Port range for RTP
port-min=10000
port-max=20000 # Timeouts
timeout=60
silent-timeout=600
final-timeout=7200 # Logging
log-level=5
log-facility=daemon # Recording (optional)
# recording-dir=/var/spool/rtpengine
# recording-method=pcap
# recording-format=eth # TOS/DSCP marking for QoS
tos=184 # Pidfile
pidfile=/run/rtpengine/rtpengine.pid # Number of worker threads
num-threads=4
EOF
cat > /etc/rtpengine/rtpengine.conf << 'EOF'
[rtpengine]
# Control socket — Kamailio connects here
listen-ng=127.0.0.1:2223 # Network interfaces
interface=public/YOUR_SERVER_IP # Port range for RTP
port-min=10000
port-max=20000 # Timeouts
timeout=60
silent-timeout=600
final-timeout=7200 # Logging
log-level=5
log-facility=daemon # Recording (optional)
# recording-dir=/var/spool/rtpengine
# recording-method=pcap
# recording-format=eth # TOS/DSCP marking for QoS
tos=184 # Pidfile
pidfile=/run/rtpengine/rtpengine.pid # Number of worker threads
num-threads=4
EOF
# Enable and -weight: 500;">start RTPEngine
-weight: 500;">systemctl -weight: 500;">enable --now rtpengine
-weight: 500;">systemctl -weight: 500;">status rtpengine # Verify it's listening
ss -ulnp | grep rtpengine
# Enable and -weight: 500;">start RTPEngine
-weight: 500;">systemctl -weight: 500;">enable --now rtpengine
-weight: 500;">systemctl -weight: 500;">status rtpengine # Verify it's listening
ss -ulnp | grep rtpengine
# Enable and -weight: 500;">start RTPEngine
-weight: 500;">systemctl -weight: 500;">enable --now rtpengine
-weight: 500;">systemctl -weight: 500;">status rtpengine # Verify it's listening
ss -ulnp | grep rtpengine
/* Module loading */
loadmodule "rtpengine.so" /* Module parameters */
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
/* Module loading */
loadmodule "rtpengine.so" /* Module parameters */
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
/* Module loading */
loadmodule "rtpengine.so" /* Module parameters */
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
route[NATMANAGE] { /* Determine RTPEngine flags based on NAT -weight: 500;">status and direction */ if (is_request()) { if (has_body("application/sdp")) { if (isflagset(5)) { /* Caller is NATed — replace private IPs, force relay */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { /* Caller is not NATed — just relay for consistency */ rtpengine_manage("ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if (isflagset(5)) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { rtpengine_manage("ICE=-weight: 500;">remove"); } } } return;
}
route[NATMANAGE] { /* Determine RTPEngine flags based on NAT -weight: 500;">status and direction */ if (is_request()) { if (has_body("application/sdp")) { if (isflagset(5)) { /* Caller is NATed — replace private IPs, force relay */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { /* Caller is not NATed — just relay for consistency */ rtpengine_manage("ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if (isflagset(5)) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { rtpengine_manage("ICE=-weight: 500;">remove"); } } } return;
}
route[NATMANAGE] { /* Determine RTPEngine flags based on NAT -weight: 500;">status and direction */ if (is_request()) { if (has_body("application/sdp")) { if (isflagset(5)) { /* Caller is NATed — replace private IPs, force relay */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { /* Caller is not NATed — just relay for consistency */ rtpengine_manage("ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if (isflagset(5)) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { rtpengine_manage("ICE=-weight: 500;">remove"); } } } return;
}
-weight: 500;">apt -weight: 500;">install -y coturn
# Configuration in /etc/turnserver.conf
-weight: 500;">apt -weight: 500;">install -y coturn
# Configuration in /etc/turnserver.conf
-weight: 500;">apt -weight: 500;">install -y coturn
# Configuration in /etc/turnserver.conf
#!KAMAILIO /* ===== Global Parameters ===== */
listen=udp:YOUR_SERVER_IP:5060 advertise YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060 advertise YOUR_SERVER_IP:5060 debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
server_signature=no
force_rport=yes /* Always add rport */ /* ===== Module Loading ===== */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so" /* ===== Module Parameters ===== */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio") modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600) modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)") modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* ===== Routing Logic ===== */
request_route { /* Sanity checks */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { exit; } /* NAT detection */ route(NATDETECT); /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE: $fU → $tU\n"); /* Release RTPEngine session */ rtpengine_delete(); } if (is_method("INVITE|UPDATE|ACK")) { route(NATMANAGE); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Authenticate */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { route(REGISTRAR); exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE — lookup and route */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } /* NAT manage for initial INVITE */ route(NATMANAGE); route(RELAY); exit;
} route[RELAY] { t_on_reply("MANAGE_REPLY"); t_on_failure("MANAGE_FAILURE"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } if (is_method("INVITE|MESSAGE|SUBSCRIBE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[REGISTRAR] { if (nat_uac_test(63)) { setflag(5); fix_nated_register(); force_rport(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
} route[NATDETECT] { if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } return;
} route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { if (isflagset(5)) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { rtpengine_manage("ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
} onreply_route[MANAGE_REPLY] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE); }
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n"); if ($T_reply_code == 486 || $T_reply_code == 408) { xlog("L_INFO", "User $rU busy or timeout\n"); }
}
#!KAMAILIO /* ===== Global Parameters ===== */
listen=udp:YOUR_SERVER_IP:5060 advertise YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060 advertise YOUR_SERVER_IP:5060 debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
server_signature=no
force_rport=yes /* Always add rport */ /* ===== Module Loading ===== */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so" /* ===== Module Parameters ===== */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio") modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600) modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)") modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* ===== Routing Logic ===== */
request_route { /* Sanity checks */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { exit; } /* NAT detection */ route(NATDETECT); /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE: $fU → $tU\n"); /* Release RTPEngine session */ rtpengine_delete(); } if (is_method("INVITE|UPDATE|ACK")) { route(NATMANAGE); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Authenticate */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { route(REGISTRAR); exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE — lookup and route */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } /* NAT manage for initial INVITE */ route(NATMANAGE); route(RELAY); exit;
} route[RELAY] { t_on_reply("MANAGE_REPLY"); t_on_failure("MANAGE_FAILURE"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } if (is_method("INVITE|MESSAGE|SUBSCRIBE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[REGISTRAR] { if (nat_uac_test(63)) { setflag(5); fix_nated_register(); force_rport(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
} route[NATDETECT] { if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } return;
} route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { if (isflagset(5)) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { rtpengine_manage("ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
} onreply_route[MANAGE_REPLY] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE); }
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n"); if ($T_reply_code == 486 || $T_reply_code == 408) { xlog("L_INFO", "User $rU busy or timeout\n"); }
}
#!KAMAILIO /* ===== Global Parameters ===== */
listen=udp:YOUR_SERVER_IP:5060 advertise YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060 advertise YOUR_SERVER_IP:5060 debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
server_signature=no
force_rport=yes /* Always add rport */ /* ===== Module Loading ===== */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so" /* ===== Module Parameters ===== */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio") modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600) modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)") modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* ===== Routing Logic ===== */
request_route { /* Sanity checks */ if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) { exit; } /* NAT detection */ route(NATDETECT); /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { xlog("L_INFO", "BYE: $fU → $tU\n"); /* Release RTPEngine session */ rtpengine_delete(); } if (is_method("INVITE|UPDATE|ACK")) { route(NATMANAGE); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Authenticate */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { route(REGISTRAR); exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE — lookup and route */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } /* NAT manage for initial INVITE */ route(NATMANAGE); route(RELAY); exit;
} route[RELAY] { t_on_reply("MANAGE_REPLY"); t_on_failure("MANAGE_FAILURE"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } if (is_method("INVITE|MESSAGE|SUBSCRIBE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[REGISTRAR] { if (nat_uac_test(63)) { setflag(5); fix_nated_register(); force_rport(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit;
} route[NATDETECT] { if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } return;
} route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { if (isflagset(5)) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } else { rtpengine_manage("ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
} onreply_route[MANAGE_REPLY] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE); }
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n"); if ($T_reply_code == 486 || $T_reply_code == 408) { xlog("L_INFO", "User $rU busy or timeout\n"); }
}
/* Module loading */
loadmodule "tls.so" /* Global: add TLS listen address */
listen=tls:YOUR_SERVER_IP:5061 /* TLS module parameters */
modparam("tls", "config", "/etc/kamailio/tls.cfg")
modparam("tls", "tls_force_run", 1) /* Start even if cert is missing */ /* Alternative: inline TLS config (instead of separate tls.cfg) */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "ca_list", "/etc/kamailio/certs/chain.pem")
modparam("tls", "tls_method", "TLSv1.2+") /* Minimum TLS 1.2 */
modparam("tls", "verify_certificate", 0) /* 0=don't require client cert */
modparam("tls", "require_certificate", 0) /* 0=allow non-TLS clients */
modparam("tls", "connection_timeout", 60)
/* Module loading */
loadmodule "tls.so" /* Global: add TLS listen address */
listen=tls:YOUR_SERVER_IP:5061 /* TLS module parameters */
modparam("tls", "config", "/etc/kamailio/tls.cfg")
modparam("tls", "tls_force_run", 1) /* Start even if cert is missing */ /* Alternative: inline TLS config (instead of separate tls.cfg) */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "ca_list", "/etc/kamailio/certs/chain.pem")
modparam("tls", "tls_method", "TLSv1.2+") /* Minimum TLS 1.2 */
modparam("tls", "verify_certificate", 0) /* 0=don't require client cert */
modparam("tls", "require_certificate", 0) /* 0=allow non-TLS clients */
modparam("tls", "connection_timeout", 60)
/* Module loading */
loadmodule "tls.so" /* Global: add TLS listen address */
listen=tls:YOUR_SERVER_IP:5061 /* TLS module parameters */
modparam("tls", "config", "/etc/kamailio/tls.cfg")
modparam("tls", "tls_force_run", 1) /* Start even if cert is missing */ /* Alternative: inline TLS config (instead of separate tls.cfg) */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "ca_list", "/etc/kamailio/certs/chain.pem")
modparam("tls", "tls_method", "TLSv1.2+") /* Minimum TLS 1.2 */
modparam("tls", "verify_certificate", 0) /* 0=don't require client cert */
modparam("tls", "require_certificate", 0) /* 0=allow non-TLS clients */
modparam("tls", "connection_timeout", 60)
# /etc/kamailio/tls.cfg [server:default]
method = TLSv1.2+
verify_certificate = no
require_certificate = no
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/chain.pem # Specific config for a trusted peer
[server:TRUNK_IP:5061]
method = TLSv1.2+
verify_certificate = yes
require_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/trunk-ca.pem [client:default]
method = TLSv1.2+
verify_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/chain.pem
# /etc/kamailio/tls.cfg [server:default]
method = TLSv1.2+
verify_certificate = no
require_certificate = no
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/chain.pem # Specific config for a trusted peer
[server:TRUNK_IP:5061]
method = TLSv1.2+
verify_certificate = yes
require_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/trunk-ca.pem [client:default]
method = TLSv1.2+
verify_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/chain.pem
# /etc/kamailio/tls.cfg [server:default]
method = TLSv1.2+
verify_certificate = no
require_certificate = no
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/chain.pem # Specific config for a trusted peer
[server:TRUNK_IP:5061]
method = TLSv1.2+
verify_certificate = yes
require_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/trunk-ca.pem [client:default]
method = TLSv1.2+
verify_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/chain.pem
# Install certbot
-weight: 500;">apt -weight: 500;">install -y certbot # Obtain certificate (standalone mode — -weight: 500;">stop Kamailio briefly)
-weight: 500;">systemctl -weight: 500;">stop kamailio
certbot certonly --standalone -d YOUR_DOMAIN # Certificate files are at:
# /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem
# /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem
# /etc/letsencrypt/live/YOUR_DOMAIN/chain.pem # Create symlinks for Kamailio
mkdir -p /etc/kamailio/certs
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem /etc/kamailio/certs/fullchain.pem
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem /etc/kamailio/certs/privkey.pem
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/chain.pem /etc/kamailio/certs/chain.pem # Set permissions
chown -R kamailio:kamailio /etc/kamailio/certs/ # Auto-renewal with Kamailio reload
cat > /etc/letsencrypt/renewal-hooks/post/kamailio.sh << 'HOOK'
#!/bin/bash
-weight: 500;">systemctl reload kamailio
HOOK
chmod +x /etc/letsencrypt/renewal-hooks/post/kamailio.sh -weight: 500;">systemctl -weight: 500;">start kamailio
# Install certbot
-weight: 500;">apt -weight: 500;">install -y certbot # Obtain certificate (standalone mode — -weight: 500;">stop Kamailio briefly)
-weight: 500;">systemctl -weight: 500;">stop kamailio
certbot certonly --standalone -d YOUR_DOMAIN # Certificate files are at:
# /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem
# /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem
# /etc/letsencrypt/live/YOUR_DOMAIN/chain.pem # Create symlinks for Kamailio
mkdir -p /etc/kamailio/certs
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem /etc/kamailio/certs/fullchain.pem
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem /etc/kamailio/certs/privkey.pem
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/chain.pem /etc/kamailio/certs/chain.pem # Set permissions
chown -R kamailio:kamailio /etc/kamailio/certs/ # Auto-renewal with Kamailio reload
cat > /etc/letsencrypt/renewal-hooks/post/kamailio.sh << 'HOOK'
#!/bin/bash
-weight: 500;">systemctl reload kamailio
HOOK
chmod +x /etc/letsencrypt/renewal-hooks/post/kamailio.sh -weight: 500;">systemctl -weight: 500;">start kamailio
# Install certbot
-weight: 500;">apt -weight: 500;">install -y certbot # Obtain certificate (standalone mode — -weight: 500;">stop Kamailio briefly)
-weight: 500;">systemctl -weight: 500;">stop kamailio
certbot certonly --standalone -d YOUR_DOMAIN # Certificate files are at:
# /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem
# /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem
# /etc/letsencrypt/live/YOUR_DOMAIN/chain.pem # Create symlinks for Kamailio
mkdir -p /etc/kamailio/certs
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem /etc/kamailio/certs/fullchain.pem
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem /etc/kamailio/certs/privkey.pem
ln -sf /etc/letsencrypt/live/YOUR_DOMAIN/chain.pem /etc/kamailio/certs/chain.pem # Set permissions
chown -R kamailio:kamailio /etc/kamailio/certs/ # Auto-renewal with Kamailio reload
cat > /etc/letsencrypt/renewal-hooks/post/kamailio.sh << 'HOOK'
#!/bin/bash
-weight: 500;">systemctl reload kamailio
HOOK
chmod +x /etc/letsencrypt/renewal-hooks/post/kamailio.sh -weight: 500;">systemctl -weight: 500;">start kamailio
mkdir -p /etc/kamailio/certs
cd /etc/kamailio/certs # Generate CA
openssl genrsa -out ca-key.pem 4096
openssl req -x509 -new -nodes -key ca-key.pem -sha256 -days 3650 \ -out ca-cert.pem \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=SIP-CA" # Generate server certificate
openssl genrsa -out privkey.pem 2048
openssl req -new -key privkey.pem -out server.csr \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=YOUR_DOMAIN" # Sign with CA
openssl x509 -req -in server.csr -CA ca-cert.pem -CAkey ca-key.pem \ -CAcreateserial -out fullchain.pem -days 365 -sha256 # Copy CA cert as chain
cp ca-cert.pem chain.pem # Set ownership
chown -R kamailio:kamailio /etc/kamailio/certs/
chmod 600 /etc/kamailio/certs/privkey.pem
mkdir -p /etc/kamailio/certs
cd /etc/kamailio/certs # Generate CA
openssl genrsa -out ca-key.pem 4096
openssl req -x509 -new -nodes -key ca-key.pem -sha256 -days 3650 \ -out ca-cert.pem \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=SIP-CA" # Generate server certificate
openssl genrsa -out privkey.pem 2048
openssl req -new -key privkey.pem -out server.csr \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=YOUR_DOMAIN" # Sign with CA
openssl x509 -req -in server.csr -CA ca-cert.pem -CAkey ca-key.pem \ -CAcreateserial -out fullchain.pem -days 365 -sha256 # Copy CA cert as chain
cp ca-cert.pem chain.pem # Set ownership
chown -R kamailio:kamailio /etc/kamailio/certs/
chmod 600 /etc/kamailio/certs/privkey.pem
mkdir -p /etc/kamailio/certs
cd /etc/kamailio/certs # Generate CA
openssl genrsa -out ca-key.pem 4096
openssl req -x509 -new -nodes -key ca-key.pem -sha256 -days 3650 \ -out ca-cert.pem \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=SIP-CA" # Generate server certificate
openssl genrsa -out privkey.pem 2048
openssl req -new -key privkey.pem -out server.csr \ -subj "/C=US/ST=State/L=City/O=MyOrg/CN=YOUR_DOMAIN" # Sign with CA
openssl x509 -req -in server.csr -CA ca-cert.pem -CAkey ca-key.pem \ -CAcreateserial -out fullchain.pem -days 365 -sha256 # Copy CA cert as chain
cp ca-cert.pem chain.pem # Set ownership
chown -R kamailio:kamailio /etc/kamailio/certs/
chmod 600 /etc/kamailio/certs/privkey.pem
/* In tls.cfg — require client certificates */
[server:default]
method = TLSv1.2+
verify_certificate = yes
require_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/ca-cert.pem
/* In tls.cfg — require client certificates */
[server:default]
method = TLSv1.2+
verify_certificate = yes
require_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/ca-cert.pem
/* In tls.cfg — require client certificates */
[server:default]
method = TLSv1.2+
verify_certificate = yes
require_certificate = yes
private_key = /etc/kamailio/certs/privkey.pem
certificate = /etc/kamailio/certs/fullchain.pem
ca_list = /etc/kamailio/certs/ca-cert.pem
if ($proto == "tls") { xlog("L_INFO", "TLS peer: $tls_peer_subject\n"); xlog("L_INFO", "TLS peer CN: $tls_peer_subject_cn\n"); xlog("L_INFO", "TLS peer verified: $tls_peer_verified\n"); if ($tls_peer_verified != 1) { sl_send_reply(403, "Certificate Verification Failed"); exit; }
}
if ($proto == "tls") { xlog("L_INFO", "TLS peer: $tls_peer_subject\n"); xlog("L_INFO", "TLS peer CN: $tls_peer_subject_cn\n"); xlog("L_INFO", "TLS peer verified: $tls_peer_verified\n"); if ($tls_peer_verified != 1) { sl_send_reply(403, "Certificate Verification Failed"); exit; }
}
if ($proto == "tls") { xlog("L_INFO", "TLS peer: $tls_peer_subject\n"); xlog("L_INFO", "TLS peer CN: $tls_peer_subject_cn\n"); xlog("L_INFO", "TLS peer verified: $tls_peer_verified\n"); if ($tls_peer_verified != 1) { sl_send_reply(403, "Certificate Verification Failed"); exit; }
}
/* In NATMANAGE route — encrypt media */
route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { if ($proto == "tls" || $proto == "wss") { /* TLS/WSS client — use SRTP on client side, plain RTP to backend */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove SDES-off RTP/SAVP"); } else { /* Plain SIP — no SRTP */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
}
/* In NATMANAGE route — encrypt media */
route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { if ($proto == "tls" || $proto == "wss") { /* TLS/WSS client — use SRTP on client side, plain RTP to backend */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove SDES-off RTP/SAVP"); } else { /* Plain SIP — no SRTP */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
}
/* In NATMANAGE route — encrypt media */
route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { if ($proto == "tls" || $proto == "wss") { /* TLS/WSS client — use SRTP on client side, plain RTP to backend */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove SDES-off RTP/SAVP"); } else { /* Plain SIP — no SRTP */ rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
}
loadmodule "pike.so"
modparam("pike", "sampling_time_unit", 2) /* 2-second window */
modparam("pike", "reqs_density_per_unit", 30) /* Max 30 requests per window */
modparam("pike", "remove_latency", 4) /* Block for 4 windows (8 seconds) */
loadmodule "pike.so"
modparam("pike", "sampling_time_unit", 2) /* 2-second window */
modparam("pike", "reqs_density_per_unit", 30) /* Max 30 requests per window */
modparam("pike", "remove_latency", 4) /* Block for 4 windows (8 seconds) */
loadmodule "pike.so"
modparam("pike", "sampling_time_unit", 2) /* 2-second window */
modparam("pike", "reqs_density_per_unit", 30) /* Max 30 requests per window */
modparam("pike", "remove_latency", 4) /* Block for 4 windows (8 seconds) */
request_route { /* Anti-flood — check before anything else */ if (src_ip != myself) { if (!pike_check_req()) { xlog("L_ALERT", "PIKE: Blocking flood from $si\n"); /* Silently drop — don't send reply (wastes resources) */ exit; } } /* ... rest of routing ... */
}
request_route { /* Anti-flood — check before anything else */ if (src_ip != myself) { if (!pike_check_req()) { xlog("L_ALERT", "PIKE: Blocking flood from $si\n"); /* Silently drop — don't send reply (wastes resources) */ exit; } } /* ... rest of routing ... */
}
request_route { /* Anti-flood — check before anything else */ if (src_ip != myself) { if (!pike_check_req()) { xlog("L_ALERT", "PIKE: Blocking flood from $si\n"); /* Silently drop — don't send reply (wastes resources) */ exit; } } /* ... rest of routing ... */
}
loadmodule "htable.so" /* Define a blacklist table */
modparam("htable", "htable", "ipban=>size=8;autoexpire=3600;")
/* size=8: 2^8 = 256 slots, autoexpire=3600: entries expire after 1 hour */ /* Define a counter table for rate limiting per user */
modparam("htable", "htable", "ratelimit=>size=10;autoexpire=60;")
loadmodule "htable.so" /* Define a blacklist table */
modparam("htable", "htable", "ipban=>size=8;autoexpire=3600;")
/* size=8: 2^8 = 256 slots, autoexpire=3600: entries expire after 1 hour */ /* Define a counter table for rate limiting per user */
modparam("htable", "htable", "ratelimit=>size=10;autoexpire=60;")
loadmodule "htable.so" /* Define a blacklist table */
modparam("htable", "htable", "ipban=>size=8;autoexpire=3600;")
/* size=8: 2^8 = 256 slots, autoexpire=3600: entries expire after 1 hour */ /* Define a counter table for rate limiting per user */
modparam("htable", "htable", "ratelimit=>size=10;autoexpire=60;")
request_route { /* Check blacklist */ if ($sht(ipban=>$si) != $null) { xlog("L_WARN", "Blocked blacklisted IP: $si\n"); exit; /* Silently drop */ } /* After pike blocks, add to blacklist */ if (src_ip != myself) { if (!pike_check_req()) { $sht(ipban=>$si) = 1; xlog("L_ALERT", "BLACKLISTED: $si (pike trigger)\n"); exit; } } /* Per-user rate limiting (max 10 INVITE per minute) */ if (is_method("INVITE") && !has_totag()) { $sht(ratelimit=>$fU) = $sht(ratelimit=>$fU) + 1; if ($sht(ratelimit=>$fU) > 10) { sl_send_reply(429, "Too Many Requests"); exit; } }
}
request_route { /* Check blacklist */ if ($sht(ipban=>$si) != $null) { xlog("L_WARN", "Blocked blacklisted IP: $si\n"); exit; /* Silently drop */ } /* After pike blocks, add to blacklist */ if (src_ip != myself) { if (!pike_check_req()) { $sht(ipban=>$si) = 1; xlog("L_ALERT", "BLACKLISTED: $si (pike trigger)\n"); exit; } } /* Per-user rate limiting (max 10 INVITE per minute) */ if (is_method("INVITE") && !has_totag()) { $sht(ratelimit=>$fU) = $sht(ratelimit=>$fU) + 1; if ($sht(ratelimit=>$fU) > 10) { sl_send_reply(429, "Too Many Requests"); exit; } }
}
request_route { /* Check blacklist */ if ($sht(ipban=>$si) != $null) { xlog("L_WARN", "Blocked blacklisted IP: $si\n"); exit; /* Silently drop */ } /* After pike blocks, add to blacklist */ if (src_ip != myself) { if (!pike_check_req()) { $sht(ipban=>$si) = 1; xlog("L_ALERT", "BLACKLISTED: $si (pike trigger)\n"); exit; } } /* Per-user rate limiting (max 10 INVITE per minute) */ if (is_method("INVITE") && !has_totag()) { $sht(ratelimit=>$fU) = $sht(ratelimit=>$fU) + 1; if ($sht(ratelimit=>$fU) > 10) { sl_send_reply(429, "Too Many Requests"); exit; } }
}
/* In the AUTH route — log failures */
route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { /* Log for fail2ban — note the specific format */ xlog("L_WARN", "AUTH_FAILED: method=REGISTER user=$fU from=$si\n"); www_challenge("YOUR_DOMAIN", 1); exit; } }
}
/* In the AUTH route — log failures */
route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { /* Log for fail2ban — note the specific format */ xlog("L_WARN", "AUTH_FAILED: method=REGISTER user=$fU from=$si\n"); www_challenge("YOUR_DOMAIN", 1); exit; } }
}
/* In the AUTH route — log failures */
route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { /* Log for fail2ban — note the specific format */ xlog("L_WARN", "AUTH_FAILED: method=REGISTER user=$fU from=$si\n"); www_challenge("YOUR_DOMAIN", 1); exit; } }
}
cat > /etc/fail2ban/filter.d/kamailio.conf << 'EOF'
[Definition]
failregex = AUTH_FAILED:.*from=<HOST>
ignoreregex =
EOF
cat > /etc/fail2ban/filter.d/kamailio.conf << 'EOF'
[Definition]
failregex = AUTH_FAILED:.*from=<HOST>
ignoreregex =
EOF
cat > /etc/fail2ban/filter.d/kamailio.conf << 'EOF'
[Definition]
failregex = AUTH_FAILED:.*from=<HOST>
ignoreregex =
EOF
cat > /etc/fail2ban/jail.d/kamailio.conf << 'EOF'
[kamailio]
enabled = true
filter = kamailio
logpath = /var/log/kamailio/kamailio.log
maxretry = 5
findtime = 300
bantime = 3600
action = iptables-allports[name=kamailio, protocol=all]
EOF
cat > /etc/fail2ban/jail.d/kamailio.conf << 'EOF'
[kamailio]
enabled = true
filter = kamailio
logpath = /var/log/kamailio/kamailio.log
maxretry = 5
findtime = 300
bantime = 3600
action = iptables-allports[name=kamailio, protocol=all]
EOF
cat > /etc/fail2ban/jail.d/kamailio.conf << 'EOF'
[kamailio]
enabled = true
filter = kamailio
logpath = /var/log/kamailio/kamailio.log
maxretry = 5
findtime = 300
bantime = 3600
action = iptables-allports[name=kamailio, protocol=all]
EOF
# Make sure Kamailio logs to a file (rsyslog)
cat > /etc/rsyslog.d/kamailio.conf << 'EOF'
local0.* /var/log/kamailio/kamailio.log
EOF mkdir -p /var/log/kamailio
-weight: 500;">systemctl -weight: 500;">restart rsyslog
-weight: 500;">systemctl -weight: 500;">restart fail2ban
# Make sure Kamailio logs to a file (rsyslog)
cat > /etc/rsyslog.d/kamailio.conf << 'EOF'
local0.* /var/log/kamailio/kamailio.log
EOF mkdir -p /var/log/kamailio
-weight: 500;">systemctl -weight: 500;">restart rsyslog
-weight: 500;">systemctl -weight: 500;">restart fail2ban
# Make sure Kamailio logs to a file (rsyslog)
cat > /etc/rsyslog.d/kamailio.conf << 'EOF'
local0.* /var/log/kamailio/kamailio.log
EOF mkdir -p /var/log/kamailio
-weight: 500;">systemctl -weight: 500;">restart rsyslog
-weight: 500;">systemctl -weight: 500;">restart fail2ban
┌──────────────┐ │ Kamailio │ │ (Dispatcher)│ └──────┬───────┘ │ ┌────────────┼────────────┐ │ │ │ ┌────▼───┐ ┌────▼───┐ ┌────▼───┐ │Asterisk│ │Asterisk│ │Asterisk│ │ #1 │ │ #2 │ │ #3 │ └────────┘ └────────┘ └────────┘
┌──────────────┐ │ Kamailio │ │ (Dispatcher)│ └──────┬───────┘ │ ┌────────────┼────────────┐ │ │ │ ┌────▼───┐ ┌────▼───┐ ┌────▼───┐ │Asterisk│ │Asterisk│ │Asterisk│ │ #1 │ │ #2 │ │ #3 │ └────────┘ └────────┘ └────────┘
┌──────────────┐ │ Kamailio │ │ (Dispatcher)│ └──────┬───────┘ │ ┌────────────┼────────────┐ │ │ │ ┌────▼───┐ ┌────▼───┐ ┌────▼───┐ │Asterisk│ │Asterisk│ │Asterisk│ │ #1 │ │ #2 │ │ #3 │ └────────┘ └────────┘ └────────┘
loadmodule "dispatcher.so" modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "table_name", "dispatcher")
modparam("dispatcher", "flags", 2) /* Failover support */
modparam("dispatcher", "ds_ping_interval", 10) /* Health check every 10s */
modparam("dispatcher", "ds_ping_method", "OPTIONS") /* Use SIP OPTIONS */
modparam("dispatcher", "ds_probing_threshold", 3) /* 3 failures = down */
modparam("dispatcher", "ds_inactive_threshold", 3) /* 3 successes = up */
modparam("dispatcher", "ds_probing_mode", 1) /* Probe all destinations */
modparam("dispatcher", "ds_ping_from", "sip:kamailio@YOUR_DOMAIN")
modparam("dispatcher", "ds_ping_reply_codes", "class2;class3;class4")
loadmodule "dispatcher.so" modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "table_name", "dispatcher")
modparam("dispatcher", "flags", 2) /* Failover support */
modparam("dispatcher", "ds_ping_interval", 10) /* Health check every 10s */
modparam("dispatcher", "ds_ping_method", "OPTIONS") /* Use SIP OPTIONS */
modparam("dispatcher", "ds_probing_threshold", 3) /* 3 failures = down */
modparam("dispatcher", "ds_inactive_threshold", 3) /* 3 successes = up */
modparam("dispatcher", "ds_probing_mode", 1) /* Probe all destinations */
modparam("dispatcher", "ds_ping_from", "sip:kamailio@YOUR_DOMAIN")
modparam("dispatcher", "ds_ping_reply_codes", "class2;class3;class4")
loadmodule "dispatcher.so" modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "table_name", "dispatcher")
modparam("dispatcher", "flags", 2) /* Failover support */
modparam("dispatcher", "ds_ping_interval", 10) /* Health check every 10s */
modparam("dispatcher", "ds_ping_method", "OPTIONS") /* Use SIP OPTIONS */
modparam("dispatcher", "ds_probing_threshold", 3) /* 3 failures = down */
modparam("dispatcher", "ds_inactive_threshold", 3) /* 3 successes = up */
modparam("dispatcher", "ds_probing_mode", 1) /* Probe all destinations */
modparam("dispatcher", "ds_ping_from", "sip:kamailio@YOUR_DOMAIN")
modparam("dispatcher", "ds_ping_reply_codes", "class2;class3;class4")
/* dispatcher table: setid, destination, flags, priority, attrs, description */ -- Set 1: Asterisk backends (Round-Robin)
INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.10:5060', 0, 0, 'weight=50;duid=ast1', 'Asterisk 1'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.11:5060', 0, 0, 'weight=30;duid=ast2', 'Asterisk 2'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.12:5060', 0, 0, 'weight=20;duid=ast3', 'Asterisk 3'); -- Set 2: Media servers (Priority-based failover)
INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (2, 'sip:10.0.0.20:5060', 0, 0, 'duid=media1', 'Primary Media'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (2, 'sip:10.0.0.21:5060', 0, 1, 'duid=media2', 'Backup Media');
/* dispatcher table: setid, destination, flags, priority, attrs, description */ -- Set 1: Asterisk backends (Round-Robin)
INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.10:5060', 0, 0, 'weight=50;duid=ast1', 'Asterisk 1'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.11:5060', 0, 0, 'weight=30;duid=ast2', 'Asterisk 2'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.12:5060', 0, 0, 'weight=20;duid=ast3', 'Asterisk 3'); -- Set 2: Media servers (Priority-based failover)
INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (2, 'sip:10.0.0.20:5060', 0, 0, 'duid=media1', 'Primary Media'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (2, 'sip:10.0.0.21:5060', 0, 1, 'duid=media2', 'Backup Media');
/* dispatcher table: setid, destination, flags, priority, attrs, description */ -- Set 1: Asterisk backends (Round-Robin)
INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.10:5060', 0, 0, 'weight=50;duid=ast1', 'Asterisk 1'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.11:5060', 0, 0, 'weight=30;duid=ast2', 'Asterisk 2'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (1, 'sip:10.0.0.12:5060', 0, 0, 'weight=20;duid=ast3', 'Asterisk 3'); -- Set 2: Media servers (Priority-based failover)
INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (2, 'sip:10.0.0.20:5060', 0, 0, 'duid=media1', 'Primary Media'); INSERT INTO dispatcher (setid, destination, flags, priority, attrs, description)
VALUES (2, 'sip:10.0.0.21:5060', 0, 1, 'duid=media2', 'Backup Media');
# Reload dispatcher data
kamcmd dispatcher.reload
# Reload dispatcher data
kamcmd dispatcher.reload
# Reload dispatcher data
kamcmd dispatcher.reload
modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list")
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list")
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list")
modparam("dispatcher", "ds_probing_mode", 1)
# Format: setid destination flags priority attrs
# Set 1: Asterisk backends
1 sip:10.0.0.10:5060 0 0 weight=50;duid=ast1
1 sip:10.0.0.11:5060 0 0 weight=30;duid=ast2
1 sip:10.0.0.12:5060 0 0 weight=20;duid=ast3 # Set 2: Media servers (priority failover)
2 sip:10.0.0.20:5060 0 0 duid=media1
2 sip:10.0.0.21:5060 0 1 duid=media2
# Format: setid destination flags priority attrs
# Set 1: Asterisk backends
1 sip:10.0.0.10:5060 0 0 weight=50;duid=ast1
1 sip:10.0.0.11:5060 0 0 weight=30;duid=ast2
1 sip:10.0.0.12:5060 0 0 weight=20;duid=ast3 # Set 2: Media servers (priority failover)
2 sip:10.0.0.20:5060 0 0 duid=media1
2 sip:10.0.0.21:5060 0 1 duid=media2
# Format: setid destination flags priority attrs
# Set 1: Asterisk backends
1 sip:10.0.0.10:5060 0 0 weight=50;duid=ast1
1 sip:10.0.0.11:5060 0 0 weight=30;duid=ast2
1 sip:10.0.0.12:5060 0 0 weight=20;duid=ast3 # Set 2: Media servers (priority failover)
2 sip:10.0.0.20:5060 0 0 duid=media1
2 sip:10.0.0.21:5060 0 1 duid=media2
route[DISPATCH] { /* Select a backend from set 1 using round-robin (algorithm 0) */ if (!ds_select_dst(1, 0)) { /* No backends available */ xlog("L_ERR", "DISPATCHER: No backends available in set 1\n"); sl_send_reply(503, "Service Unavailable"); exit; } xlog("L_INFO", "DISPATCH: Routing $rU to $du (set 1)\n"); /* Enable failover — if this backend fails, try the next */ t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit;
} failure_route[DISPATCH_FAILURE] { if (t_is_canceled()) exit; /* Mark the failed destination as inactive */ if (t_check_status("5[0-9][0-9]") || t_check_status("408")) { xlog("L_WARN", "DISPATCH: Backend $du failed ($T_reply_code), trying next\n"); ds_mark_dst("ip"); /* Mark as inactive (probing) */ /* Try the next backend in the set */ if (ds_next_dst()) { xlog("L_INFO", "DISPATCH: Failover to $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit; } /* All backends down */ xlog("L_ERR", "DISPATCH: All backends in set 1 are down\n"); t_reply(503, "All Backends Unavailable"); exit; }
}
route[DISPATCH] { /* Select a backend from set 1 using round-robin (algorithm 0) */ if (!ds_select_dst(1, 0)) { /* No backends available */ xlog("L_ERR", "DISPATCHER: No backends available in set 1\n"); sl_send_reply(503, "Service Unavailable"); exit; } xlog("L_INFO", "DISPATCH: Routing $rU to $du (set 1)\n"); /* Enable failover — if this backend fails, try the next */ t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit;
} failure_route[DISPATCH_FAILURE] { if (t_is_canceled()) exit; /* Mark the failed destination as inactive */ if (t_check_status("5[0-9][0-9]") || t_check_status("408")) { xlog("L_WARN", "DISPATCH: Backend $du failed ($T_reply_code), trying next\n"); ds_mark_dst("ip"); /* Mark as inactive (probing) */ /* Try the next backend in the set */ if (ds_next_dst()) { xlog("L_INFO", "DISPATCH: Failover to $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit; } /* All backends down */ xlog("L_ERR", "DISPATCH: All backends in set 1 are down\n"); t_reply(503, "All Backends Unavailable"); exit; }
}
route[DISPATCH] { /* Select a backend from set 1 using round-robin (algorithm 0) */ if (!ds_select_dst(1, 0)) { /* No backends available */ xlog("L_ERR", "DISPATCHER: No backends available in set 1\n"); sl_send_reply(503, "Service Unavailable"); exit; } xlog("L_INFO", "DISPATCH: Routing $rU to $du (set 1)\n"); /* Enable failover — if this backend fails, try the next */ t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit;
} failure_route[DISPATCH_FAILURE] { if (t_is_canceled()) exit; /* Mark the failed destination as inactive */ if (t_check_status("5[0-9][0-9]") || t_check_status("408")) { xlog("L_WARN", "DISPATCH: Backend $du failed ($T_reply_code), trying next\n"); ds_mark_dst("ip"); /* Mark as inactive (probing) */ /* Try the next backend in the set */ if (ds_next_dst()) { xlog("L_INFO", "DISPATCH: Failover to $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit; } /* All backends down */ xlog("L_ERR", "DISPATCH: All backends in set 1 are down\n"); t_reply(503, "All Backends Unavailable"); exit; }
}
Backend UP: ─── OPTIONS ──► Backend ◄── 200 OK ──── Backend (Counter: successes = 0 → stays UP) Backend goes DOWN: ─── OPTIONS ──► Backend ◄── [timeout] ── Backend (Counter: failures = 1, 2, 3 → marked INACTIVE when threshold reached) Backend recovers: ─── OPTIONS ──► Backend ◄── 200 OK ──── Backend (Counter: successes = 1, 2, 3 → marked ACTIVE when threshold reached)
Backend UP: ─── OPTIONS ──► Backend ◄── 200 OK ──── Backend (Counter: successes = 0 → stays UP) Backend goes DOWN: ─── OPTIONS ──► Backend ◄── [timeout] ── Backend (Counter: failures = 1, 2, 3 → marked INACTIVE when threshold reached) Backend recovers: ─── OPTIONS ──► Backend ◄── 200 OK ──── Backend (Counter: successes = 1, 2, 3 → marked ACTIVE when threshold reached)
Backend UP: ─── OPTIONS ──► Backend ◄── 200 OK ──── Backend (Counter: successes = 0 → stays UP) Backend goes DOWN: ─── OPTIONS ──► Backend ◄── [timeout] ── Backend (Counter: failures = 1, 2, 3 → marked INACTIVE when threshold reached) Backend recovers: ─── OPTIONS ──► Backend ◄── 200 OK ──── Backend (Counter: successes = 1, 2, 3 → marked ACTIVE when threshold reached)
# Show all destinations and their -weight: 500;">status
kamcmd dispatcher.list # Output format:
# SET DEST FLAGS PRIORITY ATTRS
# 1 sip:10.0.0.10:5060 AP 0 weight=50
# 1 sip:10.0.0.11:5060 AP 0 weight=30
# 1 sip:10.0.0.12:5060 IP 0 weight=20 # Flags: A=Active, I=Inactive, P=Probing, D=Disabled, X=Deleted # Manually set destination state
kamcmd dispatcher.set_state ip 1 sip:10.0.0.12:5060 # Set to Inactive/Probing
kamcmd dispatcher.set_state ap 1 sip:10.0.0.12:5060 # Set to Active/Probing
# Show all destinations and their -weight: 500;">status
kamcmd dispatcher.list # Output format:
# SET DEST FLAGS PRIORITY ATTRS
# 1 sip:10.0.0.10:5060 AP 0 weight=50
# 1 sip:10.0.0.11:5060 AP 0 weight=30
# 1 sip:10.0.0.12:5060 IP 0 weight=20 # Flags: A=Active, I=Inactive, P=Probing, D=Disabled, X=Deleted # Manually set destination state
kamcmd dispatcher.set_state ip 1 sip:10.0.0.12:5060 # Set to Inactive/Probing
kamcmd dispatcher.set_state ap 1 sip:10.0.0.12:5060 # Set to Active/Probing
# Show all destinations and their -weight: 500;">status
kamcmd dispatcher.list # Output format:
# SET DEST FLAGS PRIORITY ATTRS
# 1 sip:10.0.0.10:5060 AP 0 weight=50
# 1 sip:10.0.0.11:5060 AP 0 weight=30
# 1 sip:10.0.0.12:5060 IP 0 weight=20 # Flags: A=Active, I=Inactive, P=Probing, D=Disabled, X=Deleted # Manually set destination state
kamcmd dispatcher.set_state ip 1 sip:10.0.0.12:5060 # Set to Inactive/Probing
kamcmd dispatcher.set_state ap 1 sip:10.0.0.12:5060 # Set to Active/Probing
#!KAMAILIO
#!define WITH_DISPATCHER listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
server_signature=no /* Modules */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "dispatcher.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "dialog.so" /* Module parameters */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio") modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600) modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) /* Dispatcher — load balancer */
modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "ds_ping_interval", 10)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3)
modparam("dispatcher", "ds_inactive_threshold", 3)
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_ping_from", "sip:kamailio@YOUR_DOMAIN")
modparam("dispatcher", "ds_ping_reply_codes", "class2;class3;class4") /* NAT */
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)") modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* Dialog — for call-load balancing */
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4) /* ===== Routing ===== */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) exit; /* NAT detection */ if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { rtpengine_delete(); } if (has_body("application/sdp")) { route(NATMANAGE); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Authenticate */ route(AUTH); /* REGISTER — save locally, don't forward to backends */ if (is_method("REGISTER")) { if (nat_uac_test(63)) { fix_nated_register(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE — dispatch to backend Asterisk servers */ if (is_method("INVITE")) { /* Track the dialog for call-load balancing */ setflag(4); dlg_manage(); /* NAT manage */ route(NATMANAGE); /* Dispatch to backend set 1 */ route(DISPATCH); exit; } /* Other requests — try location lookup first */ if (lookup("location")) { route(RELAY); exit; } sl_send_reply(404, "Not Found"); exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } if (is_method("INVITE|MESSAGE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[DISPATCH] { /* Round-robin (algorithm 0) across set 1 */ if (!ds_select_dst(1, 0)) { xlog("L_ERR", "DISPATCH: All backends down for $rU\n"); sl_send_reply(503, "Service Unavailable"); exit; } xlog("L_INFO", "DISPATCH: $rU → $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit;
} route[RELAY] { t_on_reply("MANAGE_REPLY"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
} onreply_route[MANAGE_REPLY] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE); }
} failure_route[DISPATCH_FAILURE] { if (t_is_canceled()) exit; if (t_check_status("5[0-9][0-9]") || t_check_status("408")) { xlog("L_WARN", "DISPATCH: $du failed ($T_reply_code), failover\n"); ds_mark_dst("ip"); if (ds_next_dst()) { xlog("L_INFO", "DISPATCH: Failover → $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit; } xlog("L_ERR", "DISPATCH: All backends exhausted\n"); t_reply(503, "All Backends Unavailable"); exit; }
}
#!KAMAILIO
#!define WITH_DISPATCHER listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
server_signature=no /* Modules */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "dispatcher.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "dialog.so" /* Module parameters */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio") modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600) modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) /* Dispatcher — load balancer */
modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "ds_ping_interval", 10)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3)
modparam("dispatcher", "ds_inactive_threshold", 3)
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_ping_from", "sip:kamailio@YOUR_DOMAIN")
modparam("dispatcher", "ds_ping_reply_codes", "class2;class3;class4") /* NAT */
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)") modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* Dialog — for call-load balancing */
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4) /* ===== Routing ===== */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) exit; /* NAT detection */ if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { rtpengine_delete(); } if (has_body("application/sdp")) { route(NATMANAGE); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Authenticate */ route(AUTH); /* REGISTER — save locally, don't forward to backends */ if (is_method("REGISTER")) { if (nat_uac_test(63)) { fix_nated_register(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE — dispatch to backend Asterisk servers */ if (is_method("INVITE")) { /* Track the dialog for call-load balancing */ setflag(4); dlg_manage(); /* NAT manage */ route(NATMANAGE); /* Dispatch to backend set 1 */ route(DISPATCH); exit; } /* Other requests — try location lookup first */ if (lookup("location")) { route(RELAY); exit; } sl_send_reply(404, "Not Found"); exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } if (is_method("INVITE|MESSAGE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[DISPATCH] { /* Round-robin (algorithm 0) across set 1 */ if (!ds_select_dst(1, 0)) { xlog("L_ERR", "DISPATCH: All backends down for $rU\n"); sl_send_reply(503, "Service Unavailable"); exit; } xlog("L_INFO", "DISPATCH: $rU → $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit;
} route[RELAY] { t_on_reply("MANAGE_REPLY"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
} onreply_route[MANAGE_REPLY] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE); }
} failure_route[DISPATCH_FAILURE] { if (t_is_canceled()) exit; if (t_check_status("5[0-9][0-9]") || t_check_status("408")) { xlog("L_WARN", "DISPATCH: $du failed ($T_reply_code), failover\n"); ds_mark_dst("ip"); if (ds_next_dst()) { xlog("L_INFO", "DISPATCH: Failover → $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit; } xlog("L_ERR", "DISPATCH: All backends exhausted\n"); t_reply(503, "All Backends Unavailable"); exit; }
}
#!KAMAILIO
#!define WITH_DISPATCHER listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
auto_aliases=no
server_signature=no /* Modules */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "dispatcher.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "dialog.so" /* Module parameters */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio") modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 600) modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) /* Dispatcher — load balancer */
modparam("dispatcher", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "ds_ping_interval", 10)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_threshold", 3)
modparam("dispatcher", "ds_inactive_threshold", 3)
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_ping_from", "sip:kamailio@YOUR_DOMAIN")
modparam("dispatcher", "ds_ping_reply_codes", "class2;class3;class4") /* NAT */
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)") modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* Dialog — for call-load balancing */
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4) /* ===== Routing ===== */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) exit; /* NAT detection */ if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } /* Record-Route */ if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog requests */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) { rtpengine_delete(); } if (has_body("application/sdp")) { route(NATMANAGE); } route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } /* CANCEL */ if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Authenticate */ route(AUTH); /* REGISTER — save locally, don't forward to backends */ if (is_method("REGISTER")) { if (nat_uac_test(63)) { fix_nated_register(); } if (!save("location")) { sl_send_reply(500, "Registration Failed"); } exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE — dispatch to backend Asterisk servers */ if (is_method("INVITE")) { /* Track the dialog for call-load balancing */ setflag(4); dlg_manage(); /* NAT manage */ route(NATMANAGE); /* Dispatch to backend set 1 */ route(DISPATCH); exit; } /* Other requests — try location lookup first */ if (lookup("location")) { route(RELAY); exit; } sl_send_reply(404, "Not Found"); exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } return; } if (is_method("INVITE|MESSAGE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } if ($au != $fU) { sl_send_reply(403, "Forbidden"); exit; } consume_credentials(); } return;
} route[DISPATCH] { /* Round-robin (algorithm 0) across set 1 */ if (!ds_select_dst(1, 0)) { xlog("L_ERR", "DISPATCH: All backends down for $rU\n"); sl_send_reply(503, "Service Unavailable"); exit; } xlog("L_INFO", "DISPATCH: $rU → $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit;
} route[RELAY] { t_on_reply("MANAGE_REPLY"); if (!t_relay()) { sl_send_reply(500, "Server Error"); } exit;
} route[NATMANAGE] { if (is_request()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } if (is_reply()) { if (has_body("application/sdp")) { rtpengine_manage("replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } return;
} onreply_route[MANAGE_REPLY] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE); }
} failure_route[DISPATCH_FAILURE] { if (t_is_canceled()) exit; if (t_check_status("5[0-9][0-9]") || t_check_status("408")) { xlog("L_WARN", "DISPATCH: $du failed ($T_reply_code), failover\n"); ds_mark_dst("ip"); if (ds_next_dst()) { xlog("L_INFO", "DISPATCH: Failover → $du\n"); t_on_failure("DISPATCH_FAILURE"); route(RELAY); exit; } xlog("L_ERR", "DISPATCH: All backends exhausted\n"); t_reply(503, "All Backends Unavailable"); exit; }
}
loadmodule "dialog.so" modparam("dialog", "db_mode", 1) /* 1 = write to DB on changes */
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4) /* Flag to -weight: 500;">enable dialog tracking */
modparam("dialog", "timeout_avp", "$avp(dlg_timeout)")
modparam("dialog", "default_timeout", 43200) /* 12-hour max call duration */ /* Dialog profiles — for per-user call limits */
modparam("dialog", "profiles_with_value", "caller;callee")
modparam("dialog", "profiles_no_value", "active_calls")
loadmodule "dialog.so" modparam("dialog", "db_mode", 1) /* 1 = write to DB on changes */
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4) /* Flag to -weight: 500;">enable dialog tracking */
modparam("dialog", "timeout_avp", "$avp(dlg_timeout)")
modparam("dialog", "default_timeout", 43200) /* 12-hour max call duration */ /* Dialog profiles — for per-user call limits */
modparam("dialog", "profiles_with_value", "caller;callee")
modparam("dialog", "profiles_no_value", "active_calls")
loadmodule "dialog.so" modparam("dialog", "db_mode", 1) /* 1 = write to DB on changes */
modparam("dialog", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("dialog", "dlg_flag", 4) /* Flag to -weight: 500;">enable dialog tracking */
modparam("dialog", "timeout_avp", "$avp(dlg_timeout)")
modparam("dialog", "default_timeout", 43200) /* 12-hour max call duration */ /* Dialog profiles — for per-user call limits */
modparam("dialog", "profiles_with_value", "caller;callee")
modparam("dialog", "profiles_no_value", "active_calls")
request_route { /* ... (sanity, NAT, in-dialog handling) ... */ if (is_method("INVITE") && !has_totag()) { /* New call — -weight: 500;">enable dialog tracking */ setflag(4); dlg_manage(); /* Set dialog timeout (optional) */ $avp(dlg_timeout) = 7200; /* 2-hour limit for this call */ /* Set dialog profile for the caller */ set_dlg_profile("caller", "$fU"); set_dlg_profile("callee", "$rU"); set_dlg_profile("active_calls"); /* ... route the call ... */ }
}
request_route { /* ... (sanity, NAT, in-dialog handling) ... */ if (is_method("INVITE") && !has_totag()) { /* New call — -weight: 500;">enable dialog tracking */ setflag(4); dlg_manage(); /* Set dialog timeout (optional) */ $avp(dlg_timeout) = 7200; /* 2-hour limit for this call */ /* Set dialog profile for the caller */ set_dlg_profile("caller", "$fU"); set_dlg_profile("callee", "$rU"); set_dlg_profile("active_calls"); /* ... route the call ... */ }
}
request_route { /* ... (sanity, NAT, in-dialog handling) ... */ if (is_method("INVITE") && !has_totag()) { /* New call — -weight: 500;">enable dialog tracking */ setflag(4); dlg_manage(); /* Set dialog timeout (optional) */ $avp(dlg_timeout) = 7200; /* 2-hour limit for this call */ /* Set dialog profile for the caller */ set_dlg_profile("caller", "$fU"); set_dlg_profile("callee", "$rU"); set_dlg_profile("active_calls"); /* ... route the call ... */ }
}
route[CHECK_CALL_LIMITS] { /* Max 5 simultaneous calls per caller */ if (get_profile_size("caller", "$fU", "$avp(caller_calls)")) { if ($avp(caller_calls) >= 5) { xlog("L_WARN", "Call limit reached for $fU ($avp(caller_calls) active)\n"); sl_send_reply(486, "Busy — Call Limit Reached"); exit; } } /* Max 2 simultaneous calls per callee */ if (get_profile_size("callee", "$rU", "$avp(callee_calls)")) { if ($avp(callee_calls) >= 2) { xlog("L_WARN", "Callee $rU busy ($avp(callee_calls) active)\n"); sl_send_reply(486, "Busy Here"); exit; } } return;
}
route[CHECK_CALL_LIMITS] { /* Max 5 simultaneous calls per caller */ if (get_profile_size("caller", "$fU", "$avp(caller_calls)")) { if ($avp(caller_calls) >= 5) { xlog("L_WARN", "Call limit reached for $fU ($avp(caller_calls) active)\n"); sl_send_reply(486, "Busy — Call Limit Reached"); exit; } } /* Max 2 simultaneous calls per callee */ if (get_profile_size("callee", "$rU", "$avp(callee_calls)")) { if ($avp(callee_calls) >= 2) { xlog("L_WARN", "Callee $rU busy ($avp(callee_calls) active)\n"); sl_send_reply(486, "Busy Here"); exit; } } return;
}
route[CHECK_CALL_LIMITS] { /* Max 5 simultaneous calls per caller */ if (get_profile_size("caller", "$fU", "$avp(caller_calls)")) { if ($avp(caller_calls) >= 5) { xlog("L_WARN", "Call limit reached for $fU ($avp(caller_calls) active)\n"); sl_send_reply(486, "Busy — Call Limit Reached"); exit; } } /* Max 2 simultaneous calls per callee */ if (get_profile_size("callee", "$rU", "$avp(callee_calls)")) { if ($avp(callee_calls) >= 2) { xlog("L_WARN", "Callee $rU busy ($avp(callee_calls) active)\n"); sl_send_reply(486, "Busy Here"); exit; } } return;
}
# View active calls
kamcmd dlg.list # Count active dialogs
kamcmd dlg.stats_active # Get dialog by Call-ID
kamcmd dlg.dlg_list_ctx # View profile sizes (active calls per user)
kamcmd dlg.profile_list caller
kamcmd dlg.profile_list active_calls # Terminate a dialog
kamcmd dlg.end_dlg <h_entry> <h_id>
# View active calls
kamcmd dlg.list # Count active dialogs
kamcmd dlg.stats_active # Get dialog by Call-ID
kamcmd dlg.dlg_list_ctx # View profile sizes (active calls per user)
kamcmd dlg.profile_list caller
kamcmd dlg.profile_list active_calls # Terminate a dialog
kamcmd dlg.end_dlg <h_entry> <h_id>
# View active calls
kamcmd dlg.list # Count active dialogs
kamcmd dlg.stats_active # Get dialog by Call-ID
kamcmd dlg.dlg_list_ctx # View profile sizes (active calls per user)
kamcmd dlg.profile_list caller
kamcmd dlg.profile_list active_calls # Terminate a dialog
kamcmd dlg.end_dlg <h_entry> <h_id>
loadmodule "acc.so"
loadmodule "acc_db.so" /* Database CDR backend (included in kamailio-mysql-modules) */ /* Accounting to database */
modparam("acc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("acc", "db_flag", 1) /* Flag to trigger DB accounting */
modparam("acc", "db_missed_flag", 2) /* Flag for missed call logging */
modparam("acc", "failed_transaction_flag", 3) /* Flag for failed transactions */ /* What to log */
modparam("acc", "log_level", 1)
modparam("acc", "log_flag", 1) /* CDR generation — log duration, -weight: 500;">start/end time */
modparam("acc", "cdr_enable", 1)
modparam("acc", "cdr_start_on_confirmed", 1) /* Start CDR timer on 200 OK */
modparam("acc", "cdr_log_enable", 1) /* Extra fields to log */
modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_user=$rU;dst_domain=$rd;" "callid=$ci;ua=$ua") /* CDR extra fields */
modparam("acc", "cdr_extra", "src_user=$fU;dst_user=$rU;src_ip=$si;callid=$ci")
loadmodule "acc.so"
loadmodule "acc_db.so" /* Database CDR backend (included in kamailio-mysql-modules) */ /* Accounting to database */
modparam("acc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("acc", "db_flag", 1) /* Flag to trigger DB accounting */
modparam("acc", "db_missed_flag", 2) /* Flag for missed call logging */
modparam("acc", "failed_transaction_flag", 3) /* Flag for failed transactions */ /* What to log */
modparam("acc", "log_level", 1)
modparam("acc", "log_flag", 1) /* CDR generation — log duration, -weight: 500;">start/end time */
modparam("acc", "cdr_enable", 1)
modparam("acc", "cdr_start_on_confirmed", 1) /* Start CDR timer on 200 OK */
modparam("acc", "cdr_log_enable", 1) /* Extra fields to log */
modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_user=$rU;dst_domain=$rd;" "callid=$ci;ua=$ua") /* CDR extra fields */
modparam("acc", "cdr_extra", "src_user=$fU;dst_user=$rU;src_ip=$si;callid=$ci")
loadmodule "acc.so"
loadmodule "acc_db.so" /* Database CDR backend (included in kamailio-mysql-modules) */ /* Accounting to database */
modparam("acc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("acc", "db_flag", 1) /* Flag to trigger DB accounting */
modparam("acc", "db_missed_flag", 2) /* Flag for missed call logging */
modparam("acc", "failed_transaction_flag", 3) /* Flag for failed transactions */ /* What to log */
modparam("acc", "log_level", 1)
modparam("acc", "log_flag", 1) /* CDR generation — log duration, -weight: 500;">start/end time */
modparam("acc", "cdr_enable", 1)
modparam("acc", "cdr_start_on_confirmed", 1) /* Start CDR timer on 200 OK */
modparam("acc", "cdr_log_enable", 1) /* Extra fields to log */
modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_user=$rU;dst_domain=$rd;" "callid=$ci;ua=$ua") /* CDR extra fields */
modparam("acc", "cdr_extra", "src_user=$fU;dst_user=$rU;src_ip=$si;callid=$ci")
request_route { /* ... */ if (is_method("INVITE") && !has_totag()) { setflag(1); /* Enable DB accounting */ setflag(2); /* Log missed calls */ setflag(3); /* Log failed transactions */ setflag(4); /* Enable dialog tracking (needed for CDR duration) */ dlg_manage(); /* ... route the call ... */ }
}
request_route { /* ... */ if (is_method("INVITE") && !has_totag()) { setflag(1); /* Enable DB accounting */ setflag(2); /* Log missed calls */ setflag(3); /* Log failed transactions */ setflag(4); /* Enable dialog tracking (needed for CDR duration) */ dlg_manage(); /* ... route the call ... */ }
}
request_route { /* ... */ if (is_method("INVITE") && !has_totag()) { setflag(1); /* Enable DB accounting */ setflag(2); /* Log missed calls */ setflag(3); /* Log failed transactions */ setflag(4); /* Enable dialog tracking (needed for CDR duration) */ dlg_manage(); /* ... route the call ... */ }
}
-- View CDRs
SELECT id, method, from_tag, to_tag, callid, sip_code, sip_reason, src_user, dst_user, src_ip, time
FROM acc
ORDER BY time DESC
LIMIT 20; -- CDR table (if cdr_enable=1)
SELECT id, start_time, end_time, duration, src_user, dst_user, src_ip, callid, sip_code, sip_reason
FROM cdrs
ORDER BY start_time DESC
LIMIT 20;
-- View CDRs
SELECT id, method, from_tag, to_tag, callid, sip_code, sip_reason, src_user, dst_user, src_ip, time
FROM acc
ORDER BY time DESC
LIMIT 20; -- CDR table (if cdr_enable=1)
SELECT id, start_time, end_time, duration, src_user, dst_user, src_ip, callid, sip_code, sip_reason
FROM cdrs
ORDER BY start_time DESC
LIMIT 20;
-- View CDRs
SELECT id, method, from_tag, to_tag, callid, sip_code, sip_reason, src_user, dst_user, src_ip, time
FROM acc
ORDER BY time DESC
LIMIT 20; -- CDR table (if cdr_enable=1)
SELECT id, start_time, end_time, duration, src_user, dst_user, src_ip, callid, sip_code, sip_reason
FROM cdrs
ORDER BY start_time DESC
LIMIT 20;
/* In modparam section */
modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_user=$rU;dst_domain=$rd;" "callid=$ci;ua=$ua;" "trunk=$avp(trunk_name);" "campaign=$avp(campaign)") /* In routing logic — set custom fields before acc triggers */
if (is_method("INVITE") && !has_totag()) { $avp(trunk_name) = "protech"; $avp(campaign) = "uk_sales"; setflag(1); /* ... */
}
/* In modparam section */
modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_user=$rU;dst_domain=$rd;" "callid=$ci;ua=$ua;" "trunk=$avp(trunk_name);" "campaign=$avp(campaign)") /* In routing logic — set custom fields before acc triggers */
if (is_method("INVITE") && !has_totag()) { $avp(trunk_name) = "protech"; $avp(campaign) = "uk_sales"; setflag(1); /* ... */
}
/* In modparam section */
modparam("acc", "db_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_user=$rU;dst_domain=$rd;" "callid=$ci;ua=$ua;" "trunk=$avp(trunk_name);" "campaign=$avp(campaign)") /* In routing logic — set custom fields before acc triggers */
if (is_method("INVITE") && !has_totag()) { $avp(trunk_name) = "protech"; $avp(campaign) = "uk_sales"; setflag(1); /* ... */
}
ALTER TABLE acc ADD COLUMN trunk VARCHAR(64) DEFAULT '';
ALTER TABLE acc ADD COLUMN campaign VARCHAR(64) DEFAULT '';
ALTER TABLE acc ADD COLUMN trunk VARCHAR(64) DEFAULT '';
ALTER TABLE acc ADD COLUMN campaign VARCHAR(64) DEFAULT '';
ALTER TABLE acc ADD COLUMN trunk VARCHAR(64) DEFAULT '';
ALTER TABLE acc ADD COLUMN campaign VARCHAR(64) DEFAULT '';
┌─────────┐ WSS/SRTP ┌──────────┐ SIP/RTP ┌──────────┐
│ Browser │ ◄────────────► │ Kamailio │ ◄──────────► │ Asterisk │
│ SIP.js │ │+RTPEngine│ │ (PBX) │
└─────────┘ └──────────┘ └──────────┘ Browser → Kamailio: SIP over WSS (port 8443), DTLS-SRTP media
Kamailio → Asterisk: SIP over UDP (port 5060), plain RTP media
RTPEngine: Converts DTLS-SRTP ↔ plain RTP
┌─────────┐ WSS/SRTP ┌──────────┐ SIP/RTP ┌──────────┐
│ Browser │ ◄────────────► │ Kamailio │ ◄──────────► │ Asterisk │
│ SIP.js │ │+RTPEngine│ │ (PBX) │
└─────────┘ └──────────┘ └──────────┘ Browser → Kamailio: SIP over WSS (port 8443), DTLS-SRTP media
Kamailio → Asterisk: SIP over UDP (port 5060), plain RTP media
RTPEngine: Converts DTLS-SRTP ↔ plain RTP
┌─────────┐ WSS/SRTP ┌──────────┐ SIP/RTP ┌──────────┐
│ Browser │ ◄────────────► │ Kamailio │ ◄──────────► │ Asterisk │
│ SIP.js │ │+RTPEngine│ │ (PBX) │
└─────────┘ └──────────┘ └──────────┘ Browser → Kamailio: SIP over WSS (port 8443), DTLS-SRTP media
Kamailio → Asterisk: SIP over UDP (port 5060), plain RTP media
RTPEngine: Converts DTLS-SRTP ↔ plain RTP
/* Module loading */
loadmodule "websocket.so"
loadmodule "xhttp.so" /* HTTP handling for WebSocket -weight: 500;">upgrade */
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "tls.so" /* Listen on WSS (WebSocket Secure) */
listen=tls:YOUR_SERVER_IP:8443 /* WebSocket parameters */
modparam("websocket", "keepalive_mechanism", 1) /* 1 = PING/PONG */
modparam("websocket", "keepalive_timeout", 30) /* Seconds */
modparam("websocket", "keepalive_processes", 1) /* TLS for WSS */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "tls_method", "TLSv1.2+")
/* Module loading */
loadmodule "websocket.so"
loadmodule "xhttp.so" /* HTTP handling for WebSocket -weight: 500;">upgrade */
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "tls.so" /* Listen on WSS (WebSocket Secure) */
listen=tls:YOUR_SERVER_IP:8443 /* WebSocket parameters */
modparam("websocket", "keepalive_mechanism", 1) /* 1 = PING/PONG */
modparam("websocket", "keepalive_timeout", 30) /* Seconds */
modparam("websocket", "keepalive_processes", 1) /* TLS for WSS */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "tls_method", "TLSv1.2+")
/* Module loading */
loadmodule "websocket.so"
loadmodule "xhttp.so" /* HTTP handling for WebSocket -weight: 500;">upgrade */
loadmodule "nathelper.so"
loadmodule "rtpengine.so"
loadmodule "tls.so" /* Listen on WSS (WebSocket Secure) */
listen=tls:YOUR_SERVER_IP:8443 /* WebSocket parameters */
modparam("websocket", "keepalive_mechanism", 1) /* 1 = PING/PONG */
modparam("websocket", "keepalive_timeout", 30) /* Seconds */
modparam("websocket", "keepalive_processes", 1) /* TLS for WSS */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "tls_method", "TLSv1.2+")
/* Handle HTTP requests (WebSocket -weight: 500;">upgrade) */
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($hdr(Upgrade) =~ "websocket" && $hdr(Connection) =~ "Upgrade" && $rm == "GET") { /* Validate Origin header (optional but recommended) */ # if ($hdr(Origin) != "https://YOUR_DOMAIN") { # xhttp_reply(403, "Forbidden", "text/plain", "Bad Origin"); # exit; # } /* Accept WebSocket -weight: 500;">upgrade */ if (ws_handle_handshake()) { exit; } } /* Non-WebSocket HTTP request — reject */ xhttp_reply(404, "Not Found", "text/plain", "WebSocket Only"); exit;
}
/* Handle HTTP requests (WebSocket -weight: 500;">upgrade) */
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($hdr(Upgrade) =~ "websocket" && $hdr(Connection) =~ "Upgrade" && $rm == "GET") { /* Validate Origin header (optional but recommended) */ # if ($hdr(Origin) != "https://YOUR_DOMAIN") { # xhttp_reply(403, "Forbidden", "text/plain", "Bad Origin"); # exit; # } /* Accept WebSocket -weight: 500;">upgrade */ if (ws_handle_handshake()) { exit; } } /* Non-WebSocket HTTP request — reject */ xhttp_reply(404, "Not Found", "text/plain", "WebSocket Only"); exit;
}
/* Handle HTTP requests (WebSocket -weight: 500;">upgrade) */
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($hdr(Upgrade) =~ "websocket" && $hdr(Connection) =~ "Upgrade" && $rm == "GET") { /* Validate Origin header (optional but recommended) */ # if ($hdr(Origin) != "https://YOUR_DOMAIN") { # xhttp_reply(403, "Forbidden", "text/plain", "Bad Origin"); # exit; # } /* Accept WebSocket -weight: 500;">upgrade */ if (ws_handle_handshake()) { exit; } } /* Non-WebSocket HTTP request — reject */ xhttp_reply(404, "Not Found", "text/plain", "WebSocket Only"); exit;
}
route[NATMANAGE_WEBRTC] { if (is_request()) { if (has_body("application/sdp")) { if ($proto == "ws" || $proto == "wss") { /* WebRTC → SIP: convert DTLS-SRTP to plain RTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove " "RTP/AVP SDES-off"); } else if (ds_is_from_list(1)) { /* Reply from backend (SIP) → WebRTC: convert RTP to DTLS-SRTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force " "RTP/SAVPF SDES-off " "DTLS=passive"); } else { /* SIP to SIP — standard relay */ rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if ($proto == "ws" || $proto == "wss") { /* Reply going to WebRTC client */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force " "RTP/SAVPF SDES-off " "DTLS=passive"); } else { /* Reply going to SIP endpoint */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove " "RTP/AVP SDES-off"); } } } return;
}
route[NATMANAGE_WEBRTC] { if (is_request()) { if (has_body("application/sdp")) { if ($proto == "ws" || $proto == "wss") { /* WebRTC → SIP: convert DTLS-SRTP to plain RTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove " "RTP/AVP SDES-off"); } else if (ds_is_from_list(1)) { /* Reply from backend (SIP) → WebRTC: convert RTP to DTLS-SRTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force " "RTP/SAVPF SDES-off " "DTLS=passive"); } else { /* SIP to SIP — standard relay */ rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if ($proto == "ws" || $proto == "wss") { /* Reply going to WebRTC client */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force " "RTP/SAVPF SDES-off " "DTLS=passive"); } else { /* Reply going to SIP endpoint */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove " "RTP/AVP SDES-off"); } } } return;
}
route[NATMANAGE_WEBRTC] { if (is_request()) { if (has_body("application/sdp")) { if ($proto == "ws" || $proto == "wss") { /* WebRTC → SIP: convert DTLS-SRTP to plain RTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove " "RTP/AVP SDES-off"); } else if (ds_is_from_list(1)) { /* Reply from backend (SIP) → WebRTC: convert RTP to DTLS-SRTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force " "RTP/SAVPF SDES-off " "DTLS=passive"); } else { /* SIP to SIP — standard relay */ rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if ($proto == "ws" || $proto == "wss") { /* Reply going to WebRTC client */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force " "RTP/SAVPF SDES-off " "DTLS=passive"); } else { /* Reply going to SIP endpoint */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove " "RTP/AVP SDES-off"); } } } return;
}
# /etc/nginx/sites-available/kamailio-wss
upstream kamailio_wss { server 127.0.0.1:8080; # Kamailio WS (non-TLS) listener
} server { listen 443 ssl; server_name YOUR_DOMAIN; ssl_certificate /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; location /ws { proxy_pass http://kamailio_wss; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "-weight: 500;">upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 3600s; proxy_send_timeout 3600s; }
}
# /etc/nginx/sites-available/kamailio-wss
upstream kamailio_wss { server 127.0.0.1:8080; # Kamailio WS (non-TLS) listener
} server { listen 443 ssl; server_name YOUR_DOMAIN; ssl_certificate /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; location /ws { proxy_pass http://kamailio_wss; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "-weight: 500;">upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 3600s; proxy_send_timeout 3600s; }
}
# /etc/nginx/sites-available/kamailio-wss
upstream kamailio_wss { server 127.0.0.1:8080; # Kamailio WS (non-TLS) listener
} server { listen 443 ssl; server_name YOUR_DOMAIN; ssl_certificate /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; location /ws { proxy_pass http://kamailio_wss; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "-weight: 500;">upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 3600s; proxy_send_timeout 3600s; }
}
/* Plain WebSocket behind Nginx */
listen=tcp:127.0.0.1:8080
/* Plain WebSocket behind Nginx */
listen=tcp:127.0.0.1:8080
/* Plain WebSocket behind Nginx */
listen=tcp:127.0.0.1:8080
<!DOCTYPE html>
<html>
<head> <title>WebRTC Phone</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jssip/3.10.0/jssip.min.js"></script>
</head>
<body> <h2>WebRTC SIP Phone</h2> <div> <input type="text" id="target" placeholder="Extension (e.g., 1002)" /> <button onclick="makeCall()">Call</button> <button onclick="hangUp()">Hang Up</button> </div> <div id="-weight: 500;">status">Disconnected</div> <audio id="remoteAudio" autoplay></audio> <script>
// Configuration
const socket = new JsSIP.WebSocketInterface('wss://YOUR_DOMAIN:8443');
const configuration = { sockets: [socket], uri: 'sip:1001@YOUR_DOMAIN', password: 'MySecurePass123', display_name: 'Web User 1001', register: true, session_timers: false
}; // Create User Agent
const ua = new JsSIP.UA(configuration);
let currentSession = null; // Event handlers
ua.on('connected', () => { document.getElementById('-weight: 500;">status').textContent = 'Connected';
}); ua.on('registered', () => { document.getElementById('-weight: 500;">status').textContent = 'Registered';
}); ua.on('registrationFailed', (e) => { document.getElementById('-weight: 500;">status').textContent = 'Registration failed: ' + e.cause;
}); ua.on('newRTCSession', (data) => { const session = data.session; currentSession = session; if (session.direction === 'incoming') { document.getElementById('-weight: 500;">status').textContent = 'Incoming call from ' + session.remote_identity.uri; // Auto-answer (or show accept/reject buttons) session.answer({ mediaConstraints: { audio: true, video: false } }); } session.on('confirmed', () => { document.getElementById('-weight: 500;">status').textContent = 'Call active'; }); session.on('ended', () => { document.getElementById('-weight: 500;">status').textContent = 'Call ended'; currentSession = null; }); session.on('failed', (e) => { document.getElementById('-weight: 500;">status').textContent = 'Call failed: ' + e.cause; currentSession = null; }); session.on('peerconnection', (e) => { e.peerconnection.ontrack = (event) => { document.getElementById('remoteAudio').srcObject = event.streams[0]; }; });
}); // Start the User Agent
ua.-weight: 500;">start(); // Make a call
function makeCall() { const target = document.getElementById('target').value; if (!target) return; const options = { mediaConstraints: { audio: true, video: false }, pcConfig: { iceServers: [ { urls: 'stun:stun.l.google.com:19302' } ] } }; currentSession = ua.call('sip:' + target + '@YOUR_DOMAIN', options);
} // Hang up
function hangUp() { if (currentSession) { currentSession.terminate(); }
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head> <title>WebRTC Phone</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jssip/3.10.0/jssip.min.js"></script>
</head>
<body> <h2>WebRTC SIP Phone</h2> <div> <input type="text" id="target" placeholder="Extension (e.g., 1002)" /> <button onclick="makeCall()">Call</button> <button onclick="hangUp()">Hang Up</button> </div> <div id="-weight: 500;">status">Disconnected</div> <audio id="remoteAudio" autoplay></audio> <script>
// Configuration
const socket = new JsSIP.WebSocketInterface('wss://YOUR_DOMAIN:8443');
const configuration = { sockets: [socket], uri: 'sip:1001@YOUR_DOMAIN', password: 'MySecurePass123', display_name: 'Web User 1001', register: true, session_timers: false
}; // Create User Agent
const ua = new JsSIP.UA(configuration);
let currentSession = null; // Event handlers
ua.on('connected', () => { document.getElementById('-weight: 500;">status').textContent = 'Connected';
}); ua.on('registered', () => { document.getElementById('-weight: 500;">status').textContent = 'Registered';
}); ua.on('registrationFailed', (e) => { document.getElementById('-weight: 500;">status').textContent = 'Registration failed: ' + e.cause;
}); ua.on('newRTCSession', (data) => { const session = data.session; currentSession = session; if (session.direction === 'incoming') { document.getElementById('-weight: 500;">status').textContent = 'Incoming call from ' + session.remote_identity.uri; // Auto-answer (or show accept/reject buttons) session.answer({ mediaConstraints: { audio: true, video: false } }); } session.on('confirmed', () => { document.getElementById('-weight: 500;">status').textContent = 'Call active'; }); session.on('ended', () => { document.getElementById('-weight: 500;">status').textContent = 'Call ended'; currentSession = null; }); session.on('failed', (e) => { document.getElementById('-weight: 500;">status').textContent = 'Call failed: ' + e.cause; currentSession = null; }); session.on('peerconnection', (e) => { e.peerconnection.ontrack = (event) => { document.getElementById('remoteAudio').srcObject = event.streams[0]; }; });
}); // Start the User Agent
ua.-weight: 500;">start(); // Make a call
function makeCall() { const target = document.getElementById('target').value; if (!target) return; const options = { mediaConstraints: { audio: true, video: false }, pcConfig: { iceServers: [ { urls: 'stun:stun.l.google.com:19302' } ] } }; currentSession = ua.call('sip:' + target + '@YOUR_DOMAIN', options);
} // Hang up
function hangUp() { if (currentSession) { currentSession.terminate(); }
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head> <title>WebRTC Phone</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/jssip/3.10.0/jssip.min.js"></script>
</head>
<body> <h2>WebRTC SIP Phone</h2> <div> <input type="text" id="target" placeholder="Extension (e.g., 1002)" /> <button onclick="makeCall()">Call</button> <button onclick="hangUp()">Hang Up</button> </div> <div id="-weight: 500;">status">Disconnected</div> <audio id="remoteAudio" autoplay></audio> <script>
// Configuration
const socket = new JsSIP.WebSocketInterface('wss://YOUR_DOMAIN:8443');
const configuration = { sockets: [socket], uri: 'sip:1001@YOUR_DOMAIN', password: 'MySecurePass123', display_name: 'Web User 1001', register: true, session_timers: false
}; // Create User Agent
const ua = new JsSIP.UA(configuration);
let currentSession = null; // Event handlers
ua.on('connected', () => { document.getElementById('-weight: 500;">status').textContent = 'Connected';
}); ua.on('registered', () => { document.getElementById('-weight: 500;">status').textContent = 'Registered';
}); ua.on('registrationFailed', (e) => { document.getElementById('-weight: 500;">status').textContent = 'Registration failed: ' + e.cause;
}); ua.on('newRTCSession', (data) => { const session = data.session; currentSession = session; if (session.direction === 'incoming') { document.getElementById('-weight: 500;">status').textContent = 'Incoming call from ' + session.remote_identity.uri; // Auto-answer (or show accept/reject buttons) session.answer({ mediaConstraints: { audio: true, video: false } }); } session.on('confirmed', () => { document.getElementById('-weight: 500;">status').textContent = 'Call active'; }); session.on('ended', () => { document.getElementById('-weight: 500;">status').textContent = 'Call ended'; currentSession = null; }); session.on('failed', (e) => { document.getElementById('-weight: 500;">status').textContent = 'Call failed: ' + e.cause; currentSession = null; }); session.on('peerconnection', (e) => { e.peerconnection.ontrack = (event) => { document.getElementById('remoteAudio').srcObject = event.streams[0]; }; });
}); // Start the User Agent
ua.-weight: 500;">start(); // Make a call
function makeCall() { const target = document.getElementById('target').value; if (!target) return; const options = { mediaConstraints: { audio: true, video: false }, pcConfig: { iceServers: [ { urls: 'stun:stun.l.google.com:19302' } ] } }; currentSession = ua.call('sip:' + target + '@YOUR_DOMAIN', options);
} // Hang up
function hangUp() { if (currentSession) { currentSession.terminate(); }
}
</script>
</body>
</html>
#!KAMAILIO listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
listen=tls:YOUR_SERVER_IP:5061
listen=tls:YOUR_SERVER_IP:8443 debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
tcp_children=8
auto_aliases=no
server_signature=no
force_rport=yes /* Modules */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "xhttp.so"
loadmodule "websocket.so"
loadmodule "tls.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so" /* TLS */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "tls_method", "TLSv1.2+") /* WebSocket */
modparam("websocket", "keepalive_mechanism", 1)
modparam("websocket", "keepalive_timeout", 30) /* Transaction */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) /* User location */
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 300) /* Auth */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) /* NAT + RTPEngine */
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* WebSocket -weight: 500;">upgrade handler */
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($hdr(Upgrade) =~ "websocket" && $hdr(Connection) =~ "Upgrade" && $rm == "GET") { if (ws_handle_handshake()) exit; } xhttp_reply(404, "Not Found", "text/plain", "WebSocket Only"); exit;
} /* Main routing */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) exit; /* NAT */ if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } /* Tag WebSocket requests */ if ($proto == "ws" || $proto == "wss") { setflag(6); /* Flag 6 = WebRTC client */ } if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) rtpengine_delete(); if (has_body("application/sdp")) route(NATMANAGE_WS); route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Auth */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { if (nat_uac_test(63)) fix_nated_register(); if (!save("location")) sl_send_reply(500, "Failed"); exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } route(NATMANAGE_WS); route(RELAY); exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } return; } if (is_method("INVITE|MESSAGE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } consume_credentials(); } return;
} route[RELAY] { t_on_reply("MANAGE_REPLY_WS"); t_on_failure("MANAGE_FAILURE"); if (!t_relay()) sl_send_reply(500, "Relay Error"); exit;
} route[NATMANAGE_WS] { if (is_request()) { if (has_body("application/sdp")) { if (isflagset(6)) { /* FROM WebRTC → TO SIP: DTLS-SRTP → plain RTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove RTP/AVP SDES-off"); } else { /* FROM SIP → TO WebRTC or SIP */ rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if (isflagset(6)) { /* Reply TO WebRTC client: plain RTP → DTLS-SRTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force RTP/SAVPF SDES-off DTLS=passive"); } else { rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } return;
} onreply_route[MANAGE_REPLY_WS] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE_WS); }
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n");
}
#!KAMAILIO listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
listen=tls:YOUR_SERVER_IP:5061
listen=tls:YOUR_SERVER_IP:8443 debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
tcp_children=8
auto_aliases=no
server_signature=no
force_rport=yes /* Modules */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "xhttp.so"
loadmodule "websocket.so"
loadmodule "tls.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so" /* TLS */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "tls_method", "TLSv1.2+") /* WebSocket */
modparam("websocket", "keepalive_mechanism", 1)
modparam("websocket", "keepalive_timeout", 30) /* Transaction */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) /* User location */
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 300) /* Auth */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) /* NAT + RTPEngine */
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* WebSocket -weight: 500;">upgrade handler */
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($hdr(Upgrade) =~ "websocket" && $hdr(Connection) =~ "Upgrade" && $rm == "GET") { if (ws_handle_handshake()) exit; } xhttp_reply(404, "Not Found", "text/plain", "WebSocket Only"); exit;
} /* Main routing */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) exit; /* NAT */ if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } /* Tag WebSocket requests */ if ($proto == "ws" || $proto == "wss") { setflag(6); /* Flag 6 = WebRTC client */ } if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) rtpengine_delete(); if (has_body("application/sdp")) route(NATMANAGE_WS); route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Auth */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { if (nat_uac_test(63)) fix_nated_register(); if (!save("location")) sl_send_reply(500, "Failed"); exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } route(NATMANAGE_WS); route(RELAY); exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } return; } if (is_method("INVITE|MESSAGE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } consume_credentials(); } return;
} route[RELAY] { t_on_reply("MANAGE_REPLY_WS"); t_on_failure("MANAGE_FAILURE"); if (!t_relay()) sl_send_reply(500, "Relay Error"); exit;
} route[NATMANAGE_WS] { if (is_request()) { if (has_body("application/sdp")) { if (isflagset(6)) { /* FROM WebRTC → TO SIP: DTLS-SRTP → plain RTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove RTP/AVP SDES-off"); } else { /* FROM SIP → TO WebRTC or SIP */ rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if (isflagset(6)) { /* Reply TO WebRTC client: plain RTP → DTLS-SRTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force RTP/SAVPF SDES-off DTLS=passive"); } else { rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } return;
} onreply_route[MANAGE_REPLY_WS] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE_WS); }
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n");
}
#!KAMAILIO listen=udp:YOUR_SERVER_IP:5060
listen=tcp:YOUR_SERVER_IP:5060
listen=tls:YOUR_SERVER_IP:5061
listen=tls:YOUR_SERVER_IP:8443 debug=2
log_stderror=no
log_facility=LOG_LOCAL0
children=8
tcp_children=8
auto_aliases=no
server_signature=no
force_rport=yes /* Modules */
loadmodule "kex.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "pv.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "xhttp.so"
loadmodule "websocket.so"
loadmodule "tls.so"
loadmodule "db_mysql.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "auth.so"
loadmodule "auth_db.so"
loadmodule "nathelper.so"
loadmodule "rtpengine.so" /* TLS */
modparam("tls", "private_key", "/etc/kamailio/certs/privkey.pem")
modparam("tls", "certificate", "/etc/kamailio/certs/fullchain.pem")
modparam("tls", "tls_method", "TLSv1.2+") /* WebSocket */
modparam("websocket", "keepalive_mechanism", 1)
modparam("websocket", "keepalive_timeout", 30) /* Transaction */
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1) /* User location */
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("registrar", "max_expires", 3600)
modparam("registrar", "default_expires", 300) /* Auth */
modparam("auth_db", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 0) /* NAT + RTPEngine */
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping", 1)
modparam("nathelper", "received_avp", "$avp(RECEIVED)")
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223") /* WebSocket -weight: 500;">upgrade handler */
event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($hdr(Upgrade) =~ "websocket" && $hdr(Connection) =~ "Upgrade" && $rm == "GET") { if (ws_handle_handshake()) exit; } xhttp_reply(404, "Not Found", "text/plain", "WebSocket Only"); exit;
} /* Main routing */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } if (!sanity_check("17895", "7")) exit; /* NAT */ if (nat_uac_test(63)) { setflag(5); fix_nated_contact(); force_rport(); } /* Tag WebSocket requests */ if ($proto == "ws" || $proto == "wss") { setflag(6); /* Flag 6 = WebRTC client */ } if (is_method("INVITE|SUBSCRIBE")) { record_route(); } /* In-dialog */ if (has_totag()) { if (loose_route()) { if (is_method("BYE")) rtpengine_delete(); if (has_body("application/sdp")) route(NATMANAGE_WS); route(RELAY); exit; } if (is_method("ACK") && t_check_trans()) exit; sl_send_reply(404, "Not Found"); exit; } if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; } t_check_trans(); /* Auth */ route(AUTH); /* REGISTER */ if (is_method("REGISTER")) { if (nat_uac_test(63)) fix_nated_register(); if (!save("location")) sl_send_reply(500, "Failed"); exit; } /* OPTIONS */ if (is_method("OPTIONS") && ($rU == $null || $rU == "")) { sl_send_reply(200, "OK"); exit; } /* INVITE */ if (!lookup("location")) { sl_send_reply(404, "User Not Found"); exit; } route(NATMANAGE_WS); route(RELAY); exit;
} route[AUTH] { if (is_method("REGISTER")) { if (!www_authorize("YOUR_DOMAIN", "subscriber")) { www_challenge("YOUR_DOMAIN", 1); exit; } return; } if (is_method("INVITE|MESSAGE")) { if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; } consume_credentials(); } return;
} route[RELAY] { t_on_reply("MANAGE_REPLY_WS"); t_on_failure("MANAGE_FAILURE"); if (!t_relay()) sl_send_reply(500, "Relay Error"); exit;
} route[NATMANAGE_WS] { if (is_request()) { if (has_body("application/sdp")) { if (isflagset(6)) { /* FROM WebRTC → TO SIP: DTLS-SRTP → plain RTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-demux ICE=-weight: 500;">remove RTP/AVP SDES-off"); } else { /* FROM SIP → TO WebRTC or SIP */ rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } if (is_reply()) { if (has_body("application/sdp")) { if (isflagset(6)) { /* Reply TO WebRTC client: plain RTP → DTLS-SRTP */ rtpengine_manage( "replace-origin replace-session-connection " "rtcp-mux-offer ICE=force RTP/SAVPF SDES-off DTLS=passive"); } else { rtpengine_manage( "replace-origin replace-session-connection ICE=-weight: 500;">remove"); } } } return;
} onreply_route[MANAGE_REPLY_WS] { if (-weight: 500;">status =~ "[12][0-9][0-9]") { route(NATMANAGE_WS); }
} failure_route[MANAGE_FAILURE] { if (t_is_canceled()) exit; xlog("L_WARN", "Call to $rU failed: $T_reply_code\n");
}
Internet DMZ LAN
───────────────────────────────────────────────────────────────── ┌──────────┐ ┌──────────┐
SIP Phones ────────────►│ Kamailio │──────────────►│ Asterisk │
SIP Trunks ────────────►│ (SBC) │ │ (PBX) │
WebRTC ────────────►│ │──────────────►│ Asterisk │ │+RTPEngine│ │ (PBX) │ └──────────┘ └──────────┘ Port 5060/5061 │ Port 5060 Port 8443 (WSS) │ (Internal) Port 10000-20000 (RTP via RTPEngine)
Internet DMZ LAN
───────────────────────────────────────────────────────────────── ┌──────────┐ ┌──────────┐
SIP Phones ────────────►│ Kamailio │──────────────►│ Asterisk │
SIP Trunks ────────────►│ (SBC) │ │ (PBX) │
WebRTC ────────────►│ │──────────────►│ Asterisk │ │+RTPEngine│ │ (PBX) │ └──────────┘ └──────────┘ Port 5060/5061 │ Port 5060 Port 8443 (WSS) │ (Internal) Port 10000-20000 (RTP via RTPEngine)
Internet DMZ LAN
───────────────────────────────────────────────────────────────── ┌──────────┐ ┌──────────┐
SIP Phones ────────────►│ Kamailio │──────────────►│ Asterisk │
SIP Trunks ────────────►│ (SBC) │ │ (PBX) │
WebRTC ────────────►│ │──────────────►│ Asterisk │ │+RTPEngine│ │ (PBX) │ └──────────┘ └──────────┘ Port 5060/5061 │ Port 5060 Port 8443 (WSS) │ (Internal) Port 10000-20000 (RTP via RTPEngine)
; /etc/asterisk/pjsip.conf (Asterisk 16+) [transport-udp]
type=transport
protocol=udp
bind=0.0.0.0 ; Trust Kamailio as a SIP peer
[kamailio]
type=identify
endpoint=kamailio
match=YOUR_SERVER_IP [kamailio]
type=endpoint
context=from-kamailio
disallow=all
allow=alaw
allow=ulaw
allow=opus
direct_media=no
trust_id_inbound=yes
trust_id_outbound=yes
send_pai=yes [kamailio]
type=aor
max_contacts=1
; /etc/asterisk/pjsip.conf (Asterisk 16+) [transport-udp]
type=transport
protocol=udp
bind=0.0.0.0 ; Trust Kamailio as a SIP peer
[kamailio]
type=identify
endpoint=kamailio
match=YOUR_SERVER_IP [kamailio]
type=endpoint
context=from-kamailio
disallow=all
allow=alaw
allow=ulaw
allow=opus
direct_media=no
trust_id_inbound=yes
trust_id_outbound=yes
send_pai=yes [kamailio]
type=aor
max_contacts=1
; /etc/asterisk/pjsip.conf (Asterisk 16+) [transport-udp]
type=transport
protocol=udp
bind=0.0.0.0 ; Trust Kamailio as a SIP peer
[kamailio]
type=identify
endpoint=kamailio
match=YOUR_SERVER_IP [kamailio]
type=endpoint
context=from-kamailio
disallow=all
allow=alaw
allow=ulaw
allow=opus
direct_media=no
trust_id_inbound=yes
trust_id_outbound=yes
send_pai=yes [kamailio]
type=aor
max_contacts=1
; /etc/asterisk/extensions.conf
[from-kamailio]
; Calls from Kamailio arrive here
exten => _X.,1,NoOp(Call from Kamailio: ${CALLERID(num)} → ${EXTEN}) same => n,Dial(PJSIP/${EXTEN},30) same => n,Hangup()
; /etc/asterisk/extensions.conf
[from-kamailio]
; Calls from Kamailio arrive here
exten => _X.,1,NoOp(Call from Kamailio: ${CALLERID(num)} → ${EXTEN}) same => n,Dial(PJSIP/${EXTEN},30) same => n,Hangup()
; /etc/asterisk/extensions.conf
[from-kamailio]
; Calls from Kamailio arrive here
exten => _X.,1,NoOp(Call from Kamailio: ${CALLERID(num)} → ${EXTEN}) same => n,Dial(PJSIP/${EXTEN},30) same => n,Hangup()
<!-- FreeSWITCH: /etc/freeswitch/sip_profiles/internal.xml -->
<!-- Add Kamailio as an ACL -->
<param name="apply-inbound-acl" value="kamailio"/> <!-- /etc/freeswitch/autoload_configs/acl.conf.xml -->
<list name="kamailio" default="deny"> <node type="allow" cidr="YOUR_SERVER_IP/32"/>
</list>
<!-- FreeSWITCH: /etc/freeswitch/sip_profiles/internal.xml -->
<!-- Add Kamailio as an ACL -->
<param name="apply-inbound-acl" value="kamailio"/> <!-- /etc/freeswitch/autoload_configs/acl.conf.xml -->
<list name="kamailio" default="deny"> <node type="allow" cidr="YOUR_SERVER_IP/32"/>
</list>
<!-- FreeSWITCH: /etc/freeswitch/sip_profiles/internal.xml -->
<!-- Add Kamailio as an ACL -->
<param name="apply-inbound-acl" value="kamailio"/> <!-- /etc/freeswitch/autoload_configs/acl.conf.xml -->
<list name="kamailio" default="deny"> <node type="allow" cidr="YOUR_SERVER_IP/32"/>
</list>
loadmodule "topoh.so" modparam("topoh", "mask_ip", "YOUR_SERVER_IP")
modparam("topoh", "mask_callid", 1) /* Mask Call-ID */
modparam("topoh", "sanity_checks", 1) /* Validate before unmasking */
loadmodule "topoh.so" modparam("topoh", "mask_ip", "YOUR_SERVER_IP")
modparam("topoh", "mask_callid", 1) /* Mask Call-ID */
modparam("topoh", "sanity_checks", 1) /* Validate before unmasking */
loadmodule "topoh.so" modparam("topoh", "mask_ip", "YOUR_SERVER_IP")
modparam("topoh", "mask_callid", 1) /* Mask Call-ID */
modparam("topoh", "sanity_checks", 1) /* Validate before unmasking */
BEFORE (external peer sees internal IPs): Via: SIP/2.0/UDP 10.0.0.10:5060 ← Internal Asterisk IP exposed! Contact: <sip:[email protected]> ← Internal IP exposed! Record-Route: <sip:10.0.0.10;lr> ← Internal IP exposed! AFTER (topoh masks everything): Via: SIP/2.0/UDP YOUR_SERVER_IP:5060 Contact: <sip:1001@YOUR_SERVER_IP> Record-Route: <sip:YOUR_SERVER_IP;lr>
BEFORE (external peer sees internal IPs): Via: SIP/2.0/UDP 10.0.0.10:5060 ← Internal Asterisk IP exposed! Contact: <sip:[email protected]> ← Internal IP exposed! Record-Route: <sip:10.0.0.10;lr> ← Internal IP exposed! AFTER (topoh masks everything): Via: SIP/2.0/UDP YOUR_SERVER_IP:5060 Contact: <sip:1001@YOUR_SERVER_IP> Record-Route: <sip:YOUR_SERVER_IP;lr>
BEFORE (external peer sees internal IPs): Via: SIP/2.0/UDP 10.0.0.10:5060 ← Internal Asterisk IP exposed! Contact: <sip:[email protected]> ← Internal IP exposed! Record-Route: <sip:10.0.0.10;lr> ← Internal IP exposed! AFTER (topoh masks everything): Via: SIP/2.0/UDP YOUR_SERVER_IP:5060 Contact: <sip:1001@YOUR_SERVER_IP> Record-Route: <sip:YOUR_SERVER_IP;lr>
route[TO_ASTERISK] { /* Add custom headers for Asterisk to use */ append_hf("X-Caller-IP: $si\r\n"); append_hf("X-Original-URI: $ou\r\n"); append_hf("X-Auth-User: $au\r\n"); /* Set P-Asserted-Identity for the backend */ remove_hf("P-Asserted-Identity"); append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); /* Remove headers that shouldn't reach the backend */ remove_hf("Proxy-Authorization"); /* Route to backend */ $du = "sip:10.0.0.10:5060"; route(RELAY);
}
route[TO_ASTERISK] { /* Add custom headers for Asterisk to use */ append_hf("X-Caller-IP: $si\r\n"); append_hf("X-Original-URI: $ou\r\n"); append_hf("X-Auth-User: $au\r\n"); /* Set P-Asserted-Identity for the backend */ remove_hf("P-Asserted-Identity"); append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); /* Remove headers that shouldn't reach the backend */ remove_hf("Proxy-Authorization"); /* Route to backend */ $du = "sip:10.0.0.10:5060"; route(RELAY);
}
route[TO_ASTERISK] { /* Add custom headers for Asterisk to use */ append_hf("X-Caller-IP: $si\r\n"); append_hf("X-Original-URI: $ou\r\n"); append_hf("X-Auth-User: $au\r\n"); /* Set P-Asserted-Identity for the backend */ remove_hf("P-Asserted-Identity"); append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); /* Remove headers that shouldn't reach the backend */ remove_hf("Proxy-Authorization"); /* Route to backend */ $du = "sip:10.0.0.10:5060"; route(RELAY);
}
route[DOMAIN_ROUTE] { /* Route based on destination domain */ if ($rd == "customer-a.com") { $du = "sip:10.0.0.10:5060"; /* Customer A's Asterisk */ xlog("L_INFO", "Routing to Customer A backend\n"); } else if ($rd == "customer-b.com") { $du = "sip:10.0.0.11:5060"; /* Customer B's Asterisk */ xlog("L_INFO", "Routing to Customer B backend\n"); } else if ($rd == "customer-c.com") { /* Customer C uses dispatcher set 3 */ if (!ds_select_dst(3, 0)) { sl_send_reply(503, "Service Unavailable"); exit; } } else { /* Unknown domain */ sl_send_reply(404, "Domain Not Served"); exit; } route(RELAY); exit;
}
route[DOMAIN_ROUTE] { /* Route based on destination domain */ if ($rd == "customer-a.com") { $du = "sip:10.0.0.10:5060"; /* Customer A's Asterisk */ xlog("L_INFO", "Routing to Customer A backend\n"); } else if ($rd == "customer-b.com") { $du = "sip:10.0.0.11:5060"; /* Customer B's Asterisk */ xlog("L_INFO", "Routing to Customer B backend\n"); } else if ($rd == "customer-c.com") { /* Customer C uses dispatcher set 3 */ if (!ds_select_dst(3, 0)) { sl_send_reply(503, "Service Unavailable"); exit; } } else { /* Unknown domain */ sl_send_reply(404, "Domain Not Served"); exit; } route(RELAY); exit;
}
route[DOMAIN_ROUTE] { /* Route based on destination domain */ if ($rd == "customer-a.com") { $du = "sip:10.0.0.10:5060"; /* Customer A's Asterisk */ xlog("L_INFO", "Routing to Customer A backend\n"); } else if ($rd == "customer-b.com") { $du = "sip:10.0.0.11:5060"; /* Customer B's Asterisk */ xlog("L_INFO", "Routing to Customer B backend\n"); } else if ($rd == "customer-c.com") { /* Customer C uses dispatcher set 3 */ if (!ds_select_dst(3, 0)) { sl_send_reply(503, "Service Unavailable"); exit; } } else { /* Unknown domain */ sl_send_reply(404, "Domain Not Served"); exit; } route(RELAY); exit;
}
loadmodule "domain.so"
modparam("domain", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
loadmodule "domain.so"
modparam("domain", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
loadmodule "domain.so"
modparam("domain", "db_url", "mysql://kamailio:YOUR_KAMAILIO_DB_PASSWORD@localhost/kamailio")
-- Add served domains
INSERT INTO domain (domain) VALUES ('customer-a.com');
INSERT INTO domain (domain) VALUES ('customer-b.com');
-- Add served domains
INSERT INTO domain (domain) VALUES ('customer-a.com');
INSERT INTO domain (domain) VALUES ('customer-b.com');
-- Add served domains
INSERT INTO domain (domain) VALUES ('customer-a.com');
INSERT INTO domain (domain) VALUES ('customer-b.com');
/* Check if we serve this domain */
if (!is_domain_local("$rd")) { sl_send_reply(403, "Domain Not Served"); exit;
}
/* Check if we serve this domain */
if (!is_domain_local("$rd")) { sl_send_reply(403, "Domain Not Served"); exit;
}
/* Check if we serve this domain */
if (!is_domain_local("$rd")) { sl_send_reply(403, "Domain Not Served"); exit;
}
/* Integrated SBC config: Kamailio in front of 2 Asterisk servers */ route[ROUTE_TO_BACKEND] { /* Determine which backend based on called number pattern */ if ($rU =~ "^1[0-9]{3}$") { /* 4-digit extensions starting with 1 → Asterisk 1 */ $du = "sip:10.0.0.10:5060"; xlog("L_INFO", "Routing ext $rU → Asterisk 1\n"); } else if ($rU =~ "^2[0-9]{3}$") { /* 4-digit extensions starting with 2 → Asterisk 2 */ $du = "sip:10.0.0.11:5060"; xlog("L_INFO", "Routing ext $rU → Asterisk 2\n"); } else if ($rU =~ "^[0-9]{10,}$") { /* External number (10+ digits) → dispatcher set for trunks */ if (!ds_select_dst(2, 4)) { /* Set 2, priority algorithm */ sl_send_reply(503, "No Trunks Available"); exit; } xlog("L_INFO", "Routing PSTN $rU → trunk $du\n"); } else { sl_send_reply(404, "Number Not Routable"); exit; } /* Add integration headers */ append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); append_hf("X-Kamailio-Server: sbc01\r\n"); /* Enable dialog tracking */ setflag(4); dlg_manage(); /* Manage media */ route(NATMANAGE); /* Forward with failover */ t_on_failure("BACKEND_FAILURE"); route(RELAY); exit;
} failure_route[BACKEND_FAILURE] { if (t_is_canceled()) exit; if (t_check_status("408|5[0-9][0-9]")) { xlog("L_WARN", "Backend $du failed ($T_reply_code)\n"); /* Try alternate backend */ if ($rU =~ "^1[0-9]{3}$") { /* Asterisk 1 failed — try Asterisk 2 */ $du = "sip:10.0.0.11:5060"; } else if ($rU =~ "^2[0-9]{3}$") { /* Asterisk 2 failed — try Asterisk 1 */ $du = "sip:10.0.0.10:5060"; } else { /* Trunk failover — use dispatcher */ if (ds_next_dst()) { t_on_failure("BACKEND_FAILURE"); route(RELAY); exit; } t_reply(503, "All Backends Down"); exit; } t_on_failure("FINAL_FAILURE"); route(RELAY); exit; }
} failure_route[FINAL_FAILURE] { if (t_is_canceled()) exit; xlog("L_ERR", "All backends failed for $rU\n");
}
/* Integrated SBC config: Kamailio in front of 2 Asterisk servers */ route[ROUTE_TO_BACKEND] { /* Determine which backend based on called number pattern */ if ($rU =~ "^1[0-9]{3}$") { /* 4-digit extensions starting with 1 → Asterisk 1 */ $du = "sip:10.0.0.10:5060"; xlog("L_INFO", "Routing ext $rU → Asterisk 1\n"); } else if ($rU =~ "^2[0-9]{3}$") { /* 4-digit extensions starting with 2 → Asterisk 2 */ $du = "sip:10.0.0.11:5060"; xlog("L_INFO", "Routing ext $rU → Asterisk 2\n"); } else if ($rU =~ "^[0-9]{10,}$") { /* External number (10+ digits) → dispatcher set for trunks */ if (!ds_select_dst(2, 4)) { /* Set 2, priority algorithm */ sl_send_reply(503, "No Trunks Available"); exit; } xlog("L_INFO", "Routing PSTN $rU → trunk $du\n"); } else { sl_send_reply(404, "Number Not Routable"); exit; } /* Add integration headers */ append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); append_hf("X-Kamailio-Server: sbc01\r\n"); /* Enable dialog tracking */ setflag(4); dlg_manage(); /* Manage media */ route(NATMANAGE); /* Forward with failover */ t_on_failure("BACKEND_FAILURE"); route(RELAY); exit;
} failure_route[BACKEND_FAILURE] { if (t_is_canceled()) exit; if (t_check_status("408|5[0-9][0-9]")) { xlog("L_WARN", "Backend $du failed ($T_reply_code)\n"); /* Try alternate backend */ if ($rU =~ "^1[0-9]{3}$") { /* Asterisk 1 failed — try Asterisk 2 */ $du = "sip:10.0.0.11:5060"; } else if ($rU =~ "^2[0-9]{3}$") { /* Asterisk 2 failed — try Asterisk 1 */ $du = "sip:10.0.0.10:5060"; } else { /* Trunk failover — use dispatcher */ if (ds_next_dst()) { t_on_failure("BACKEND_FAILURE"); route(RELAY); exit; } t_reply(503, "All Backends Down"); exit; } t_on_failure("FINAL_FAILURE"); route(RELAY); exit; }
} failure_route[FINAL_FAILURE] { if (t_is_canceled()) exit; xlog("L_ERR", "All backends failed for $rU\n");
}
/* Integrated SBC config: Kamailio in front of 2 Asterisk servers */ route[ROUTE_TO_BACKEND] { /* Determine which backend based on called number pattern */ if ($rU =~ "^1[0-9]{3}$") { /* 4-digit extensions starting with 1 → Asterisk 1 */ $du = "sip:10.0.0.10:5060"; xlog("L_INFO", "Routing ext $rU → Asterisk 1\n"); } else if ($rU =~ "^2[0-9]{3}$") { /* 4-digit extensions starting with 2 → Asterisk 2 */ $du = "sip:10.0.0.11:5060"; xlog("L_INFO", "Routing ext $rU → Asterisk 2\n"); } else if ($rU =~ "^[0-9]{10,}$") { /* External number (10+ digits) → dispatcher set for trunks */ if (!ds_select_dst(2, 4)) { /* Set 2, priority algorithm */ sl_send_reply(503, "No Trunks Available"); exit; } xlog("L_INFO", "Routing PSTN $rU → trunk $du\n"); } else { sl_send_reply(404, "Number Not Routable"); exit; } /* Add integration headers */ append_hf("P-Asserted-Identity: <sip:$fU@YOUR_DOMAIN>\r\n"); append_hf("X-Kamailio-Server: sbc01\r\n"); /* Enable dialog tracking */ setflag(4); dlg_manage(); /* Manage media */ route(NATMANAGE); /* Forward with failover */ t_on_failure("BACKEND_FAILURE"); route(RELAY); exit;
} failure_route[BACKEND_FAILURE] { if (t_is_canceled()) exit; if (t_check_status("408|5[0-9][0-9]")) { xlog("L_WARN", "Backend $du failed ($T_reply_code)\n"); /* Try alternate backend */ if ($rU =~ "^1[0-9]{3}$") { /* Asterisk 1 failed — try Asterisk 2 */ $du = "sip:10.0.0.11:5060"; } else if ($rU =~ "^2[0-9]{3}$") { /* Asterisk 2 failed — try Asterisk 1 */ $du = "sip:10.0.0.10:5060"; } else { /* Trunk failover — use dispatcher */ if (ds_next_dst()) { t_on_failure("BACKEND_FAILURE"); route(RELAY); exit; } t_reply(503, "All Backends Down"); exit; } t_on_failure("FINAL_FAILURE"); route(RELAY); exit; }
} failure_route[FINAL_FAILURE] { if (t_is_canceled()) exit; xlog("L_ERR", "All backends failed for $rU\n");
}
# Server information
kamcmd core.version
kamcmd core.uptime
kamcmd core.info # Process management
kamcmd core.ps # List processes
kamcmd core.psx # Extended process list # Statistics
kamcmd stats.get_statistics all # All statistics
kamcmd stats.get_statistics core: # Core statistics
kamcmd stats.get_statistics tmx: # Transaction statistics
kamcmd stats.get_statistics dialog: # Dialog (call) statistics
kamcmd stats.get_statistics usrloc: # Registration statistics
kamcmd stats.get_statistics dispatcher: # Dispatcher statistics # User location (registrations)
kamcmd ul.dump # Dump all registrations
kamcmd ul.lookup location 1001 # Look up specific user
kamcmd ul.rm location 1001 # Remove a registration
kamcmd ul.flush # Flush expired contacts # Dispatcher
kamcmd dispatcher.list # List all destinations with -weight: 500;">status
kamcmd dispatcher.reload # Reload from database
kamcmd dispatcher.set_state ap 1 sip:10.0.0.10:5060 # Set Active
kamcmd dispatcher.set_state ip 1 sip:10.0.0.10:5060 # Set Inactive # Dialog
kamcmd dlg.list # List active calls
kamcmd dlg.stats_active # Active call count
kamcmd dlg.end_dlg <h_entry> <h_id> # Terminate a call # TLS
kamcmd tls.list # List TLS connections
kamcmd tls.info # TLS module info # Memory
kamcmd pkg.stats # Per-process memory
kamcmd mod.stats # Per-module memory # Configuration reload
kamcmd cfg.sets # List config variables
# Server information
kamcmd core.version
kamcmd core.uptime
kamcmd core.info # Process management
kamcmd core.ps # List processes
kamcmd core.psx # Extended process list # Statistics
kamcmd stats.get_statistics all # All statistics
kamcmd stats.get_statistics core: # Core statistics
kamcmd stats.get_statistics tmx: # Transaction statistics
kamcmd stats.get_statistics dialog: # Dialog (call) statistics
kamcmd stats.get_statistics usrloc: # Registration statistics
kamcmd stats.get_statistics dispatcher: # Dispatcher statistics # User location (registrations)
kamcmd ul.dump # Dump all registrations
kamcmd ul.lookup location 1001 # Look up specific user
kamcmd ul.rm location 1001 # Remove a registration
kamcmd ul.flush # Flush expired contacts # Dispatcher
kamcmd dispatcher.list # List all destinations with -weight: 500;">status
kamcmd dispatcher.reload # Reload from database
kamcmd dispatcher.set_state ap 1 sip:10.0.0.10:5060 # Set Active
kamcmd dispatcher.set_state ip 1 sip:10.0.0.10:5060 # Set Inactive # Dialog
kamcmd dlg.list # List active calls
kamcmd dlg.stats_active # Active call count
kamcmd dlg.end_dlg <h_entry> <h_id> # Terminate a call # TLS
kamcmd tls.list # List TLS connections
kamcmd tls.info # TLS module info # Memory
kamcmd pkg.stats # Per-process memory
kamcmd mod.stats # Per-module memory # Configuration reload
kamcmd cfg.sets # List config variables
# Server information
kamcmd core.version
kamcmd core.uptime
kamcmd core.info # Process management
kamcmd core.ps # List processes
kamcmd core.psx # Extended process list # Statistics
kamcmd stats.get_statistics all # All statistics
kamcmd stats.get_statistics core: # Core statistics
kamcmd stats.get_statistics tmx: # Transaction statistics
kamcmd stats.get_statistics dialog: # Dialog (call) statistics
kamcmd stats.get_statistics usrloc: # Registration statistics
kamcmd stats.get_statistics dispatcher: # Dispatcher statistics # User location (registrations)
kamcmd ul.dump # Dump all registrations
kamcmd ul.lookup location 1001 # Look up specific user
kamcmd ul.rm location 1001 # Remove a registration
kamcmd ul.flush # Flush expired contacts # Dispatcher
kamcmd dispatcher.list # List all destinations with -weight: 500;">status
kamcmd dispatcher.reload # Reload from database
kamcmd dispatcher.set_state ap 1 sip:10.0.0.10:5060 # Set Active
kamcmd dispatcher.set_state ip 1 sip:10.0.0.10:5060 # Set Inactive # Dialog
kamcmd dlg.list # List active calls
kamcmd dlg.stats_active # Active call count
kamcmd dlg.end_dlg <h_entry> <h_id> # Terminate a call # TLS
kamcmd tls.list # List TLS connections
kamcmd tls.info # TLS module info # Memory
kamcmd pkg.stats # Per-process memory
kamcmd mod.stats # Per-module memory # Configuration reload
kamcmd cfg.sets # List config variables
# User management
kamctl add 1001 password123 # Add user
kamctl rm 1001 # Remove user
kamctl passwd 1001 newpass # Change password
kamctl showdb subscriber # List all users # Address/ACL management
kamctl address add 1 10.0.0.5 32 5060 "Asterisk Backend"
kamctl address show
kamctl address reload # Dispatcher management
kamctl dispatcher add 1 sip:10.0.0.10:5060 0 0 "" "Backend 1"
kamctl dispatcher show
kamctl dispatcher reload # Database operations
kamctl db show version # Show table versions
kamctl db exec "SELECT * FROM location" # Raw SQL query # Online monitoring
kamctl monitor # Real-time stats display
kamctl stats # One-shot statistics # Fifo commands
kamctl fifo which # List available FIFO commands
# User management
kamctl add 1001 password123 # Add user
kamctl rm 1001 # Remove user
kamctl passwd 1001 newpass # Change password
kamctl showdb subscriber # List all users # Address/ACL management
kamctl address add 1 10.0.0.5 32 5060 "Asterisk Backend"
kamctl address show
kamctl address reload # Dispatcher management
kamctl dispatcher add 1 sip:10.0.0.10:5060 0 0 "" "Backend 1"
kamctl dispatcher show
kamctl dispatcher reload # Database operations
kamctl db show version # Show table versions
kamctl db exec "SELECT * FROM location" # Raw SQL query # Online monitoring
kamctl monitor # Real-time stats display
kamctl stats # One-shot statistics # Fifo commands
kamctl fifo which # List available FIFO commands
# User management
kamctl add 1001 password123 # Add user
kamctl rm 1001 # Remove user
kamctl passwd 1001 newpass # Change password
kamctl showdb subscriber # List all users # Address/ACL management
kamctl address add 1 10.0.0.5 32 5060 "Asterisk Backend"
kamctl address show
kamctl address reload # Dispatcher management
kamctl dispatcher add 1 sip:10.0.0.10:5060 0 0 "" "Backend 1"
kamctl dispatcher show
kamctl dispatcher reload # Database operations
kamctl db show version # Show table versions
kamctl db exec "SELECT * FROM location" # Raw SQL query # Online monitoring
kamctl monitor # Real-time stats display
kamctl stats # One-shot statistics # Fifo commands
kamctl fifo which # List available FIFO commands
loadmodule "jsonrpcs.so"
modparam("jsonrpcs", "pretty_format", 1)
modparam("jsonrpcs", "transport", 1) /* Optional: HTTP-based JSONRPC */
loadmodule "xhttp.so"
/* Add to event_route[xhttp:request] or use separate port */
loadmodule "jsonrpcs.so"
modparam("jsonrpcs", "pretty_format", 1)
modparam("jsonrpcs", "transport", 1) /* Optional: HTTP-based JSONRPC */
loadmodule "xhttp.so"
/* Add to event_route[xhttp:request] or use separate port */
loadmodule "jsonrpcs.so"
modparam("jsonrpcs", "pretty_format", 1)
modparam("jsonrpcs", "transport", 1) /* Optional: HTTP-based JSONRPC */
loadmodule "xhttp.so"
/* Add to event_route[xhttp:request] or use separate port */
# Using kamcmd (which uses JSONRPC internally)
kamcmd -s tcp:localhost:2049 core.version # Using -weight: 500;">curl (if HTTP transport is enabled)
-weight: 500;">curl -s -X POST http://localhost:5060/jsonrpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"core.version","id":1}' # Get active calls count
-weight: 500;">curl -s -X POST http://localhost:5060/jsonrpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"stats.get_statistics","params":[["dialog:"]],"id":1}'
# Using kamcmd (which uses JSONRPC internally)
kamcmd -s tcp:localhost:2049 core.version # Using -weight: 500;">curl (if HTTP transport is enabled)
-weight: 500;">curl -s -X POST http://localhost:5060/jsonrpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"core.version","id":1}' # Get active calls count
-weight: 500;">curl -s -X POST http://localhost:5060/jsonrpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"stats.get_statistics","params":[["dialog:"]],"id":1}'
# Using kamcmd (which uses JSONRPC internally)
kamcmd -s tcp:localhost:2049 core.version # Using -weight: 500;">curl (if HTTP transport is enabled)
-weight: 500;">curl -s -X POST http://localhost:5060/jsonrpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"core.version","id":1}' # Get active calls count
-weight: 500;">curl -s -X POST http://localhost:5060/jsonrpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"stats.get_statistics","params":[["dialog:"]],"id":1}'
# Install the Kamailio Prometheus exporter
# Option 1: Go binary
-weight: 500;">wget https://github.com/florentchauveau/kamailio_exporter/releases/latest/download/kamailio_exporter_linux_amd64
chmod +x kamailio_exporter_linux_amd64
mv kamailio_exporter_linux_amd64 /usr/local/bin/kamailio_exporter # Create systemd -weight: 500;">service
cat > /etc/systemd/system/kamailio-exporter.-weight: 500;">service << 'EOF'
[Unit]
Description=Kamailio Prometheus Exporter
After=kamailio.-weight: 500;">service [Service]
ExecStart=/usr/local/bin/kamailio_exporter \ --kamailio.address=unix:/var/run/kamailio/kamailio_ctl \ --web.listen-address=:9494
Restart=always
User=kamailio [Install]
WantedBy=multi-user.target
EOF -weight: 500;">systemctl daemon-reload
-weight: 500;">systemctl -weight: 500;">enable --now kamailio-exporter
# Install the Kamailio Prometheus exporter
# Option 1: Go binary
-weight: 500;">wget https://github.com/florentchauveau/kamailio_exporter/releases/latest/download/kamailio_exporter_linux_amd64
chmod +x kamailio_exporter_linux_amd64
mv kamailio_exporter_linux_amd64 /usr/local/bin/kamailio_exporter # Create systemd -weight: 500;">service
cat > /etc/systemd/system/kamailio-exporter.-weight: 500;">service << 'EOF'
[Unit]
Description=Kamailio Prometheus Exporter
After=kamailio.-weight: 500;">service [Service]
ExecStart=/usr/local/bin/kamailio_exporter \ --kamailio.address=unix:/var/run/kamailio/kamailio_ctl \ --web.listen-address=:9494
Restart=always
User=kamailio [Install]
WantedBy=multi-user.target
EOF -weight: 500;">systemctl daemon-reload
-weight: 500;">systemctl -weight: 500;">enable --now kamailio-exporter
# Install the Kamailio Prometheus exporter
# Option 1: Go binary
-weight: 500;">wget https://github.com/florentchauveau/kamailio_exporter/releases/latest/download/kamailio_exporter_linux_amd64
chmod +x kamailio_exporter_linux_amd64
mv kamailio_exporter_linux_amd64 /usr/local/bin/kamailio_exporter # Create systemd -weight: 500;">service
cat > /etc/systemd/system/kamailio-exporter.-weight: 500;">service << 'EOF'
[Unit]
Description=Kamailio Prometheus Exporter
After=kamailio.-weight: 500;">service [Service]
ExecStart=/usr/local/bin/kamailio_exporter \ --kamailio.address=unix:/var/run/kamailio/kamailio_ctl \ --web.listen-address=:9494
Restart=always
User=kamailio [Install]
WantedBy=multi-user.target
EOF -weight: 500;">systemctl daemon-reload
-weight: 500;">systemctl -weight: 500;">enable --now kamailio-exporter
# /etc/prometheus/prometheus.yml
scrape_configs: - job_name: 'kamailio' static_configs: - targets: ['localhost:9494'] scrape_interval: 15s
# /etc/prometheus/prometheus.yml
scrape_configs: - job_name: 'kamailio' static_configs: - targets: ['localhost:9494'] scrape_interval: 15s
# /etc/prometheus/prometheus.yml
scrape_configs: - job_name: 'kamailio' static_configs: - targets: ['localhost:9494'] scrape_interval: 15s
# /etc/rsyslog.d/kamailio.conf
local0.* /var/log/kamailio/kamailio.log
# /etc/rsyslog.d/kamailio.conf
local0.* /var/log/kamailio/kamailio.log
# /etc/rsyslog.d/kamailio.conf
local0.* /var/log/kamailio/kamailio.log
# Authentication failures (potential brute force)
grep "AUTH_FAILED" /var/log/kamailio/kamailio.log | tail -20 # Pike blocks (flood detection)
grep "PIKE" /var/log/kamailio/kamailio.log | tail -20 # Dispatcher failures (backend down)
grep "DISPATCH.*failed" /var/log/kamailio/kamailio.log | tail -20 # Registration events
grep "REGISTER" /var/log/kamailio/kamailio.log | tail -20 # Call failures
grep "failed.*T_reply_code" /var/log/kamailio/kamailio.log | tail -20
# Authentication failures (potential brute force)
grep "AUTH_FAILED" /var/log/kamailio/kamailio.log | tail -20 # Pike blocks (flood detection)
grep "PIKE" /var/log/kamailio/kamailio.log | tail -20 # Dispatcher failures (backend down)
grep "DISPATCH.*failed" /var/log/kamailio/kamailio.log | tail -20 # Registration events
grep "REGISTER" /var/log/kamailio/kamailio.log | tail -20 # Call failures
grep "failed.*T_reply_code" /var/log/kamailio/kamailio.log | tail -20
# Authentication failures (potential brute force)
grep "AUTH_FAILED" /var/log/kamailio/kamailio.log | tail -20 # Pike blocks (flood detection)
grep "PIKE" /var/log/kamailio/kamailio.log | tail -20 # Dispatcher failures (backend down)
grep "DISPATCH.*failed" /var/log/kamailio/kamailio.log | tail -20 # Registration events
grep "REGISTER" /var/log/kamailio/kamailio.log | tail -20 # Call failures
grep "failed.*T_reply_code" /var/log/kamailio/kamailio.log | tail -20
cat > /etc/logrotate.d/kamailio << 'EOF'
/var/log/kamailio/kamailio.log { daily rotate 14 compress delaycompress missingok notifempty postrotate /usr/bin/-weight: 500;">systemctl reload rsyslog > /dev/null 2>&1 || true endscript
}
EOF
cat > /etc/logrotate.d/kamailio << 'EOF'
/var/log/kamailio/kamailio.log { daily rotate 14 compress delaycompress missingok notifempty postrotate /usr/bin/-weight: 500;">systemctl reload rsyslog > /dev/null 2>&1 || true endscript
}
EOF
cat > /etc/logrotate.d/kamailio << 'EOF'
/var/log/kamailio/kamailio.log { daily rotate 14 compress delaycompress missingok notifempty postrotate /usr/bin/-weight: 500;">systemctl reload rsyslog > /dev/null 2>&1 || true endscript
}
EOF
loadmodule "siptrace.so" modparam("siptrace", "duplicate_uri", "sip:HOMER_IP:9060")
modparam("siptrace", "hep_mode_on", 1)
modparam("siptrace", "hep_version", 3)
modparam("siptrace", "hep_capture_id", 100)
modparam("siptrace", "trace_on", 1)
modparam("siptrace", "trace_flag", 22) /* In request_route — -weight: 500;">enable tracing for all traffic */
request_route { setflag(22); /* Enable SIP trace */ sip_trace(); /* ... */
}
loadmodule "siptrace.so" modparam("siptrace", "duplicate_uri", "sip:HOMER_IP:9060")
modparam("siptrace", "hep_mode_on", 1)
modparam("siptrace", "hep_version", 3)
modparam("siptrace", "hep_capture_id", 100)
modparam("siptrace", "trace_on", 1)
modparam("siptrace", "trace_flag", 22) /* In request_route — -weight: 500;">enable tracing for all traffic */
request_route { setflag(22); /* Enable SIP trace */ sip_trace(); /* ... */
}
loadmodule "siptrace.so" modparam("siptrace", "duplicate_uri", "sip:HOMER_IP:9060")
modparam("siptrace", "hep_mode_on", 1)
modparam("siptrace", "hep_version", 3)
modparam("siptrace", "hep_capture_id", 100)
modparam("siptrace", "trace_on", 1)
modparam("siptrace", "trace_flag", 22) /* In request_route — -weight: 500;">enable tracing for all traffic */
request_route { setflag(22); /* Enable SIP trace */ sip_trace(); /* ... */
}
# Get current debug level
kamcmd cfg.get core debug # Set debug to maximum (temporary — resets on -weight: 500;">restart)
kamcmd cfg.seti core debug 6 # Set back to production level
kamcmd cfg.seti core debug 2
# Get current debug level
kamcmd cfg.get core debug # Set debug to maximum (temporary — resets on -weight: 500;">restart)
kamcmd cfg.seti core debug 6 # Set back to production level
kamcmd cfg.seti core debug 2
# Get current debug level
kamcmd cfg.get core debug # Set debug to maximum (temporary — resets on -weight: 500;">restart)
kamcmd cfg.seti core debug 6 # Set back to production level
kamcmd cfg.seti core debug 2
/* Add strategic xlog statements */
request_route { xlog("L_INFO", ">>> $rm from $fU@$si:$sp to $rU R-URI=$ru\n"); /* Trace specific call by Call-ID */ if ($ci =~ "abc123") { xlog("L_DBG", "[TRACE] Headers:\n$mb\n"); }
} route[RELAY] { xlog("L_INFO", "RELAY: $rm → $du (branch=$T_branch_idx)\n");
} onreply_route[MANAGE_REPLY] { xlog("L_INFO", "<<< Reply $T_reply_code $T_reply_reason for $rm ($ci)\n");
} failure_route[MANAGE_FAILURE] { xlog("L_WARN", "!!! FAILURE: $T_reply_code for $rU from $du ($ci)\n");
}
/* Add strategic xlog statements */
request_route { xlog("L_INFO", ">>> $rm from $fU@$si:$sp to $rU R-URI=$ru\n"); /* Trace specific call by Call-ID */ if ($ci =~ "abc123") { xlog("L_DBG", "[TRACE] Headers:\n$mb\n"); }
} route[RELAY] { xlog("L_INFO", "RELAY: $rm → $du (branch=$T_branch_idx)\n");
} onreply_route[MANAGE_REPLY] { xlog("L_INFO", "<<< Reply $T_reply_code $T_reply_reason for $rm ($ci)\n");
} failure_route[MANAGE_FAILURE] { xlog("L_WARN", "!!! FAILURE: $T_reply_code for $rU from $du ($ci)\n");
}
/* Add strategic xlog statements */
request_route { xlog("L_INFO", ">>> $rm from $fU@$si:$sp to $rU R-URI=$ru\n"); /* Trace specific call by Call-ID */ if ($ci =~ "abc123") { xlog("L_DBG", "[TRACE] Headers:\n$mb\n"); }
} route[RELAY] { xlog("L_INFO", "RELAY: $rm → $du (branch=$T_branch_idx)\n");
} onreply_route[MANAGE_REPLY] { xlog("L_INFO", "<<< Reply $T_reply_code $T_reply_reason for $rm ($ci)\n");
} failure_route[MANAGE_FAILURE] { xlog("L_WARN", "!!! FAILURE: $T_reply_code for $rU from $du ($ci)\n");
}
/* In kamailio.cfg — global parameter */
cfgtrace=1 /* Trace all route block execution */
/* In kamailio.cfg — global parameter */
cfgtrace=1 /* Trace all route block execution */
/* In kamailio.cfg — global parameter */
cfgtrace=1 /* Trace all route block execution */
if ($ci =~ "debug-this-call") { setflag(25); /* Use cfgtrace for this specific request */
}
if ($ci =~ "debug-this-call") { setflag(25); /* Use cfgtrace for this specific request */
}
if ($ci =~ "debug-this-call") { setflag(25); /* Use cfgtrace for this specific request */
}
/* Ensure Record-Route is always added */
if (is_method("INVITE|SUBSCRIBE")) { record_route();
} /* Handle stale in-dialog requests gracefully */
if (has_totag()) { if (!loose_route()) { if (is_method("ACK")) { if (t_check_trans()) exit; exit; /* Silently absorb orphan ACKs */ } if (is_method("BYE")) { /* Reply 481 but don't log as error — normal after -weight: 500;">restart */ sl_send_reply(481, "Call Leg Does Not Exist"); exit; } }
}
/* Ensure Record-Route is always added */
if (is_method("INVITE|SUBSCRIBE")) { record_route();
} /* Handle stale in-dialog requests gracefully */
if (has_totag()) { if (!loose_route()) { if (is_method("ACK")) { if (t_check_trans()) exit; exit; /* Silently absorb orphan ACKs */ } if (is_method("BYE")) { /* Reply 481 but don't log as error — normal after -weight: 500;">restart */ sl_send_reply(481, "Call Leg Does Not Exist"); exit; } }
}
/* Ensure Record-Route is always added */
if (is_method("INVITE|SUBSCRIBE")) { record_route();
} /* Handle stale in-dialog requests gracefully */
if (has_totag()) { if (!loose_route()) { if (is_method("ACK")) { if (t_check_trans()) exit; exit; /* Silently absorb orphan ACKs */ } if (is_method("BYE")) { /* Reply 481 but don't log as error — normal after -weight: 500;">restart */ sl_send_reply(481, "Call Leg Does Not Exist"); exit; } }
}
# Check if the user exists in subscriber table
kamctl showdb subscriber | grep 1001 # Check realm mismatch
# The realm in www_authorize/proxy_authorize MUST match
# what the SIP phone uses
# Check if the user exists in subscriber table
kamctl showdb subscriber | grep 1001 # Check realm mismatch
# The realm in www_authorize/proxy_authorize MUST match
# what the SIP phone uses
# Check if the user exists in subscriber table
kamctl showdb subscriber | grep 1001 # Check realm mismatch
# The realm in www_authorize/proxy_authorize MUST match
# what the SIP phone uses
/* Ensure realm matches your domain/IP */
if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { /* Try with IP as realm if domain fails */ if (!proxy_authorize("YOUR_SERVER_IP", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; }
}
/* Ensure realm matches your domain/IP */
if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { /* Try with IP as realm if domain fails */ if (!proxy_authorize("YOUR_SERVER_IP", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; }
}
/* Ensure realm matches your domain/IP */
if (!proxy_authorize("YOUR_DOMAIN", "subscriber")) { /* Try with IP as realm if domain fails */ if (!proxy_authorize("YOUR_SERVER_IP", "subscriber")) { proxy_challenge("YOUR_DOMAIN", 1); exit; }
}
# Watch for looping messages
grep "483" /var/log/kamailio/kamailio.log # Check Via headers in the SIP trace — if you see
# the same server IP appearing multiple times, it's a loop
# Watch for looping messages
grep "483" /var/log/kamailio/kamailio.log # Check Via headers in the SIP trace — if you see
# the same server IP appearing multiple times, it's a loop
# Watch for looping messages
grep "483" /var/log/kamailio/kamailio.log # Check Via headers in the SIP trace — if you see
# the same server IP appearing multiple times, it's a loop
/* Ensure mf_process_maxfwd_header is at the TOP of request_route */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } /* Check for loop: same message arriving twice */ if (is_method("INVITE") && !has_totag()) { if (t_lookup_request()) { /* Already processing this transaction — retransmission */ exit; } }
}
/* Ensure mf_process_maxfwd_header is at the TOP of request_route */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } /* Check for loop: same message arriving twice */ if (is_method("INVITE") && !has_totag()) { if (t_lookup_request()) { /* Already processing this transaction — retransmission */ exit; } }
}
/* Ensure mf_process_maxfwd_header is at the TOP of request_route */
request_route { if (!mf_process_maxfwd_header(10)) { sl_send_reply(483, "Too Many Hops"); exit; } /* Check for loop: same message arriving twice */ if (is_method("INVITE") && !has_totag()) { if (t_lookup_request()) { /* Already processing this transaction — retransmission */ exit; } }
}
# 1. Is Kamailio listening?
ss -ulnp | grep 5060 # 2. Check for registration attempts in logs
grep "REGISTER" /var/log/kamailio/kamailio.log | tail -20 # 3. Verify user exists
kamctl showdb subscriber # 4. Check current registrations
kamcmd ul.dump # 5. Test registration manually
sipsak -U -C sip:test@YOUR_SERVER_IP \ -s sip:1001@YOUR_SERVER_IP \ -u 1001 -a password123
# 1. Is Kamailio listening?
ss -ulnp | grep 5060 # 2. Check for registration attempts in logs
grep "REGISTER" /var/log/kamailio/kamailio.log | tail -20 # 3. Verify user exists
kamctl showdb subscriber # 4. Check current registrations
kamcmd ul.dump # 5. Test registration manually
sipsak -U -C sip:test@YOUR_SERVER_IP \ -s sip:1001@YOUR_SERVER_IP \ -u 1001 -a password123
# 1. Is Kamailio listening?
ss -ulnp | grep 5060 # 2. Check for registration attempts in logs
grep "REGISTER" /var/log/kamailio/kamailio.log | tail -20 # 3. Verify user exists
kamctl showdb subscriber # 4. Check current registrations
kamcmd ul.dump # 5. Test registration manually
sipsak -U -C sip:test@YOUR_SERVER_IP \ -s sip:1001@YOUR_SERVER_IP \ -u 1001 -a password123
# 1. Is RTPEngine running?
-weight: 500;">systemctl -weight: 500;">status rtpengine
ss -ulnp | grep rtpengine # 2. Check RTPEngine statistics
echo "list totals" | nc -u 127.0.0.1 2223 # 3. Are RTP ports open in the firewall?
# Port range 10000-20000 UDP must be open
ss -ulnp | grep -E "1[0-9]{4}|20000" # 4. Check if NAT is being detected
grep "NAT detected" /var/log/kamailio/kamailio.log | tail -10 # 5. Capture SIP to check SDP
ngrep -W byline -d any port 5060 | grep -A20 "INVITE"
# Look at c= and m= lines — do they contain private IPs?
# 1. Is RTPEngine running?
-weight: 500;">systemctl -weight: 500;">status rtpengine
ss -ulnp | grep rtpengine # 2. Check RTPEngine statistics
echo "list totals" | nc -u 127.0.0.1 2223 # 3. Are RTP ports open in the firewall?
# Port range 10000-20000 UDP must be open
ss -ulnp | grep -E "1[0-9]{4}|20000" # 4. Check if NAT is being detected
grep "NAT detected" /var/log/kamailio/kamailio.log | tail -10 # 5. Capture SIP to check SDP
ngrep -W byline -d any port 5060 | grep -A20 "INVITE"
# Look at c= and m= lines — do they contain private IPs?
# 1. Is RTPEngine running?
-weight: 500;">systemctl -weight: 500;">status rtpengine
ss -ulnp | grep rtpengine # 2. Check RTPEngine statistics
echo "list totals" | nc -u 127.0.0.1 2223 # 3. Are RTP ports open in the firewall?
# Port range 10000-20000 UDP must be open
ss -ulnp | grep -E "1[0-9]{4}|20000" # 4. Check if NAT is being detected
grep "NAT detected" /var/log/kamailio/kamailio.log | tail -10 # 5. Capture SIP to check SDP
ngrep -W byline -d any port 5060 | grep -A20 "INVITE"
# Look at c= and m= lines — do they contain private IPs?
# Check dispatcher -weight: 500;">status
kamcmd dispatcher.list
# Are backends marked as Active (AP) or Inactive (IP)? # Check if health checking is working
grep "OPTIONS" /var/log/kamailio/kamailio.log | tail -10
# Check dispatcher -weight: 500;">status
kamcmd dispatcher.list
# Are backends marked as Active (AP) or Inactive (IP)? # Check if health checking is working
grep "OPTIONS" /var/log/kamailio/kamailio.log | tail -10
# Check dispatcher -weight: 500;">status
kamcmd dispatcher.list
# Are backends marked as Active (AP) or Inactive (IP)? # Check if health checking is working
grep "OPTIONS" /var/log/kamailio/kamailio.log | tail -10
# /etc/sysctl.conf — network tuning for SIP proxy
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
net.core.netdev_max_backlog = 5000
net.ipv4.udp_mem = 8388608 12582912 16777216
net.ipv4.ip_local_port_range = 10000 65535
net.core.somaxconn = 4096
fs.file-max = 1000000 # Apply
sysctl -p # Increase open file limit for Kamailio
cat >> /etc/security/limits.d/kamailio.conf << 'EOF'
kamailio soft nofile 65536
kamailio hard nofile 65536
EOF
# /etc/sysctl.conf — network tuning for SIP proxy
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
net.core.netdev_max_backlog = 5000
net.ipv4.udp_mem = 8388608 12582912 16777216
net.ipv4.ip_local_port_range = 10000 65535
net.core.somaxconn = 4096
fs.file-max = 1000000 # Apply
sysctl -p # Increase open file limit for Kamailio
cat >> /etc/security/limits.d/kamailio.conf << 'EOF'
kamailio soft nofile 65536
kamailio hard nofile 65536
EOF
# /etc/sysctl.conf — network tuning for SIP proxy
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
net.core.netdev_max_backlog = 5000
net.ipv4.udp_mem = 8388608 12582912 16777216
net.ipv4.ip_local_port_range = 10000 65535
net.core.somaxconn = 4096
fs.file-max = 1000000 # Apply
sysctl -p # Increase open file limit for Kamailio
cat >> /etc/security/limits.d/kamailio.conf << 'EOF'
kamailio soft nofile 65536
kamailio hard nofile 65536
EOF
#!/bin/bash
# Kamailio Quick Diagnostics
echo "=== Kamailio Diagnostics ==="
echo "" echo "--- Version & Uptime ---"
kamcmd core.version 2>/dev/null || echo "Kamailio not running!"
kamcmd core.uptime 2>/dev/null echo ""
echo "--- Listening Sockets ---"
ss -ulnp 2>/dev/null | grep kamailio
ss -tlnp 2>/dev/null | grep kamailio echo ""
echo "--- Active Registrations ---"
REGS=$(kamcmd ul.dump 2>/dev/null | grep -c "AOR::" || echo "0")
echo "Registered users: $REGS" echo ""
echo "--- Active Calls ---"
kamcmd stats.get_statistics dialog: 2>/dev/null | grep active || echo "No dialog module" echo ""
echo "--- Dispatcher Status ---"
kamcmd dispatcher.list 2>/dev/null | grep -E "URI|FLAGS" || echo "No dispatcher" echo ""
echo "--- Key Statistics ---"
kamcmd stats.get_statistics core:rcv_requests 2>/dev/null
kamcmd stats.get_statistics core:err_requests 2>/dev/null
kamcmd stats.get_statistics tmx:5xx_transactions 2>/dev/null echo ""
echo "--- Memory Usage ---"
kamcmd stats.get_statistics shmem: 2>/dev/null | head -5 echo ""
echo "--- Recent Errors (last 10) ---"
grep -i "error\|critical\|alert" /var/log/kamailio/kamailio.log 2>/dev/null | tail -10 echo ""
echo "--- RTPEngine Status ---"
-weight: 500;">systemctl is-active rtpengine 2>/dev/null || echo "RTPEngine not installed" echo ""
echo "=== Done ==="
#!/bin/bash
# Kamailio Quick Diagnostics
echo "=== Kamailio Diagnostics ==="
echo "" echo "--- Version & Uptime ---"
kamcmd core.version 2>/dev/null || echo "Kamailio not running!"
kamcmd core.uptime 2>/dev/null echo ""
echo "--- Listening Sockets ---"
ss -ulnp 2>/dev/null | grep kamailio
ss -tlnp 2>/dev/null | grep kamailio echo ""
echo "--- Active Registrations ---"
REGS=$(kamcmd ul.dump 2>/dev/null | grep -c "AOR::" || echo "0")
echo "Registered users: $REGS" echo ""
echo "--- Active Calls ---"
kamcmd stats.get_statistics dialog: 2>/dev/null | grep active || echo "No dialog module" echo ""
echo "--- Dispatcher Status ---"
kamcmd dispatcher.list 2>/dev/null | grep -E "URI|FLAGS" || echo "No dispatcher" echo ""
echo "--- Key Statistics ---"
kamcmd stats.get_statistics core:rcv_requests 2>/dev/null
kamcmd stats.get_statistics core:err_requests 2>/dev/null
kamcmd stats.get_statistics tmx:5xx_transactions 2>/dev/null echo ""
echo "--- Memory Usage ---"
kamcmd stats.get_statistics shmem: 2>/dev/null | head -5 echo ""
echo "--- Recent Errors (last 10) ---"
grep -i "error\|critical\|alert" /var/log/kamailio/kamailio.log 2>/dev/null | tail -10 echo ""
echo "--- RTPEngine Status ---"
-weight: 500;">systemctl is-active rtpengine 2>/dev/null || echo "RTPEngine not installed" echo ""
echo "=== Done ==="
#!/bin/bash
# Kamailio Quick Diagnostics
echo "=== Kamailio Diagnostics ==="
echo "" echo "--- Version & Uptime ---"
kamcmd core.version 2>/dev/null || echo "Kamailio not running!"
kamcmd core.uptime 2>/dev/null echo ""
echo "--- Listening Sockets ---"
ss -ulnp 2>/dev/null | grep kamailio
ss -tlnp 2>/dev/null | grep kamailio echo ""
echo "--- Active Registrations ---"
REGS=$(kamcmd ul.dump 2>/dev/null | grep -c "AOR::" || echo "0")
echo "Registered users: $REGS" echo ""
echo "--- Active Calls ---"
kamcmd stats.get_statistics dialog: 2>/dev/null | grep active || echo "No dialog module" echo ""
echo "--- Dispatcher Status ---"
kamcmd dispatcher.list 2>/dev/null | grep -E "URI|FLAGS" || echo "No dispatcher" echo ""
echo "--- Key Statistics ---"
kamcmd stats.get_statistics core:rcv_requests 2>/dev/null
kamcmd stats.get_statistics core:err_requests 2>/dev/null
kamcmd stats.get_statistics tmx:5xx_transactions 2>/dev/null echo ""
echo "--- Memory Usage ---"
kamcmd stats.get_statistics shmem: 2>/dev/null | head -5 echo ""
echo "--- Recent Errors (last 10) ---"
grep -i "error\|critical\|alert" /var/log/kamailio/kamailio.log 2>/dev/null | tail -10 echo ""
echo "--- RTPEngine Status ---"
-weight: 500;">systemctl is-active rtpengine 2>/dev/null || echo "RTPEngine not installed" echo ""
echo "=== Done ==="
chmod +x /usr/local/bin/kamailio-diag.sh
chmod +x /usr/local/bin/kamailio-diag.sh
chmod +x /usr/local/bin/kamailio-diag.sh - Introduction
- Architecture Overview
- Installation — Debian 12 / Ubuntu 24.04
- Configuration Language
- SIP Routing
- User Authentication
- NAT Traversal
- TLS & Security
- Load Balancing with Dispatcher
- Dialog & Accounting
- WebRTC Gateway
- Integration with Media Servers
- Monitoring & Management
- Troubleshooting - Scaling: Your Asterisk server handles 200 registrations fine, but you need to support 10,000+ SIP devices
- Security (SBC): You want a hardened front-end that absorbs SIP attacks before they reach your PBX
- Load balancing: You have 3+ Asterisk servers and need to distribute calls intelligently
- Multi-tenant routing: You operate a SIP platform serving multiple customers/domains
- Geographic routing: Route calls to the nearest media server based on caller location
- WebRTC gateway: Bridge browser-based SIP.js/JsSIP clients to your traditional SIP infrastructure
- Registration proxy: Offload thousands of SIP registrations from your media server
- Topology hiding: Hide your internal network structure from external SIP peers
- Regulatory compliance: Apply call routing rules, rate limiting, and CDR collection at the edge - Parses the SIP message
- Executes the routing logic defined in kamailio.cfg
- Forwards, replies, or drops the message based on your rules - Global parameters — server settings (listen address, debug level, etc.)
- Module loading — which modules to activate
- Module parameters — settings for each module
- Route blocks — the actual SIP processing logic - reply_route — global reply processing (all responses)
- onsend_route — executed just before sending a message out
- event_route — triggered by internal events (e.g., event_route[tm:local-request]) - Let endpoints send media directly to each other (simple, but breaks with NAT)
- Use RTPEngine as a media relay (required for NAT, WebRTC, SRTP)
- Route through a B2BUA like Asterisk (which handles both signaling and media) - OS: Debian 12 (Bookworm) or Ubuntu 24.04 (Noble) — 64-bit
- RAM: 1 GB minimum, 4 GB+ recommended for production
- CPU: 1 core minimum, 4+ cores for production
- Disk: 10 GB minimum
- Network: Static public IP, ports 5060/UDP+TCP, 5061/TCP (TLS), 8443/TCP (WSS) - calculate_ha1=1: Passwords are stored in plaintext; Kamailio calculates the HA1 hash. Set to 0 if you store pre-computed HA1 hashes (more secure).
- password_column: Which column in the subscriber table contains the password.
- use_domain=0: Ignore the domain part — authenticate by username only. Set to 1 for multi-domain setups. - Signaling: Kamailio tries to send SIP replies to 192.168.1.100 — which is unreachable from the internet
- Media: The SDP body says "send RTP to 192.168.1.100:10000" — the other party cannot reach this address - STUN: Helps clients discover their public IP. Lightweight but fails with symmetric NAT.
- TURN: Full relay server. Works with all NAT types but adds latency and bandwidth cost.
- ICE: Framework that uses both STUN and TURN to find the best media path. - NAT detection on all incoming requests
- SIP signaling fix-up (Contact, Via, rport)
- RTPEngine integration for media relay
- NAT keepalive pings to registered clients
- Proper RTPEngine session cleanup on BYE
- In-dialog NAT management for re-INVITEs and replies - Kamailio restarted during an active call
- Record-Route was not added to the initial INVITE
- Dialog module lost state - calculate_ha1=1 — passwords stored in plaintext, Kamailio hashes them
- calculate_ha1=0 — passwords stored as HA1 hashes already - Verify nathelper module is loaded and nat_uac_test() is called
- Verify rtpengine module is loaded and rtpengine_manage() is called for both requests AND replies
- Verify Record-Route is added so in-dialog requests go through Kamailio
- Verify force_rport is applied for NATed clients
- Verify fix_nated_contact() is called to rewrite Contact headers
- Verify RTPEngine firewall ports are open (10000-20000 UDP)