Биллинговая система Nodeny
22 Ноября 2024, 17:51:35 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
Новости: Прекращена поддержка версии Nodeny 49
 
   Начало   Помощь Поиск Войти Регистрация  
Страниц: 1 2 3 [4]
  Печать  
Автор Тема: Deadlock-и, дубликаты ip и прочая хрень  (Прочитано 21879 раз)
fet4
Старожил
****

Карма: 2
Offline Offline

Сообщений: 326


Просмотр профиля Email
« Ответ #45 : 01 Марта 2018, 10:16:31 »

Цитировать
если клиента нет в бд, отвечает accept, но вместо framed ip выдавай framed pool, чтобы accel выдавал сам гостевой ип из своего пула
Да почитал вики accel можно сгородить гостевой пул у него по Framed-Pool. Но хотелось бы рулить ip с биллинга.

Цитировать
Тут смысл не в том, чтоб он был освобожден четко во время, а смысл в том, чтобы всегда хватало свободных ip перед тем как ip запросит подключившийся
А ну да, т.е. возможно не сразу они очистятся главное что бы был запас но с этим проблем нет, у меня выдаются в динамике серые.

Цитировать
Не. Тогда нужен будет реконнект после того как абон зарегается. Я думаю, можно будет подправить процедуры, чтобы у выданного гостю ip в пуле устанавливалось поле release. Сегодня потестирую и напишу.
Из-за реконнекта и не хочется городить отдельный пул для гостей, это доп нагрузка сразу вылезет по звонкам тп.
Жду Стас.
Записан
Efendy
Администратор
Спец
*****

Карма: 138
Offline Offline

Сообщений: 4790



Просмотр профиля
« Ответ #46 : 01 Марта 2018, 14:20:43 »

Код:
DROP FUNCTION IF EXISTS `get_ip_by_tag`;
DELIMITER $$
CREATE FUNCTION `get_ip_by_tag`( user_id INTEGER UNSIGNED, tag VARCHAR(64) )
    RETURNS VARCHAR(15) NO SQL
BEGIN
    DECLARE user_ip VARCHAR(15);
    DECLARE real_ip VARCHAR(15) DEFAULT 0;
    DECLARE row_cnt INTEGER;
    DECLARE ip_id INTEGER;
    DECLARE tries INTEGER DEFAULT 30;
    DECLARE id_min INTEGER;
    DECLARE id_max INTEGER;

    SELECT INET_NTOA(ip) INTO user_ip FROM ip_pool
        WHERE uid = user_id AND type='static' LIMIT 1;
    IF( user_ip IS NOT NULL ) THEN RETURN user_ip; END IF;

    SELECT 1 INTO real_ip FROM users_services WHERE uid = user_id AND tags LIKE '%,realip,%';

    SELECT id, INET_NTOA(ip) INTO ip_id, user_ip FROM ip_pool
        WHERE uid = user_id AND type = 'dynamic' AND realip = IF(real_ip>0,1,0)
            AND tags LIKE CONCAT('%,', tag, ',%')
        LIMIT 1;

    IF( ip_id IS NOT NULL) THEN
        UPDATE ip_pool SET `release` = UNIX_TIMESTAMP() + 3600
            WHERE id = ip_id AND uid = user_id;
        SELECT ROW_COUNT() INTO row_cnt;
        IF( row_cnt > 0 ) THEN RETURN user_ip; END IF;
    END IF;

    SELECT MAX(id), MIN(id) INTO id_max, id_min
        FROM ip_pool
        WHERE type = 'dynamic' AND realip = IF(real_ip>0,1,0)
            AND tags LIKE CONCAT('%,', tag, ',%');

    sel_ip: WHILE tries > 0 DO
        SELECT id, INET_NTOA(ip) INTO ip_id, user_ip
            FROM ip_pool
            WHERE uid = 0
                AND id >= (CEIL(RAND() * (id_max - id_min)) + id_min)
                AND id <= id_max
                AND `release` < UNIX_TIMESTAMP()
                AND tags LIKE CONCAT('%,', tag, ',%')
                LIMIT 1;
        IF( user_ip IS NOT NULL) THEN
            UPDATE ip_pool SET uid = user_id, `release` = UNIX_TIMESTAMP() + 3600
                WHERE id = ip_id AND uid = 0 AND `release` < UNIX_TIMESTAMP();
            SELECT ROW_COUNT() INTO row_cnt;
            IF( row_cnt > 0 ) THEN RETURN user_ip; END IF;
            SET tries = tries - 5;
        END IF;
        SET tries = tries - 1;
    END WHILE;

END$$
DELIMITER ;

Код:
DROP PROCEDURE IF EXISTS `radreply_ipoe`;
DELIMITER $$
CREATE PROCEDURE `radreply_ipoe`(IN `login` VARCHAR(64), IN `tag` VARCHAR(64))
BEGIN
    DECLARE rad_mac VARCHAR(12);
    DECLARE rad_dev_mac VARCHAR(12);
    DECLARE rad_port VARCHAR(12);
    DECLARE usr_id INT;
    DECLARE usr_ip VARCHAR(15);
    DECLARE usr_onecon INT;
    DECLARE usr_onedev INT;
    
    SELECT SUBSTRING_INDEX(login, '-', 1) INTO rad_mac;
    SELECT SUBSTRING_INDEX(login, '-', -1) INTO rad_port;
    SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(login, '-', 2), '-', -1) INTO rad_dev_mac;
    
    SELECT uid, oneconnect INTO usr_id, usr_onecon FROM mac_uid
        WHERE device_mac=rad_dev_mac
            AND device_port=rad_port
            AND (mac=rad_mac OR (oneconnect>0 AND device_mac<>''))
        LIMIT 1;
                
    IF usr_id IS NOT NULL AND usr_id > 0 THEN
    
        SELECT get_ip_by_tag(usr_id, tag) INTO usr_ip;
        UPDATE mac_uid SET ip=0 WHERE ip=INET_ATON(usr_ip) AND uid<>usr_id;
        UPDATE mac_uid SET ip=INET_ATON(usr_ip), time=UNIX_TIMESTAMP() WHERE uid=usr_id;
        
        IF usr_onecon > 0 OR usr_onedev > 0 THEN
            UPDATE mac_uid SET mac=NULL WHERE mac=rad_mac AND oneconnect=0;
            UPDATE mac_uid SET mac=rad_mac, device_port=rad_port WHERE device_mac=rad_dev_mac
                AND (device_port=rad_port AND oneconnect>0);
        END IF;
        
    ELSE

        UPDATE mac_uid SET mac=NULL WHERE mac=rad_mac AND oneconnect>0;

        START TRANSACTION;
        SELECT INET_NTOA(ip) INTO usr_ip FROM ip_pool
            WHERE uid=0 AND type='dynamic'
                AND tags LIKE CONCAT('%,', tag ,',%')
                AND `release` < UNIX_TIMESTAMP()
            ORDER BY RAND() LIMIT 1 FOR UPDATE;

        INSERT INTO mac_uid VALUES(
            NULL, rad_mac, INET_ATON(usr_ip), 0, UNIX_TIMESTAMP(), rad_dev_mac, rad_port, 0)
        ON DUPLICATE KEY
            UPDATE ip=IF(ip>0 AND device_mac=rad_dev_mac AND device_port=rad_port, ip, INET_ATON(usr_ip)),
                uid=0, device_mac=rad_dev_mac, device_port=rad_port, time=UNIX_TIMESTAMP();
        COMMIT;
        
        SELECT INET_NTOA(ip) INTO usr_ip FROM mac_uid
            WHERE mac=rad_mac AND device_mac=rad_dev_mac AND device_port=rad_port;

        UPDATE ip_pool SET `release` = UNIX_TIMESTAMP() + 3600
            WHERE ip = INET_ATON(usr_ip);
            
    END IF;
    
    SELECT NULL, login, 'Framed-IP-Address', usr_ip, '=';

END$$
DELIMITER ;

  • В get_ip_by_tag я сделал так, чтобы не захватывал свободные ip, в которых время релиза еще не настало
  • В radreply_ipoe я выкинул onedevice, потому что не понимаю что это. Давай избавляться от него ибо логика итак пиздец какая получается. Расскажи, что оно означает, может реализуем как-нибудь иначе
  • Обрати внимание, что я не проверяю выдан ли данный ip кому-то по таблице mac_uid - логику перенес на поле release в таблице ip_pool
« Последнее редактирование: 01 Марта 2018, 15:06:19 от Efendy » Записан
fet4
Старожил
****

Карма: 2
Offline Offline

Сообщений: 326


Просмотр профиля Email
« Ответ #47 : 01 Марта 2018, 15:58:56 »

Ну и в radupdate наверно нужно тоже добавить, дабы не вышел срок годности ip?
Код:
UPDATE ip_pool SET `release` = UNIX_TIMESTAMP() + 3600
            WHERE ip = INET_ATON(usr_ip);

onedevice это тоже что и oneconnect, за исключение того что проверятся только устройство

Цитировать
Обрати внимание, что я не проверяю выдан ли данный ip кому-то по таблице mac_uid - логику перенес на поле release в таблице ip_pool
годная логика, если uid = 0 AND `release` < UNIX_TIMESTAMP() считаем что ip свободен.
Записан
Efendy
Администратор
Спец
*****

Карма: 138
Offline Offline

Сообщений: 4790



Просмотр профиля
« Ответ #48 : 01 Марта 2018, 16:12:08 »

Только апдейтить надо в случае если user_id is null т.к в set_auth есть уже такой апдейт
Записан
fet4
Старожил
****

Карма: 2
Offline Offline

Сообщений: 326


Просмотр профиля Email
« Ответ #49 : 01 Марта 2018, 16:16:19 »

Только апдейтить надо в случае если user_id is null т.к в set_auth есть уже такой апдейт
Нуда или вынести его от туда в radupdate
Записан
Efendy
Администратор
Спец
*****

Карма: 138
Offline Offline

Сообщений: 4790



Просмотр профиля
« Ответ #50 : 01 Марта 2018, 18:12:23 »

Только апдейтить надо в случае если user_id is null т.к в set_auth есть уже такой апдейт
Нуда или вынести его от туда в radupdate
Не, выносить не надо. Пусть get_ip и set_auth будут у всех одинаковыми. Хоть какая-то стабильность
Записан
NodenY45
NoDeny
Старожил
*

Карма: 2
Offline Offline

Сообщений: 365


Просмотр профиля
« Ответ #51 : 14 Ноября 2018, 20:37:07 »

Время от времени я вижу жалобы на то, что бывают разного рода глюки с выдачей динамических ip. Я тут хорошо покрутил все и кажется понял в чем проблема. На самом деле основные проблемы 2:

1) deadlock-и в mysql - это когда некоторые запросы блокируют друг-друга и из-за этого не дают выполняться другим
2) бывает, что ip на nas-е не соответствует ip, которое светится в биллинге

Насчет deadlock-ов. Причин тут несколько:
1) сука тупой mysql. Вот реально гадина, я тебя всегда защищал, но ты впадаешь в лок на запросах, которые ну никак логически не должны приводить к такому
2) большая нагрузка

Начнем с нагрузки. Старайтесь выносить такие модули ядра как сбор трафика и заглушка на другой сервер. Особенно заглушка ибо на нее бывает льется столько трафика, что отжирает 100% проца. Если нет возможности вынести - хотя бы ограничьте количество сессий с одного ip на заглушку.

Что касается mysql. Мне пришлось кординально переделать процедуру get_ip. Я сделал такие 2 серьезных изменения:
1) сделал код примитивным и очень избыточным, что бы все sql были максимально нежными. Процедура стала выглядеть тупо, не показывайте ее профи))
2) основной затык был в выборе незанятого динамического ip. Дело в том, что mysql при большом количестве запросов выдавало одну и туже свободную запись, в этом проблемы нет, это разруливалось, но из-за такой коллизии сильно росла нагрузка. Я бы мог сделать выборку и из нее выбрать случайную строку, но mysql в этом случае создает временную таблицу, а это тоже плохо. Поэтому я поступил хитро: в таблице ip (ip_pool) я физически перегруппировую записи по типу, тегам и т.д. Для этого я создал процедуру normalize_ippool:

Код:
DROP FUNCTION IF EXISTS `normalize_ippool`;
DELIMITER $$
CREATE FUNCTION `normalize_ippool` ( )
    RETURNS TINYINT NO SQL
BEGIN
    DECLARE mid INTEGER UNSIGNED;

    CREATE TEMPORARY TABLE temp_ip_pool(
        id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
        ip_id BIGINT UNSIGNED NOT NULL,
        PRIMARY KEY (id)
    );

    SELECT MAX(id) into mid FROM ip_pool;
    UPDATE ip_pool SET id = id + mid;
    INSERT temp_ip_pool (SELECT NULL, id FROM ip_pool ORDER BY realip, type, tags);
    UPDATE ip_pool i JOIN temp_ip_pool t ON i.id = t.ip_id SET i.id = t.id;
    DROP TEMPORARY TABLE IF EXISTS temp_ip_pool;

    RETURN 1;
END$$
DELIMITER ;

Вызов этой функции я добавлю в админку при изменении любого ip/пула.

Благодаря этому в get_ip я сразу беру рандомную строку без предварительной выборки, зная только начальный и конечный id нужной группы.

Конечно, вам эта инфа особо не интересна, это я так для себя чтоб сохранилось в истории, ну и может теоретически кому-то понадобится.

Насчет дубликатов ip. Имеется ввиду такая ситуация: есть nas и есть сервер с биллингом. Сервер перезагружается, загружается больше 6 минут, запускает модуль ядра auth, который сразу освобождает все ip по таймауту. Он-то думает, что раз 6 минут не было аккаунтинга по данным ip - значит все сессии на насе кильнулись. Он же не знает, что просто небыло коннекта с nas. А на nas сесси-то висят, а биллинг считает ip свободными и выдает другим абонам. Когда я давно придумал схему с освобождением ip, я подумал, что сервер стартанет за пару минут. Кроме того, я не предусмотрел, что может быть разрыв канала больше чем 6 минут. Поэтому сейчас я установил таймаут в час (3600 секунд) в get_ip и set_auth:

Код:
DROP PROCEDURE IF EXISTS `set_auth`;
DELIMITER $$
CREATE PROCEDURE `set_auth` (IN usr_ip VARCHAR(15), IN auth_properties VARCHAR(255))
BEGIN
  DECLARE usr_id INT;
  SELECT uid INTO usr_id FROM ip_pool WHERE INET_ATON(usr_ip) = ip LIMIT 1;

  IF( usr_id > 0 ) THEN

    INSERT INTO auth_now SET
        ip = usr_ip,
        properties = auth_properties,
        start = UNIX_TIMESTAMP(),
        last = UNIX_TIMESTAMP()
    ON DUPLICATE KEY UPDATE
        properties = IF(auth_properties!='',auth_properties,properties),
        last = UNIX_TIMESTAMP();

    UPDATE ip_pool SET `release` = UNIX_TIMESTAMP() + 3600
        WHERE ip = INET_ATON(usr_ip) AND type = 'dynamic' LIMIT 1;
  END IF;
END$$
DELIMITER ;
Код:
DROP FUNCTION IF EXISTS `get_ip`;
DELIMITER $$
CREATE FUNCTION `get_ip` ( user_id INTEGER UNSIGNED )
    RETURNS VARCHAR(15) NO SQL
BEGIN
    DECLARE user_ip VARCHAR(15);
    DECLARE real_ip VARCHAR(15) DEFAULT 0;
    DECLARE row_cnt INTEGER;
    DECLARE ip_id INTEGER;
    DECLARE tries INTEGER DEFAULT 30;
    DECLARE id_min INTEGER;
    DECLARE id_max INTEGER;

    SELECT INET_NTOA(ip) INTO user_ip FROM ip_pool
        WHERE uid = user_id AND type='static' LIMIT 1;
    IF( user_ip IS NOT NULL ) THEN RETURN user_ip; END IF;

    SELECT 1 INTO real_ip FROM users_services WHERE uid = user_id AND tags LIKE '%,realip,%';

    SELECT id, INET_NTOA(ip) INTO ip_id, user_ip FROM ip_pool
        WHERE uid = user_id AND type = 'dynamic' AND realip = IF(real_ip>0,1,0)
        LIMIT 1;

    IF( ip_id IS NOT NULL) THEN
        UPDATE ip_pool SET `release` = UNIX_TIMESTAMP() + 3600
            WHERE id = ip_id AND uid = user_id;
        SELECT ROW_COUNT() INTO row_cnt;
        IF( row_cnt > 0 ) THEN RETURN user_ip; END IF;
    END IF;

    SELECT MAX(id), MIN(id) INTO id_max, id_min
        FROM ip_pool
        WHERE type = 'dynamic' AND realip = IF(real_ip>0,1,0);

    sel_ip: WHILE tries > 0 DO
        SELECT id, INET_NTOA(ip) INTO ip_id, user_ip
            FROM ip_pool
            WHERE uid = 0
                AND id >= (CEIL(RAND() * (id_max - id_min)) + id_min)
                AND id <= id_max
                LIMIT 1;
        IF( user_ip IS NOT NULL) THEN
            UPDATE ip_pool SET uid = user_id, `release` = UNIX_TIMESTAMP() + 3600
                WHERE id = ip_id AND uid = 0;
            SELECT ROW_COUNT() INTO row_cnt;
            IF( row_cnt > 0 ) THEN RETURN user_ip; END IF;
            SET tries = tries - 5;
        END IF;
        SET tries = tries - 1;
    END WHILE;

END$$
DELIMITER ;

Нужно ли после изменений ребутать ядро или модуль дхцп?
Записан
NodenY45
NoDeny
Старожил
*

Карма: 2
Offline Offline

Сообщений: 365


Просмотр профиля
« Ответ #52 : 15 Ноября 2018, 04:32:11 »

После применения процедур айпи выдается в течении двух минут.....
при выводе
Код:
tail -f /usr/local/nodeny/logs/dhcp.events.log
в логи сыпется куча дублей по две по три одинаковых строки с коммитами, и не останавливается. уже не знаю что смотреть....

что это может быть? версия биллинга и дхцп модуля последние.

в dhcpd.log шлет много такого флуда:
Код:
Nov 15 04:37:11 localhost dhcpd: reuse_lease: lease age 39 (secs) under 25% threshold, reply with unaltered, existing lease for 10.10.20.18

прописал в дхцп конфиг пока что вот это
Код:
dhcp-cache-threshold 0;

Наблюдаю, дубли пропали. Не знаю правильно ли это, поправьте, если что.
Записан
NodenY45
NoDeny
Старожил
*

Карма: 2
Offline Offline

Сообщений: 365


Просмотр профиля
« Ответ #53 : 15 Ноября 2018, 10:25:40 »

Сейчас перестали освобождаться айпи.
Может ли это быть связано с dhcp-cache-threshold 0; ?

И можно ли чистить файл /var/db/dhcpd/dhcpd.leases ?
Записан
kosmich
Пользователь
**

Карма: 1
Offline Offline

Сообщений: 90


Просмотр профиля
« Ответ #54 : 15 Ноября 2018, 13:13:26 »

Сейчас перестали освобождаться айпи.
Так они сейчас через 3600 секунд освобождаются.
Записан
NodenY45
NoDeny
Старожил
*

Карма: 2
Offline Offline

Сообщений: 365


Просмотр профиля
« Ответ #55 : 15 Ноября 2018, 15:43:16 »

Сейчас перестали освобождаться айпи.
Так они сейчас через 3600 секунд освобождаются.

Вот вырубил с порта клиента. Уже более 3600сек, авторизация не пропадает... Непонимающий
Записан
kosmich
Пользователь
**

Карма: 1
Offline Offline

Сообщений: 90


Просмотр профиля
« Ответ #56 : 16 Ноября 2018, 12:42:49 »

Сейчас перестали освобождаться айпи.
Так они сейчас через 3600 секунд освобождаются.

Вот вырубил с порта клиента. Уже более 3600сек, авторизация не пропадает... Непонимающий
ip клиента освобождается через 3600сек после того как закончилась авторизация. Вы не правильно выразились. Не завершается авторизация.
Биллинг считает что клиент "жив". Ясное дело что ip не освобождается.
Возможно процедуры, возможно аккаунтинг. Не приходит сигнал что клиент завершил авторизацию, там если не ошибаюсь, несколько вариантов.
Записан
Страниц: 1 2 3 [4]
  Печать  
 
Перейти в:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.20 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!