Solved

How do I summarise CIDR addresses in PHP?

Posted on 2007-12-05
7
1,611 Views
Last Modified: 2008-02-01
I have a list of CIDR addresses that can be summarised/aggregated and I wish to do this in PHP but cannot find a PEAR package or class through the likes of http://phpclasses.org to do this task.

There is a Perl extension Net::CIDR::Lite and a Ruby class but I'm having a hard time trying to convert this to PHP nor do I want to spend much time on that.

Here is an excerpt of a possible output.
58.6.0.0/15

58.65.248.0/21

58.84.64.0/18

58.84.128.0/18

58.84.192.0/19

58.84.240.0/20

58.87.0.0/20

58.96.0.0/16

58.97.128.0/17

58.104.0.0/13

58.145.128.0/19

58.160.0.0/12

58.178.0.0/15

58.181.64.0/19

58.181.112.0/20

59.86.160.0/19

59.100.0.0/15

59.102.0.0/16

59.151.128.0/18

59.152.224.0/19

59.154.0.0/18

59.154.64.0/19

59.154.96.0/23

59.154.98.0/24

59.154.99.0/25

59.154.99.128/27

59.154.99.160/28

59.154.99.176/29

59.154.99.184/30

59.154.99.192/26

59.154.100.0/22

59.154.104.0/21

59.154.112.0/20

59.154.128.0/17

59.167.0.0/16

59.191.192.0/19

59.191.224.0/20

Open in new window

0
Comment
Question by:kebabs
  • 4
  • 3
7 Comments
 
LVL 2

Expert Comment

by:Waschman
ID: 20433089
You'll need to use the binary operators in PHP (| & << >> etc).
Then you can use the convert-to-binary functions to convert between int and bin.

I assume you know how to calculate summaries with CIDR addressing.
// &	AND

// |	OR

// >>	right-bit-shift

// <<	left-bit-shift

// ^	XOR

$a = 8;

$b = 5;

$c = $a | $b;

echo $c .'<br>';// Prints 13
 

$a = 46;

$b = 248;

$c = $a & $b;

echo $c .'<br>';// Prints 40
 

$a = 10;

$b = 5;

$c = $a ^ $b;

echo $c .'<br>';// Prints 15
 

$a = 128;//10000000

$b = 1;

$c = $a >> $b;//0100000

echo $c .'<br>';// Prints 64
 

$a = 6;//110

$b = 2;

$c = $a << $b;//11000

echo $c .'<br>';// Prints 24

Open in new window

0
 
LVL 8

Author Comment

by:kebabs
ID: 20433610
Bitwise operators give me too much of a head ache, possibly the worst aspect of writing code. Otherwise, I could simply transcode the Perl/Ruby variants. I was hoping for a ready solution.
0
 
LVL 2

Expert Comment

by:Waschman
ID: 20434384
Sorry for that! I searched Google and it didn't come up with anything useful. I want to make a real code that works but this weekend I don't have the time. Maybe tonight when I come home. I'm a nightowl! One question though... are the IP-addresses you submitted already summarized? (I think they are) If so, can you please send some example IP-addresses that should be summarized.

For example: Any IPs that summarize into 58.84.240.0/20.
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 8

Author Comment

by:kebabs
ID: 20435713
The example was not a good one... below is one that summarises better. If you do take the time to write one, make sure to leave a point of contact as I doubt some points here are enough reward :)
# Summarised (9 CIDR addresses)

58.147.128.0/19

117.55.192.0/20

121.100.48.0/21

125.213.192.0/19

202.56.176.0/20

202.86.16.0/20

203.215.32.0/20

210.80.0.0/19

210.80.32.0/19
 

# Unsummarised (72 CIDR addresses)

58.147.128.0/19

62.142.210.32/27

63.243.149.0/27

63.243.150.8/29

63.243.150.16/28

64.86.63.64/27

80.247.139.0/24

82.205.190.0/23

82.205.192.0/21

82.205.202.0/23

82.205.204.0/22

82.205.246.0/25

84.11.26.128/26

84.11.33.0/24

84.11.79.0/24

87.249.84.0/24

117.55.192.0/20

117.104.224.0/21

121.100.48.0/21

121.127.32.0/19

125.213.192.0/19

202.56.176.0/20

202.86.16.0/20

202.133.9.240/28

202.133.13.8/29

202.133.13.16/28

202.133.13.40/29

202.133.13.56/29

202.133.13.96/28

202.174.133.8/29

202.174.133.32/28

202.174.133.144/29

202.174.134.0/29

202.174.144.0/29

202.174.144.216/29

202.174.148.48/29

202.174.148.128/29

202.174.153.184/29

202.174.154.0/29

202.174.154.24/29

202.174.154.96/28

202.174.154.128/29

202.174.154.160/29

202.174.154.180/30

202.174.154.184/30

202.174.154.212/30

202.174.155.128/29

202.174.155.144/29

202.174.155.216/29

202.174.155.240/29

202.174.156.64/26

202.174.156.192/28

202.174.156.208/29

202.174.156.248/29

202.174.157.192/27

202.174.157.232/29

203.88.66.64/29

203.88.66.160/29

203.88.82.240/29

203.88.88.24/29

203.88.88.32/29

203.88.88.40/30

203.88.88.160/29

203.88.88.224/29

203.208.205.192/26

203.215.32.0/20

208.35.53.176/28

210.5.226.0/24

212.116.232.208/28

217.10.167.0/24

217.195.144.24/29

217.195.144.32/30

Open in new window

0
 
LVL 8

Author Comment

by:kebabs
ID: 20435717
BTW, this was data from 2 different sources covering Afghanistan's IP ranges (the country was only for example's sake). I think the unsummarised list is an older/newer list as there seems to be some ranges that are not represented in the summarised one.
0
 
LVL 2

Expert Comment

by:Waschman
ID: 20436269
Hmm, it was more difficult than I thought it should be. I'm too tired I have to much to do in school so I don't have the time. I have a problem with my code-logic, but I'm posting my code so far and some pseudo-code for you to try. I need to point out that I'm studying for the CCNA exam and so my CIDR knowledge is a bit bad, but I think I know how to calculate it (taking common bits and return network_address/bits). See pseudo for more details.

I am sorry for this but I hope you get a solution soon. You can e-mail me on waschman at gmail dot com when you find a solution. Good luck!
/**

 * Get single IP-address class

 *

 * @param string $ip

 */

function get_ip_class($ip)

{

	$class = false;

	$octet = explode('.', $ip);

	if ($octet[0] <= 126) $class = 'A';

	elseif ($octet[0] == 127) $class = 'Loopback';

	elseif ($octet[0] <= 191) $class = 'B';

	elseif ($octet[0] <= 223) $class = 'C';

	elseif ($octet[0] <= 239) $class = 'Multicast';

	else $class = 'Reserved';

	return $class;

}
 
 

/**

 * Enter description here...

 *

 * @param array $ip

 */

function summarize($ips)

{

	$count = count($ips);

	$octet_diff = 0;

	// Save all octets into an array

	for ($pos=0; $pos<4; $pos++) {

		for ($i=0; $i<$count; $i++) {

			$octets[$i] = explode('.', $ips[$i], 4);

			// Save first octet so we can check which octet differs from

			// the others. Crucial when we want to know where to begin checking

			// of binary digits.

			if ($i==0) $first = $octets[$i][$pos];

			// Find different octets

			if ($first != $octets[$i][$pos]) {

				$debug = $i .':'. $pos;//save which IP and octet

				$octet_diff = $pos;

				break;

			}

		}

	}

	

	echo 'Debug: Position of octet that differs is '. $debug .'<br>';

	

	// Logic problem here -- too tired and too much in to do in school instead

	

	// Some kind of main-loop.

	// Check the diffing octet in IP x and x+1.

	// Convert them both to binary (or leave as int).

	// Loop through binary positions, starting from the left.

	// Is ANDing y bit of octet in IP x y bit of octet in IP x+1 equal to 1?

	// If 1 goto next loop.

	// Now compare y bit of octet in IP x+1 and x+2.

	// Is ANDing equal to 1?

	// And so on...

	//

	// Loop through y bit of all IP:s passed to summarize().

	// If every ANDing did get 1 move on to y+1 bit.

	// And repeat above until you encounter an ANDing equal to 0.

	// If 0 then we have all the bits needed to summarize the IP-addresses.

	//   We only have to keep track of the number of bits tested from left

	//   to right to have the number of bits used for the subnetmask.

}
 
 
 

// Should summarize into 192.168.16.0/20

$ip[] = '192.168.1.0';

$ip[] = '192.168.2.0';

$ip[] = '192.168.3.0';

$ip[] = '192.168.4.0';

$ip[] = '192.168.5.0';

$ip[] = '192.168.6.0';

$ip[] = '192.168.7.0';

$ip[] = '192.168.8.0';

$ip[] = '192.168.9.0';

$ip[] = '192.168.10.0';
 

$cidr_ip = summarize($ip);

echo '<p>'. $cidr_ip .'</p>';

Open in new window

0
 
LVL 2

Accepted Solution

by:
Waschman earned 500 total points
ID: 20475225
Hello again!
I got tired of studying all day long so I sat down tonight a few hours and tried to complete the code. And yeehaa, it's working pretty good right now. Atleast during my few tests. The IP-addresses that I've tried are commented almost at the bottom of the script. Just uncomment a block you want to try. Something important to note is that the code is not optimized and there are some missing error-checking-rutines (valid IP etc). Maybe there's an easier way of coding this, but I've done it quite fast and the code works (for the moment!) so I don't care about optimizing the code at the moment.

You said that I should add contact information if I did finish the code so you could give me a better award. That do you mean with this - money? If you mean that I have a paypal account you could send to (if you know what it is). I'll give you my contact information if you give a reply or if you send me an e-mail to waschman at gmail dot com.

Anyway, here's the code that works:
<?php

/**

 * Filename: cidr-summarize.php

 * Owner: Waschman, waschman at gmail dot com, waschman.blogspot.com (swedish)

 */
 
 

/**

 * Get single IP-address class

 *

 * @param string $ip

 */

function get_ip_class($ip)

{

        $class = false;

        $octet = explode('.', $ip);

        if ($octet[0] <= 126) $class = 'A';

        elseif ($octet[0] == 127) $class = 'Loopback';

        elseif ($octet[0] <= 191) $class = 'B';

        elseif ($octet[0] <= 223) $class = 'C';

        elseif ($octet[0] <= 239) $class = 'Multicast';

        else $class = 'Reserved';

        return $class;

}
 
 

/**

 * Decimal to network binary, preceeded with 0

 *

 * @param int $dec

 * @return string

 */

function dec2netbin($dec)

{

	$ip = str_pad((string)(decbin($dec)), 8, "0", STR_PAD_LEFT);

	return $ip;

}
 
 

/**

 * This returns 1 if both variables are 0 and 0 or 1 and 1.

 * Regular binary AND returns 1 if both only are 1 and 1.

 * 

 * OBS! Don't work on numbers, only a single 0 and 1!

 *

 * @param bit $a

 * @param bit $b

 * @return string (0 or 1)

 */

function special_binary_and($a, $b)

{

	if ($a == $b) return '1';

	else return '0';

}

 

 

/**

 * Summarizes IP-addresses into a single CIDR address with mask-suffix.

 *

 * IMPORTANT NOTE: This function needs to be optimized and cleaned. Correct 

 * error-checking should also be implemented. Code not for use in critical 

 * computer environments. Also note that all functions included in this package

 * is required, except for the get_ip_class() function.

 * 

 * @author Waschman, waschman at gmail dot com, waschman.blogspot.com (swedish)

 * @copyright Free for everyone to use, but only if you keep author and copyright information intact.

 * @version 0.8.0

 * @param array $ip

 * @param bool $debugon

 * @return array

 */

function cidrsummarize($ips, $debugon=false)

{

	//

	// Code should be cleaned and optimized.

	//

	

    $count = count($ips);

    $octet_diff = 0;

    // Save all octets into an array

    for ($pos=0; $pos<4; $pos++) {

            for ($i=0; $i<$count; $i++) {

                    $octets[$i] = explode('.', $ips[$i], 4);

                    // Save first octet so we can check which octet differs from

                    // the others. Crucial when we want to know where to begin 

                    // checking of binary digits.

                    if ($i==0)

                    {

                    	$first_ip = $octets[$i][$pos];

                    	echo $debugon?'<p>$firstip:'. $first_ip .'</p>':'';

                    	continue;

                    }

                    // Find different octets

                    echo $debugon?'<p>IS '. $first_ip .' == '. 

                    	$octets[$i][$pos] .'? ':'';

                    if ($first_ip != $octets[$i][$pos]) {

                    	echo $debugon?'no, that means we found an octet that is

                    		different.</p>':'';

                        echo $debugon?'Debug: Position of octet that 

                        	differs is '. $i .':'. $pos:'';

                        $octet_diff = $pos;// Save position that is different

                        break 2;// Exit 2 for-loops

                    }

                    echo $debugon?'yes</p>':'';

            }

    }

    

    

    //

    // Set the number of bits so far to use as mask.

    // This depends on which octet that differ.

    //

    $mask_bits = 0;

    if ($octet_diff < 1) $mask_bits = 0;

    elseif ($octet_diff < 2) $mask_bits = 8;

    elseif ($octet_diff < 3) $mask_bits = 16;

    else $mask_bits = 24;

    

    //

    // Starting main-loop for checking diffing bits.

    //

    $count = count($octets); 

    for ($i=$octet_diff; $i<4; $i++) {

    	if ($i==$octet_diff)

    	{

    		$first_octet = $octets[0][$i];

    	}

    	

    	for ($bit_pos=0; $bit_pos<8; $bit_pos++) {

    	

    		echo $debugon?'<h2>Debug: '. $bit_pos .'</h2>':'';

        	for ($j=0; $j<$count; $j++) {

        		if ($j==0)

        		{

        			continue;

        		}

        		else 

        		{

        			$next_octet = $octets[$j][$i];

        		}

        		

        		echo $debugon?'<p>Debug: First octet='. $first_octet 

        			.', next octet='. $next_octet .'</p>'."\n":'';

        		

        		// ANDing bits!

        		$a = dec2netbin($first_octet);

        		$b = dec2netbin($next_octet);

        		$anding = special_binary_and($a[$bit_pos], $b[$bit_pos]);

        		echo $debugon?'<p>Debug: ('. $a .')'. $a[$bit_pos] .' : ('. 

        			$b .')'. $b[$bit_pos] .'</p>':'';

        		

        		// If we find a bit that doesn't match

        		if ($anding == 0)

        		{

        			$add_mask_bits = $bit_pos;// create the mask

        			$cidr_octet = str_pad('', 8, '0', STR_PAD_LEFT);

        			// create the cidr ip octet (in binary)

        			if ($bit_pos != 0)

        			{

        				$cidr_octet[$bit_pos-1] = 1;

        			}

        			else 

        			{

        				echo $debugon?'Debug: $bit_pos='. $bit_pos .'<br>':'';

        				$cidr_octet[$bit_pos] = 0;

        			}

        			echo $debugon?'<h2>Debug: Different octet, new number: '. 

        				$cidr_octet .'</h2>':'';

        			$cidr_octet = bindec($cidr_octet);// octet in decimal

        			break 3;// exit 3 for-loops

        		}

        	}

        	

    	}

    }

    

    //

    // Create the real cidr ip address from the information gathered

    //

    $cidr_ip = '';

    for ($octet_idx=0; $octet_idx<4; $octet_idx++) {

    	if ($octet_idx==$octet_diff)

    	{

    		$cidr_ip .= $cidr_octet .'.';

    	}

    	elseif ($octet_idx > $octet_diff)

    	{

    		$cidr_ip .= 0 .'.';

    	}

    	else 

    	{

    		$cidr_ip .= $octets[0][$octet_idx] .'.';

    	}

    }

    

    // Cut off trailing dot

    $cidr_ip = substr($cidr_ip, 0, -1);

    

    

    //

    // Create the /bits mask

    //

    $mask_bits += $add_mask_bits;

    

    

    //

    // Create the binary mask

    //

    $mask_binary = '';

    for ($i=1; $i<=32; $i++) {

    	if ($i <= $mask_bits)

    	{

        	if ($i % 8 == 0)

        		$mask_binary .= '1.';//add dot

        	else

        		$mask_binary .= '1';

    	}

    	else 

    	{

    		if ($i % 8 == 0)

        		$mask_binary .= '0.';//add dot

        	else

        		$mask_binary .= '0';

    	}

    }

    

    // Cut off trailing dot

    $mask_binary = substr($mask_binary, 0, -1);

    

    

    //

    // Create the dotted decimal mask

    //

    $mask_decimal_parts = explode('.', $mask_binary, 4);

    $mask_dotted_decimal = bindec($mask_decimal_parts[0]) .'.';

    $mask_dotted_decimal .= bindec($mask_decimal_parts[1]) .'.';

    $mask_dotted_decimal .= bindec($mask_decimal_parts[2]) .'.';

    $mask_dotted_decimal .= bindec($mask_decimal_parts[3]);

    

    

    echo $debugon?'<p>Debug: Mask bits is: /'. $mask_bits  .'<br> 

    	IP-address is: '. $cidr_ip .'<br>

    	Subnetmask is: '. $mask_dotted_decimal .'<br>

    	Subnetmask in binary is: '. $mask_binary .'</p>':'';

	

    $return_array['cidr_ip'] = $cidr_ip;

    $return_array['mask_bits'] = $mask_bits;

    $return_array['mask_dotted_decimal'] = $mask_dotted_decimal;

    $return_array['mask_binary'] = $mask_binary;

    

    return $return_array;

}
 
 

// *** Remove the comments from one of the blocks to test the summarization. ***

 

 

// My example :: Should summarize into 192.168.16.0/20

/*$ip[] = '192.168.1.0';

$ip[] = '192.168.2.0';

$ip[] = '192.168.3.0';

$ip[] = '192.168.4.0';

$ip[] = '192.168.5.0';

$ip[] = '192.168.6.0';

$ip[] = '192.168.7.0';

$ip[] = '192.168.8.0';

$ip[] = '192.168.9.0';

$ip[] = '192.168.10.0';*/
 

// My example :: Should summarize into 128.0.0.0/1

/*$ip[] = '191.168.4.0';

$ip[] = '192.168.5.0';

$ip[] = '193.168.6.0';

$ip[] = '194.168.7.0';*/
 

// Example addresses from your submitted examples

/*$ip[] = '82.205.190.0';

$ip[] = '82.205.192.0';

$ip[] = '82.205.202.0';

$ip[] = '82.205.204.0';

$ip[] = '82.205.246.0';*/
 

$ip[] = '203.88.66.64';

$ip[] = '203.88.66.160';

$ip[] = '203.88.82.240';

$ip[] = '203.88.88.24';

$ip[] = '203.88.88.32';

$ip[] = '203.88.88.40';

$ip[] = '203.88.88.160';

$ip[] = '203.88.88.224';
 

/*$ip[] = '217.10.167.0';

$ip[] = '217.195.144.24';

$ip[] = '217.195.144.32';*/
 
 
 
 
 

// Return an array with information about the summarization.

// You can use this in the URL if you want debugging: [this-file].php?debug=1

$use_debugging = (isset($_GET['debug'])) ? $_GET['debug'] : false;

$cidr_ip = cidrsummarize($ip, $use_debugging);

echo '<hr><p>Summarized IP-address: '. $cidr_ip['cidr_ip'] .'/'. $cidr_ip['mask_bits'] .'</p>';
 

echo '<pre>';

print_r($cidr_ip);

echo '</pre>';

?>

Open in new window

0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

This article will explain how to display the first page of your Microsoft Word documents (e.g. .doc, .docx, etc...) as images in a web page programatically. I have scoured the web on a way to do this unsuccessfully. The goal is to produce something …
This article discusses how to create an extensible mechanism for linked drop downs.
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now