юзеру выделился IP адрес.
он попользовался, но вышел до запуска traffic2sql (обычное явление)
зашел новый юзер и получил тот же адрес
traffic2sql наконец запустилась и тупо записала весь трафик, скачанный первым юзером с момента своего последнего запуска новому юзеру, владельцу IP. Второй юзер платит за трафик первого.
Неприятно, правда? Пока что у этой проблемы одно решение: выделять клиентам статические IP адреса. Для кого-то может это и решение, но при 3-4 тысячах абонентов это теряет смысл и приходится напильничком Абиллс подравнивать, а именно не допустить выдачу освободившегося адреса другому юзеру. Может кому-то пригодится. Полный патч не даю, т.к. достаточно изменений, не имеющих отношения к этому.
1) заводим таблицу:
2) В Abills/mysql/Acct.pm в функции accounting(), ближе к концу обработки RADIUS Stop пакета в этом условии:
CREATE TABLE `dirty_ip` (
`framed_ip_address` varchar(15) NOT NULL,
`inserted_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`uid` int(10) unsigned NOT NULL,
PRIMARY KEY (`framed_ip_address`,`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
т.е. непосредственно _перед_ удалением из таблицы онлайнов:# Stop status
elsif ($acct_status_type == 2) {
добавляем:$self->query($db, "DELETE FROM dv_calls WHERE (acct_session_id=\"$RAD->{ACCT_SESSION_ID}\" .....
Первый запрос подчищает "грязные" (т.е. освобожденные юзером за последнюю минуту) IP, которые в базе уже больше минуты. traffic2sql у меня запускается раз в минуту, и больше минуты эти записи не нужны, поэтому необходимо периодически удалять старые.$self->query($db, "DELETE FROM dirty_ip WHERE ABS(TIMESTAMPDIFF(minute, DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i'), DATE_FORMAT(inserted_at, '%Y-%m-%d %H:%i'))) > 1;", 'do');
$self->query($db, "INSERT INTO dirty_ip SET framed_ip_address='$RAD->{FRAMED_IP_ADDRESS}',uid='$self->{UID}'", 'do');
Второй запрос помечает IP как "грязный", чтобы до начала следующей минуты (когда запустится traffic2sql) он никому больше кроме юзера, освободившего его не выделялся.
3) В Abills/mysql/Auth.pm в функции get_ip() ближе к середине, после этого куска добавляем выделенное жирным:
Примечание: код в цитате выше был изменен Fri Oct 31 06:40:33 UTC 2008$self->query($db, "SELECT c.framed_ip_address
FROM (dv_calls c, nas_ippools np)
WHERE c.nas_id=np.nas_id AND np.pool_id in ( $used_pools ) AND (status=1 or status>=3);");
$list = $self->{list};
if ($attr->{TP_IPPOOL}) {
$self->query($db, "SELECT INET_ATON(framed_ip_address), uid FROM dirty_ip dip JOIN ippools ipp ON (ip=INET_ATON(framed_ip_address)) WHERE ipp.id='$attr->{TP_IPPOOL}' AND ABS(TIMESTAMPDIFF(minute, DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i'), DATE_FORMAT(inserted_at, '%Y-%m-%d %H:%i'))) < 2 ORDER BY inserted_at DESC;");
} else {
$self->query($db, "SELECT INET_ATON(framed_ip_address), uid FROM dirty_ip dip JOIN ippools ipp ON (ip=INET_ATON(framed_ip_address)) JOIN nas_ippools nasipp ON (ipp.id=nasipp.pool_id) WHERE nasipp.nas_id='$nas_num' AND ABS(TIMESTAMPDIFF(minute, DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i'), DATE_FORMAT(inserted_at, '%Y-%m-%d %H:%i'))) < 2 ORDER BY inserted_at DESC;");
}
my $dirty_list = $self->{list};
# try to reuse user's last assigned IP within current time window (which is how often traffic2sql runs)
foreach my $ip (@$dirty_list) {
if ($ip->[1] == $self->{UID}) {
return int2ip($ip->[0]);
}
}
Это узнает текущий список грязных IP адресов и возвращает один из них только если данный юзер сам освободил его менее минуты назад. Получается, что юзер получит тот же самый адрес при переподключении и не сможет избежать тарификации за трафик, если раскусит существующий алгоритм работы Абиллса.
Чуть ниже меняем этот кусок (опять таки жирное добавлено мной):
Это позволит при выделении динамичного адреса клиенту исключить не только тех, кто уже в онлайне, но и грязных (т.е. тех, кто были в онлайне вплоть до одной минуты назад).for(my $i=0; $i<=$#pools_arr; $i++) {
%pool = %{ $pools_arr[0] };
foreach my $ip (@$list, @$dirty_list)
if(exists($pool{$ip->[0]})) {
delete($pool{$ip->[0]});
$self->{USED_IPS}++;
}
Вот в общем-то и все. Таблица будет жить своей жизнью и сама себя обслуживать.