IPA.CONF(5)

НАИМЕНОВАНИЕ
ipa.conf -- конфигурационный файл для ipa(8)

ОПИСАНИЕ
Файл ipa.conf это конфигурационный файл для ipa(8). Этот файл или любой-другой файл, указанный в опции -f в командной строке ipa(8), обрабатывается когда ipa(8) начинает работать или получает сигнал SIGHUP.

ФОРМАТ ФАЙЛА
Файл ipa.conf может быть достаточно простым и довольно сложным. Главная идея заключается в том, чтобы разместить как можно больше установок в одном конфигурационном файле(ах), вместо того чтобы использовать внешние программы для решения задач связанных с обработкой статистики.

Эта страница документации содержит полное описание формата конфигурационного файла и полную документацию о всех возможностях ipa(8).

Почти после каждого параграфа приведен пример. Так как IPA пакет не содержит каких-либо модулей, то в примерах используются модули ipa_ipfw, ipa_ip6fw, ipa_atest и ipa_db_sdb, только лишь потому, что это были первые модули учёта и базы данных, разработанные для IPA.

  Общий синтаксис
Любая логическая строка в конфигурационном файле может быть записана в несколько текстовых строках для улучшения читаемости конфигурации. Нет никакого правила в какой строке размещать зарезервированные слова, аргументы или специальные символы. Если формат позволяет использовать один пробельный символ (пробел или символ табуляции), то можно использовать столько пробельных символов, сколько необходимо для улучшения форматирования конфигурационного файла. Все элементы в конфигурационном файле регистрозависимые. Конфигурационный файл состоит из секций, параметров и комментариев.

  Комментарии
Существуют shell-подобные и C-подобные комментарии. Если вы используете C-подобный комментарий в shell-подобном комментарии, то C-подобный комментарий игнорируется.

Пример:

# Shell-подобный комментарий.
/* C-подобный комментарий. */
/*
 * Другой C-подобный комментарий.
 */

  Секции и параметры
Секция состоит из имени секции, опциональных аргументов и тела секции. Тело секции должно быть заключено в фигурные скобки:

секция [[=] аргумент] {
    /* Параметры и секции. */
}

Параметр состоит из имени и опциональных аргументов. Каждый параметр должен заканчиваться символом `;' в конце списка своих аргументов:

параметр [[=] аргумент];

Символ `=' после имени секции и параметра опционален. Некоторые параметры похоже на переменные (для таких параметров естественно использовать символ `='), другие похожи на инструкции. В любом случае, в можете выбрать синтаксис, который вам наиболее подходит.

Аргумент может содержать строки:

"строка"

Возможно использовать последовательности ``\t'', ``\n'', ``\\'' и ``\"'', чтобы представить символ табуляции, символ новой строки, обратный слэш и двойную кавычку внутри строки. Если необходимо разделить строку на несколько линий (строк в файле), то используйте символ `\' в конце текущей линии (не добавляйте лишних пробельных символов после обратного слэша). Если строка записана в несколько линий без символов `\', то каждый символ новой строки добавляется в строку.

  Макропеременные
Определение макропеременной имеет следующую форму:

${переменная} = "строка";

Имя любой макропеременной может состоять из букв, цифр, символов '_' и знаков доллара. Что является буквой определяется функцией isalpha(3), которая использует текущую локаль.

Значением любой макропеременной должно быть строка, когда макропеременная расширяется, то первый и последний символы двойной кавычки убираются.

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

Есть несколько предопределённых макропеременных:

${$}         - символ `$';
${rule}      - имя текущего правила;
${limit}     - имя текущего лимита;
${sublimit}  - имя текущего подлимита;
${threshold} - имя текущего порога;
${autorule}  - имя текущего автоправила.

Любая макропеременная (включая предопределённые), за исключением ${$}, по необходимости может быть переопределена. Не рекомендуется переопределять или уничтожать предопределённые макропеременные в модулях.

Макропеременная ${$} не может быть использована для конструирования имён макропеременных (см. пример).

Макропеременные могут использоваться почте где-угодно в конфигурационном файле. Когда макропеременная расширяется, её значение расширяется рекурсивно. Макропеременные расширяются в момент их использования, а не в момент их определения. Макропеременные также расширяются в строках. Расширение макропеременных это свойство внутреннего парсера конфигурационного файла, поэтому макропеременная ${rule} не может быть использовано в автоправилах и шаблонах правил (см. информацию о подстановках в командных строках).

Пример:

${a} = "${b}";      # Определение ${a}.
${b} = "1";         # Определение ${b}.
param = ${a};       # Расширяется в 1.
${b} = "2";         # Переопределение ${b}.
param = ${a};       # Расширяется в 2.
param = "${$}{b}";  # Расширяется в ${b} (последовательность
                    # символов внутри строки).
section {
    ${a} = "1";     # Определение локальной ${a}, которая
                    # скрывает глобальную ${a}.
    ${c} = "4";     # Определение локальной ${c}.
    param = ${a};   # Расширяется в 1.
    subsection {
        ${a} = "2"; # Переопределение локальной ${a}.
        ${b} = "3"; # Переопределение глобальной ${b}.
    }
    param = ${a};   # Расширяется в 2.
    param = ${b};   # Расширяется в 3.
}
# param = ${c};     <-- Ошибка: ${c} не определена как глобальная.

  Включение файлов
Возможно хранить конфигурацию в нескольких файлах. Файлы могут быть включены при помощи следующих параметров:

include "/путь/файл";
include_files "/директория/шаблон";

Параметр include включает один файл. Параметр include_files включает несколько файлов из указанной директории, имена которых совпадают с указанным шаблоном.

Эти параметры могут быть использованы где угодно в конфигурационном файле, за исключением секций модулей, содержимое указанных файлов включается в конфигурацию немедленно. Возможно включать файлы из включённых файлов и т.д. Каждый включённый файл должен содержать корректно определённые параметры с аргументами, комментарии и секции с аргументами, но он может содержать незакрытые секции.

Возможно использовать регулярные выражения POSIX в качестве шаблонов в параметрах include_files, для этого установите параметр posix_re_pattern в ``yes'' перед параметрами, которые включают файлы с регулярными выражениями POSIX в качестве шаблонов (значение по умолчанию равно ``no''):

posix_re_pattern = <boolean>;

Этот параметр должен быть расположен отдельно.

Включаемые файлы должны иметь того владельца, кто запустил ipa(8) и не должны иметь прав на запись для группы и остальных пользователей. Если файлы включаются с помощью параметра include_files, то и директория указанная в этом параметре тоже должна иметь те же свойства.

Пример:

posix_re_pattern = yes;
include "/usr/local/etc/ipa.local.conf";
include_files "/usr/local/etc/ipa/LAN/.";

Первый параметр включает один файл, второй параметр включает каждый файл в данной директории, регулярное выражение POSIX ``.'' означает любой символ.

/* posix_re_pattern = no; */
include_files "/usr/local/etc/ipa/LAN/*";

Здесь используется шаблон shell. Первую строку необходимо раскомментировать, если перед этим использовались регулярные выражения POSIX.

  Использование модулей учёта
IPA модули учёта используются для сбора статистики. ipa(8) и модули учёта работают вместе с помощью ipa_ac_mod API, описанного в странице документации ipa_mod(3).

Параметр ac_mod динамически загружает указанный модуль учёта:

ac_mod "имя_файла";

Этот параметр должен быть расположен отдельно. Возможно использовать несколько модулей учёта одновременно.

Данное имя файла должно быть именем shared-object (shared library) файла если ipa(8) использует интерфейс dlopen(3) или это может быть .la файл если используется интерфейс библиотеки libtool ltdl.

Пример:

ac_mod "ipa_ipfw.so";
ac_mod "ipa_ip6fw.so";

Эти параметры загружают два модуля учёта.

  Использование модулей баз данных
IPA модули баз данных используются для хранения статистики. ipa(8) и модули баз данных работают вместе с помощью ipa_db_mod API, описанного в странице документации ipa_mod(3).

Параметр db_mod динамически загружает указанный модуль базы данных:

db_mod "имя_файла";

Этот параметр должен быть расположен отдельно. Возможно использовать несколько модулей баз данных одновременно.

Данное имя файла должно быть именем shared-object (shared library) файла если ipa(8) использует интерфейс dlopen(3) или это может быть .la файл если используется интерфейс библиотеки libtool ltdl.

Пример:

db_mod "ipa_db_sdb.so";

Этот параметр загружает один модуль базы данных.

  Конфигурирование модулей
Документация для IPA модуля должна давать всю информацию как конфигурировать модуль. Обычно конфигурация IPA модуля интегрируется в конфигурационный файл ipa.conf(5).

Каждый модуль имеет конфигурационный префикс, который используется для того, чтобы отличать секции и параметры модуля. Если параметр записан в форме:

префикс:параметр [[=] аргумент];

то ipa(8) попытается найти загруженный модуль с конфигурационным префиксом ``префикс'', затем передаст этот параметр для разбора найденному модулю.

Секции также могут иметь префиксы:

префикс:секция [[=] аргумент] {
    /* Параметры и секции модуля. */
}

В этом случае параметры и секции внутри такой секции должны быть записаны без префикса и они будут переданы соответствующему модулю для обработки.

Документация для модуля должна описывать сам модуль, конфигурационный префикс модуля, имя базы данных или системы учёта и все секции и параметры модуля.

Пример:

sdb: {
    allow_symlinks = yes;
}
ipfw:debug = 1;

Приведенное имя секции может показаться несколько странным, но здесь всё правильно. Модуль может иметь нулевые имена секций и параметров.

  Единицы статистики
Некоторые параметры и секции могут принимать аргументы в байтах, времени и 64-битных беззнаковых целых числах. В ipa_mod(3) такой тип данных определён как IPA_CONF_TYPE_VALUE. Иногда желательно использовать только один тип данных для таких значений, так как ``10'', ``10m'' и ``10M'' являются корректными типами данных и означают 10, 10 минут и 10 Мбайт соответственно.

Параметр value_units может быть использован для определения желаемого типа данных аргументов с типом данных IPA_CONF_TYPE_VALUE и для контроля их реальных значений:

value_units = <type>;

Этот параметр должен быть расположен отдельно. Доступны следующие значения: ``any'' (по умолчанию), ``time'', ``bytes'' и ``number''. Лучше размещать этот параметр перед другими параметрами и секциями.

Параметры limit, threshold, threshold_deviation и секция sublimit принимают аргументы с типом данных IPA_CONF_TYPE_VALUE.

  Правила учёта статистики
ipa(8) выполняет учёт статистика основываясь на правилах. Существуют статические и динамические правила. Статическое правило определяется в секции rule. Динамическое правило не имеет описания в конфигурационном файле, но любое динамическое правило генерируется на лету из некоторого автоправила, определённого в секции autorule.

Несколько правил (статических, динамических) могут разделять одни и те же установки. Существует несколько путей это сделать. Первый путь это использование секции global. Второй путь это использование секций rulepat (шаблоны правил). И третий путь это определение общих установок для динамических правил в секциях autorule.

Если какое-то правило (статическое, динамическое) не содержит установок для какой-то секции или параметра (динамические правила также наследуют установки от своих автоправил), то наследуются установки из подходящей секции rulepat, далее наследуются установки из секции global, если некоторые секции или параметра всё ещё не определены, то используются установки по умолчанию. Запустите ipa(8) с ключами -tt, чтобы увидеть реальные значения всех параметров.

Следующие параметры могут быть использованы в секциях global, rulepat, rule и autorule: ac_list, db_list, append_time, update_time, worktime, ctl_rule_acl, debug_exec, debug_limit, debug_limit_init, debug_threshold, debug_threshold_init.

  Использование систем учёта
Параметр ac_list определяет список используемых систем учёта:

ac_list = <список>;

<Список> это набор имён, разделённых пробельными символами. Чтобы получить имена систем учёта, прочтите документацию для модулей, которые вы указали в параметрах ac_mod.

Если какое-то правило содержит параметр ac_list, то системы учёта, перечисленные в его значении, будут опрашиваться для получения статистики для этого правила. Этот параметр позволяет создавать список систем учёта для каждого правила по отдельности.

Существует одна встроенная система учёта null: эта система учёта всегда возвращает в качестве статистики 0. Если параметр ac_list не определён, то используется система учёта null.

Пример:

ac_mod "ipa_ipfw.so";
ac_mod "ipa_ip6fw.so";
global {
    ac_list = ipfw ip6fw;
}

Здесь определены две системы учёта.

  Использование баз данных
Параметр db_list определяет список используемых баз данных:

db_list = <список>;

<Список> это набор имён, разделённых пробельными символами. Чтобы получить имена баз данных, прочтите документацию для модулей, которые вы указали в параметрах db_mod.

Если какое-то правило содержит параметр db_list, то базы данных, перечисленные в его значении, будут использовать для сохранения статистики для этого правила. Этот параметр позволяет создавать список баз данных для каждого правила по отдельности.

Существует одна встроенная база данных null: все данные, посланные в эту базу данных, сразу же исчезаю. Если параметр db_list не определён, то используется база данных null.

Пример:

db_mod "ipa_db_sdb.so";
global {
    db_list = sdb;
}

Здесь определена одна база данных.

  Учёт статистики по временным интервалам недели
По умолчанию учёт статистики выполняется для всех дней недели, но существует возможность выполнять учёт статистики только для некоторых временных интервалов. Параметр worktime определяет временные интервалы, когда должен выполняться учёт статистики:

worktime = <X> h1:m1-h2:m2 [h1:m1-h2:m2];
worktime = <X> *;

<X> обозначает день недели. Допустимые значения для <X> следующие: `S' воскресенье, `M' понедельник, `T' вторник, `W' среда, `H' четверг, `F' пятница, `A' суббота. Может быть только одна запись для каждого дня. Временные интервалы не должны перекрываться или быть расположенными не по порядку.

Если необходимо указать весь день, то необходимо использовать символ `*' или интервал 00:00-24:00.

Когда worktime разрешает выполнять учёт статистики, то секция в которой он располагается называется ``активной'', иначе она называется ``неактивной''.

Что конкретно этот параметр определяет для автоправил, правил, лимитов и порогов читайте в соответствующих параграфах.

Заметьте, что интервалы заданные в параметре worktime не гарантируют, что точно такие же временные интервалы окажутся в базе данных, так как запущенная копия ipa(8) может иметь низкий приоритет или система может быть слишком нагруженной.

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

Пример:

Выполнять учёт статистики только по понедельникам, вторникам и средам:

worktime = M * T * W *;

Выполнять учёт статистики в четверг с 8:00 до 14:30 и с 18:20 до 21:00, в воскресенье с полуночи до 10:35 (значение записано в нескольких строках только для выравнивания):

worktime = H 08:00-14:30 18:20-21:00
           S 00:00-10:35;

  Временные интервалы базы данных
Параметр update_time определяет временные интервалы, когда необходимо обновить статистику для правила:

update_time = <время>;

Если этот параметр не указан, то значение по умолчанию равно 1 минуте.

Параметр append_time определяет временные интервалы, когда необходимо добавить новую учётную запись для правила:

append_time = <время>;

Этот параметр не имеет значения по умолчанию. Новая учётная запись для каждого правила добавляется в базу данных в конце каждого дня в любом случае.

Обычно значение параметра append_time больше чем значение параметра update_time.

ipa(8) пытается объединить несколько временных событий в одно чтобы уменьшить использование ресурсов, это свойство имеет другой интересный момент. Например, если update_time равен 5 минутам, то ipa(8) всегда планирует временные события для этого параметра в 00:00, 00:05, 00:10 и т.д.

Существуют программы, которые могут изменять UTC или локальное время, также временная зона может быть изменена сама по себе. Эти события могут вызвать не критические ошибки типа ``some time related problems occurred'' в ipa(8). Параметры update_time и append_time могут вызвать больше подобных ошибок. Например, если вы синхронизируете время слишком часто и значение параметра update_time почти равно временному интервалу выполнения синхронизации времени, то можете получить множество подобных предупреждающих сообщений.

Параметр sensitive_time определяет насколько временные события чувствительны к изменениям времени:

sensitive_time = <время>;

Этот параметр не должен быть расположен в какой-либо секции. Значение по умолчанию равно 30 секундам.

Параметр wakeup_time определяет максимальное число секунд, которое может спать ipa(8). Этот параметр сообщает ipa(8) когда необходимо пробудиться и проверить всё ли в порядке с временем, временной зоной и т.п.:

wakeup_time = <время>;

По умолчанию значение этого параметра равно 10 минутам. Этот параметр не должен быть расположен в какой-либо секции.

Пример:

global {
    update_time = 30s;
    append_time = 1h 30m;
}

Символ `s' обозначает секунды, `m' минуты, `h' часы (пробельные символы в <времени> опциональны). Если <время> определено как составное значение, то часы должны быть указаны перед минутами и секундами, а минуты перед секундами.

  Описание правил, лимитов и порогов
Секции rule, limit и threshold могут иметь параметр info, который передаётся базе данных и используется для описания секции:

info = "строка";

Значение этого параметра не должно содержать символов `\n' и `\t'. Обычно это значение должно помочь вспомнить для чего используется то или иное правило, лимит или порог.

Динамическое правило получает своё описание от модуля учёта, который его сгенерировал, поэтому вы не можете указать описания динамических правил в конфигурационном файле.

Пример:

rule 10.1.2.3-in {
    info = "Мой трафик от ISP";
    /* ... */
}

Иногда имя правила не очень информативно, поэтому описание правила это хорошая идея.

  Имена правил, лимитов, порогов и автоправил
Любой символ в любом имени должен быть буквой, цифрой или символом пунктуации из таблицы ASCII символов.

Имя не может содержать символ двойной кавычки, символы '/' и '\'.

Необходимо давать такие имена, которые также являются корректными именами для используемой вами базы данных.

Эти ограничения также справедливы для имён динамических правил и для имён используемых в командах в параметрах ictl.

  Статические правила
Статические правила называются ``статическими'' потому, что они существуют в конфигурационном файле и ни один модуль учёта не может их уничтожить.

Секция rule описывает установки для одного статического правила:

rule <имя-правила> {
    /* Параметры и секции правила. */
}

Секция rule не требует каких-либо обязательных установок. Если какое-то правило не содержит ни одной секции и параметра, то оно называется пустым правилом. Очевидно, что такие правила бессмысленны, поэтому любое правило имеет несколько параметров (собственные или наследованные).

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

Пример:

ac_mod "ipa_ipfw.so";
ac_mod "ipa_ip6fw.so";
db_mod "ipa_db_sdb.so";
rule local.traf {
    ac_list = ipfw ip6fw;
    db_list = sdb;
    info = "Входящий трафик для моей LAN";
    sdb:db_group = staff;
    ipfw:rules = 100 200 300;
    ip6fw:rules = 1.10;
}

Здесь правило использует две системы учёта и одну базу данных. Оно также имеет описание и несколько настроек для модулей.

  Запуск команд
Существует несколько секций позволяющие определить список команд, которые должны быть запущены если произойдёт некоторое событие. Параметр exec используется для запуска команд:

exec [<пользователь>] "/path/command [аргументы]";

Параметр exec без <пользователя> запускает команду с привилегиями пользователя запустившего ipa(8), т.е. не предпринимаются никакие действия по смене пользователя или группы пользователя для запускаемой команды.

Параметр exec с <пользователем> запускает команду с привилегиями заданного пользователя. Пользователь может быть задан только по имени. ipa(8) определит UID и GID (группы) пользователя в момент запуска команды. Этот параметр может быть использован если ipa(8) запущена супер-пользователем.

Если необходимо использовать имя правила в какой-то командной строке в секции rulepat или autorule, то используйте подстановки. Определены две подстановки: %rule% обозначает имя правила, %% обозначает символ `%'.

Эти подстановки не работают в командных строках, расположенных в секциях rule, вместо этого используйте макропеременную ${rule} и одиночный символ `%'.

По умолчанию команды должны быть заданы с абсолютными путевыми именами, но возможно задавать команды с относительными именами, установив параметр only_abs_paths в ``no'' (значение по умолчанию равно ``yes''):

only_abs_paths = <boolean>;

Все команды запускаются с помощью shell, поэтому могут быть указаны любые конструкции командной строки:

<shell_path> <shell_arg1> /path/command [аргументы]

Заметьте, что стандартный ввод (stdint), стандартный вывод (stdout) и стандартный поток ошибок (stderr) обрабатываются также как и в ipa(8).

<Shell_path> определяется когда IPA компилируется (см. реальный путь в выводе команды ``ipa -v''), но возможно точно указать путь к оболочке с помощью параметра shell_path:

shell_path = "/path/shell";

<Shell_arg1> по умолчанию равен ``-c'', но он может быть переопределён в параметре shell_arg1:

shell_arg1 = "<arg1>";

Если нет достаточно ресурсов и ipa(8) не в состоянии, например, запустить потомка (ошибка вызова fork(2)), то ipa(8) завершит свою работу с кодом ошибки. Но если какая-то ошибка возникнет в потомке, который запускает команды, то ipa(8) проигнорирует эти ошибки и потомок просто отправит предупреждающие сообщения в лог.

Параметры only_abs_paths, shell_path и shell_arg1 должны быть расположены отдельно.

Пример:

startup {
    exec "/bin/echo \"ipa started\" | mail me";
    exec nobody "/usr/local/bin/something";
}
only_abs_path = no;
shutdown {
    exec "echo \"ipa stopped\" | mail me";
}
rulepat "^client" {
    startup {
        exec "command %rule%";
    }
}
rule 1 {
    shutdown {
        exec "echo rule off >> /tmp/${rule}.log";
    }
}

В первой секции команда ``mail'' задана без абсолютного путевого имени. Это корректно, так как только первая команда проверяется на абсолютное путевое имя, ipa(8) не интерпретирует конструкции командной строки оболочки.

Для включения имени правила в командную строку в секции rulepat используется подстановка %rule%, в секции rule используется макропеременная ${rule}.

  Запуск команд при старте и завершении работы
В секции startup (shutdown) можно определить команды, которые должны быть запущены, когда ipa(8) начинает (завершает) свою работу. Эти секции могут располагаться самостоятельно (глобальные команды) и в секциях autorule, rulepat, rule, limit, sublimit и threshold.

Если эти секции располагаются самостоятельно (см. использование этих секций в других секциях), то они могут содержать только параметры sync_exec, ictl (только внутри startup) и exec.

Алгоритм запуска команд из секций startup (shutdown) следующий:

1   запустить глобальные команды;

2   запустить команды из секций rule;

2а  в каждой секции rule запустить команды для лимитов и порогов;

2б  в каждой секции limit запустить команды для подлимитов.

Когда ipa(8) перечитывает конфигурационный файл (когда она получает сигнал SIGHUP), тогда команды в секциях startup игнорируются, но будут использоваться новые команды для секций shutdown.

Пример:

startup {
    exec "command1";
}
rule 1 {
    startup {
        exec "command2";
    }
    limit 1 {
        /* ... */
        startup {
            exec "command3";
        }
    }
}
rule 2 {
    startup {
        exec "command4";
    }
}

Здесь команды запускаются в следующем порядке: command1, command2, command3 и command4.

  Синхронный и асинхронный запуск команд
Существует два режима запуска команд: синхронный и асинхронный. В синхронном режиме ipa(8) ждёт завершения запущенных команд. В асинхронном режиме ipa(8) запустив команды, не ждёт завершения запущенных команд, а продолжает свою работу.

По умолчанию команды во всех секциях startup и shutdown запускаются синхронно, во всех остальных секциях команды запускаются асинхронно.

Параметр sync_exec позволяет определить асинхронный или синхронный режим для любой секции:

sync_exec = <boolean>;

Этот параметр может использовать в любой секции, которая содержит параметры exec.

Пример:

startup {
    sync_exec = no;
    exec "/path/something";
}

В этом примере синхронный режим, определённый по умолчанию, для секции startup изменён на асинхронный.

  О статистике
В IPA статистика это 64-битные беззнаковые целые числа. Статистикой может быть что угодно (время, байты или числа). Если некоторое правило использует несколько систем учёта, то ipa(8) суммирует статистику полученную от каждой системы статистики. Обычно модуль статистики возвращает положительную статистику, но он может возвращать и отрицательную статистику. Статистика передаётся в базу данных в виде 64-битных беззнаковых целых чисел вместе с текущим локальным временем.

Каждое правило имеет два внутренних 64-битных целых беззнаковых счётчика: один для положительной статистики и второй для отрицательной статистики. В любой момент времени один из этих счётчиков равен нулю. Если счётчик отрицательной статистики переполняется, то ipa(8) сообщает о случившейся проблеме и завершает свою работу, так как такое переполнение означает неправильную конфигурацию. Если счётчик положительной статистики переполняется, то ipa(8) делает запрос базе данных на добавление новой записи для правила со статистикой не поместившейся во внутреннем счётчике.

Любой лимит или порог имеет такие же два внутренних счётчика, но если переполнится счётчик положительной или отрицательной статистики для лимита или порога, то ipa(8) сообщает о случившейся проблемы и завершает свою работу, так как такое переполнение означает неправильную конфигурацию. Также каждый порог имеет несколько счётчиков для временных интервалов (отрицательный или положительный знак хранится в битовой карте).

Обычно положительная статистика больше чем отрицательная, но это не является жёстким правилом. Только положительная статистика сохраняется в базе данных, отрицательная статистика хранится в памяти и когда ipa(8) завершает свою работу и некоторое правило (лимит или порог) имеет отрицательную статистику, то эта статистика теряется (предупреждающее сообщение будет послано в лог-файл). Другими словами, ipa(8) способна вычитать статистику из текущего значения счётчика положительной статистики и нет возможности вычитать статистику из старой статистики в базе данных.

  Лимиты: введение
Лимит должен рассматриваться в контексте некоторого правила. Иногда лимиты в IPA называются триггерами. Если есть необходимость выполнять какие-то действия, когда статистика для правила достигнет определённого значения за некоторый период времени, тогда должна быть использована секция limit. Эта секция может иметь несколько секций и должна содержать по крайне мере один параметр по имени limit:

limit <имя-лимита> {
    limit = <значение-лимита>;
    /* Параметры и секции лимита. */
}

Одно правило может иметь несколько лимитов. Если правило содержит хотя бы один лимит, то это правило не наследует лимиты из подходящей секции rulepat.

Любой лимит имеет 64-битный беззнаковый целый счётчик для положительной статистики. Этот счётчик изменяется синхронно со счётчиком правила. Если значение счётчика станет равным или большим чем значение параметра limit, то лимит становится достигнутый и его счётчик больше не будет изменяться.

<Значение-лимита> может быть: байты, время или 64-битовое беззнаковое целое число (см. примеры ниже); и это значение может быть равно нулю.

Лимит может проходить несколько состояний: лимит не достигнут, лимит достигнут (плюс опциональный запуск команд), достигнутый лимит должен быть перезапущен (плюс опциональный запуск команд) и псевдо состояние когда не достигнутый лимит должен быть перезапущен. Секция limit может иметь несколько параметров и секций, которые определяют состояния лимита (описано ниже).

Если лимит не достигнут, то его статистика проверяется каждый раз, когда обновляется статистика его правила (временной интервал не больше чем значение параметра update_time).

Пример:

rule my-account {
    /* Параметры и секции правила. */
    limit 1 {
        limit = 1M 500K;
        info = "Байтовый лимит";
    }
    limit 2 {
        limit = 2h 30m;
        info = "Временной лимит";
    }
    limit 3 {
        limit = 1234567890;
        info = "Численный лимит";
    }
}

В этом примере правило содержит три независимых лимита.

Если значение параметра limit задаётся в байтах, то символ `T' обозначает Тбайты, `G' Гбайты, `M' Мбайты, `K' Кбайты, `B' байты (пробельные символы опциональны). Если значение определяется как сложное значение, то Тбайты должны быть указаны до Гбайтов и Мбайтов и так далее. 1 Кбайт равен 1024 байтам.

  Перезапуск не достигнутого лимита
Параметр restart в секции restart позволяет определить время перезапуска не достигнутого лимита:

limit <имя-лимита> {
    /* Параметры и секции лимита. */
    restart {
        restart = <время-перезапуска>;
        /* Команды. */
    }
}

Время когда новый лимит был создан или достигнутый лимит был перезапущен снова известно как ``время старта''. <Время-перезапуска> является относительным по отношению к времени к времени старта лимита (см. примеры).

Секция restart может содержать опциональные команды которые будут запущены, когда не достигнутый лимит будет перезапускаться.

Системы учёта, используемые правилом лимита, будут проинформированы когда не достигнутый лимит будет перезапускаться. Поэтому если вы не указали команды в секции restart, то некоторые действия всё ещё могу быть выполнены системами учёта.

Если лимит не содержит параметра restart, то этот лимит (если он не достигнут) не может быть автоматически перезапущен. Но он может быть перезапущен утилитой ipactl(8) или параметрами ictl и все команды из секции restart будут запущены.

Не достигнутый лимит может перезапуститься только если его правило активно.

Пример 1:

rule my.traf {
    ac_list = ipfw;
    ipfw:rules = 100 200;
    limit 1 {
        limit = 1G;
        restart {
            restart = 20h 30m;
        }
    }
}

Здесь значение параметра restart добавляется к времени старта лимита и вычисленное время является временем перезапуска лимита. Здесь можно рассматривать время перезапуска как число секунд со времени старта лимита.

Символ `s' обозначает секунды, `m' минуты, `h' часы, `D' дни и `W' недели.

Пример 2:

rule my.traf {
    ac_list = ipfw;
    ipfw:rules = 100 200;
    limit 1 {
        limit = 1G;
        restart {
            restart = +W;
        }
    }
}

Здесь лимит будет перезапущен в конце недели после времени старта лимита. Символ после знака `+' означает: `m' минуту, `h' час, `D' день, `W' неделю, `M' месяц.

Пример 3:

rule my.traf {
    ac_list = ipfw;
    ipfw:rules = 100 200;
    limit 1 {
        limit = 1G;
        restart {
            restart = +M 2D;
        }
    }
}

В этом примере лимит будет перезапущен на третий день следующего месяца после времени старта лимита: сначала вычисляется время перезапуска для ``+'' (начало следующего месяца), затем ``2D'' (два дня) добавляется к вычисленной дате.

Заметьте, что ``2D +M'' это не тоже самое: здесь ``2D'' добавляется ко времени старта и затем вычисляется время перезапуска для ``+M'' (и здесь всегда получается первый день некоторого следующего месяца).

  Действия, когда лимит будет достигнут
Если лимит будет достигнут, то команды перечисленные в секции reach будут запущены:

limit <имя-лимита> {
    /* Параметры и секции лимита. */
    reach {
        /* Команды. */
    }
}

Секция reach может отсутствовать или быть пустой, в этом случае никакие команды не будут запускаться по достижению лимита.

Системы учёта, используемые правилом лимита, будут проинформированы, когда лимит станет достигнутым. Поэтому если вы даже не указали команды в секции reach, то некоторые действия всё ещё могу быть выполнены системами учёта.

Пример:

rule my.traf {
    ac_list = ipfw;
    ipfw:rules = 100;
    limit 1 {
        limit = 1G;
        restart {
            restart = +W;
        }
        reach {
            exec "/somewhere/stop_traffic";
        }
    }
}

Здесь определён лимит в 1 Гбайт. Если этот лимит не будет достигнут, то он будет перезапущен в конце недели. Если этот лимит будет достигнут, то будет запущена заданная команда.

  Перезапуск достигнутого лимита
Параметр expire в секции expire позволяет определить время перезапуска достигнутого лимита:

limit <имя-лимита> {
    /* Параметры и секции лимита. */
    expire {
        expire = <время-перезапуска>;
        /* Команды. */
    }
}

<Время-перезапуска> является относительным по отношению к времени к времени достижения лимита (см. примеры).

Секция expire может содержать опциональные команды, которые будут запущены, когда достигнутый лимит будет перезапускаться.

Системы учёта, используемые правилом лимита, будут проинформированы когда достигнутый лимит перезапускается. Поэтому если вы не указали команды в секции expire, то некоторые действия всё ещё могу быть выполнены системами учёта.

Если лимит не содержит параметра expire, то этот лимит (если он достигнут) не может быть автоматически перезапущен, но он может быть перезапущен утилитой ipactl(8) и параметрами ictl и все команды из секции expire будут запущены.

Значение параметра expire может быть 0s, это означает, что достигнутый лимит будет перезапущен мгновенно.

Достигнутый лимит может быть перезапущен только когда его правило активно.

Пример:

rule my.traf {
    ac_list = ipfw;
    ipfw:rules = 100;
    limit 1 {
        limit = 1G;
        restart {
            restart = +W;
        }
        reach {
            exec "/somewhere/stop_traffic";
        }
        expire {
            expire = +W;
            exec "/somewhere/start_traffic";
        }
    }
}

Здесь приведен лимит в 1 Гбайт в неделю. Если лимит будет достигнут, то он перезапустится в начале недели. Если этот лимит не будет достигнут, то он всё равно будет перезапущен в начале недели.

  Активные и неактивные лимиты
Если лимит не имеет собственного параметра worktime, то он наследует этот параметр от своего правила. Любой лимит может иметь собственный параметр worktime.

Если правило неактивно, то все его лимиты также неактивны. Если лимит неактивен, то времена определяемые параметрами restart и expire не проверяется.

Возможно иметь активное правило и неактивный лимит, но не возможно иметь неактивное правило и активный лимит. Все временные интервалы в параметре worktime лимита должны быть подмножеством временных интервалов параметра worktime правила. Чтобы проверить подмножества временных интервалов в статических правилах и автоправилах запустить ipa(8) с ключами -tt.

Пример:

rule my-account {
    /* ... */
    worktime = A * S *;
    limit 1 {
        limit = 10M;
        worktime = A 08:00-21:00 S 08:00-21:00;
    }
}

Здесь правило активно только по субботам и воскресеньям, а лимит активен с 08:00 до 21:00 по тем же дням. Так как временной интервал 08:00-21:00 является подмножеством временного интервала 00:00-24:00, то в этих двух параметрах всё корректно.

  Секции startup и shutdown для правила
Правило может иметь собственные секции startup и shutdown с следующими подсекциями:

if_any_reached     - если любой из лимитов правила достигнут;
if_any_not_reached - если любой из лимитов правила не достигнут;
if_all_reached     - если все лимиты правила достигнуты;
if_all_not_reached - если все лимиты правила не достигнуты.

Эти секции определяют команды, которые должны быть запущены, если состояния лимитов попадают под условие подсекции.

Любое правило может иметь пустую секцию startup или shutdown, в этом случае эта секция не наследуются из подходящей секции rulepat.

Пример:

rule my.traf {
    ac_list = ipfw;
    ipfw:rules = 100;
    startup {
        exec "/somewhere/count_traffic";
        if_any_reached {
            /* ... */
            exec "/bin/echo \"Какой-то из лимитов в правиле \
                  ${rule} был достигнут\" | mail admin";
        }
    }
    limit 1 {
        limit = 1G;
        restart {
            restart = +M;
        }
        info = "1G в месяц";
        /* ... */
    }
    limit 2 {
        limit = 500M;
        restart {
            restart = +W;
        }
        info = "500M в неделю";
        /* ... */
    }
}

Здесь приведены два лимита: 1 Гбайт в месяц и 500 Мбайт в неделю, секция if_any_reached будет использована если любой из этих лимитов будет достигнут на момент старта ipa(8). Также startup секция правила содержит одну команду, которая всегда запускается при старте. Здесь используется обратный слэш для разделения командной строки.

  Секции startup и shutdown для лимита
Лимит может иметь собственные секции startup и shutdown с следующими дополнительными подсекциями:

if_reached     - если лимит достигнут;
if_not_reached - если лимит не достигнут.

Эти секции определяют команды, которые необходимо запустить, если состояние лимита подпадает под условие подсекции.

Системы учёта, используемые правилом лимита, информируются при старте и завершении работы о том достигнут или не достигнут лимит. Поэтому, если вы не указали команды для старта и/или останова для лимита, то некоторые действия всё ещё могу быть выполнены системами учёта.

  Базы данных и лимиты (пороги)
По умолчанию лимит наследует список баз данных своего правила, но лимит может использовать собственный список баз данных:

rule <имя-правила> {
    /* Параметры и секции правила. */
    db_list = <список1>;
    limit <имя-лимита> {
        /* Параметры и секции лимита. */
        db_list = <список2>;
    }
}

<Список1> и <список2> могут содержать общие элементы, в любом случае <список1> используется только для правила, а <список2> используется только для лимита.

Зачем использовать раздельные списки баз данных для правила и лимита? Не все базы данных работают с лимитами и даже если какая-то база данных работает с лимитами, она может поддерживать не все функции (методы) для лимитов. См. детали реализации в странице документации ipa_mod(3).

Допустим некоторый лимит использует несколько баз данных. Первая база данных, которая в состоянии вернуть состояние лимита, будет опрошена о текущем состоянии лимита. Даже если какая-то другая база данных имеет другое состояние лимита, она не будет опрошена и её состояние лимита будет обновлено.

Заметьте, что порядок баз данных для лимита важен.

Прочтите в документации к базе данных может ли она работать с лимитами и что конкретно модуль поддерживает, когда работает с лимитами.

Всё сказанное выше также справедливо для порогов.

  Использование значения параметра limit (threshold) из базы данных
Параметр load_limit позволяет брать значение параметра limit из базы данных:

limit <имя-лимита> {
    /* Параметры и секции лимита. */
    load_limit = <boolean>;
}

Если необходимо загрузить значение параметра limit из базы данных, то установите значение параметра load_limit в ``yes'' (значение по умолчанию равно ``no''). Если база данных не имеет текущего состояния лимита (если лимит новый), то будет использоваться значение параметра limit из конфигурационного файла, поэтому параметр limit всегда должен быть определён.

Одним из возможных случаев почему необходимо установить этот параметр в ``yes'' является использование утилиты ipactl(8) для изменения значения параметра limit на лету.

Этот параметр может быть определён в секции global. Если какой-то лимит не содержит этот параметр, то его значение будет наследоваться из секции global.

  Инициализация лимитов
Когда ipa(8) стартует, перечитывает конфигурационный файл или выполняет переинициализацию при возникновении проблем с временем, выполняются следующие шаги для каждого лимита:

1.  Текущее состояние лимита читается из базы данных. Если база данных не имеет состояния лимита, то новое состояние лимита регистрируется со значением параметра limit из конфигурационного файла и с нулевым счётчиком и инициализация лимита завершена, иначе ipa(8) выполняет второй шаг (2а или 2б);

2а. Если лимит не достигнут с текущим состоянием из базы данных, то если значение параметра load_limit равно ``no'', то ipa(8) обновляет значение параметра limit из конфигурационного файла; если значение параметра load_limit равно ``yes'', то ipa(8) игнорирует значение параметра limit из конфигурационного файла и берёт это значение из базы данных. В любом случае лимит помечается как не достигнутый, даже если с новыми установками он становится достигнутым. Затем ipa(8) обновляет дату когда лимит должен быть перезапущен согласно значению параметра restart из конфигурационного файла, игнорируя эту дату из базы данных. Если время старта не достигнутого лимита больше чем текущее время, то ipa(8) изменяет время старта лимита на текущее время. Счётчик лимита не изменяется.

2б. Если лимит достигнут, то ipa(8) помечает этот лимит как достигнутый и не обновляет значение параметра limit в базе данных (т.е. временно не учитывается значение параметра load_limit). Затем ipa(8) обновляет дату когда лимит должен быть перезапущен согласно значению параметра expire из конфигурационного файла, игнорируя это значение из базы данных.

Достигнутый или не достигнутый лимит может быть перезапущен во время инициализации в том случае, если база данных вернула некорректные данные о состоянии лимита.

Чтобы проверить текущее состояние лимита используйте ipactl(8) с командой status.

  Лимиты: диаграмма состояний
Для улучшения понимания работы лимитов ниже приведена временная диаграмма состояний произвольного лимита:

                 +--------------+--> Restart (Tstart)
                 |              |
                 Trestart_exec  Treach_exec    Texpire_exec
--|------>------||------>------||------>------||------------->
  Tstart      Trestart         Treach       Texpire       time

  Подлимиты
Подлимит это часть некоторого лимита. Главное назначение подлимита это регистрация события, когда будет достигнута некоторая часть значения параметра limit. Так как подлимит является частью секции limit, то его значение должно быть задано в тех же единицах, что и значение параметра limit или в процентах:

limit <имя-лимита> {
    limit = <значение-лимита>;
    /* Параметры и секции лимита. */
    sublimit <значение-подлимита> {
        /* Секции подлимита. */
    }
}

Подлимиты могут содержать только секции reach, startup и shutdown. Все эти секции имеют тот же самый формат и имеют тот же самый смысл, что и те же самые секции для лимитов. Лимит может иметь несколько подлимитов.

Системы учёта не информируются о событиях подлимитов, так как подлимиты невидимы для модулей. Так как состояния подлимитов не хранятся в базах данных, поэтому, если возможно, то лучше использовать подлимиты, чем добавлять дополнительные лимиты для правила.

Пример:

rule my.traf {
    /* ... */
    limit l1 {
        limit = 1G;
        load_limit = yes;
        restart {
            restart = +M;
        }
        info = "1G в месяц";
        /* ... */
        sublimit 50% {
            reach {
                exec "/bin/echo \"Достигнута половина лимита \
                      ${limit} правила ${rule}\" | mail me";
            }
        }
    }
}

Здесь подлимит посылает email, когда половина лимита t1 будет достигнута. Даже если значение параметра limit будет изменено утилитой ipactl(8), значение подлимита будет скорректировано, так как оно задано в процентах.

  Пороги: введение
Порог позволяет наблюдать за статистикой правила за некоторый временной промежуток до текущего момента времени и делать некоторые действия, когда статистика за этот временной промежуток меньше, равна или больше чем заданное значение. Этот временной интервал также может быть назван ``скользящим временным окном''. Порог описывается секцией threshold следующего формата:

threshold <имя-порога> {
    threshold = <значение-порога>;
    threshold_time_width = <время>;
    threshold_time_slice = <время>;
    /* Другие параметры и секции порога. */
}

Одно правило может иметь несколько порогов. Если правило содержит хотя бы один порог, то это правило не наследует пороги из подходящей секции rulepat.

Любой порог имеет 64-битный беззнаковый целый счётчик, который изменяется и сравнивается со значением параметра threshold. Если значение счётчика становится меньше, равно или больше чем значение параметра threshold, то запускаются команды из опциональных секции below_threshold, equal_threshold или above_threshold. Счётчик порога изменяется синхронно со счётчиком правила каждый threshold_time_slice временной интервал.

<Значение-порога> может быть: байты, время или 64-битное беззнаковое целое число; и это значение может быть равно нулю.

Возможно определить отклонение <значения-порога> при помощи параметра threshold_deviation. Значение параметра threshold_deviation должно быть задано в тех же единицах, что и значение параметра threshold или в процентах.

Значение параметра threshold_time_width определяет ширину скользящего временного окна. Значение параметра threshold_time_slice определяет временные интервалы движения скользящего временного окна. Значение параметра threshold_time_width должно быть больше чем значение параметра threshold_time_slice и должно быть делится нацело на это значение.

Счётчик порога представляет снимок статистики правила за последние threshold_time_width секунд. В отличии от лимитов, пороги не имеют ``времени старта'', так как их статистика скользит во времени дискретными временными интервалами равными threshold_time_slice секундам. Эти два параметра могут быть определены в секции global и они будут наследоваться, при их отсутствии в каком-то пороге.

Системы учёта, используемые правилом порога, оповещаются о событиях порога, поэтому системы учёта также могут выполнять некоторые действия, когда значение счётчика порога меньше, равно или больше чем значение параметра threshold.

Чтобы ограничить число раз запуска команд или оповещения систем учёта из секций below_threshold (X), equal_threshold (Y) и above_threshold (Z) используйте параметр threshold_balance:

threshold_balance = X:Y:Z;

Этот параметр может быть определён в секции global и если какой-то пороге содержит этот параметр, то его значение будет наследоваться из секции global.

Есть три внутренних счётчика x, y и z, которые считают количество последовательных запусков команд и количество оповещений систем учёта из секций below_threshold, equal_threshold и above_threshold. Эти счётчики изначально равны X, Y и Z соответственно. Когда счётчик порога меньше чем значение параметра threshold и x не равно нулю, то значение x декрементируется, y устанавливается равным Y, z устанавливается равным Z, затем запускаются команды из секции below_threshold и системы учёта информируются о событии порога. Аналогичные действия делаются для счётчиков y и z.

Чтобы задать безграничные значения для X, Y или Z, установите их как `-'. В реализации безграничное число равно максимальному значению типа данных По умолчанию значение этого параметра равно -:-:-.

Пример:

rule lan {
    ac_list = ipfw;
    ipfw:rules = 100 200 -300;
    update_time = 1m;
    limit l1 {
        limit = 1G;
        info = "Контроль каждого 1G трафика";
        reach {
            exec "/bin/echo \"1G правила ${rule} достигнут\" |
                  mail me";
        }
        expire {
            expire = 0s;
        }
    }
    threshold t1 {
        threshold = 500M;
        threshold_balance = 1:-:1;
        threshold_deviation = 50M;
        threshold_time_width = 24h;
        threshold_time_slice = 15m;
        info = "Порог в 500M плюс-минус 50M за 24h";
        below_threshold {
            exec "/somewhere/increase_bandwidth ${rule}";
        }
        above_threshold {
            exec "/somewhere/decrease_bandwidth ${rule}";
        }
    }
}

Здесь правило имеет один порог и один лимит.

Порог позволяет контролировать трафик в 500 Мбайт плюс-минус 50 Мбайт (мы увеличиваем скорость некоторой программой increase_bandwidth и уменьшаем скорость некоторой программой decrease_bandwidth) за 24 часа (это один день, но это не день недели, здесь 24 часа обозначают временной интервал). Время продвижения порога равно 15 минутам, заметьте, что в этом примере порог не будет проверяться каждую минуту (значение параметра update_time для правила).

Статистика для правил и лимита будет обновляться каждую минуту. Лимит позволяет отправлять email, когда будет использован очередной 1 Гбайт трафика.

  Активные и неактивные пороги
Пороги подобно лимитам могут быть активными и неактивными и иметь собственные параметры worktime. Поэтому, для большей информации прочтите соответствующий параграф для лимитов.

Чему будет равно значение счётчика порога, когда порог был неактивным и становится активным из-за значения параметра worktime? Первое решение это позволить временному окну порога перемещаться в временном интервале неактивности, второе решение это ``переместить'' временное окно порога через временной интервал неактивности. В первом решении статистика для счётчика порога в временном интервале неактивности равна 0 и значение счётчика порога уменьшается. Во втором решении статистика в временном интервале неактивности игнорируется (она пропускается) и значение счётчика порога не изменятся.

Чему будет равно значение счётчика порога когда ipa(8) начинает свою работу и в базе данных уже есть состояние порога? Существует также два решения этой ситуации, как и в предыдущем вопросе.

Возможно выбирать решения для выше описанных ситуаций с помощью параметра threshold_type, его значение равно битам объединённых по ИЛИ (даны в шестнадцатеричной системе счисления):

0x1 переместится через временной интервал, когда ipa(8) не работала (была остановлена), этот бит проверяется во время инициализации порога;

0x2 переместится через временной интервал, когда порог был неактивным, этот бит проверяется когда порог снова становится активным в соответствии с установками своего параметра worktime.

По умолчанию значение параметра threshold_type равно 0. Возможные значения этого параметра следующие: 0, 1, 2 и 3 (0x1|0x2). Нормальные значения для этого параметра 0 и 3, значения с установленным только одним битом должны использоваться с осторожностью, так как когда порог инициализируется, он рассматривается как активный (прочтите параграф об инициализации порогов).

Этот параметр может быть определён в секции global и он будет использован, если какой-то порог не имеет этого параметра, то его значение будет унаследовано из секции global.

Пример:

rule client {
    ac_list = ipfw;
    ipfw:rules = 100 200 300;
    update_time = 1m;
    threshold t {
        threshold = 100M;
        threshold_deviation = 10%;
        threshold_time_width = 5h;
        threshold_time_slice = 15m;
        threshold_type = 3;
        worktime = M 08:00-21:00 T 08:00-21:00 W 08:00-21:00
                   H 08:00-21:00 F 08:00-21:00;
        info = "Порог в 100M плюс-минус 10% за 5h (тип 3)";
        below_threshold {
            exec "/somewhere/increase_bandwidth ${rule}";
        }
        above_threshold {
            exec "/somewhere/decrease_bandwidth ${rule}";
        }
    }
}

Предположим, что данное правило это правило для клиента, который имеет доступ в Internet каждый рабочий день с 08:00 до 21:00. Мы разрешаем ему использовать скорость в 100 Мбайт плюс-минус 10% за каждые 5 часов (мы увеличиваем скорость некоторой программой increase_bandwidth и уменьшаем скорость некоторой программой decrease_bandwidth).

Предположим, что текущее значение счётчика порога равно 90 Мбайт в 21:00. Когда наступит 08:00 следующего дня по умолчанию текущее значение счётчика порога станет 0 Мбайт, так как в интервале между 21:00 и 08:00 следующего дня содержится по крайней мере один интервал в 5 часов. Здесь используется тип порога 3 и временное окно ``переместится'' с 21:00 в 08:00 и текущее значение счётчика порога останется неизменённым, то есть 90 Мбайт в 08:00 следующего дня. Мы также можем остановить ipa(8) в 21:00 и запустить её снова в 08:00 и временное окно порога также ``переместится'' с 21:00 в 08:00.

Если есть несколько клиентов с такими же установками, то в 08:00 ни один из них не сможет интенсивно начать использовать собственную часть общего трафика.

  Секции startup и shutdown для порога
Порог может иметь собственные секции startup и shutdown, которые они могут содержать только списки команд.

  Инициализация порогов
Когда ipa(8) стартует, перечитывает конфигурационный файл или выполняет переинициализацию при возникновении проблем с временем, выполняются следующие шаги для каждого порога:

1   Текущее состояние порога читается из базы данных. Если база данных не имеет состояния порога, то новое состояние порога регистрируется с значением параметра threshold из конфигурационного файла и с нулевым счётчиком и инициализация порога завершена, иначе выполняется второй шаг.

2   Состояние порога обновляется с учётом значений параметров load_threshold и threshold_type. Так как статистика порога для временных интервалов не хранится в базе данных, то статистика для временных интервалов инициализируется приблизительно в соответствии с текущим локальным временем, временными отметками порога и значением его счётчика.

Когда порог инициализируется предполагается что он активен, даже если его параметр worktime отмечает этот порог неактивным.

  Пороги: временная диаграмма
Для улучшения понимания работы порогов ниже приведена временная диаграмма:

     <-------------- time_width ------------->
(t1) |---c1--|---c2--|---c3--|---c4--|---c5--| --> скольжение
(t2)         |---c2--|---c3--|---c4--|---c5--|---c6--| --> скольжение
     <-slice->
-----|-------|-------|-------|-------|-------|-------|-------|-->
     t1      t2                                               время

На этой диаграмме видна статистика порога для временных отметок t1 и t2. Вся статистика представляется как сумма ci, каждая ci равна статистике правила в течении одного интервала времени t2 - t1. Порог скользит во времени дискретно и его статистика это снимок статистики правила за time_width секунд.

  Динамические правила
Динамические правила генерируются на лету из автоправил специально разработанными модулями учёта. Внутри ipa(8) статические и динамические правила почти неразличимы и любой параметр (кроме только трёх параметров) и секция из статических правил может быть использован в динамических правилах.

Автоправило определяется секцией autorule:

autorule <имя-автоправила> {
    /* Параметры и секции. */
}

Динамическое правило похоже на статическое правило. Существует всего два ограничения для автоправил (таким образом и для динамических правил тоже): автоправило может иметь только одну систему учёта в значении параметра ac_list и автоправило не может иметь параметров ac_gather_* и ictl.

Конфигурационный файл может иметь несколько автоправил одновременно. Любое автоправило должно иметь по крайней мере параметр ac_list с одним элементом в его значении. (Возможно реализовать поддержку нескольких систем учёта для одного автоправила, но это бессмысленно.) Этот элемент в значении параметра ac_list определяет систему учёта, которая может создавать и уничтожать динамические правила. Каждый раз когда эта система учёта опрашивает для получения новой статистики, она имеет шанс создать и/или уничтожить (ранее созданные) динамические правила.

Если вы хотите использовать некоторую базу данных для динамических правил, то эта база данных должна поддерживать динамические правила.

Если автоправило имеет параметр worktime, то этот параметр используется автоправилом. Динамическое правило сгенерированные от этого автоправила наследует параметр worktime_rule из автоправила как свой параметр worktime. Если в автоправиле нет параметра worktime, то он наследуется из секции global.

Так как автоправило и его динамические правила могут иметь разные параметры worktime, то возможно неактивное автоправило и активные динамические правила и наоборот.

Динамическое правило наследует установки от своего автоправила, если какие-то параметры и секции всё ещё не определены, то они наследуются от подходящей секции rulepat, затем из секции global и затем используются установки по умолчанию.

Если автоправило имеет секции startup и shutdown, то эти секции для динамических правил, а не для самого автоправила. Если необходимо использовать имя динамического правила в командной строке, то не используйте макропеременную ${rule}, так как она расширяется внутренним парсером конфигурационного файла, вместо этого используйте подстановки командной строке.

Любое автоправило может иметь пустую секцию startup или shutdown, в этом случае эта секция не наследуются из подходящей секции rulepat для его динамических правил.

Если необходимо использовать различные лимиты (пороги) для динамических правил, то определите эти лимиты (пороги) в различных секциях rulepat.

Пример:

ac_mod "ipa_atest.so";
db_mod "ipa_db_sdb.so";
global {
    db_list = sdb;
    append_time = 1h;
}
autorule a {
    ac_list = atest;
    update_time = 1m;
    limit 1 {
        limit = 100M;
        restart {
            restart = +W;
        }
        reach {
            exec "/somewhere/stop_traffic.sh %rule%";
            exec "/bin/echo \"%rule%'s limit ${limit} reached\" |
                  mail admin";
        }
        expire {
            expire = +M;
            exec "/somewhere/start_traffic.sh %rule%";
        }
    }
}

Здесь каждое динамическое правило сгенерированное из автоправила наследует параметр автоправила update_time и подобно статическим правилам каждое динамическое правило наследует параметры db_list и append_time из секции global. Каждое динамическое правило будет иметь один лимит, так как нельзя использовать макропеременную ${rule} в динамические правилам (на самом деле в автоправилах), то используется подстановка %rule%. Использование макропеременной ${limit} является корректным, так как имя лимита известно для парсера конфигурационного файла.

  Шаблоны правил
Использование шаблонов правил это эффективный метод для разделения общих установок для нескольких правил. Секция global позволяет определить общие установки для любых правил, динамические правила могут наследовать установки от своих автоправил. Шаблоны правил позволяют определить общие установки для классов статических и динамических правил.

Если какое-то статическое или динамическое правило не имеет какого-то параметра или секции, то оно может наследовать этот параметр или секцию из подходящего шаблона правила. Шаблон правила определяется в секциях rulepat:

rulepat "<regexp>" {
    /* Параметры и секции. */
}

Каждый шаблон правила поименован регулярным выражением POSIX. ipa(8), разобрав конфигурационный файл, находит подходящий шаблон правила для каждого статического правила и добавляет неопределённые установки для статического правила из шаблона правила. Аналогично, создав динамическое правило, ipa(8) находит подходящий шаблон правила и добавляет неопределённые установки для динамического правила из шаблона правила.

По умолчанию, когда подходящий шаблон правила найден, поиск завершается. Чтобы продолжить поиск следующих шаблонов правил, установите значение параметра check_next_rulepat в ``yes'' (значение по умолчанию равно ``no''):

check_next_rulepat = <boolean>;

Этот параметр может быть расположен только в секции rulepat.

Любой параметр (кроме параметров ac_gather_* и ictl) и любая секция, которая может быть использована в секции rule может быть использована в секции rulepat.

Шаблоны правил могут быть указаны в любом месте в конфигурационном файле, важен их порядок, так как их регулярные выражение проверяются в том же самом порядке, в котором расположены шаблоны правил в конфигурационном файле.

Модули также могут ожидать параметры и секции в секциях rulepat.

Пример:

ac_mod "ipa_ipfw.so";
db_mod "ipa_db_sdb.so";
global {
    ac_list = ipfw;
    db_list = sdb;
    update_time = 1m;
    load_limit = yes;
    sdb:db_group = staff;
}
rulepat "0${$}" {
    update_time = 30s;
    threshold 1 {
        threshold = 1G;
        threshold_deviation = 10%;
        threshold_time_width = 10h;
        threshold_time_slice = 5m;
        below_threshold {
            exec "/somewhere/increase-bandwidth.sh %rule%";
        }
        above_threshold {
            exec "/somewhere/decrease-bandwidth.sh %rule%";
        }
    }
}
rulepat "^client" {
    worktime = M 08:00-20:00 T 08:00-20:00 W 08:00-20:00
               H 08:00-20:00 F 08:00-20:00 A 08:00-17:00;
}

Здесь первая секция rulepat ``ловит'' все правила с нулём в конце их имени (макропеременная ${$} расширяется в одиночный символ `$'). Так как значение параметра check_next_rulepat равно ``yes'', то проверяется следующий шаблон правила. Вторая секция rulepat ``ловит'' все правила с подстрокой ``client'' в начале их имён.

  Сбор статистики из нескольких правил
Обычно каждое правило получает статистику от модулей учёта, но иногда необходимо суммировать статистику из нескольких правил и невозможно или слишком трудоёмко дать эту задачу модулям учёта.

Параметры ac_gather_add и ac_gather_sub позволяют получать статистику для одного правила из нескольких правил:

rule <имя-правила> {
    /* Параметры и секции правила. */
    ac_gather_add = "<regexp>";
    ac_gather_sub = "<regexp>";
}

Здесь <regexp> это регулярное выражение POSIX. Если имя какого-то правила соответствует этому регулярному выражению, то его статистика переходит к правилу в котором указаны параметры ac_gather_* и добавляется к (ac_gather_add) или вычитается из (ac_gather_sub) статистики правила. Также возможно строить сложные зависимости с использованием этих параметров (см. пример).

Этот параметр может быть указан только в секции rule, но он может собирать статистику со статических и динамических правил.

Параметр ac_list является синхронным по отношению к секции rule: когда некоторое правило обновляется, статистика забирается из каждой системы учёта, используемой правилом. Параметры ac_gather_* являются асинхронными по отношению к секции rule: текущее правило получает статистику из правила соответствующего регулярному выражению подходящих параметров ac_gather_* только тогда, когда это совпадающее правило само обновляется.

Правило может содержать параметры ac_gather_* и параметр ac_list.

Пример:

ac_mod "ipa_ipfw.so";
global {
    ac_list = ipfw;
}
rule client1 {
    ipfw:rules = 100 102 104;
    info = "Статистика для первого клиента";
}
rule client2 {
    ipfw:rules = 200 202 204;
    info = "Статистика для второго клиента";
    /* ac_gather_add = "^clients${$}"; <-- ОШИБКА! */
}
rule clients {
    ac_gather_add = "^client[[:digit:]]+${$}";
    info = "Статистика для всех клиентов";
}
rule server {
    ipfw:rules = 1000 1002;
    info = "Статистика для сервера";
}
rule all_stat {
    ac_gather_add = "(server|clients)${$}";
    info = "Статистика для всех в моей LAN";
}
rule all_except_client2_stat {
    ac_gather_add = "^all_stat${$}";
    ac_gather_sub = "^client2${$}";
    info = "Статистика для всей в моей LAN, кроме client2";
}

Здесь у нас шесть правил: client1, client2, clients, server, all_stat и all_except_client2_stat. Правило clients получает статистику из правил client1 и client2. Правило all_stat получает статистику из правил clients и server. И правило all_except_client2_stat получает статистику из правил clients и server исключая статистику от client2.

Правило client2 имеет неправильный параметр ac_gather_add в комментарии, если бы этот параметр существовал в этом правиле, то получили бы зацикливание в зависимости правил: client2->clients->client2... ipa(8) не проверяет зацикливания в зависимостях правил.

  Использование программы ipactl(8)
Программа ipactl(8) позволяет посылать ipa(8) управляющие команды через Unix domain сокет. Перед тем как использовать эту программу необходимо разрешить её использование установкой параметра ctl_enable в ``yes'' (значение по умолчанию равно ``no''):

ctl_enable = <boolean>;

Если значение этого параметра равно ``yes'', то ipa(8) создаст локальный сокет и будет готова для приёма управляющих команд на этом сокете. Созданный Unix domain сокет будет иметь владельца, равному пользователю, запустившего ipa(8).

Права доступа к сокету определяются параметром ctl_socket_perm (значение по умолчанию равно ``u''):

ctl_socket_perm = <права>;

<Права> это последовательность букв: `u', `g' и `o'. Эти символы определяют кому (пользователю, группе или остальным пользователям) разрешено посылать данные в сокет. Остальным пользователям можно разрешить отправлять данные в сокет, только если ipa(8) использует ACL (списки управление доступом).

Существует путь к этому сокету, заданный по умолчанию (см. вывод команды ``ipactl -h''), но этот путь возможно переопределить в параметре ctl_socket_path:

ctl_socket_path = "/path/to/socket";

По умолчанию сокет имеет таймаут 10 секунд, но возможно изменить этот таймаут в параметре ctl_timeout:

ctl_timeout = <время>;

Существует лимит на максимальный размер управляющей команды получаемой ipa(8). Его значение по умолчанию равно 1 Кбайт. Это значение может быть изменено в параметре ctl_query_max_size:

ctl_query_max_size = <байты>;

ipa(8), запущенная на FreeBSD и NetBSD, способна проверять отправителя сообщений от ipactl(8) (проверьте это в выводе команды ``ipa -v''), поэтому на этих системах необходимо также определять параметры ctl_acl_class, ctl_dump_acl, ctl_freeze_acl, ctl_stat_acl и ctl_rule_acl.

Параметр ctl_acl_class определяет класс ACL: имя ACL с последующим определением ACL:

ctl_acl_class <класс> [<ACL>];

Этот параметр должен быть расположен отдельно.

ACL состоит из элементов, разделённых пробельными символами, каждый элемент имеет следующий формат:

[!]<пользователь>|%<группа>

Символ `!' обозначает, что доступ запрещён. Символ `%' обозначает, что последующее имя это имя группы. <Пользователь> и <группа> должны быть заданы как имена пользователей и групп, идентификаторы UID и GID здесь не работают. Элементы в ACL проверяются слева на право. Здесь под ``доступ запрещён'' понимается, что пользователю не разрешено использовать некоторую управляющую команду. Когда прибывает управляющее сообщение, ipa(8) переводит UID и GID отправителя сообщения в имя пользователя и имя группы и проверяет соответствующий ACL.

Параметр ctl_dump_acl применяет указанный ACL к команде dump:

ctl_dump_acl = <класс>;

Параметр ctl_freeze_acl применяет указанный ACL к команде freeze:

ctl_freeze_acl = <класс>;

Параметр ctl_stat_acl применяет ACL для команд status и memory:

ctl_stat_acl = <класс>;

Параметр ctl_rule_acl применяет указанный ACL к правилу для команд delete, expire, restart, set и status и к автоправилам для команд create и status:

ctl_rule_acl = <класс>;

Если ACL не определён и не наследуется, то он воспринимается как пустой ACL, и обозначает, что доступ запрещён для всех.

Давайте рассмотрим как использовать все эти параметры на примерах.

Пример 1:

ctl_enable = yes;
ctl_socket_perm = ug;

В этом примере ipa(8) не знает как получить информацию об отправителе управляющих сообщений программы ipactl(8). Пользователю и группе разрешено отправлять команды в сокет (это контролируется правами доступа сокета). Из соображений безопасности невозможно разрешить другим пользователям выполнять запись в сокет.

Пример 2:

ctl_enable = yes;
ctl_socket_path = "/var/tmp/ipactl.sock";
ctl_timeout = 10s;

Здесь изменён путь к сокету и таймаут.

Пример 3:

ctl_enable = yes;
ctl_socket_perm = ugo;
ctl_acl_class = empty;
ctl_acl_class = root   root;
ctl_acl_class = admins root !john %wheel;
ctl_dump_acl = root;
global {
    ctl_rule_acl = admins;
    /* ... */
}
rulepat "^vip" {
    ctl_rule_acl = root;
    /* ... */
}
rulepat "^staff" {
    ctl_rule_acl = admins;
    /* ... */
}
rule lan-all {
    ctl_rule_acl = empty;
    /* ... */
}

В этом примере ipa(8) знает как получить информацию об отправителе управляющих сообщений программы ipactl(8). Определены три ACL класса: empty, root и admins. Остальным пользователям разрешено отправлять команды в сокет.

  Встроенный ipactl
Любая программа может быть запущена из любой секции, которая содержит параметры exec. В таких секциях также может быть запущена утилита ipactl(8) в асинхронном режиме (если ipactl(8) будет запущена в синхронном режиме и если она будет соединяться с процессом ipa(8), который её вызвал, и если таймаут для соединения будет безграничным, то её выполнение никогда не завершится).

Но асинхронный вызов ipactl(8) в параметре exec имеет один недостаток: такой вызов не является атомарным и требует некоторого времени. Проблема усиливается, если необходимо вызвать ipactl(8) несколько раз из одной и той же секции.

Параметр ictl это встроенная версия ipactl(8), которая поддерживает следующие команды:

ictl "-r <правило> set ...";
ictl "-r <правило> -l <лимит> expire|restart|set ...";
ictl "-r <правило> -t <порог> set ...";

Форматы значений этого параметра аналогичны форматам соответствующих команд в ipactl(8). Этот параметр может быть использован в любой секции, которая содержит параметры exec и он должен быть указан перед параметрами exec из той же самой секции. Возможно использовать несколько параметров ictl. Заданная управляющая команда в этом параметре всегда вызывается синхронно, игнорируя значение параметра sync_exec. В действительности ipactl(8) вообще не запускается, так как соответствующие функции вызываются напрямую.

Параметр ictl может быть использован только внутри статических правил и глобальной секции startup. Возможно реализовать поддержку этого параметра для динамических правил, но непонятно необходимо ли это или нет.

Есть одно ограничение на управляющие команды. Управляющая команда не может быть вызвана для правила (лимита или порога), из которого она вызывается. Возможно создать циклы из вызовов управляющих команд (например, управляющая команда вызывается из одного лимита и изменяет состояние другого лимита, тот другой лимит вызывает управляющую команду, которая изменяет состояние исходного лимита). Такие циклы проверяются в ipa(8) и считаются результатом неправильной конфигурации.

Как было сказано выше параметр ictl может быть использован в секциях startup, но вы должны использовать этот параметр в этих секциях только в том случае, если вы полностью понимаете что вы делаете.

Любая команда заданная в параметре ictl: 1) для лимита является синхронной по отношению к событиям этого лимита; 2) для порога является асинхронной по отношению к событиям этого порога; 3) для правила является асинхронной по отношению к событиям лимитов и порогов этого правила.

Если вы хотите проверить корректность указанных имён правил, лимитов и порогов в управляющих командах, то запустите ipa(8) с ключами -tt, так как одиночный ключ -t не проверяет присутствие имён.

Пример:

rule 1 {
    /* ... */
    limit 1 {
        /* ... */
        expire {
            expire = +W;
            ictl "-r 1 -l 2 expire";
            ictl "-r 2 -l 1 restart";
            ictl "-r 2 set counter +1000";
            /* ... */
        }
    }
    limit 2 {
        /* ... */
    }
}
rule 2 {
    /* ... */
    limit 1 {
        /* ... */
    }
}

Когда достигнутый первый лимит из первого правила будет перезапускаться, то он также выполнит ``expire'' для (возможно достигнутого) второго лимита из первого правила, перезапустит (возможно недостигнутый) первый лимит из второго правила, а также добавит 1000 единиц статистики второму правилу.

  Замораживание работы ipa(8)
Иногда необходимо быть уверенным, что ipa(8) ничего не выполняет в течении некоторого промежутка времени. Существуют два параметра, которые позволяют заморозить работу ipa(8).

Первый параметр sleep_after_dump позволяет определить период времени, в течении которого ipa(8) должна приостановить свою работу и игнорировать все сигналы после выполнения команды dump от ipactl(8).

Второй параметр freeze_time позволяет определить период времени, в течении которого ipa(8) должна приостановить свою работу и игнорировать все сигналы после получения команды freeze от ipactl(8).

Эти параметры имеют следующие аргументы:

freeze_time = <время>;
sleep_after_dump = <время>;

Значения этих параметров не должны быть относительно большими, так как существует вероятность того, что какое-то из временных событий будет проверено слишком поздно.

Эти параметры должны быть расположены отдельно.

Пример:

freeze_time = 30s
sleep_after_dump = 5s;

Здесь сообщается ipa(8), что необходимо приостановить свою работу на 5 секунд после получения команды dump, и приостановить свою работу на 30 секунд после получения команды freeze. Это общий случай: отправляем команду dump, например, если необходимо получить текущую статистику позже, и отправляем команду freeze, чтобы заморозить работу ipa(8) и произвести некоторые изменения в системе. Здесь предполагается, что 30 секунд будет достаточно, чтобы сделать все необходимые изменения в системе.

  Порядок активных правил
ipa(8) проверяет правила начиная с головы очереди активных правил. Если все правила независимы, то вы не должны беспокоится об их порядке, но если, например, правило r1 должно быть проверено перед правилом r2, то прочтите следующие несколько параграфов.

Если нет параметров ac_gather_* и нет параметров worktime, то порядок активных правил такой же как и порядок правил в конфигурационном файле.

Если есть по крайней мере один ac_gather_* параметр, то порядок активных правил изменяется, чтобы последовать зависимостям определённых в параметрах ac_gather_*, но если какие-то правила не соответствуют регулярным выражениям определённым в параметрах ac_gather_*, то их относительный порядок такой же как и их относительный порядок в конфигурационном файле.

Если нет параметров ac_gather_* и если есть правила с параметрами worktime, то порядок этих правил может быть изменён.

Динамические правила всегда добавляются в начало очереди активных правил, так как динамические правила не могут иметь параметров ac_gather_*, но некоторые статические правила могут получать статистику из динамических правил.

Если необходимо сохранять порядок активных правил таким же как и порядок правил в конфигурационном файле, то установите параметр keep_rules_order в ``yes'' (значение по умолчанию равно ``no''):

keep_rules_order = <boolean>;

Будьте осторожны с порядком правил в конфигурационном файле, если у вас есть по крайне мере один параметр ac_gather_* и параметр keep_rules_order установлен в ``yes'': располагайте правила, которые отдают статистику, перед правилами, которые принимают статистику.

Лимиты и пороги в одном правиле проверяются в том же самом порядке, в котором они записаны в конфигурационном файле и этот порядок не меняется.

Заметьте, что некоторые модули чувствительны к порядку активных правил.

Этот параметр не должен быть расположен в какой-либо секции.

Пример:

keep_rules_order = yes;

Теперь ipa(8) не будет менять порядок активных правил.

  Отладка
Иногда необходимо определить почему что-то идёт не так как должно было быть. Существует несколько параметров, которые могут быть использованы для отладки:

debug_ac_null  - отладка использования системы учёта null (отдельно, 1);
debug_db_null  - отладка использования базы данных null (отдельно, 1);
debug_time     - отладка различных временных событий (отдельно, 2);
debug_worktime - отладка параметров worktime (отдельно, 1);
debug_exec     - отладка параметров exec (правило, 1);
debug_autorule - отладка автоправил (отдельно, 1);
debug_limit    - отладка событий лимитов (правило, 1);
debug_limit_init - сообщать об инициализации лимита (правило, 1);
debug_threshold  - отладка событий порогов (правило, 1);
debug_threshold_init - сообщать об инициализации порога (правило, 1).

Каждый параметр отладки принимает уровень отладки как аргумент, максимальный уровень отладки для каждого параметра определён как номер в скобках. Если в скобках есть слово ``отдельно'', то этот параметр не должен располагаться в какой-либо секции. Если в скобках есть слово ``правило'', то этот параметр может быть расположен в секциях global, rule, rulepat и autorule.

По умолчанию отладка отключена.

Пример:

debug_worktime = 1;
debug {
    debug_limit_init = 1;
}

В этом примере в лог-файл будет посылаться детальная информация о интервалах параметров worktime и об инициализации лимитов для всех правил.

ФАЙЛЫ
ipa.conf

(запустите ipa с ключём -h, чтобы увидеть путевое имя конфигурационного файла, используемое по умолчанию)

ДРУГИЕ ИСТОЧНИКИ
ipa(8), ipactl(8), ipastat(8), ipastat.conf(5), ipa_mod(3)

АВТОР
Andrey Simonenko <simon@comsys.ntu-kpi.kiev.ua>

НЕДОРАБОТКИ
Если вы обнаружите какие-либо ошибки, то, пожалуйста, сообщите мне по email.