104 lines
No EOL
2.8 KiB
PHP
104 lines
No EOL
2.8 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Exception;
|
|
|
|
class Subnet
|
|
{
|
|
/**
|
|
* Calculate subnet
|
|
*
|
|
* @param string $subnet
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
public static function calculate(string $subnet): array
|
|
{
|
|
[$ip, $cidr] = explode('/', $subnet);
|
|
|
|
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
|
return self::calculateIPv4($ip, intval($cidr));
|
|
} elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
|
return self::calculateIPv6($ip, intval($cidr));
|
|
} else {
|
|
throw new Exception("Invalid IP address.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate IPv4
|
|
*
|
|
* @param string $ip
|
|
* @param int $cidr
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
private static function calculateIPv4(string $ip, int $cidr): array
|
|
{
|
|
if ($cidr < 0 || $cidr > 32) {
|
|
throw new Exception("CIDR must be between 0 and 32.");
|
|
}
|
|
|
|
$hosts = ($cidr === 32) ? 1 : (1 << (32 - $cidr)) - 2; // Special case for /32
|
|
$ipLong = ip2long($ip);
|
|
$mask = -1 << (32 - $cidr);
|
|
$network = $ipLong & $mask;
|
|
|
|
$firstIP = $network + 1;
|
|
$lastIP = $network + $hosts;
|
|
|
|
return [
|
|
'network' => long2ip($network),
|
|
'first' => long2ip($firstIP),
|
|
'last' => long2ip($lastIP),
|
|
'hosts' => $hosts,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Calculate IPv6
|
|
*
|
|
* @param string $ip
|
|
* @param int $cidr
|
|
* @return array
|
|
* @throws \Exception
|
|
*/
|
|
private static function calculateIPv6(string $ip, int $cidr): array
|
|
{
|
|
if ($cidr < 0 || $cidr > 128) {
|
|
throw new Exception("CIDR must be between 0 and 128.");
|
|
}
|
|
|
|
// Convert IP to binary representation
|
|
$binaryIP = inet_pton($ip);
|
|
if ($binaryIP === false) {
|
|
throw new Exception("Failed to parse IPv6 address.");
|
|
}
|
|
|
|
// Calculate network address
|
|
$network = substr($binaryIP, 0, intval($cidr / 8));
|
|
$remainder = $cidr % 8;
|
|
|
|
if ($remainder > 0) {
|
|
$lastByte = ord($binaryIP[$cidr / 8]) & (0xFF << (8 - $remainder));
|
|
$network .= chr($lastByte);
|
|
}
|
|
|
|
// Pad the rest with zeros
|
|
$network = str_pad($network, 16, "\0");
|
|
|
|
// Calculate first and last addresses
|
|
$networkAddress = inet_ntop($network);
|
|
$totalHosts = $cidr == 128 ? 1 : bcpow(2, 128 - $cidr);
|
|
$firstHost = ($cidr == 128) ? $networkAddress : inet_ntop($network);
|
|
$lastHost = inet_ntop(pack("H*", str_repeat("F", 32)));
|
|
|
|
return [
|
|
'network' => $networkAddress,
|
|
'first' => $firstHost,
|
|
'last' => $lastHost,
|
|
'hosts' => $totalHosts,
|
|
];
|
|
}
|
|
} |