Menu

OpenSips parallel forking

2012-06-20
2013-05-09
  • SC Integrasoft SRL

    Hello,
    I try to write a script for parallel forking, but it doesn't work. The main script was generated by osipsconfig and it's a standard residential script with NAT traversal. For forking I use seturi(user1) and append_branch(user2), The user1 receives the INVITE but user2 not.

    Any idea?

    Hunor

    My script:

    fork=yes
    children=4

    /* uncomment the following lines to enable debugging */
    debug=3
    #fork=no
    log_stderror=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

    /* comment the next line to enable the auto discovery of local aliases
       based on revers DNS on IPs */
    auto_aliases=no

    #advertised_address=193.231.162.88
    #advertised_port=5060

    listen=udp:193.231.162.88:5060   # CUSTOMIZE ME

    disable_tcp=no
    listen=tcp:193.231.162.88:5060   # CUSTOMIZE ME

    #disable_tls=yes

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

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

    #### SIGNALING module
    loadmodule "signaling.so"

    #### StateLess module
    loadmodule "sl.so"

    #### Transaction Module
    loadmodule "tm.so"
    modparam("tm", "fr_timer", 10)
    modparam("tm", "fr_inv_timer", 30)
    modparam("tm", "restart_fr_on_each_reply", 0)
    modparam("tm", "onreply_avp_mode", 1)

    #### Record Route Module
    loadmodule "rr.so"
    /* do not append from tag to the RR (no need for this script) */
    modparam("rr", "append_fromtag", 0)

    #### MAX ForWarD module
    loadmodule "maxfwd.so"

    #### SIP MSG OPerationS module
    loadmodule "sipmsgops.so"

    #### FIFO Management Interface
    loadmodule "mi_fifo.so"
    modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
    modparam("mi_fifo", "fifo_mode", 0666)

    #### URI module
    loadmodule "uri.so"
    modparam("uri", "use_uri_table", 0)
    modparam("uri", "db_url", "mysql://opensips:opensipsrw@localhost/opensips")

    #### MYSQL module
    loadmodule "db_mysql.so"

    #### USeR LOCation module
    loadmodule "usrloc.so"
    modparam("usrloc", "nat_bflag", 10)
    modparam("usrloc", "db_mode",   2)
    modparam("usrloc", "db_url", "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME

    #### REGISTRAR module
    loadmodule "registrar.so"
    modparam("registrar", "tcp_persistent_flag", 7)
    modparam("registrar", "received_avp", "$avp(received_nh)")
    /* uncomment the next line not to allow more than 10 contacts per AOR */
    #modparam("registrar", "max_contacts", 10)

    #### ACCounting module
    loadmodule "acc.so"
    /* what special events should be accounted ? */
    modparam("acc", "early_media", 0)
    modparam("acc", "report_cancels", 0)
    /* by default we 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)
    modparam("acc", "failed_transaction_flag", 3)
    /* account triggers (flags) */
    modparam("acc", "db_flag", 1)
    modparam("acc", "db_missed_flag", 2)
    modparam("acc", "db_url",
    "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME

    #### AUTHentication modules
    loadmodule "auth.so"
    loadmodule "auth_db.so"
    modparam("auth_db", "calculate_ha1", yes)
    modparam("auth_db", "password_column", "password")
    modparam("auth_db", "db_url",
    "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME
    modparam("auth_db", "load_credentials", "")

    #### DIALOG module
    loadmodule "dialog.so"
    modparam("dialog", "dlg_match_mode", 1)
    modparam("dialog", "default_timeout", 21600)  # 6 hours timeout
    modparam("dialog", "db_mode", 2)
    modparam("dialog", "db_url",
    "mysql://opensips:opensipsrw@localhost/opensips") # CUSTOMIZE ME

    ####  NAT modules
    loadmodule "nathelper.so"
    modparam("usrloc","nat_bflag", 6)
    modparam("nathelper","received_avp", "$avp(42)")
    modparam("nathelper", "natping_interval", 5)
    modparam("nathelper", "ping_nated_only", 1)
    modparam("nathelper", "sipping_bflag", 7)
    modparam("nathelper", "sipping_from", "sip:pinger@193.231.162.88:5060")

    loadmodule "rtpproxy.so"
    modparam("rtpproxy", "rtpproxy_sock", "udp:127.0.0.1:7890") # CUSTOMIZE ME

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

    # main request routing logic

    route{
    xlog("\n\nROUTE\n\n");
    force_rport();
    if (nat_uac_test("23")) {
    xlog("nat_uac_test\n");
    if (is_method("REGISTER")) {
    fix_nated_register();
    setbflag(10);
    } else {
    fix_nated_contact();
    setflag(10);
    }
    }

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

    if (has_totag()) {
    xlog("has totag\n");
    # sequential request withing a dialog should
    # take the path determined by record-routing
    if (loose_route()) {
    xlog("loose route\n");
    # validate the sequential request against dialog
    if ( $DLG_status!=NULL && !validate_dialog() ) {
    xlog("In-Dialog $rm from $si (callid=$ci) is not valid according to dialog\n");
    ## exit;
    }

    if (is_method("BYE")) {
    xlog("totag-BYE\n");
    setflag(1); # do accounting …
    setflag(3); # … even if the transaction fails
    } else if (is_method("INVITE")) {
    xlog("totag-INVITE");
    # even if in most of the cases is useless, do RR for
    # re-INVITEs alos, as some buggy clients do change route set
    # during the dialog.
    record_route();
    }

    if (check_route_param("nat=yes")) {
    xlog("totag-nat=yes\n");
    setflag(10);
    }

    # route it out to whatever destination was set by loose_route()
    # in $du (destination URI).
    route(1);
    } else {
    xlog("!loose route\n");
    if ( is_method("ACK") ) {
    xlog("!loose-ACK");
    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
    exit;
    }
    }
    sl_send_reply("404","Not here");
    }
    exit;
    }

    # CANCEL processing
    if (is_method("CANCEL"))
    {
    if (t_check_trans())
    t_relay();
    exit;
    }

    t_check_trans();

    if ( !(is_method("REGISTER")  ) ) {

    if (from_uri==myself)

    {

    # authenticate if from local subscriber
    # authenticate all initial non-REGISTER request that pretend to be
    # generated by local subscriber (domain from FROM URI is local)
    if (!proxy_authorize("", "subscriber")) {
    proxy_challenge("", "0");
    exit;
    }
    if (!db_check_from()) {
    sl_send_reply("403","Forbidden auth ID");
    exit;
    }

    consume_credentials();
    # caller authenticated

    } else {
    # if caller is not local, then called number must be local

    if (!uri==myself) {
    send_reply("403","Rely forbidden");
    exit;
    }
    }

    }

    # preloaded route checking
    if (loose_route()) {
    xlog("L_ERR",
    "Attempt to route with preloaded Route's ");
    if (!is_method("ACK"))
    sl_send_reply("403","Preload Route denied");
    exit;
    }

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

    # account only INVITEs
    if (is_method("INVITE")) {
    xlog("\n\nINVITE\n\n");

    #forking
                    if ( from_uri == "sip:test4@193.231.162.88" ) {
                        xlog("\nFORKING\n");
                        seturi("sip:test1@193.231.162.88");
                        append_branch("sip:test2@193.231.162.88");
                        append_branch("sip:test3@193.231.162.88");
                   }

    # create dialog with timeout
    if ( !create_dialog("B") ) {
    send_reply("500","Internal Server Error");
    exit;
    }

    setflag(1); # do accounting
    }

    if (!uri==myself) {
    append_hf("P-hint: outbound\r\n");

    route(1);
    }

    # requests for my domain

    if (is_method("PUBLISH|SUBSCRIBE"))
    {
    sl_send_reply("503", "Service Unavailable");
    exit;
    }

    if (is_method("REGISTER"))
    {

    # authenticate the REGISTER requests
    if (!www_authorize("", "subscriber"))
    {
    www_challenge("", "0");
    exit;
    }

    if (!db_check_to())
    {
    sl_send_reply("403","Forbidden auth ID");
    exit;
    }

    if ( proto==TCP ||  0 ) setflag(7);

    if (!save("location"))
    sl_reply_error();

    exit;
    }

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

    # do lookup with method filtering
    if (!lookup("location","m")) {
    if (!db_does_uri_exist()) {
    send_reply("420","Bad Extension");
    exit;
    }

    t_newtran();
    t_reply("404", "Not Found");
    exit;
    }

    if (isbflagset(10)) setflag(10);

    # when routing via usrloc, log the missed calls also
    setflag(2);
    route(1);
    }

    route {
    # for INVITEs enable some additional helper routes
    if (is_method("INVITE")) {

    if (isflagset(10)) {
        if (has_body("application/sdp"))
    rtpproxy_offer("ro");
    }

    t_on_branch("2");
    t_on_reply("2");
    t_on_failure("1");
    }

    if (isflagset(10)) {
    add_rr_param(";nat=yes");
    }

    if (!t_relay()) {
    send_reply("500","Internal Error");
    };
    exit;
    }

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

    onreply_route {
    if (nat_uac_test("1"))
    fix_nated_contact();
    if ( isflagset(10) )
        if (has_body("application/sdp"))
    rtpproxy_answer("ro");
    xlog("incoming reply\n");
    }

    failure_route {

    xlog("on failure");
    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")) {
    ##t_reply("404","Not found");
    ## exit;
    ##}

    }

    local_route {
    if (is_method("BYE") && $DLG_dir=="UPSTREAM") {

    acc_db_request("200 Dialog Timeout", "acc");

    }
    }

     
  • SC Integrasoft SRL

    Hi,
    I saw that the administrators were here, because they have answered other posts, but no mine. I need help!, because even the example script for forking doesn't work, from here: http://opensips.svn.sourceforge.net/viewvc/opensips/trunk/examples/fork.cfg?revision=5150&view=markup
    The script in the link enters an infinite loop and consumes all memory at INVITE.
    In normal mode the OpenSIPS proxy works fine for me, but I cannot modify it to enable parallel forking or to make it work behind a NAT.
    Please just tell me if it's possible to do that!

    Thanks,
    Hunor

     
  • Bogdan-Andrei Iancu

    Hi Hunor,

    in the route try to print all the branches you have as destination:
      xlog("RURI : $ru  ; branches: $(branch(uri))\n" );

    Regards,
    Bogdan

     
  • SC Integrasoft SRL

    Hi Bogdan,
    Thank you for reply!
    Now with this log a call looks like on log level 3:

    RURI: sip:test1@193.231.162.167:5060;line=bf23940763471d6; branches: sip:test2@193.231.162.88
    new branch at sip:test1@193.231.162.167:5060;line=bf23940763471d6
    new branch at sip:test2@193.231.162.88
    new branch at sip:test3@193.231.162.88
    new branch at sip:test1@192.168.1.146;line=bf23940763471d6
    incoming reply
    incoming reply
    incoming reply
    incoming reply
    incoming reply
    RURI: sip:test4@192.168.1.199:4142;line=d1eef9b31a93120; branches: sip:test4@193.231.162.167:4142;line=d1eef9b31a93120
    incoming reply
    on failure
    RURI: sip:193.231.162.88;lr; branches: <null>

    The INVITE arrives to the test1, but to the other users(test2, test3) not.

    Multumesc;)
    Hunor

     
  • Bogdan-Andrei Iancu

    I guess your problem is that lookup() fetches the location only for RURI (and not for the rest of the branches) - tthis is why only first destination gets to phone. The rest of them are probably sent out by opensips and spiralling back  - check with ngrep on lo.

    Regards,
    Bogdan

     
  • SC Integrasoft SRL

    Hi,
    Sorry for the late answer.
    Here is the result:

    ngrep -d lo port 5060
    interface: lo (127.0.0.0/255.0.0.0)
    filter: (ip or ip6) and ( port 5060 )
    #
    U 193.231.162.88:5060 -> 193.231.162.88:5060
      INVITE sip:test2@193.231.162.88 SIP/2.0..Record-Route: <sip:193.231.162.88;lr;did=e0a.472afbd7>..Via: SIP/2.0/UDP 193.231.162.88;branch=z9hG4bKe8eb.865913e3.1..Via: SIP/2.0/UDP 193.231.
      162.150:5060;received=193.231.162.150;rport=5060;branch=z9hG4bK1739..From: <sip:test4@193.231.162.88>;tag=27400..To: <sip:dummy@193.231.162.88>..Call-ID: 23192..CSeq: 21 INVITE..Contact
      : <sip:test4@193.231.162.150>..Content-Type: application/sdp..Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO..Max-Forwards: 69..User-Agent: Linphone/3
      .5.2 (eXosip2/3.6.0)..Subject: Phone call..Content-Length:   318….v=0..o=test4 4076 4076 IN IP4 193.231.162.150..s=Talk..c=IN IP4 193.231.162.150..t=0 0..m=audio 7078 RTP/AVP 112 111
      110 3 0 8 101..a=rtpmap:112 speex/32000..a=fmtp:112 vbr=on..a=rtpmap:111 speex/16000..a=fmtp:111 vbr=on..a=rtpmap:110 speex/8000..a=fmtp:110 vbr=on..a=rtpmap:101 telephone-event/8000..a
      =fmtp:101 0-11..                                                                                                                                                                        
    #
    U 193.231.162.88:5060 -> 193.231.162.88:5060
      SIP/2.0 407 Proxy Authentication Required..Via: SIP/2.0/UDP 193.231.162.88;received=193.231.162.88;rport=5060;branch=z9hG4bKe8eb.865913e3.1..Via: SIP/2.0/UDP 193.231.162.150:5060;receiv
      ed=193.231.162.150;rport=5060;branch=z9hG4bK1739..From: <sip:test4@193.231.162.88>;tag=27400..To: <sip:dummy@193.231.162.88>;tag=ec500a06cb2b870afbf5ea7b0b0d5e27.faa0..Call-ID: 23192..C
      Seq: 21 INVITE..Proxy-Authenticate: Digest realm="193.231.162.88", nonce="4ff134470000001927f16e64199429fa97d4b7ddbd9f5d19"..Server: OpenSIPS (1.8.0-notls (i386/linux))..Content-Length:
       0….                                                                                                                                                                                  
    #
    U 193.231.162.88:5060 -> 193.231.162.88:5060
      ACK sip:test2@193.231.162.88 SIP/2.0..Via: SIP/2.0/UDP 193.231.162.88;branch=z9hG4bKe8eb.865913e3.1..From: <sip:test4@193.231.162.88>;tag=27400..Call-ID: 23192..To: <sip:dummy@193.231.1
      62.88>;tag=ec500a06cb2b870afbf5ea7b0b0d5e27.faa0..CSeq: 21 ACK..Max-Forwards: 70..User-Agent: OpenSIPS (1.8.0-notls (i386/linux))..Content-Length: 0….                                
    exit

    (to test1 arrives the INVITE, but to test2 not)

    P.S.: If it is easier for you, I can give you ssh access to the server.

    Regards,
    Hunor

     
  • Bogdan-Andrei Iancu

    Well, the issue is clear - the additional branches (aside the RURI) are not resolved by lookup(), so they keep the IP of the server -> spiral and rejected by opensips.

    What you can do is:

    1) using the "branch" pseudo variable, you can do a small loop into script to make lookup() to go through all branches, so all original branches will be solved.

    2) let the logic as it is, but when handling INVITEs, skip auth if the INVITE comes from opensips itself - allow spiralling without auth. In this case, the additional branches will be solved via lookup() at the second passing through opensips.

    Regards,
    Bogdan

     
  • SC Integrasoft SRL

    Hi,
    The 2nd step is clear, I have documentation for $branch variable, too.
    But I don't know how can I force the lookup() function to resolve different branches.
    Could you give me a small example?
    Thanks,
    Hunor

     
  • Bogdan-Andrei Iancu

    It is a matter of scripting - keep all the destinations (where you want to fork) into AVPs and push the values one by one into RURI and call lookup().

    Regards,
    Bogdan

     
  • SC Integrasoft SRL

    Hi!

    I tried to do the way you said, but I couldn't append the destinations to the branch while performing lookups. Instead, I appended all destinations at the beginning, and then made this small loop:

    $var(i) = 0;
            # the following line will evaluate to 3; there are 3 destinations
            $var(itemNumber) = set_count("$avp(1)")+1;
            while ($var(i) < $var(itemNumber)) {
                if (!lookup("location","m")) {
                        xlog("in lookup location error\n");
                        if (!db_does_uri_exist()) {
                                send_reply("420","Bad Extension");
                                    exit;
                        }

                        t_newtran();
                        t_reply("404", "Not Found");
                            exit;
                }
                xlog("BEFORE: $(branch(uri))\t$(branch(uri))\t$(branch(uri))\t$(branch(uri))\n");
                append_branch();
                xlog("BRANCH: $(branch(uri))\t$(branch(uri))\t$(branch(uri))\t$(branch(uri))\n");
                remove_branch(0);
                xlog("AFTER: $(branch(uri))\t$(branch(uri))\t$(branch(uri))\t$(branch(uri))\n");
                $var(i) = $var(i) + 1;
            }

    But, again, there is a problem with append_branch() (without parameters). When I use it the first time, it puts the first branch at the end of the $branch array, but then in the next iterations it only repeats the last element, not the first. Is there any kind of method to make a circular shift of the elements of the $branch array?

    Regards,
    Hunor

     
  • SC Integrasoft SRL

    OK, I got the solution. I put all the destinations into an avp, then I loop through the elements and put them in the $ru variable. Then I call append_branch().

    Regards,
    Hunor

     
  • SC Integrasoft SRL

    Hi!
    Something is still wrong now. The forking has to be done for a number of predefined clients. But when a client is not available (i.e. not registered on the SIP server) none of the others will receive the call. Is there a workaround for the unavailable contacts to be simply ignored?

    Regards,
    Hunor

     
  • Bogdan-Andrei Iancu

    I guess your looping through the users breaks via a 404 Not Found if one of the users is not registered - you need to change that logic to send the 404 back only if *ALL* users are offline (and not if one is offline).

    Regards,
    Bogdan

     
  • SC Integrasoft SRL

    Thank you, that helped!

    Best regards,
    Hunor