Рабочий вариант utm + freeradius + ppp + динамические IP

Форум для размещения материалов по реализации различных схем использования ПО, решению частых проблем и предупреждению частых ошибок
Закрыто
wingman
Сообщения: 136
Зарегистрирован: Чт дек 07, 2006 15:36
Контактная информация:

Рабочий вариант utm + freeradius + ppp + динамические IP

Сообщение wingman » Ср июн 02, 2010 20:02

Преимущества данного варианта:
- Если у вас до этого была статика - юзерам ничего не нужно переделывать. Не нужно никаких услуг коммутируемого доступа.
- Реализуется стандартными *nix-средствами: freeradius, perl
- Гибко и масштабируемо: можно переводить VPN-серверы по одному и решать, нужно оно или нет =)
- Бесплатно - дарю :D

Необходимо:
- freeradius
- perl
- urfa-клиент


Суть вкратце: за распределение IP-адресов отвечают NAS'ы. Они выдают IP и сообщают его радиусу в accounting-start пакете.
Далее радиус привязывает выданный IP абоненту, чтобы верно считался трафик по netflow. К сожалению, такая, как кто-то тут удачно выразился "пинаемая статика" - единственный вариант, предоставляемый UTM'ом. Их "коммутируемый доступ" работает по тому же принципу.

Итак.

1. IP из пула выдаются НАС'ом. В нашем случае - выдаются тем, для кого не пришел от радиуса аттрибут 'Framed-IP-Address', из пула в данном случае "mainpool".

2. У нас статические ипы выдаются из отдельного диапазона, поэтому если в билинге привязан 93.157.x.x - выдаётся Framed-IP-address, иначе - Framed-Pool.

Код: Выделить всё

authorize_reply_query = "SELECT '','%{SQL-User-Name}','Framed-IP-Address',inet_ntoa( ip_groups.ip & 0xffffffff ) AS ip,'=' \
    FROM UTM5.ip_groups WHERE uname ='%{SQL-User-Name}' AND is_deleted = '0' \
    AND inet_ntoa( ip_groups.ip & 0xffffffff ) LIKE '93.157.%' \
UNION SELECT '', '%{SQL-User-Name}', 'Framed-Pool', 'mainpool', '=' FROM ip_groups \
    WHERE uname ='%{SQL-User-Name}' AND inet_ntoa( ip_groups.ip & 0xffffffff ) NOT LIKE '93.157.%' \
UNION select '', '%{SQL-User-Name}', 'Mikrotik-Rate-Limit', radius_data.value, '=' \
    FROM radius_data \
    WHERE radius_data.owner_id=( select sl.service_id FROM ip_groups ig, iptraffic_service_links isl, service_links sl \
                                where ig.uname='%{SQL-User-Name}' and ig.is_deleted=0 and ig.ip_group_id=isl.ip_group_id \
                                 and isl.is_deleted=0 and isl.id=sl.id and sl.is_deleted=0) \
    AND radius_data.vendor=14988" 

3. Чтобы при этом считался трафик по netflow, необходимо выданный IP привязывать к аккаунту пользователя. Для этого я подключил rlm_perl с использованием единственной функции - accounting.
Можно, в принципе, использовать и rlm_exec, но перл загружается при старте радиуса, а exec - каждый раз при вызове, что гораздо более ресурсоёмко.

/etc/raddb/radiusd.conf:

Код: Выделить всё

modules {
        perl {
                module = /etc/raddb/rad.pl
        }
..........
} 
/etc/raddb/sites-enabled/default:

Код: Выделить всё

accounting {
   detail
   sql
   perl
} 
/etc/raddb/rad.pl -- это прилагаемый к поставке фрирадиуса example.pl с переделанной ф-ией accounting.
rad.pl:

Код: Выделить всё

use strict;
use DBI;

my $DBH = DBI->connect("DBI:mysql:database=UTM5;host=localhost", "xxx", "xxx");
$DBH->{mysql_auto_reconnect}=1;
my $sth = "";
my $URFA_CALL = "/netup/utm5/bin/utm5_urfaclient ";


.....................
# Function to handle accounting
sub accounting {

        # Тут проверяется, не из диапазона ли реальных внешников выдан IP
        if($RAD_REQUEST{'Acct-Status-Type'} eq 'Start' && not($RAD_REQUEST{'Framed-IP-Address'} =~ m/93.157.(xxx|yyy|zzz|zxc|cxz).[0-9]+/i) )
        {
                my $fip = $RAD_REQUEST{'Framed-IP-Address'};
                my $uname = $RAD_REQUEST{'SQL-User-Name'};
                my $sessid = $RAD_REQUEST{'Acct-Session-Id'};

                # 1. Find ip-goup with this (dynamic) ip AND! empty login (just ensure it's dynamic) AND remove it
                $sth = $DBH->prepare("SELECT ip_group_id FROM `ip_groups` where `ip`=inet_aton('$fip') AND uname='' AND is_deleted=0");
                $sth->execute;
                if($sth->rows)
                {
                        while(my $ref = $sth->fetchrow_arrayref)
                        {
                                my $ipg_id = $$ref[0];
                                `$URFA_CALL -a iphome_delete_from_ipgroup -ip_group_id $ipg_id -ip_address "$fip"`;
                        }
                }

                # 2. Find connected user info AND add this ip to hem:
                $sth = $DBH->prepare("SELECT service_links.id as slink_id, service_links.service_id as service_id,
                users.id as uid, accounts.id as aid, periodic_service_links.discount_period_id AS did
                FROM ip_groups
                INNER JOIN iptraffic_service_links isl ON isl.ip_group_id=ip_groups.ip_group_id
                INNER JOIN service_links ON service_links.id=isl.id
                INNER JOIN periodic_service_links ON periodic_service_links.id=service_links.id
                INNER JOIN accounts ON accounts.id=service_links.account_id
                INNER JOIN users ON users.basic_account=accounts.id
                WHERE ip_groups.uname='$uname' AND ip_groups.is_deleted=0
                AND isl.is_deleted=0
                AND accounts.is_deleted=0
                AND users.is_deleted=0");
                $sth->execute;
                while(my $ref = $sth->fetchrow_arrayref)
                {
                        my $slink_id = $$ref[0];
                        my $service_id = $$ref[1];
                        my $uid = $$ref[2];
                        my $aid = $$ref[3];
                        my $did = $$ref[4];
                        `$URFA_CALL -a iphome_add_ip -user_id $uid -account_id $aid -slink_id $slink_id -service_id $service_id -ip_address "$fip" -discount_period_id $did`;
                }


        }

        return RLM_MODULE_OK;
} 

/netup//utm5/xml/iphome_delete_from_ipgroup.xml

Код: Выделить всё

<?xml version="1.0"?>
<urfa>
        <parameter name="ip_group_id"/>
        <parameter name="ip_address"/>
<call function="rpcf_delete_from_ipgroup_by_ipgroup"/>
</urfa> 
/netup//utm5/xml/iphome_add_ip.xml

Код: Выделить всё

<?xml version="1.0"?>
<urfa>
    <parameter name="user_id"/>
    <parameter name="account_id"/>
    <parameter name="slink_id"/>
    <parameter name="service_id"/>
    <parameter name="service_type" value="3"/>
    <parameter name="discount_period_id"/>
    <parameter name="ip_address"/>
    <parameter name="ip_not_vpn" value="0"/>
    <parameter name="mask" value="255.255.255.255"/>
    <parameter name="return_type" value="integer_return"/>
<call function="rpcf_add_service_to_user"/>
</urfa> 

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

Для умелого инженера/админа/кодера прикрутить и переделать под себя труда абсолютно не составит.

Если вы не знаете, с какой стороны подойти к фрирадиусу и что такое perl, просьба с подобными проблемами в личку не стучаться, за вас настраивать ничего не буду. По крайней мере бесплатно ;))

А так - если будут комментарии или какая-нибуть "шлифовка" - буду рад

Pulse
Сообщения: 945
Зарегистрирован: Вт окт 03, 2006 12:58

Сообщение Pulse » Ср июл 28, 2010 10:50

А сколько у вас абонентов?
дело в том, что.... запрос
# 2. Find connected user info AND add this ip to hem:
выполняется очень долго

Код: Выделить всё

+----------+------------+------+-------+-----+
| slink_id | service_id | uid  | aid   | did |
+----------+------------+------+-------+-----+
|    31546 |         64 | 3096 | 13095 |  16 |
+----------+------------+------+-------+-----+
1 row in set &#40;1 min 3.11 sec&#41;

mikkey finn
Сообщения: 1611
Зарегистрирован: Пт ноя 10, 2006 15:23

Сообщение mikkey finn » Ср июл 28, 2010 11:38

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

Код: Выделить всё

select `service_links`.`id` AS `slink_id`,`service_links`.`service_id` AS `service_id`,`UTM5`.`users`.`id` AS `uid`,`UTM5`.`accounts`.`id` AS `aid`,`UTM5`.`periodic_service_links`.`discount_period_id` AS `did` from `UTM5`.`ip_groups` join `UTM5`.`iptraffic_service_links` `isl` join `UTM5`.`service_links` join `UTM5`.`periodic_service_links` join `UTM5`.`accounts` join `UTM5`.`users` where &#40;&#40;`UTM5`.`ip_groups`.`ip_group_id` = `isl`.`ip_group_id`&#41; and &#40;`UTM5`.`periodic_service_links`.`id` = `isl`.`id`&#41; and &#40;`service_links`.`id` = `isl`.`id`&#41; and &#40;`UTM5`.`accounts`.`id` = `service_links`.`account_id`&#41; and &#40;`UTM5`.`users`.`basic_account` = `service_links`.`account_id`&#41; and &#40;`UTM5`.`users`.`is_deleted` = 0&#41; and &#40;`UTM5`.`accounts`.`is_deleted` = 0&#41; and &#40;`isl`.`is_deleted` = 0&#41; and &#40;`UTM5`.`ip_groups`.`is_deleted` = 0&#41; and &#40;`UTM5`.`ip_groups`.`uname` = _utf8'$username'&#41;&#41;
Ну и план выполнения примерно такой:

Код: Выделить всё

+----+-------------+------------------------+--------+------------------------------------------------+-----------+---------+-------------------------------------+-------+-------------+
| id | select_type | table                  | type   | possible_keys                                  | key       | key_len | ref                                 | rows  | Extra       |
+----+-------------+------------------------+--------+------------------------------------------------+-----------+---------+-------------------------------------+-------+-------------+
|  1 | SIMPLE      | isl                    | ALL    | PRIMARY,index_55232d39c6b30c255118489783ed36e2 | NULL      | NULL    | NULL                                | 41382 | Using where |
|  1 | SIMPLE      | periodic_service_links | eq_ref | PRIMARY                                        | PRIMARY   | 4       | UTM5.isl.id                         |     1 |             |
|  1 | SIMPLE      | service_links          | eq_ref | PRIMARY,index_4ac9ffe9ebe0322b6fb7d34f3e346a6b | PRIMARY   | 4       | UTM5.isl.id                         |     1 |             |
|  1 | SIMPLE      | accounts               | eq_ref | PRIMARY,index_33a254f7035d715e438372133be258b5 | PRIMARY   | 4       | UTM5.service_links.account_id       |     1 | Using where |
|  1 | SIMPLE      | ip_groups              | ref    | ig_idx,index_264d0ebccce540730e5da3d221dfa3d9  | ig_idx    | 5       | UTM5.isl.ip_group_id                |    19 | Using where |
|  1 | SIMPLE      | users                  | ref    | u_acc_idx                                      | u_acc_idx | 10      | UTM5.service_links.account_id,const |    19 | Using where |
+----+-------------+------------------------+--------+------------------------------------------------+-----------+---------+-------------------------------------+-------+-------------+
Очевидно, что самое первое - isl самое долгое - перебор 41 тыщи записей.Если бы использовался подходящий индекс - то гораздо быстрее решился бы вопрос.

wingman
Сообщения: 136
Зарегистрирован: Чт дек 07, 2006 15:36
Контактная информация:

Сообщение wingman » Вт окт 12, 2010 07:17

Эммм, ну да.. У нас уже далеко не только нетаповские родные индексы в бд =)

drag0mir
Сообщения: 64
Зарегистрирован: Сб ноя 24, 2007 13:46
Откуда: Нижний Новгород

Сообщение drag0mir » Пн мар 28, 2011 07:04

приветствую :)
вот тоже начали думать о динамике реальных ip, данная схема подразумевает то что NASы являются цисками и микротиками, а у нас NAS ввиде обыкновенной PC-шки с линухом, где поднят pppoe-server и pptpd сооответственно.
есть у них опции конечно, чтоб они выдавали из своего пула типа,
но врятли они поймут то что им передаст радиусклиент
Framed-Pool mainpool
как поступить в этом случае?
есть какие нить идеи?
юзаю UTM-5.2.1-007+freeradius

solomon
Сообщения: 316
Зарегистрирован: Вт мар 16, 2010 08:39

Сообщение solomon » Пт дек 16, 2011 13:42

deleted
Последний раз редактировалось solomon Ср фев 01, 2012 20:04, всего редактировалось 1 раз.

solomon
Сообщения: 316
Зарегистрирован: Вт мар 16, 2010 08:39

Сообщение solomon » Чт дек 22, 2011 07:51

deleted
Последний раз редактировалось solomon Ср фев 01, 2012 20:04, всего редактировалось 1 раз.

solomon
Сообщения: 316
Зарегистрирован: Вт мар 16, 2010 08:39

Сообщение solomon » Пт июл 20, 2012 12:01

wingman, скажите как вы скармливаете параметры шейпинга циске ?

drag0mir
Сообщения: 64
Зарегистрирован: Сб ноя 24, 2007 13:46
Откуда: Нижний Новгород

Сообщение drag0mir » Вт авг 21, 2012 14:09

если кому интересно, вот еще одно решение основаное на данном
http://linuxsnippets.net/ru/snippet/utm ... rver-linux

solomon
Сообщения: 316
Зарегистрирован: Вт мар 16, 2010 08:39

Сообщение solomon » Ср авг 22, 2012 06:39

Решение основанное на посте wingman'a - статья отличная вчера прям только нашел ее в инете

drag0mir
Сообщения: 64
Зарегистрирован: Сб ноя 24, 2007 13:46
Откуда: Нижний Новгород

Сообщение drag0mir » Вс сен 02, 2012 15:57

solomon писал(а):Решение основанное на посте wingman'a - статья отличная вчера прям только нашел ее в инете
Спасибо )) рад что кому-то пригодилось ))

Закрыто