Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP5 calculate IPv6 range from cidr prefix?

Tags:

php

ipv6

cidr

I am able to do this with IPv4 using code snippets from various online sources. I was wondering if there was a way to do it with IPv6.

Basically I just need a form that I can enter an IPv6 address and prefix (ex: f080:42d2:581a::0/68) and it calculates the network address, first useable address, last useable address, and broadcast address. Then just prints to screen. Not looking to store it in a database or anything yet.

like image 200
Damainman Avatar asked Apr 10 '12 08:04

Damainman


People also ask

How many subnets do you get with a 56 CIDR in IPv6?

Medium customers get a /56 prefix. That gives them 256 /64 subnets.

Can CIDR be used for IPv6?

IPv6 networks are written using CIDR notation and use the same CIDR technology as is employed by CIDR on IPv4. Under CIDR, IPv6 unicast addresses can be aggregated with prefixes of arbitrary bit length, similar to IPv4 addresses.

What is the prefix length used for IPv6 subnets?

10.255. The prefix-length in IPv6 is the equivalent of the subnet mask in IPv4. However, rather than being expressed in four octets like it is in IPv4, it is expressed as an integer between 1 through 128.


2 Answers

First of all: IPv6 doesn't have network and broadcast addresses. You can use all addresses in a prefix. Second: On a LAN the prefix length is always (well, 99.x% of the time) a /64. Routing a /68 would break IPv6 features like stateless auto configuration.

Below is a verbose implementation of an IPv6 prefix calculator:

<?php

/*
 * This is definitely not the fastest way to do it!
 */

// An example prefix
$prefix = '2001:db8:abc:1400::/54';

// Split in address and prefix length
list($firstaddrstr, $prefixlen) = explode('/', $prefix);

// Parse the address into a binary string
$firstaddrbin = inet_pton($firstaddrstr);

// Convert the binary string to a string with hexadecimal characters
# unpack() can be replaced with bin2hex()
# unpack() is used for symmetry with pack() below
$firstaddrhex = reset(unpack('H*', $firstaddrbin));

// Overwriting first address string to make sure notation is optimal
$firstaddrstr = inet_ntop($firstaddrbin);

// Calculate the number of 'flexible' bits
$flexbits = 128 - $prefixlen;

// Build the hexadecimal string of the last address
$lastaddrhex = $firstaddrhex;

// We start at the end of the string (which is always 32 characters long)
$pos = 31;
while ($flexbits > 0) {
  // Get the character at this position
  $orig = substr($lastaddrhex, $pos, 1);

  // Convert it to an integer
  $origval = hexdec($orig);

  // OR it with (2^flexbits)-1, with flexbits limited to 4 at a time
  $newval = $origval | (pow(2, min(4, $flexbits)) - 1);

  // Convert it back to a hexadecimal character
  $new = dechex($newval);

  // And put that character back in the string
  $lastaddrhex = substr_replace($lastaddrhex, $new, $pos, 1);

  // We processed one nibble, move to previous position
  $flexbits -= 4;
  $pos -= 1;
}

// Convert the hexadecimal string to a binary string
# Using pack() here
# Newer PHP version can use hex2bin()
$lastaddrbin = pack('H*', $lastaddrhex);

// And create an IPv6 address from the binary string
$lastaddrstr = inet_ntop($lastaddrbin);

// Report to user
echo "Prefix: $prefix\n";
echo "First: $firstaddrstr\n";
echo "Last: $lastaddrstr\n";

?>

It should output:

Prefix: 2001:db8:abc:1400::/54
First: 2001:db8:abc:1400::
Last: 2001:db8:abc:17ff:ffff:ffff:ffff:ffff
like image 188
Sander Steffann Avatar answered Sep 20 '22 07:09

Sander Steffann


For those who stumble upon this question, you can do this more effectively using the dtr_pton and dtr_ntop functions and dTRIP class found on GitHub.

We also have noticed a lack of focus and tools with IPv6 in PHP, and put together this article, http://www.highonphp.com/5-tips-for-working-with-ipv6-in-php, which may be of help to others.

Function Source

This converts and IP to a binary representation:

/**
 * dtr_pton
 *
 * Converts a printable IP into an unpacked binary string
 *
 * @author Mike Mackintosh - [email protected]
 * @param string $ip
 * @return string $bin
 */
function dtr_pton( $ip ){

    if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){
        return current( unpack( "A4", inet_pton( $ip ) ) );
    }
    elseif(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)){
        return current( unpack( "A16", inet_pton( $ip ) ) );
    }

    throw new \Exception("Please supply a valid IPv4 or IPv6 address");

    return false;
}

This converts a binary representation to printable IP:

/**
 * dtr_ntop
 *
 * Converts an unpacked binary string into a printable IP
 *
 * @author Mike Mackintosh - [email protected]
 * @param string $str
 * @return string $ip
 */
function dtr_ntop( $str ){
    if( strlen( $str ) == 16 OR strlen( $str ) == 4 ){
        return inet_ntop( pack( "A".strlen( $str ) , $str ) );
    }

    throw new \Exception( "Please provide a 4 or 16 byte string" );

    return false;
}

Examples

Using the dtr_pton function you can:

$ip = dtr_pton("fe80:1:2:3:a:bad:1dea:dad");
$mask = dtr_pton("ffff:ffff:ffff:ffff:ffff:fff0::");

Get your Network and Broadcast:

var_dump( dtr_ntop( $ip & $mask ) );
var_dump( dtr_ntop( $ip | ~ $mask ) );

And your output would be:

string(18) "fe80:1:2:3:a:ba0::"
string(26) "fe80:1:2:3:a:baf:ffff:ffff"
like image 37
Mike Mackintosh Avatar answered Sep 24 '22 07:09

Mike Mackintosh