Freeswitch: mod_dptools: limit

Устанавливает ограничение (лимит) входящих/исходящих вызовов для определенного ресурса.

Limit управляет ограничением для ресурса. Ресурсом может выступать любой уникальный идетификатор (переменная) инициализируемый во время произвольного вызова.

Когда лимит достигнут, вызов автоматически переводится на расширение «limit_exceeded» в текущем или произвольно заданное в другом контексте.

Обратите внимание, что ограничение активно только в данном контексте — т.е., если вы переводите входящий вызов из диалплана public на добавочный номер в диалплане default — любое ограничение, которое вы только что установили в диалплане default, будет сброшено. Если лимит установлен на номер назначения, а затем вызов переводится на другой добавочный номер даже в том же контексте, лимит будет уменьшен, несмотря на то что вызов еще продолжается.

Также лимит не будет уменьшен, если вы перевели вызов с использованием метода «REFER», например кнопкой TRANSFER телефонного аппарата.

Приложения диалплана

Несколько приложений диалплана реализуют разные варианты ограничений (limit).

Freeswitch: mod: mod_dptools.

Выполняет приложение диалплана transfer, если лимит достигнут.

limit <backend> <realm> <resource> <max[/interval]> [<transfer_destination_number> [<dialplan> [<context>]]

backend

Хранилище использумое приложением limit, для сохранения состояний.

realm

Произвольное имя для лимита.

resource

Идентификатор ресурса по которому устанавливается ограничение вызовов. Например ресурсом может выступать имя транка (gateway) или переменная caller_id_number и т.д.

max

Максимальное кол-во вызовов разрешенное к пропуску или кол-во звонков в сек. Если не задано или задано отрицательное значение, то лимит действует только как счетчик.

Аргумент interval поддерживают только Freeswitch: mod_hash и hiredis хранилища (backend).

transfer_destination_number

Перевод на расширение диалплана. Опционально, если не задано будет переведено на расшрение 'limit_exceeded' в текущем диалплане и контексте.

Разновидностью приложения limit является limit_execute. Оно отличается тем, что выполняет заданное приложение диалплана, только если лимит не достигнут.

limit_execute <backend> <realm> <resource> <max[/interval]> <application> [application arguments]

backend

Хранилище использумое приложением limit, для хранения состояний.

realm

Произвольное и уникальное имя для лимита.

resource

Идентификатор ресурса по которому устанавливается ограничение вызовов. Например ресурсом может выступать имя транка (gateway) или переменная caller_id_number и т.д.

max

Максимальное кол-во одновременных запусков или кол-во выполнений в интервале/сек. Если не задано или задано отрицательное значение, то лимит действует только как счетчик. Если установить лимит равным нулю, то ничего выполнено не будет.

Аргумент interval поддерживают только Freeswitch: mod_hash и hiredis хранилища (backend).

application

Приложение которое будет выполнено, если лимит не достигнут.

application arguments

Аргументы приложения

API

API приложения limit. Вы также можете использовать команды API в диалплане:

<action application="set" data="api_result=${limit_usage(<backend> <realm> <id>)}"/>

limit_reset

Сбросить все лимиты для заданного хранилища (backend).

  • DB: удалить всё на этом хосте.
  • Hiredis: не применимо. 
    • чтобы сбросить используйте API Hiredis:
    • hiredis_raw set <resource_name> 0
  • Hash: не применимо.
limit_reset <backend>

limit_status

Получить текущий статус данного хранилища (backend). Поддерживается только для DB.

limit_status <backend>

limit_usage

Получить текущее значение счётчика.

limit_usage <backend> <realm> <id>

uuid_limit_release

Вручную уменьшить счетчик на 1, для данного UUID. (Где и как получить UUID не уточняется)
Если realm/resource назначен, уменьшает только данный лимит, в обратном случая удаляет все лимиты содежащие данный UUID (опять же чего UUID?)

uuid_limit_release <uuid> <backend> [realm] [resource]

limit_interval_reset

Вручную сбросить счётчик интервала к нулю, до старта следующего интервала: только для Freeswitch: mod_hash

limit_interval_reset <backend> <realm> <resource>

hash_remote

Вы можете получить доступ к ресурсам другого FS используя hash_remote. hash_remote API использует ESL. Настройте сервера и данные доступа в conf/autoload_configs/hash.conf.xml

<configuration name="hash.conf" description="Hash Configuration">
  <remotes>
	<!-- List of hosts from where to pull usage data -->
	<remote name="test11" host="10.10.10.11" port="8021" password="ClueCon" interval="5000" />
  </remotes>
</configuration>

Удаленный сервер будет опрашиваться каждые 5 секунд и добавлять limit hash c удалённых серверов на текущий сервер. Таким образом, получить данные удаленного сервера, в дальнейшем можно стандартными командами API.

hash_remote <list>|<kill> [name]|<rescan>

Следующие переменные Freeswitch: Channel Variables будут заданы, при вызове limit.

  • «limit_realm»
  • «limit_id»
  • «limit_max»

Эти переменные канала могут быть использованы для обращения к Limit по uuid, realm и id,

Переменные затрагиваемые Limit

  • limit_ignore_transfer=true - используйте, если вызов принят через транк (gateway) и переведен на расширение (extension), но счетчик не должен быть изменен.
  • limit_ignore_transfer=false - в обратном случае счетчик будет уменьшен.

Backends

db

mod_db - реализация обращения через API и Диалплан к хранилищу данных. База данных может быть squlite или ODBC. Преимущество db хранилища в возможности использовать единую БД для кластера серверов.

hash

mod_hash - как и следует из названия модуль реализует хранение данных в хэш таблице, гарантируя наибыстрейший доступ к данным среди всех представленных бэкендов.
Используйте следующий синтаксис в диалплане:

<action application="limit" data="hash <realm> <id> [<max>[/<interval>] [number [dialplan [context]]]" />

Установите ограничение вызовов в заданный интервал, например 5/1 установит лимит в 5 звонков в секунду.
Приложение переводит вызов на указанное расширение/диалплан/контекст если лимит достиг предела.
Вы можете указать приложению класть трубку автоматически указав ! перед расширением/номером, в этом случае расширение будет интерпретировано как hangup cause.

<action application="limit" data="hash inbound 15142223333 5/1 !USER_BUSY" />

Если максимум не задан, то лимит будет работать как счётчик без ограничения вызовов.

hiredis

mod_hiredis https://redis.io - используйте преимущества nosql базы данных для бэкенда.
Предположим нам надо сравнить набираемый номер с базой содержащей несколько миллионов номеров, например База перешедших абонентов содержит на момент написания этих строк 17 897 525 номеров (25-07-2023).
Поиск в sql субд может занять в 10 раз больше времени, чем в nosql. Таким образом redis предпочтительно использовать для для поиска однострочных данных, а скорость и низкая стоимость ресурсов для выполнения запроса, будет главным преимуществом этого бэкенда.
Маршрутизация при помощи redis

Какой бэкенд лучше использовать?

BackendSpeed Постоянные данные Cluster-abilityInterval Support
Hash fastestno hash_remoteyes
DB slow yes possible no
Hiredisfast yes, configurableyes yes

Примеры

Ограничение доступа к приложению

Иногда требуется ограничить доступ к приложению, например при попытке вызова через транк (gateway), используйте для этого limit_execute:

<action application="limit_execute" data="hash <realm> <id> <max>[/<interval>] <application> <data>" />

в следующем случае имеется 2 транка и кол-во одновременных вызовов через каждый транк не должно превышать 5-ти:

<extension name="outbound">
 <condition field="destination_number" expression="^1?[2-9]\d{2}[2-9]\d{6}$">
  <action application="limit_execute" data="hash outbound gateway1 5 bridge sofia/gateway/gateway1/${destination_number}" />
  <action application="limit_execute" data="hash outbound gateway2 5 bridge sofia/gateway/gateway2/${destination_number}" />
 </condition>
</extension>

Ограничение одновременных вызовов пользователя

Следующий пример ограничивает одновременные вызовы пользователя домена до 1 звонка. В данном примере переменная max_calls задана равной 1 в диалплане, но вы можете назначить ее как глобальную переменную в vars.xml или как пользовательскую переменную в directory.xml.

<extension name="limit_exceeded">
 <condition field="destination_number" expression="^limit_exceeded$">
	<action application="playback" data="/sounds/overthelimit.wav"/>
	<action application="hangup"/>
 </condition>
</extension>
<extension name="limit" continue="true">
 <condition>
 <!-- You can set ${max_calls} per user in directory.conf and remove the line below -->
	<action application="set" data="max_calls=1" inline="true"/>
	<action application="limit" data="db $${domain} ${sip_auth_username} ${max_calls}"/>
 </condition>
</extension>

Обратите внимание, что расширение limit_exceeded задано до расширения limit, т.к. приложение limit() использует transfer(), которое перечитывает диалплан сначала. Это важно во избежание трансферной петли, чтобы вызов снова и снова не попадал в расширение limit. Иначе эту проблему можно решить используя regex в condition расширения limit.

Ограничение скорости набора, Anti-SPIT(Спам)

Ограничим кол-во вызовов в секунду по IP и номеру:

	<action application="set" data="calls_per_second=2" />
	<action application="limit" data="hash ${sip_received_ip} ${destination_number} ${calls_per_second}/1" />

Ограничить вызовы до 5-ти в 10 минут:

	<action application="limit" data="hash ${sip_received_ip} ${destination_number} 5/600" />

User Busy

This checks the current usage a limit counter before dialing a user, conditionally returning user_busy or putting the call through under a specific condition over the value of this counter. This can be useful if you wish to check against a counter managed by an external application or incremented by other events.

	<action application="bridge" data="${cond(${limit_usage(db time_spent in_bed)} <= 60 ? error/user_busy : user/$1)}" />

Note: The above action will not increment the limit counter.

Note: In recent versions, in order for this function to return a non-zero value you must call the limit application before. It should set some limit for this resource (even -1 which is unlimited) in order to enable counting.

If you wish to set a limit bound to the b-leg part of a call (ex: outgoing counter), it is only possible using loopback channels. The following will do so:

	<action application="set" data="destnum=${destination_number}" />
	<action application="bridge" data="loopback/context/gw1,loopback/context/gw2" />

And inside the corresponding context:

<extension name="gw1">
	<condition field="destination_number" expression="gw1">
		<action application="limit" data="db outgoing gw1 10" />
		<action application="bridge" data="sofia/gateway/gw1/${destnum}" />
	</condition>
</extension>
<extension name="gw2">
	<condition field="destination_number" expression="gw2">
		<action application="limit" data="db outgoing gw2 5" />
		<action application="bridge" data="sofia/gateway/gw2/${destnum}" />
	</condition>
</extension>

The result of this example is that if the first gateway has too many channels open, then it cleans up the limit data for the first gateway before trying the next gateway.

If this was done within an extension as a series of limit and bridge apps, then the limit data wouldn't be cleaned up until the a-leg returned to the CS_ROUTING state. That would mean calls would continue holding a channel open on a gateway they had tried while they were still connected to another gateway.

Example below, things to note:

  • auto_hunt=true so that you can jump directly to extensions without going through the entire dialplan
  • Replace PROVIDER1..3 with the relevant gateways
  • Replace PROVIDER1..3_CHANNEL_LIMIT with the relevant channel limit
  • transfer is done after the bridge so that failover works between the providers, ie, PROVIDER1 may be down
  • This example is only for 10 digit US phone numbers, please adapt as needed to other dialplans

 

<extension name="Outbound calls">
<!-- support calls to 10 digit US phone numbers directly -->
	<condition field="destination_number" expression="^(\d{10})$" break="on-true">
		<action application="set" data="continue_on_fail=true"/>
		<action application="set" data="hangup_after_bridge=true"/>
		<action application="enum" data="1$1 e164.arpa"/>
		<action application="bridge" data="${enum_auto_route}"/>
		<action application="enum" data="1$1 e164.org"/>
		<action application="bridge" data="${enum_auto_route}"/>
		<action application="enum" data="1$1 nrenum.net"/>
		<action application="bridge" data="${enum_auto_route}"/>
		<action application="set" data="auto_hunt=true"/>
		<action application="limit" data="$${domain} gw_PROVIDER1 PROVIDER1_CHANNEL_LIMIT usdirect2"/>
		<action application="bridge" data="sofia/gateway/PROVIDER1/1$1"/>
		<action application="transfer" data="usdirect2"/>
	</condition>
</extension>
<extension name="usdirect2">
	<condition field="destination_number" expression="^usdirect2$"/>
	<condition field="rdnis" expression="^(\d{10}$)">
		<action application="limit" data="db $${domain} gw_PROVIDER2 PROVIDER2_CHANNEL_LIMIT usdirect3"/>
		<action application="bridge" data="sofia/gateway/PROVIDER2/1$1"/>
		<action application="transfer" data="usdirect3"/>
	</condition>
</extension>
<extension name="usdirect3">
	<condition field="destination_number" expression="^usdirect3$"/>
	<condition field="rdnis" expression="^(\d{10}$)">
		<action application="limit" data="db $${domain} gw_PROVIDER3 PROVIDER3_CHANNEL_LIMIT"/>
		<action application="bridge" data="sofia/gateway/PROVIDER2/1$1"/>
	</condition>
</extension>
<extension name="limit_exceeded">
	<condition field="destination_number" expression="^limit_exceeded$">
		<action application="playback" data="/sounds/overthelimit.wav"/>
		<action application="hangup"/>
	</condition>
</extension>

To get the number of concurrent calls per gateway ip address, you may issue a command as follows:

To monitor "inbound"
# fs_cli -x 'limit_usage db inbound 1.2.3.4'
To monitor "outbound
# fs_cli -x 'limit_usage db outbound 5.6.7.8'

Example Dialplan:

<extension name="customer_a">
	<condition field="network_addr" expression="^1\.2\.3\.4$"/>
	<condition field="destination_number" expression="^(.*)$">
		<action application="limit" data="db inbound 1.2.3.4 10000" />
		<action application="limit_execute" data="db outbound 5.6.7.8 10000 bridge sofia/gateway/5.6.7.8/$1"/>
	</condition>
</extension>

If you have the default configs then locate the Local_Extension in conf/dialplan/default.xml. Add this line right after the condition:

	<action application="limit" data="hash ${domain} $1 1 handle_over_limit XML over_limit_actions"/>

Then add this new file as «limits.xml» in conf/dialplan/ :

<include>
	<context name="over_limit_actions">
		<extension name="oops, too many calls for this one">
			<condition field="destination_number" expression="handle_over_limit">
				<action application="answer"/>
				<action application="playback" data="ivr/ivr-no_no_no.wav"/>
				<action application="hangup" data="USER_BUSY"/>
			</condition>
		</extension> 
	</context>
</include>

Now when you call a local extension it won't allow more than one call. Note that you can change the value in the limit's data argument. For example, this would cause a limit of 4 concurrent calls, sending the 5th call into «oops, too many calls» extension:

<action application="limit" data="hash ${domain} $1 4 handle_over_limit XML over_limit_actions"/>

Here is another example that when this person makes a call to a 7-digit number or does 1+ 7 or more digits, it will add to his limit totals. However, if he just calls another 4-digit extension on the system then it won't add to his limit. Put it right after the «global» extension in default.xml:

<!-- set outound caller limit -->
<extension name="set outbound limit" continue="true">
	<condition field="destination_number" expression="^1?\d{7}" break="on-false"/>
	<condition field="caller_id_number" expression="^(10[01][0-9])">
		<action application="limit" data="hash ${domain} $1 4 handle_over_limit XML over_limit_actions"/>
		<action application="log" data="INFO Added limit for caller $1"/>
	</condition>
</extension>

Some examples of using limit to protect against toll fraud is here.

Here are some Dialplan Recipes using limit.

  • freeswitch/mod/mod_dptools/limit.txt
  • Последние изменения: 2023/08/01