[OpenSIPS-Users] [OpenSIPS] sample script that works with cdrtools, freeradius, nat, drouting

Jinsong Hu jinsong_hu at hotmail.com
Mon May 11 08:31:21 CEST 2009


Hi, There:
  It looks ag-projects is maintaining the cdrtools, media proxy. but I 
searched around and didn't find anywhere there is a script that supports all 
the needed feature: cdrtools, mediaproxy, nat_traversal, and drouting. so 
now I'm trying to be a little brave and post my script that includes all 
above. this script doesn't handle instance message, but only voice calls. 
can any body spot problems with this script ?
  The goal of the script is to let locally registered user to use gateway to 
make outgoing call, and receive incoming call. the numbering plan is for US. 
free radius should have good authenticaing and  accounting for different 
messages, and some special DID are mapped to several numbers and routed to 
asterisk. Hopefully this script will be useful for a general VOIP carrier.
  I try to paste the document to be comment. Hopefully, by going through 
this exercise, we can get a good starting script for people to use as a 
model starting script.


Jimmy



#######################################################################
#
# $Id: opensips.cfg,v 1.13 2009/05/11 06:06:00 jinsong Exp $
#
# OpenSIPS basic configuration script
#     by Anca Vamanu <anca at voice-system.ro>
#
# Please refer to the Core CookBook at 
http://www.opensips.org/dokuwiki/doku.php
# for a explanation of possible statements, functions and parameters.
#
#INVITE :Invites a user to a call
#ACK : Acknowledgement is used to facilitate reliable message exchange for 
INVITEs.
#BYE :Terminates a connection between users
#CANCEL :Terminates a request, or search, for a user. It is used if a client 
sends an INVITE and then changes its decision to call the recipient.
#OPTIONS :Solicits information about a server's capabilities.
#REGISTER :Registers a user's current location
#INFO :Used for mid-session signaling
#MESSAGE : IMS send message
#SUBSCRIBE : IMS presence subscribe message
#PUBLISH: IMS publish message

#1xx: Provisional -- request received, continuing to process the request;
#2xx: Success -- the action was successfully received, understood, and 
accepted;
#3xx: Redirection -- further action needs to be taken in order to complete 
the request;
#4xx: Client Error -- the request contains bad syntax or cannot be fulfilled 
at this server;
#5xx: Server Error -- the server failed to fulfill an apparently valid 
request;
#6xx: Global Failure -- the request cannot be fulfilled at any server.

#This function sets the value of the flag given as parameter to 1 (true). 
The value of the parameter must be an integer between 0 and 31.





####### Global Parameters #########

debug=3
log_stderror=no
log_facility=LOG_LOCAL0

fork=yes
children=4

/* uncomment the following lines to enable debugging */
#debug=6
#fork=no
#log_stderror=yes

/* uncomment the next line to disable TCP (default on) */
#disable_tcp=yes

/* uncomment the next line to enable the auto temporary blacklisting of
   not available destinations (default disabled) */
#disable_dns_blacklist=no

/* uncomment the next line to enable IPv6 lookup after IPv4 dns
   lookup failures (default disabled) */
#dns_try_ipv6=yes

#disable dns to scale
dns=no
rev_dns=no

/* uncomment the next line to disable the auto discovery of local aliases
   based on revers DNS on IPs (default on) */
#auto_aliases=no
alias=machinename.somedomain.com



/* uncomment the following lines to enable TLS support  (default off) */
#disable_tls = no
#listen = tls:your_IP:5061
#tls_verify_server = 1
#tls_verify_client = 1
#tls_require_client_certificate = 0
#tls_method = TLSv1
#tls_certificate = "/etc/opensips/tls/user/user-cert.pem"
#tls_private_key = "/etc/opensips/tls/user/user-privkey.pem"
#tls_ca_list = "/etc/opensips/tls/user/user-calist.pem"


port=5060

/* uncomment and configure the following line if you want opensips to
   bind on a specific interface/port/proto (default bind on all available) 
*/
#listen=udp:192.168.1.2:5060


####### Modules Section ########

#set module path
mpath="/usr/lib/opensips/modules/"

/* uncomment next line for MySQL DB support */
loadmodule "db_mysql.so"
loadmodule "mi_fifo.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "signaling.so"
loadmodule "registrar.so"
loadmodule "textops.so"
loadmodule "uri_db.so"
loadmodule "uri.so"
loadmodule "xlog.so"
loadmodule "acc.so"
/* uncomment next lines for MySQL based authentication support
   NOTE: a DB (like db_mysql) module must be also loaded */
loadmodule "auth.so"
loadmodule "auth_db.so"
/* uncomment next line for aliases support
   NOTE: a DB (like db_mysql) module must be also loaded */
loadmodule "alias_db.so"
/* uncomment next line for multi-domain support
   NOTE: a DB (like db_mysql) module must be also loaded
   NOTE: be sure and enable multi-domain support in all used modules
         (see "multi-module params" section ) */
loadmodule "domain.so"
/* uncomment the next two lines for presence server support
   NOTE: a DB (like db_mysql) module must be also loaded */
#loadmodule "presence.so"
#loadmodule "presence_xml.so"

#loadmodule "carrierroute.so"
loadmodule "drouting.so"
loadmodule "siptrace.so"
loadmodule "pike.so"
loadmodule "ratelimit.so"

loadmodule "auth_radius.so"
loadmodule "avp_radius.so"
#loadmodule "uri_radius.so"
loadmodule "group_radius.so"

loadmodule "dispatcher.so"

loadmodule "dialog.so"
loadmodule "mediaproxy.so"
#loadmodule "nathelper.so"
loadmodule "nat_traversal.so"

# ----------------- setting module-specific parameters ---------------


# ----- mi_fifo params -----
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")


# ----- rr params -----
# add value to ;lr param to cope with most of the UAs
modparam("rr", "enable_full_lr", 1)


# ----- rr params -----
#modparam("registrar", "method_filtering", 1)
/* uncomment the next line to disable parallel forking via location */
# modparam("registrar", "append_branches", 0)
/* uncomment the next line not to allow more than 10 contacts per AOR */
modparam("registrar", "max_contacts", 10)


# ----- uri_db params -----
/* by default we disable the DB support in the module as we do not need it
   in this configuration */
modparam("uri_db", "use_uri_table", 0)
modparam("uri_db", "db_url", "")


# ----- acc params -----
/* what sepcial events should be accounted ? */
#modparam("acc", "early_media", 1)
#modparam("acc", "report_ack", 1)
#modparam("acc", "report_cancels", 1)
/* by default ww do not adjust the direct of the sequential requests.
   if you enable this parameter, be sure the enable "append_fromtag"
   in "rr" module */
#modparam("acc", "detect_direction", 0)
/* uncomment the following lines to enable DB accounting also */
#modparam("acc", "db_flag", 1)
#modparam("acc", "db_missed_flag", 1)


# global acc parameters
modparam("acc", "failed_transaction_flag", 1)
modparam("acc", "report_cancels",     0)
modparam("acc", "report_ack",         0)
modparam("acc", "early_media",        0)

modparam("acc", "log_level",          1)
modparam("acc", "log_flag",           1)
modparam("acc", "log_missed_flag",    1)

modparam("acc|auth_radius|group_radius|avp_radius", "radius_config", 
"/etc//radiusclient-ng/radiusclient.conf")
modparam("acc", "radius_flag",        1)
modparam("acc", "radius_missed_flag", 1)
modparam("acc", "radius_extra",       "User-Name=$Au; \
                                       Calling-Station-Id=$from; \
                                       Called-Station-Id=$to; \
                                       Sip-Translated-Request-URI=$ru; \
                                       Sip-RPid=$avp(s:rpid); \
                                       Source-IP=$avp(s:source_ip); \
                                       Source-Port=$avp(s:source_port); \
                                       SIP-Proxy-IP=$avp(s:sip_proxy_ip); \
                                       Canonical-URI=$avp(s:can_uri); \
                                       Billing-Party=$avp(s:billing_party); 
\
                                       Divert-Reason=$avp(s:divert_reason); 
\
                                       User-Agent=$hdr(user-agent); \
                                       Contact=$hdr(contact); \
                                       Event=$hdr(event); \
                                       ENUM-TLD=$avp(s:enum_tld)")

modparam("siptrace", "db_url", 
"mysql://opensips:password@localhost/opensips")
modparam("siptrace", "traced_user_avp", "$avp(s:traced_user)")
modparam("siptrace", "trace_on",        1)
modparam("siptrace", "trace_flag",      2)


# ----- usrloc params -----
#0 - This disables database completely. Only memory will be used. Contacts 
will not survive restart.
#1 - Write-Through scheme. All changes to usrloc are immediately reflected 
in database too.
#2 - Write-Back scheme. All changes are made to memory and database 
synchronization is done in the timer.
#3 - DB-Only scheme. No memory
#modparam("usrloc", "db_mode",   0)
/* uncomment the following lines if you want to enable DB persistency
   for location entries */
modparam("usrloc", "db_mode",   2)
modparam("usrloc", "db_url",
 "mysql://opensips:password@localhost/opensips")


# ----- auth_db params -----
/* uncomment the following lines if you want to enable the DB based
   authentication */
modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "db_url",
 "mysql://opensips:password@localhost/opensips")
modparam("auth_db", "load_credentials", "")


# ----- alias_db params -----
/* uncomment the following lines if you want to enable the DB based
   aliases */
#modparam("alias_db", "db_url",
# "mysql://opensips:password@localhost/opensips")


# ----- domain params -----
/* uncomment the following lines to enable multi-domain detection
   support */
#modparam("domain", "db_url",
# "mysql://opensips:password@localhost/opensips")
#modparam("domain", "db_mode", 1)   # Use caching


# ----- multi-module params -----
/* uncomment the following line if you want to enable multi-domain support
   in the modules (dafault off) */
#modparam("alias_db|auth_db|usrloc|uri_db", "use_domain", 1)


# ----- presence params -----
/* uncomment the following lines if you want to enable presence */
#modparam("presence|presence_xml", "db_url",
# "mysql://opensips:password@localhost/opensips")
#modparam("presence_xml", "force_active", 1)
#modparam("presence", "server_address", "sip:192.168.1.2:5060")

# ----- carrierroute params -----
/* uncomment the following line if you want to enable carrierroute support
   in the modules (dafault off) */
#modparam("carrierroute", "db_url", 
"mysql://opensips:password@localhost/opensips")
#modparam("carrierroute", "config_source", "db")

modparam("drouting", "db_url", 
"mysql://opensips:password@localhost/opensips")
modparam("drouting", "ruri_avp", '$avp(dr_ruri)')

modparam("drouting", "config_source", "db")

modparam("dispatcher", "db_url", 
"mysql://opensips:password@localhost/opensips")

modparam("nat_traversal", "keepalive_state_file", 
"/var/run/opensips/keepalive_state")

modparam("mediaproxy","mediaproxy_socket", 
"/var/run/mediaproxy/dispatcher.sock")
modparam("mediaproxy", "mediaproxy_timeout", 500)
modparam("mediaproxy", "signaling_ip_avp", "$avp(s:nat_ip)")
modparam("mediaproxy", "media_relay_avp", "$avp(s:media_relay)")



####### Routing Logic ########


# main request routing logic

route{

 if (!mf_process_maxfwd_header("10")) {
  sl_send_reply("483","Too Many Hops");
  exit;
 }

    if (msg:len >=  2048 ) {
         sl_send_reply("513", "Message too big");
         exit;
    };

 #pike_check_req Process the source IP of the current request and returns 
false if the IP was exceeding the blocking limit
    if (!pike_check_req()) { exit; };

    #rate limit
    if (is_method("INVITE|REGISTER|SUBSCRIBE")) {
                #rl_check The method will return an error code if the limit 
for the matched algorithm is reached.
                if (!rl_check()) {
                        #For the current request, a "503 - Server 
Unavailable" reply is sent back.
                        rl_drop();
                        exit;
                };
    };


 #we only handle voice, not any other things.
 if (is_method("PUBLISH|MESSAGE|SUBSCRIBE"))
 {
  sl_send_reply("503", "Service Unavailable");
  exit;
 }


 #1 - tests if client has a private IP address (as defined by RFC1918)
 #in the Contact field of the SIP message.
 #2 - tests if client has contacted OpenSIPS from an address that is 
different
 #from the one in the Via field. Both the IP and port are compared by this 
test.
 #4 - tests if client has a private IP address (as defined by RFC1918) in 
the top
 #Via field of the SIP message.


    if (client_nat_test("3")) {
     fix_contact();

     if ((method=="REGISTER"  ||(method=="INVITE" && !has_totag())) )
  {
      nat_keepalive();
  }
 }

    # check if user is suspended
    if(is_method("REGISTER|INVITE|OPTIONS"))
    {
        if (radius_is_user_in("From", "suspended")) {
            sl_send_reply("403", "Forbidden - suspended");
            exit;
        };
    };

 #use engate media proxy to fully control the media
 if (method==INVITE && (client_nat_test("3")  || 
search("^Route:.*;nat=yes")) ) {
      engage_media_proxy();
 }

    #has_totag() indicate in-dialog request. all in dialog request are 
processed in this block
 if (has_totag()) {
  # sequential request withing a dialog should
  # take the path determined by record-routing

        #loose_route() is used to route is usually used to
        #route in-dialog requests (like ACK, BYE, reINVITE).
  #The loose_route function analyzes the Route: headers in the requests.
  #If there is no Route: header, the function returns FALSE and routing
  #should be done with normal lookup functions. If a Route: header is found,
  #the function returns 1 and behaves as described in section 16.12 of RFC 
3261.
  #There is only one exception: If the request is out-of-dialog (no to-tag)
  #and there is only one Route: header indicating the local proxy,
  #then the Route: header is removed and the function returns FALSE.
  if (loose_route()) {
   # mark routing logic in request
            append_hf("P-hint: rr-enforced\r\n");

   #some provider GW (incorrectly) updated the contact info of an 
established dialog when it got an ACK. fix it
   if(is_method("ACK")) {
    if(is_present_hf("Contact")) remove_hf("Contact");
   };
   route(1);
  } else {

   if ( is_method("ACK") ) {
       #t_check_trans Returns true if the current request is associated to a 
transaction
    if ( t_check_trans() ) {
     # non loose-route, but stateful ACK; must be an ACK after a 487 or e.g.
     #404 from upstream server
     t_relay();
     exit;
    } else {
     # ACK without matching transaction ... ignore and discard.\n");
     xlog("L_WARN", "[$mi] discarding ACK\n");
     exit;
    }
   }
   #in-dialog , not loose route, and not ACK, we discard.
   sl_send_reply("404","Not here");
  }
  #regardless of whatever happens, all in-dialog has to end here.
  exit;
 }

 t_check_trans();

 # CANCEL processing.
 if (is_method("CANCEL"))
 {
  setflag(1); # do accounting ...
  setflag(2); # sip trace
     #Returns true if the current request is associated to a transaction.
     #CANCEL request - true if the cancelled INVITE transaction exists
  if (t_check_trans())
   t_relay();
  exit;
 }

 #following must be initial requests, or register.
 #command must be INVITE, ACK, BYE,  OPTIONS, REGISTER

 # authenticate if from local subscriber (uncomment to enable auth)
 #if (!(method=="REGISTER") && from_uri==myself)
 #{
 # if (!proxy_authorize("", "subscriber")) {
 #  proxy_challenge("", "0");
 #  exit;
 # }
 # if (!check_from()) {
 #  sl_send_reply("403","Forbidden auth ID");
 #  exit;
 # }

 # consume_credentials();
 # # caller authenticated
 #}


 # record routing
 if (!is_method("REGISTER"))
  record_route();

 # account only INVITEs
 if (is_method("INVITE")) {
  setflag(1); # do accounting
  setflag(2); # sip trace
 }

 #fraud detection block. we don't allow outsiders who are not authenticated 
to use our gateway.
 if (!uri==myself)
 /* replace with following line if multi-domain support is used */
 ##if (!is_uri_host_local())
 {
     # check if user is allowed to do voip calls to other domains
        if(is_method("INVITE")) {
            #for caller calling outside, but not in our voip group, we 
forbid.
            #this is needed to fight against fraud.
            if (!radius_is_user_in("From", "voip")) {
                sl_send_reply("403", "Forbidden VoIP");
                exit;
            };
        };
        # mark routing logic in request
  append_hf("P-hint: outbound\r\n");

  route(1);
  exit;
 }


    #process REGISTER to local server.
 if (is_method("REGISTER"))
 {
  # authenticate the REGISTER requests (uncomment to enable auth)
  if (!radius_www_authorize("machinename.somedomain.com")
   && !www_authorize("machinename.somedomain.com", "subscriber"))
  {
   www_challenge("machinename.somedomain.com", "0");
   exit;
  }
  ##
  ##if (!check_to())
  ##{
  ## sl_send_reply("403","Forbidden auth ID");
  ## exit;
  ##}

  if (client_nat_test("3")) fix_nated_register();
  if (!save("location"))
   sl_reply_error();
  exit;
 }

    #process INVITE, ACK, BYE, OPTIONS for local server in the following 
blocks

 if ($rU==NULL) {
  # request with no Username in RURI
  sl_send_reply("484","Address Incomplete");
  exit;
 }


 #lookup(domain) extracts username from Request-URI and tries to find
 #all contacts for the username in usrloc
 #return codes
 #1 - contacts found and returned.
 #-1 - no contact found.
 #-2 - contacts found, but method not supported.
 #-3 - internal error during processing
 #if (!lookup("location")) {
 # switch ($retcode) {
 #  case -1:
 #  case -3:
 #   t_newtran();
 #   t_reply("404", "Not Found");
 #   exit;
 #  case -2:
 #   sl_send_reply("405", "Method Not Allowed");
 #   exit;
 # }
 #}


    #process INVITE, ACK, BYE, OPTIONS for local server in the following 
blocks
    #It is critical to save $avp(s:can_uri) after the Proxy has performed
    #all possible lookups except DNS.
    #The Canonical-URI will be used for rating the session.
    $avp(s:can_uri) = $ru;
 route(1);
}


#route[1] process INVITE, ACK, BYE,  OPTIONS
route[1] {


        if (is_method("INVITE") ) {

                # normalization to e164
                # http://en.wikipedia.org/wiki/NANP
                if($ruri.user =~ "^\+[1-9][0-9]+")
                {
                        strip(1);
                }
#               if($ruri.user =~ "^00[1-9][0-9]+") {
#                       strip(2);
#               }
#               if($ruri.user =~ "^0[1-9][0-9]+") {
#                       strip(1);
#                       #prefix("49");
#               }


                #in the US , dialing 1NPANXXXXXX  11 digits
                if($ruri.user =~ "^1[1-9][0-9]{9}") {
                        #do nothing
                }
                #in the US, dialing NPANXXXXXX 10 digits
                else if($ruri.user =~ "^[1-9][0-9]{9}") {
                        prefix("1");
                }
                #in the US, dialing NXXXXXX 7 digits local number.
                else if($ruri.user =~ "^[1-9][0-9]{6}") {
                        $rU = $(fU{s.substr, 0, 4}) + $rU;
                }
                # 411 Local Directory Assistance
                else if (uri=~"^sip:411 at .*") {
                        # the uri with a default call to "local directory 
assistance".
                        $rU = $(fU{s.substr, 0, 4}) + "5551212";
                }

                # 611 Local Directory Assistance
                else if (uri=~"^sip:611 at .*") {
                        # the uri with a default call to "local directory 
assistance".
                        rewriteuri("sip:17775551212 at machinename.somedomain.com");
                }
                #911 is handled by E911 service provider
                else {
                        sl_send_reply("404", "Invalid destination");
                        exit;
                }

                # Set the callerid for the user from an AVP
          #if (avp_db_load("$from/username", "s:callerid")) {
          #      subst('/^From: (.*)>(.*)$/From: $avp(callerid)>\2/ig');
          #};

        }

  if (is_method("INVITE|BYE")) {
    setflag(1); # do accounting ...
    setflag(2); # sip trace
    #call the accounting functions explicitly in local_route for
    #the internally generated BYEs as they do not trigger accounting by just
    #setting the accounting flag
       acc_rad_request("200 ok");
                acc_log_request("200 ok");
  }

        #change access point phone number to inbound route for asterisk
        alias_db_lookup("dbaliases");
        #forward asterisk inbound route with dispatcher as load balancer
        if (is_method("INVITE") && $ruri =~ "^sip:17771000101 at .*" ) {
                        #dispatcher select from set 1 using algorithm 0.
                        if(!ds_select_dst("1", "0"))
                        {
                                sl_send_reply("404", "no destination");
                        }
                        if(!t_relay())  sl_reply_error();
                        exit;
        };

        #special relaying to asterisk finished, now we process regular 
requests.

  #INVITE, ACK, BYE,  OPTIONS to locally registered user.
        if (lookup("location"))
        {
                if (is_method("INVITE")) {
                        t_on_branch("1");
                        t_on_reply("1");
                        t_on_failure("1");
                }
                if (!t_relay()) {
                        sl_reply_error();
                };
                exit;
        }

  #INVITE to outgoing gateway, route it out.
        if (is_method("INVITE") ) {
                #if (cr_route("default", "machinename.somedomain.com", 
"$rU", "$rU", "call_id")) {
                if (do_routing()) {
                        t_on_failure("11");
                        if (!t_relay()) {
                                sl_reply_error();
                        };
                        exit;
                };
                exit;
        };

        if (!t_relay()) {
                sl_reply_error();
        };
        exit;
}


branch_route[1] {
 xlog("new branch at $ru\n");
}


onreply_route[1] {
 xlog("incoming reply\n");
}


failure_route[1] {
 if (t_was_cancelled()) {
  exit;
 }

 # uncomment the following lines if you want to block client
 # redirect based on 3xx replies.
 ##if (t_check_status("3[0-9][0-9]")) {
 ##t_reply("404","Not found");
 ## exit;
 ##}

 # uncomment the following lines if you want to redirect the failed
 # calls to a different new destination
 ##if (t_check_status("486|408")) {
 ## sethostport("192.168.2.100:5060");
 ## append_branch();
 ## # do not set the missed call flag again
 ## t_relay();
 ##}
}


######################
# "default" failover #
######################
failure_route[11] {
 xlog("L_INFO", "entering failure_route[11] for reply code 
'$T_reply_code'\n");

 if (t_was_cancelled()) {
  exit;
 }

 if (t_check_status("408|5[0-9][0-9]")) {
  #xlog("L_INFO","cr_tree_rewrite_uri(\"default\", \"1\");\n");
  #if (cr_route("default", "machinename.somedomain.com", "$rU", "$rU", 
"call_id")) {
  if (do_routing()) {
   t_on_failure("12");
   append_branch();
   route(1);
  };
  exit;
 } else if (t_check_status("3[0-9][0-9]")) {
  t_reply("404","Not found");
  exit;
 }
}

failure_route[12] {
 xlog("L_INFO", "entering failure_route[12] for reply code 
'$T_reply_code'\n");
 if (t_was_cancelled()) {
  exit;
 }

 if (t_check_status("408|5[0-9][0-9]")) {
  xlog("L_INFO","cr_tree_rewrite_uri(\"default\", \"2\");\n");
  #if (cr_route("default", "machinename.somedomain.com", "$rU", "$rU", 
"call_id")) {
  if (do_routing()) {
   t_on_failure("13");
   append_branch();
   route(1);
  };
  exit;
 } else if (t_check_status("3[0-9][0-9]")) {
  t_reply("404","Not found");
  exit;
 }
}

failure_route[13] {
 xlog("L_INFO", "entering failure_route[13] for reply code 
'$T_reply_code'\n");
 if (t_was_cancelled()) {
  exit;
 }
}






More information about the Users mailing list