王新阳

wangxinyang

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>
2026-02-08
2026-02-09 星期一 农历腊月二十二