ratelimit Module

Ovidiu Sas

Bogdan Vasile Harjoc

Hendrik Scholz

Edited by

Ovidiu Sas

Edited by

Bogdan Vasile Harjoc

Edited by

Hendrik Scholz

   Copyright © 2006 Freenet Cityline GmbH

   Copyright © 2008 VoIP Embedded Inc.
   Revision History
   Revision $Revision: 5906 $ $Date: 2009-07-21 10:45:05 +0300
                              (Tue, 21 Jul 2009) $
     __________________________________________________________

   Table of Contents

   1. Admin Guide

        1.1. Overview
        1.2. Use Cases
        1.3. Static Rate Limiting Algorithms

              1.3.1. Tail Drop Algorithm (TAILDROP)
              1.3.2. Random Early Detection Algorithm (RED)
              1.3.3. Network Algorithm (NETWORK)

        1.4. Dynamic Rate Limiting Algorithms

              1.4.1. Feedback Algorithm (FEEDBACK)

        1.5. Dependencies

              1.5.1. OpenSIPS Modules
              1.5.2. External Libraries or Applications

        1.6. Exported Parameters

              1.6.1. timer_interval (integer)
              1.6.2. queue (integer:string)
              1.6.3. pipe (integer:string:integer)
              1.6.4. reply_code (integer)
              1.6.5. reply_reason (string)

        1.7. Exported Functions

              1.7.1. rl_check([pvar])
              1.7.2. rl_check_pipe([pipe_no])
              1.7.3. rl_drop([[min ], max])

        1.8. Exported MI Functions

              1.8.1. rl_stats
              1.8.2. rl_set_pipe
              1.8.3. rl_get_pipes
              1.8.4. rl_set_queue
              1.8.5. rl_get_queues
              1.8.6. rl_set_pid
              1.8.7. rl_get_pid
              1.8.8. rl_push_load
              1.8.9. rl_set_dbg

        1.9. Known limitations

   List of Examples

   1.1. Set timer_interval parameter
   1.2. Set queue parameter
   1.3. Set pipe parameter
   1.4. Set reply_code parameter
   1.5. Set reply_reason parameter
   1.6. rl_check usage
   1.7. rl_check_pipe usage
   1.8. rl_drop usage

Chapter 1. Admin Guide

1.1. Overview

   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 OpenSIPS.

   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).

1.2. Use Cases

   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 OpenSIPS cluster when
   run on the dispatching box in front.

   A sample configuration snippet might look like this:
...
        if (is_method("INVITE|REGISTER|SUBSCRIBE") {
                if (!rl_check()) {
                        rl_drop();
                        exit;
                };
        };
...

   Upon every incoming request listed above rl_check 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.

1.3. Static Rate Limiting Algorithms

   The ratelimit module supports two different statc algorithms to
   be used by rl_check to determine whether a message should be
   blocked or not.

1.3.1. Tail Drop Algorithm (TAILDROP)

   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.

   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 retransmissed after given time, the same
   on all devices with the same firmware/by the same vendor.

1.3.2. Random Early Detection Algorithm (RED)

   Random Early Detection 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) OpenSIPS
   will return errors to the OpenSIPS 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 OpenSIPS might drop messages although the limit might
   not be reached within the interval. Decrease the timer interval
   if you encounter this.

1.3.3. Network Algorithm (NETWORK)

   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.

1.4. Dynamic Rate Limiting Algorithms

   When running OpenSIPS on different machines, one has to adjust
   the drop rates for the static algorithms to maintain a sub 100%
   load average or packets 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.

   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).

1.4.1. Feedback Algorithm (FEEDBACK)

   Using the PID Controller model (see Wikipedia page), 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).

   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.

   Generally though, as real life request rates drift by less,
   adapting should happen much faster.

1.5. Dependencies

1.5.1. OpenSIPS Modules

   The following modules must be loaded before this module:
     * No dependencies on other OpenSIPS modules.

1.5.2. External Libraries or Applications

   The following libraries or applications must be installed
   before running OpenSIPS with this module loaded:
     * None.

1.6. Exported Parameters

1.6.1. timer_interval (integer)

   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.

   IMPORTANT: A too small value may lead to performance penalties
   due to timer process overloading.

   Default value is 10.

   Example 1.1. Set timer_interval parameter
...
modparam("ratelimit", "timer_interval", 5)
...

1.6.2. queue (integer:string)

   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.

   To specify a queue that accepts all methods, use * 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.

   Example 1.2. Set queue parameter
...
# 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:*")
...

1.6.3. pipe (integer:string:integer)

   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.

   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).

   Example 1.3. Set pipe parameter
...
# 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")
...

1.6.4. reply_code (integer)

   The code of the reply sent by OpenSIPS while limiting.

   Default value is 503.

   Example 1.4. Set reply_code parameter
...
modparam("ratelimit", "reply_code", 505)
...

1.6.5. reply_reason (string)

   The reason of the reply sent by OpenSIPS while limiting.

   Default value is "Server Unavailable".

   Example 1.5. Set reply_reason parameter
...
modparam("ratelimit", "reply_reason", "Limiting")
...

1.7. Exported Functions

1.7.1.  rl_check([pvar])

   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 pseudo variabile. It is recommended to
   provide the pipe number via an integer pseudovariabile.

   The method will return an error code if the limit for the
   matched algorithm is reached.

   Meaning of the parameters is as follows:
     * pvar - the pseudovariable holding the pipe id to be used by
       ratelimit.

   This function can be used from REQUEST_ROUTE.

   Example 1.6. rl_check usage
...
        # perform queue/pipe match for current method
        if (!rl_check()) {
                rl_drop();
                exit;
        };
...
        # use pipe no 1 for the current method
        # set int pvar to 1
        $var(p) = 1;
        if (!rl_check("$var(p)")) {
                rl_drop();
                exit;
        };
...
        # use pipe no 1 for the current method
        # set str pvar to 1
        $var(p) = "1";
        if (!rl_check("$var(p)") {
                rl_drop();
                exit;
        };
...

1.7.2.  rl_check_pipe([pipe_no])

   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 method will return an error code if the limit for the
   matched algorithm is reached.

   Meaning of the parameters is as follows:
     * pipe_no - the pipe id to be used by ratelimit.

   This function can be used from REQUEST_ROUTE.

   Example 1.7. rl_check_pipe usage
...
        # perform queue/pipe match for current method
        if (!rl_check_pipe()) {
                rl_drop();
                exit;
        };
...
        # use pipe no 1 for the current method
        if (!rl_check_pipe("1") {
                rl_drop();
                exit;
        };
...

1.7.3.  rl_drop([[min ], max])

   For the current request, a "503 - Server Unavailable" reply is
   sent back. The reply may or may not have a "Retry-After"
   header. If no parameter is given, there will be no
   "Retry-After" header. If only the max parameter is given, the
   reply will contain a "Retry-After: max" header. If both min and
   max params are given, the reply will contain a "Retry-After:
   random" header with random being a random value between the
   given min and max.

   Meaning of the parameters is as follows:
     * min - the minimum value of "Retry-After" header.
     * max - the maximum value of "Retry-After" header.

   This function can be used from REQUEST_ROUTE.

   Example 1.8. rl_drop usage
...
        if (!rl_check()) {
                # send back a "503 - Server Unavailable"
                # with a "Retry-After: 5"
                rl_drop("5");
                exit;
        };
...

1.8. Exported MI Functions

1.8.1.  rl_stats

   Lists the parameters and variabiles in the ratelimit module.

   Name: rl_stats

   Parameters: none

   MI FIFO Command Format:
                :rl_stats:_reply_fifo_file_
                _empty_line_

1.8.2.  rl_set_pipe

   Sets the pipe parameters for the given pipe id.

   Name: rl_set_pipe

   Parameters:
     * pipe_id - pipe id.
     * pipe_algorithm - the algorithm assigned to the given pipe
       id.
     * pipe_limit - the limit assigned to the given pipe id.

   MI FIFO Command Format:
                :rl_set_pipe:_reply_fifo_file_
                2
                RED
                10
                _empty_line_

1.8.3.  rl_get_pipes

   Gets the list of in use pipes.

   Name: rl_get_pipes

   Parameters: none

   MI FIFO Command Format:
                :rl_get_pipes:_reply_fifo_file_
                _empty_line_

1.8.4.  rl_set_queue

   Sets the queue parameters for the given queue id.

   Name: rl_set_queue

   Parameters:
     * queue_id - queue id.
     * queue_method - the method assigned to the given queue id.
     * pipe_id - the pipe id assigned to the given queue id.

   MI FIFO Command Format:
                :rl_set_queue:_reply_fifo_file_
                3
                INVITE
                2
                _empty_line_

1.8.5.  rl_get_queues

   Gets the list of in use queues.

   Name: rl_get_queues

   Parameters: none

   MI FIFO Command Format:
                :rl_get_queues:_reply_fifo_file_
                _empty_line_

1.8.6.  rl_set_pid

   Sets the PID Controller parameters for the Feedback Algorithm.

   Name: rl_set_pid

   Parameters:
     * ki - the integral parameter.
     * kp - the proportional parameter.
     * kd - the derivative parameter.

   MI FIFO Command Format:
                :rl_set_pid:_reply_fifo_file_
                0.5
                0.5
                0.5
                _empty_line_

1.8.7.  rl_get_pid

   Gets the list of in use PID Controller parameters.

   Name: rl_get_pid

   Parameters: none

   MI FIFO Command Format:
                :rl_get_pid:_reply_fifo_file_
                _empty_line_

1.8.8.  rl_push_load

   Force the value of the load parameter. This methos is usefull
   for testing the Feedback algorithm.

   Name: rl_push_load

   Parameters:
     * load - the forced value of load (it must be greater then
       0.0 and smaller then 1.0).

   MI FIFO Command Format:
                :rl_push_load:_reply_fifo_file_
                0.85
                _empty_line_

1.8.9.  rl_set_dbg

   This MI function will enable/disable a WARNING debug log
   exposing the internal counters for each pipe (useful in
   monitoring the ratelimit internals).

   Name: rl_set_dbg

   Parameters:
     * dbg - the debug value (0 means disable and 1 means enable).

   MI FIFO Command Format:
                :rl_set_dbg:_reply_fifo_file_
                1
                _empty_line_

1.9. Known limitations

   The pipes and queues are stored as static vectors, so no more
   than MAX_PIPES/MAX_QUEUES can be added without recompilation.
     * MAX_PIPES - 16
     * MAX_QUEUES - 10
