Выдача ip в зависимости от того, к какому NAS подключен абонент
Статья актуальна как для dhcp так и для pppoe, настроеных на использование протокола radius.
Суть заключается в том, что dhcp или pppoe-сервер передает на radius свой идентификатор. Например, мак-адрес, либо свой номер, либо любой иной идентификатор. Какой именно - разберем позже. Сначала нужно создать в mysql функцию, которая будет выдавать ip из разных пулов в зависимости от идентификатора. Этот идентификатор назовем «тегом».
Предварительно, в админке заходим в настройки ip пулов, выбираем, скажем пул 10.0.0.2 ... 10.0.4.254, устанавливаем тип «динамический», а в поле «теги» вписываем слово «nas1». Для другого пула теги в значение «nas2» и т.д.
Создаем mysql процедуру get_ip_by_tag:
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 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 ;
Проверим:
SELECT get_ip_by_tag(371, 'nas1');
где 371 - id абонента. У него не должны быть подключены статические ip!
Должны получить ip из пула для nas1. Проверим так же и для nas2.
Теперь изменим процедуру radreply - она возвращает dhcp или pppoe серверу ip в зависимости от тега. В качестве логина в dhcp используется мак-адрес абонента. Процедура radreply для dhcp:
DROP PROCEDURE IF EXISTS `radreply`; DELIMITER $$ CREATE PROCEDURE `radreply`( IN login VARCHAR(64), tag VARCHAR(64) ) BEGIN DECLARE usr_mac VARCHAR(12); DECLARE usr_ip VARCHAR(15); DECLARE usr_id INT; SELECT REPLACE(login, ':', '') INTO usr_mac; SELECT uid INTO usr_id FROM mac_uid WHERE mac=usr_mac; 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; ELSE START TRANSACTION; SELECT INET_NTOA(ip) INTO usr_ip FROM ip_pool WHERE uid=0 AND type='dynamic' AND `release` < UNIX_TIMESTAMP() AND tags LIKE CONCAT('%,', tag ,',%') ORDER BY RAND() LIMIT 1 FOR UPDATE; INSERT INTO mac_uid VALUES( NULL, usr_mac, INET_ATON(usr_ip), 0, UNIX_TIMESTAMP(), 0, 0, 0, '') ON DUPLICATE KEY UPDATE ip=IF(ip>0,ip,INET_ATON(usr_ip)), time=UNIX_TIMESTAMP(); COMMIT; SELECT INET_NTOA(ip) INTO usr_ip FROM mac_uid WHERE mac=usr_mac; UPDATE ip_pool SET `release` = UNIX_TIMESTAMP() + 3600 WHERE ip = INET_ATON(usr_ip); END IF; SELECT NULL, login, 'Framed-IP-Address', usr_ip, '='; SELECT NULL, login, 'Session-Timeout', '600', '='; END$$ DELIMITER ;
DROP PROCEDURE IF EXISTS `radupdate`; DELIMITER $$ CREATE PROCEDURE `radupdate`(IN login VARCHAR(64), IN ip VARCHAR(16), IN properties VARCHAR(255), tag VARCHAR(64)) BEGIN DECLARE usr_mac VARCHAR(12); DECLARE usr_ip VARCHAR(15); DECLARE usr_id INT; SELECT REPLACE(login, ':', '') INTO usr_mac; SELECT uid INTO usr_id FROM mac_uid WHERE mac=usr_mac; IF usr_id IS NULL THEN SELECT ip INTO usr_ip; ELSE SELECT get_ip_by_tag(usr_id, tag) INTO usr_ip; END IF; CALL set_auth(usr_ip, CONCAT('mod=pppoe;', REPLACE(properties,':',''))); END$$ DELIMITER ;
Проверка:
CALL radreply('00:11:22:33:44:77', 'nas1');
- здесь 00:11:22:33:44:77 - мак абонента, должен быть прописан в его учетке.
В ответ на вышепреведенный запрос мы должны получить подобное:
+------+-------------------+-------------------+-----------+---+ | NULL | login | Framed-IP-Address | usr_ip | = | +------+-------------------+-------------------+-----------+---+ | NULL | 00:11:22:33:44:77 | Framed-IP-Address | 10.0.1.19 | = | +------+-------------------+-------------------+-----------+---+ +------+-------------------+-----------------+-----+---+ | NULL | login | Session-Timeout | 600 | = | +------+-------------------+-----------------+-----+---+ | NULL | 00:11:22:33:44:77 | Session-Timeout | 600 | = | +------+-------------------+-----------------+-----+---+
Теперь необходимо настроить dhcp сервер чтобы он сообщал radius-у свой идентификатор. Можно использовать мак-адрес этого сервера либо другой параметр, например mikrotik в параметре Called-Station-Id посылает параметр Name, указываемый в его dhcp-настройках.
Меняем конфиг /usr/local/etc/raddb/sql.conf:
authorize_check_query = "call radcheck('%{User-Name}')" authorize_reply_query = "call radreply('%{User-Name}', '%{Called-Station-Id}')" postauth_query = "call radupdate('%{User-Name}','%{reply:Framed-IP-Address}','nas=%{NAS-IP-Address}', '%{Called-Station-Id}')" accounting_update_query = "call radupdate('%{User-Name}','%{Framed-IP-Address}','nas=%{NAS-IP-Address}', '%{Called-Station-Id}')"
Запускаем Radius в режиме debug:
radiusd -X
и смотрим какие sql выполняются:
... [sql] expand: call radreply('%{User-Name}', '%{Called-Station-Id}') -> call radreply('00:11:22:33:44:55', 'nas1') ...