A2Billing and OpenSIPS – Part 3

This post has the actual config of OpenSIPS described in part 1 and part 2. Some of this config will not make sense unless you read those parts.

First a warning … many, many people want to use your call credit!! Make sure that your systems are secure. If only the OpenSIPS server needs to talk to your A2Billing/Asterisk servers over SIP then use a fierwall to block other connections.

In the configuration below OpenSIPS does not handle the Audio/RTP traffic, this is passed directly to the Asterisk/A2Billing server.

The code below is the whole opensips.cfg file, just broken up with some description. All indentation has been removed, apologies if this sometimes makes it difficult to read.

First some global settings, including the IP address of the OpenSIPS server –

listen=udp:1.1.1.1:5060 # CUSTOMIZE ME
debug=1
log_stderror=no
log_facility=LOG_LOCAL6
fork=yes
children=4
dns_try_ipv6=no
auto_aliases=no
disable_tcp=yes
disable_tls=yes
server_signature=no

next we load the modules that are required –

mpath="/usr/local/lib/opensips/modules/"
loadmodule "signaling.so"
loadmodule "sl.so"
loadmodule "tm.so"
loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "sipmsgops.so"
loadmodule "mi_fifo.so"
loadmodule "uri.so"
loadmodule "db_mysql.so"
loadmodule "avpops.so"
loadmodule "acc.so"
loadmodule "dispatcher.so"
loadmodule "permissions.so"
loadmodule "dialog.so"
loadmodule "siptrace.so"
loadmodule "auth.so"
loadmodule "auth_db.so"

now we set the database URL for the modules that are going to use our MySQL database. This database is on the A2Billing server –

modparam("acc|dispatcher|permissions|dialog|siptrace|auth_db|avpops","db_url","mysql://username:[email protected]/opensips")

now we set some other module options –

modparam("tm", "fr_inv_timer", 30)
modparam("tm", "restart_fr_on_each_reply", 0)
modparam("tm", "onreply_avp_mode", 1)
modparam("rr", "append_fromtag", 0)
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
modparam("mi_fifo", "fifo_mode", 0666)
modparam("uri", "use_uri_table", 0)
modparam("acc", "db_flag", 1)
modparam("acc", "early_media", 0)
modparam("acc", "report_cancels", 0)
modparam("acc", "detect_direction", 0)
modparam("acc", "failed_transaction_flag", 3)
modparam("acc", "log_flag", 1)
modparam("acc", "log_missed_flag", 2)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_probing_mode", 0)
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "force_dst", 1)
modparam("dispatcher", "ds_ping_interval", 30)
modparam("dialog", "db_mode", 1)
modparam("dialog", "dlg_match_mode", 2)
modparam("siptrace", "trace_on", 0)
modparam("siptrace", "trace_flag", 22)
modparam("auth_db", "load_credentials", "")
modparam("auth_db", "skip_version_check", 1)

and now to the actual routing. The first block has some general default code that rejects some packets and also immediately relays sip dialogs that have already been established –

route{
if (!mf_process_maxfwd_header("10") && $retcode==-1) {
sl_send_reply("483","Too Many Hops");
exit;
}
if (has_totag()) {
if (loose_route()) {
if (is_method("BYE")) {
setflag(1); # do accounting ...
setflag(3); # ... even if the transaction fails
} else if (is_method("INVITE")) {
record_route();
}
route(RELAY);
} else {
if ( is_method("ACK") ) {
if ( t_check_trans() ) {
t_relay();
exit;
} else {
exit;
}
}
sl_send_reply("404","Not here");
}
exit;
}

next more generic checks. Relay CANCEL messages and reject others with incomplete URIs –

if (is_method("CANCEL")) {
if (t_check_trans())
t_relay();
exit;
} else if (!is_method("INVITE")) {
send_reply("405","Method Not Allowed");
xlog("$rm FAILED: $si / $ct / $fu\n");
exit;
}
if ($rU==NULL) {
sl_send_reply("484","Address Incomplete");
exit;
}

next drop some more invalid packets –

if (loose_route()) {
xlog("L_ERR","Attempt to route with preloaded Route's [$fu/$tu/$ru/$ci]");
if (!is_method("ACK"))
sl_send_reply("403","Preload Route denied");
exit;
}

next we are going to create a route header on the packet and switch on some accounting –

record_route();
setflag(1);

now we are going to choose the Asterisk server that the call is passed to. This uses the ds_select_dst command which is part of the dispatcher module. The available Asterisk servers are selected from the ‘dispatcher’ table in the MysQL database. The ‘1’ relates to the ‘setid’ field in the ‘dispatcher’ table, and the ‘destination’ field should be in the format ‘sip:2.2.2.2:5060’ –

if ( !ds_select_dst("1","0") ) {
send_reply("500","No Destination available");
xlog("$rm FAILED: NO DESTINATION: $si / $tu / $ru\n");
exit;
}
t_on_failure("GW_FAILOVER");

Now the good stuff! This is where we do IP authentication, and route the call if it is valid. We use the ‘check_source_address’ command which is part of the permissions module. This is going to look in the MySQL database for a matching IP address. The “1” is a group ID that we hard coded when we set up the VIEW in MySQL. If the IP address matches then the account code is returned to us (because we stored it in the context_info field in MySQL), and we set this to the variable “$avp(accountcode”. We then set this variable in a header on the SIP INVITE packet and send it to A2Billing. In part 2 I showed how to extract this SIP header and set the account code in the Asterisk dialplan, so that A2Billing knows which customer to charge for the call –

if (check_source_address("1","$avp(accountcode)")) {
xlog("L_INFO", "IP $rm DIALLED: $si / $ru / Accountcode: $avp(accountcode) ");
remove_hf("P-Accountcode");
append_hf("P-Accountcode: $avp(accountcode)\r\n");
route(RELAY);
};

next, if IP authentication above failed we want to challenge the caller (the IP address sending the SIP INVITE) for a Username and Secret. “subscriber” is the name of the VIEW in MySQL where we are storing the customer credentials. These are picked up straight from the A2BIlling customers SIP account –

if (!proxy_authorize("", "subscriber")) {
$var(debug) = proxy_authorize("", "subscriber");
xlog("Not Proxy Authorize: $var(debug)");
proxy_challenge("", "0");
exit;
}

now, if the customer passed the authorisation above, we want to send the call to our Asterisk/A2billing server. First though we want to set the Account Code so that A2Billing knows which customer to charge the call to. This is similar to what we did for IP authentication, but we need to run a separate command to retrieve the account code from the ‘rpid’ field in the ‘subscriber’ table where we stored it. $au is the authorized username –

avp_db_query("select rpid from subscriber where username='$au'","$avp(accountcode)");
remove_hf("P-Accountcode");
append_hf("P-Accountcode: $avp(accountcode)\r\n");
xlog("L_INFO", "AUTH $rm DIALLED: $si / $ru / Accountcode: $avp(accountcode) ");
consume_credentials();
route(RELAY);

}

Finally, we called route(RELAY) several times in the script above, and we define that here, and a couple of other bits, we forward the packets with t_relay –

route[RELAY] {
if (!t_relay()) {
sl_reply_error();
};
exit;
}

failure_route[GW_FAILOVER] {
if (t_was_cancelled()) {
exit;
}
if (t_check_status("(408)|([56][0-9][0-9])")) {
xlog("Failed trunk $rd/$du detected \n");
if ( ds_next_dst() ) {
t_on_failure("GW_FAILOVER");
t_relay();
exit;
}
send_reply("500","All GW are down");
}
}

And that’s the end of the config file!

The code above is definitely not designed to be totally cut and paste. You are going to have to check some documentation and have a fair understanding of what’s going on and how the call is being handled. I would also suggest learning the xlog command and some of the variables available. This you can use at various points in the script to log some output and see why you calls might be failing.

If anyone experienced with OpenSIPS (or Kamailio) can offer any suggestions for how to improve the config I’d be interested to hear them. Also, of anyone with Kamailio experience could let me know how different that config would look in that I’d be interested to hear that too. Thanks!

3 thoughts on “A2Billing and OpenSIPS – Part 3

  1. Karim Makki

    Hello matt,

    First of all i would like to thank you for this very useful tutorial. i’m trying to make a connection between Kamailio 4.0 and A2Billing
    I got a list of variables and modules that have changed, so i had no choice but to execute the OpenSIPS script in Kamailio, and run through the errors one by one
    (I will provide to you the full list once i get the connexion up and running, i saw that you were looking for a Kamailio version of your tutorial)

    But now i’m stuck on one function, which is “check_source_address” that takes two variables (the group ID, and the account code)
    On Kamailio, there is an equivalent function, “allow_source_address” that only takes one variable, which is the group ID.
    Another function would be “allow-address” that can take the group ID, the IP address and the port of the peer, but i couldn’t find a function that would use the accound code.
    This is a really important parameter as a2billing has to know which client is calling…

    Would you have an idea about how to resolve this issue? i would be very thankful if you do 🙂

    Thank you,

    Karim

  2. Darren Williams Post author

    Thanks for this great set of guides. Theyve actually inspired me to sit down and have a good look at implementing this sort of solution ourselves.

    Im plodding away at it at the moment just let you know where Im at. Were using Kamailio 4.1.

    We have registration working we also have inbound and outbound working too the dids come in to the Kamailio box as at the moment we have both clients and providers connecting to port 5060. A2Billing will only process calls TO the provider.

    We are using a view to the a2billing table for authentication etc. We have discovered that this was just a matter of adding a field sippasswd and copying the asterisk secrets there. The secret then needs deleting from the a2billing cc_sip_buddies table. The dids are handled using kamailios dbalias table that is again a view to a2billing. We initially had a problem where after the alias lookup the invite was going out to [email protected] rather than [email protected] but that was easily rectified by doing a rU=oU, after the alias lookup if the traffic was from asterisk.

    I only need to get the a2billing ACLs fixed just now then I should have a better config which I will post when working.

    Karim: We solved your problem by creating a Kamailio table for addresses. I will revisit using a view soon but Kamailio was unable to start when using a view.

    If you add (modparampermissions peertagavp avpi707 say we then populated the addesses tables tag column with the account code this can then be used by:

    remove_hf(“P-Accountcode”);
    append_hf(“P-Accountcode: $avp(i:707)\r\n”);
    return;

    which achieved what Matt did with the OpenSips.

    Ill post a full config when I nail this last bit.

    Once again thanks!

  3. Jagan

    Hi Darren ,

    Can you please post the Openisps configuration file with all possibilities with a2billing.

    i thing this configuration need Openisps 1.8 above ?

    Thanks
    Jag

Comments are closed.