Does the IPv4 address match this IP range / mask?

Either with PHP, or with RegExp (or both), how do I match the range of IP addresses?

Examples of Incoming IP Addresses

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 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 does not account for ranges. It just allows you to map the incoming number to see if it is an IP address, where each octet is 0-255.

EDIT:

There is also this function that I found in a comment on php.net on the ip2long function.

 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 is short and sweet, but does not match the situation in the star. I also don't know if this is completely because it returns the true result when I thought it would be a lie:

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

... but maybe I don’t understand what kind of person this is. Maybe I needed to use / 24.

+6
source share
5 answers

I adapted the php.net answer and made it better.

 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 is the result:

 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 
+10
source

Converting to 32-bit unsigned and using boolean / bitwise operations.

For example, convert 192.168.25.1 to 0xC0A81901.

Then you can see if it matches the mask 192.168.25 / 24 by converting the decimal part to the dotted dot of the mask, i.e. 0xC0A81900, and creating a 24-bit mask, i.e. 0xFFFFFF00.

Perform a bitwise AND between the specified address and mask and compare with the dotted decimal part of the mask specification. For instance,

 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 I would use in C to convert from decimal point to unsigned n-bit. See http://php.net/manual/en/function.inet-pton.php

+4
source

I improved the above example (I have a netmask with / 29, so it doesn't work).

 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 the following:

 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 like this:

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

Regex really doesn't look like the right tool for working with subnet masks (at least not decimal). It can be done, but it will be ugly.

I highly recommend breaking the string into 4 integers, combining them with a 32-bit int, and then using standard bitwise operations (mostly bitwise-AND followed by comparison).

+1
source

Use strpos to match them as strings.

 <?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); ?> 
-2
source

Source: https://habr.com/ru/post/914751/


All Articles