Skip to content
LIVE FEED

WikiOpenSIPS Configuration Deep Dive: Syntax, Functions & Parameters

Bố cục bài viết

Trang này

Sunex 01/08/2026 · 20 phút đọc

Ngày tạo

26/07/2026

Lượt xem

51

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

  1. Configuration Syntax Fundamentals
  2. Pseudo-Variables (PVs)
  3. Variable Types (Storage)
  4. Operators and Expressions
  5. Control Structures
  6. Route Types
  7. Core Functions Reference
  8. Essential Module Functions
  9. Flags
  10. Preprocessor Directives
  11. 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

VariableDescriptionExample Value
$ruFull Request-URIsip:alice@domain.com
$rUUsername from R-URIalice
$rdDomain from R-URIdomain.com
$rpPort from R-URI5060
$rPTransport from R-URIUDP
$ru_qR-URI parameters;user=phone

From Header Components

VariableDescriptionExample Value
$fuFull From URIsip:bob@domain.com
$fUFrom usernamebob
$fdFrom domaindomain.com
$fnFrom display nameBob Smith
$ftFrom taga1b2c3d4

To Header Components

VariableDescriptionExample Value
$tuFull To URIsip:alice@domain.com
$tUTo usernamealice
$tdTo domaindomain.com
$tnTo display nameAlice
$ttTo tagx1y2z3

Contact Header

VariableDescription
$ctFull Contact header
$ct.fields(uri)Contact URI
$ct.fields(expires)Contact expires value
$ct.fields(q)Contact q-value

Via Header

VariableDescription
$viVia IP address
$vpVia port
$vbVia branch parameter

Source/Destination Network Info

VariableDescription
$siSource IP address
$spSource port
$sPSource protocol (UDP/TCP/TLS)
$RiReceived IP (local)
$RpReceived port (local)
$socket_inIncoming socket
$socket_outOutgoing socket

Message Properties

VariableDescription
$rmRequest method (INVITE, BYE, etc.)
$rsResponse status code
$rrResponse reason phrase
$mbMessage body
$mlMessage length
$csCSeq number
$ciCall-ID
$uaUser-Agent header

Time Variables

VariableDescription
$TsUnix timestamp (seconds)
$TfFormatted timestamp
$TSTimestamp 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

VariableDescription
$rcReturn code of last function
$retcodeSame 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

CategorySyntaxExample
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)
Flagssetflag(N)setflag(ACC)
Routesroute(name)route(auth)
Match=~$fU =~ "^[0-9]+$"

Additional Resources

Contact
Facebook
TOP