[Challenge] IPv4 subnet mask validator

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

[Challenge] IPv4 subnet mask validator

Post 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)
Last edited by VladSun on Wed Mar 31, 2010 8:02 am, edited 1 time in total.
There are 10 types of people in this world, those who understand binary and those who don't
User avatar
Weiry
Forum Contributor
Posts: 323
Joined: Wed Sep 09, 2009 5:55 am
Location: Australia

Re: [Challenge] IPv4 subnet mask validator

Post 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
Last edited by Weiry on Wed Mar 31, 2010 10:34 am, edited 1 time in total.
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Re: [Challenge] IPv4 subnet mask validator

Post 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.
There are 10 types of people in this world, those who understand binary and those who don't
User avatar
Weiry
Forum Contributor
Posts: 323
Joined: Wed Sep 09, 2009 5:55 am
Location: Australia

Re: [Challenge] IPv4 subnet mask validator

Post 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.
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Re: [Challenge] IPv4 subnet mask validator

Post 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 ;)
There are 10 types of people in this world, those who understand binary and those who don't
User avatar
Weiry
Forum Contributor
Posts: 323
Joined: Wed Sep 09, 2009 5:55 am
Location: Australia

Re: [Challenge] IPv4 subnet mask validator

Post 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 :D
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Re: [Challenge] IPv4 subnet mask validator

Post 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) . ')');
}
User avatar
AbraCadaver
DevNet Master
Posts: 2572
Joined: Mon Feb 24, 2003 10:12 am
Location: The Republic of Texas
Contact:

Re: [Challenge] IPv4 subnet mask validator

Post 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]
mysql_function(): WARNING: This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi or PDO_MySQLextension should be used. See also MySQL: choosing an API guide and related FAQ for more information.
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Re: [Challenge] IPv4 subnet mask validator

Post 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 :P

So the challenge now is to increase your script speed :)
There are 10 types of people in this world, those who understand binary and those who don't
User avatar
timWebUK
Forum Contributor
Posts: 239
Joined: Thu Oct 29, 2009 6:48 am
Location: UK

Re: [Challenge] IPv4 subnet mask validator

Post 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.
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Re: [Challenge] IPv4 subnet mask validator

Post 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?
User avatar
AbraCadaver
DevNet Master
Posts: 2572
Joined: Mon Feb 24, 2003 10:12 am
Location: The Republic of Texas
Contact:

Re: [Challenge] IPv4 subnet mask validator

Post 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');
}
Last edited by AbraCadaver on Thu Apr 01, 2010 1:50 pm, edited 1 time in total.
mysql_function(): WARNING: This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi or PDO_MySQLextension should be used. See also MySQL: choosing an API guide and related FAQ for more information.
User avatar
AbraCadaver
DevNet Master
Posts: 2572
Joined: Mon Feb 24, 2003 10:12 am
Location: The Republic of Texas
Contact:

Re: [Challenge] IPv4 subnet mask validator

Post by AbraCadaver »

Last one, I think... :wink:

Code: Select all

function isValidIPv4Mask($mask)
{
    return ($result = log((ip2long($mask)^-1)+1,2)) != 0 && $result-(int)$result == 0;
}
mysql_function(): WARNING: This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi or PDO_MySQLextension should be used. See also MySQL: choosing an API guide and related FAQ for more information.
User avatar
AbraCadaver
DevNet Master
Posts: 2572
Joined: Mon Feb 24, 2003 10:12 am
Location: The Republic of Texas
Contact:

Re: [Challenge] IPv4 subnet mask validator

Post 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)));
}
mysql_function(): WARNING: This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi or PDO_MySQLextension should be used. See also MySQL: choosing an API guide and related FAQ for more information.
User avatar
VladSun
DevNet Master
Posts: 4313
Joined: Wed Jun 27, 2007 9:44 am
Location: Sofia, Bulgaria

Re: [Challenge] IPv4 subnet mask validator

Post 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 :(
There are 10 types of people in this world, those who understand binary and those who don't
Post Reply