简体   繁体   English

匹配给定IP范围/掩码的IPv4地址?

[英]Match IPv4 address given IP range/mask?

Either with PHP or a RegExp (or both), how do I match a range of IP addresses? 使用PHP或RegExp(或两者),我如何匹配一系列IP地址?

Sample Incoming IPs 传入IP示例

10.210.12.12
10.253.12.12
10.210.12.254
10.210.12.95
10.210.12.60

Sample Ranges 样本范围

10.210.12.0/24
10.210.12.0/16
10.210.*.*
10.*.*.*

I know that I can do this: 我知道我可以这样做:

?:(?: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]?)

...but it doesn't take ranges into account. ......但它没有考虑范围。 It merely lets you match an incoming number to see if it's an IP address where each octet is 0-255. 它只允许您匹配传入的号码,以查看它是否是每个八位位组为0-255的IP地址。

EDIT: 编辑:

There's also this function that I found in a comment at php.net on the ip2long function. 我在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

It's short and sweet, but doesn't match the asterisk situation. 它短而甜,但与星号情况不符。 I also don't know if it's entirely accurate because it returns a true result on this when I thought it would be a false: 我也不知道它是否完全准确,因为当我认为它是假的时候它会返回一个真实的结果:

echo ip_in_network("192.168.2.1","192.167.0.0",1);

...but perhaps I misunderstand what the /1 would be. ......但也许我误解了/ 1会是什么。 Perhaps I needed to use /24. 也许我需要使用/ 24。

I adapted an answer from php.net and made it better. 我改编了一个来自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'));

This results with: 结果如下:

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

I've improved on the above example (I have a netmask with /29 so it doesn't work). 我已经改进了上面的例子(我有一个/ 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);
}

If you want to see it in action, add this: 如果你想看到它的实际效果,请添加:

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";

Run it with something like this: 用这样的东西运行它:

print var_dump(check_netmask($argv[1], $argv[2]));

Convert to 32 bit unsigned and use boolean/bitwise operations. 转换为32位无符号并使用布尔/按位运算。

For example, convert 192.168.25.1 to 0xC0A81901. 例如,将192.168.25.1转换为0xC0A81901。

Then, you can see if it matches the mask 192.168.25/24 by converting the dotted-decimal portion of the mask, ie, 0xC0A81900, and creating a 24 bit mask, ie, 0xFFFFFF00. 然后,您可以通过转换掩码的点分十进制部分(即0xC0A81900)并创建24位掩码(即0xFFFFFF00)来查看它是否与掩码192.168.25 / 24匹配。

Perform a bitwise AND between the address in question and the mask and compare to the dotted decimal portion of the mask specification. 在所讨论的地址和掩码之间执行按位AND,并与掩码规范的点分十进制部分进行比较。 For example, 例如,

0xC0A81901 AND 0xFFFFFF00 ==> 0xC0A81900 (result)

compare 0xC0A81900 (result) to 0xC0A81900.

I don't know PHP, but google tells me that PHP has inet_pton(), which is what I would use in C to perform the conversion from dotted-decimal to n-bit unsigned. 我不知道PHP,但谷歌告诉我PHP有inet_pton(),这是我在C中用来执行从点分十进制到n位无符号的转换。 See http://php.net/manual/en/function.inet-pton.php http://php.net/manual/en/function.inet-pton.php

Use this library: https://github.com/S1lentium/IPTools 使用此库: 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

Regex really doesn't sound like the right tool to deal with subnet masks (at least not in decimal). 正则表达式听起来不像是处理子网掩码的正确工具(至少不是十进制)。 It can be done, but it will be ugly. 它可以做到,但它会很难看。

I strongly suggest parsing the string into 4 integers, combining to a 32-bit int, and then using standard bitwise operations (basically a bitwise-AND, and then a comparison). 我强烈建议将字符串解析为4个整数,组合为32位int,然后使用标准的按位运算(基本上是按位-AND,然后进行比较)。

Use strpos to match them as strings. 使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM