Модуль ядра ses traf: відмінності між версіями

Матеріал з NoDeny
Перейти до навігації Перейти до пошуку
 
(Не показано 26 проміжних версій цього користувача)
Рядок 1: Рядок 1:
==Общая информация==
==Общая информация==
Если вы используете Radius, то в нем есть механизм регулярной отправки текущих данных по трафику по каждому абоненту. Это называется accounting. По умолчанию, NoDeny использует accounting только для того, чтобы удостовериться, что клиент все еще подключен. Данные по трафику просто игнорируются. Мы можем настроить отправку этих данных в биллинг.
Если вы используете Radius, то в нем есть механизм регулярной отправки текущих данных по трафику по каждому абоненту. Это называется accounting. По умолчанию, NoDeny использует accounting только для того, чтобы удостовериться, что клиент все еще подключен. Данные по трафику просто игнорируются. При этом можно настроить их отправку в биллинг.


Нам необходимо изменить mysql-процедуры accounting-а так, чтобы они:
Необходимо изменить mysql-процедуры accounting-а так, чтобы они:
# принимали трафик от radius
# принимали трафик от radius
# записывали в таблицу ses_traf
# записывали в таблицу ses_traf


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


==Настроим radius на отправку данных по трафику==
==Настроим radius на отправку данных по трафику==
Рядок 14: Рядок 14:


<pre>
<pre>
postauth_query = "call radupdate('%{User-Name}','%{reply:Framed-IP-Address}',\
postauth_query = "call radupdate( параметр1, параметр2, ... )"
                'user=%{Calling-Station-Id};nas=%{NAS-IP-Address}')"
</pre>
</pre>


Рядок 21: Рядок 20:


<pre>
<pre>
postauth_query = "call radupdate('%{User-Name}','%{Framed-IP-Address}',\
postauth_query = "call radupdate( параметр1, параметр2, ... ,\
                     'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}','%{Acct-Session-Id}',\
                     'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}','%{Acct-Session-Id}',\
                     (%{%{Acct-Input-Gigawords}:-0} * POWER(2, 32)) + %{%{Acct-Input-Octets}:-0},\
                     (%{%{Acct-Input-Gigawords}:-0} * POWER(2, 32)) + %{%{Acct-Input-Octets}:-0},\
Рядок 30: Рядок 29:


<pre>
<pre>
accounting_update_query = "call radupdate('%{User-Name}','%{reply:Framed-IP-Address}',\
accounting_update_query = "call radupdate( параметр1, параметр2, ... )"
                'user=%{Calling-Station-Id};nas=%{NAS-IP-Address}')"
</pre>
</pre>


Рядок 37: Рядок 35:


<pre>
<pre>
accounting_update_query = "call radupdate('%{User-Name}','%{Framed-IP-Address}',\
accounting_update_query = "call radupdate( параметр1, параметр2, ... ,\
                    'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}','%{Acct-Session-Id}',0,0})"
        'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}','%{Acct-Session-Id}', 0, 0)"
</pre>
</pre>


Рядок 46: Рядок 44:


<pre>
<pre>
            start {
    start {
                query = "call radupdate('%{User-Name}','%{Framed-IP-Address}',\
        query = "call radupdate( параметр1, параметр2, ... )"
                        'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}')"
    }
            }
</pre>
</pre>


на
на


<div style="white-space: pre;">
<div>
            start {
    start {
                query = "call radupdate('%{User-Name}','%{Framed-IP-Address}',\
        query = "call radupdate( параметр1, параметр2, ... ,\
                        'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}',{{color|#229944|'%{Acct-Session-Id}',\
                  'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}',\
                        (%{%{Acct-Input-Gigawords}:-0} * POWER(2, 32)) + %{%{Acct-Input-Octets}:-0},\
                  '%{Acct-Session-Id}',\
                        (%{%{Acct-Output-Gigawords}:-0} * POWER(2, 32)) + %{%{Acct-Output-Octets}:-0})"}}
                  (%{%{Acct-Input-Gigawords}:-0} * POWER(2, 32)) + %{%{Acct-Input-Octets}:-0},\
            }
                  (%{%{Acct-Output-Gigawords}:-0} * POWER(2, 32)) + %{%{Acct-Output-Octets}:-0})"
    }
</div>
</div>
{{color|#229944|Looks good}}
 
а также
Обратите внимание, если в старой процедуре вы передаете строку 'user=...', то добавьте к ней ';ses=%{Acct-Session-Id}'
<br><br>
 
Также


<pre>
<pre>
     post-auth {
     post-auth {
         query = "call radupdate('%{User-Name}','%{reply:Framed-IP-Address}',\
         query = "call radupdate( параметр1, параметр2, ... )"
                'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}')"
     }
     }
</pre>
</pre>
Рядок 76: Рядок 76:
<pre>
<pre>
     post-auth {
     post-auth {
         query = "call radupdate('%{User-Name}','%{reply:Framed-IP-Address}',\
         query = "call radupdate( параметр1, параметр2, ... ,\
                 'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}', '%{Acct-Session-Id}', 0, 0)"
                 'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}', '%{Acct-Session-Id}', 0, 0)"
     }
     }
Рядок 82: Рядок 82:


==Меняем процедуру radupdate==
==Меняем процедуру radupdate==
=== Freeradius версии 2===
<pre>
CREATE DEFINER=`root`@`localhost` PROCEDURE `radupdate`(IN login VARCHAR(64), IN ip VARCHAR(16),
    IN properties VARCHAR(255), IN ses VARCHAR(32), IN trafin BIGINT(20), IN trafout BIGINT(20))
BEGIN
  DECLARE usr_id INT;
  DECLARE usr_ip VARCHAR(15) DEFAULT NULL;
  SELECT id INTO usr_id FROM users WHERE name=login LIMIT 1;
  SELECT get_ip(usr_id) INTO usr_ip;
  CALL set_auth(usr_ip, CONCAT('mod=pppoe;',REPLACE(properties,':','')));
  INSERT INTO ses_traf SET ses_id=ses, traf_in=trafin, traf_out=trafout, time=UNIX_TIMESTAMP(), uid=usr_id;
END
</pre>
=== Freeradius версии 3===
=== Freeradius версии 3===


Рядок 87: Рядок 102:
DROP PROCEDURE IF EXISTS `radupdate`;
DROP PROCEDURE IF EXISTS `radupdate`;
DELIMITER $$
DELIMITER $$
CREATE PROCEDURE `radupdate`(IN login VARCHAR(64), IN usr_ip VARCHAR(16), IN properties VARCHAR(255),
CREATE PROCEDURE `radupdate`(
    параметр1, параметр2, ... ,
    IN ses VARCHAR(32), IN trafin BIGINT(20), IN trafout BIGINT(20)
)
BEGIN
    ...
    INSERT INTO ses_traf SET ses_id=ses, traf_in=trafin, traf_out=trafout, time=UNIX_TIMESTAMP(), uid=...;
    ...
END$$
DELIMITER ;
</pre>
 
Т.е. вы должны добавить в процедуру 3 входных параметра:
* id сессии
* входящий трафик сессии
* исходящий трафик сессии
 
Процедура должна записать эти данные в таблицу ses_traf. Обратите внимание, что в поле uid этой таблицы мы должны записать id абонента, с которым связан трафик. Например, это может быть реализовано так:
 
 
<pre>
DROP PROCEDURE IF EXISTS `radupdate`;
DELIMITER $$
CREATE PROCEDURE `radupdate`(
    IN login VARCHAR(64), IN usr_ip VARCHAR(16), IN properties VARCHAR(255),
     IN ses VARCHAR(32), IN trafin BIGINT(20), IN trafout BIGINT(20))
     IN ses VARCHAR(32), IN trafin BIGINT(20), IN trafout BIGINT(20))
BEGIN
BEGIN
     IF( SELECT 1 FROM v_ips i JOIN users u ON i.uid=u.id WHERE u.name=login AND i.ip=usr_ip ) THEN
     IF( SELECT 1 FROM v_ips i JOIN users u ON i.uid=u.id WHERE u.name=login AND i.ip=usr_ip ) THEN
         CALL set_auth(usr_ip, CONCAT('mod=pppoe;',REPLACE(properties,':','')));
         CALL set_auth(usr_ip, CONCAT(';ses=', ses, ';mod=pppoe;',REPLACE(properties,':','')));
         INSERT INTO ses_traf SET ses_id=ses, traf_in=trafin, traf_out=trafout, time=UNIX_TIMESTAMP(),
         INSERT INTO ses_traf SET ses_id=ses, traf_in=trafin, traf_out=trafout, time=UNIX_TIMESTAMP(),
             uid=(SELECT id FROM users WHERE name=login LIMIT 1);
             uid=(SELECT id FROM users WHERE name=login LIMIT 1);
    ELSE
        UPDATE users SET name=login WHERE name=login LIMIT 1;
     END IF;
     END IF;
END$$
END$$
DELIMITER ;
DELIMITER ;
</pre>
===Проверка===
Перезапустите radius-сервер и выполните sql-запрос:
<pre>
select * from ses_traf;
</pre>
Должны получить табличку с трафиком абонентов. Если нет - запускайте radiusd -X и смотрите какие ошибки он выводит.
==Запускаем модуль ядра  ses_traf==
<pre>
perl nokernel.pl -m=ses_traf -v
</pre>
</pre>

Поточна версія на 15:46, 8 вересня 2023

Общая информация

Если вы используете Radius, то в нем есть механизм регулярной отправки текущих данных по трафику по каждому абоненту. Это называется accounting. По умолчанию, NoDeny использует accounting только для того, чтобы удостовериться, что клиент все еще подключен. Данные по трафику просто игнорируются. При этом можно настроить их отправку в биллинг.

Необходимо изменить mysql-процедуры accounting-а так, чтобы они:

  1. принимали трафик от radius
  2. записывали в таблицу ses_traf

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

Настроим radius на отправку данных по трафику

Freeradius версии 2

В файле /usr/local/etc/raddb/sql.conf заменить

postauth_query = "call radupdate( параметр1, параметр2, ... )"

на

postauth_query = "call radupdate( параметр1, параметр2, ... ,\
                    'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}','%{Acct-Session-Id}',\
                    (%{%{Acct-Input-Gigawords}:-0} * POWER(2, 32)) + %{%{Acct-Input-Octets}:-0},\
                    (%{%{Acct-Output-Gigawords}:-0} * POWER(2, 32)) + %{%{Acct-Output-Octets}:-0})"

а также

accounting_update_query = "call radupdate( параметр1, параметр2, ... )"

на

accounting_update_query = "call radupdate( параметр1, параметр2, ... ,\
        'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}','%{Acct-Session-Id}', 0, 0)"

Freeradius версии 3

В файле /usr/local/etc/raddb/mods-enabled/sql заменить

    start {
        query = "call radupdate( параметр1, параметр2, ... )"
    }

на

   start {
       query = "call radupdate( параметр1, параметр2, ... ,\
                  'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}',\
                  '%{Acct-Session-Id}',\
                  (%{%{Acct-Input-Gigawords}:-0} * POWER(2, 32)) + %{%{Acct-Input-Octets}:-0},\
                  (%{%{Acct-Output-Gigawords}:-0} * POWER(2, 32)) + %{%{Acct-Output-Octets}:-0})"
   }

Обратите внимание, если в старой процедуре вы передаете строку 'user=...', то добавьте к ней ';ses=%{Acct-Session-Id}'

Также

    post-auth {
        query = "call radupdate( параметр1, параметр2, ... )"
    }

на

    post-auth {
        query = "call radupdate( параметр1, параметр2, ... ,\
                'user=%{Calling-Station-Id};nas=%{NAS-IP-Address};ses=%{Acct-Session-Id}', '%{Acct-Session-Id}', 0, 0)"
    }

Меняем процедуру radupdate

Freeradius версии 2

CREATE DEFINER=`root`@`localhost` PROCEDURE `radupdate`(IN login VARCHAR(64), IN ip VARCHAR(16),
    IN properties VARCHAR(255), IN ses VARCHAR(32), IN trafin BIGINT(20), IN trafout BIGINT(20))
BEGIN
  DECLARE usr_id INT;
  DECLARE usr_ip VARCHAR(15) DEFAULT NULL;
  SELECT id INTO usr_id FROM users WHERE name=login LIMIT 1;
  SELECT get_ip(usr_id) INTO usr_ip;
  CALL set_auth(usr_ip, CONCAT('mod=pppoe;',REPLACE(properties,':','')));
  INSERT INTO ses_traf SET ses_id=ses, traf_in=trafin, traf_out=trafout, time=UNIX_TIMESTAMP(), uid=usr_id;
END

Freeradius версии 3

DROP PROCEDURE IF EXISTS `radupdate`;
DELIMITER $$
CREATE PROCEDURE `radupdate`(
    параметр1, параметр2, ... ,
    IN ses VARCHAR(32), IN trafin BIGINT(20), IN trafout BIGINT(20)
)
BEGIN
    ...
    INSERT INTO ses_traf SET ses_id=ses, traf_in=trafin, traf_out=trafout, time=UNIX_TIMESTAMP(), uid=...;
    ...
END$$
DELIMITER ;

Т.е. вы должны добавить в процедуру 3 входных параметра:

  • id сессии
  • входящий трафик сессии
  • исходящий трафик сессии

Процедура должна записать эти данные в таблицу ses_traf. Обратите внимание, что в поле uid этой таблицы мы должны записать id абонента, с которым связан трафик. Например, это может быть реализовано так:


DROP PROCEDURE IF EXISTS `radupdate`;
DELIMITER $$
CREATE PROCEDURE `radupdate`(
    IN login VARCHAR(64), IN usr_ip VARCHAR(16), IN properties VARCHAR(255),
    IN ses VARCHAR(32), IN trafin BIGINT(20), IN trafout BIGINT(20))
BEGIN
    IF( SELECT 1 FROM v_ips i JOIN users u ON i.uid=u.id WHERE u.name=login AND i.ip=usr_ip ) THEN
        CALL set_auth(usr_ip, CONCAT(';ses=', ses, ';mod=pppoe;',REPLACE(properties,':','')));
        INSERT INTO ses_traf SET ses_id=ses, traf_in=trafin, traf_out=trafout, time=UNIX_TIMESTAMP(),
            uid=(SELECT id FROM users WHERE name=login LIMIT 1);
    END IF;
END$$
DELIMITER ;

Проверка

Перезапустите radius-сервер и выполните sql-запрос:

select * from ses_traf;

Должны получить табличку с трафиком абонентов. Если нет - запускайте radiusd -X и смотрите какие ошибки он выводит.

Запускаем модуль ядра ses_traf

perl nokernel.pl -m=ses_traf -v