UTM5 + freeradius (2 dynamic ip pool) + urfaclient на php + pppoe-server (linux)

Имеем мы биллинг UTM5.
Решили отказаться от NATа и раздать клиентам реальные динамические ip-шники.
Порыскав на форуме netup.ru, я не нашел ничего, что удовлетворяло бы моим запросам.
NASы у нас в виде машин на Линухе, freeradius, urfaclient на php написали умельцы с форума. Единственное, что нашел по теме, и за это огромное человеческое спасибо wingman'у с форума, который осветил эту тему более ярко:
http://www.netup.ru/phpbb/viewtopic.php?t=7948&highlight=%E4%E8%ED%E0%EC...
Собственно говоря? от этого и начал отталкиваться. Разница заключалась в том, что как я уже говорил NASы наши на обыкновенных линухах и urfaclient наphp. А еще мне надо было не один пул динамических ip-шников, а два. Один из внешних реальных, а второй для тех кто заблокирован. В способе, описанном у вингмана, он передает имя пула адресов NAS-у и NAS уже раздает адреса клиентам, Микротики и циски так умеют, а вот pppoe-server умеет работать только с одним пулом, поэтому было решено реализовать ippool-ы на freeradius.
После долгих и упорных чтений доков по фрирадиусу порой абсолютно бесполезных, и кучи экспериментов я всё-таки родил решение )))

Начнем по порядку:

В файле /etc/freeradius/modules/ippool создаём 2 пула таким образом

ippool dynamic {
     range-start = 91.219.59.100
     range-stop = 91.219.59.110
     netmask = 255.255.255.255
     cache-size = 800
     session-db = ${db_dir}/db2.ippool
     ip-index = ${db_dir}/db2.ipindex
     override = no
     maximum-timeout = 0
     key = "%{NAS-IP-Address} %{NAS-Port}"
}
ippool main_pool {
     range-start = 172.16.1.1
     range-stop = 172.16.16.254
     netmask = 255.255.255.255
     cache-size = 800
     session-db = ${db_dir}/db.ippool
     ip-index = ${db_dir}/db.ipindex
     override = no
     maximum-timeout = 0
     key = "%{NAS-IP-Address} %{NAS-Port}"
}

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

вот что следует добавить в /etc/freeradius/sites-enabled/default

authorize {
     preprocess
     auth_log
     update control {
            Pool-Name = 'main_pool'
        }
     chap
     files
     sql
}
accounting {
     detail
     radutmp
     sql
     perl
}
session {
     radutmp
     sql
}
post-auth {
     dynamic
     main_pool
     Post-Auth-Type REJECT {
        attr_filter.access_reject
     }
}

по дефолту всем клиентам будут выдаваться адреса из пула main_pool.
так же добавляем files в authorize, чтоб прочитать файлик users, который приведен ниже.
в секцию accounting добавляем perl, об этом позже. ну и в post-auth - названия обоих пулов

/etc/freeradius/users

суть записи думаю ясна: всем, кто входит в группу "dynamic" - меняем Pool-Name на "dynamic" (изначально у всех был main_pool)

DEFAULT Sql-Group == "dynamic", Pool-Name := "dynamic"
          Fall-Through = Yes

Теперь соответственно, возникает вопрос, как нужных пользователей засунуть в нужную группу.
разберем файл /etc/freeradius/sql/mysql/dialup.conf

authorize_check_query - авторизуем всех у кого совпадают связка логин/пароль, даже заблокированых. Также сообщаем о том, что возможно только 1 одновременное подключение типа Simultaneous-Use=1

authorize_reply_query - тем клиентам, у которых постоянный внешний ip - выдаем их адрес, я для этого выделил отдельную подсеть 91.219.58.0/24

group_membership_query - здесь делаем проверку, какой группе принадлежит клиент, всем не заблоченым - присваиваем группу dynamic (на которую мы ссылаемся в файле users)

authorize_group_check_query и authorize_group_reply_query - нужны обязательно если есть результат в group_membership_query. В них передаются дополнительные атрибуты NAS-у, но мне это не нужно, они тут для того чтоб были, без них freeradius ругается, можно забить в них что угодно.

остальные транзакции для аккаунтинга, не из этой темы )

     sql_user_name = "%{User-Name}"
 
authorize_check_query="SELECT ip_groups.ip_group_id,ip_groups.uname,'Cleartext-Password',ip_groups.upass,':=' FROM UTM5.ip_groups,UTM5.iptraffic_service_links,UTM5.service_links,UTM5.accounts \
WHERE ip_groups.uname='%{SQL-User-Name}' AND ip_groups.is_deleted='0' AND iptraffic_service_links.is_deleted='0' AND service_links.is_deleted='0' AND accounts.is_deleted='0' \
AND ip_groups.ip_group_id=iptraffic_service_links.ip_group_id AND iptraffic_service_links.id=service_links.id AND service_links.account_id=accounts.id \
UNION SELECT ip_groups.ip_group_id,ip_groups.uname,'Simultaneous-Use','1',':=' FROM UTM5.ip_groups,UTM5.iptraffic_service_links,UTM5.service_links,UTM5.accounts WHERE 
ip_groups.uname='%{SQL-User-Name}' \
AND ip_groups.is_deleted='0' AND iptraffic_service_links.is_deleted='0' AND service_links.is_deleted='0' AND accounts.is_deleted='0' \
AND ip_groups.ip_group_id=iptraffic_service_links.ip_group_id AND iptraffic_service_links.id=service_links.id AND service_links.account_id=accounts.id"
 
authorize_reply_query = "SELECT '','%{SQL-User-Name}','Framed-IP-Address',inet_ntoa( ip_groups.ip & 0xffffffff ) AS ip,'=' \
FROM UTM5.ip_groups,UTM5.accounts,UTM5.service_links,UTM5.iptraffic_service_links \
WHERE ip_groups.uname='%{SQL-User-Name}' AND ip_groups.is_deleted='0' AND iptraffic_service_links.is_deleted='0' AND service_links.is_deleted='0' AND accounts.is_deleted='0' \
AND inet_ntoa( ip_groups.ip & 0xffffffff ) LIKE '91.219.58.%' AND accounts.is_blocked='0' AND ip_groups.ip_group_id=iptraffic_service_links.ip_group_id \
AND iptraffic_service_links.id=service_links.id AND service_links.account_id=accounts.id"
 
group_membership_query = "SELECT 'dynamic' as GroupName \
FROM UTM5.ip_groups,UTM5.iptraffic_service_links,UTM5.service_links,UTM5.accounts \
WHERE ip_groups.uname='%{SQL-User-Name}' AND ip_groups.is_deleted='0' AND iptraffic_service_links.is_deleted='0' AND service_links.is_deleted='0' AND accounts.is_deleted='0' \
AND accounts.is_blocked='0' AND ip_groups.ip_group_id=iptraffic_service_links.ip_group_id AND iptraffic_service_links.id=service_links.id AND service_links.account_id=accounts.id"
 
authorize_group_check_query = "SELECT '', 'dynamic' as GroupName, 'Framed-Pool' as Attribute, 'dynamic' as Value, ':=' as Op FROM UTM5.ip_groups,UTM5.iptraffic_service_links,UTM5.service_links,UTM5.accounts \
WHERE ip_groups.uname='%{SQL-User-Name}' AND ip_groups.is_deleted='0' AND iptraffic_service_links.is_deleted='0' AND service_links.is_deleted='0' AND accounts.is_deleted='0' AND accounts.is_blocked!='0' \
AND ip_groups.ip_group_id=iptraffic_service_links.ip_group_id AND iptraffic_service_links.id=service_links.id AND  service_links.account_id=accounts.id"
 
authorize_group_reply_query = "SELECT '', 'dynamic' as GroupName, 'Framed-Pool' as Attribute, 'dynamic' as Value, ':=' as Op FROM UTM5.ip_groups,UTM5.iptraffic_service_links,UTM5.service_links,UTM5.accounts \
WHERE ip_groups.uname='%{SQL-User-Name}' AND ip_groups.is_deleted='0' AND iptraffic_service_links.is_deleted='0' AND service_links.is_deleted='0' AND accounts.is_deleted='0' AND accounts.is_blocked!='0' \
AND ip_groups.ip_group_id=iptraffic_service_links.ip_group_id AND iptraffic_service_links.id=service_links.id AND  service_links.account_id=accounts.id"
 
accounting_start_query = "INSERT INTO dhs_sessions_log (account_id, slink_id, recv_date,last_update_date,Framed_IP_Address, NAS_Port, Acct_Delay_Time, Acct_Session_Id, NAS_Port_Type, User_Name, Service_Type, Framed_Protocol, NAS_IP_Address, NAS_Id,Acct_Status_Type, Acct_Input_Packets, Acct_Input_Octets, Acct_Input_Gigawords, Acct_Output_Packets, Acct_Output_Octets, Acct_Output_Gigawords, Acct_Session_Time, Acct_Terminate_Cause, Called_Station_Id, Calling_Station_Id ) \
VALUES ( (select account_id from service_links where id in ( select id from iptraffic_service_links where is_deleted <> '1' and ip_group_id in \
( select ip_group_id from ip_groups where uname = '%{SQL-User-Name}' and is_deleted <> '1' ))), \
( select id from iptraffic_service_links where is_deleted <> '1' and ip_group_id in ( select ip_group_id from ip_groups where uname = '%{SQL-User-Name}' and is_deleted <> '1' )), '%l','0', \
(INET_ATON(CONCAT_WS('.',SUBSTRING_INDEX('%{Framed-IP-Address}','.',-1),SUBSTRING_INDEX(SUBSTRING_INDEX('%{Framed-IP-Address}','.',-2),'.',1),SUBSTRING_INDEX(SUBSTRING_INDEX('%{Framed-IP-Address}','.',2),'.',-1), \
SUBSTRING_INDEX('%{Framed-IP-Address}','.',1)))), '%{NAS-Port}', '%{Acct-Delay-Time}', '%{Acct-Session-Id}', '%{NAS-Port-Type}', '%{SQL-User-name}', '%{Service-Type}', '%{Framed-Protocol}', \
(INET_ATON(CONCAT_WS('.',SUBSTRING_INDEX('%{NAS-IP-Address}','.',-1), SUBSTRING_INDEX(SUBSTRING_INDEX('%{NAS-IP-Address}','.',-2),'.',1),SUBSTRING_INDEX(SUBSTRING_INDEX('%{NAS-IP-Address}','.',2),'.',-1), \
'%{%{Acct-Output-Octets}:-0}', '%{%{Acct-Output-Gigawords}:-0}', '%{%{Acct-Session-Time}:-0}', '%{%{Acct-Terminate-Cause}:-0}', '%{Called-Station-Id}','%i' );"
 
accounting_update_query = "UPDATE dhs_sessions_log SET last_update_date=UNIX_TIMESTAMP('%S'), Acct_Status_Type='3', Acct_Input_Packets ='%{Acct-Input-Packets}', Acct_Input_Octets ='%{Acct-Input-Octets}', Acct_Input_Gigawords ='%{Acct-Input-Gigawords}', Acct_Output_Packets ='%{Acct-Output-Packets}', Acct_Output_Octets ='%{Acct-Output-Octets}', Acct_Output_Gigawords ='%{Acct-Output-Gigawords}', Acct_Terminate_Cause='%{Acct-Terminate-Cause}', Acct_Session_Time ='%{Acct-Session-Time}' WHERE Acct_Session_Id = '%{Acct-Session-Id}'"
 
accounting_stop_query = "UPDATE dhs_sessions_log SET last_update_date=UNIX_TIMESTAMP('%S'), Acct_Status_Type='2', Acct_Input_Packets ='%{Acct-Input-Packets}', Acct_Input_Octets ='%{Acct-Input-Octets}', Acct_Input_Gigawords ='%{Acct-Input-Gigawords}', Acct_Output_Packets ='%{Acct-Output-Packets}', Acct_Output_Octets ='%{Acct-Output-Octets}', Acct_Output_Gigawords ='%{Acct-Output-Gigawords}', Acct_Terminate_Cause='%{Acct-Terminate-Cause}', Acct_Session_Time ='%{Acct-Session-Time}' WHERE Acct_Session_Id = '%{Acct-Session-Id}'"

В принципе с раздачей адресов из пулов на этом всё. должно всё красиво работать. Если кому то не нравится логика, то можно переделать наоборот, всем изначально из внешнего пула, а заблокированным присваиваем другую группу и другой пул. те же яйца, только наоборот )) мне после 3-ех дней секса с фрирадиусом уже было лень что-то править, работает и слава богу ))

Далее нужно было прибивать каждый выданный внешний динамический ip клиенту в биллинге, иначе трафик по netflow считался бы криво. вот тут и пришел на помощь скрипт на перле wingman-а с форума, как всегда без напильника не обошлось, так как пришлось его скрещивать с urfaclient на php.
Напомню, то что для этого нужно было подключить в /etc/freeradius/sites-enabled/default в секции аккаунтинга модуль perl, с подключением этого модуля тоже прошло не всё гладко, freeradius ни как не хотел с ним дружить из за несовпадения версий перла
помогло это:

export LD_PRELOAD=/usr/lib/libperl.so.5.10

на будущее засунул эту строку в скрипт стартующий freeradius, может вам и не понадобится совсем.

/etc/freeradius/modules/perl

perl {
     module = ${confdir}/rad.pl
     func_stop_accounting = accounting_stop
}

rad.pl - это переделаный скрипт example.pl wingman-ом, ну а я уже под себя подогнал
на данный момент он подогнан под ourfa
но можно также использовать с URFA-client на php, но я закомментил эти строчки.
если что переставите комментарии на ourfa

use threads;
use threads::shared;
use strict;
use DBI;
use vars qw(%RAD_REQUEST %RAD_REPLY %RAD_CHECK);
use Data::Dumper;
use constant    RLM_MODULE_REJECT=>    0;#  /* immediately reject the request */
use constant    RLM_MODULE_FAIL=>      1;#  /* module failed, don't reply */
use constant    RLM_MODULE_OK=>        2;#  /* the module is OK, continue */
use constant    RLM_MODULE_HANDLED=>   3;#  /* the module handled the request, so stop. */
use constant    RLM_MODULE_INVALID=>   4;#  /* the module considers the request invalid. */
use constant    RLM_MODULE_USERLOCK=>  5;#  /* reject the request (user is locked out) */
use constant    RLM_MODULE_NOTFOUND=>  6;#  /* user not found */
use constant    RLM_MODULE_NOOP=>      7;#  /* module succeeded without doing anything */
use constant    RLM_MODULE_UPDATED=>   8;#  /* OK (pairs modified) */
use constant    RLM_MODULE_NUMCODES=>  9;#  /* How many return codes there are */
sub accounting {
my $DBH = DBI->connect('DBI:mysql:UTM5:localhost', 'root', 'password');
$DBH->{mysql_auto_reconnect}=1;
my $sth = "";
my $URFA_CALL_DEL = "/netup/utm5/urfa/urfaphp/URFA_del_ipgrp.php";
my $URFA_CALL_ADD = "/netup/utm5/urfa/urfaphp/URFA_add_service.php";
my $URFA_CALL = "/usr/bin/ourfa_client -S rsa_cert";
        # Тут проверяется, не из диапазона ли реальных внешников выдан IP
        if($RAD_REQUEST{'Acct-Status-Type'} eq 'Start' && not($RAD_REQUEST{'Framed-IP-Address'} =~ m/91.219.58.(xxx|yyy|zzz|zxc|cxz).[0-9]+/i) )
        {
#               `echo "123" >> /etc/freeradius/log`;
                my $fip = $RAD_REQUEST{'Framed-IP-Address'};
                my $uname = $RAD_REQUEST{'SQL-User-Name'};
                my $sessid = $RAD_REQUEST{'Acct-Session-Id'};
                # 1. Find ip-group 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 (0xffffffff & 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];
                                `echo "del ip=$fip group_id=$ipg_id" >> /etc/freeradius/log`;
#                                `php $URFA_CALL_DEL $ipg_id $fip`;
                                `$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];
                        `echo "add ip=$fip slink=$slink_id service_id=$service_id uid=$uid account=$aid dis_period=$did" >> /etc/freeradius/log`;
                        `$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`;
#                        `php $URFA_CALL_ADD $fip $slink_id $service_id $uid $aid $did`;
                }
        }
        return RLM_MODULE_OK;
}
sub accounting_stop {
my $DBH = DBI->connect('DBI:mysql:UTM5:localhost', 'root', 'password');
$DBH->{mysql_auto_reconnect}=1;
my $sth = "";
my $URFA_CALL_DEL = "/netup/utm5/urfa/urfaphp/URFA_del_ipgrp.php";
my $URFA_CALL_ADD = "/netup/utm5/urfa/urfaphp/URFA_add_service.php";
my $URFA_CALL = "/usr/bin/ourfa_client -S rsa_cert"; 
 
        if($RAD_REQUEST{'Acct-Status-Type'} eq 'Stop')
        {
                my $fip = $RAD_REQUEST{'Framed-IP-Address'};
                my $uname = $RAD_REQUEST{'SQL-User-Name'};
                my $sessid = $RAD_REQUEST{'Acct-Session-Id'};
                # 1. Find ip-group 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 (0xffffffff & 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];
                                `echo "Acc-Stop del ip=$fip group_id=$ipg_id login=$uname" >> /etc/freeradius/log`;
#                                `php $URFA_CALL_DEL $ipg_id $fip`;
                                `$URFA_CALL -a iphome_delete_from_ipgroup -ip_group_id $ipg_id -ip_address $fip`;
                        } 
                }
 
        }
 
        return RLM_MODULE_OK;
}

привожу xml для ourfa

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>

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>

введены также 2 переменные ссылающиеся на 2 других скрипта на php ))
публикую и их

URFA_del_ipgrp.php

<?php
require_once("URFAClient.php");
$UTM_LOGIN="логин к ядру";
$UTM_PWD="пароль к логину";
$UTM_IP="192.168.201.2";
$UTM_PORT="11758";
$UTM_SSL=0;
 
try {
    $urfa_admin = new URFAClient_Admin($UTM_LOGIN, $UTM_PWD,$UTM_IP,$UTM_PORT, $UTM_SSL);
}
catch (Exception $exception) {
    echo "Error in line ", $exception->getLine();
    echo $exception->getMessage();
}
 
$id = $argv[1];
$ip = $argv[2];
$mask = "255.255.255.255";
 
$data = $urfa_admin->rpcf_delete_from_ipgroup_by_ipgroup($id,$ip,$mask);
 
?>

URFA_add_service.php

<?php
require_once("URFAClient.php");
$UTM_LOGIN="";
$UTM_PWD="";
$UTM_IP="192.168.201.2";
$UTM_PORT="11758";
$UTM_SSL=0;
 
try {
    $urfa_admin = new URFAClient_Admin($UTM_LOGIN, $UTM_PWD,$UTM_IP,$UTM_PORT, $UTM_SSL);
}
catch (Exception $exception) {
    echo "Error in line ", $exception->getLine();
    echo $exception->getMessage();
}
 
$ip     = $argv[1];
$slink_id       = $argv[2];
$service_id     = $argv[3];
$uid    = $argv[4];
$aid    = $argv[5];
$did    = $argv[6];
$payment_start_date = time();
 
$service = array();
$service['user_id']           = $uid;
$service['account_id']        = $aid;
$service['discount_period_id']= $did;
$service['service_id']        = $service_id;
$service['slink_id']          = $slink_id;
$service['return_type']       = 'integer_return';
$service['service_type']      = '3';
$service['ip_groups'][0]['ip_address'] = $ip;
$service['ip_groups'][0]['mask']       = '255.255.255.255';
$service['ip_groups'][0]['ip_not_vpn'] = 0;
$service['is_blocked']        = 0;
$service['start_date']        = $payment_start_date;
$service['expire_date']       = mktime(0,0,0,1,1,2020); // 2000000000;
$service['unabon']            = 0;
$service['unprepay']          = 0;
$service['tariff_link_id']    = 0;
//$service['tariff_link_id']    = $tariff_link_id['tariff_link_id'];
 
$added_srv = $urfa_admin->rpcf_add_service_to_user($service);
?>

если не знакомы с urfaclient на php, то его можно найти тут
http://wiki.flintnet.ru/doku.php?id=urfaclient_php
ну и почитать можно тут
http://www.netup.ru/phpbb/viewtopic.php?t=6402&highlight=urfaclient

ну и последнее, чтоб исключить одновременные подключения я решил юзать radutmp.
пробовал sql, но что-то мне не понравилось глючность этого метода с постоянными косяками.
решил перейти на radutmp, пока что наблюдаю за его поведением.

Прошло пару лет и вот я наткнулся на один баг.
У меня пользователи pppoe начали получать виртуальные ip-адреса из подсетей 10.67.15.0/24 и 10.67.16.0/24.
При чем начали получать не все, а только некоторые и в основном с одного только NAS-а.
Это служебные пулы pppoe-сервера по умолчанию. Если радиус не назначил пользователю ip-адрес то ему по умолчанию назначает pppoe-server.
Ну так вот. Начал я разбираться почему у меня freeradius не назначает клиентам адреса из динамического пула.
Дебаг выдал вот такую трактовку:

Login OK: [volkov/<CHAP-Password>] (from client ngtu2 port 535 cli E8:11:32:19:61:75)
# Executing section post-auth from file /etc/freeradius/sites-enabled/default
+- entering group post-auth {...}
[dynamic] expand: %{NAS-IP-Address} %{NAS-Port} -> 192.168.201.10 535
[dynamic] MD5 on 'key' directive maps to: 273a4119bc02ec2494476f16e6800829
[dynamic] Searching for an entry for key: '273a4119bc02ec2494476f16e6800829'
[dynamic] No available ip addresses in pool.
++[dynamic] returns notfound
++[main_pool] returns noop
++[exec] returns noop
Sending Access-Accept of id 233 to 192.168.201.10 port 57225
Framed-Protocol = PPP
Framed-Compression = Van-Jacobson-TCP-IP
Finished request 2340.
Going to the next request

Ключевая строка
[dynamic] No available ip addresses in pool.

говорит о том что в пуле закончились адреса (((
вот ведь незадача, потому что в онлайне сидит намного меньше, чем в пуле и по идее должно хватать.
оказывается есть хорошая утилита для работы с пулом и называется она rlm_ippool_tool.
По идее можно не заморачиваться и ваще грохнуть базу с пулом, но это ведет к других не очень хорошим последствиям.
Дело в том, что база ippool формируется на основании вычисления MD5 от "IP-NAS NAS-Port".
и заполняется вот такими строками:
# rlm_ippool_tool -v /etc/freeradius/db2.ippool /etc/freeradius/db2.ipindex | tail -n 3
KEY: '4f902b618e3e7fdab46c5c593cb155c3' - ipaddr:176.115.148.115 active:1 cli:0 num:1
KEY: '5fd87eb252c5875d839ef47fd3df9baf' - ipaddr:176.115.151.250 active:0 cli:0 num:0
KEY: '1e1ed8bab73fb34c790515c91bb832ed' - ipaddr:176.115.146.157 active:1 cli:0 num:1

фрирадиус вычисляет каждый раз MD5 и ищет совпадение в базе, если нашел присваивает клиенту ip из базы, если не нашел - добавляют новую запись и делает её активной.
Кароче говоря, если вдруг какой то NAS падает и на другие будет больше нагрузка, то количество вариантов "IP-NAS NAS-Port" увеличивается, поэтому и увеличивается количество активных записей в пуле, и в один прекрасный момент их перестает хватать. Есть самый простой способ - это просто удалить эти базы, но тогда пользователям радиусу не с чем будет сверяться и он начнет выдавать ip-адреса, которые уже кому то выдал, а это не есть гуд. Как вариант можно кильнуть всех пользователей при этом, но это как-то не гуманно и создаст большую нагрузку на радиус, когда все станут подключаться одновременно, а если пользователей несколько тысяч, то это ваще слишком топорно.
Поэтому я написал скрипт, который засунул в крон раз в месяц, чтоб он иногда чистил этот ippool.
К сожалению утилита rlm_ippool_tool не умеет удалять записи из базы выборочно, а только все разом, поэтому пришлось извернуться с помощью утилиты radwho. Суть в том, что сначала полностью очищаем базу ippool, а потом на основании radwho добавляет в базу ippool нужные записи.
#!/bin/bash
 
# скрипт очищает пул динамических адресов ippool
# выдирает из базы radutmp все текущие записи с пулом динамических ip-адресов
# и вставляет их в очищенную базу ippool
# рекомендуется запускать его периодически например раз в неделю или месяц для того чтобы не закончились записи в ippool
 
db_ippool=/etc/freeradius/db2.ippool
db_ipindex=/etc/freeradius/db2.ipindex
ip_pool=176.115
 
/usr/bin/rlm_ippool_tool -r $db_ippool $db_ipindex
radwho_db=(`/usr/bin/radwho -r | grep $ip_pool | awk -F, {'print $4","$6","$7'} | sed 's/^S//'`)
count_radwho_db=${#radwho_db[@]}
for (( i = 0 ; i < $count_radwho_db ; i++ ))
do
  if [ "`radwho | grep ${radwho_db[$i]}`" == "" ];then
    nas=`echo ${radwho_db[$i]} | awk -F, {'print $2'}`
    nasport=`echo ${radwho_db[$i]} | awk -F, {'print $1'}`
    ip=`echo ${radwho_db[$i]} | awk -F, {'print $3'}`
    /usr/bin/rlm_ippool_tool -n $db_ippool $db_ipindex $ip $nas $nasport
  fi
done

На этом всё. Всем спасибо. благодарю за внимание. :)

Комментарии

Здравствуйте! Настроил все

Здравствуйте! Настроил все как написано в статье. Возникла некоторая проблема. При авторизации в логах Tue Apr 24 06:21:38 2012 : Auth: Login incorrect: [vasia/] (from client 192.168.0.4 port 13 cli 54e6fccb7f19 / 54:e6:fc:cb:7f:19]. Не могу понять в чем проблема.

[vasia/no User-Password

[vasia/no User-Password attribute]
а до этого всё было ок?
надо смотреть дебаг типа freeradius -X
но тут уже проблема у вас скорей всего в секции authenticate {}
скорее всего там включены какие нибудь неизвестные протоколы, которых нет в dictionary
либо может в подключении используете какой нить ms-chap

я отстраиваю freeradius+utm5

я отстраиваю freeradius+utm5 впервые. Дебаг выдает
WARNING: Empty authorize section. Using default return values.
ERROR: No authenticate method (Auth-Type) found for the request: Rejecting the user
Failed to authenticate the user.

если первый раз, то точно ли

если первый раз, то точно ли нужен мной описанный метод? расскажите что имеете и что нужно. а вообще есть не мало инфы по этой теме на форуме netup.ru

После долгого курения манов,

После долгого курения манов, разобрался с данной проблемой. Спасибо автору статьи за помощь в решении данной проблемы.

Существует два пула адресов

Существует два пула адресов dynamic - белый адреса, dynamic2 - серые адреса NAT. Каким образом можно решить задачу при окончании пула адресов dynamic, начать выдавать пул адресов dynamic2?

хе, вот это вопросец, у меня

хе, вот это вопросец, у меня бывало заканчивались адреса в пуле, тогда они начинались выдаваться pppoe-сервером типа 10.68.*.*? это потому что не было условий больше никаких видимо. честно сказать не знаю возможно ли, нужно экспериментировать с условиями в файле users или же может помочь
update control{ Pool-Name = 'main_pool' } в секции authorize, может он будет по дефолту выдавать от туда. а можно в pppoe-сервере назначит нужный пул адресов, в случае обломов во фрирадиусе pppoe-сервер назначит из своего пула.
ну это догадки, нужно экспериментировать.
а вообще лучше мне кажется не стоит выдавать белые адреса, если их может не хватить на всех клиентов, а заказать еще дополнительно пул белых )) это самое правильное решение ))

Реализовали у себя выдачу IP

Реализовали у себя выдачу IP адресов из пулов (серые, белые, а также IP, при отрицательном балансе). Теперь столкнулись с другой проблемой: перестают бегать keep-alive. Уже и версию Freeradius меняли, итог тот же.
Может кто сталкивался с такой проблемой?

не замечал. а как

не замечал. а как проявляется?

ну с пакетами разобрались,

ну с пакетами разобрались, теперь MySQL ложиться, не успевает записывать запросы в базу. 800 онлайна на 2-х серверах, каждую минуту по запросу и все, видимо придется переходить на PostgreSQL

может мускул оттюнить? у меня

может мускул оттюнить?
у меня 1200 клиентов в онлайне по вечерам с 3-ех NAS-ов.
но я не считаю трафик. netflow выключил.
потому что в 009 баг мощный, что он там валит базу если считаешь трафик.

мускул оттюнили, но не

мускул оттюнили, но не помогает, а общий онлайн 4500-5000, УТМ 007