Тарификация услуг по протоколу radius

advertisement
Россия, г. Астрахань
Июль 2008 г.
АСР «Дельта» версия 1.0
Описание алгоритмов программы.
Данное руководство содержит описание алгоритмов авторизации и
расчета стоимости услуг, предоставляемых абонентам с помощью серверов
доступа (например, Cisco AS 5350 или аналогичных). Разграничение доступа и
сбор тарификационных данных осуществляется посредством протокола Radius.
Управление состоянием сессии (терминирование сессии) осуществляется по
протоколу SNMP.
Услуга коммутируемого доступа предоставляется абонентам –
пользователям телефонной сети общего пользования. С помощью модема
абонент набирает номер сервера доступа (модемного пула) и подключается к
сети Интернет. С точки зрения телефонной станции, данное соединение
является обычным телефонным соединением (разговором) абонентов, однако
вместо голосовой информации, канал используется для передачи данных.
Данная технология, в настоящее время является устаревшей т.к. максимальная
полоса канала, образованного таким способом, в самых лучших условиях, не
может превышать 56 кбит/сек. В реальности, фактическая полоса канала в 2
раза уже т.к. на существующих телефонных сетях по прежнему, используются
АТС старого типа – координатной и/или декадно-шаговой системы, которые в
процессе соединения создают значительные импульсные помехи. В России,
данная технология доступа к сети Интернет все еще активно используется
операторами связи. В обращении находятся карты доступа, содержащие
реквизиты для подключения к сети Интернет (номер телефона доступа, имя
пользователя, пароль). Также, данная услуга предоставляется некоторым
абонентам на основании договора.
Услуга передачи речевой информации (IP телефония), также
предоставляется абонентам – пользователям телефонной сети общего
пользования. В отличие от коммутируемого доступа, абонентам
предоставляется возможность осуществлять голосовые междугородные и
международные вызовы через операторов IP телефонии – таких как, «ЗебраТелеком», «Гамбург-Телеком», «Комстар» и пр. В отличии от обычных
операторов междугородней и международной связи, таких как «Ростелеком», у
операторов IP телефонии, обычно, нет своих выделенных междугородних
каналов связи или сети передачи данных. В качестве среды передачи голосовой
информации используется сеть Интернет. Благодаря тому, что операторы IP
телефонии имеются практически во всех странах мара, любой вызов будет
обслужен – всегда найдется оператор, готовый «приземлить» вызов. Благодаря
использованию сети Интернет и отсутствию необходимости использования
дорогостоящих междугородних каналов связи, стоимость таких вызовов может
быть в несколько раз меньше, чем у классических операторов междугородней
связи, что делает данную услугу весьма привлекательной для абонентов.
Расчет стоимости вызовов через IP телефонию, также, отличается
способом тарификации от обычных вызовов, набранных через «восьмерку» –
для вызовов через IP обычно применяется посекундная тарификация, выгодная
для абонентов. Обычные вызовы через операторов МГ/МН связи
тарифицируются поминутно, с округлением до целых минут в большую сторону
– фактически, абонент почти всегда переплачивает за лишнюю минуту
разговора.
Для доступа к серверу IP телефонии, абонент набирает номер телефона
сервера доступа. После установки соединения, абонент следует инструкциям
голосового информатора – вводит PIN код карты (или номер своего лицевого
счета), а затем, набирает номер телефона вызываемого абонента.
В случае, если абонент имеет договор с оператором на предоставление
услуг передачи речевой информации, после набора номера сервера доступа
абонент слышит непрерывный гудок (ответ станции) – что означает готовность
оборудования к приему номера вызываемого абонента. Авторизация, в данном
случае, выполняется по номеру телефона вызывающего абонента.
Услуга
«приземление
трафика»
или
«завершение
вызова»
предоставляется присоединенным операторам IP телефонии. Суть данной
услуги – маршрутизация вызовов в телефонную сеть общего пользования.
Например, оператор «Зебра-Телеком», находящийся в городе Москве,
маршрутизирует вызовы в Астраханскую область на оператора «АстраханьТелеком». Оператор «Астрахань-Телеком» имеет точки присоединения с
другими операторами фиксированной и мобильной телефонной связи
Астраханской области, что позволяет оператору «Астрахань-Телеком»
обслуживать принятые вызовы.
«Дельта» поддерживает тарификацию всех вышеназванных услуг связи,
причем, для небольшого регионального оператора связи с небольшим
количеством абонентов, возможно использование одного сервера доступа для
всей группы услуг.
Рассмотрение процесса предоставления услуг и их тарификации
целесообразно начать с сервера доступа – например Cisco 5350. Сервер доступа
является многофункциональным программируемым устройством, имеющим
несколько интерфейсов для подключения к сети передачи данных по протоколу
TCP/IP, и к телефонной сети общего пользования по протоколу ISDN. Для
подключения к сети передачи данных используется адаптер Ethernet, а к
телефонной сети – платы потоков PRI E1. Взаимодействие с биллинговой
системой осуществляется по протоколам Radius и SNMP.
Доступ к услугам может осуществляться:
-
По договору с абонентом;
По карте доступа;
Информация о договорных аккаунтах и картах доступа содержится в
таблице «cards», которая находится в базе данных «MAIN». Эта таблица
содержит исчерпывающую информацию по каждой учетной записи: имя
пользователя, пароль, тип учетной записи, тариф на время работы в сети, тариф
на трафик, тариф на IP телефонию и пр. информация. Полная информация о
полях этой приводится в следующей таблице и комментариях:
Имя поля
serial
uid
pin
card_type
Тип данных
Int
Nvarchar (25)
Nvarchar (25)
Int
Sn
Sk
begin_date
Activate_date
end_date
price1
price2
price3
ab_code
Overdraft
float (8)
float (8)
Datetime
Datetime
Datetime
Int
Int
Int
Int
Int
Disabled
on_delta
Int
Int
Назначение поля
Серийный номер карты или аккаунта (ключ)
Имя пользователя
Пароль
Тип учетной записи: 1-карта экспресс оплаты, 2карта доступа, 3-карта универсальная, 4договорной аккаунт;
Начальная сумма карты
Фактический остаток карты
Дата создания карты или аккаунта
Дата первой активации карты или аккаунта
Дата завершения карты или аккаунта
Тариф на время работы в сети (коммут. доступ)
Тариф на трафик (коммут. доступ)
Тариф на IP телефонию
Код абонента, на которого строить начисления
-1 может пользоваться в кредит, 0 – нет (для
карт)
-1 заблокирован, 0 – нет (для карт – окончена)
-1 создан в «Дельте», 0 – перенесен (тех. поле)
Поле «serial» – уникальный серийный номер карты, присваиваемый карте
при генерации. Этот номер, также, печатается на самой карте при ее
изготовлении в типографии. По данному полю создан первичный ключ на
таблицу т.к. серийный номер карты всегда уникальный. Для договорных
аккаунтов выделяется некоторый диапазон серийных номеров, которые выдает
администратор на этапе установке системы. В нашем случае, разделение
серийных номеров следующее:
Номер от
1
1000
1000000
Номер до
1000
99999
-
Назначение
Аккаунты коммутируемого доступа
Аккаунты передачи речевой информации
Карты всех типов
Для аккаунтов серийные номера не имеют смысла, однако, в любом случае – это
поле обязательное.
Поле «uid» содержит имя пользователя. Допускаются любые символы,
пригодные для имени пользователя, которые воспринимает сервер доступа.
Однако мы рекомендуем использовать только латинские буквы a-z и цифры 0-9,
а также символ нижнего подчеркивания. Такие символы будут гарантированно
восприняты сервером доступа и легко запоминаются абонентом. Имя
пользователя имеет прямой смысл только для услуги коммутируемого доступа
по картам и договорам, а также – для карт экспресс оплаты услуг. Для передачи
речевой информации имя пользователя является синтетическим т.к. никак не
связано с тем, что приходит с сервера доступа в соответствующем Radius
атрибуте. Данное поле – индексированное. Оно не может быть пустым.
Важная особенность данного поля состоит в том, что в журнал истории
соединений (динамический массив accounting) в поле «username» пишется
значение этого поля т.е. связь между аккаунтом и историей его соединений
осуществляется по значению данного поля. Также, в начислениях, в качестве
номера устройства, используется значение этого поля (только для договорных
аккаунтов).
Поле «pin» является PIN-кодом карты или паролем договорного
аккаунта. Это поле актуально для всех видов учетных записей Radius сервера.
Для коммутируемого доступа поле содержит пароль пользователя. Так-же, как
имя пользователя, в нем допускаются любые символы, которые могут
использоваться в качестве пароля. Для упрощения администрирования и
удобства абонентов, мы рекомендуем использовать только цифры 0-9 в
количестве 11-14 знаков, что вполне достаточно, для исключения возможности
перебора пароля злоумышленниками. Требует некоторого пояснения значение
этого поля для IP телефонии. Известно, что номер телефона абонента не может
содержать никакие другие символы, кроме цифр 0-9. Также, с помощью
наборного устройства в коде DTMF можно передать только цифры 0-9 и 2
управляющих символа: «#» и «*». Существуют DTMF клавиатуры, способные
выдавать частотные коды для некоторых букв, однако это скорее исключение,
чем правило. На основании вышесказанного можно сделать вывод, что для поля
«pin» учетной записи IP телефонии имеют смысл только цифры 0-9, никакие
другие символы не допускаются. Как отмечалось выше, доступ к серверу IP
телефонии осуществляется по паролю или по номеру телефона вызывающего
абонента, причем номера телефонов сервера доступа, для каждого способа
доступа используются разные. Соответственно, если в поле PIN задана
последовательность цифр «8512610002» содержащая менее 12 знаков – то это
номер телефона вызывающего абонента, а последовательность «783536578833»
содержащая 12 или более знаков (число знаков пароля задано константой) – это
пароль доступа. Поскольку пароль доступа, в нашем случае, не может быть
короче 12 знаков, а номер вызывающего абонента не может быть длиннее 10
знаков разделение типа доступа осуществляется именно по этому критерию.
Данное поле – индексированное. Оно не может быть пустым.
Поле «card_type» содержит тип учетной записи и может принимать
следующие значения:
Значение
1
2
3
4
Тип учетной записи
Карта экспресс-оплаты (для пополнения лицевого счета абонента)
Карта доступа (коммутируемый доступ или IP телефония)
Карта универсальная (экспресс-оплата + карта доступа)
Договорной аккаунт (коммутируемый доступ или IP телефония)
Никакие другие значения этого поля не допускаются, поле обязательное.
Основное назначение этого поля – исключение возможности использования
карт или аккаунтов не по назначению. Например, исключение возможности
пополнить лицевой счет с помощью аккаунта или карты доступа. Также, данное
поле используется для аналитики.
Поля «sn» и «sk» имеют смысл только для карт. Первое поле содержит
номинал (сумму) карты и в процессе «жизни» карты не изменяется. Второе поле
содержит фактический остаток счета карты. По мере использования карты
значение этого поля уменьшается. Для аккаунтов значение этих полей не имеет
смысла, а также, если поле «overdraft» имеет значение «–1», поле «sk» в
процессе тарификации не изменяется.
Следующие три поля «begin_date», «activate_date», «end_date» содержат
основные метки даты и времени процесса «жизни» карты или аккаунта.
Назначение этих полей понятно из их названия, поле «end_date» содержит дату
окончания срока действия карты т.е. невозможно использовать карту с
закончившимся сроком действия, даже если на счете карты есть средства.
Значения полей «price1», «price2», «price3» содержат тарифный план
карты или аккаунта. Поле «price1» и «price2» имеют смысл только для
коммутируемого доступа. Допускается одновременное использование обоих
полей – в этом случае с карты будут списываться средства за время работы в
сети и трафик т.н. «смешанный» режим тарификации. Также, возможно
указание только одного из полей – средства будут списываться за время работы
в сети или за трафик. Для аккаунтов, аналогично, будут строиться начисления.
Если оба поля не заданы воспользоваться услугой коммутируемого доступа по
данной учетной записи будет невозможно. Для безлимитного учета стоимости
данной услуги следует применять «нулевые» тарифные планы. Поле «price3»
содержит тариф на IP телефонию. Поскольку оператор сам вправе назначать
тарифы на данный вид услуг для каждой группы абонентов, возможно создание
нескольких профилей со стоимостью направлений для разных видов карт или
аккаунтов в т.ч. – «нулевые» профили, например для служебного пользования.
Для пояснения этого механизма посмотрите таблицу «price_mtr_voip» которая
содержит справочник стоимости направлений для IP телефонии.
Поле «ab_code» содержит код абонента, на которого следует строить
начисления. В случае договорного аккаунта, данное поле принимает значение
кода абонента – см. таблицу «abonents» которому принадлежит аккаунт. Для
карт, обычно заводится отдельный абонент служебного типа, на которого
строятся начисления от всех существующих карт. Начисления строятся по двум
оборотам (видам договоров):
-
«Интернет»
«Услуги передачи речевой информации»
Поскольку при тарификации известен тип сессии (см. об этом далее) программа
«знает» на какой из оборотов следует строить начисления. Для карт экспрессоплаты начисления от их активации не строятся т.к. при активации последних,
средства со счета карты, в полном объеме, поступают на счет абонента,
которые, в последствии, списываются в процессе потребления абонентом услуг
связи.
Поле «overdraft» предназначено для установки «кредитного» режима
тарификации услуг. Данное поле, для всех карт должно иметь значение «0» что
означает нормальную тарификацию услуг, списание средств со счета карты и
блокировка карты (отключение последней текущей сессии) при завершении
средств на счете карты. При значении поля «-1» включается режим, при
котором происходит только тарификация услуг, однако средства со счета
аккаунта не списываются, доступ к услугам не ограничивается. Данное поле
необходимо только для договорных аккаунтов т.к. у каждого договора есть свой
лицевой счет, списание средств с которого осуществляется при формировании
начислений. Внимание! Инициализация этого поля для карт создаст «вечную»
карту…
Поле «disabled» предназначено для принудительной блокировки карты
или аккаунта – например, для временного отключения за неуплату договорного
аккаунта. При завершении средств на счете карты, данное поле, автоматически
инициализируется значением «-1». Для прекращения действия (блокировки)
серии карт необходимо заполнить поле значением «-1». Значение «0» означает,
что карта или аккаунт является действующим.
Поле «on_delta» является служебным и используется при конвертации
базы данных (при миграции данных). Суть поля в том, что карты или аккаунты,
созданные в «Дельте» помечаются значением «1», а карты перенесенные из
внешнего источника данных значением «0». Это позволяет производить
миграцию данных сколько угодно раз (например для отладки программы
миграции), при этом карты и аккаунты, созданные в «Дельте» не удалятся. Для
всех других целей, данное поле не используется.
Для понимания процессов авторизации и тарификации рассмотрим
примеры практического использования карт и аккаунтов для доступа к услугам.
При коммутируемом доступе (по карте или по договору) абонент, с
помощью модема дозванивается до сервера доступа. В момент установки
соединения, от сервера доступа передается Radius пакет авторизации,
содержащий в себе следующие атрибуты:
-
IP адрес сервера доступа;
Номер телефона вызывающего абонента;
Имя пользователя;
Пароль пользователя;
В момент прихода Radius пакета, вызывается внешний обработчик,
находящийся в /etc/raddb/auth.sh на сервере расчетов – ниже приводится полный
листинг этого скрипта:
#!/bin/sh
# LOGFILE=/var/log/radacct/au.log
LOGFILE=/dev/null
echo "doing" >>"$LOGFILE"
ulimit -a >>"$LOGFILE"
ulimit -c 1000000
pwd >>"$LOGFILE"
env >>"$LOGFILE"
exec /etc/raddb/auth.tcl "$@" 2>> "$LOGFILE"
Последняя строчка вызывет прикладной обработчик, реализующий
бизнес-логику процесса авторизации для всех видов услуг, доступ к которым
обеспечивается с помощью Radius сервера.
Первая строчка (закоментированная) предназвачена для включения
режима отладки. В файл /var/log/radacct/au.log будет выводиться полное
содержимое всех Radius пакетов авториции, приходящее с серверов доступа. В
нормальном режиме работы эта функция должна быть выключена.
В зависимости от типа сессии, прикладной обработчик выполнят
определенную ветку алгоритма, которая, состоит из следующих блоков кода:
-
С помощью соответствующего SQL запроса проверяется подлинность
информации, переданной пользователем;
Возвращается код завершения «0», означающий разрешение доступа к
услуге или «–1», запрещающий доступ.
В случае разрешения доступа, возвращаются некоторые атрибуты Radius
серверу, которые, передаются в сервер доступа.
Этот процесс иллюстрирует фрагмент программы обработчика, отвечающий за
управление доступом к услуге коммутируемого доступа:
1
{ # dialup
set strSQL "SELECT C.uid, C.pin, C.sk, C.overdraft FROM cards C \
WHERE uid = '$session(User-Name)' AND pin='$session(User-Password)'
AND '$data' BETWEEN CONVERT(nvarchar, C.begin_date, 112) \
AND CONVERT(nvarchar, C.end_date, 112) AND disabled = 0 \
AND (C.price1 IS NOT NULL OR C.price2 IS NOT NULL) \
AND C.card_type > 1 "
set user_info [main $strSQL]
if {$user_info == ""} {
set strSQL "EXEC u_update_acc 1, '$session(User-Name)',
'$timestamp', 0, 0, 0, \
-1, '[pid]', 0, 0, 0, 0, \
'[getenv Calling-Station-Id]', '$session(User-Password)', \
'[getenv Client-IP-Address]', 'Username or Password is wrong.' \r "
set res [main $strSQL]
exit -1
}
if {[lindex [lindex $user_info 0] 3] == 0} {
if {[lindex [lindex $user_info 0] 2] < $sm } {
set strSQL "EXEC u_update_acc 1, '$session(User-Name)',
'$timestamp', 0, 0, 0, \
-1, '[pid]', 0, 0, 0, 0, \
'[getenv Calling-Station-Id]', '$session(User-Password)', \
'[getenv Client-IP-Address]', 'Account: [format "%.0f" [lindex
[lindex $user_info 0] 2]] rub. Access deny.' \r "
set res [main $strSQL]
exit -1
}
}
puts "Connect-Info = connection"
puts "Service-Type = Framed-User"
puts "Framed-Protocol = PPP"
puts "Framed-Compression = Van-Jacobson-TCP-IP"
puts "Ascend-Client-Primary-DNS = $dns1"
puts "Ascend-Client-Secondary-DNS = $dns2"
puts "Class=1"
}
}
}
Этот блок кода – самый простой в обработчике т.к. услуга
коммутируемого доступа, сама по себе, простая, по сравнению с другими
услугами, доступ к которым разграничивается с помощью Radius сервера.
Первый SQL запрос проверяет подлинность абонента. Далее,
проверяется остаток лицевого счета карты (если снят флаг «overdraft») и если он
положительный (больше значения, заданного константой $sm) абонента
пускают в сеть. В конце блока кода – набор дополнительных атрибутов,
передаваемых в сервер доступа для дальнейшей установки соединения.
Вызовы хранимой процедуры «u_update_acc» необходимы для
фиксирования в динамическом массиве «accounting» таких ошибок, как
неправильный пароль или недостаток средств на счете. Фиксация таких ошибок
необходима для работы службы технической поддержки. Данная хранимая
процедура предназначена для добавления и обновления записи в динамическом
массива «accounting». Подробное описание этой процедуры и динамического
массива следует далее.
Команда «exit –1» приводит к немедленному отключению абонента от
сервера доступа, если доступ к услуге запрещен.
Алгоритм управления доступом к услугам передачи речевой информации
значительно сложнее. Как отмечалось выше, существует два режима доступа к
серверу IP телефонии:
-
По номеру телефона вызывающего абонента;
-
По паролю (PIN коду карты);
Первый способ доступа используется только для договорных абонентов –
данный способ, более удобен, чем ввод пароля. Возможность доступа по номеру
вызывающего абонента особенно полезна юридическим лицам: многие
организации используют мини АТС. Если запрограммировать станцию на
подмену набора первой цифры номера «8» набором номера сервера доступа, то
пользователь внутренней сети, набирая цифру «8» услышит непрерывный
гудок, выдаваемый сервером доступа, означающий готовность станции к
приему номера вызываемого абонента. Таким образом, использование IP
телефонии не будет усложнять набор междугородних и международных
номеров. При должной полосе и качестве каналов связи, абонент не заметит
разницы между вызовами через обычного оператора и IP телефонию.
Способ доступа по паролю хорош тем, что абонент может попользоваться
услугой с любого телефона, аппарат которого оснащен тональным
номеронабирателем. После набора номера сервера доступа (он должен
отличаться от того, который используется для первого способа доступа) абонент
слышит приглашение: «наберите номер Вашего счета и нажмите клавишу #».
При использовании карты, после ввода номера счета, если номер счета
правильный, абонент слышит: «на Вашем счету – 100 рублей. Наберите номер,
по которому Вы хотите позвонить». При этом, сумма остатка счета – значение
поля «sk» таблицы «cards».
Если у абонента договор с оператором (значение поля «overdraft» = -1)
абонент слышит фразу: «наберите номер, по которому Вы хотите позвонить».
Состояния счета, в этом случае, абоненту не проговаривается.
Абонент не может получить доступ к услугам IP телефонии если:
-
Номер телефона А или пароль не опознаны;
Значение поля «disabled» = -1;
Значение поля «end_date» меньше даты осуществления вызова;
Значение поля «sk» <= значения константы $sm и поле «overdraft» = 0;
Поле «price3» не инициализировано;
Поле «card_type» содержит значение «1».
Во всех остальных случаях, доступ к услуге предоставляется. Обработчик
Radius пакетов авторизации находится в том-же файле, где размещается
обработчик коммутируемого доступа: /etc/raddb/auth.tcl.
Следует особо рассмотреть ситуацию, после того, как абонент
заканчивает набор номера «Б» и нажимает клавишу «#» - что воспринимается
сервером доступа как команда начала установки соединения.
В отличии от коммутируемого доступа, звонок по IP телефонии, в
случае установки соединения с вызываемым абоентом, «порождает» на сервер
доступа 2 отдельные сессии:
1. Сессия, в процессе «жизни» которой установлено соединение
вызывающего абонента до сервера доступа т.н. «входящая» сессия;
2. Сессия, которая создается после набора вызываемого номера до того
момента, как абонент «А» или «Б» положит трубку. Эту сессию,
можно назвать «исходящая».
С точки зрения тарификации, интересна только вторая сессия т.к.
абонент должен оплачивать только соединение т.е. непосредственно сам
разговор. Разговором можно (и нужно) считать время, которое начинается от
получения линейного сигнала «Ответ» до завершения соединения, т.е. когда
абонент «А» или «Б» положат трубку. Именно по этой причине, в базу данных
записываются только исходящие сессии, это полезно и с точки зрения
оптимизации размера базы данных. Информацию о входящих сессиях можно
получить из тарификационных записей станции, к которой подключен сервер
доступа или вывести в txt файл в процессе отладки системы.
После завершения набора номера «Б», перед началом установки
соединения исходящий сессии, от сервера доступа приходит еще один Radius
пакет авторизации. Логика анализа этого пакета несколько отличается от
анализа первого авторизационного пакета. Если абонент имеет договор на
предоставление услуг, в ответ на этот пакет, в сервер доступа всегда
возвращается код завершения «0», означающий разрешение на установку
соединения. Если абонент пользуется услугой по карте доступа, вычисляется
максимальное время разговора для набранного абонентом вызываемого номера
(на этот момент он уже известен). Вот так это происходит:
if {$overdraft_possible == 0 && "[get-Called-Station-Id]" ne ""}
set strSQL "SELECT TOP 1 price FROM price_mtr_voip \
WHERE '[get-Called-Station-Id]' LIKE seq + '%' \
AND unit_id = [lindex [lindex $user_info 0] 4] \
ORDER BY LEN(seq) DESC "
set dest_info [main $strSQL]
if {$dest_info ne ""} {
set price [expr [lindex [lindex $dest_info 0] 0] / 60.0]
} else {
set price 0
}
if {[expr $account / $price ] < 10} {set price 0}
puts "Cisco-VoIP-Credit-Time =\"h323-credit-time=\
[format "%.0f" [expr $account / $price - 6 ]]\""
}
if {$account < 2 && $account > [expr $sm - 0.01]
&& $overdraft_possible == 0} {set account 2}
if {$account < $sm && $overdraft_possible == 0} {set account 0}
puts "Class=$session(User-Password)"
puts "Connect-Info = connection"
puts "Cisco-VoIP-Billing-Model = \"h323-billing-model= \
[expr $overdraft_possible ? 0 : 1]\""
puts "Cisco-VoIP-Credit-Amount = \
"h323-credit-amount=$account\""
} else {
exit –1
}
В начале блока кода содержится условие, выделящее абонентов,
пользующихся услугой по карте. Также проверяется наличие информации о
номере «Б». Далее, «зная» номер «Б» выполняется получение стоимости
направления для профиля данного аккаунта или карты. На основании остатка
счета и стоимости 1 минуты разговора по данному направлению, вычисляется
время в секундах, которое абонент может разговаривать без риска уйти в
«минус». В конце блока кода следуют инструкции, передающие необходимые
атрибуты в сервер доступа. После передачи этих атрибутов, начинается процесс
дальнейшей установки соединения.
Авторизация доступа к услуге «приземления» трафика основана
на проверка IP адреса вызывающей стороны (это может быть присоединенный
оператор или VoIP шлюз). На сервере доступа имеется следующий диалпир:
dial-peer voice 100 voip
permission orig
huntstop
application acceptzebra
incoming called-number .T
voice-class codec 1
voice-class h323 1
dtmf-relay rtp-nte h245-signal h245-alphanumeric
no call fallback
fax protocol t38 ls-redundancy 5 hs-redundancy 0 fallback none
no vad
Строка 4 вызывает внешнее приложение, ссылка на которое прописана в
конфигурации сервера доступа следующим образом:
call application voice acceptzebra tftp://192.168.0.57/scripts/accept_zebra.tcl
Приложение загружается в память сервера доступа с TFTP сервера при
первом его вызове. Данный файл, содержит блок кода, в котором содержатся IP
адреса хостов, с которых разрешено принимать вызовы. В данном файле, также,
содержится локальный план нумерации, используемый в данном регионе,
который анализируется при всех входящих вызовах для предотвращение
попыток маршрутизации вызовов на другие направления, например
междугородние. Таким образом, могут быть обслужены только вызовы,
удовлетворяющие следующим условиям:
-
IP адрес хоста является допустимым;
Номер телефона «Б» принадлежит к плану нумерации данного региона;
Эти простые условия отсекают весь возможный «фрод», который, к сожалению,
в подобных сетях не редкость. Следует заметить, что для подобных вызовов
авторизационные Radius-пакеты не формируются и соответсвенно, не
обрабатываются Radius сервером.
Процесс тарификации всех услуг, предоставляемых сервером доступа,
также, реализован с помощью протокола Radius. Тарификационные данные
поступают в accounting пакетах, которые формируются сервером доступа для
каждой активной сессии. Accounting пакеты бывают следующих типов:
Тип пакета
Start
Alive
Stop
Описание
Стартовый пакет – передается при старте новой сессии
Сессия активна – передается периодически во время жизни сессии
Стоповый пакет – передается в момент завершения сессии
Все пакеты содержат набор атрибутов, которые содержат информацию о
параметрах сессии. В зависимости от вида услуги (IP телефония или
коммутируемый доступ) набор атрибутов и их значения различаются. В
обработчике различные блоки кода с совершенно разной логикой отвечают за
анализ этих пакетов.
В момент получения accounting пакета, Radius вызывает внешний
обработчик пакета. Скрипт, /etc/raddb/acct.sh, листинг которого приводится
ниже, в свою очередь, вызывает прикладной обработчик, который содержит
логику обработки каждого пакета.
#!/bin/sh
# LOGFILE=/var/log/radacct/al.log
LOGFILE=/dev/null
echo "doing" >>"$LOGFILE"
ulimit -a >>"$LOGFILE"
ulimit -c 1000000
pwd >>"$LOGFILE"
env >>"$LOGFILE"
exec /etc/raddb/acct.tcl "$@" 2>>"$LOGFILE"
echo "Done $?" >>"$LOGFILE"
Аналогично обработке пакетов авторизации, скрипт предусматривает
вывод содержимого пакетов в текстовый log файл (строка 1) и запуск
программы обработчика (предпоследняя строка).
Результат обработки accounting пакетов (результат их тарификации)
записывается в базу данных. По причине возможного большого объема
тарификационных данных, информация в базе данных содержится в
динамическом массиве – наборе таблиц с одинаковой структурой, где каждая
таблица соответствует суточному временному интервалу. Каждой сессии,
параметры которой имеют смысл для тарификации, соответствует 1 запись.
Также, возможные ошибки при авторизации и тарификации фиксируются по
мере их возникновения. Для пояснения приводится структура таблиц массива:
Формат имени таблицы aYYYYMMDD, таблицы размещаются в базе
данных INET. Например, таблица INET..a20090501 содержит набор
тарификационных данных за 01.05.2009 года.
Имя поля
S_id
Username
Data
Ts
Tm
From_bytes
To_bytes
Session_type
Session_id
Price1
Price2
Summa1
Summa2
Moved
Requestor_info *
Service_info *
Service_descr *
Provider_info *
Disconnect_reason
Тип данных
Int
Nvarchar (25)
Datetime
Int
Int
Bigint
Bigint
Int
Nvarchar (30)
Int
Int
Float
Float
Int
Nvarchar (60)
Nvarchar (50)
Nvarchar (50)
Nvarchar (50)
Nvarchar (50)
Назначение поля
Счетчик, ключевое поле
Имя пользователя
Дата и время начала сессии
Время сессии в секундах
Время сессии в минутах (окр. целых мин)
Получено абонентом байтов
Отправлено абонентов байтов
Тип сессии
ID сессии
Код тарифа на время
Код тарифа на трафик
Сумма времени сессии
Сумма трафика сессии
Сессия вошла в начисления
Информация о вызывающем абоненте
Информация об услуге
Дополнительная информация об услуге
Информация провайдера
Причина завершения сессии
Поля, отмеченные символом «*» для разных типов сессий принимают
определенные значения, варианты которых описаны ниже.
Поле «s_id» является счетчиком – при добавлении новой записи,
значение счетчика увеличивается на 1. Это поле – первичный ключ таблицы.
Поле «username» содержит имя пользователя, открывшего сессию. Значение
этого поля формируется при старте сессии, далее – не изменяется.
Поле «data» содержит дату и время начала сессии. Значение этого поля
формируется при старте сессии, далее – не изменяется. Поле обязательное.
Поле «ts» содержит текущую длительность сессии в секундах. Значение
этого поля обновляется в процессе жизни сессии – по получению accounting
пакетов.
Поле «tm» аналогично полю «ts», но содержит количество минут,
округленное в большую сторону до полной минуты (аналогично тому, как
тарифицируется межгород).
Поля «from_bytes» и «to_bytes» содержат данные о трафике сессии.
Значение этого поля обновляется по получению accounting пакетов.
Поле «session_type» содержит тип сессии. Поле обязательное.
Допускаются значения только указанные в таблице:
Значение
1
2
3
4
5
6
7
-1
Краткое описание типа сессии
Коммутируемый доступ карточный и договорный.
IP телефония по АОНу договорной.
IP телефония по паролю карточный и договорной.
PPPoe договорной.
Активация карты экспресс-оплаты (PPPoe).
Смена пользователем тарифного плана (PPPoe).
IP телефония услуга «приземление трафика».
Ошибки.
Сессии с кодами 1, 2, 3, 4, 7 являются тарифицируемыми. Сессии с кодами 5 и 6
содержат информацию о событиях. Код –1 зарезервирован для различных
ошибок, возникающих в процессе авторизации и тарификации.
Поле «session_id» содержит уникальный ID сессии (для тарифицируемых
типов сессий с кодами 1, 2, 3, 4, 7). Формат поля следующий:
X-AABBCCDD или X-AABBCCDDEEFFGGHH
Где X – условный номер сервера доступа, который сопоставляется с IP адресом
сервера доступа. Последовательность символов после дефиса – ID сессии в том
виде, в каком его выдает сервер доступа. Значение может быть 32х или 64х
битным, например Cisco 7600 отдает 64 битный ID сесии, а Cisco 5350 – 32
битный. Для сессии с кодом 5 поле содержит серийный номер карты экспресс –
оплаты, для сессии с кодом 6 – ID записи текущего тарифного плана из таблицы
«inet_plans».
Поля «price1» и «price2» содержат коды тарифов на время и трафик,
согласно которым тарифицировалась данная сессия. Значение первого поля
имеет смысл для всех видов сессий (кроме 5 и 6), значение второго поля –
только для сессий вида 1.
Поля «summa1» и «summa2» (сумма значений этих полей) содержат
итоговую сумму сессии, которая складывается из суммы времени и суммы
трафика для данной сессии. Для IP телефонии имеет смысл только поле
«summa1». Для коммутируемого доступа – оба поля, в зависимости от настроек
тарифного плана аккаунта.
Поле «moved» содержит признак того, что сессия вошла в начисления.
Если сессия не вошла в начисления, значение поля «0», иначе «1». Имеет смысл
только для сессий, общая сумма которых не равна 0.
Поле «Requestor_info», в зависимости от типа сессии, может принимать
следующие значения:
Тип сессии
1
2, 3
4
5
6
7
Значение поля Requestor_info
Номер телефона вызывающего абонента
Номер телефона вызывающего абонента
Класс доступа, полоса канала, MAC адрес абонента
Имя пользователя карты экспресс-оплаты
Код старого тарифного плана, код нового тарифного плана.
Номер телефона вызывающего абонента
Поле «Service_info», в зависимости от типа сессии, может принимать
следующие значения:
Тип сессии
1
2, 3
4
5
6
7
Значение поля Service_info
Строка информации (полоса канала, протокол, компрессия)
Номер телефона вызываемого абонента
IP адрес абонента
PIN код карты экспресс-оплаты
Суточная абонплата по новому тарифному плану
Номер телефона вызываемого абонента
Поле «Service_desc», в зависимости от типа сессии, может принимать
следующие значения:
Тип сессии Значение поля Service_desc
2, 3
Название направления (города) вызываемого абонента.
Для остальных видов сессий значение поля не определено.
Поле «Provider_info», в зависимости от типа сессии, может принимать
следующие значения:
Тип сессии
1
2, 3
4
5
6
7
Значение поля Provider_info
IP адрес абонента
Направление, куда был маршрутизирован вызов
IP адрес сервера доступа
Код договора, на который был зачислен платеж
Остаток счета абонента на момент смены тарифного плана
Направление, откуда был принят вызов (IP адрес узла)
Для сессий типа 2, 3 и 7 значение поля может быть примерно следующим:
Значение поля
H323-remote-address=213.145.43.43
H323-remote-address=195.128.80.221
Serial3/0:31
Описание
IP адрес сервера IP телефонии 1
IP адрес сервера IP телефонии 2
Номер слота, порта, канала
Таким образом, для IP телефонии, можно проследить, куда был
маршрутизирован каждый вызов, что позволяет выполнить проверку встречных
счетов от присоединенных операторов IP телефонии за услуги пропуска
трафика. Последняя запись в таблице требует некоторых пояснений. Сервер
доступа может быть запрограммирован так, что отдельные направления, могут
маршрутизироваться через обычных операторов междугородней связи. Это
необходимо в случае, если присоединенный оператор IP телефонии не имеет
возможности обслуживать вызовы по некоторым направления. Данная ситуация
типична для некоторых стран СНГ и ближнего зарубежья. Такие вызовы
приходится «заворачивать» обратно, в сторону телефонной сети общего
пользования, для последующей маршрутизации их междугородним операторам.
Содержимое поля, в этом случае, идентифицирует канал на сервере доступа,
куда ушел конкретный вызов.
Поле «disconnect_reason» содержит причину завершения сессии или
содержит значение NULL если сессия активна. Поскольку причины могут быть
разными, полезно их фиксировать в базе данных, например для отладки
прохождения соединений. Также, это поле содержит остаток на счете карты в
момент отключения сессии. Далее приводятся различные варианты значений
этого поля:
Значение поля
disc-cause-ext=PPP Receive Term
Описание
Получен сигнал завершения сессии со
стороны абонента (PPPoe)
disc-cause-ext=Local Admin Disc
Сессия завершена по команде со
стороны сервера доступа (PPPoe)
disc-cause-ext=TCP Foreign Host В
процессе
сессии
произошла
Close
неожиданная потеря связи (PPPoe)
disc-cause-ext=Call Disconnect
Команда отключения соединения со
стороны абонента (PPPoe)
User-Request
Сессия прервана пользователем (PPP)
Lost-Carrier
Неожиданная потеря связи (PPP)
Admin-Reset
Сессия прервана сервером доступа (PPP)
h323-disconnect-cause=10
Вызов завершен с кодом XX (VoIP)
Таким образом, по информации о завершении сессии можно узнать
причину. Для IP телефонии нужно анализировать значение кода disconnect-causе
Запись (добавление, обновление) информации в динамическом массиве
accounting осуществляется только с помощью хранимой процедуры
«u_update_acc». Процедура имеет несколько функций, которые выполняются в
зависимости от параметров, передаваемых в хранимую процедуру при ее
вызове. Далее приводится полное описание этой процедуры – это важно для
понимания процессов, происходящих при тарификации услуг. Процедура
возвращает число обработанных записей. В нормальном случае должно всегда
быть «1», что означает, что запись о сессии была успешно добавлена или
обновлена. Любое другое значение можно считать ошибкой, например возврат
значения «0» будет означать, что сессия не найдена. Реакция на ошибку
выполнения этой процедуры всегда приводит к немедленному завершению
сессии, вызвавшей ошибку, путем запуска соответствующей программы.
Параметр
@mode
Тип данных
Int
@username
@data
@ts
Nvarchar (25)
Datetime
Int
@from_bytes
@to_bytes
@session_type
@session_id
@price1
@price2
@sk
Bigint
Bigint
Int
Nvarchar (30)
Int
Int
Int
@overdraft
@requestor_info
@service_info
@provider_info
@disconnect_rea
son
Int
Nvarchar (60)
Nvarchar (50)
Nvarchar (50)
Nvarchar (50)
Описание
Режим вызова (1 – добавление новой записи
в массив, 2 – обновление существующей
записи).
Имя пользователя.
Дата и время начала сессии.
Длительность сессии, сек, или –1 если не
известно.
Трафик к абоненту, байт.
Трафик от абонента, байт.
Тип сессии (см. описание типов сессий).
ID сессии.
Код тарифа на время.
Код тарифа на трафик.
Текущий остаток на счете карты или
абонента
Разрешено пользоваться к кредит, 0 – нет.
Информация о вызывающем абоненте.
Информация об услуге.
Информация провайдера.
Причина завершения сессии.
Параметр @mode определяет режим работы процедуры. При получении
стартового accounting пакета, процедура добавляет в динамический массив
запись о новой сессии. При получении accounting пакетов «Alive» и «Stop»
процедура обновляет запись. В зависимости от типа сессии, происходит
тарификация сессии, согласно тарифам, установленным, для аккаунта. Этот
параметр обязательный.
Параметр @username содержит имя пользователя. Передается во всех
режимах вызова процедуры. Параметр обязательный.
Параметр @data содержит дату и время начала сессии. Имеет смысл
только при старте сессии. При обновлении информации о сессии, параметр
содержит дату и время получения accounting пакета.
Параметр @ts содержит длительность сессии в секундах. В случае, если
значение параметра «-1» фактическая длительность сессии вычисляется в
процедуре.
Параметры @from_bytes и @to_bytes содержат трафик сессии, в байтах.
Имеют смысл для сессий типа 1 и 4.
Параметр @sk содержит остаток счета карты и используется для
тарификации. Имеет смысл для коммутируемого доступа и IP телефонии. По
исчерпанию средств на счете карте выполняется блокировка карты, заносится
информация о дате завершения карты. Также, при выходе из процедуры,
возвращается значение «2», которое означает, что средства на карте
закончились и сессию нужно немедленно отключить.
Параметр @overdraft разрешает или запрещает пользоваться абоненту
услугой в кредит. При работе в кредит, услуга тарифицируется, но остаток счета
не анализируется, соответственно, сессия тоже не разрывается.
Значение всех остальных параметров аналогично значению полей таблиц
динамического массива, которые описывались ранее.
Рассмотрим примеры обработки accounting пакетов и тарификации услуг.
При коммутируемом доступе, в момент получения стартового пакета, создается
запись о новой сессии. На основании имени пользователя (оно содержится в
accounting пакете) из базы данных извлекается информация по данному
аккаунту следующим SQL запросом:
1
{ # Dialup PPP
set strSQL "SELECT C.uid, C.sk, C.overdraft, \
ISNULL(C.price1, 0) as price1, ISNULL(C.price2, 0) as price2, \
C.disabled FROM cards C \
WHERE uid = '$session(User-Name)' \
AND '$data' BETWEEN C.begin_date AND C.end_date \
AND (C.price1 IS NOT NULL OR C.price2 IS NOT NULL) \
AND C.card_type > 1 "
if {[catch {set user_info [main $strSQL]} sqlerrmsg ]} {
if {[string range $sqlerrmsg 0 10] ne "23000 -1605" } {
disconnect [getenv Client-IP-Address]\
[getenv Framed-IP-Address] "General SQL error: Get
account [getenv User-Name] info."
}
}
Переменная $user_info содержит список полей, перечисленных инструкцией
SELECT вышеуказанного запроса. В случае возникновения ошибки выполнения
или отсутствия записей выполняется немедленное отключение сессии. В случае
нормального получения данных выполняется вызов процедуры «u_update_acc» c
параметром @mode = 1:
set strSQL "EXEC u_update_acc 1, '[lindex [lindex $user_info 0] 0]',\
'$timestamp', 0, 0, 0, $session(Session-Type), '$session(Session-Id)', \
[lindex [lindex $user_info 0] 3], [lindex [lindex $user_info 0] 4], \
'[lindex [lindex $user_info 0] 1]', '[lindex [lindex $user_info 0] 2]', \
'[getenv Calling-Station-Id]', '[getenv Connect-Info]',\
'[getenv Framed-IP-Address]', NULL "
Далее, проверяется результат выполнения процедуры. В случае возникновения
ошибки – сессия отключается:
if {[catch {set res [main $strSQL]} sqlerrmsg ]} {
if {[string range $sqlerrmsg 0 10] ne "23000 -1605" } {
disconnect [getenv Client-IP-Address]\
[getenv Framed-IP-Address]\
"General SQL error: acc insert record."
write_log "$strSQL Session_ID: '$session(Session-Id)'"
}
}
При получении Alive accounting пакета выполняется тарификация сессии.
На основании имени пользователя, из базы данных извлекается информация об
аккаунте, аналогично тому, как это описывалось выше. Параметры аккаунта, а
также значения некоторых Radius атрибутов передаются в процедуру
u_update_acc, которая осуществляет тарификацию сессии. Далее приводится
листинг обработчика, содержащего вызов процедуры:
Alive
{
if {[lindex [lindex $user_info 0] 5] == -1} {
disconnect [getenv Client-IP-Address] [getenv Framed-IP-Address]\
"Account [lindex [lindex $user_info 0] 0] is BLOCKED."
}
set strSQL "EXEC u_update_acc 2, '\
[lindex [lindex $user_info 0] 0]', '$timestamp', \
'[getenv Acct-Session-Time]', \
'[getenv Acct-Output-Octets]', \
'[getenv Acct-Input-Octets]', $session(Session-Type), \
'$session(Session-Id)', [lindex [lindex $user_info 0] 3],\
[lindex [lindex $user_info 0] 4],\
'[lindex [lindex $user_info 0] 1]',\
'[lindex [lindex $user_info 0] 2]', \
'[getenv Calling-Station-Id]', '[getenv Connect-Info]', \
'[getenv Framed-IP-Address]', NULL "
if {[catch {set res [main $strSQL]} sqlerrmsg ]} {
if {[string range $sqlerrmsg 0 10] ne "23000 -1605" } {
disconnect [getenv Client-IP-Address] [getenv Framed-IP-Address]\
"General SQL error PPP update acc. Session-ID:\
$session(Session-Id), ts: [getenv Acct-Session-Time],\
username: [getenv User-Name] "
}
}
if {"[lindex $res 0]" ne ""} {
switch [lindex $res 0] {
0 {
disconnect [getenv Client-IP-Address]\
[getenv Framed-IP-Address] "Record not found.\
Session-ID: $session(Session-Id),\
ts:[getenv Acct-Session-Time], username:\
[getenv User-Name] "
}
2 {
disconnect [getenv Client-IP-Address]\
[getenv Framed-IP-Address]\
"No money (account)."
}
}
} else {
disconnect [getenv Client-IP-Address] [getenv Framed-IP-Address]\
"No acc update resutl. Session-ID: $session(Session-Id),\
ts:[getenv Acct-Session-Time], username:[getenv User-Name] "
}
}
Для тарификации сессии используются значения следующих Radius атрибутов:
- Acct-Session-Time – длительность сессии в секундах;
- Acct-Output-Octets – получено байт абонентом;
- Acct-Input-Octets – отправлено байт абонентом;
Каждый последующий Alive пакет содержит значения вышеназванных
атрибутов, поэтому сумма сессии вычисляется путем умножения тарифа (время
или трафик) аккаунта на значение атрибута. Значения атрибутов – итоговые т.е.
предидущие значения атрибутов уже учтены в каждом новом пакете,
соответственно, при поступлении каждого accounting пакета сумма сессии
каждый раз пересчитывается. Следующий листинг показывает, как
производится тарификация сессий – по времени и трафику:
IF (@price1 != 0 OR @price2 !=0) AND @session_type IN (1, 4) AND @username IS NOT NULL BEGIN
/* тарификация PPP */
IF @session_type = 1 BEGIN
IF @price1 != 0 BEGIN
/* время */
SELECT TOP 1 @price = ISNULL(price, 0) FROM prices WHERE unit_id = @price1
SET @summa1 = @price/60.0/60.0*@tss
IF @summa1 != 0 BEGIN
UPDATE cards SET activate_date = @data WHERE activate_date IS NULL
AND uid = @username AND price1 = @price1
IF @overdraft = 0 BEGIN
IF @sk - @summa1 < @lsm BEGIN
/* кончились деньги блокируем аккаунт */
SET @res = 2
UPDATE cards SET sk = sk - @summa1,
disabled = -1, end_date = @data
WHERE uid = @username AND price1 IS NOT NULL
AND price1 = @price1 AND overdraft = 0
END
ELSE BEGIN
UPDATE cards SET sk = sk - @summa1
WHERE uid = @username
AND price1 IS NOT NULL AND price1 = @price1
AND overdraft = 0
END
SET @sk = @sk - @summa1
END
END
END
IF @price2 != 0 BEGIN
/* трафик */
SELECT TOP 1 @price = ISNULL(price, 0) FROM prices WHERE unit_id = @price2
SET @summa2 = @price/1024.0/1024.0*@bss
IF @summa2 != 0 BEGIN
UPDATE cards SET activate_date = @data
WHERE activate_date IS NULL
AND uid = @username AND price2 = @price2
IF @overdraft = 0 BEGIN
IF @sk - @summa2 < @lsm BEGIN
/* кончились деньги блокируем аккаунт */
SET @res = 2
UPDATE cards SET sk = sk - @summa2,
disabled = -1, end_date = @data
WHERE uid = @username AND price2 IS NOT NULL
AND price2 = @price2 AND overdraft = 0
END
ELSE BEGIN
UPDATE cards SET sk = sk - @summa2
WHERE uid = @username AND price2 IS NOT NULL
AND price2 = @price2 AND overdraft = 0
END
SET @sk = @sk - @summa2
END
END
END
END
END
После выполнения этого блока кода переменные @summa1 и @summa2
содержат сумму сессии. Остается только записать их значения в массив
accounting. Следующий запрос выполняет обновление данных сессии:
IF @mode = 2 BEGIN
/* обновление acc записи */
SET @strSQL = 'UPDATE INET..a' + LTRIM(RTRIM(@tbl)) + ' SET ts = ' + CASE
WHEN @price1 = 0 AND @price2 = 0 THEN STR(@ts)
ELSE ' ts + ' + STR(@tss)
END + ',
tm = ' + STR(@tm) + ', from_bytes = ' + STR(ISNULL(@from_bytes, 0), 18, 0) + ',
to_bytes = ' + STR(ISNULL(@to_bytes, 0), 18, 0) + ',
summa1 = ' + CASE
WHEN @price1 = 0 THEN STR(ISNULL(@summa1, 0), 16, 20)
ELSE ' summa1 + ' + STR(ISNULL(@summa1, 0), 16, 20)
END + ',
summa2 = ' + CASE
WHEN @price2 = 0 THEN STR(ISNULL(@summa2, 0), 16, 20)
ELSE ' summa2 + ' + STR(ISNULL(@summa2, 0), 16, 20)
END + ',
disconnect_reason = ' + CASE
WHEN @disconnect_reason IS NULL THEN ' NULL '
ELSE CHAR(39) + @disconnect_reason + CHAR(32) +
STR(ROUND(ISNULL(@sk, 0), 2), 16, 2) + CHAR(39)
END + ',
provider_info = ' + CHAR(39) + @provider_info + CHAR(39) + '
WHERE username = ' + CHAR(39) + @username + CHAR(39) + '
AND session_type = ' + STR(@session_type) + '
AND session_id = ' + CHAR(39) + @session_id + CHAR(39) + '
'
EXEC (@strSQL)
/* возврат количества обновленных записей, должно быть всегда 1, иначе ошибка */
SELECT CASE @res
WHEN NULL THEN @@rowcount
ELSE @res
END as rc
END
Обновление записей в accounting массиве – процесс, запускающийся
каждый раз при получении нового Alive пакета для каждой текущей сессии.
При большой нагрузке на сервер, во многих биллинговых системах возникает
перегрузка сервера баз данных по причине того, что accounting записывется в
одну большую таблицу. Поиск нужной сесии и ее апдейт требует поиск нужной
записи, что занимает время. Поскольку в «Дельте» используется динамический
массив, содержащий в каждой таблице относительно небольшое число записей,
поиск и апдейт записей выполняется очень быстро и не загружает сервер. При
количестве открытых сессий более 1000 (что нормально для небольшого по
размеру оператора) и параметре accounting update periodic = 1 минуте на
серверах доступа, Alive пакеты следует, практически непрерывно. При такой
нагрузке сервер базы данных выполняет не менее 20 – 30 апдейтов за 1 секунду.
Благодаря динамическому массиву, эти операции выполняются очень быстро и
не загружают сервер!
Обработка Stop пакета не отличается от обработки Alive пакетов в части
тарификации и обновления данных сессии. Отличие состоит в том, что при
получении Stop пакета в поле «disconnect_reason» помещается причина
отключения, т.е. значение соответствующего Radius атрибута.
Тарификация услуг передачи речевой информации несколько отличается
от коммутируемого доступа. Перед описанием этого процесса целесообразно
рассмотреть работу программы с датой и временем. По причине того, что
оператор может использовать несколько серверов доступа, дата и время на
каждом сервере может различаться. Cisco предлагает следующий алгоритм для
установки и синхронизации времени:
# установка часового пояса для Москвы
clock timezone MSK 3
clock summer-time MSK recurring last Sun Mar 2:00 last Sun Oct 3:00
# синхронизация
ntp clock-period 17180299
ntp master 3
ntp server 194.87.0.32
Казалось бы, все просто. Дата и время подтягивается с NTP сервера.
Однако, как выяснилось, время как-то «уходит», и требуется подгонка его один
раз в пол года. По каким-то причинам, синхронизация неправильно
отрабатывает. Также, были зафиксированы случаи, когда дата на Cisco AS 5350
самопроизвольно сбивалось на дефолтовую. Учитывая, что сервером доступа
может быть несколько, данное обстоятельсто превращается в серьезную
проблему для данного оборудования. По этой причине, при формировании и
обновлении информации о сессиях, дата и время, передаваемая с серверов
доступа не используется вообще, для всех видов сессий. Вместо этого
подтягивается дата и время с сервера расчетов, что более надежно, хотя-бы
потому, что данный сервер – один. Вот так это делается:
/etc/cron.daily/sync_clock
#!/bin/sh
ntpdate -b ntp1.demos.net
Этой строчки более чем достаточно для актуализации времени на сервере
расчетов.
В скриптах acct.tcl и auth.tcl, в начале, присутствует следующий код:
set data [clock format [clock seconds] -format "%Y-%m-%d"]
set timestamp [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S"]
Таким образом, в момент запуска, в переменной $data содержится дата на
момент запуска скрипта, а в переменной $timestamp – дата и время. Далее, для
обновления информации о сессиях используются значения этих переменных. В
случае коммутируемого доступа и PPPoe – все просто т.к. началом сессии
можно считать дату и время получения от сервера доступа стартового
аккаунтинг-пакета. В случае с IP телефонией все значительно сложнее. Как уже
отмечалось, разговор начинает тарифицироваться только с поднятия трубки
абонентом «Б» (время, которое прошло до того – тарифицировать нельзя!),
однако поднятие трубки абонентом «Б» не порождает стартовый accounting
пакет, он приходит ранее, в момент завершения процесса Call_Setup т.е.
фактически, непосредственно перед первым гудком посылки вызова, до
поднятия трубки абонентом «Б». Что самое плохое, событие «Ответ» вообще не
приводит к каким-либо событиям со стороны сервера доступа, о которых он
может сообщить биллингу по протоколу Radius. Данное обстоятельство
значительно усложняет алгоритм online тарификации вызовов для IP телефонии,
реализованной с помощью сервера доступа Cisco и протокола Radius. Во многих
биллинговых системах тарификация IP телефонии выполняется только по Stop
пакету сессии, причем дата и время берется с сервера доступа. Данный метод
алгоритмически прост, однако имеет три очень серьезных недостатка:
1. В случае потери Stop пакета (что вполне нормальное явление) вызов вообще
не протарифицируется.
2. В случае сбоя даты и времени на сервере доступа вызов попадет в другой
расчетный период и опять-же, не протарифицируется.
3. В случае экстренной перезагрузки сервера расчетов и/или сервера баз
данных, все текущие вызовы по IP телефонии будут не протарифицированы,
в случае, если Stop пакеты от них пришли во время пока система была
неработоспособна.
Есть еще один момент, который нужно учитывать при тарификации только
по Stop пакету сессии. В течение сессии, невозможно получить текущую
стоимость вызова и соответственно, текущий остаток счета карты. Таким
образом, если не сработает отключение сессии по исчерпанию значения
атрибута Cisco-VoIP-Credit-Time, сессия будет продолжаться больше
максимального значения времени, установленного значением данного атрибута.
Когда абоненты всласть наговорятся друг с другом, последует Stop пакет,
который начислит фактическую стоимость их разговора, а поскольку, сессия не
была вовремя отключена, карта улетит в «минус». Данная ошибка была
замечена на серверах доступа Cisco 5350 и проявлялась на моделях с 4
потоками. В случае тарификации по Stop пакету, предотвратить данную ошибку
невозможно в принципе, поскольку в процессе разговора не известна сумма
начислений.
Учитывая вышесказанное, в «Дельте» было принято решение отказаться
от такой схемы тарификации вообще. Благодаря выведению алгоритмов,
которые будут описаны ниже, тарификация сессий IP телефонии
осуществляется аналогично сессиям коммутируемого доступа, что позволяет
иметь информацию о стоимости соединения и остатке счета карты в любой
момент времени в течение соединения. На основании этой информации, в
случае отказа сервера доступа отключить сессию по истечению значения
атрибута Cisco-VoIP-Credit-Time, сработает скрипт, который срубит такую
сессию с циски принудительно, посредством передачи SNMP команд.
Как отмечалось выше, стартовый пакет формируется при завершении
процесса Call_Setup, однако он не является основанием для начала
тарификации. Сигналом для начала тарификации вызова является наличие двух
атрибутов в первом, пришедшем Alive пакете. Листинг процедуры возвращает
время в секундах от завершения процесса Call_Setup до поднятия трубки
абонентом «Б»:
proc get-cptone-sec {} {
if {"[getenv Cisco-VoIP-Setup-Time]" ne "" &&
"[getenv Cisco-VoIP-Connect-Time]" ne ""} {
if {"[getenv Acct-Session-Time]" ne "" &&
[getenv Acct-Session-Time] == 0} {
return 0
}
set d [string range "[getenv Cisco-VoIP-Setup-Time]" 16 end]
set e [string range "[getenv Cisco-VoIP-Connect-Time]" 18 end]
return [expr [clock scan "[clock format [clock scan [string range $d 16 end]] \
-format "%Y-%m-%d"]\
[clock format [clock scan [string range $d 0 7]] -format "%H:%M:%S"]"] - \
[clock scan "[clock format [clock scan [string range $e 16 end]] \
-format "%Y-%m-%d"]\
[clock format [clock scan [string range $e 0 7]] -format "%H:%M:%S"]"]]
} else {
return 0
}
}
Здесь анализируются значения двух Radius атрибутов: Cisco-VoIP-Setup-Time и
Cisco-VoIP-Connect-Time. Вычитая штамп времени завершения процедуры
Call_Setup из значения Connect_Time получаем время в секундах, которое
прошло от момента начала вызова (в нашем случае – от стартового пакета) до
момента поднятия трубки абонентом «Б». Обратите внимание, пока «Б» не
поднимет трубку, значение атрибута Cisco-VoIP-Connect-Time будет пустым,
процедура возвратит значение «0». Еще характерная особенность данной
процедуры – она возвращает отрицательное значение секунд, что необходимо
для правильного вызова последующей процедуры тарификации. Таким образом,
при приходе первого Alive пакета сессии нам известно:
-
Дата и время получения стартового пакета;
Количество секунд до момента установки соединения c абонентом «Б»;
Дата и время получения Alive пакета;
Теперь получаем длительность вызова, вычитая из текущего штампа времени
дату и время начала вызова и количество секунд, которое прошло в ожидании
поднятия трубки абонентом «Б». Следующий листинг демонстрирует весь
процесс online тарификации IP телефонии:
Alive {
if {"[getenv Cisco-VoIP-Connect-Time]" ne ""} {
set strSQL "EXEC u_update_acc 2, '[lindex [lindex $user_info 0] 0]', \
'$timestamp', [get-cptone-sec], \
'[getenv Acct-Output-Octets]', \
'[getenv Acct-Input-Octets]', $session(Session-Type), \
'$session(Session-Id)', [lindex [lindex $user_info 0] 4], 0, \
'[lindex [lindex $user_info 0] 2]', '[lindex [lindex $user_info 0] 3]', \
'[getenv Calling-Station-Id]', '[get-Called-Station-Id]', \
'[get-dest-dialpeer]', NULL \r "
if {[catch {set res [main $strSQL]} sqlerrmsg ]} {
if {[string range $sqlerrmsg 0 10] ne "23000 -1605" } {
voip_disconnect [getenv Client-IP-Address] [getenv Calling-Station-Id] "General SQ
write_log "$strSQL $sqlerrmsg"
}
}
if {"[lindex $res 0]" ne ""} {
switch [lindex $res 0] {
0 {
voip_disconnect [getenv Client-IP-Address] [getenv Calling-Station-Id] "Acc re
}
2 {
voip_disconnect [getenv Client-IP-Address] [getenv Calling-Station-Id] "No mon
}
}
} else {
voip_disconnect [getenv Client-IP-Address] [getenv Calling-Station-Id] "No VoIP acc re
}
}
}
Код хранимой процедуры u_update_acc, отвечающий за тарификацию:
SET @lsm = 0.5
/* минимальный остаток счета */
SET @tss = 0
SET @bss = 0
SET @price1 = ISNULL(@price1, 0)
SET @price2 = ISNULL(@price2, 0)
SET @overdraft = ISNULL(@overdraft, 0)
/* поиск таблицы в массиве - VoIP */
IF @ts < 0 AND @mode = 2 BEGIN
SET @strSQL = 'SELECT @tdata = data, @tss = ISNULL(ts, 0)
FROM INET..a' + LTRIM(RTRIM(CONVERT(nvarchar, @data, 112))) + '
WHERE username = ' + CHAR(39) + @username + CHAR(39) + '
AND session_type = ' + STR(@session_type) + '
AND session_id = ' + CHAR(39) + @session_id + CHAR(39) + '
'
EXECUTE sp_executesql @strSQL, N'@tdata datetime output, @tss int output', @tdata = @tdata output,
@tss = @tss output
IF @tdata IS NULL BEGIN
/* ищем таблицу в прошлых сутках т.к. сессия перешла через границу суток */
SET @strSQL = 'SELECT @tdata = data, @tss = ISNULL(ts, 0) FROM
INET..a' + LTRIM(RTRIM(CONVERT(nvarchar, DATEADD(day, -1, @data), 112))) + '
WHERE username = ' + CHAR(39) + @username + CHAR(39) + '
AND session_type = ' + STR(@session_type) + '
AND session_id = ' + CHAR(39) + @session_id + CHAR(39) + ''
EXECUTE sp_executesql @strSQL, N'@tdata datetime output, @tss int output, @price1 int output',
@tdata = @tdata output, @tss = @tss output
END
IF @tdata IS NOT NULL BEGIN
/* получение имени таблицы в acc массиве */
SET @tbl = CONVERT(nvarchar, @tdata, 112)
END
END
IF @ts < 0 BEGIN
/* расчет приращения длительности сессии VoIP */
SET @tss = DATEDIFF(second, @tdata, @data) - @tss - ABS(@ts)
IF DATEDIFF(second, @tdata, @data) - ABS(@ts) > 5 SET @tm = 1 + (DATEDIFF(second, @tdata,
@data) - ABS(@ts)-1)/60 ELSE SET @tm = 0
END
IF @price1 != 0 AND @session_type IN (2, 3) AND @username IS NOT NULL BEGIN
/* тарификация VoIP */
SELECT TOP 1 @service_desc = dest, @price = ISNULL(price, 0) FROM price_mtr_voip
WHERE @service_info LIKE seq + '%' AND unit_id = @price1
ORDER BY LEN(seq) DESC
SET @summa1 = @price/60.0*@tss
IF @summa1 != 0 BEGIN
UPDATE cards SET activate_date = @data WHERE activate_date IS NULL
AND uid = @username AND price3 IS NOT NULL AND price3 = @price1
IF @overdraft = 0 BEGIN
IF @sk - @summa1 < @lsm BEGIN
/* окончение средств на счете */
SET @res = 2
UPDATE cards SET sk = sk - @summa1, disabled = -1,
end_date = @data WHERE uid = @username
AND price3 IS NOT NULL
AND overdraft = 0
END
ELSE BEGIN
UPDATE cards SET sk = sk - @summa1 WHERE uid = @username
AND price3 IS NOT NULL AND price3 = @price1 AND overdraft = 0
END
/* остаток счета */
SET @sk = @sk - @summa1
END
END
END
IF @mode = 2 BEGIN
/* обновление acc записи */
SET @strSQL = 'UPDATE INET..a' + LTRIM(RTRIM(@tbl)) + ' SET ts = ' + CASE
WHEN @price1 = 0 AND @price2 = 0 THEN STR(@ts)
ELSE ' ts + ' + STR(@tss) END + ', tm = ' + STR(@tm) + ',
from_bytes = ' + STR(ISNULL(@from_bytes, 0), 18, 0) + ',
to_bytes = ' + STR(ISNULL(@to_bytes, 0), 18, 0) + ', summa1 = ' + CASE
WHEN @price1 = 0 THEN STR(ISNULL(@summa1, 0), 16, 20)
ELSE ' summa1 + ' + STR(ISNULL(@summa1, 0), 16, 20) END + ',
summa2 = ' +
CASE
WHEN @price2 = 0 THEN STR(ISNULL(@summa2, 0), 16, 20)
ELSE ' summa2 + ' + STR(ISNULL(@summa2, 0), 16, 20)
END + ', disconnect_reason = ' +
CASE
WHEN @disconnect_reason IS NULL THEN ' NULL '
ELSE CHAR(39) + @disconnect_reason + CHAR(32) + STR(ROUND(ISNULL(@sk, 0), 2), 16, 2) + CHAR(39)
END + ',
provider_info = ' + CHAR(39) + @provider_info + CHAR(39) + '
WHERE username = ' + CHAR(39) + @username + CHAR(39) + '
AND session_type = ' + STR(@session_type) + '
AND session_id = ' + CHAR(39) + @session_id + CHAR(39) + '
'
EXEC (@strSQL)
/* возврат количества обновленных записей, должно быть всегда 1, иначе ошибка */
SELECT CASE @res
WHEN NULL THEN @@rowcount
ELSE @res
END as rc
END
Обработка Stop пакета сессии не отличается от обработки Alive пакета.
Разница состоит только в передаче в процедуру u_update_acc параметра
@disconnect_reason, который содержит причину завершения сессии.
Данный алгоритм, дает следующие преимущества перед алгоритмом
тарификации по Stop пакету:
1.
2.
3.
4.
5.
Потеря одного Alive пакета не влияет на тарификацию.
Допускается потеря Stop пакета.
Дата и время на сервере доступа может быть произвольной.
Всегда доступна информация о текущей длительности и стоимости сессии.
Имеется возможность отключить сессию принудительно по остатку счета.
Терминирование сессий (принудительное отключение) является
необходимым условием для предотвращения образования отрицательных
остатков на счетах абонентов по авансовой системе расчетов и остатков счетов
карт. Данный процесс, вызывается также при фиксировании любой ошибки
обработки accountiong пакета, что является необходимой мерой
предосторожности программы в случае возможных сбоев в процессах
тарификации. Также, сессия может быть отключена по команде
администратора. Для терминирования сессий в скрипте acct.tcl имеются две
процедуры – для разных типов сессий.
proc disconnect {nas ip reason} {
# терминирует активную PPP и PPPoe сессию
set a [expr ! [catch {exec /etc/raddb/destroy.sh $nas $ip $reason} infovar]]
return $a
};
proc voip_disconnect {nas tel reason} {
# терминирует активную VoIP сессию
set a [expr ! [catch {exec /etc/raddb/destroy_voip.sh $nas $tel $reason} infovar]]
return $a
};
Как видно из названий процедур, первая терминирует PPP и PPPoe соединения,
вторая – IP телефонию. Параметры для первой процедуры:
-
nas:
ip:
reason:
IP адрес сервера доступа.
IP адрес абонента.
Причина отключения сессии.
Параметры для второй процедуры:
-
nas:
tel:
reason:
IP адрес сервера доступа.
Номер телефона абонента «А», подключенного к ТФОП.
Причина отключения сессии.
Обе процедуры вызывают внешние программы, которые, выполняет
отключение сессии с помощью SNMP команд, передаваемых в сервер доступа.
Завершающий этап тарификации – свертка тарификационных данных и
построение начислений на договора абонентов. Данная операция реализована в
виде хранимой процедуры на SQL сервере. Процедура поддерживает несколько
режимов работы, что позволяет управлять формированием начислений по
данным разделам услуг. Процедура может быть вызвана из формы «Периоды»
нажатием кнопки «РасчVoIP» или вызвана с консоли SQL сервера. В случае
необходимости иметь актуализированные текущие начисления по данному
разделу услуг, необходимо организовать вызов процедуры из планировщика с
интервалом не чаще, чем раз в час.
Для вызова процедуры свертки используйте команду:
USE MAIN
GO
EXEC u_calc_radius ‘2009-05-01’, ‘2009-05-31’, 2, 0, 0
GO
Параметры процедуры приведены в следующей таблице:
Параметр
@begin_date
@end_date
@mode
@ab_code
@pid
Тип данных
Datetime
Datetime
Int
Int
Int
Назначение
Дата начала интервала расчета.
Дата окончания интервала расчета.
Режим вызова процедуры.
Код абонента, которого необходимо пересчитать.
Номер процесс при интерактивном запуске.
Все параметры являются обязательными. Параметры @begin_date и @end_date
задают интервал расчета процедуры. Следует помнить, что заданный интервал
дат не должен попадать в закрытый или несуществующий расчетный период.
Параметр @mode определяет режим работы процедуры. Может принимать
следующие значения:
Значение параметра
1
2
3
Описание
Тарифицировать только новые сессии.
Выполнить режим 3, а затем – выполнить расчет.
Удалить все результаты расчета.
Другие значения этого параметра не допускаются.
Параметр @ab_code позволяет выполнить пересчет договоров отдельно взятого
абонента, что может быть полезно при необходимости сформировать
начисления только по одному абоненту, например, в случае, если аккаунт
абонента был неправильно заведен администратором.
Параметр @pid используется только при интерактивном запуске процедуры
(при вызове ее из приложения) для графического отображения выполнения
процесса формирования начислений. При вызове с консоли SQL сервера
значение этого параметра должно быть равно 0.
Приложение 1.
Список файлов и их назначение на сервер расчетов для тарификации услуг
по протоколу Radius.
Путь и имя файла
/etc/init.d/radiusd
Назначение
Загрузчик radius сервера.
/etc/raddb/users
/etc/raddb/clients
/etc/raddb/dictionary.*
/etc/raddb/radmanage.tcl
/etc/raddb/auth.sh
/etc/raddb/acct.sh
/etc/raddb/auth.tcl
/etc/raddb/acct.tcl
/etc/raddb/destroy.sh
/etc/raddb/destroy_voip.sh
/etc/raddb/flowcache.tcl
/var/log/radacct/destroy.log
/var/log/radacct/radius.log
/var/log/radacct/au.log
/var/log/radacct/al.log
/var/tftp/scripts/accepzebra.tcl
/var/tftp/scripts/pin_auth.tcl
/var/tftp/scripts/clid_authen.tcl
/var/tftp/scripts/noauth_pull.tcl
/var/tftp/voice/*.au
Конфигурация внешних обработчиков Radius пакетов.
Список серверов доступа для Radius сервера.
Словари Radius атрибутов пакетов.
Набор функций обработки Radius пакетов.
Загрузчик обработчика пакетов аутенфикации.
Загрузчик обработчика пакетов accounting.
Обработчик пакетов авторизации.
Обработчик пакетов accounting.
Скрипт терминирования сессий PPP и PPPoe.
Скрипт терминирования сессий VoIP.
Скрипт, анализирующий текущий трафик PPPoe сессии
Журнал терминирования всех видов сессий.
Журнал Radius сервера.
Журнал пакетов аутенфикации.
Журнал пакетов accounting.
Обработчик вызовов входящего VoIP диалпира.
Обработчик кода доступа к IP телефонии.
Обработчик номера «А» для доступа к IP телефонии.
Обработчик доступа без аутенфикации.
Набор голосовых сообщений сервера IP телефонии.
Приложение 2.
Список объектов и их назначение на сервере базы данных для
тарификации услуг по протоколу Radius.
Имя объекта
INET..aYYYYMMDD
MAIN..u_update_acc
MAIN..u_calc_radius
MAIN..cards
MAIN..units
MAIN..unit_types
MAIN..prices
MAIN..price_mtr_voip
Назначение
Набор таблиц динамического массива accounting.
Хранимая процедура обработки accounting пакетов.
Процедура свертки данных и построений начислений.
Таблица учетных записей RADIUS сервера.
Таблица справочник услуг.
Таблица справочник разделов услуг.
Таблица справочник цен услуг.
Справочник стоимости направления IP телефонии.
Download