Выдача 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')
...