Page tree
Skip to end of metadata
Go to start of metadata

Каждый плагин может использоваться в нескольких экземплярах с различными конфигурациями. Конфигурация экземпляра плагина описывается в ветке plugins<plugin_name><plugin_instance_name>, где <plugin_name> — наименование плагина, а <plugin_instance_name> — наименование конкретного его экземпляра.

Плагин proxy

Плагин предназначен для маршрутизации запросов к другим экземплярам плагинов этого же агента. Для этого в Perl-модуле hard.pm сервера FreeRADIUS указывается экземпляр плагина proxy, в конфигурации которого определяются все необходимые маршруты.

Правила маршрутизации запросов

plugins → proxy → <plugin_instance_name> → routes

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

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

Использован будет первый маршрут, для которого соответствующее выражение вернёт истину. Если ни один из описанных маршрутов не может быть применён, запрос будет передан плагину, указанному в ключе plugins → proxy → <plugin_instance_name> → default конфигурации.

Пример конфигурации

 

plugins:
  proxy:
    dhcp:
      routes:
        base/dhcp-o82: $request.RAD_REQUEST.try("DHCP-Relay-Circuit-Id").present?()
        base/dhcp-ip-private: $request.RAD_REQUEST.DHCP-Client-IP-Address.private_ip4?()
 
      default: "base/dhcp-ip-public"

В данном случае агент HARD имеет три активных экземпляра плагина base, предназначенных для разных схем авторизации абонентов. При этом для всех схем используется один общий экземпляр FreeRADIUS в режиме DHCP-сервера. В нём указан плагин proxy/dhcp агента HARD. Плагин proxy/base выбирает обработчика каждого полученного запроса следующим образом:

  1. Если в запросе присутствует атрибут DHCP-Relay-Circuit-Id, запрос передаётся на обработку экземпляру dhcp-o82 плагина base. Результат обработки отправляется в FreeRADIUS.
  2. Если в запросе присутствует атрибут DHCP-Client-IP-Address (но нет DHCP-Relay-Circuit-Id) и в нём указан частный IP-адрес, запрос передаётся на обработку экземпляру dhcp-o82 плагина base. Результат обработки отправляется в FreeRADIUS.

Плагин base

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

Стэк обработки запроса

plugins → base → <plugin_instance_name> → call_stack

Определяет порядок вызова рабочим процессом фильтров для обработки поступившего запроса:

  1. Вызываются фильтры в прямом (сверху вниз) порядке, при этом пропускаются after-фильтры.
  2. Запрос обрабатывается ядром плагина: выполняется действие, соответствующее типу запроса и формируется ответ.
  3. Вызываются фильтры в обратном (снизу вверх) порядке, при этом пропускаются before-фильтры.
Пример стэка обработки запроса — IPoE-доступ с идентификацией по IP-адресу и динамической авторизацией сервисов Cisco ISG
plugins:
  base:
    isg-ipoe-by-ip:
      call_stack:
        # Установка тега с типом запроса
        - set_tag_before/request_type
 
        # Отправка отказа в случае возникновения ошибки
        - reject_on_error/main

        # Игнорирование лишнего аккаунтинга
        - check_attr_before/finish_processing_of_unnecessary_accounting

        # Аккаунтинг по базовой сессии
        - set_tag_before/base-session-accounting

        # Авторизация ISG-сервисов вида IPOE-SC-INET-<Rate>-<Burst>
        - set_tag_before/auth-internet-access-isg-service
        - match_before/get_speed_limit_values_for_internet-access-isg-service
        - format_after/set_cisco-service-info_attr_for_internet-access-isg-service

        # Добавление префикса ко всем активируемым сервисам
        - map_after/add_prefix_to_isg-services

Действия ядра плагина

plugins → base → <plugin_instance_name> → actions

В данной ветви конфигурации содержатся отдельные блоки, определяющие действия ядра плагина для каждого поддерживаемого типа запроса. На текущий момент поддерживаются 4 типа запросов сервера FreeRADIUS:

  1. Авторизация.
  2. Аутентификация.
  3. Пост-аутентификация.
  4. Аккаунтинг.

Общее описание того, что происходит на этих стадиях в самом сервере FreeRADIUS доступно в вики проекта: http://wiki.freeradius.org/guide/Concepts.

Авторизация — действие authorize

На данном этапе обработки запроса RADIUS-сервером от агента HARD требуется:

  1. Определить, известно ли ему абонентское оборудование, запросившее доступ.
  2. Предоставить RADIUS-серверу корректный пароль для выполнения аутентификации.

Для этого в действии выполняются:

  1. Подбор профилей оборудования.
  2. Формирование ответа.
Подбор профилей оборудования

Параметры подбора профилей оборудования определяются в блоках plugins → base → <plugin_instance_name> → actions → authorize → customer_profile и plugins → base → <plugin_instance_name> → actions → authorize → provider_profile для профилей абонентского и операторского оборудования соответственно. Подобранные профили записываются в системные переменные $customer_profile и $provider_profile, а идентификатор привязки абонентского профиля к операторскому — в переменную $bind_id.

Данные блоки имеют одинаковую структуру:

КлючДопустимые значенияОписание

 

set_by

search_cache

Поиск профиля в кэше (MongoDB). Поиск выполняется в соответствии с критериями, указанными в ключе query. Хотя бы один из профилей (абонентский либо операторский) должен подбираться с использованием данного метода. Для подбора обоих профилей данный метод, как правило, используется только при необходимости формировать различные ответы в зависимости от результатов подбора каждого из профилей и наличия между ними подходящей привязки. Пример использования — IPoE-портал, для которого при авторизации абонентскому оборудованию выдаются различные атрибуты в зависимости от актуальности данных о нём в Гидре.

Если оба профиля подбираются с использованием этого метода, системная переменная $bind_id (идентификатор привязки абонентского оборудования к операторскому) заполняется в соответствии с типом привязки, указанном в ключе plugins → base → <plugin_instance_name> → actions → authorize → default_bind_type.

set_first_from_customer_profileПрофиль операторского оборудования извлекается из ранее подобранного профиля абонентского оборудования. В ключе filter определяются критерии отбора операторских профилей, а из удовлетворяющих фильтру профилей выбирается первый.
set_first_from_provider_profileПрофиль абонентского оборудования извлекается из ранее подобранного профиля операторского оборудования. В ключе filter определяются критерии отбора абонентских профилей, а из удовлетворяющих фильтру профилей выбирается первый. Данный метод, обычно используется для схемы доступа, в которой абонентское оборудование идентифицируется по данным операторского оборудования, к которому оно подключено: DHCP Option 82, Q-in-Q VLAN и т. д. При этом дополнительная фильтрация может использваться для проверки MAC-адреса абонентского оборудования, или ограничения профилей соответствующим нужной схеме доступа шаблоном, если одновременно используется несколько схем.
queryОдна или несколько пар key:value

Запрос для поиска профиля в кэше. Используется только с search_cache в качестве значения set_by. На основании пар key:value строится запрос в базу данных кэша.

Несмотря на то, что эти пары задаются не последовательностью, их порядок имеет значение. При запуске, агент формирует в БД кэша недостающие индексы для оптимизации работы с ней. Индексы для подбора профилей формируются с учётом порядка ключей в данном блоке. В связи с этим для оптимальной работы рекомендуется располагать ключи key в порядке убывания селективности: от более селективных (логин, MAC-адрес), к менее селективным (номер порта коммутатора, IP-адрес сервера доступа).

В качестве value такой пары может быть указан список значений. В таком случае соответствующий ключ профиля должен должен иметь значение, равное любому из элементов списка: в запросе к БД кэша к этому ключу применится оператор $in. Это позволяет, например, подбирать профиль по адресу привязанной к абонентскому оборудованию подсети, в которую входит IP-адрес из запроса, если маска подсети не известна. С помощью фильтра map_before для IP-адреса вычисляются подсети всех возможных размеров и список этих подсетей указывается в качестве критерия подбора профиля абонентского оборудования.

filterОдна или несколько пар key:valueФильтр для отбора вложенных профилей. Используется только с set_first_from_customer_profile или set_first_from_provider_profile в качестве значения set_by. В качестве критерия фильтра можно указывать тип привязки абонентского оборудования к операторскому, значения атрибутов подбираемого профиля и т. д.
Пример раздельного подбора абонентского и операторского профилей — использование IPoE-портала и идентификация по VLAN'у и номеру порта коммутатора с дополнительной проверкой MAC-адреса
plugins:
  base:
    jmx5-qinq:
      actions:
        authorize:
          customer_profile: &customer_profile
            set_by: search_cache
            query:
              MAC-Address: $request.RAD_REQUEST.User-Name.normalize_mac()

          provider_profile: &provider_profile
            set_by: search_cache
            query:
              Switch-VLAN: $var.Remote-Circuit-Ids.Remote_Id
              Switch-Port-Code: $var.Remote-Circuit-Ids.Circuit_Id

          default_bind_type: network_connection
Пример подбора операторского профиля и выбор абонентского из него — идентификация по данным DHCP Option 82
plugins:
  base:
    se-ipoe-by-o82:
      actions:
        authorize:
          provider_profile:
            set_by: search_cache
            query:
              Switch-IP: $request.RAD_REQUEST.Agent-Remote-Id.substring(6).unhex()
              Switch-Port-Code: $request.RAD_REQUEST.Agent-Circuit-Id.substring(12).to_i(16)

          customer_profile:
            set_by: set_first_from_provider_profile
            filter:
              Auth-Scheme: '"DHCP+L2TP"'
Пример подбора абонентского профиля без подбора операторского — идентификация по логину
plugins:
  base:
    l2tp-by-login:
      actions:
        authorize:
          customer_profile: &customer_profile
            set_by: search_cache
            query:
              L2TP-Login: $request.RAD_REQUEST.User-Name.lower().regexp_replace("@.*$|\\\\t|\s")
              Auth-Scheme: '"DHCP+L2TP"'

          provider_profile: {}
Формирование ответа

После подбора профилей, можно формировать ответ. На этом этапе в распоряжении ядра плагина имеются следующие данные:

  • Обрабатываемый запрос в $request.
  • Профиль абонентского оборудования в $customer_profile.
  • Профиль операторского оборудования в $provider_profile.
  • Идентификатор привязки абонентского оборудования к операторскому в $bind_id.
  • Установленные before-фильтрами пользовательские переменные в словаре $var и теги в $context.

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

Шаблоны ответов определяются как последовательность и размещаются в ключе plugins → base → <plugin_instance_name> → actions → authorize → reply конфигурации плагина. Элементы данной последовательности имеют следующую структуру:

КлючДопустимые значенияОписание
conditionЛогическое вычисляемое выражениеУсловие применения шаблона: шаблон выбирается, если вычисляемое выражение из данного ключа вернуло истину, либо не задано.

templateRAD_CHECK

 

Пары key:value или возвращающее словарь вычисляемое выражение.

Шаблон блока RAD_CHECK ответа на запрос, где key — наименование RADIUS-атрибута ответа, а value — вычисляемое выражение, результат которого станет значением этого атрибута. В качестве value могут использоваться как единичные выражения, так и последовательности таких выражений. Последовательности выражений применяются для атрибутов, допускающих множественные значения. Атрибуты блока RAD_CHECK в сервере FreeRADIUS используются для управления процессом обработки запроса:

templateRAD_REPLY

 

Пары key:value или возвращающее словарь вычисляемое выражение.

Шаблон блока RAD_REPLY ответа на запрос, где key — наименование RADIUS-атрибута ответа, а value — вычисляемое выражение, результат которого станет значением этого атрибута. В качестве value могут использоваться как единичные выражения, так и последовательности таких выражений. Последовательности выражений применяются для атрибутов, допускающих множественные значения, таких как, например, Cisco-AVPair.

templateresult

Значение из словаря $rlm

Числовой код результата обработки запроса. Набор кодов и их предназначение описаны в вики проекта FreeRADIUS. Как правило используются два основных кода:

  • $rlm.OK — успешная авторизация.
  • $rlm.REJECT — отказ в авторизации.

Ядро плагина по очереди (сверху вниз) проверяет каждый шаблон ответа на возможность его применения: если условие применения выполняется, для формирования ответа используется этот шаблон, если нет — ядро переходит к проверке условия для следующего.

Формирование ответа, включая выбор шаблона, может выполняться не самим ядром плагина, а специальным фильтром render_reply_after. При наличии такого фильтра в стэке обработки запроса, ядро плагина не формирует ответ, а только подбирает профили оборудования. Это позволяет провести дополнительные вычисления и проверки с помощью after-фильтров и уже на основании их результатов формировать ответ.

В блоках RAD_CHECK и RAD_REPLY шаблонов ответа можно использовать специальный ключ '*'. Значением такого ключа должно быть вычисляемое выражение, возвращающее словарь «Наименование RADIUS-атрибута — Его значение». При наличии такой записи, плагин добавит в ответ все атрибуты из этого словаря. Если некоторые атрибуты при этом необходимо исключить, следует в выражение добавить метод except.

Пример последовательности шаблонов ответов — IPoE с использованием сервисной модели Cisco ISG
plugins:
  base:
    isg-ipoe-by-ip:
      actions:
        authorize:
          reply: &default_reply
            # Авторизация ISG-сервисов вида IPOE-SC-INET-<Rate>-<Burst>
            - condition: $context.has_tag?("auth-internet-access-isg-service") and $request.RAD_REQUEST.User-Name.like?("^IPOE-SC-INET(-\d+){2}$")
              template:
                RAD_CHECK: &default_rad_check
                  # Добавляем все атрибуты из блока RAD_CHECK запроса, кроме «правильного» пароля
                  '*': $request.RAD_CHECK.except("Cleartext-Password")
                  # Идентификация абонентского оборудования выполняется по IP-адресу, а для сервисов вовсе не нужна.
                  # Поэтому для FreeRADIUS передаём в качестве «правильного» пароль из запроса.
                  Cleartext-Password: $request.RAD_REQUEST.User-Password

                RAD_REPLY:
                  Cisco-AVPair:
                    - '"subscriber:accounting-list=HYDRA-IPOE"'
                    - '"ip:traffic-class=in access-group name IPOE-ACL-ALL-TRAFF priority 1000"'
                    - '"ip:traffic-class=out access-group name IPOE-ACL-ALL-TRAFF priority 1000"'
                  Acct-Interim-Interval: '"300"'
 
                result: $rlm.OK

            # Оборудование не подобралось - отказ
            - condition: $customer_profile.null?()
              template:
                RAD_CHECK:
                  Auth-Type: '"Reject"'

                RAD_REPLY:
                  Reply-Message: '"Incorrect IP address"'

                result: $rlm.NOTFOUND

            # Всё в порядке
            - condition: $customer_profile.attributes.try("Internet-Access-Service-State") == "SERV_STATE_Provision"
              template:
                RAD_CHECK:
                  <<: *default_rad_check

                RAD_REPLY: &ok_rad_reply
                  Cisco-AVPair: '"subscriber:accounting-list=HYDRA-IPOE"'
                  Acct-Interim-Interval: '"300"'
                  Idle-Timeout: '"86400"'
                  Cisco-Account-Info: $customer_profile.attributes.ISG-IPoE-Services.split(",")

                result: $rlm.OK

            # Доступ заблокирован
            - template:
                RAD_CHECK:
                  <<: *default_rad_check

                RAD_REPLY:
                  <<: *ok_rad_reply
                  Cisco-Account-Info:
                    - '"IPOE-SC-REDIRECT"'
                    - '"IPOE-SC-OPENGARDEN"'

                result: $rlm.OK

Аутентификация — действие authenticate

На данном этапе RADIUS-сервер от агента HARD требуется подготовить атрибуты ответа, которые сервер отправит в случае успешной аутентификации модулем pap, eap, mschap и т.п. Для этого в действии выполняются:

  1. Подбор профилей оборудования.
  2. Проверка ограничения количества одновременных сессий.
  3. Формирование ответа.

Подбор профилей и шаблоны ответов настраиваются аналогично действию authorize. Как правило, критерии подбора профилей и шаблоны ответов используются те же, что и в authorize — чтобы их не повторять в конфигурации, рекомендуется применять такие элементы YAML-формата как якори и ссылки.

Ограничение количества одновременных сессий

Проверка количества сессий выполняется в разрезе профилей оборудования следующим образом:

  1. Разрешённое количество сессий определяется как наибольшее значение атрибута Simultaneous-Use среди подобранных абонентского и операторского профилей. Если такой атрибут в обоих профилях отсутствует или пуст, проверка не выполняется.
  2. К базе данных кэша делается запрос для подсчёта текущего количества активных сессий. Учитываются только сессии, привязанные к подобранным ранее профилям оборудования, и находящиеся в активном состоянии: Начата или Обновлена.
  3. Если текущее количество сессий больше или равно разрешённому, запрос маркируется тегом simultaneous-limit-exceeded.

Проверка наличия данного может быть использована как для выбора шаблона ответа, так и в after-фильтрах. Это позволяет, например, не отказывать в авторизации «лишним» сессиям, а отправлять для них особый набор атрибутов.

Пример настройки аутентификации по логину и паролю с ограничением на количество одновременных сессий
plugins:
  base:
    l2tp-by-login:
      actions:
        authorize:
...
        authenticate:
          customer_profile:
            # Критерии подбора абонентского профиля идентичны таковым в authorize
            <<: *customer_profile
 
          provider_profile: {}

          reply:
            # Достигнуто ограничение на количество одновременных сессий
            - condition: $context.has_tag?('simultaneous-limit-exceeded')
              template:
                RAD_REPLY:
                  Reply-Message: '"You are already logged in"'

                result: $rlm.REJECT

            # Фиксированный внешний IP-адрес
            - condition: '    $customer_profile.attributes.try("Internet-Access-Service-Status") == "SERV_STATE_Provision"
                          and $customer_profile.attributes.Real-IP-Address.ip4?()'
              template:
                RAD_REPLY:
                  Acct-Interim-Interval: '"300"'
                  Cisco-AVPair:
                    - '"ip:vrf-id=REAL"'
                    - '"ip:ip-unnumbered=loopback221"'
                  Class: $customer_profile.attributes.L2TP-Class.coalesce("7")
                  Framed-IP-Address: $customer_profile.attributes.Real-IP-Address
                  Idle-Timeout: '"86400"'

                result: $rlm.OK

            # Динамический внутренний IP-адрес
            - condition: $customer_profile.attributes.try("Internet-Access-Service-Status") == "SERV_STATE_Provision"
              template:
                RAD_REPLY:
                  Acct-Interim-Interval: '"300"'
                  Cisco-AVPair:
                    - '"ip:vrf-id=NAT"'
                    - '"ip:ip-unnumbered=loopback220"'
                  Class: $customer_profile.attributes.L2TP-Class.coalesce("5")
                  Framed-Pool: '"NAT-1"'
                  Idle-Timeout: '"86400"'

                result: $rlm.OK

            # Доступ заблокирован (недостаточно средств)
            - template:
                RAD_REPLY:
                  Acct-Interim-Interval: '"600"'
                  Cisco-AVPair:
                    - '"ip:vrf-id=NOPAY"'
                    - '"ip:ip-unnumbered=loopback222"'
                  Cisco-Service-Info: '"QU;256000;9600;9600;D;256000;9600;9600"'
                  Framed-Pool: '"FREE-DNAT"'
                  Idle-Timeout: '"86400"'
                  mpd-limit:
                    - '"out#100=all shape 256000 pass"'
                    - '"in#100=all shape 256000 pass"'
                  Session-Timeout: '"86400"'
 
                result: $rlm.OK

Пост-аутентификация — действие post_auth

На данном этапе обработки запросов они передаются сервером FreeRADIUS в агент HARD, как правило, только при работе в режиме DHCP-cервера. Также может применяться для выполнения дополнительных обработок ответа перед отправкой его сервером FreeRADIUS.

Примером такой обработки является вызов хука для отправки в очередь сообщения, содержащего динамический IP-адрес, выдаваемый сервером FreeRADIUS. Если пул динамических IP-адресов реализован на стороне RADIUS-сервера, то выдача адреса из такого пула выполняется сервером в начале этапа пост-аутентификации. В этот момент серверу уже известно, нужно ли выдавать динамический IP-адрес, и если нужно, то из какого пула.

Операции выполняются те же, что и в действии authorize:

  1. Подбор профилей оборудования.
  2. Формирование ответа.

Критерии подбора профилей и шаблоны ответов настраиваются аналогично действию authorize.

Пример настройки пост-аутентификации — FreeRADIUS в роли DHCP-сервера с индентификацией по данным опции 82
plugins:
  base:
    dhcp-by-o82:
      actions:
        post_auth:
          provider_profile:
            set_by: search_cache
            query:
              Switch-IP: $request.RAD_REQUEST.DHCP-Relay-Remote-Id.substring(6).unhex()
              Switch-Port-Code: $request.RAD_REQUEST.DHCP-Relay-Circuit-Id.substring(12).to_i(16)

          customer_profile:
            set_by: set_first_from_provider_profile
            filter:
              Auth-Scheme: '"DHCP+L2TP"'

          reply:
            # Всё в порядке
            - condition: '    $customer_profile.present?()
                          and $customer_profile.attributes.DHCP-IP-Address.ip4?()
                          and $customer_profile.attributes.DHCP-Gateway-IP.ip4?()
                          and $customer_profile.attributes.DHCP-Subnet-Mask.ip4_mask?()'
              template:
                RAD_REPLY:
                  DHCP-Domain-Name-Server:
                    - '"192.168.11.15"'
                    - '"192.168.11.16"'
                  DHCP-IP-Address-Lease-Time: '"86400"'
                  DHCP-Router-Address: $customer_profile.attributes.DHCP-Gateway-IP
                  DHCP-Subnet-Mask: $customer_profile.attributes.DHCP-Subnet-Mask
                  DHCP-Your-IP-Address: $customer_profile.attributes.DHCP-IP-Address

                result: $rlm.OK
 
            # Профиль абонентского оборудования не подобрался - отказ
            - condition: $customer_profile.null?()
              template:
                RAD_REPLY: {}
                reject_reason: '"Customer equipment profile not found // Option 82"'
                result: $rlm.REJECT

            # В профиле нет маски/адреса/шлюза - отказ
            - template:
                RAD_REPLY: {}
                reject_reason: '"Not valid customer equipment profile // Option 82"'
                result: $rlm.REJECT

Аккаунтинг — действие accounting

Данное действие предназначено для обработки всех запросов аккаунтинга.

Дата и время события аккаунтинга определяются по атрибуту Event-Timestamp с учётом задержки пакета, указанной в атрибуте Acct-Delay-Time. Формат даты и времени задаётся в ключе plugins → base → <plugin_instance_name> → actions → accounting → event_timestamp → format и по умолчанию соответствует локали en_US.UTF-8. Правила преобразования и описания формата идентичны таковым у метода datetime.strptime в Python.

Аккаунтинг по сессиям: Start, Stop, Interim-Update

Для каждого полученного пакета аккаунтинга вычисляется уникальный внешний идентификатор: как MD5-хэш от строки из разделённых точкой с запятой внешнего идентификатора сессии (атрибут Acct-Session-Id) и значений атрибутов, перечисленных в ключе plugins → base → <plugin_instance_name> → actions → accounting → session → unique_id_attributes.

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

Схема поиска сессий

На схеме  t1 - момент начала поиска сессии. От момента t1 до (t1 - ... → accounting → session → lookup → hours) (синий интервал) подбирается активная сессия (сессия, не закртая по таймауту), сессия закртая по таймауту ищется на интервале от t1 до (t1 - ... → accounting → session → lookup → timed_out_days) (не отмечен на схеме). Зеленый интервал - интервал, в котором batch закрыл сессии, обычно этот интервал и синий интервал поиска не закрытых по таймауту сессий - пересекаются (в крайнем случае - идут встык). Если по каким либо причинам batch не работал определенное время, между этими интервалами возникнет пробел, сессия, попавшая в этот пробел не подберется, и создастся ее дубль. Для разрешения таких ситуаций к интервалу подбора активных сессий добавляется ... → accounting → session → lookup → additional_hours (обозначен красным не схеме).

При обработке первого пакета аккаунтинга по сессии ядро плагина подбирает профили абонентского и операторского оборудования в соответствии с критериями, указанными в ключах customer_profile и provider_profile блока plugins → base → <plugin_instance_name> → actions → accounting. Эти профили фиксируются в сессии и используются для определения её типа при загрузке в основную БД Гидры. Подбор профилей настраивается аналогично действию authorize. Как правило, критерии подбора профилей используются те же, что и в действии authorize — чтобы их не повторять в конфигурации, рекомендуется применять такие элементы YAML-формата как якори и ссылки.

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

Обновление и запись новых сведений о сессии выполняется в соответствии с настраиваемым шаблоном. Шаблоны сессий определяются как последовательность и размещаются в ключе plugins → base → <plugin_instance_name> → actions → accounting → session → templates конфигурации плагина. Элементы данной последовательности имеют следующую структуру:

КлючДопустимые значенияОписание
conditionЛогическое вычисляемое выражениеУсловие применения шаблона: шаблон выбирается, если вычисляемое выражение из данного ключа вернуло истину, либо не задано.
attributesПары key:value

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

Атрибуты сессии вычисляются и сохраняются только при обработке первого пакета аккаунтинга по данной сессии. Помимо этих атрибутов, при обработке Stop-пакета в отдельный параметр сессии записывается значение атрибута Acct-Terminate-Cause, отражающего причину завершения сессии.

template

Пары key:value.Шаблон ответа, где key — наименование RADIUS-атрибута ответа, а value — вычисляемое выражение, результат которого станет значением этого атрибута. В качестве value могут использоваться как единичные выражения, так и последовательности таких выражений. Последовательности выражений применяются для атрибутов, допускающих множественные значения, таких как, например, Cisco-AVPair.
services

Последовательность структур из трёх полей:

  • service_id
  • value
  • unit

Перечень услуг, которые необходимо тарифицировать в рамках данной сессии:

  • service_id — идентификатор номенклатурной позиции услуги в Гидре.
  • value — вычисляемое выражение, результат которого интерпретируется как общее потреблённое с начала данной сессии количество услуги.
  • unit — единица измерения услуги. Допустимые значения поля: bytes, kilobytes, megabytes, gigabytes, seconds, minutes, hours, days, months, bits_per_second, kilobits_per_second, megabits_per_second.

Как правило, в рамках одной сессии тарифицируется две детализированных потоковых услуги: для входящего направления трафика и для исходящего. Для вычисления количества по счётчикам Acct-Input-Octets, Acct-Output-Octets, Acct-Input-Gigawords и Acct-Output-Gigawords используются, как правило, следующие вычисляемые выражения:

  • $request.RAD_REQUEST.Acct-Input-Octets.to_i() + 4 * $request.RAD_REQUEST.Acct-Input-Gigawords.to_i().gigabytes()
  • $request.RAD_REQUEST.Acct-Output-Octets.to_i() + 4 * $request.RAD_REQUEST.Acct-Output-Gigawords.to_i().gigabytes()

service_profiletemplate_id

Целое числоДанный параметр применяется только при использовании на сервором доступа сервисной модели, при которой аккаунтинг приходит отдельно для базовой сесии абонента и для предоставляемых в рамках неё сервисов. Для шаблонов сервисных сессий в нём необходимо указать идентфикатор дочернего шабона абонентских профилей, который соответствует данному сервису. Тип такой сервисной сессии будет выбран на основании указанного сервисного шаблона.
load_to_hydratrue, falseФлаг, отражающий необходимость загрузки записи о данной сессии в основную БД Гидры. По умолчанию загружаются все сессии — данный флаг имеет смысл явно указывать только для «гостевых» сессий, для которых в Гидре нет подходящих профилей оборудования.

Ядро плагина по очереди (сверху вниз) проверяет каждый шаблон ответа на возможность его применения: если условие применения выполняется, для формирования ответа используется этот шаблон, если нет — ядро переходит к проверке условия для следующего.

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

Пример настройки действия accounting — учёт L2TP-сессий с тарификация интернет-трафика для сессий с доступом в Интернет
plugins:
  base:
    l2tp-by-login:
      actions:
        authorize:
...
        authenticate:
...
        accounting:
          # Абонентский профиль подбирается так же, как и при авторизации
          customer_profile:
            <<: *customer_profile
          
          # Операторский профиль не используется
          provider_profile: {}

          session:
            # Набор атрибутов для формирования уникального внешнего идентификатора сессии
            unique_id_attributes: [User-Name, NAS-IP-Address, Framed-IP-Address, NAS-Port]
 
            # Шаблоны сессий
            templates:
              # Сессия с доступом в Интернет

              - condition: '   $customer_profile.attributes.try("Service-State-Code") == "SERV_STATE_Provision"
                            or $session.attributes.try("Service-State-Code") == "SERV_STATE_Provision"'

                attributes: &session_attributes
                  Calling-Station-Id: $request.RAD_REQUEST.try("Calling-Station-Id")
                  Called-Station-Id: $request.RAD_REQUEST.try("Called-Station-Id")
                  Framed-IP-Address: $request.RAD_REQUEST.try("Framed-IP-Address")
                  NAS-Identifier: $request.RAD_REQUEST.NAS-Identifier
                  NAS-IP-Address: $request.RAD_REQUEST.NAS-IP-Address
                  Service-State-Code: $customer_profile.attributes.try("Service-State-Code")
                  Session-Template: '"Full access session"'
                  User-Name: $request.RAD_REQUEST.User-Name
 
                # Тарификация потоковых услуг передачи данных
                # Счётчики в пакетах приходят относительно сервера доступа — необходимо инвертировать направление для абонента
                services:
                    # Услуга «Интернет-трафик вх.»
                  - service_id: 40213701
                    value: $request.RAD_REQUEST.try("Acct-Output-Octets", "0").to_i() + 4 * $request.RAD_REQUEST.try("Acct-Output-Gigawords", "0").to_i().gigabytes()
                    unit: bytes
 
                    # Услуга «Интернет-трафик исх.»
                  - service_id: 40213501
                    value: $request.RAD_REQUEST.try("Acct-Input-Octets", "0").to_i() + 4 * $request.RAD_REQUEST.try("Acct-Input-Gigawords", "0").to_i().gigabytes()
                    unit: bytes
 
              # Гостевые сессии незарегистрированных абонентов — не нужно загружать в Гидру
              - condition: '   $customer_profile.null?()
                           and $session.customer_profile_id.empty?()'
                attributes:
                  <<: *session_attributes
                  Session-Template: '"Guest session"'
                load_to_hydra: false
 
              # Сессии с ограниченным доступом — не нужно тарифицировать услуги
              - attributes:
                  <<: *session_attributes
                  Session-Template: '"Restricted session"'
 
                services: {}
Пример настройки действия accounting — сервисная модель Cisco ISG с раздельной тарификацией локального и интернет-трафика
plugins:
  base:
    isg-ipoe-by-ip:
      actions:
        authorize:
...
        authenticate:
...
        accounting:
          # Абонентский профиль подбирается так же, как и при авторизации: по IP-адресу абонентского оборудования
          customer_profile:
            <<: *customer_profile
 
          # Операторский профиль не используется
          provider_profile: {}

          session:
            templates:
              # Базовая сессия
              - condition: $request.RAD_REQUEST.try("Cisco-Service-Info").empty?()
 
                attributes: &session_attributes
                  User-Name: $request.RAD_REQUEST.User-Name
                  NAS-IP-Address: $request.RAD_REQUEST.NAS-IP-Address
                  NAS-Port-Id: $request.RAD_REQUEST.NAS-Port-Id
                  Cisco-AVPair: $request.RAD_REQUEST.Cisco-AVPair
                  Cisco-Service-Info: $request.RAD_REQUEST.try("Cisco-Service-Info")
 
              # Сервисная сессия доступа в Интернет
              - condition: $request.RAD_REQUEST.Cisco-Service-Info.like?("^NIPOE-SC-(INET|NIGHT)-.+$")
                attributes:
                  # Набор сохраняемых атрибутов тот же, что и для базовой сессии
                  <<: *session_attributes
 
                services:
                  # Услуга «Интернет-трафик исх.»
                  - service_id: 40213501
                    value: $request.RAD_REQUEST.try("Acct-Input-Octets", "0").to_i() + 4 * $request.RAD_REQUEST.try("Acct-Input-Gigawords", "0").to_i().gigabytes()
                    unit: bytes
 
                  # Услуга «Интернет-трафик вх.»
                  - service_id: 40213701
                    value: $request.RAD_REQUEST.try("Acct-Output-Octets", "0").to_i() + 4 * $request.RAD_REQUEST.try("Acct-Output-Gigawords", "0").to_i().gigabytes()
                    unit: bytes
 
                service_profile:
                  # Идентификатор шаблона абонентских профилей «IPoE - Доступ к сети Интернет»
                  template_id: 25244901
 
              # Сервисная сессия доступа к локальной сети с учётом трафика
              - condition: $request.RAD_REQUEST.Cisco-Service-Info.like?("^NIPOE-SC-(LOCAL|PRIVATE)$")
                attributes:
                  # Набор сохраняемых атрибутов тот же, что и для базовой сессии
                  <<: *session_attributes

                services:
                  # Услуга «Локальный трафик исх.»
                  - service_id: 40214001
                    value: $request.RAD_REQUEST.try("Acct-Input-Octets", "0").to_i() + 4 * $request.RAD_REQUEST.try("Acct-Input-Gigawords", "0").to_i().gigabytes()
                    unit: bytes

                  # Услуга «Локальный трафик вх.»
                  - service_id: 40214201
                    value: $request.RAD_REQUEST.try("Acct-Output-Octets", "0").to_i() + 4 * $request.RAD_REQUEST.try("Acct-Output-Gigawords", "0").to_i().gigabytes()
                    unit: bytes

                service_profile:
                  # Идентификатор шаблона абонентских профилей «IPoE - Доступ к локальной сети»
                  template_id: 25245101

              # Прочие сервисные сессии, для которых тарификация услуг не требуется
              - attributes:
                  # Набор сохраняемых атрибутов тот же, что и для базовой сессии
                  <<: *session_attributes
 
                service_profile:
                  # Идентификатор шаблона абонентских профилей «IPoE - Доступ к локальной сети»
                  template_id: 25245101
Пакеты Accounting-On и Accounting-Off

Пакеты аккаунтинга, в которых атрибут Acct-Status-Type имеет значение Accounting-On или Accounting-Off, могут отправляться сервером доступа при его запуске или перед остановкой. Они используются для того чтобы RADIUS-сервер на основании одного пакета мог корректно завершить все имеющиеся в его базе сессии данного сервера доступа.

Для обработки таких пакетов в конфигурации плагина задаётся набор атрибутов сессий, которые должны быть завершены при обработке данного on/off-пакета. В ключе plugins → base → <plugin_instance_name> → actions → accounting → on_off_attributes указывается список пар key:value, где key — наименование атрибута сессии, а value — значение данного атрибута.

При настройке обработки on/off-пакетов необходимо убедиться, что сохранение указанных в ключе on_off_attributes атрибутов сессий настроено в блоке plugins → base → <plugin_instance_name> → actions → accounting → session → templates → attributes.

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

Пример настройки обработки on/off-пакетов — сессии для завершения подбираются по совпадению значения NAS-IP-Address в сессии и в on/off-пакете
plugins:
  base:
    ran-by-login:
      actions:
        authorize:
...
        authenticate:
...
        accounting:
          session:
            templates:
              - attributes:
                  NAS-IP-Address: $request.RAD_REQUEST.NAS-IP-Address
                  User-Name: $request.RAD_REQUEST.User-Name

          on_off_attributes:
            NAS-IP-Address: $request.RAD_REQUEST.NAS-IP-Address

Хуки

Для каждого действия в ключе plugins → base → <plugin_instance_name> → actions → <action_name> → hooks можно указать список хуков, которые будут выполнены ядром плагина по окончании обработки запроса.

Элементами списка являются строки формата <hook_type>/<hook_instance_name>, где <hook_type> — тип хука, а <hook_instance_name> — наименование экземпляра хука.

Настройка самих хуков описана в отдельной статье: Хуки.

Загрузка сведений о сессиях в БД Гидры

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

/etc/cron.d/hard
# HARD RADIUS accounting loader
*/3 *   * * *   hard    /opt/hydra/hard/init/hard.sh -f /etc/hydra/hard batch &>/dev/null

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

Записи о сессиях для загрузки выбираются из коллекции sessions базы данных кэша — загружаются только те записи, которые были обновлены с момента последней загрузки. Для каждой записи о сессии при загрузке в БД Гидры подбирается Тип сессии. Выбор типа выполняется по соответствию шаблонов абонентского и операторского профилей, указанных в типе сессии, профилям оборудования, которые были подобраны ядром плагина при обработке первого пакета аккаунтинга по данной сессии. Если при обработке аккаунтинга по сессии использовался шаблон с указанным в ключе service_profiletemplate_id идентификатором дочернего шаблона абонентских профилей, то тип сессии подбирается с использованием этого шаблона вместо привязанного абонентского профиля.

Записи о сессия загружаются в таблицу EP_SESSIONS основной базы данных Гидры. Сведения о потреблённых услугах, которые необходимо протарифицировать, загружаются в таблицу EX_DATA_COLLECT, записи из которой обрабатываются заданием «Обработка данных RADIUS-аккаунтинга сессий в провижининге».

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

Закрытие «зависших» сессий по таймауту

Если с момента последнего обновления записи о сессии прошло больше часов, чем указано в ключе plugins → base → <plugin_instance_name> → actions → accounting → session → lookup → hours конфигурации плагина, сессия переводится в состояние Завершена по таймауту, в качестве даты и времени завершения такой сессии проставляются текущие дата и время. Завернные по таймауту сессии не учитываются при проверке во время аутентификации ограничения на количество одновременных сессий. Если по такой сессии, пока она не заархивирована, придёт пакет аккаунтинга, она будет восстановлена и обновлена в соответствии с данными этого пакета.

Наличие сессий в состоянии Завершена по таймауту является симптомом проблем RADIUS-взаимодействия между сервером доступа и RADIUS-сервером. Наиболее частые причины появления таких сессий:

  • Отключение или перезагрузка сервера доступа, при которой он не отправляет на RADIUS-сервер пакет Accounting-On или Accounting-Off. Решение — настроить сервер доступа на отправку таких пакетов.
  • Некорректно заданное в конфигурации агента значение таймаута завершения сессий. Рекомендуется в качестве значения указывать 1.5-2.5 используемого интервала аккаунтинга (атрибут Acct-Interim-Interval), для того чтобы даже потеря одного пакета аккаунтинга не приводила к завершению сессии по таймауту. Если же таймаут окажется меньше интервала аккаунтинга, то большинство все сессий будет постоянно завершаться по таймауту.
  • Различные сетевые проблемы, из-за которых часть пакетов аккаунтинга не доходят до RADIUS-сервера. Решение зависит от характера проблемы, для локализации которой, обычно, анализируются логи RADIUS-сервера, сервера доступа и снимаемые между этими серверами дампы RADIUS-взаимодействия.

Обработка пакетов Accounting-On и Accounting-Off

Записи о пакетах данного типа выбираются из коллекции accounting базы данных кэша. Для каждой записи выполняется перевод в состояние Завершена всех сессий, удовлетворяющих следующим условиям:

  • Сессия активна, то есть находится в состоянии Начата или Обновлена.
  • Время последнего пакета аккаунтинга по сессии меньше времени on/off-пакета.
  • Атрибуты on/off-пакета, сохранённые при его получении, имеются среди атрибутов сессии, причём значения соответствующих атрибутов совпадают.

Архивация старых сессий

Если с момента последнего обновления сессии прошло больше дней, чем указано в ключе plugins → base → <plugin_instance_name> → actions → accounting → session → lookup → timed_out_days, то такая сессия архивируется. Архивные сессии игнорируются при обработке пакетов аккаунтинга и, в частности, не могут быть восстановлены из состояния Завершена по таймауту.

Очистка кэша от неактуальных записей

Во избежание неконтролируемого роста базы данных кэша из неё удаляются устаревшие данные, которые более не будут использоваться агентом:

  • Неактивные профили оборудования и привязки абонентского оборудования к операторскому.
  • Архивные сессии, которые были загружены в основную базу данных Гидры.
  • Обработанные записи о пакетах Accounting-On и Accounting-Off.
  • Сеансы загрузки сессий и услуг в основную базу данных Гидры.
  • No labels