I'm looking for quick/simple method for matching a given IP4 dotted quad IP to a CIDR notation mask.
I have a bunch of IPs I need to see if they match a range of IPs.
example:
$ips = array('10.2.1.100', '10.2.1.101', '10.5.1.100', '1.2.3.4'); foreach ($ips as $IP) { if (cidr_match($IP, '10.2.0.0/16') == true) { print "you're in the 10.2 subnet\n"; } }
What would cidr_match()
look like?
It doesn't really have to be simple, but fast would be good. Anything that uses only built-in/common functions is a bonus (as I'm likely to get one person to show me something in pear that does this, but I can't depend on pear or that package being installed where my code is deployed).
The process to determine the subnet mask for a CIDR address is straight forward. The number of bits in the network portion of the address are converted to 1s and right padded with 0s until there are 32 numbers. The sequence of numbers is then divided into 4 octets.
A CIDR block of /0 would allow access to any IP address between 0.0. 0.0 and 255.255. 255.255, while a CIDR block of /32 would only allow access to the IP address that precedes it. Adding /8 after an IP address will block all IP addresses that do not match the first 8 bits, or first number, in the address included.
The CIDR number is typically preceded by a slash “/” and follows the IP address. For example, an IP address of 131.10. 55.70 with a subnet mask of 255.0. 0.0 (which has 8 network bits) would be represented as 131.10.
CIDR notation is really just shorthand for the subnet mask, and represents the number of bits available to the IP address. For instance, the /24 in 192.168. 0.101/24 is equivalent to the IP address 192.168. 0.101 and the subnet mask 255.255.
If only using IPv4:
ip2long()
to convert the IPs and the subnet range into long integerssomething like this should work:
function cidr_match($ip, $range) { list ($subnet, $bits) = explode('/', $range); if ($bits === null) { $bits = 32; } $ip = ip2long($ip); $subnet = ip2long($subnet); $mask = -1 << (32 - $bits); $subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned return ($ip & $mask) == $subnet; }
In a similar situation, I ended up using symfony/http-foundation.
When using this package, your code would look like:
$ips = array('10.2.1.100', '10.2.1.101', '10.5.1.100', '1.2.3.4'); foreach($ips as $IP) { if (\Symfony\Component\HttpFoundation\IpUtils::checkIp($IP, '10.2.0.0/16')) { print "you're in the 10.2 subnet\n"; } }
It also handles IPv6.
Link: https://packagist.org/packages/symfony/http-foundation
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With