feat(NetworkConfigManager): improve IP address generation by passing used IPs to ensure uniqueness across tunnel creations; enhance error messaging for IP allocation failures
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m46s

This commit is contained in:
2026-01-22 21:17:50 +07:00
parent cd2c0d275e
commit ba0f0273e3
+9 -6
View File
@@ -536,7 +536,7 @@ function NetworkConfigManager() {
} }
const newInterfaces = []; const newInterfaces = [];
const usedIps = new Set(getAllUsedIpsSet); const usedIps = new Set(getAllUsedIpsSet); // Создаем Set с уже использованными IP из существующих интерфейсов
// Создаем туннели для каждой пары // Создаем туннели для каждой пары
templateServerPairs.forEach((pair, index) => { templateServerPairs.forEach((pair, index) => {
@@ -560,15 +560,16 @@ function NetworkConfigManager() {
// Генерируем IP адреса (учитываем уже использованные в этой итерации) // Генерируем IP адреса (учитываем уже использованные в этой итерации)
// Важно: Local IP и Remote IP должны быть разными и в одной сети /30 // Важно: Local IP и Remote IP должны быть разными и в одной сети /30
// Передаем usedIps в функцию, чтобы она учитывала IP, выделенные для предыдущих туннелей
let localIp = null; let localIp = null;
let remoteIp = null; let remoteIp = null;
let attempts = 0; let attempts = 0;
const maxAttempts = 1000; const maxAttempts = 1000;
while ((!localIp || !remoteIp || localIp === remoteIp) && attempts < maxAttempts) { while ((!localIp || !remoteIp || localIp === remoteIp) && attempts < maxAttempts) {
const candidateLocal = generateFreePrivateIp(null, false, null, pairIpPoolId || null); const candidateLocal = generateFreePrivateIp(null, false, null, pairIpPoolId || null, usedIps);
if (candidateLocal && !usedIps.has(candidateLocal)) { if (candidateLocal && !usedIps.has(candidateLocal)) {
const candidateRemote = generateFreePrivateIp(null, true, candidateLocal, pairIpPoolId || null); const candidateRemote = generateFreePrivateIp(null, true, candidateLocal, pairIpPoolId || null, usedIps);
if (candidateRemote && !usedIps.has(candidateRemote) && candidateRemote !== candidateLocal) { if (candidateRemote && !usedIps.has(candidateRemote) && candidateRemote !== candidateLocal) {
localIp = candidateLocal; localIp = candidateLocal;
remoteIp = candidateRemote; remoteIp = candidateRemote;
@@ -581,7 +582,7 @@ function NetworkConfigManager() {
} }
if (!localIp || !remoteIp || localIp === remoteIp) { if (!localIp || !remoteIp || localIp === remoteIp) {
notify.error(`Не удалось найти свободные IP адреса для туннеля ${index + 1} (${server1.dns || server1.ip} ↔ ${server2.dns || server2.ip})`); notify.error(`Не удалось найти свободные IP адреса для туннеля ${index + 1} (${server1.dns || server1.ip} ↔ ${server2.dns || server2.ip}). Возможно, в пуле закончились свободные адреса.`);
return; return;
} }
@@ -781,8 +782,10 @@ function NetworkConfigManager() {
// === Генерация свободного IP адреса из приватных диапазонов === // === Генерация свободного IP адреса из приватных диапазонов ===
// Генерирует IP в сетях /30 (четные базовые адреса) // Генерирует IP в сетях /30 (четные базовые адреса)
// Если указан ipPoolId, использует IP пул, иначе использует приватные диапазоны RFC 1918 // Если указан ipPoolId, использует IP пул, иначе использует приватные диапазоны RFC 1918
const generateFreePrivateIp = (excludeIp = null, forRemote = false, pairedLocalIp = null, ipPoolId = null) => { // usedIpsOverride - опциональный Set уже использованных IP (если не передан, создается копия getAllUsedIpsSet)
const usedIps = new Set(getAllUsedIpsSet); const generateFreePrivateIp = (excludeIp = null, forRemote = false, pairedLocalIp = null, ipPoolId = null, usedIpsOverride = null) => {
// Создаем копию Set, чтобы не модифицировать оригинальный
const usedIps = usedIpsOverride || new Set(getAllUsedIpsSet);
if (excludeIp) { if (excludeIp) {
usedIps.delete(excludeIp.trim()); usedIps.delete(excludeIp.trim());
} }