IPcalc-u-later/app/Services/Subnet.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 = (1 << (32 - $cidr)) - 2; // Excludes network and broadcast
$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,
];
}
}