[英]Match IPv4 address given IP range/mask?
使用PHP或RegExp(或兩者),我如何匹配一系列IP地址?
10.210.12.12
10.253.12.12
10.210.12.254
10.210.12.95
10.210.12.60
10.210.12.0/24
10.210.12.0/16
10.210.*.*
10.*.*.*
我知道我可以這樣做:
?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)
......但它沒有考慮范圍。 它只允許您匹配傳入的號碼,以查看它是否是每個八位位組為0-255的IP地址。
編輯:
我在php.net上的ip2long函數的注釋中找到了這個函數。
function ip_in_network($ip, $net_addr, $net_mask){
if($net_mask <= 0){ return false; }
$ip_binary_string = sprintf("%032b",ip2long($ip));
$net_binary_string = sprintf("%032b",ip2long($net_addr));
return (substr_compare($ip_binary_string,$net_binary_string,0,$net_mask) === 0);
}
ip_in_network("192.168.2.1","192.168.2.0",24); //true
ip_in_network("192.168.6.93","192.168.0.0",16); //true
ip_in_network("1.6.6.6","128.168.2.0",1); //false
它短而甜,但與星號情況不符。 我也不知道它是否完全准確,因為當我認為它是假的時候它會返回一個真實的結果:
echo ip_in_network("192.168.2.1","192.167.0.0",1);
......但也許我誤解了/ 1會是什么。 也許我需要使用/ 24。
我改編了一個來自php.net的答案並且做得更好。
function netMatch($network, $ip) {
$network=trim($network);
$orig_network = $network;
$ip = trim($ip);
if ($ip == $network) {
echo "used network ($network) for ($ip)\n";
return TRUE;
}
$network = str_replace(' ', '', $network);
if (strpos($network, '*') !== FALSE) {
if (strpos($network, '/') !== FALSE) {
$asParts = explode('/', $network);
$network = @ $asParts[0];
}
$nCount = substr_count($network, '*');
$network = str_replace('*', '0', $network);
if ($nCount == 1) {
$network .= '/24';
} else if ($nCount == 2) {
$network .= '/16';
} else if ($nCount == 3) {
$network .= '/8';
} else if ($nCount > 3) {
return TRUE; // if *.*.*.*, then all, so matched
}
}
echo "from original network($orig_network), used network ($network) for ($ip)\n";
$d = strpos($network, '-');
if ($d === FALSE) {
$ip_arr = explode('/', $network);
if (!preg_match("@\d*\.\d*\.\d*\.\d*@", $ip_arr[0], $matches)){
$ip_arr[0].=".0"; // Alternate form 194.1.4/24
}
$network_long = ip2long($ip_arr[0]);
$x = ip2long($ip_arr[1]);
$mask = long2ip($x) == $ip_arr[1] ? $x : (0xffffffff << (32 - $ip_arr[1]));
$ip_long = ip2long($ip);
return ($ip_long & $mask) == ($network_long & $mask);
} else {
$from = trim(ip2long(substr($network, 0, $d)));
$to = trim(ip2long(substr($network, $d+1)));
$ip = ip2long($ip);
return ($ip>=$from and $ip<=$to);
}
}
function ech($b) {
if ($b) {
echo "MATCHED\n";
} else {
echo "DID NOT MATCH\n";
}
}
echo "CLASS A TESTS\n";
ech(netMatch('10.168.1.0-10.168.1.100', '10.168.1.90'));
ech(netMatch('10.168.*.*', '10.168.1.90'));
ech(netMatch('10.168.0.0/16', '10.168.1.90'));
ech(netMatch('10.169.1.0/24', '10.168.1.90'));
ech(netMatch('10.168.1.90', '10.168.1.90'));
echo "\nCLASS B TESTS\n";
ech(netMatch('130.168.1.0-130.168.1.100', '130.168.1.90'));
ech(netMatch('130.168.*.*', '130.168.1.90'));
ech(netMatch('130.168.0.0/16', '130.168.1.90'));
ech(netMatch('130.169.1.0/24', '130.168.1.90'));
ech(netMatch('130.168.1.90', '130.168.1.90'));
echo "\nCLASS C TESTS\n";
ech(netMatch('192.168.1.0-192.168.1.100', '192.168.1.90'));
ech(netMatch('192.168.*.*', '192.168.1.90'));
ech(netMatch('192.168.0.0/16', '192.168.1.90'));
ech(netMatch('192.169.1.0/24', '192.168.1.90'));
ech(netMatch('192.168.1.90', '192.168.1.90'));
echo "\nCLASS D TESTS\n";
ech(netMatch('230.168.1.0-230.168.1.100', '230.168.1.90'));
ech(netMatch('230.168.*.*', '230.168.1.90'));
ech(netMatch('230.168.0.0/16', '230.168.1.90'));
ech(netMatch('230.169.1.0/24', '230.168.1.90'));
ech(netMatch('230.168.1.90', '230.168.1.90'));
echo "\nCLASS E TESTS\n";
ech(netMatch('250.168.1.0-250.168.1.100', '250.168.1.90'));
ech(netMatch('250.168.*.*', '250.168.1.90'));
ech(netMatch('250.168.0.0/16', '250.168.1.90'));
ech(netMatch('250.169.1.0/24', '250.168.1.90'));
ech(netMatch('250.168.1.90', '250.168.1.90'));
結果如下:
CLASS A TESTS
from orig network (10.168.1.0-10.168.1.100) used network (10.168.1.0-10.168.1.100) for (10.168.1.90)
MATCHED
from orig network (10.168.*.*) used network (10.168.0.0/16) for (10.168.1.90)
MATCHED
from orig network (10.168.0.0/16) used network (10.168.0.0/16) for (10.168.1.90)
MATCHED
from orig network (10.169.1.0/24) used network (10.169.1.0/24) for (10.168.1.90)
DID NOT MATCH
used network (10.168.1.90) for (10.168.1.90)
MATCHED
CLASS B TESTS
from orig network (130.168.1.0-130.168.1.100) used network (130.168.1.0-130.168.1.100) for (130.168.1.90)
MATCHED
from orig network (130.168.*.*) used network (130.168.0.0/16) for (130.168.1.90)
MATCHED
from orig network (130.168.0.0/16) used network (130.168.0.0/16) for (130.168.1.90)
MATCHED
from orig network (130.169.1.0/24) used network (130.169.1.0/24) for (130.168.1.90)
DID NOT MATCH
used network (130.168.1.90) for (130.168.1.90)
MATCHED
CLASS C TESTS
from orig network (192.168.1.0-192.168.1.100) used network (192.168.1.0-192.168.1.100) for (192.168.1.90)
MATCHED
from orig network (192.168.*.*) used network (192.168.0.0/16) for (192.168.1.90)
MATCHED
from orig network (192.168.0.0/16) used network (192.168.0.0/16) for (192.168.1.90)
MATCHED
from orig network (192.169.1.0/24) used network (192.169.1.0/24) for (192.168.1.90)
DID NOT MATCH
used network (192.168.1.90) for (192.168.1.90)
MATCHED
CLASS D TESTS
from orig network (230.168.1.0-230.168.1.100) used network (230.168.1.0-230.168.1.100) for (230.168.1.90)
MATCHED
from orig network (230.168.*.*) used network (230.168.0.0/16) for (230.168.1.90)
MATCHED
from orig network (230.168.0.0/16) used network (230.168.0.0/16) for (230.168.1.90)
MATCHED
from orig network (230.169.1.0/24) used network (230.169.1.0/24) for (230.168.1.90)
DID NOT MATCH
used network (230.168.1.90) for (230.168.1.90)
MATCHED
CLASS E TESTS
from orig network (250.168.1.0-250.168.1.100) used network (250.168.1.0-250.168.1.100) for (250.168.1.90)
MATCHED
from orig network (250.168.*.*) used network (250.168.0.0/16) for (250.168.1.90)
MATCHED
from orig network (250.168.0.0/16) used network (250.168.0.0/16) for (250.168.1.90)
MATCHED
from orig network (250.169.1.0/24) used network (250.169.1.0/24) for (250.168.1.90)
DID NOT MATCH
used network (250.168.1.90) for (250.168.1.90)
MATCHED
我已經改進了上面的例子(我有一個/ 29的網絡掩碼,所以它不起作用)。
function check_netmask($mask, $ip) {
@list($net, $bits) = explode('/', $mask);
$bits = isset($bits) ? $bits : 32;
$bitmask = -pow(2, 32-$bits) & 0x00000000FFFFFFFF;
$netmask = ip2long($net) & $bitmask;
$ip_bits = ip2long($ip) & $bitmask;
return (($netmask ^ $ip_bits) == 0);
}
如果你想看到它的實際效果,請添加:
print("IP Bits: " . str_pad(decbin(ip2long($ip)), 32, '0', STR_PAD_LEFT));
print "\n";
print("Bitmask: " . str_pad(decbin($bitmask), 32, '0', STR_PAD_LEFT));
print "\n";
print("Netmask: " . str_pad(decbin($netmask), 32, '0', STR_PAD_LEFT));
print "\n";
print("Match: " . str_pad(decbin($netmask ^ $ip_bits), 32, '0', STR_PAD_LEFT));
print "\n";
用這樣的東西運行它:
print var_dump(check_netmask($argv[1], $argv[2]));
轉換為32位無符號並使用布爾/按位運算。
例如,將192.168.25.1轉換為0xC0A81901。
然后,您可以通過轉換掩碼的點分十進制部分(即0xC0A81900)並創建24位掩碼(即0xFFFFFF00)來查看它是否與掩碼192.168.25 / 24匹配。
在所討論的地址和掩碼之間執行按位AND,並與掩碼規范的點分十進制部分進行比較。 例如,
0xC0A81901 AND 0xFFFFFF00 ==> 0xC0A81900 (result)
compare 0xC0A81900 (result) to 0xC0A81900.
我不知道PHP,但谷歌告訴我PHP有inet_pton(),這是我在C中用來執行從點分十進制到n位無符號的轉換。 見http://php.net/manual/en/function.inet-pton.php
使用此庫: https : //github.com/S1lentium/IPTools
//Check if IP is within Range:
echo Range::parse('192.168.1.1-192.168.1.254')->contains(new IP('192.168.1.5')); // true
echo Range::parse('::1-::ffff')->contains(new IP('::1234')); // true
正則表達式聽起來不像是處理子網掩碼的正確工具(至少不是十進制)。 它可以做到,但它會很難看。
我強烈建議將字符串解析為4個整數,組合為32位int,然后使用標准的按位運算(基本上是按位-AND,然后進行比較)。
使用strpos將它們匹配為字符串。
<?php
$ips = array();
$ips[0] = "10.210.12.12";
$ips[1] = "10.253.12.12";
$ips[2] = "10.210.12.254";
$ips[3] = "10.210.12.95";
$ips[4] = "10.210.12.60";
$matches = array();
foreach($ips as $ip){
if(strpos($ip, "10.253.") === 0){
$matches[] = $ip;
}
}
print_r($matches);
?>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.