ip地址范围与cidr块互转,判断ip地址是否在给定范围或cidr块中
php版
<?php
class Ip_util{
/**
* 将IP地址范围转换为CIDR块。
*
* @param string $range 格式为 "start_ip-end_ip" 的字符串。
* @return string[] 返回一个包含CIDR格式字符串的数组 (e.g., ["192.168.1.0/24"])。
*/
public static function rangeToCIDR($range) {
list($startIp, $endIp) = explode('-', trim($range));
$start = ip2long($startIp);
$end = ip2long($endIp);
if ($start === false || $end === false || $start > $end) {
throw new InvalidArgumentException("Invalid IP range: $range");
}
$results = [];
while ($start <= $end) {
// 寻找以 $start 为起点的最大网络块
$mask = 32;
for ($i = 1; $i <= 32; $i++) {
$blockSize = 1 << $i;
$maskVal = ~($blockSize - 1);
$blockStart = $start & $maskVal;
// 如果这个块的起始地址小于当前start(说明对齐失败),
// 或者这个块的结束地址超过了范围的结束地址,则不能选择此块
if ($blockStart < $start || ($blockStart + $blockSize - 1) > $end) {
break;
}
// 否则,这个块是有效的,更新掩码
$mask = 32 - $i;
}
$results[] = long2ip($start) . '/' . $mask;
$start += 1 << (32 - $mask);
}
return $results;
}
/**
* 将CIDR块转换为IP地址范围。
*
* @param string $cidr 格式为 "ip_address/prefix_length" 的字符串。
* @return string 返回一个IP范围格式的字符串 (e.g., "192.168.1.0-192.168.1.255")。
*/
public static function cidrToRange($cidr) {
list($ip, $prefixLength) = explode('/', trim($cidr));
$ipNum = ip2long($ip);
if ($ipNum === false || !is_numeric($prefixLength) || $prefixLength < 0 || $prefixLength > 32) {
throw new InvalidArgumentException("Invalid CIDR format: $cidr");
}
$prefixLength = (int)$prefixLength;
$netMask = ~((1 << (32 - $prefixLength)) - 1);
$networkAddress = $ipNum & $netMask;
$hostBits = 32 - $prefixLength;
$totalAddressesInSubnet = pow(2, $hostBits);
$broadcastAddress = $networkAddress + $totalAddressesInSubnet - 1;
$startIp = long2ip($networkAddress);
$endIp = long2ip($broadcastAddress);
return "$startIp-$endIp";
}
/**
* 检查IP是否在CIDR列表中
* @param string $ip
* @param array $cidrList
* @return bool
*/
public static function isInCidrList($ip, $cidrList) {
$ipLong = ip2long($ip);
if ($ipLong === false) {
return false; // 无效IP
}
foreach ($cidrList as $cidr) {
list($networkStr, $prefixLen) = explode('/', trim($cidr));
$prefixLen = (int)$prefixLen;
if (!is_numeric($prefixLen) || $prefixLen < 0 || $prefixLen > 32) {
continue; // 跳过无效CIDR
}
$networkLong = ip2long($networkStr);
if ($networkLong === false) {
continue; // 跳过无效网络地址
}
// 计算网络掩码
$mask = ~((1 << (32 - $prefixLen)) - 1);
// 检查IP和网络地址的网络部分是否相同
if (($ipLong & $mask) === ($networkLong & $mask)) {
return true;
}
}
return false;
}
/**
* 检查IP是否在IP范围列表中
* @param string $ip
* @param array $rangeList
* @return bool
*/
public static function isInRangeList($ip, $rangeList) {
$ipLong = ip2long($ip);
if ($ipLong === false) {
return false; // 无效IP
}
foreach ($rangeList as $range) {
list($startStr, $endStr) = explode('-', trim($range));
$startLong = ip2long($startStr);
$endLong = ip2long($endStr);
if ($startLong === false || $endLong === false) {
continue; // 跳过无效范围
}
// 检查IP是否在范围内
if ($ipLong >= $startLong && $ipLong <= $endLong) {
return true;
}
}
return false;
}
}
// --- 示例用法 ---
$ranges = [
'192.168.1.0-192.168.1.255',
'192.168.2.0-192.168.5.0',
'192.168.2.0-192.168.5.128',
'192.168.2.0-192.168.5.255',
'192.168.0.0-192.168.255.255',
'192.1.127.0-192.5.127.255',
'192.1.127.0-192.5.128.169',
'192.1.127.0-192.5.127.0'
];
$cidrs=[];
foreach ($ranges as $range) {
echo "输入范围: $range\n";
$result = Ip_util::rangeToCIDR($range);
$cidrs=array_merge($cidrs,$result);
echo "输出CIDR(s): [" . implode(', ', array_map(function($c) { return "\"$c\""; }, $result)) . "]\n\n";
echo "逆推:\n";
foreach($result as $row){
echo Ip_util::cidrToRange($row),"\n";
}
echo "\n\n";
}
$targetIp='192.5.128.150';
echo "目标IP: $targetIp\n\n";
echo "检查IP是否在范围列表中:\n";
foreach ($ranges as $range) {
$inRange = Ip_util::isInRangeList($targetIp, [$range]);
echo " - '$range': " . ($inRange ? "是" : "否") . "\n";
}
echo "总体结果 (在任一范围内): " . (Ip_util::isInRangeList($targetIp, $ranges) ? "是" : "否") . "\n\n";
echo "检查IP是否在CIDR列表中:\n";
foreach ($cidrs as $cidr) {
$inCidr = Ip_util::isInCidrList($targetIp, [$cidr]);
echo " - '$cidr': " . ($inCidr ? "是" : "否") . "\n";
}
echo "总体结果 (在任一CIDR中): " . (Ip_util::isInCidrList($targetIp, $cidrs) ? "是" : "否") . "\n";
?>
js版
<script>
/**
* IP范围转CIDR列表
* @param {string} ipRange
* @returns {string[]}
*/
function rangeToCIDR(ipRange) {
const [startIp, endIp]=ipRange.split('-');
const ipToInt = ip => ip.split('.').reduce((acc, x) => (acc << 8) + (+x), 0);
const intToIp = int => [24, 16, 8, 0].map(s => (int >> s) & 0xFF).join('.');
let start = ipToInt(startIp);
const end = ipToInt(endIp);
const results = [];
while (start <= end) {
// 找到start的最低位1的位置
let mask = 32;
for (let i = 1; i <= 32; i++) {
const blockSize = 1 << i;
const maskVal = ~(blockSize - 1);
const blockStart = start & maskVal;
if (blockStart < start || blockStart + blockSize - 1 > end) {
break;
}
mask = 32 - i;
}
results.push(`${intToIp(start)}/${mask}`);
start += 1 << (32 - mask);
}
return results;
}
/**
* CIDR转IP范围
* @param {string} cidr
* @returns {string}
*/
function cidrToRange(cidr) {
const ipToInt = ip => ip.split('.').reduce((acc, octet, i) => acc + (parseInt(octet) << (24 - i * 8)), 0);
const intToIp = int => [24, 16, 8, 0].map(shift => (int >>> shift) & 0xFF).join('.');
const [ip, prefixStr] = cidr.split('/');
const prefix = parseInt(prefixStr, 10);
const mask = ~0 << (32 - prefix);
const ipInt = ipToInt(ip);
const network = ipInt & mask;
return intToIp(network)+'-'+intToIp(network + (1 << (32 - prefix)) - 1);
return {
start: intToIp(network),
end: intToIp(network + (1 << (32 - prefix)) - 1)
};
}
/**
* 检查IP是否在CIDR列表中
* @param {string} ip
* @param {string[]} cidrList
* @returns {boolean}
*/
function isIpInCidrList(ip, cidrList) {
const ipLong = ip.split('.').reduce((acc, x) => (acc << 8) + (+x), 0) >>> 0;
if (isNaN(ipLong)) return false; // 无效IP
for (const cidr of cidrList) {
const [networkStr, prefixLenStr] = cidr.split('/');
const prefixLen = parseInt(prefixLenStr, 10);
if (isNaN(prefixLen) || prefixLen < 0 || prefixLen > 32) {
continue; // 跳过无效CIDR
}
const networkLong = networkStr.split('.').reduce((acc, x) => (acc << 8) + (+x), 0) >>> 0;
if (isNaN(networkLong)) {
continue; // 跳过无效网络地址
}
const mask = ~((1 << (32 - prefixLen)) - 1) >>> 0;
if ((ipLong & mask) === (networkLong & mask)) {
return true;
}
}
return false;
}
/**
* 检查IP是否在IP范围列表中
* @param {string} ip
* @param {string[]} rangeList
* @returns {boolean}
*/
function isIpInRangeList(ip, rangeList) {
const ipLong = ip.split('.').reduce((acc, x) => (acc << 8) + (+x), 0) >>> 0;
if (isNaN(ipLong)) return false; // 无效IP
for (const range of rangeList) {
const [startStr, endStr] = range.split('-');
const startLong = startStr.split('.').reduce((acc, x) => (acc << 8) + (+x), 0) >>> 0;
const endLong = endStr.split('.').reduce((acc, x) => (acc << 8) + (+x), 0) >>> 0;
if (isNaN(startLong) || isNaN(endLong)) {
continue; // 跳过无效范围
}
if (ipLong >= startLong && ipLong <= endLong) {
return true;
}
}
return false;
}
// --- 示例用法 ---
const ranges = [
'192.168.1.0-192.168.1.255',
'192.168.2.0-192.168.5.0',
'192.168.2.0-192.168.5.128',
'192.168.2.0-192.168.5.255',
'192.168.0.0-192.168.255.255',
'192.1.127.0-192.5.127.255',
'192.1.127.0-192.5.128.169',
'192.1.127.0-192.5.127.0'
];
cidrs=[];
ranges.forEach(range => {
console.log(`输入范围: ${range}`);
const result = rangeToCIDR(range);
cidrs=cidrs.concat(result);
console.log(`输出CIDR(s): [${result.map(c => `"${c}"`).join(', ')}]\n`);
console.log("逆推:\n");
for(let index in result){
console.log(cidrToRange(result[index])+"\n");
}
/*
$.each(result,function(index,row){
console.log(cidrToRange(row)+"\n");
});
*/
console.log("\n\n");
});
const targetIp = "192.5.128.150";
console.log(`目标IP: ${targetIp}\n`);
console.log("检查IP是否在范围列表中:");
ranges.forEach(range => {
const inRange = isIpInRangeList(targetIp, [range]);
console.log(` - '${range}': ${inRange ? '是' : '否'}`);
});
console.log(`总体结果 (在任一范围内): ${isIpInRangeList(targetIp, ranges) ? '是' : '否'}\n`);
console.log("检查IP是否在CIDR列表中:");
cidrs.forEach(cidr => {
const inCidr = isIpInCidrList(targetIp, [cidr]);
console.log(` - '${cidr}': ${inCidr ? '是' : '否'}`);
});
console.log(`总体结果 (在任一CIDR中): ${isIpInCidrList(targetIp, cidrs) ? '是' : '否'}`);
</script>
