WikiOpenSIPS Configuration Deep Dive: Syntax, Functions & Parameters
Bố cục bài viết
OpenSIPS Configuration Deep Dive: Syntax, Functions & Parameters
Table of Contents
1. Configuration Syntax Fundamentals
Comments
Data Types
String Formatting
2. Pseudo-Variables (PVs) - Complete Reference
Request URI Components
From Header Components
To Header Components
Contact Header
Via Header
Source/Destination Network Info
Message Properties
Time Variables
Return Codes
3. Variable Types (Storage)
Script Variables ($var)
AVPs (Attribute-Value Pairs) ($avp)
Shared Variables ($shv)
Dialog Variables ($dlg_val)
Hash Table Variables ($sht)
4. Operators and Expressions
Arithmetic Operators
Comparison Operators
Logical Operators
String Matching Operators
Bitwise Operators
5. Control Structures
If-Else Statements
Switch Statement
While Loop
For-Each Loop (with AVPs)
6. Route Types
Main Request Route
Named Routes
Branch Route
Failure Route
Reply Route (onreply_route)
Local Route
Error Route
Startup Route
Timer Route
Event Route
7. Core Functions Reference
SIP Message Manipulation
Method Checking
URI Validation
Flow Control
8. Essential Module Functions
Transaction Module (tm)
Stateless Module (sl)
Record-Route Module (rr)
Registrar Module
User Location Module (usrloc)
Authentication Module (auth + auth_db)
Dialog Module
NAT Helper Module
RTPProxy Module
Dispatcher Module (Load Balancing)
Accounting Module
9. Flags
Branch Flags vs Message Flags
10. Preprocessor Directives
11. Complete Example with Comments
Quick Reference Card
Additional Resources
Trang này
Sunex
01/08/2026 · 20 phút đọc
Ngày tạo
26/07/2026
Lượt xem
50
Bản
2
Đọc
20m
OpenSIPS Configuration Deep Dive: Syntax, Functions & Parameters
A comprehensive reference for understanding every aspect of the OpenSIPS configuration language.
Table of Contents
- Configuration Syntax Fundamentals
- Pseudo-Variables (PVs)
- Variable Types (Storage)
- Operators and Expressions
- Control Structures
- Route Types
- Core Functions Reference
- Essential Module Functions
- Flags
- Preprocessor Directives
- Complete Example with Comments
1. Configuration Syntax Fundamentals
Comments
# Single line comment (shell style)
// Single line comment (C++ style)
/* Multi-line
comment (C style) */
Data Types
# Integer
children=4
log_level=3
# String (use quotes for special chars/spaces)
mpath="/usr/lib/opensips/modules/"
alias="sip.example.com"
# Boolean
debug_mode=no # or: yes, true, false, 0, 1
disable_tcp=no
# IP Address
listen=udp:192.168.1.1:5060
listen=udp:eth0:5060 # Interface name
listen=udp:*:5060 # All interfaces and port 5060
String Formatting
# Concatenation
$var(fulluri) = "sip:" + $rU + "@" + $rd;
# Embedded variables in strings
xlog("L_INFO", "Call from $fu to $tu\n");
# Escape sequences
\n # newline
\t # tab
\\ # backslash
\" # quote
2. Pseudo-Variables (PVs) - Complete Reference
Pseudo-variables provide access to SIP message content and internal data.
Request URI Components
| Variable | Description | Example Value |
|---|---|---|
$ru | Full Request-URI | sip:alice@domain.com |
$rU | Username from R-URI | alice |
$rd | Domain from R-URI | domain.com |
$rp | Port from R-URI | 5060 |
$rP | Transport from R-URI | UDP |
$ru_q | R-URI parameters | ;user=phone |
From Header Components
| Variable | Description | Example Value |
|---|---|---|
$fu | Full From URI | sip:bob@domain.com |
$fU | From username | bob |
$fd | From domain | domain.com |
$fn | From display name | Bob Smith |
$ft | From tag | a1b2c3d4 |
To Header Components
| Variable | Description | Example Value |
|---|---|---|
$tu | Full To URI | sip:alice@domain.com |
$tU | To username | alice |
$td | To domain | domain.com |
$tn | To display name | Alice |
$tt | To tag | x1y2z3 |
Contact Header
| Variable | Description |
|---|---|
$ct | Full Contact header |
$ct.fields(uri) | Contact URI |
$ct.fields(expires) | Contact expires value |
$ct.fields(q) | Contact q-value |
Via Header
| Variable | Description |
|---|---|
$vi | Via IP address |
$vp | Via port |
$vb | Via branch parameter |
Source/Destination Network Info
| Variable | Description |
|---|---|
$si | Source IP address |
$sp | Source port |
$sP | Source protocol (UDP/TCP/TLS) |
$Ri | Received IP (local) |
$Rp | Received port (local) |
$socket_in | Incoming socket |
$socket_out | Outgoing socket |
Message Properties
| Variable | Description |
|---|---|
$rm | Request method (INVITE, BYE, etc.) |
$rs | Response status code |
$rr | Response reason phrase |
$mb | Message body |
$ml | Message length |
$cs | CSeq number |
$ci | Call-ID |
$ua | User-Agent header |
Time Variables
| Variable | Description |
|---|---|
$Ts | Unix timestamp (seconds) |
$Tf | Formatted timestamp |
$TS | Timestamp with microseconds |
$time(format) | Custom time format |
# Time format examples
$time(%Y-%m-%d) # 2024-01-15
$time(%H:%M:%S) # 14:30:45
$time(%A) # Monday
Return Codes
| Variable | Description |
|---|---|
$rc | Return code of last function |
$retcode | Same as $rc |
3. Variable Types (Storage)
Script Variables ($var)
Temporary variables, exist only during script execution:
$var(counter) = 0;
$var(name) = "John";
$var(uri) = $fu;
# Increment
$var(counter) = $var(counter) + 1;
# String operations
$var(full) = $var(first) + " " + $var(last);
AVPs (Attribute-Value Pairs) ($avp)
Transaction-persistent variables:
# Named AVPs
$avp(caller_id) = $fU;
$avp(is_registered) = 1;
# Can hold multiple values (stack)
$avp(routes) = "gw1.example.com";
$avp(routes) = "gw2.example.com"; # Pushed on top
# Access specific index
$(avp(routes)[0]) # Top value (gw2)
$(avp(routes)[1]) # Second value (gw1)
$(avp(routes)[*]) # All values
# Delete AVP
$avp(caller_id) = $null;
Shared Variables ($shv)
Global variables shared across all processes:
# Define in modparam
modparam("cfgutils", "shvset", "call_count:i:0")
modparam("cfgutils", "shvset", "debug_mode:s:off")
# Use in script
$shv(call_count) = $shv(call_count) + 1;
if ($shv(debug_mode) == "on") {
xlog("L_DBG", "Debug message\n");
}
Dialog Variables ($dlg_val)
Persist throughout the entire dialog (call):
# Set in INVITE processing
$dlg_val(account_code) = "ACC123";
$dlg_val(start_time) = $Ts;
# Access in BYE processing
$var(duration) = $Ts - $dlg_val(start_time);
Hash Table Variables ($sht)
In-memory key-value storage:
# Define hash table
modparam("htable", "htable", "ipban=>size=8;autoexpire=300;")
modparam("htable", "htable", "cache=>size=10;")
# Use in script
$sht(ipban=>$si) = 1; # Ban IP
$sht(cache=>$fU::profile) = "premium"; # Cache data
# Check existence
if ($sht(ipban=>$si) != $null) {
sl_send_reply(403, "Forbidden");
exit;
}
# Delete
$sht(ipban=>$si) = $null;
# Iteration
sht_iterator_start("iter", "ipban");
while (sht_iterator_next("iter")) {
xlog("Key: $shtitkey(iter) Value: $shtitval(iter)\n");
}
sht_iterator_end("iter");
4. Operators and Expressions
Arithmetic Operators
$var(a) = 10 + 5; # Addition: 15
$var(b) = 10 - 5; # Subtraction: 5
$var(c) = 10 * 5; # Multiplication: 50
$var(d) = 10 / 3; # Division: 3
$var(e) = 10 mod 3; # Modulo: 1
Comparison Operators
# Numeric comparison
$var(x) == 5 # Equal
$var(x) != 5 # Not equal
$var(x) > 5 # Greater than
$var(x) < 5 # Less than
$var(x) >= 5 # Greater or equal
$var(x) <= 5 # Less or equal
# String comparison
$fU == "alice" # Exact match
$fU != "alice" # Not equal
Logical Operators
# AND
if (is_method("INVITE") && is_present_hf("Privacy")) { }
# OR
if (is_method("REGISTER") || is_method("SUBSCRIBE")) { }
# NOT
if (!is_method("ACK")) { }
# Combined
if ((is_method("INVITE") || is_method("UPDATE")) && has_totag()) { }
String Matching Operators
# Regular expression match
if ($fU =~ "^[0-9]+$") {
# Username is all digits
}
# Negated regex match
if ($fU !~ "^test") {
# Username doesn't start with "test"
}
# Substring match
if ($ua =~ ".*Oby.*") {
# User-Agent contains "Oby"
}
Bitwise Operators
$var(flags) = $var(flags) | 2; # OR (set bit)
$var(flags) = $var(flags) & ~2; # AND NOT (clear bit)
$var(flags) = $var(flags) ^ 2; # XOR (toggle bit)
5. Control Structures
If-Else Statements
if (is_method("INVITE")) {
# Handle INVITE
} else if (is_method("REGISTER")) {
# Handle REGISTER
} else {
# Handle other methods
}
Switch Statement
switch ($rm) {
case "INVITE":
route(handle_invite);
break;
case "REGISTER":
route(handle_register);
break;
case "BYE":
case "CANCEL":
# Both BYE and CANCEL
route(handle_end);
break;
default:
route(handle_other);
}
While Loop
$var(i) = 0;
while ($var(i) < 10) {
xlog("L_INFO", "Iteration $var(i)\n");
$var(i) = $var(i) + 1;
}
For-Each Loop (with AVPs)
# Iterate over multi-value AVP
$var(i) = 0;
while ($(avp(routes)[$var(i)]) != $null) {
xlog("Route: $(avp(routes)[$var(i)])\n");
$var(i) = $var(i) + 1;
}
6. Route Types
Main Request Route
route {
# Entry point for all SIP requests
# Called for every incoming request
if (!mf_process_maxfwd_header(10)) {
sl_send_reply(483, "Too Many Hops");
exit;
}
# ... routing logic
}
Named Routes
# Definition
route[auth] {
if (!www_authorize("realm", "subscriber")) {
www_challenge("realm", 0);
exit;
}
consume_credentials();
}
route[relay] {
if (!t_relay()) {
sl_reply_error();
}
exit;
}
# Calling named routes
route(auth);
route(relay);
# With return value
route[check_user] {
if ($fU == "admin") {
return(1);
}
return(-1);
}
# Check return value
if (route(check_user) == 1) {
# User is admin
}
Branch Route
Called before each branch is sent:
branch_route[outbound] {
# Modify each branch before sending
xlog("L_INFO", "Branching to $ru\n");
# Set different source socket per branch
if ($ru =~ ".*external.com.*") {
$socket_out = "udp:203.0.113.1:5060";
}
}
# Activate in routing logic
t_on_branch("outbound");
Failure Route
Called when transaction fails:
failure_route[missed] {
# Check if cancelled by caller
if (t_was_cancelled()) {
exit;
}
# Check for busy
if (t_check_status("486")) {
# Forward to voicemail
$ru = "sip:voicemail@" + $rd;
t_relay();
exit;
}
# No answer (408) or unavailable (480)
if (t_check_status("408|480")) {
# Try backup destination
$ru = "sip:" + $rU + "@backup.domain.com";
t_relay();
exit;
}
}
# Activate in routing logic
t_on_failure("missed");
Reply Route (onreply_route)
Process SIP responses:
onreply_route {
# Global reply route - processes all replies
xlog("L_INFO", "Reply: $rs $rr\n");
# NAT handling for replies
if (nat_uac_test(1)) {
fix_nated_contact();
}
}
onreply_route[handle_nat] {
# Named reply route
if (has_body("application/sdp")) {
rtpproxy_answer("ro");
}
}
# Activate
t_on_reply("handle_nat");
Local Route
Process locally generated requests:
local_route {
# Called for requests generated by OpenSIPS itself
# (e.g., CANCELs, in-dialog requests from B2BUA)
if (is_method("CANCEL")) {
# Add custom headers to CANCEL
append_hf("X-Cancel-Reason: Caller Abort\r\n");
}
}
Error Route
Handle script errors:
error_route {
xlog("L_ERR", "Error: $err.class - $err.level - $err.info\n");
if ($err.class == "parse") {
sl_send_reply(400, "Bad Request - Parse Error");
}
exit;
}
Startup Route
Execute once at startup:
startup_route {
# Initialize shared variables
$shv(total_calls) = 0;
# Cache data
avp_db_query("SELECT value FROM config WHERE key='max_calls'",
"$avp(max_calls)");
$shv(max_calls) = $avp(max_calls);
xlog("L_NOTICE", "OpenSIPS started, max_calls=$shv(max_calls)\n");
}
Timer Route
Periodic execution:
timer_route[stats_report, 60] {
# Runs every 60 seconds
xlog("L_INFO", "Active calls: $shv(active_calls)\n");
# Cleanup expired entries
sht_delete("cache", "expired");
}
timer_route[keepalive, 30] {
# Send OPTIONS pings every 30 seconds
route(send_keepalives);
}
Event Route
React to internal events:
event_route[E_PIKE_BLOCKED] {
# Called when pike module blocks an IP
xlog("L_WARN", "IP blocked: $avp(source)\n");
$sht(banned=>$avp(source)) = 1;
}
event_route[E_DLG_STATE_CHANGED] {
# Dialog state change
if ($dlg_val(state) == 4) { # Confirmed
$shv(active_calls) = $shv(active_calls) + 1;
}
}
event_route[E_SCRIPT_SHUTDOWN] {
# OpenSIPS shutting down
xlog("L_NOTICE", "Shutting down gracefully\n");
}
7. Core Functions Reference
SIP Message Manipulation
# Append header
append_hf("X-Custom: value\r\n");
append_hf("X-Caller: $fU\r\n", "Call-ID"); # After Call-ID
# Insert header at top
insert_hf("P-Hint: first\r\n");
# Remove header
remove_hf("User-Agent");
remove_hf_re("^X-.*"); # Regex match
# Modify header
replace_hf("Subject", "New Subject");
# Append to body
append_body_part("text/plain", "Additional content");
# URI manipulation
rewriteuri("sip:newuser@newdomain.com");
# Or equivalently:
$ru = "sip:newuser@newdomain.com";
# Rewrite user only
rewriteuser("newuser");
# Or:
$rU = "newuser";
# Rewrite host
rewritehost("newdomain.com");
# Or:
$rd = "newdomain.com";
# Rewrite port
rewriteport(5080);
# Or:
$rp = 5080;
Method Checking
is_method("INVITE") # Single method
is_method("INVITE|BYE|CANCEL") # Multiple methods
# Alternative using $rm
if ($rm == "INVITE") { }
URI Validation
# Check if URI is valid SIP URI
if (is_uri("$fu")) {
xlog("Valid From URI\n");
}
# Check specific URI types
is_sip_uri("$ru") # sip: URI
is_tel_uri("$ru") # tel: URI
# Check if request URI targets local domain
if (is_myself("$rd")) {
route(local);
} else {
route(relay);
}
Flow Control
exit; # Stop script execution
drop; # Drop message silently (no reply)
return(1); # Return from route with value
break; # Break from switch/while
8. Essential Module Functions
Transaction Module (tm)
loadmodule "tm.so"
# Parameters
modparam("tm", "fr_timeout", 5) # Final response timeout (seconds)
modparam("tm", "fr_inv_timeout", 120) # INVITE final response timeout
modparam("tm", "wt_timer", 5) # Wait timer for ACK
modparam("tm", "delete_timer", 2) # Transaction deletion delay
modparam("tm", "T1_timer", 500) # Initial retransmission (ms)
modparam("tm", "T2_timer", 4000) # Maximum retransmission (ms)
modparam("tm", "restart_fr_on_each_reply", 1) # Reset timeout on provisional
# Functions
t_relay(); # Stateful relay
t_relay("udp:192.168.1.1:5060"); # Relay to specific destination
t_newtran(); # Create new transaction
t_reply(200, "OK"); # Send reply statelessly
t_send_reply(200, "OK"); # Send reply statefully
t_check_trans(); # Check if transaction exists
t_check_status("486|600"); # Check response status
t_was_cancelled(); # In failure_route
t_on_failure("route_name"); # Set failure route
t_on_reply("route_name"); # Set reply route
t_on_branch("route_name"); # Set branch route
t_set_fr(10, 180); # Set timeout for this transaction
# (final_response, invite_final)
Stateless Module (sl)
loadmodule "sl.so"
# Functions
sl_send_reply(200, "OK"); # Send stateless reply
sl_reply_error(); # Send error based on internal error
Record-Route Module (rr)
loadmodule "rr.so"
# Parameters
modparam("rr", "enable_full_lr", 1) # Add full ";lr" parameter
modparam("rr", "append_fromtag", 1) # Add from-tag for dialog matching
modparam("rr", "enable_double_rr", 1) # Double Record-Route (for NAT)
modparam("rr", "add_username", 1) # Add username to Record-Route
# Functions
record_route(); # Add Record-Route header
record_route_preset("sip:1.2.3.4:5060"); # Add specific Record-Route
loose_route(); # Route based on Route headers
# Returns true if routes exist
is_direction("downstream"); # Check direction in dialog
is_direction("upstream");
add_rr_param(";key=value"); # Add parameter to RR
$rr_param = ";nat=yes"; # Alternative
Registrar Module
loadmodule "registrar.so"
# Parameters
modparam("registrar", "max_expires", 3600) # Maximum registration (sec)
modparam("registrar", "min_expires", 60) # Minimum registration
modparam("registrar", "default_expires", 600) # Default if not specified
modparam("registrar", "max_contacts", 10) # Max bindings per AOR
modparam("registrar", "retry_after", 60) # Retry-After on 5xx
# Functions
save("location"); # Save registration
save("location", "memory-only"); # Don't persist to DB
lookup("location"); # Lookup and rewrite R-URI
lookup("location", "method-filtering"); # Filter by method support
registered("location"); # Check if AOR registered
registered("location", "$tu"); # Check specific URI
# Get registration details
$avp(contacts) = $ulc(location=>contact);
$avp(expires) = $ulc(location=>expires);
User Location Module (usrloc)
loadmodule "usrloc.so"
# Parameters
modparam("usrloc", "db_mode", 2)
# 0 = No DB (memory only)
# 1 = Write-through (slow, safe)
# 2 = Write-back (fast, periodic flush)
# 3 = DB only (no memory cache)
modparam("usrloc", "db_url", "mysql://user:pass@host/db")
modparam("usrloc", "timer_interval", 60) # Cleanup interval
modparam("usrloc", "use_domain", 1) # Domain in AOR
modparam("usrloc", "nat_bflag", "NAT") # NAT branch flag
modparam("usrloc", "matching_mode", 0)
# 0 = Contact-only matching
# 1 = Contact + Call-ID matching
Authentication Module (auth + auth_db)
loadmodule "auth.so"
loadmodule "auth_db.so"
# Parameters
modparam("auth_db", "db_url", "mysql://user:pass@host/db")
modparam("auth_db", "calculate_ha1", 1) # Calculate HA1 from password
modparam("auth_db", "password_column", "password")
modparam("auth_db", "use_domain", 1) # Domain in lookup
modparam("auth", "nonce_expire", 30) # Nonce validity (sec)
modparam("auth", "rpid_avp", "$avp(rpid)") # Remote-Party-ID AVP
# Functions - Request authentication
www_authorize("realm", "subscriber"); # Check Authorization header
www_challenge("realm", 0); # Send 401 challenge
# Second param: qop (0=no qop, 1=auth)
# Functions - Proxy authentication
proxy_authorize("realm", "subscriber"); # Check Proxy-Authorization
proxy_challenge("realm", 0); # Send 407 challenge
consume_credentials(); # Remove auth headers
check_from(); # Verify From matches auth
check_to(); # Verify To matches auth
# Get authenticated user
$au # Auth username
$aU # Auth username (alias)
$ar # Auth realm
Dialog Module
loadmodule "dialog.so"
# Parameters
modparam("dialog", "db_mode", 1)
# 0 = No DB
# 1 = Real-time write
# 2 = Delayed write
modparam("dialog", "db_url", "mysql://user:pass@host/db")
modparam("dialog", "dlg_match_mode", 1)
# 0 = Match on DID only
# 1 = Match on DID + From tag
# 2 = Match on DID + From tag + CSeq
modparam("dialog", "default_timeout", 43200) # 12 hours
modparam("dialog", "profiles_with_value", "caller")
modparam("dialog", "profiles_no_value", "total")
# Functions
create_dialog(); # Create dialog tracking
create_dialog("PF"); # P=ping, F=fail on bye
# Set dialog flags
dlg_flag("DLG_FLAG_BYEONTIMEOUT");
# Dialog variables (persist for call duration)
$dlg_val(key) = "value";
# Set timeout
dlg_set_timeout(3600); # 1 hour
dlg_set_timeout(3600, "$fU", "$tU"); # With IDs
# Dialog profiles (for counting/limiting)
set_dlg_profile("caller", "$fU"); # Add to profile with value
set_dlg_profile("total"); # Add to profile without value
if (get_profile_size("caller", "$fU", "$var(count)")) {
if ($var(count) > 5) {
sl_send_reply(486, "Too Many Calls");
exit;
}
}
# Check if in profile
is_in_profile("caller", "$fU");
# Get dialog info
$dlg(id) # Dialog ID
$dlg(state) # Dialog state
$dlg(timeout) # Current timeout
NAT Helper Module
loadmodule "nathelper.so"
# Parameters
modparam("nathelper", "natping_interval", 30) # Keep-alive interval
modparam("nathelper", "ping_nated_only", 1) # Only ping NAT clients
modparam("nathelper", "sipping_bflag", "SIP_PING") # Use SIP OPTIONS
modparam("nathelper", "received_avp", "$avp(rcv)")
# Functions
nat_uac_test(flags);
# Flag bits:
# 1 = Private IP in Contact
# 2 = Private IP in Via
# 4 = Private IP in SDP
# 8 = Via != source IP
# 16 = Port in Via != source port
# 32 = Contact != source IP
# Common usage:
if (nat_uac_test(19)) { # 1+2+16
setflag(NAT);
force_rport();
fix_nated_contact();
}
force_rport(); # Add rport to Via
add_rcv_param(); # Add received parameter
fix_nated_contact(); # Fix Contact header
fix_nated_sdp(flags);
# Flags:
# 1 = Add direction=active
# 2 = Rewrite IP in c= line
# 4 = Add direction=active to audio
# 8 = Add direction=active to video
fix_nated_register(); # Fix Contact in REGISTER
RTPProxy Module
loadmodule "rtpproxy.so"
# Parameters
modparam("rtpproxy", "rtpproxy_sock", "udp:127.0.0.1:7722")
modparam("rtpproxy", "rtpproxy_sock", "unix:/var/run/rtpproxy.sock")
modparam("rtpproxy", "rtpproxy_timeout", 1000) # Timeout (ms)
# Multiple proxies with weights
modparam("rtpproxy", "rtpproxy_sock",
"udp:rtp1.example.com:7722=2 udp:rtp2.example.com:7722=1")
# Functions
rtpproxy_offer(flags);
rtpproxy_answer(flags);
# Flags:
# r = Replace SDP
# o = Offer (vs answer)
# a = Asymmetric codecs
# l = Lookup (rewrite only)
# i = Internal network
# e = External network
# f = Force (ignore if already proxied)
# w = Symmetric RTP
# Common usage in INVITE route
if (isflagset(NAT)) {
rtpproxy_offer("ro");
}
# In onreply_route
if (isflagset(NAT) && has_body("application/sdp")) {
rtpproxy_answer("ro");
}
# Stop proxying
rtpproxy_unforce();
# Check stream
rtpproxy_stream2uac("ringtone", 10); # Play tone to caller
rtpproxy_stream2uas("mohfile", -1); # MOH to callee
rtpproxy_stop_stream2uac();
Dispatcher Module (Load Balancing)
loadmodule "dispatcher.so"
# Parameters
modparam("dispatcher", "db_url", "mysql://user:pass@host/db")
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_ping_interval", 10) # Probe every 10 sec
modparam("dispatcher", "ds_probing_mode", 1) # 1=probe inactive
modparam("dispatcher", "ds_probing_threshold", 3) # Failures before inactive
modparam("dispatcher", "options_reply_codes", "200,501,403")
# Functions
ds_select_dst(set, algorithm);
# Algorithms:
# 0 = Hash over Call-ID
# 1 = Hash over From URI
# 2 = Hash over To URI
# 3 = Hash over R-URI
# 4 = Round-robin
# 5 = Hash over auth username
# 6 = Random
# 7 = Hash over PV
# 8 = Priority-based
# 9 = Weight-based
ds_select_dst(1, 4); # Set 1, round-robin
# With failover
ds_select_dst(1, 4, 2); # 2 destinations
# Select and set socket
ds_select_domain(1, 4);
# Next destination on failure
ds_next_dst(); # In failure_route
# Manual state control
ds_mark_dst("i"); # Mark inactive
ds_mark_dst("p"); # Mark probing
ds_mark_dst("a"); # Mark active
# Check if destination active
ds_is_in_list("$rd", "$rp", 1); # Check host:port in set
# Get destination count
$var(count) = ds_count(1, "a"); # Count active in set 1
Accounting Module
loadmodule "acc.so"
# Parameters
modparam("acc", "db_url", "mysql://user:pass@host/db")
modparam("acc", "db_flag", "ACC_DB")
modparam("acc", "db_missed_flag", "ACC_MISSED")
modparam("acc", "cdr_flag", "CDR")
modparam("acc", "detect_direction", 1)
# Extra fields to log
modparam("acc", "db_extra",
"src_user=$fU;src_domain=$fd;dst_user=$tU;dst_domain=$td")
# CDR extra columns
modparam("acc", "cdr_extra",
"src=$dlg_val(caller);dst=$dlg_val(callee);billsec=$dlg_val(duration)")
# In routing logic
route {
if (is_method("INVITE") && !has_totag()) {
setflag(ACC_DB);
setflag(CDR);
}
}
# Manual accounting
do_accounting("db", "missed");
9. Flags
Branch Flags vs Message Flags
# Message flags - persist for the message
setflag(FLAG_NAME);
resetflag(FLAG_NAME);
if (isflagset(FLAG_NAME)) { }
# Branch flags - per-branch
setbflag(BFLAG_NAME);
resetbflag(BFLAG_NAME);
if (isbflagset(BFLAG_NAME)) { }
# Define flags at top of config
#!define FLAG_ACC 1
#!define FLAG_NAT 2
#!define BFLAG_NATB 6
# Use with number
setflag(1);
# Use with name (if defined in module)
modparam("usrloc", "nat_bflag", "NAT")
# Then use: isbflagset("NAT")
10. Preprocessor Directives
# Define constant
#!define WITH_NAT
#!define MAX_CALLS 100
#!define DB_URL "mysql://user:pass@localhost/opensips"
# Conditional compilation
#!ifdef WITH_NAT
loadmodule "nathelper.so"
#!endif
#!ifndef PRODUCTION
log_level=4
#!else
log_level=2
#!endif
# Include external file
#!include_file "routes/auth.cfg"
#!include_file "routes/presence.cfg"
# Define with value
#!subst "/DB_HOST/192.168.1.1/"
modparam("usrloc", "db_url", "mysql://user:pass@DB_HOST/db")
# Import environment variable
#!substdef "!DBPASS!$env(MYSQL_PASSWORD)!"
11. Complete Example with Comments
#
# OpenSIPS Comprehensive Configuration
#
####### Global Parameters #########
#!define WITH_NAT
#!define WITH_AUTH
#!define WITH_MYSQL
#!define DBURL "mysql://opensips:password@localhost/opensips"
# Logging
debug_mode=no
log_level=3
log_facility=LOG_LOCAL0
log_stderror=no
# Process settings
children=4
auto_aliases=no
# Network
listen=udp:eth0:5060
listen=tcp:eth0:5060
advertised_address="sip.example.com"
advertised_port=5060
# TCP settings
tcp_children=4
tcp_connect_timeout=3
tcp_max_connections=2048
####### Modules Section ########
mpath="/usr/lib/x86_64-linux-gnu/opensips/modules/"
# Core modules
loadmodule "signaling.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "uri.so"
loadmodule "textops.so"
loadmodule "sipmsgops.so"
# Database
#!ifdef WITH_MYSQL
loadmodule "db_mysql.so"
#!endif
# User location
loadmodule "usrloc.so"
loadmodule "registrar.so"
# Authentication
#!ifdef WITH_AUTH
loadmodule "auth.so"
loadmodule "auth_db.so"
#!endif
# NAT handling
#!ifdef WITH_NAT
loadmodule "nathelper.so"
loadmodule "rtpproxy.so"
#!endif
# Dialog and accounting
loadmodule "dialog.so"
loadmodule "acc.so"
# Utilities
loadmodule "cfgutils.so"
loadmodule "exec.so"
####### Module Parameters ########
# Transaction
modparam("tm", "fr_timeout", 5)
modparam("tm", "fr_inv_timeout", 120)
# Record-Route
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1)
# User Location
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "db_url", DBURL)
modparam("usrloc", "nat_bflag", "NAT")
# Registrar
modparam("registrar", "max_expires", 3600)
modparam("registrar", "max_contacts", 5)
# Authentication
#!ifdef WITH_AUTH
modparam("auth_db", "db_url", DBURL)
modparam("auth_db", "calculate_ha1", 1)
modparam("auth_db", "password_column", "password")
#!endif
# NAT
#!ifdef WITH_NAT
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("rtpproxy", "rtpproxy_sock", "udp:127.0.0.1:7722")
#!endif
# Dialog
modparam("dialog", "db_mode", 1)
modparam("dialog", "db_url", DBURL)
# Accounting
modparam("acc", "db_url", DBURL)
modparam("acc", "db_flag", "ACC")
modparam("acc", "cdr_flag", "CDR")
####### Routing Logic ########
# Main request route
route {
# -------- Initial Checks --------
# Max-Forwards check
if (!mf_process_maxfwd_header(10)) {
sl_send_reply(483, "Too Many Hops");
exit;
}
# Sanity check
if ($ml > 65535) {
sl_send_reply(513, "Message Too Large");
exit;
}
# -------- CANCEL Handling --------
if (is_method("CANCEL")) {
if (t_check_trans()) {
t_relay();
}
exit;
}
# Absorb retransmissions
t_check_trans();
# -------- NAT Detection --------
#!ifdef WITH_NAT
route(nat_detect);
#!endif
# -------- In-Dialog Requests --------
if (has_totag()) {
if (loose_route()) {
#!ifdef WITH_NAT
if (isflagset(NAT)) {
add_rr_param(";nat=yes");
}
#!endif
if (is_method("INVITE|UPDATE")) {
record_route();
}
if (is_method("BYE")) {
# Account end of call
setflag(ACC);
}
route(relay);
exit;
}
if (is_method("ACK")) {
if (t_check_trans()) {
t_relay();
exit;
}
exit;
}
sl_send_reply(404, "Not Found");
exit;
}
# -------- REGISTER Handling --------
if (is_method("REGISTER")) {
#!ifdef WITH_AUTH
if (!www_authorize("example.com", "subscriber")) {
www_challenge("example.com", 0);
exit;
}
if (!check_from()) {
sl_send_reply(403, "Forbidden - From Mismatch");
exit;
}
consume_credentials();
#!endif
if (!save("location")) {
sl_reply_error();
}
exit;
}
# -------- Authentication --------
#!ifdef WITH_AUTH
if (is_method("INVITE|SUBSCRIBE|MESSAGE")) {
if (!www_authorize("example.com", "subscriber")) {
www_challenge("example.com", 0);
exit;
}
if (!check_from()) {
sl_send_reply(403, "Forbidden - From Mismatch");
exit;
}
consume_credentials();
}
#!endif
# -------- INVITE Processing --------
if (is_method("INVITE")) {
# Create dialog for call tracking
create_dialog("PF");
# Set accounting flags
setflag(ACC);
setflag(CDR);
# Store call info
$dlg_val(caller) = $fU;
$dlg_val(callee) = $tU;
$dlg_val(start) = $Ts;
# Record-Route
record_route();
# Set failure route
t_on_failure("missed_call");
}
# -------- Location Lookup --------
if (!lookup("location")) {
switch ($retcode) {
case -1: # Not found
sl_send_reply(404, "Not Found");
exit;
case -2: # Method not supported
sl_send_reply(405, "Method Not Allowed");
exit;
}
}
# -------- Relay --------
route(relay);
}
# NAT Detection Route
route[nat_detect] {
if (nat_uac_test(19)) {
xlog("L_INFO", "NAT detected for $si:$sp\n");
setflag(NAT);
force_rport();
if (is_method("REGISTER")) {
fix_nated_register();
} else {
fix_nated_contact();
}
}
}
# Relay Route
route[relay] {
#!ifdef WITH_NAT
if (isflagset(NAT)) {
rtpproxy_offer("ro");
}
#!endif
# Set reply route
t_on_reply("handle_reply");
if (!t_relay()) {
sl_reply_error();
}
exit;
}
# Reply Route
onreply_route[handle_reply] {
#!ifdef WITH_NAT
if (nat_uac_test(1)) {
fix_nated_contact();
}
if (isflagset(NAT) && has_body("application/sdp")) {
rtpproxy_answer("ro");
}
#!endif
}
# Failure Route
failure_route[missed_call] {
if (t_was_cancelled()) {
exit;
}
# Log missed call
xlog("L_INFO", "Missed call from $fU to $tU\n");
# Busy - try voicemail
if (t_check_status("486|600")) {
$ru = "sip:" + $tU + "@voicemail.example.com";
t_relay();
exit;
}
# No answer - try voicemail
if (t_check_status("408|480")) {
$ru = "sip:" + $tU + "@voicemail.example.com";
t_relay();
exit;
}
}
# Startup route
startup_route {
xlog("L_NOTICE", "OpenSIPS started successfully\n");
}
Quick Reference Card
| Category | Syntax | Example |
|---|---|---|
| Variables | $var(name) | $var(count) = 0 |
| AVPs | $avp(name) | $avp(user) = $fU |
| Shared | $shv(name) | $shv(total) = 100 |
| Dialog | $dlg_val(key) | $dlg_val(start) = $Ts |
| Hash | $sht(table=>key) | $sht(cache=>$fU) |
| Flags | setflag(N) | setflag(ACC) |
| Routes | route(name) | route(auth) |
| Match | =~ | $fU =~ "^[0-9]+$" |