Documentation on ip2long
ip2long = Converts a string containing an (IPv4) Internet Protocol dotted address into a long integer
The function ip2long() generates an long integer representation of IPv4 Internet network address from its Internet standard format (dotted string) representation.
ip2long() will also work with non-complete IP addresses. Read » http://publibn.boulder.ibm.com/doc_link/en_US/a_doc_lib/libs/commtrf2/inet_addr.htm for more info.
Usage, params, and more on ip2long
int ip2long ( string $ip_address
)
ip_address
A standard format address.
Returns the long integer or FALSE
if ip_address
is invalid.
Notes and warnings on ip2long
Basic example of how to use: ip2long
Example #1 ip2long() Example
<?php
$ip = gethostbyname('www.example.com');
$out = "The following URLs are equivalent:<br />\n";
$out .= 'http://www.example.com/, http://' . $ip . '/, and http://' . sprintf("%u", ip2long($ip)) . "/<br />\n";
echo $out;
?>
Example #2 Displaying an IP address
This second example shows how to print a converted address with the printf() function in both PHP 4 and PHP 5:
<?php
$ip = gethostbyname('www.example.com');
$long = ip2long($ip);
if ($long == -1 || $long === FALSE) {
echo 'Invalid IP, please try again';
} else {
echo $ip . "\n"; // 192.0.34.166
echo $long . "\n"; // -1073732954
printf("%u\n", ip2long($ip)); // 3221234342
}
?>
Other code examples of ip2long being used
A quick method to convert a netmask (ex: 255.255.255.240) to a cidr mask (ex: /28):
<?php
function mask2cidr($mask){
$long = ip2long($mask);
$base = ip2long('255.255.255.255');
return 32-log(($long ^ $base)+1,2);
/* xor-ing will give you the inverse mask,
log base 2 of that +1 will return the number
of bits that are off in the mask and subtracting
from 32 gets you the cidr notation */
}
?>
Hi,
based on examples above I have mixed class IPFilter with netMatch function wich gives me complete class for IP4 check including CIDR IP format:
<?php
class IP4Filter {
private static $_IP_TYPE_SINGLE = 'single';
private static $_IP_TYPE_WILDCARD = 'wildcard';
private static $_IP_TYPE_MASK = 'mask';
private static $_IP_TYPE_CIDR = 'CIDR';
private static $_IP_TYPE_SECTION = 'section';
private $_allowed_ips = array();
public function __construct($allowed_ips) {
$this->_allowed_ips = $allowed_ips;
}
public function check($ip, $allowed_ips = null) {
$allowed_ips = $allowed_ips ? $allowed_ips : $this->_allowed_ips;
foreach ($allowed_ips as $allowed_ip) {
$type = $this->_judge_ip_type($allowed_ip);
$sub_rst = call_user_func(array($this, '_sub_checker_' . $type), $allowed_ip, $ip);
if ($sub_rst) {
return true;
}
}
return false;
}
private function _judge_ip_type($ip) {
if (strpos($ip, '*')) {
return self :: $_IP_TYPE_WILDCARD;
}
if (strpos($ip, '/')) {
$tmp = explode('/', $ip);
if (strpos($tmp[1], '.')) {
return self :: $_IP_TYPE_MASK;
} else {
return self :: $_IP_TYPE_CIDR;
}
}
if (strpos($ip, '-')) {
return self :: $_IP_TYPE_SECTION;
}
if (ip2long($ip)) {
return self :: $_IP_TYPE_SINGLE;
}
return false;
}
private function _sub_checker_single($allowed_ip, $ip) {
return (ip2long($allowed_ip) == ip2long($ip));
}
private function _sub_checker_wildcard($allowed_ip, $ip) {
$allowed_ip_arr = explode('.', $allowed_ip);
$ip_arr = explode('.', $ip);
for ($i = 0; $i < count($allowed_ip_arr); $i++) {
if ($allowed_ip_arr[$i] == '*') {
return true;
} else {
if (false == ($allowed_ip_arr[$i] == $ip_arr[$i])) {
return false;
}
}
}
}
private function _sub_checker_mask($allowed_ip, $ip) {
list($allowed_ip_ip, $allowed_ip_mask) = explode('/', $allowed_ip);
$begin = (ip2long($allowed_ip_ip) & ip2long($allowed_ip_mask)) + 1;
$end = (ip2long($allowed_ip_ip) | (~ ip2long($allowed_ip_mask))) + 1;
$ip = ip2long($ip);
return ($ip >= $begin && $ip <= $end);
}
private function _sub_checker_section($allowed_ip, $ip) {
list($begin, $end) = explode('-', $allowed_ip);
$begin = ip2long($begin);
$end = ip2long($end);
$ip = ip2long($ip);
return ($ip >= $begin && $ip <= $end);
}
private function _sub_checker_CIDR($CIDR, $IP) {
list ($net, $mask) = explode('/', $CIDR);
return ( ip2long($IP) & ~((1 << (32 - $mask)) - 1) ) == ip2long($net);
}
}
?>
For me this code works great, so I wanna thank to all You guys!!!
The following function ipToHex will take an IP (v4 or v6 formatted) and if it is valid, will return a 32 byte hex string representing that address. Requires php >= 5.2 as it uses the filter_var function.
<?php
function ipToHex($ipAddress) {
$hex = '';
if(strpos($ipAddress, ',') !== false) {
$splitIp = explode(',', $ipAddress);
$ipAddress = trim($splitIp[0]);
}
$isIpV6 = false;
$isIpV4 = false;
if(filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
$isIpV6 = true;
}
else if(filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
$isIpV4 = true;
}
if(!$isIpV4 && !$isIpV6) {
return false;
}
// IPv4 format
if($isIpV4) {
$parts = explode('.', $ipAddress);
for($i = 0; $i < 4; $i++) {
$parts[$i] = str_pad(dechex($parts[$i]), 2, '0', STR_PAD_LEFT);
}
$ipAddress = '::'.$parts[0].$parts[1].':'.$parts[2].$parts[3];
$hex = join('', $parts);
}
// IPv6 format
else {
$parts = explode(':', $ipAddress);
// If this is mixed IPv6/IPv4, convert end to IPv6 value
if(filter_var($parts[count($parts) - 1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
$partsV4 = explode('.', $parts[count($parts) - 1]);
for($i = 0; $i < 4; $i++) {
$partsV4[$i] = str_pad(dechex($partsV4[$i]), 2, '0', STR_PAD_LEFT);
}
$parts[count($parts) - 1] = $partsV4[0].$partsV4[1];
$parts[] = $partsV4[2].$partsV4[3];
}
$numMissing = 8 - count($parts);
$expandedParts = array();
$expansionDone = false;
foreach($parts as $part) {
if(!$expansionDone && $part == '') {
for($i = 0; $i <= $numMissing; $i++) {
$expandedParts[] = '0000';
}
$expansionDone = true;
}
else {
$expandedParts[] = $part;
}
}
foreach($expandedParts as &$part) {
$part = str_pad($part, 4, '0', STR_PAD_LEFT);
}
$ipAddress = join(':', $expandedParts);
$hex = join('', $expandedParts);
}
// Validate the final IP
if(!filter_var($ipAddress, FILTER_VALIDATE_IP)) {
return false;
}
return strtolower(str_pad($hex, 32, '0', STR_PAD_LEFT));
}
$ips = array(
'::192.168.0.2',
'0:0:0:0:0:0:192.168.0.2',
'192.168.0.2',
'::C0A8:2',
'0:0:0:0:0:0:C0A8:2'
);
$finals = array();
foreach($ips as $ip) {
$finals[] = ipToHex($ip);
}
var_dump($finals);
?>
Convert IP to unsigned long
<?php
//$strIP : IP in String-format
//$lngIP : IP in unsigned long
if (($lngIP=ip2long($strIP)) < 0){ $lngIP += 4294967296 ;}
// ADD 2^32
thats all.
?>
Here some working ip2long6 and long2ip6 functions - keep in mind that this needs php gmp-lib:
<?php
$ipv6 = "2001:4860:a005::68";
function ip2long6($ipv6) {
$ip_n = inet_pton($ipv6);
$bits = 15; // 16 x 8 bit = 128bit
while ($bits >= 0) {
$bin = sprintf("%08b",(ord($ip_n[$bits])));
$ipv6long = $bin.$ipv6long;
$bits--;
}
return gmp_strval(gmp_init($ipv6long,2),10);
}
function long2ip6($ipv6long) {
$bin = gmp_strval(gmp_init($ipv6long,10),2);
if (strlen($bin) < 128) {
$pad = 128 - strlen($bin);
for ($i = 1; $i <= $pad; $i++) {
$bin = "0".$bin;
}
}
$bits = 0;
while ($bits <= 7) {
$bin_part = substr($bin,($bits*16),16);
$ipv6 .= dechex(bindec($bin_part)).":";
$bits++;
}
// compress
return inet_ntop(inet_pton(substr($ipv6,0,-1)));
}
print $ipv6long = ip2long6($ipv6)."\n";
print $ipv6 = long2ip6($ipv6long)."\n";
?>
outputs:
42541956150894553250710573749450571880
2001:4860:a005::68
In 32bits systems, you cannot convert ipv6 to long, but you can convert ip2bin and bin2ip
This function converts ipv4 and ipv6, returns false if ip is not valid
<?php
function ip2bin($ip)
{
if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false)
return base_convert(ip2long($ip),10,2);
if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false)
return false;
if(($ip_n = inet_pton($ip)) === false) return false;
$bits = 15; // 16 x 8 bit = 128bit (ipv6)
while ($bits >= 0)
{
$bin = sprintf("%08b",(ord($ip_n[$bits])));
$ipbin = $bin.$ipbin;
$bits--;
}
return $ipbin;
}
function bin2ip($bin)
{
if(strlen($bin) <= 32) // 32bits (ipv4)
return long2ip(base_convert($bin,2,10));
if(strlen($bin) != 128)
return false;
$pad = 128 - strlen($bin);
for ($i = 1; $i <= $pad; $i++)
{
$bin = "0".$bin;
}
$bits = 0;
while ($bits <= 7)
{
$bin_part = substr($bin,($bits*16),16);
$ipv6 .= dechex(bindec($bin_part)).":";
$bits++;
}
return inet_ntop(inet_pton(substr($ipv6,0,-1)));
}
?>
To get the network adress out of the broadcast adress and netmask just to an AND on it:
<?php
// simple example
$bcast = ip2long("192.168.178.255");
$smask = ip2long("255.255.255.0");
$nmask = $bcast & $smask;
echo long2ip($nmask); // Will give 192.168.178.0
?>
With this example you are able to check if a given host is in your own local net or not (on linux):
<?php
/**
* Check if a client IP is in our Server subnet
*
* @param string $client_ip
* @param string $server_ip
* @return boolean
*/
function clientInSameSubnet($client_ip=false,$server_ip=false) {
if (!$client_ip)
$client_ip = $_SERVER['REMOTE_ADDR'];
if (!$server_ip)
$server_ip = $_SERVER['SERVER_ADDR'];
// Extract broadcast and netmask from ifconfig
if (!($p = popen("ifconfig","r"))) return false;
$out = "";
while(!feof($p))
$out .= fread($p,1024);
fclose($p);
// This is because the php.net comment function does not
// allow long lines.
$match = "/^.*".$server_ip;
$match .= ".*Bcast:(\d{1,3}\.\d{1,3}i\.\d{1,3}\.\d{1,3}).*";
$match .= "Mask:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/im";
if (!preg_match($match,$out,$regs))
return false;
$bcast = ip2long($regs[1]);
$smask = ip2long($regs[2]);
$ipadr = ip2long($client_ip);
$nmask = $bcast & $smask;
return (($ipadr & $smask) == ($nmask & $smask));
}
?>
I wanted to build on what kaputt and spinyn contributed in a way that I think is a little more intuitive (e.g., let sprintf do all the binary conversion and padding, and let substr_compare do the trimming and comparing):
<?php
function ip_in_network($ip, $net_addr, $net_mask){
if($net_mask <= 0){ return false; }
$ip_binary_string = sprintf("%032b",ip2long($ip));
$net_binary_string = sprintf("%032b",ip2long($net_addr));
return (substr_compare($ip_binary_string,$net_binary_string,0,$net_mask) === 0);
}
ip_in_network("192.168.2.1","192.168.2.0",24); //true
ip_in_network("192.168.6.93","192.168.0.0",16); //true
ip_in_network("1.6.6.6","128.168.2.0",1); //false
?>
here is netMatch function which is the fastest I could wrote, I hope developers will include native function soon...
<?php
function netMatch ($CIDR,$IP) {
list ($net, $mask) = explode ('/', $CIDR);
return ( ip2long ($IP) & ~((1 << (32 - $mask)) - 1) ) == ip2long ($net);
}
?>
Another function for matching $_SERVER['REMOTE_ADDR'] against CIDR.
<?php
function matchCIDR($addr, $cidr) {
list($ip, $mask) = explode('/', $cidr);
$mask = 0xffffffff << (32 - $mask);
return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
}
?>
<?php
function CalculateIPRange($iprange)
{
// Daevid Vincent [daevid@daevid.com] 10.13.03
// This function will return an array of either a negative error code
// or all possible IP addresses in the given range.
// format is NNN.NNN.NNN.NNN - NNN.NNN.NNN.NNN (spaces are okay)
$temp = preg_split("/-/",$iprange, -1, PREG_SPLIT_NO_EMPTY);
$QRange1 = $temp[0];
$QRange2 = $temp[1];
if ($QRange2 == "") return array($QRange1); //special case, they didn't put a second quad parameter
//basic error handling to see if it is generally a valid IP in the form N.N.N.N
if ( preg_match("/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/",$QRange1) != 1 ) return array(-1);
if ( preg_match("/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/",$QRange2) != 1 ) return array(-1);
$quad1 = explode(".",$QRange1);
$quad2 = explode(".",$QRange2);
reset ($quad1);
while (list ($key, $val) = each ($quad1))
{
$quad1[$key] = intval($val);
if ($quad1[$key] < 0 || $quad1[$key] > 255) return array(-2);
}
reset ($quad2);
while (list ($key, $val) = each ($quad2))
{
$quad2[$key] = intval($val);
if ($quad2[$key] < 0 || $quad2[$key] > 255) return array(-2);
}
$startIP_long = sprintf("%u",ip2long($QRange1));
$endIP_long = sprintf("%u",ip2long($QRange2));
$difference = $endIP_long - $startIP_long;
//echo "startIP_long = ".$startIP_long." and endIP_long = ".$endIP_long." difference = ".$difference."<BR>";
$ip = array();
$k = 0;
for ($i = $startIP_long; $i <= $endIP_long; $i++)
{
$temp = long2ip($i);
//this is a total hack. there must be a better way.
$thisQuad = explode(".",$temp);
if ($thisQuad[3] > 0 && $thisQuad[3] < 255)
$ip[$k++] = $temp;
}
return $ip;
} //CalculateIPRange()
?>
<?php
/**
*
*get the first ip and last ip from cidr(network id and mask length)
* i will integrate this function into "Rong Framework" :)
* @author admin@wudimei.com
* @param string $cidr 56.15.0.6/16 , [network id]/[mask length]
* @return array $ipArray = array( 0 =>"first ip of the network", 1=>"last ip of the network" );
* Each element of $ipArray's type is long int,use long2ip( $ipArray[0] ) to convert it into ip string.
* example:
* list( $long_startIp , $long_endIp) = getIpRange( "56.15.0.6/16" );
* echo "start ip:" . long2ip( $long_startIp );
* echo "<br />";
* echo "end ip:" . long2ip( $long_endIp );
*/
function getIpRang( $cidr) {
list($ip, $mask) = explode('/', $cidr);
$maskBinStr =str_repeat("1", $mask ) . str_repeat("0", 32-$mask ); //net mask binary string
$inverseMaskBinStr = str_repeat("0", $mask ) . str_repeat("1", 32-$mask ); //inverse mask
$ipLong = ip2long( $ip );
$ipMaskLong = bindec( $maskBinStr );
$inverseIpMaskLong = bindec( $inverseMaskBinStr );
$netWork = $ipLong & $ipMaskLong;
$start = $netWork+1;//去掉网络号 ,ignore network ID(eg: 192.168.1.0)
$end = ($netWork | $inverseIpMaskLong) -1 ; //去掉广播地址 ignore brocast IP(eg: 192.168.1.255)
return array( $start, $end );
}
?>
I would just like to try and clear up simply that if storing IPV4 addresses in an SQL database you should use an unsigned int (4 bytes).
The easiest way to do this in PHP is to use sprintf():
<?php
$dottedFormatAddress = '127.0.0.1';
$ipv4address = sprintf("%u", ip2long($dottedFormatAddress));
?>
Primary reasons are it's compatible with database functions like MySQL's INET_ATON & INET_NTOA (which also use unsigned int's), it's efficient, and it's the most common format used by IP lookup databases.
I've write an IPFilter class to check if a ip is in given ips.
<?php
class IPFilter
{
private static $_IP_TYPE_SINGLE = 'single';
private static $_IP_TYPE_WILDCARD = 'wildcard';
private static $_IP_TYPE_MASK = 'mask';
private static $_IP_TYPE_SECTION = 'section';
private $_allowed_ips = array();
public function __construct($allowed_ips)
{
$this -> _allowed_ips = $allowed_ips;
}
public function check($ip, $allowed_ips = null)
{
$allowed_ips = $allowed_ips ? $allowed_ips : $this->_allowed_ips;
foreach($allowed_ips as $allowed_ip)
{
$type = $this -> _judge_ip_type($allowed_ip);
$sub_rst = call_user_func(array($this,'_sub_checker_' . $type), $allowed_ip, $ip);
if ($sub_rst)
{
return true;
}
}
return false;
}
private function _judge_ip_type($ip)
{
if (strpos($ip, '*'))
{
return self :: $_IP_TYPE_WILDCARD;
}
if (strpos($ip, '/'))
{
return self :: $_IP_TYPE_MASK;
}
if (strpos($ip, '-'))
{
return self :: $_IP_TYPE_SECTION;
}
if (ip2long($ip))
{
return self :: $_IP_TYPE_SINGLE;
}
return false;
}
private function _sub_checker_single($allowed_ip, $ip)
{
return (ip2long($allowed_ip) == ip2long($ip));
}
private function _sub_checker_wildcard($allowed_ip, $ip)
{
$allowed_ip_arr = explode('.', $allowed_ip);
$ip_arr = explode('.', $ip);
for($i = 0;$i < count($allowed_ip_arr);$i++)
{
if ($allowed_ip_arr[$i] == '*')
{
return true;
}
else
{
if (false == ($allowed_ip_arr[$i] == $ip_arr[$i]))
{
return false;
}
}
}
}
private function _sub_checker_mask($allowed_ip, $ip)
{
list($allowed_ip_ip, $allowed_ip_mask) = explode('/', $allowed_ip);
$begin = (ip2long($allowed_ip_ip) &ip2long($allowed_ip_mask)) + 1;
$end = (ip2long($allowed_ip_ip) | (~ip2long($allowed_ip_mask))) + 1;
$ip = ip2long($ip);
return ($ip >= $begin && $ip <= $end);
}
private function _sub_checker_section($allowed_ip, $ip)
{
list($begin, $end) = explode('-', $allowed_ip);
$begin = ip2long($begin);
$end = ip2long($end);
$ip = ip2long($ip);
return ($ip >= $begin && $ip <= $end);
}
}
?>
useage:
<?php
$filter = new IPFilter(
array(
'127.0.0.1',
'172.0.0.*',
'173.0.*.*',
'126.1.0.0/255.255.0.0',
'125.0.0.1-125.0.0.9',
));
$filter -> check('126.1.0.2');
?>
The manual states that "ip2long() will also work with non-complete IP addresses", however this is system-dependant so cannot be relied upon. For example, on my system ip2long() will return FALSE for incomplete addresses:
<?php
var_dump(ip2long("255.255")); // bool(false)
?>
This is because ip2long will use inet_pton if available, which does not support non-complete addresses. If inet_pton is not available on your system, inet_addr will be used and incomplete addresses will work as stated.
Just want to add a comment to kaputt's valuable contribution to the task of matching hosts to ip ranges, efficiently. The script works fine if the binary representation of the ip involves no leading zeros. Unfortunately, the way decbin() seems to work, leading zeros in the binary representation of the first ip quad get dropped. That is a serious matter if you're trying to match all possible candidates in the checklist. In those cases the leading zeros need to be added back to get accurate matches for values in the first quad between 0-127 (or the binary equivalent, 0-01111111).
The solution I came up with to address this issue was the following function:
<?php
function addLeadingZero($ip) {
if (($result = (32 - strlen($ip))) > 0)
return str_repeat("0", $result).$ip;
}
?>
Hi,
Please find below a little function I wrote for handling IP addresses as unsigned integers.
This function will take an IP address or IP number in ANY FORMAT (that I can think of) and will return it's decimal unsigned equivalent, as a string.
<?php
function myip2long($ip){
if (is_numeric($ip)){
return sprintf("%u", floatval($ip));
} else {
return sprintf("%u", floatval(ip2long($ip)));
}
}
?>
Here is is in action:
<?php
$ip['DottedQuadDec'] = "192.168.255.109";
$ip['PosIntStr'] = "3232300909";
$ip['NegIntStr'] = "-1062666387";
$ip['PosInt'] = 3232300909;
$ip['NegInt'] = -1062666387;
$ip['DottedQuadHex'] = "0xc0.0xA8.0xFF.0x6D";
$ip['DottedQuadOct'] = "0300.0250.0377.0155";
$ip['HexIntStr'] = "0xC0A8FF6D";
$ip['HexInt'] = 0xC0A8FF6D;
printf("\n% 16s : % 20s => % 11s => % 16s","Input Type","Input Value","Converted","Converted Back");
echo "\n ------------------------------------------------------------------";
foreach ($ip as $type => $value){
$ipnum = myip2long($value);
printf("\n% 16s : % 20s => % 11s => % 16s",$type,$value,$ipnum,long2ip($ipnum));
}
?>
I had a problem with calling this function with REMOTE_ADDR on my lighttpd web server. Turned out that IPv6 was installed on this server, so even though REMOTE_ADDR was an IPv4 address, it was being formatted using IPv6's IPv4 compatibility mode. For example, 10.0.0.1 was becoming ::ffff:10.0.0.1, which caused iplong to report the address as invalid instead of parsing it correctly.
The correct fix would of course be to update my infrastructure so that it's properly IPv6-compatible; but in the context of my particular situation, this would involve a lot of re-engineering. So in the meantime, I worked around the issue using this quick and dirty hack:
<?php
$ip = htmlspecialchars($_SERVER['REMOTE_ADDR']);
if (strpos($ip, '::') === 0) {
$ip = substr($ip, strrpos($ip, ':')+1);
}
$host = ip2long($ip);
?>
Ugly but functional.
I wrote a small function to validate a netmask (We have a form where the netmask of a given server is entered in, and I wanted to make sure it was valid). Hope this is useful.
<?php
// Netmask Validator //
function checkNetmask($ip) {
if (!ip2long($ip)) {
return false;
} elseif(strlen(decbin(ip2long($ip))) != 32 && ip2long($ip) != 0) {
return false;
} elseif(ereg('01',decbin(ip2long($ip))) || !ereg('0',decbin(ip2long($ip)))) {
return false;
} else {
return true;
}
}
?>
Here's a simple IP address match checking function.
It takes 3 arguments: ip address to check (after ip2long), ip address to check against (after ip2long), and mask to check against (integer 0-32).
Just make sure you perform ip2long on the ip addresses before inputting them to the function.
<?php
function match_ip($check_ip, $match_ip, $match_mask = 32)
{
for ($i = 0 ; $i < $match_mask ; $i++)
{
$n = pow(2, 31 - $i) ;
if (($n & $check_ip) != ($n & $match_ip))
{
return FALSE ;
}
}
return TRUE ;
}
?>
I've been looking for a function like this for a while, i hope it helps someone.
Below is a merged form of all various notes, and a better (and correct) network matching function.
<?php
function net_match($network, $ip) {
// determines if a network in the form of 192.168.17.1/16 or
// 127.0.0.1/255.255.255.255 or 10.0.0.1 matches a given ip
$ip_arr = explode('/', $network);
$network_long = ip2long($ip_arr[0]);
$x = ip2long($ip_arr[1]);
$mask = long2ip($x) == $ip_arr[1] ? $x : 0xffffffff << (32 - $ip_arr[1]);
$ip_long = ip2long($ip);
// echo ">".$ip_arr[1]."> ".decbin($mask)."\n";
return ($ip_long & $mask) == ($network_long & $mask);
}
echo net_match('192.168.17.1/16', '192.168.15.1')."\n"; // returns true
echo net_match('127.0.0.1/255.255.255.255', '127.0.0.2')."\n"; // returns false
echo net_match('10.0.0.1', '10.0.0.1')."\n"; // returns true
?>
Note: ip2long and long2ip do not function as the c linux functions inet_addr and inet_ntoa. They store the long in reverse byte order (little endian vs big endian i guess).
For example you send 10.0.0.1 to inet_ntoa you take the long from the result and you pass it to long2ip you get 1.0.0.10. You won't run into this issue unless you use a database both from c linux programs and php scripts.
My first idea was to reverse the long, but unfortunatley the long was stored as unsigned and i got a lot of problems doing calculations with it (some operations would work well others not; probably it was stored as float i do not know for sure...)
So my solution was
<?php
function str_rev_ip($str)
{
$ar=explode(".",$str);
return "$ar[3].$ar[2].$ar[1].$ar[0]";
}
?>
and i take the result from inet_ntoa and parse it as str_rev_ip(long2ip($var))
a little function to make a range in this form:
0.0.0.1/0.0.0.255 ==> 0.0.0.1/255
0.0.0.1/255.255.255.255 ==> 0.0.0.1/255.255.255.255
<?php
$primo = "62.4.32.0";
$ultimo = "62.4.63.255";
echo do_range($primo,$ultimo); //Example
function do_range($primo,$ultimo) {
list($a,$b,$c,$d)=explode(".",$primo);
list($e,$f,$g,$h)=explode(".",$ultimo);
if ($a !== $e) return "$primo/$ultimo";
else {
if ($b !== $f) return "$primo/$f.$g.$h";
else{
if ($c !== $g) return "$primo/$g.$h";
else {
if ($d !== $h) return "$primo/$h";
else return -1; //error
}
}
}
}
?>
Please write me if you have any suggestion
remixing mediator's function further:
<?php
function matchCIDR($addr, $cidr) {
list($ip, $mask) = explode('/', $cidr);
return (ip2long($addr) >> (32 - $mask) == ip2long($ip) >> (32 - mask));
}
?>
I re-wrote the functions from jbothe at hotmail dot com as a little exercise in OO and added a couple of extra functions.
<?php
//--------------
// IPv4 class
class ipv4
{
var $address;
var $netbits;
//--------------
// Create new class
function ipv4($address,$netbits)
{
$this->address = $address;
$this->netbits = $netbits;
}
//--------------
// Return the IP address
function address() { return ($this->address); }
//--------------
// Return the netbits
function netbits() { return ($this->netbits); }
//--------------
// Return the netmask
function netmask()
{
return (long2ip(ip2long("255.255.255.255")
<< (32-$this->netbits)));
}
//--------------
// Return the network that the address sits in
function network()
{
return (long2ip((ip2long($this->address))
& (ip2long($this->netmask()))));
}
//--------------
// Return the broadcast that the address sits in
function broadcast()
{
return (long2ip(ip2long($this->network())
| (~(ip2long($this->netmask())))));
}
//--------------
// Return the inverse mask of the netmask
function inverse()
{
return (long2ip(~(ip2long("255.255.255.255")
<< (32-$this->netbits))));
}
}
$ip = new ipv4("192.168.2.1",24);
print "Address: $ip->address()\n";
print "Netbits: $ip->netbits()\n";
print "Netmask: $ip->netmask()\n";
print "Inverse: $ip->inverse()\n";
print "Network: $ip->network()\n";
print "Broadcast: $ip->broadcast()\n";
?>
If you want to validate IPs using this function, please be careful:
The function filter_var should be used instead for validating IPs.
<?php
$ip = '192.168.0355.24';
var_dump(ip2long($ip) !== false); // true (expected false)
var_dump(filter_var($ip, FILTER_VALIDATE_IP) !== false); // false
$ip = '192.168.355.24';
var_dump(ip2long($ip) !== false); // false
var_dump(filter_var($ip, FILTER_VALIDATE_IP) !== false); // false
?>
Universal ip4/ip6
<?php
// encoded --
function encode_ip ($ip)
{
$d = explode('.', $ip);
if (count($d) == 4) return sprintf('%02x%02x%02x%02x', $d[0], $d[1], $d[2], $d[3]);
$d = explode(':', preg_replace('/(^:)|(:$)/', '', $ip));
$res = '';
foreach ($d as $x)
$res .= sprintf('%0'. ($x == '' ? (9 - count($d)) * 4 : 4) .'s', $x);
return $res;
}
// decoded
function decode_ip($int_ip)
{
function hexhex($value) { return dechex(hexdec($value)); };
if (strlen($int_ip) == 32) {
$int_ip = substr(chunk_split($int_ip, 4, ':'), 0, 39);
$int_ip = ':'. implode(':', array_map("hexhex", explode(':',$int_ip))) .':';
preg_match_all("/(:0)+/", $int_ip, $zeros);
if (count($zeros[0]) > 0) {
$match = '';
foreach($zeros[0] as $zero)
if (strlen($zero) > strlen($match))
$match = $zero;
$int_ip = preg_replace('/'. $match .'/', ':', $int_ip, 1);
}
return preg_replace('/(^:([^:]))|(([^:]):$)/', '$2$4', $int_ip);
}
$hexipbang = explode('.', chunk_split($int_ip, 2, '.'));
return hexdec($hexipbang[0]). '.' . hexdec($hexipbang[1]) . '.' . hexdec($hexipbang[2]) . '.' . hexdec($hexipbang[3]);
}
?>
DB:
`user_ip` varchar(32) DEFAULT NULL
Here's a function I whipped up today to deduce some important network information given a hostname (or its IP address) and its subnet mask:
<?php
function find_net($host,$mask) {
### Function to determine network characteristics
### $host = IP address or hostname of target host (string)
### $mask = Subnet mask of host in dotted decimal (string)
### returns array with
### "cidr" => host and mask in CIDR notation
### "network" => network address
### "broadcast" => broadcast address
###
### Example: find_net("192.168.37.215","255.255.255.224")
### returns:
### "cidr" => 192.168.37.215/27
### "network" => 192.168.37.192
### "broadcast" => 192.168.37.223
###
$bits=strpos(decbin(ip2long($mask)),"0");
$net["cidr"]=gethostbyname($host)."/".$bits;
$net["network"]=long2ip(bindec(decbin(ip2long(gethostbyname($host))) & decbin(ip2long($mask))));
$binhost=str_pad(decbin(ip2long(gethostbyname($host))),32,"0",STR_PAD_LEFT);
$binmask=str_pad(decbin(ip2long($mask)),32,"0",STR_PAD_LEFT);
for ($i=0; $i<32; $i++) {
if (substr($binhost,$i,1)=="1" || substr($binmask,$i,1)=="0") {
$broadcast.="1";
} else {
$broadcast.="0";
}
}
$net["broadcast"]=long2ip(bindec($broadcast));
return $net;
}
?>
Another CIDR function, but with sanity check:
<?php
function mask2prefix($mask)
{
if (($long = ip2long($mask)) === false)
return false;
for ($prefix = 0; $long & 0x80000000; ++$prefix, $long <<= 1) {}
if ($long != 0)
return false;
return $prefix;
}
?>
A common way to express IP addresses and subnet masks is to use what is refered to as slash notation. Instead of writing out:
IP = 192.168.100.2
Subnet Mask = 255.255.240.0
Writing out "192.168.100.2/20" is much shorter.
If you are looking for a way to convert a subnet mask into it's slash notation counterpart, here is a single line of code that can perform this task.
<?php
$slash_notation = strlen(preg_replace("/0/", "", decbin(ip2long($subnet_mask))));
?>
For example...
A subnet mask of 255.255.240.0 expressed in binary looks like this:
11111111111111111111000000000000
This gives us a slash notation of 20 which is simply counting the number of 1's in the masks binary representation.
Here is a function that uses this code to return an IP address and subnet mask in slash notation.
<?php
function slash_notation($ip, $mask) {
return $ip."/".strlen(preg_replace("/0/", "", decbin(ip2long($mask))));
}
?>
calling slash_notation("192.168.100.2", "255.255.255.0"); will return "192.168.100.2/24".
Hope this is of use to some of you out there.
Here is a modified version of the code posted by legetz81 (AT) yahoo (dot) com. It handles the shorter, and more common, notation: "189.128/11".
<?php
$ip_addr_cidr = "192.168.37.215/27";
cidr($ip_addr_cidr);
function cidr($ip_addr_cidr) {
$ip_arr = explode('/', $ip_addr_cidr);
$dotcount = substr_count($ip_arr[0], ".");
$padding = str_repeat(".0", 3 - $dotcount);
$ip_arr[0].=$padding;
$bin = '';
for($i=1;$i<=32;$i++) {
$bin .= $ip_arr[1] >= $i ? '1' : '0';
}
$ip_arr[1] = bindec($bin);
$ip = ip2long($ip_arr[0]);
$nm = ip2long($ip_arr[1]);
$nw = ($ip & $nm);
$bc = $nw | (~$nm);
echo "Number of Hosts: " . ($bc - $nw - 1) . "\n";
echo "Host Range: " . long2ip($nw + 1) . " -> " . long2ip($bc - 1) . "\n";
/*
This will produce:
Number of Hosts: 30
Host Range: 192.168.37.193 -> 192.168.37.222
*/
}
?>
<?php
if (!function_exists("ip2long"))
{
function ip2long($ip)
{
$ip = explode(".",$ip);
if (!is_numeric(join(NULL,$ip)) or count($ip) != 4) {return false;}
else {return $ip[3]+256*$ip[2]+256*256*$ip[1]+256*256*256*$ip[0];}
}
}
?>
The following script will print out subnet statistics when you supply the IP Address and Subnet Mask. Usefull to calculate the broadcast address and network address as well as the number of hosts and the range of IP addresses in the subnet.
#!/usr/local/bin/php
<?php
$ip_addr = "172.14.1.57";
$subnet_mask = "255.255.255.0";
$ip = ip2long($ip_addr);
$nm = ip2long($subnet_mask);
$nw = ($ip & $nm);
$bc = $nw | (~$nm);
echo "IP Address: " . long2ip($ip) . "\n";
echo "Subnet Mask: " . long2ip($nm) . "\n";
echo "Network Address: " . long2ip($nw) . "\n";
echo "Broadcast Address: " . long2ip($bc) . "\n";
echo "Number of Hosts: " . ($bc - $nw - 1) . "\n";
echo "Host Range: " . long2ip($nw + 1) . " -> " . long2ip($bc - 1) . "\n";
?>
Produces the output:
IP Address: 172.14.1.57
Subnet Mask: 255.255.255.0
Network Address: 172.14.1.0
Broadcast Address: 172.14.1.255
Number of Hosts: 254
Host Range: 172.14.1.1 -> 172.14.1.254
If you want to get the interface of an IP, based on the local route table, use this.
<?php
function GetIfaceforIP($user_ip)
{
$route = "/bin/netstat -rn";
exec($route, $aoutput);
foreach($aoutput as $key => $line)
{
if($key > 1)
{
$line = ereg_replace("[[:space:]]+",",",$line);
list($network, $gateway, $mask, $flags, $mss, $window, $irtt, $iface) = explode(",", $line)
if((ip2long($user_ip) & ip2long($mask)) == ip2long($network))
{
return $iface;
}
}
}
}
?>