Tools: Kamailio Fundamentals — Installation, Routing, Authentication & TLS (2026)

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

Code Block

Copy

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=yes

Command

Copy

# 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=yes

Command

Copy

# 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=yes

Command

Copy

# 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)