<?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>
	<title>Overview</title>
	<para>
		This module implements rate limiting for SIP requests. In contrast to
		the PIKE module this limits the flow based on a per SIP request type
		basis and not per source IP. The MI interface can be used to
		change tunables while running &kamailio;.
	</para>
	<para>
		The module implements the pipe/queue policy from BSD's ipfw manual,
		with some simplifications. In principle, each specified method is
		associated with its own queue and a number of queues are connected
		to a certain pipe (see the queue and pipe params).
	</para>
	<para>
		Please also take a look at the <quote>pipelimit</quote> module,
		that implements the pipe policy with database support. Note that it 
		doesn't implement the queues that exist in this module.
	</para>
	</section>
	<section>
	<title>Use Cases</title>
	<para>
		Limiting the rate messages are processed on a system directly
		influences the load. The ratelimit module can be used to protect a
		single host or to protect an &kamailio; cluster when run on the dispatching
		box in front.
	</para>
	<para>
		A sample configuration snippet might look like this:
	</para>
	<programlisting format="linespecific">
...
	if (is_method("INVITE|REGISTER|SUBSCRIBE") {
		if (!rl_check()) {
			append_to_reply("Retry-After: 5\r\n");
			sl_send_reply("503","Limiting");
			exit;
		};
	};
...
	</programlisting>
	<para>
		Upon every incoming request listed above <function>rl_check</function>
		is invoked. It returns an OK code if the current per request load is below the
		configured threshold. If the load is exceeded the function returns an
		error and an administrator can discard requests with a stateless
		response.
	</para>
	</section>
	<section>
	<title>Static Rate Limiting Algorithms</title>
	<para>
		The ratelimit module supports two different statc algorithms
		to be used by rl_check to determine whether a message should be
		blocked or not.
	</para>
	<section>
		<title>Tail Drop Algorithm (TAILDROP)</title>
		<para>
		This is a trivial algorithm that imposes some risks when used in
		conjunction with long timer intervals. At the start of each interval
		an internal counter is reset and incremented for each incoming
		message. Once the counter hits the configured limit rl_check returns
		an error.
		</para>
		<para>
		The downside of this algorithm is that it can lead to SIP client
		synchronization. During a relatively long interval only the first
		requests (i.e. REGISTERs) would make it through. Following messages
		(i.e. RE-REGISTERs) will all hit the SIP proxy at the same time when a
		common Expire timer expired. Other requests will be retransmitted
		after a given time, the same on all devices with the same firmware/by
		the same vendor.
		</para>
	</section>
	<section>
		<title>Random Early Detection Algorithm (RED)</title>
		<para>
		The Random Early Detection Algorithm tries to circumvent the synchronization
		problem imposed by the tail drop algorithm by measuring the average load and
		adapting the drop rate dynamically. When running with the RED algorithm
		(enabled by default) &kamailio; will return errors to the &kamailio;
		routing engine every n'th packet trying to evenly spread the measured
		load of the last timer interval onto the current interval. As a
		negative side effect &kamailio; might drop messages although the limit might
		not be reached within the interval. Decrease the timer interval if you
		encounter this.
		</para>
	</section>
	<section>
		<title>Network Algorithm (NETWORK)</title>
		<para>
		This algorithm relies on information provided by network interfaces.
		The total amount of bytes waiting to be consumed on all the network
		interfaces is retrieved once every timer_interval seconds.
		If the returned amount exceeds the limit specified in the modparam,
		rl_check returns an error. 
		</para>
	</section>
	<section>
		<title>Dynamic Rate Limiting Algorithms</title>
		<para>
		When running &kamailio; on different machines, one has to adjust the drop
		rates for the static algorithms to maintain a sub 100% load average or
		packets will start getting dropped in the network stack.  While this is not
		in itself difficult, it isn't neither accurate nor trivial: another
		server taking a notable fraction of the CPU time will require re-tuning
		the parameters.
		</para>
		<para>
		While tuning the drop rates from the outside based on a certain factor
		is possible, having the algorithm run inside ratelimit permits tuning
		the rates based on internal server parameters and is somewhat more
		flexible (or it will be when support for external load factors - as
		opposed to cpu load - is added).
		</para>
	</section>
	<section>
		<title>Feedback Algorithm (FEEDBACK)</title>
		<para>
		Using the PID Controller model
		(see <ulink url='http://en.wikipedia.org/wiki/PID_controller'>Wikipedia page</ulink>),
		the drop rate is adjusted dynamically based on the load factor so that
		the load factor always drifts towards the specified limit (or setpoint,
		in PID terms).
		</para>
		<para>
		As reading the CPU load average is relatively expensive (opening /proc/stat,
		parsing it, etc), this only happens once every timer_interval seconds and
		consequently the FEEDBACK value is only at these intervals recomputed. This
		in turn makes it difficult for the drop rate to adjust quickly.  Worst case
		scenarios are request rates going up/down instantly by thousands - it takes
		up to 20 seconds for the controller to adapt to the new request rate.
		</para>
		<para>
		Generally though, as real life request rates drift by less, adapting should
		happen much faster.
		</para>
	</section>
	</section>
	<section>
	<title>Dependencies</title>
	<section>
		<title>&kamailio; Modules</title>
		<para>
		The following modules must be loaded before this module:
			<itemizedlist>
			<listitem>
			<para>
				<emphasis>SL: Stateless request handling</emphasis>.
			</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>Parameters</title>
	<section>
		<title><varname>timer_interval</varname> (integer)</title>
		<para>
		The initial length of a timer interval in seconds. All amounts of
		messages have to be divided by this timer to get a messages per second
		value.
		</para>
		<para>
		IMPORTANT: A too small value may lead to performance penalties due to
		timer process overloading.
		</para>
		<para>
		<emphasis>
			Default value is 10.
		</emphasis>
		</para>
		<example>
		<title>Set <varname>timer_interval</varname> parameter</title>
		<programlisting format="linespecific">
...
modparam("ratelimit", "timer_interval", 5)
...
</programlisting>
		</example>
	</section>
	<section>
		<title><varname>queue</varname> (integer:string)</title>
		<para>
		The format of the queue parameter is "pipe_no:method". For each defined
		method, the algorithm defined by pipe number "pipe_no" will be used.
		</para>
		<para>
		To specify a queue that accepts all methods, use <quote>*</quote> instead of METHOD.
		As queues are matched against request methods, you will usually want to have
		this as the last queue added or other queues with specific methods will never
		match.  At this time, glob or regexp patterns are not supported.
		</para>
		<para>
		</para>
		<example>
		<title>Set <varname>queue</varname> parameter</title>
		<programlisting format="linespecific">
...
# assign pipe no 0 to method REGISTER
# assign pipe no 3 to method INVITE
# assign pipe no 2 to all other methods
modparam("ratelimit", "queue", "0:REGISTER")
modparam("ratelimit", "queue", "3:INVITE")
modparam("ratelimit", "queue", "2:*")
...
</programlisting>
		</example>
	</section>
	<section>
		<title><varname>pipe</varname> (integer:string:integer)</title>
		<para>
		The format of the pipe param is "pipe_no:algorithm:limit".  For each defined
		pipe, the given algorithm with the given limit will be used.
		</para>
		<para>
		A pipe is characterised by its algorithm and limit (bandwidth, in ipfw terms).
		When specifying a limit, the unit depends on the algorithm used and doesn't
		need to be spedified also (eg, for TAILDROP or RED, limit means packets/sec,
		whereas with the FEEDBACK algorithm, it means [CPU] load factor).
		</para>
		<example>
		<title>Set <varname>pipe</varname> parameter</title>
		<programlisting format="linespecific">
...
# define pipe 0 with a limit of 200 pkts/sec using TAILDROP algorithm
# define pipe 1 with a limit of 100 pkts/sec using RED algorithm
# define pipe 2 with a limit of 50 pkts/sec using TAILDROP algorithm
# define pipe 3 with a limit of load factor 80 using FEEDBACK algorithm
# define pipe 4 with a limit of 10000 pending bytes in the rx_queue
#                                     using NETWORK algorithm
modparam("ratelimit", "pipe", "0:TAILDROP:200")
modparam("ratelimit", "pipe", "1:RED:100")
modparam("ratelimit", "pipe", "2:TAILDROP:50")
modparam("ratelimit", "pipe", "3:FEEDBACK:80")
modparam("ratelimit", "pipe", "4:NETWORK:10000")
...
</programlisting>
		</example>
	</section>
	</section>
	<section>
	<title>Functions</title>
	<section>
		<title>
		<function moreinfo="none">rl_check([pvar])</function>
		</title>
		<para>
		Check the current request against the matched ratelimit algorithm.  If no
		parameter is provided, the queue will be matched based on method type, and
		then the pipe will be identified based on the matched queue.  If a pipe number
		is provided as a parameter, then the given pipe number will be used for
		identifying the ratelimit algorithm.
		The pipe number must be provided via a pseudovariable.  It is recommended to
		provide the pipe number via an integer pseudovariable.
		</para>
		<para>The method will return an error code if the limit for the matched
		algorithm is reached.
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
			<listitem><para>
			<emphasis>pvar</emphasis> - the pseudovariable holding the pipe id to
						    be used by ratelimit.
			</para></listitem>
		</itemizedlist>
		<para>
		This function can be used from REQUEST_ROUTE.
		</para>
		<example>
		<title><function>rl_check</function> usage</title>
		<programlisting format="linespecific">
...
	# perform queue/pipe match for current method
	if (!rl_check()) {
		append_to_reply("Retry-After: 5\r\n");
		sl_send_reply("503","Limiting");
		exit;
	};
...
	# use pipe no 1 for the current method
	# set int pvar to 1
	$var(p) = 1;
	if (!rl_check("$var(p)")) {
		append_to_reply("Retry-After: 5\r\n");
		sl_send_reply("503","Limiting");
		exit;
	};
...
	# use pipe no 1 for the current method
	# set str pvar to 1
	$var(p) = "1";
	if (!rl_check("$var(p)") {
		append_to_reply("Retry-After: 5\r\n");
		sl_send_reply("503","Limiting");
		exit; 
	};
...
</programlisting>
		</example>
	</section>
	<section>
		<title>
		<function moreinfo="none">rl_check_pipe([pipe_no])</function>
		</title>
		<para>
		Check the current request against the matched ratelimit algorithm.  If no
		parameter is provided, the queue will be matched based on method type, and
		then the pipe will be identified based on the matched queue.  If a pipe number
		is provided as a parameter, then the given pipe number will be used for
		identifying the ratelimit algorithm.
		</para>
		<para>The method will return an error code if the limit for the matched
		algorithm is reached.
		</para>
		<para>Meaning of the parameters is as follows:</para>
		<itemizedlist>
			<listitem><para>
			<emphasis>pipe_no</emphasis> - the pipe id to be used by ratelimit.
			</para></listitem>
		</itemizedlist>
		<para>
		This function can be used from REQUEST_ROUTE.
		</para>
		<example>
		<title><function>rl_check_pipe</function> usage</title>
		<programlisting format="linespecific">
...
	# perform queue/pipe match for current method
	if (!rl_check_pipe()) {
		append_to_reply("Retry-After: 5\r\n");
		sl_send_reply("503","Limiting");
		exit;
	};
...
	# use pipe no 1 for the current method
	if (!rl_check_pipe("1") {
		append_to_reply("Retry-After: 5\r\n");
		sl_send_reply("503","Limiting");
		exit; 
	};
...
</programlisting>
		</example>
	</section>
	</section>

	<section>
	<title>Exported RPC Functions</title>
	<section>
		<title>
		<function moreinfo="none">rl.stats</function>
		</title>
		<para>
		Lists the parameters and variables in the ratelimit module.
		</para>
		<para>
		Name: <emphasis>rl.stats</emphasis>
		</para>
		<para>Parameters: <emphasis>none</emphasis></para>
		<para>
		RPC Command Format:
		</para>
		<programlisting  format="linespecific">
		&sercmd; rl.stats
		</programlisting>
	</section>
	<section>
		<title>
		<function moreinfo="none">rl.set_pipe</function>
		</title>
		<para>
		Sets the pipe parameters for the given pipe id.
		</para>
		<para>
		Name: <emphasis>rl.set_pipe</emphasis>
		</para>
		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para>
			<emphasis>pipe_id</emphasis> - pipe id.
			</para></listitem>
			<listitem><para>
			<emphasis>pipe_algorithm</emphasis> - the
			algorithm assigned to the given pipe id.
			</para></listitem>
			<listitem><para>
			<emphasis>pipe_limit</emphasis> - the limit
			assigned to the given pipe id.
			</para></listitem>
		</itemizedlist>
		<para>
		RPC Command Format:
		</para>
		<programlisting  format="linespecific">
		&sercmd; rl.set_pipe 2 RED 10
		</programlisting>
	</section>
	<section>
		<title>
		<function moreinfo="none">rl.get_pipes</function>
		</title>
		<para>
		Gets the list of in use pipes.
		</para>
		<para>
		Name: <emphasis>rl.get_pipes</emphasis>
		</para>
		<para>Parameters:<emphasis>none</emphasis></para>
		<para>
		RPC Command Format:
		</para>
		<programlisting  format="linespecific">
		&sercmd; rl.get_pipes
		</programlisting>
	</section>
	<section>
		<title>
		<function moreinfo="none">rl.set_queue</function>
		</title>
		<para>
		Sets the queue parameters for the given queue id.
		</para>
		<para>
		Name: <emphasis>rl.set_queue</emphasis>
		</para>
		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para>
			<emphasis>queue_id</emphasis> - queue id.
			</para></listitem>
			<listitem><para>
			<emphasis>queue_method</emphasis> - the method
			assigned to the given queue id.
			</para></listitem>
			<listitem><para>
			<emphasis>pipe_id</emphasis> - the pipe id
			assigned to the given queue id.
			</para></listitem>
		</itemizedlist>
		<para>
		RPC Command Format:
		</para>
		<programlisting  format="linespecific">
		&sercmd; rl.set_queue 3 INVITE 2
		</programlisting>
	</section>
	<section>
		<title>
		<function moreinfo="none">rl.get_queues</function>
		</title>
		<para>
		Gets the list of in use queues.
		</para>
		<para>
		Name: <emphasis>rl.get_queues</emphasis>
		</para>
		<para>Parameters: <emphasis>none</emphasis></para>
		<para>
		RPC Command Format:
		</para>
		<programlisting  format="linespecific">
		&sercmd; rl.get_queues
		</programlisting>
	</section>
	<section>
		<title>
		<function moreinfo="none">rl.set_pid</function>
		</title>
		<para>
		Sets the PID Controller parameters for the Feedback Algorithm.
		</para>
		<para>
		Name: <emphasis>rl.set_pid</emphasis>
		</para>
		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para>
			<emphasis>ki</emphasis> - the integral parameter.
			</para></listitem>
			<listitem><para>
			<emphasis>kp</emphasis> - the proportional parameter.
			</para></listitem>
			<listitem><para>
			<emphasis>kd</emphasis> - the derivative parameter.
			</para></listitem>
		</itemizedlist>
		<para>
		RPC Command Format:
		</para>
		<programlisting  format="linespecific">
		&sercmd; rl.set_pid 0.5 0.5 0.5
		</programlisting>
	</section>
	<section>
		<title>
		<function moreinfo="none">rl.get_pid</function>
		</title>
		<para>
		Gets the list of in use PID Controller parameters.
		</para>
		<para>
		Name: <emphasis>rl.get_pid</emphasis>
		</para>
		<para>Parameters: <emphasis>none</emphasis></para>
		<para>
		RPC Command Format:
		</para>
		<programlisting  format="linespecific">
		&sercmd; rl.get_pid
		</programlisting>
	</section>
	<section>
		<title>
		<function moreinfo="none">rl.push_load</function>
		</title>
		<para>
		Force the value of the load parameter. This method is useful
		for testing the Feedback algorithm.
		</para>
		<para>
		Name: <emphasis>rl.push_load</emphasis>
		</para>
		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para>
			<emphasis>load</emphasis> - the forced value of load
			(it must be greater then 0.0 and smaller then 1.0).
			</para></listitem>
		</itemizedlist>
		<para>
		RPC Command Format:
		</para>
		<programlisting  format="linespecific">
		&sercmd; rl.push_load 0.85
		</programlisting>
	</section>
	<section>
		<title>
		<function moreinfo="none">rl.set_dbg</function>
		</title>
		<para>
		This function will enable/disable a WARNING debug log exposing the
		internal counters for each pipe (useful in monitoring the ratelimit internals).
		</para>
		<para>
		Name: <emphasis>rl.set_dbg</emphasis>
		</para>
		<para>Parameters:</para>
		<itemizedlist>
			<listitem><para>
			<emphasis>dbg</emphasis> - the debug value (0 means disable and
			1 means enable).
			</para></listitem>
		</itemizedlist>
		<para>
		RPC Command Format:
		</para>
		<programlisting  format="linespecific">
		&sercmd; rl.set_dbg 1
		</programlisting>
	</section>
	</section>

	<section>
	<title>Known limitations</title>
	<para>
	The pipes and queues are stored as static vectors, so no more than
	MAX_PIPES/MAX_QUEUES can be added without recompilation.
	<itemizedlist>
		<listitem><para>
		<emphasis>MAX_PIPES</emphasis> - 16
		</para></listitem>
		<listitem><para>
		<emphasis>MAX_QUEUES</emphasis> - 10
		</para></listitem>
	</itemizedlist>
	</para>
	</section>
</chapter>
