- Если у вас до этого была статика - юзерам ничего не нужно переделывать. Не нужно никаких услуг коммутируемого доступа.
- Реализуется стандартными *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
}
..........
}
Код: Выделить всё
accounting {
detail
sql
perl
}
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;
}
Код: Выделить всё
<?xml version="1.0"?>
<urfa>
<parameter name="ip_group_id"/>
<parameter name="ip_address"/>
<call function="rpcf_delete_from_ipgroup_by_ipgroup"/>
</urfa>
Код: Выделить всё
<?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, просьба с подобными проблемами в личку не стучаться, за вас настраивать ничего не буду. По крайней мере бесплатно ;))
А так - если будут комментарии или какая-нибуть "шлифовка" - буду рад