Корректный подсчет Ipn трафика для юзеров с динам. адресами

Ответить
chtito2
Сообщения: 479
Зарегистрирован: Чт апр 17, 2008 5:26 pm

Корректный подсчет Ipn трафика для юзеров с динам. адресами

Сообщение chtito2 »

Рассмотрим сценарий:
юзеру выделился IP адрес.
он попользовался, но вышел до запуска traffic2sql (обычное явление)
зашел новый юзер и получил тот же адрес
traffic2sql наконец запустилась и тупо записала весь трафик, скачанный первым юзером с момента своего последнего запуска новому юзеру, владельцу IP. Второй юзер платит за трафик первого.


Неприятно, правда? Пока что у этой проблемы одно решение: выделять клиентам статические IP адреса. Для кого-то может это и решение, но при 3-4 тысячах абонентов это теряет смысл и приходится напильничком Абиллс подравнивать, а именно не допустить выдачу освободившегося адреса другому юзеру. Может кому-то пригодится. Полный патч не даю, т.к. достаточно изменений, не имеющих отношения к этому.

1) заводим таблицу:

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;
2) В Abills/mysql/Acct.pm в функции accounting(), ближе к концу обработки RADIUS Stop пакета в этом условии:
# Stop status
elsif ($acct_status_type == 2) {
т.е. непосредственно _перед_ удалением из таблицы онлайнов:
$self->query($db, "DELETE FROM dv_calls WHERE (acct_session_id=\"$RAD->{ACCT_SESSION_ID}\" .....
добавляем:
$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 у меня запускается раз в минуту, и больше минуты эти записи не нужны, поэтому необходимо периодически удалять старые.

Второй запрос помечает IP как "грязный", чтобы до начала следующей минуты (когда запустится traffic2sql) он никому больше кроме юзера, освободившего его не выделялся.

3) В Abills/mysql/Auth.pm в функции get_ip() ближе к середине, после этого куска добавляем выделенное жирным:
$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]);
}
}
Примечание: код в цитате выше был изменен Fri Oct 31 06:40:33 UTC 2008

Это узнает текущий список грязных 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}++;
}
Это позволит при выделении динамичного адреса клиенту исключить не только тех, кто уже в онлайне, но и грязных (т.е. тех, кто были в онлайне вплоть до одной минуты назад).

Вот в общем-то и все. Таблица будет жить своей жизнью и сама себя обслуживать.

Ответить