224 lines
7.0 KiB
PHP
224 lines
7.0 KiB
PHP
<?php
|
|
|
|
namespace SiipAvailableIps;
|
|
|
|
class IpSearchService
|
|
{
|
|
private $apiUrl;
|
|
private $apiToken;
|
|
private $logger;
|
|
|
|
// Rangos de IPs administrativas (no recomendadas para clientes)
|
|
const ADMIN_IP_RANGES = [
|
|
'start' => 1,
|
|
'end' => 30,
|
|
'broadcast' => 254
|
|
];
|
|
|
|
public function __construct($apiUrl, $apiToken, $logger = null)
|
|
{
|
|
$this->apiUrl = $apiUrl;
|
|
$this->apiToken = $apiToken;
|
|
$this->logger = $logger;
|
|
}
|
|
|
|
/**
|
|
* Determina si una IP es administrativa (no recomendada para clientes)
|
|
*
|
|
* @param string $ip Dirección IP completa (ej: 172.16.13.5)
|
|
* @return bool True si es IP administrativa
|
|
*/
|
|
public static function isAdminIp($ip)
|
|
{
|
|
$parts = explode('.', $ip);
|
|
if (count($parts) !== 4) {
|
|
return false;
|
|
}
|
|
|
|
$lastOctet = (int)$parts[3];
|
|
|
|
return ($lastOctet >= self::ADMIN_IP_RANGES['start'] && $lastOctet <= self::ADMIN_IP_RANGES['end'])
|
|
|| $lastOctet === self::ADMIN_IP_RANGES['broadcast'];
|
|
}
|
|
|
|
/**
|
|
* Obtiene el tipo de IP (administrativa o cliente)
|
|
*
|
|
* @param string $ip Dirección IP completa
|
|
* @return array ['type' => 'admin'|'client', 'label' => string, 'recommended' => bool]
|
|
*/
|
|
public static function getIpType($ip)
|
|
{
|
|
if (self::isAdminIp($ip)) {
|
|
return [
|
|
'type' => 'admin',
|
|
'label' => 'Administración',
|
|
'recommended' => false,
|
|
'description' => 'No recomendada para clientes'
|
|
];
|
|
}
|
|
|
|
return [
|
|
'type' => 'client',
|
|
'label' => 'Apta para cliente',
|
|
'recommended' => true,
|
|
'description' => 'Recomendada para asignar a clientes'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Busca IPs disponibles en un segmento de red específico
|
|
*
|
|
* @param string $segmento El tercer octeto del segmento (ej: "5" para 172.16.5.x)
|
|
* @return array Array con 'success', 'data' (IPs disponibles), 'used' (IPs en uso), 'message'
|
|
*/
|
|
public function buscarIpsDisponibles($segmento)
|
|
{
|
|
try {
|
|
// Validar el segmento
|
|
if (!is_numeric($segmento) || $segmento < 0 || $segmento > 255) {
|
|
return [
|
|
'success' => false,
|
|
'message' => 'El segmento debe ser un número entre 0 y 255',
|
|
'data' => []
|
|
];
|
|
}
|
|
|
|
// Obtener IPs en uso desde la API
|
|
$ipsEnUso = $this->obtenerIpsEnUso();
|
|
|
|
if ($ipsEnUso === false) {
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Error al conectar con la API de UISP',
|
|
'data' => []
|
|
];
|
|
}
|
|
|
|
// Filtrar IPs del segmento solicitado
|
|
$segmentoPrefix = "172.16.$segmento.";
|
|
$segmentIps = [];
|
|
|
|
foreach ($ipsEnUso as $ip) {
|
|
if (strpos($ip, $segmentoPrefix) === 0) {
|
|
$segmentIps[] = $ip;
|
|
}
|
|
}
|
|
|
|
// Ordenar IPs del segmento
|
|
usort($segmentIps, function($a, $b) {
|
|
return intval(explode('.', $a)[3]) - intval(explode('.', $b)[3]);
|
|
});
|
|
|
|
// Generar todas las IPs posibles del segmento (1-254)
|
|
$todasLasIps = [];
|
|
for ($i = 1; $i <= 254; $i++) {
|
|
$todasLasIps[] = "172.16.$segmento.$i";
|
|
}
|
|
|
|
// Calcular IPs disponibles
|
|
$ipsDisponibles = array_values(array_diff($todasLasIps, $segmentIps));
|
|
|
|
if ($this->logger) {
|
|
$this->logger->appendLog(
|
|
sprintf(
|
|
'Búsqueda de IPs en segmento 172.16.%s.x - Disponibles: %d, En uso: %d',
|
|
$segmento,
|
|
count($ipsDisponibles),
|
|
count($segmentIps)
|
|
)
|
|
);
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'message' => sprintf(
|
|
'Se encontraron %d IPs disponibles en el segmento 172.16.%s.x',
|
|
count($ipsDisponibles),
|
|
$segmento
|
|
),
|
|
'data' => $ipsDisponibles,
|
|
'used' => $segmentIps,
|
|
'segment' => "172.16.$segmento.x"
|
|
];
|
|
|
|
} catch (\Exception $e) {
|
|
if ($this->logger) {
|
|
$this->logger->appendLog('Error en búsqueda de IPs: ' . $e->getMessage());
|
|
}
|
|
|
|
return [
|
|
'success' => false,
|
|
'message' => 'Error interno: ' . $e->getMessage(),
|
|
'data' => []
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtiene todas las IPs en uso desde la API de UISP
|
|
*
|
|
* @return array|false Array de IPs o false en caso de error
|
|
*/
|
|
private function obtenerIpsEnUso()
|
|
{
|
|
if ($this->logger) {
|
|
$this->logger->appendLog('Iniciando conexión a API: ' . $this->apiUrl);
|
|
}
|
|
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $this->apiUrl);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'accept: application/json',
|
|
"x-auth-token: {$this->apiToken}"
|
|
]);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Para entornos de desarrollo
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
|
|
|
$result = curl_exec($ch);
|
|
$curlError = curl_error($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
|
|
if ($result === false) {
|
|
if ($this->logger) {
|
|
$this->logger->appendLog('Error cURL: ' . $curlError);
|
|
$this->logger->appendLog('Código HTTP: ' . $httpCode);
|
|
}
|
|
curl_close($ch);
|
|
return false;
|
|
}
|
|
|
|
curl_close($ch);
|
|
|
|
if ($this->logger) {
|
|
$this->logger->appendLog("Respuesta HTTP: $httpCode");
|
|
$this->logger->appendLog('Longitud de respuesta: ' . strlen($result) . ' bytes');
|
|
}
|
|
|
|
if ($httpCode !== 200) {
|
|
if ($this->logger) {
|
|
$this->logger->appendLog("Error HTTP $httpCode. Respuesta: " . substr($result, 0, 500));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
$ipsEnUso = json_decode($result, true);
|
|
|
|
if (!is_array($ipsEnUso)) {
|
|
if ($this->logger) {
|
|
$this->logger->appendLog('Error: La respuesta no es un array válido. Respuesta: ' . substr($result, 0, 500));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if ($this->logger) {
|
|
$this->logger->appendLog('IPs obtenidas exitosamente: ' . count($ipsEnUso) . ' direcciones');
|
|
}
|
|
|
|
return $ipsEnUso;
|
|
}
|
|
}
|