Page 1 of 3
[Challenge] IPv4 subnet mask validator
Posted: Wed Mar 31, 2010 7:42 am
by VladSun
Write a validator for IP version 4 subnet masks in a decimal form. A valid subnet mask value is a one that has leftmost bits set to '1' and the rest rightmost bits set to '0'.
E.g.:
11111111 11111111 11111111 00000000 (i.e. 255.255.255.0) - valid
11111111 11111111 00111111 00000000 - invalid
In this challenge, these subnet masks are also considered invalid:
255.255.255.255 (11111111 11111111 11111111 11111111)
0.0.0.0 (00000000 00000000 00000000 00000000)
The function expected is:
Code: Select all
/**
* @param String $mask - decimal, dot separated representation of a IPv4 subnet mask - e.g. "255.255.255.0"
* @return bool
*/
function isValidIPv4Mask($mask)
Re: [Challenge] IPv4 subnet mask validator
Posted: Wed Mar 31, 2010 9:34 am
by Weiry
Challenge Complete!!
I created a class rather than a single function.
I thought that it might be useful for some people to work in binary? So i added a flag to turn on binary comparison.
Code includes examples.
Code: Select all
<?php
#php subnet validator
class isValidIPv4Mask{
public function __Construct(){}
public function checkSubnet($mask,$isBin=false){
if(!$this->checkChallengeMask($mask,$isBin)){
return false;
}
$segments = explode('.',$mask);
if(!$isBin){
return $this->checkValidity($this->convertToBin($segments));
}
return $this->checkValidity($segments);
}
private function convertToBin($segments){
foreach($segments as $decimal){
$binArr[] = decbin($decimal);
}
return $binArr;
}
private function checkChallengeMask($mask,$isBin=false){
if(!$isBin){
if(preg_match('/(0|255)\.(0|255)\.(0|255)\.(0|255)/',$mask)){ // check if mask is either 0.0.0.0 or 255.255.255.255
return false;
}
}else{
if(preg_match('/(0{8}|1{8})\.(0{8}|1{8})\.(0{8}|1{8})\.(0{8}|1{8})/',$mask)){// check if mask (in binary) is either 0.0.0.0 or 255.255.255.255
return false;
}
}
return true;
}
private function checkValidity($checkArr){
$binStr = implode($checkArr);
$regexFalse = array('/^0/','/(0+)(1+)(0+)/','/(1+)(0+)(1+)/');
foreach($regexFalse as $regex){
if(preg_match($regex,$binStr)){
return false;
}
}
if(preg_match('/^(1+)(0+)/',$binStr)){
return true;
}
return false;
}
};
$subnetValidator = new isValidIPv4Mask();
$subArr = array("255.255.255.254","255.128.255.128","255.63.0.255","255.128.0.256","0.0.0.0","255.255.255.255");
$binArr = array("11111111.11111111.11111111.11111111","00000000.00000000.00000000.00000000","01111111.11111111.11111111.11111111","11111111.10000000.10000000.11111110","01111111.11111111.11111111.11111110","11111111.11111111.11111111.10000000","11111111.11111111.11111111.11111111");
foreach($subArr as $sub){
if($subnetValidator->checkSubnet($sub)){
print "Subnet: {$sub} - True<br/>";
}else{
print "Subnet: {$sub} - False<br/>";
}
}
print "-----------------------------------------------------------------<br/>";
foreach($binArr as $sub){
if($subnetValidator->checkSubnet($sub,true)){
print "Bin Subnet: {$sub} - True<br/>";
}else{
print "Bin Subnet: {$sub} - False<br/>";
}
}
?>
Output:
Code: Select all
Subnet: 255.255.255.254 - True
Subnet: 255.128.255.128 - False
Subnet: 255.63.0.255 - False
Subnet: 255.128.0.256 - False
Subnet: 0.0.0.0 - False
Subnet: 255.255.255.255 - False
-----------------------------------------------------------------
Bin Subnet: 11111111.11111111.11111111.11111111 - False
Bin Subnet: 00000000.00000000.00000000.00000000 - False
Bin Subnet: 01111111.11111111.11111111.11111111 - False
Bin Subnet: 11111111.10000000.10000000.11111110 - False
Bin Subnet: 01111111.11111111.11111111.11111110 - False
Bin Subnet: 11111111.11111111.11111111.10000000 - True
Bin Subnet: 11111111.11111111.11111111.11111111 - False
EDIT::
Updated to correctly reflect valid subnets XD
Re: [Challenge] IPv4 subnet mask validator
Posted: Wed Mar 31, 2010 9:41 am
by VladSun
I must disappoint you.
Bin Subnet: 11110000.11110000.11110000.11110000 - True
Bin Subnet: 11111111.10000000.10000000.11111110 - True
These should be False ...
In a valid subnet mask you start with series of 1s only (most significant bits) and end with 0s only (least significant bits). You can't have one or more 0s between 1s. Like these:
11111111 11111111 11111111 11111000
11111111 11111111 11111111 10000000
11111111 11111111 11111000 00000000
11111111 11111000 00000000 00000000
etc.
Re: [Challenge] IPv4 subnet mask validator
Posted: Wed Mar 31, 2010 9:52 am
by Weiry
Ok.. so:
11111111 11111111 11111111 00000000 - Valid
11111111 11111111 10000000 11111111 - Invalid
?
My assumption was that as long as the subnet address does not exceed a IPv4 subnet address (0.0.0.0 - 255.255.255.255), the subnet must be valid.
Re: [Challenge] IPv4 subnet mask validator
Posted: Wed Mar 31, 2010 10:00 am
by VladSun
Weiry wrote:Ok.. so:
11111111 11111111 11111111 00000000 - Valid
11111111 11111111 10000000 11111111 - Invalid
?
Yep.
Weiry wrote:My assumption was that as long as the subnet address does not exceed a IPv4 subnet address (0.0.0.0 - 255.255.255.255), the subnet must be valid.
That would be too easy

Re: [Challenge] IPv4 subnet mask validator
Posted: Wed Mar 31, 2010 10:36 am
by Weiry
VladSun wrote:That would be too easy

Actually in a way, it made it much easier XD
Updated:: correct code. (and less of it as a result)
Definitely a good challenge, learnt a couple of things doing it too

Re: [Challenge] IPv4 subnet mask validator
Posted: Wed Mar 31, 2010 11:55 am
by Weirdan
Code: Select all
<?php
/**
* @param String $mask - decimal, dot separated representation of a IPv4 subnet mask - e.g. "255.255.255.0"
* @return bool
*/
function isValidIPv4Mask($mask) {
if (in_array($mask, array('255.255.255.255', '0.0.0.0'))) return false;
return false === strpos(sprintf("%'032b", ip2long($mask)), '01');
}
$tests = array(
'255.255.255.0' => true,
'255.255.0.255' => false,
'0.0.0.0' => false,
'255.255.255.255' => false,
'0.255.255.255' => false,
);
foreach ($tests as $mask => $expected) {
assert(var_export($expected, true) . ' === isValidIPv4Mask(' . var_export($mask, true) . ')');
}
Re: [Challenge] IPv4 subnet mask validator
Posted: Wed Mar 31, 2010 4:17 pm
by AbraCadaver
Code: Select all
function isValidIPv4Mask($mask)
{
$bin = decbin(ip2long($mask));
return strlen($bin) == 32 && preg_match('/0/', $bin) && !preg_match('/01/', $bin);
}
Code: Select all
$tests = array('255.255.255.0','255.255.0.255','0.0.0.0','255.255.255.255','0.255.255.255','255');
foreach($tests as $mask) {
echo str_pad($mask, 18) . var_export(isValidIPv4Mask($mask), true) . "\n";
}
[text]255.255.255.0 true
255.255.0.255 false
0.0.0.0 false
255.255.255.255 false
0.255.255.255 false
255 false[/text]
Re: [Challenge] IPv4 subnet mask validator
Posted: Wed Mar 31, 2010 4:51 pm
by VladSun
Very nice solutions
Weiry's one is a bit more complicated than I wanted it to be, but it's more functional. But it fails on 255.255.255.0 - it's a valid subnet mask. It also fails on invalid subnet masks like "255.0".
Weirdan's one is very short and simple (WOW

), but as AbraCadaver noticed it fails on invalid subnet masks like "255.0"
AbraCadaver's solution passes all tests and is still short and simple.
They are all sharing the same idea - string matching

, so I've made some speed tests (10000 iterations x 9 tests):
Weiry : ~3.1 sec
Weirdan : ~1.17 sec
AbraCadaver : ~1.21 sec
My solution: ~0.6 sec
So the challenge now is to increase your script speed

Re: [Challenge] IPv4 subnet mask validator
Posted: Thu Apr 01, 2010 7:18 am
by timWebUK
Am I missing something but...
255.255.255. (0, 128, 192, 224, 240, 248, 252, 254)
Are all valid subnet masks... maybe I'm misunderstanding the challenge.
Re: [Challenge] IPv4 subnet mask validator
Posted: Thu Apr 01, 2010 7:35 am
by Weirdan
timWebUK wrote:Am I missing something but...
255.255.255. (0, 128, 192, 224, 240, 248, 252, 254)
Are all valid subnet masks...
They are. What made you think they are not considered as such?
Re: [Challenge] IPv4 subnet mask validator
Posted: Thu Apr 01, 2010 10:50 am
by AbraCadaver
Should be faster, but you'll have to test the same as the others:
Code: Select all
function isValidIPv4Mask($mask)
{
return ($bin = decbin(ip2long($mask))) && strpos($bin, '0') && !strpos($bin, '01');
}
Re: [Challenge] IPv4 subnet mask validator
Posted: Thu Apr 01, 2010 1:50 pm
by AbraCadaver
Last one, I think...
Code: Select all
function isValidIPv4Mask($mask)
{
return ($result = log((ip2long($mask)^-1)+1,2)) != 0 && $result-(int)$result == 0;
}
Re: [Challenge] IPv4 subnet mask validator
Posted: Thu Apr 01, 2010 2:55 pm
by AbraCadaver
Why do you people have to start these things? I have real work to do
Code: Select all
function isValidIPv4Mask($mask)
{
return (bool)preg_match('/^[1]+0[^1]+$/',sprintf('%032b',ip2long($mask)));
}
Re: [Challenge] IPv4 subnet mask validator
Posted: Fri Apr 02, 2010 3:33 am
by VladSun
Very creative, AbraCadaver
The first one fails on 0.255.255.0 mask.
The second one is OK and its time is ~0.8 sec

It also shares the same idea as the one used in my solution
The third one fails on 255.255.255.254 mask
