Skip to content

Instantly share code, notes, and snippets.

@pavinjosdev
Forked from mdjekic/validate_cidr.php
Last active February 23, 2023 15:19
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pavinjosdev/cb1d636ea9dc2bd201d54107d10650c5 to your computer and use it in GitHub Desktop.
Save pavinjosdev/cb1d636ea9dc2bd201d54107d10650c5 to your computer and use it in GitHub Desktop.
PHP function for validating CIDR notation format (ipv4, ipv6)
<?php
/**
* Validates the format of a CIDR notation string
*
* @param string $cidr
* @return bool
*/
function validateCidr($cidr)
{
$parts = explode('/', $cidr);
if(count($parts) != 2) {
return false;
}
$ip = $parts[0];
$netmask = $parts[1];
if (!preg_match("/^\d+$/", $netmask)){
return false;
}
$netmask = intval($parts[1]);
if($netmask < 0) {
return false;
}
if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return $netmask <= 32;
}
if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
return $netmask <= 128;
}
return false;
}
?>
@pavinjosdev
Copy link
Author

pavinjosdev commented Feb 3, 2019

Improved netmask validation.

@chrisharrisonkiwi
Copy link

This validation unexpectedly let through '2403:7000:8000:900::3a/56'

The essential difference between inet and cidr data types is that inet accepts values with 'nonzero' bits to the right of the netmask, whereas cidr does NOT.

Say, if you have a /8 netmask, the 'cidr' type requires that all the 24 rightmost bits are ZERO.

Note: inet does not have this requirement.

Can you factor in the Zeroes requirement into your method?

@pavinjosdev
Copy link
Author

pavinjosdev commented Jun 7, 2019

This validation unexpectedly let through '2403:7000:8000:900::3a/56'

@chrisharrisonkiwi The function checks for valid CIDR notations of IPv4 and IPv6 addresses (not PostgreSQL data types). 2403:7000:8000:900::3a/56 is a valid CIDR notation of an IPv6 address.

From reading the RFC on IPv6 addressing, relevant page: https://tools.ietf.org/html/rfc4291#page5
it mentions this:

When writing both a node address and a prefix of that node address
   (e.g., the node's subnet prefix), the two can be combined as follows:

      the node address      2001:0DB8:0:CD30:123:4567:89AB:CDEF
      and its subnet number 2001:0DB8:0:CD30::/60

      can be abbreviated as 2001:0DB8:0:CD30:123:4567:89AB:CDEF/60

@joho1968
Copy link

Good gist, but why the double checking?

  $netmask = $parts[1];
  if (!preg_match("/^\d+$/", $netmask)){
    return false;
  }
  $netmask = intval($parts[1]);
  if ($netmask < 0) {
    return false;
  }

preg_match() will return false if there's anything but digits in $netmask, no? So how are you going to end up with a negative number? Also, would it not be a good idea to include a minimum/maximum length in the regexp for preg_match() 🤔

Just my two cents, as I'm unsure of the intent. I don't know if intval() is an actual function or a language construct, but casting using (int) may be faster.

@joho1968
Copy link

Also, I think the preg_match regex could be changed to '/^\d{1,3}$/' for further validation.

@pavinjosdev
Copy link
Author

@joho1968 The integer conversion does appear to be redundant, I believe the check for less than zero was to prevent negative integers from being passed through. Regex can't be good for speed, perhaps you could use some built-ins to check if the netmask is a positive integer?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment