mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
452 lines
16 KiB
452 lines
16 KiB
<?xml version="1.0" encoding='ISO-8859-1'?>
|
|
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
|
|
|
|
<!-- Include general documentation entities -->
|
|
<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
|
|
%docentities;
|
|
|
|
]>
|
|
<!-- Module User's Guide -->
|
|
|
|
<chapter>
|
|
|
|
<title>&adminguide;</title>
|
|
|
|
<section id="call_control.overview">
|
|
<title>Overview</title>
|
|
<para>
|
|
This module allows one to limit the duration of calls and automatically
|
|
end them when they exceed the imposed limit. Its main use case is to
|
|
implement a prepaid system, but it can also be used to impose a global
|
|
limit on all calls processed by the proxy.
|
|
</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Description</title>
|
|
<para>
|
|
Callcontrol consists of 3 components:
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>The &kamailio; call_control module</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
An external application called callcontrol which keeps track of
|
|
the calls that have a time limit and automatically ends them when
|
|
they exceed it. This application receives requests from &kamailio;
|
|
and makes requests to a rating engine (see below) to find out if
|
|
a call needs to be limited or not. When a call ends (or is ended)
|
|
it will also instruct the rating engine to debit the balance for
|
|
the caller with the consumed amount. The callcontrol application
|
|
is available from http://callcontrol.ag-projects.com/
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
A rating engine that is used to calculate the time limit based on
|
|
the caller's credit and the destination price and to debit the
|
|
caller's balance after a call ends. This is available as part of
|
|
CDRTool from http://cdrtool.ag-projects.com/
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
|
|
<para>
|
|
The callcontrol application runs on the same machine as &kamailio; and they
|
|
communicate over a filesystem socket, while the rating engine can run on
|
|
a different host and communicates with the callcontrol application using
|
|
a TCP connection.
|
|
</para>
|
|
|
|
<para>
|
|
Callcontrol is invoked by calling the call_control() function for the
|
|
initial INVITE of every call we want to apply a limit to. This will end
|
|
up as a request to the callcontrol application, which will interogate
|
|
the rating engine for a time limit for the given caller and destination.
|
|
The rating engine will determine if the destination has any associated
|
|
cost and if the caller has any credit limit and if so will return the
|
|
amount of time he is allowed to call that destination. Otherwise it will
|
|
indicate that there is no limit associated with the call. If there is a
|
|
limit, the callcontrol application will retain the session and attach
|
|
a timer to it that will expire after the given time causing it to call
|
|
back to &kamailio; with a request to end the dialog. If the rating engine
|
|
returns that there is no limit for the call, the session is discarded
|
|
by the callcontrol application and it will allow it to go proceed any
|
|
limit. An appropriate response is returned to the call_control module
|
|
that is then returned by the call_control() function call and allows
|
|
the script to make a decision based on the answer.
|
|
</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Features</title>
|
|
<para>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
Very simple API consisting of a single function that needs to be
|
|
called once for the first INVITE of every call. The rest is done
|
|
automatically in the background using dialog callbacks.
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
Gracefully end dialogs when they exceed their time by triggering
|
|
a dlg_end_dlg request into the dialog module, that will generate
|
|
two BYE messages towards each endpoint, ending the call cleanly.
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
Allow parallel sessions using one balance per subscriber
|
|
</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>
|
|
Integrates with mediaproxy's ability to detect when a call does
|
|
timeout sending media and is closed. In this case the dlg_end_dlg
|
|
that is triggered by mediaproxy will end the callcontrol session
|
|
before it reaches the limit and consumes all the credit for a call
|
|
that died and didn't actually take place. For this mediaproxy has
|
|
to be used and it has to be started by engage_media_proxy() to be
|
|
able to keep track of the call's dialog and end it on timeout.
|
|
</para>
|
|
<para>
|
|
Even when mediaproxy is unable to end the dialog because it was
|
|
not started with engage_media_proxy(), the callcantrol application
|
|
is still able to detect calls that did timeout sending media, by
|
|
looking in the radius accounting records for entries recorded by
|
|
mediaproxy for calls that did timeout. These calls will also be
|
|
ended gracefully by the callcontrol application itself.
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Dependencies</title>
|
|
<section>
|
|
<title>&kamailio; Modules</title>
|
|
<para>
|
|
The following modules must be loaded before this module:
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
<emphasis>pv</emphasis> module
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<emphasis>dialog</emphasis> module
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>External Libraries or Applications</title>
|
|
<para>
|
|
The following libraries or applications must be installed before
|
|
running &kamailio; with this module loaded:
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
<emphasis>None</emphasis>.
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
</section>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Exported parameters</title>
|
|
<section id="call_control.p.disable">
|
|
<title><varname>disable</varname> (int)</title>
|
|
<para>
|
|
Boolean flag that specifies if callcontrol should be disabled. This
|
|
is useful when you want to use the same &kamailio; configuration in
|
|
two different contexts, one using callcontrol, the other not. In the
|
|
case callcontrol is disabled, calls to the call_control() function
|
|
will return a code indicating that there is no limit associated with
|
|
the call, allowing the use of the same configuration without changes.
|
|
</para>
|
|
|
|
<para>
|
|
<emphasis>
|
|
Default value is <quote>0</quote>.
|
|
</emphasis>
|
|
</para>
|
|
|
|
<example>
|
|
<title>Setting the <varname>disable</varname> parameter</title>
|
|
<programlisting format="linespecific">
|
|
...
|
|
modparam("call_control", "disable", 1)
|
|
...
|
|
</programlisting>
|
|
</example>
|
|
</section>
|
|
|
|
<section id="call_control.p.socket_name">
|
|
<title><varname>socket_name</varname> (string)</title>
|
|
<para>
|
|
The path to the filesystem socket where the callcontrol
|
|
application listens for commands from the module.
|
|
</para>
|
|
|
|
<para>
|
|
<emphasis>
|
|
Default value is
|
|
<quote>/var/run/callcontrol/socket</quote>.
|
|
</emphasis>
|
|
</para>
|
|
|
|
<example>
|
|
<title>Setting the <varname>socket_name</varname> parameter</title>
|
|
<programlisting format="linespecific">
|
|
...
|
|
modparam("call_control", "socket_name", "/var/run/callcontrol/socket")
|
|
...
|
|
</programlisting>
|
|
</example>
|
|
</section>
|
|
|
|
<section id="call_control.p.socket_time">
|
|
<title><varname>socket_timeout</varname> (int)</title>
|
|
<para>
|
|
How long time (in milliseconds) to wait for an answer from the
|
|
callcontrol application.
|
|
</para>
|
|
|
|
<para>
|
|
<emphasis>
|
|
Default value is <quote>500</quote> ms.
|
|
</emphasis>
|
|
</para>
|
|
|
|
<example>
|
|
<title>Setting the <varname>socket_timeout</varname> parameter</title>
|
|
<programlisting format="linespecific">
|
|
...
|
|
modparam("call_control", "socket_timeout", 500)
|
|
...
|
|
</programlisting>
|
|
</example>
|
|
</section>
|
|
|
|
<section id="call_control.p.signaling_ip_avp">
|
|
<title><varname>signaling_ip_avp</varname> (string)</title>
|
|
<para>
|
|
Specification of the AVP which holds the IP address from where
|
|
the SIP signaling originated. If this AVP is set it will be used
|
|
to get the signaling IP address, else the source IP address
|
|
from where the SIP message was received will be used.
|
|
This AVP is meant to be used in cases where there are more than
|
|
one proxy in the call setup path and the proxy that actually
|
|
starts callcontrol doesn't receive the SIP messages directly
|
|
from the UA and it cannot determine the NAT IP address from
|
|
where the signaling originated. In such a case attaching a
|
|
SIP header at the first proxy and then copying that header's
|
|
value into the signaling_ip_avp on the proxy that starts
|
|
callcontrol will allow it to get the correct NAT IP address
|
|
from where the SIP signaling originated.
|
|
</para>
|
|
|
|
<para>
|
|
This is used by the rating engine which finds the rates to apply to a
|
|
call based on caller's SIP URI, caller's SIP domain or caller's IP
|
|
address (whichever yields a rate forst, in this order).
|
|
</para>
|
|
|
|
<para>
|
|
<emphasis>
|
|
Default value is <quote>$avp(s:signaling_ip)</quote>.
|
|
</emphasis>
|
|
</para>
|
|
|
|
<example>
|
|
<title>Setting the <varname>signaling_ip_avp</varname> parameter</title>
|
|
<programlisting format="linespecific">
|
|
...
|
|
modparam("call_control", "signaling_ip_avp", "$avp(s:signaling_ip)")
|
|
...
|
|
</programlisting>
|
|
</example>
|
|
</section>
|
|
|
|
<section id="call_control.p.canonical_uri_avp">
|
|
<title><varname>canonical_uri_avp</varname> (string)</title>
|
|
<para>
|
|
Specification of the AVP which holds an optional application defined
|
|
canonical request URI. When this is set, it will be used as the
|
|
destination when computing the call price, otherwise the request URI
|
|
will be used. This is useful when the username of the ruri needs to
|
|
have a different, canonical form in the rating engine computation
|
|
than it has in the ruri.
|
|
</para>
|
|
|
|
<para>
|
|
<emphasis>
|
|
Default value is <quote>$avp(s:can_uri)</quote>.
|
|
</emphasis>
|
|
</para>
|
|
|
|
<example>
|
|
<title>Setting the <varname>canonical_uri_avp</varname> parameter</title>
|
|
<programlisting format="linespecific">
|
|
...
|
|
modparam("call_control", "canonical_uri_avp", "$avp(s:can_uri)")
|
|
...
|
|
</programlisting>
|
|
</example>
|
|
</section>
|
|
|
|
<section id="call_control.p.diverter_avp_id">
|
|
<title><varname>diverter_avp_id</varname> (string)</title>
|
|
<para>
|
|
Specification of the id of an integer AVP which holds an optional
|
|
application defined diverter SIP URI. When this is set, it will be
|
|
used by the rating engine as the billing party when finding the rates
|
|
to apply to a given call, otherwise, the caller's URI taken from the
|
|
From field will be used. When set, this AVP should contain a value in
|
|
the form <quote>user@domain</quote> (no sip: prefix should be used).
|
|
</para>
|
|
<para>
|
|
This is useful when a destination diverts a call, thus becoming the
|
|
new caller. In this case the billing party is the diverter and this
|
|
AVP should be set to it, to allow the rating engine to pick the right
|
|
rates for the call. For example, if A calls B and B diverts all its
|
|
calls unconditionally to C, then the diverter AVP should the set to
|
|
B's URI, because B is the billing party in the call not A after the
|
|
call was diverted.
|
|
</para>
|
|
|
|
<para>
|
|
<emphasis>
|
|
Default value is <quote>805</quote>.
|
|
</emphasis>
|
|
</para>
|
|
|
|
<example>
|
|
<title>Setting the <varname>diverter_avp_id</varname> parameter</title>
|
|
<programlisting format="linespecific">
|
|
...
|
|
modparam("call_control", "diverter_avp_id", 805)
|
|
|
|
route {
|
|
...
|
|
# alice@example.com is paying for this call
|
|
$avp(i:805) = "sip:alice@example.com";
|
|
...
|
|
}
|
|
...
|
|
</programlisting>
|
|
</example>
|
|
</section>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Functions</title>
|
|
<section id="call_control.f.call_control">
|
|
<title><function moreinfo="none">call_control()</function></title>
|
|
<para>
|
|
Trigger the use of callcontrol for the dialog started by the INVITE
|
|
for which this function is called (the function should only be called
|
|
for the first INVITE of a call). Further in-dialog requests will be
|
|
processed automatically using internal bindings into the dialog state
|
|
machine, allowing callcontrol to update its internal state as the
|
|
dialog progresses, without any other intervention from the script.
|
|
</para>
|
|
|
|
<para>
|
|
This function should be called right before the message is sent out
|
|
using t_relay(), when all the request uri modifications are over and
|
|
a final destination has been determined.
|
|
</para>
|
|
|
|
<para>This function has the following return codes:</para>
|
|
<para>
|
|
<itemizedlist>
|
|
<listitem><para>
|
|
+2 - call has no limit
|
|
</para></listitem>
|
|
<listitem><para>
|
|
+1 - call has limit and is traced by callcontrol
|
|
</para></listitem>
|
|
<listitem><para>
|
|
-1 - not enough credit to make the call
|
|
</para></listitem>
|
|
<listitem><para>
|
|
-2 - call is locked by another call in progress
|
|
</para></listitem>
|
|
<listitem><para>
|
|
-5 - internal error (message parsing, communication, ...)
|
|
</para></listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
|
|
<para>
|
|
This function can be used from REQUEST_ROUTE.
|
|
</para>
|
|
|
|
<example>
|
|
<title>Using the <function>call_control</function> function</title>
|
|
<programlisting format="linespecific">
|
|
|
|
...
|
|
if (is_avp_set("$avp(i:805)")) {
|
|
# the diverter AVP is set, use it as billing party
|
|
$avp(s:billing_party_domain) = $(avp(i:805){uri.domain});
|
|
} else {
|
|
$avp(s:billing_party_domain) = $fd;
|
|
}
|
|
|
|
if (method==INVITE && !has_totag() &&
|
|
is_domain_local("$avp(s:billing_party_domain)")) {
|
|
call_control();
|
|
switch ($retcode) {
|
|
case 2:
|
|
# Call with no limit
|
|
case 1:
|
|
# Call has limit and is under callcontrol management
|
|
break;
|
|
case -1:
|
|
# Not enough credit (prepaid call)
|
|
sl_send_reply("402", "Not enough credit");
|
|
exit;
|
|
break;
|
|
case -2:
|
|
# Locked by another call in progress (prepaid call)
|
|
sl_send_reply("403", "Call locked by another call in progress");
|
|
exit;
|
|
break;
|
|
default:
|
|
# Internal error (message parsing, communication, ...)
|
|
if (PREPAID_ACCOUNT) {
|
|
xlog("Call control: internal server error\n");
|
|
sl_send_reply("500", "Internal server error");
|
|
exit;
|
|
} else {
|
|
xlog("L_WARN", "Cannot set time limit for postpaid call\n");
|
|
}
|
|
}
|
|
}
|
|
t_relay();
|
|
...
|
|
</programlisting>
|
|
</example>
|
|
</section>
|
|
</section>
|
|
|
|
</chapter>
|
|
|