下载某个国家所有的IP地址CIDR
一般需要限制某个国家访问时需要的一个IP地址列表
首先下载免费数据库https://db-ip.com/db/lite.php
第一步:按国家拆分 IP 范围文件
<?php
$inputFile = 'dbip/dbip-country-lite-2025-05.csv';
$inputFile = 'dbip/ip2country-v4.tsv';
$tmpDir = __DIR__ . '/country-ipv4-range';
if (!is_dir($tmpDir)) mkdir($tmpDir);
// 把 IP 范围按国家写入临时文件
$fp = fopen($inputFile, 'r');
while (($line = fgets($fp)) !== false) {
$line = trim($line);
if ($line === '' || str_starts_with($line, 'ip_start')) continue;
[$startIp, $endIp, $country] = str_getcsv($line);
if (!filter_var($startIp, FILTER_VALIDATE_IP) || !filter_var($endIp, FILTER_VALIDATE_IP)) continue;
if (!filter_var($startIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ||
!filter_var($endIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) continue;
$file = "$tmpDir/$country.txt";
file_put_contents($file, "$startIp,$endIp\n", FILE_APPEND);
}
fclose($fp);
echo "阶段一完成:已按国家拆分 IP 范围文件。\n";
第二步:处理IP数据,用最少的CIDR数量表示该国家的所有IP
<?php
function ipToLong($ip) {
return sprintf('%u', ip2long($ip));
}
// Correct algorithm for finding largest possible CIDR blocks
function rangeToCidrs($startIp, $endIp) {
$start = ipToLong($startIp);
$end = ipToLong($endIp);
$result = [];
if ($start === false || $end === false || $start > $end) {
error_log("Invalid IP range: $startIp - $endIp");
return $result;
}
while ($start <= $end) {
// Find the size of the largest block that fits
$prefix = 32;
$bit = 0;
// Find how many bits we can include
while ($bit < 32) {
// Try to flip this bit to 1
$mask = 1 << $bit;
$next_start = $start | $mask;
// If flipping makes it greater than end or changes the network portion
if ($next_start > $end || ($start & $mask) != 0) {
break;
}
$bit++;
}
// Calculate the prefix length
$max_size = $bit;
$mask_length = 32 - $max_size;
// Find the correct prefix based on network alignment
for ($i = 0; $i <= $max_size; $i++) {
$mask = ~((1 << $i) - 1) & 0xFFFFFFFF;
// If the start address is aligned on this prefix boundary
if (($start & $mask) == $start) {
$mask_length = 32 - $i;
} else {
break;
}
}
// Add the CIDR notation
$result[] = long2ip($start) . "/$mask_length";
// Move to the next address block
$start += 1 << (32 - $mask_length);
}
return $result;
}
// Compare function for sorting CIDR blocks numerically
function compareCidr($a, $b) {
// Extract IP and prefix from CIDR notation
list($ipA, $prefixA) = explode('/', $a);
list($ipB, $prefixB) = explode('/', $b);
// Convert IPs to long integers for numeric comparison
$ipALong = ipToLong($ipA);
$ipBLong = ipToLong($ipB);
// First compare by IP address
if ($ipALong != $ipBLong) {
return $ipALong - $ipBLong;
}
// If same IP, compare by prefix length
return $prefixA - $prefixB;
}
// Only execute main code if this file is directly called
if (basename($_SERVER['SCRIPT_FILENAME']) == basename(__FILE__)) {
$tmpDir = __DIR__ . '/country-ipv4-range';
$outDir = __DIR__ . '/country-ipv4-cidr';
// Check if source directory exists
if (!is_dir($tmpDir)) {
die("Error: Source directory not found: $tmpDir\n");
}
// Create output directory if it doesn't exist
if (!is_dir($outDir)) {
if (!mkdir($outDir, 0755, true)) {
die("Error: Failed to create output directory: $outDir\n");
}
}
$fileCount = 0;
$files = glob("$tmpDir/*.txt");
if (empty($files)) {
die("Error: No input files found in $tmpDir\n");
}
foreach ($files as $filePath) {
$country = basename($filePath, '.txt');
$cidrs = [];
// Check if file is readable
if (!is_readable($filePath)) {
echo "Warning: Cannot read file $filePath, skipping...\n";
continue;
}
$fp = fopen($filePath, 'r');
if ($fp === false) {
echo "Warning: Could not open file $filePath, skipping...\n";
continue;
}
while (($line = fgets($fp)) !== false) {
$line = trim($line);
if (empty($line)) continue;
$parts = explode(',', $line);
if (count($parts) < 2) {
error_log("Invalid line format in $filePath: $line");
continue;
}
[$startIp, $endIp] = $parts;
$cidrs = array_merge($cidrs, rangeToCidrs($startIp, $endIp));
}
fclose($fp);
// Remove duplicates
$cidrs = array_unique($cidrs);
// Sort CIDRs numerically instead of lexicographically
usort($cidrs, 'compareCidr');
// Write output file
if (file_put_contents("$outDir/$country.txt", implode("\n", $cidrs)) === false) {
echo "Warning: Failed to write to $outDir/$country.txt\n";
continue;
}
echo "Completed $country: " . count($cidrs) . " CIDR entries\n";
$fileCount++;
}
echo "Stage 2 complete: Exported CIDR data for $fileCount countries.\n";
}