PHP
downloads | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | conferences | my php.net

search for in the

long2ip> <inet_pton
Last updated: Fri, 22 Aug 2008

view this page in

ip2long

(PHP 4, PHP 5)

ip2longConverts a string containing an (IPv4) Internet Protocol dotted address into a proper address

Description

int ip2long ( string $ip_address )

The function ip2long() generates an 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.

Parameters

ip_address

A standard format address.

Return Values

Returns the IPv4 address or FALSE if ip_address is invalid.

ChangeLog

Version Description
5.0.0 Prior to this version, ip2long() returned -1 on failure.

Examples

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 == -|| $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
}
?>

Example #3 IP validation

ip2long() should not be used as the sole form of IP validation. Combine it with long2ip():

<?php
// make sure IPs are valid. also converts a non-complete IP into
// a proper dotted quad as explained below.
$ip long2ip(ip2long("127.0.0.1")); // "127.0.0.1"
$ip long2ip(ip2long("10.0.0")); // "10.0.0.0"
$ip long2ip(ip2long("10.0.256")); // "10.0.1.0"
?>

Notes

Note: Because PHP's integer type is signed, and many IP addresses will result in negative integers, you need to use the "%u" formatter of sprintf() or printf() to get the string representation of the unsigned IP address.

Note: ip2long() will return FALSE for the IP 255.255.255.255 in PHP 5 <= 5.0.2. It was fixed in PHP 5.0.3 where it returns -1 (same as PHP 4).

See Also



long2ip> <inet_pton
Last updated: Fri, 22 Aug 2008
 
add a note add a note User Contributed Notes
ip2long
Teguh Iskanto - Kamprettos_at_yahoo
27-Aug-2008 02:40
A quick way to find which IP address belong to which network , *without* even creating a single PHP function (pure SQL)

Scenario :
- I have one table that has a list of host ip addresses
- I have another table that list all the networks' addresses
- I need to find which host belongs to which network

nodes table
+-------------+-----------------+-------+
| ip          | mask            | name  |
+-------------+-----------------+-------+
| 192.168.1.1 | 255.255.255.0   | node1 |
| 192.168.1.1 | 255.255.255.252 | node2 |
| 192.168.2.1 | 255.255.255.252 | node3 |
+-------------+-----------------+-------+

network1 table
+-------------+---------------+------+
| ipaddr      | netmask       | name |
+-------------+---------------+------+
| 192.168.1.0 | 255.255.255.0 | net1 |
| 192.168.2.0 | 255.255.255.0 | net2 |
| 192.168.3.0 | 255.255.255.0 | net3 |
| 192.168.4.0 | 255.255.255.0 | net4 |
+-------------+---------------+------+

Solution 1 - with PHP ( Long ... ) :
1. create a PHP function that will calculate and compare IP address with the network
2. extract data from the table nodes, calculate the IP with its mask using a PHP function to get the network address
3.  do the same with table network1
4. compare result 2 and result 3 , once finished put them into an array to be presented as an HTML
5. done

Solution 2 - With SQL ( Very Very Fast & Short ) :
1. create sql with 'inet_aton' function + table joins
2. extract the data from sql outputs
3. done

how :
SQL :
select a.name as host_name,
a.ip as host_ip,
b.name as net_name
from nodes a, network1 b
where (inet_aton(a.ip) & inet_aton(a.mask) = inet_aton(b.ipaddr) & inet_aton(b.netmask));

Results :
Voilla ...
+-----------+-------------+----------+
| host_name | host_ip     | net_name |
+-----------+-------------+----------+
| node1     | 192.168.1.1 | net1     |
| node2     | 192.168.1.1 | net1     |
| node3     | 192.168.2.1 | net2     |
+-----------+-------------+----------+

Hope this helps
sy ABC damla.net
20-Jun-2008 10:57
Yes, but for safety wrapping it up with sprintf() does not hurt on 64bits either.
Anonymous
19-Jun-2008 03:09
on 64 bits system ip2long ONLY RETURNS POSITIVE VALUES

so

<?php
echo ip2long('200.200.200.200');
?>

will output -926365496 on a 32 bits system and 3368601800  on a 64 bits system
herwin at snt dot utwente nl
04-Jun-2008 03:50
The code examples explain why printing needs a casting, but beware that also calculations are performed with signed integers. In my case, the result of (ip2long('130.89.0.1') >> 24) was supposed to be 130, but the actual result was -126
chrisp-phpnet at inventivedingo dot com
31-May-2008 02:36
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:

    $ip = htmlspecialchars($_SERVER['REMOTE_ADDR']);
    if (strpos($ip, '::') === 0) {
        $ip = substr($ip, strrpos($ip, ':')+1);
    }
    $host = ip2long($ip);

Ugly but functional.
jpmarcotte at gmail dot com
20-May-2008 10:10
In using a combination of jbothe's code below and some of the "$mask = 0xFFFFFFFF << (32 - $bits)" type code, I ran into an error with some later calculations on a 64 bit machine.

Keep in mind that when you're analyzing numbers meant to be treated as 32 bits wide (such as IP addresses), you may want to truncate them. Without relying on other libraries, it was simple enough to follow any calculations that may end with different results on a 64 bit machine with " & 0xFFFFFFFF"

Though in many cases, it seems like it might be simpler to just use "~0 << ..." for initial shifting to create the network mask instead of "0xFFFFFFFF << ...". I don't know that it guarantees further operations to work as expected though.
mike at hotmail dot com
16-May-2008 11:23
one tiger one:  Your netmask function almost works.  Unfortunately, for the 2 outlying netmasks, it doesn't.

There are only 33 valid netmasks:
a netmask has to look like this (in binary)
00000000000000000000000000000000   zero
10000000000000000000000000000000   1 1  followed by 31 0's
11000000000000000000000000000000   2 1s followed by 30 0's
11100000000000000000000000000000   3 1s followed by 29 0's
11110000000000000000000000000000   4 1s followed by 28 0's
11111000000000000000000000000000   5 1s followed by 27 0's
etc.  (the 1's keep growing by 1, the 0's shrinking by 0).
lastly:
11111111111111111111111111111111   32 1s followed by 0 0's

The netmasks above translate to:

0.0.0.0
128.0.0.0
192.0.0.0
224.0.0.0
240.0.0.0
248.0.0.0
...
255.255.255.255

Your function does not accept 0.0.0.0 or 255.255.255.255 which are both valid netmasks.

the 0.0.0.0 case fails because you are not comparing with === to FALSE, but are using ! with ip2long.  ip2long for 0.0.0.0 returns 0, which is read as FALSE so the ! succeeds.

The 255.255.255.255 case fails because your 2nd elseif checks to make sure there is at least one 0 in the binary representation of the netmask, which is not a valid test for 255.255.255.255  (In fact, I'm not sure why this check was put in there).

I've computed the ip2long only once, as well as the decbin only once, to make it a bit faster.  (It's faster for valid netmasks, a bit slower with invalid bitmasks.  I just don't like computing the same value more than once)

function checkNetmask4($ip) {
  $ip2long = ip2long($ip);
  $decbin = decbin($ip2long);
  if ($ip2long === FALSE) {
    return false;
  } elseif(strlen($decbin) != 32 && $ip2long != 0) {
    return false;
  } elseif(ereg('01',$decbin)) {
    return false;
  } else {
    return true;
  }
}

I ran through all 2+ billion netmasks, and verified that this code works, returning true 33 times for the correct netmasks (1 core fully maxed for approx 9.5 hours at 1.8Ghz for anyone who cares).

The PEAR Net_IPv4 package has a validateNetmask function which does an explicit comparison to the 33 valid netmasks, which can also be poached.  I ran this code against it for 100,000 iterations, and for invalid netmasks, this code is twice as fast.  For valid netmasks, it depends on where in Net_IPv4's list the netmask is...ones further down the list take longer, as it is doing up to 33 compares.

For 0.0.0.0 the PEAR code is better than twice as fast, as 0.0.0.0 is the first element in the array.  For 255.255.255.255 this code is twice as fast, as it's the last element in the PEAR codes array.

I'm not saying you should use this code or PEARs, I just didn't want anyone to use the faulty code below.

NOT my real email.
lutel
09-Apr-2008 11:45
here is netMatch function which is the fastest I could wrote, I hope developers will include native function soon...

function netMatch ($CIDR,$IP) {
    list ($net, $mask) = explode ('/', $CIDR);
    return ( ip2long ($IP) & ~((1 << (32 - $mask)) - 1) ) == ip2long ($net);
}
php dot net at kenman dot net
01-Apr-2008 06:02
To nate, who advises that there is no reason to use an unsigned version of the IP in a MySQL DB:

I think it would depend on your application, but personally, I find it useful to store IP's as unsigneds since MySQL has 2 native functions, INET_ATON() and INET_NTOA(), which work the same as ip2long()/long2ip() _except_ that they generate the unsigned counterpart. So if you want, you could do:

-- IANA Class-B reserved/private
SELECT * FROM `servers`
WHERE `ip` >= INET_ATON('192.168.0.0')
AND `ip` <= INET_ATON('192.168.255.255');

In my current application, I find it easier to use the MySQL built-ins than the PHP counter-parts.

In case you're curious as to the names ATON and NTOA:

ATON = address to number aka. ip2long
NTOA = number to address aka. long2ip
johniskew at yahoo dot com
24-Mar-2008 06:14
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);

?>
og_sam at homail dot com
16-Mar-2008 03:15
@ samb057 at gmail dot com

This function will not return a correct bigint.

base_convert() is limited to the double type,
which usually counts 64bits and not 128 like an IPv6 address.

For example:

FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF

will __NOT__ be converted to 128^2 -1

You should use a bigint lib's multiplication function,
with decimal shifting factors defined as string constants (much faster than recalculating them every time).
Grab 8 short integers (the 16bits between the ':' with base_convert($hex,16,10)) and shift the 7 higher ones with a bigint_multiplication.

Of course you can also do it with less bigint ops using 32bit integers, but then you should create them this way (additionally 'sprintf' for making an uint32 string):
$uint32= sprintf('%u',intval(base_convert($hex,16,10)));

Finally, "bigint_add" the shifted ones to the least (unshifted) significant interger string and be happy

PS: I'm working on an IPv4/6 tool class with raw (also endianness), 6/4 compatibility and validation methods,
I'll post it here as soon as it's finished and tested.
mhakopian at gmail dot com
11-Feb-2008 07:46
Just a little function to check visitor's ip if it is in given range or not (I could'nt find anywhere so i improvise):

function in_ip_range($ip_one, $ip_two=false){
    if($ip_two===false){
        if($ip_one==$_SERVER['REMOTE_ADDR']){
            $ip=true;
        }else{
            $ip=false;
        }
    }else{
        if(ip2long($ip_one)<=ip2long($_SERVER['REMOTE_ADDR']) && ip2long($ip_two)>=ip2long($_SERVER['REMOTE_ADDR'])){
            $ip=true;
        }else{
            $ip=false;
        }
    }
    return $ip;
}
//usage
echo in_ip_range('192.168.0.0','192.168.1.254');
curda2 at hotmail dot com - Dim Works dot org
11-Feb-2008 07:01
The things that this function do is:

Structure of function:

<?php

$ip
= getenv(REMOTE_ADDR);
$numbers=explode (".",$ip);
$code=($numbers[0] * 16777216) + ($numbers[1] * 65536) + ($numbers[2] * 256) + ($numbers[3]);
//$code is the final variant

?>

that is similar to do this:

<?php

$ip
= getenv(REMOTE_ADDR);
$code=ip2long($ip);
//$code is the final variant

?>
dbemowsk
26-Jan-2008 03:41
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.

$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.

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.
nate
18-Dec-2007 09:44
Thanks to sealbreaker for the conversion method. I'd seen <? (ip2long($ip) & 0x7FFFFFFF) + 0x80000000 ?> before, but noticed it didn't work on all ips. You've discovered why. :)

In case anyone is wondering what sealbreaker means by obtaining an "integer value", s/he means an unsigned int, vs the signed version ip2long provides. Well, technically this conversion will sometimes give you a float since php's integer type is signed.

The good news is that long2ip will correctly handle converting these signed versions back into ips.

So, to get an unsigned version of an ip (ie, always >= 0):

<?

$ip
= '127.0.0.1';

// may return a php int or float
$signed = substr($ip, 0, 3) > 127 ? ((ip2long($ip) & 0x7FFFFFFF) + 0x80000000) : ip2long($ip);

// will return a string version
$signed2 = sprintf('%u', ip2long($ip));

var_dump($signed, $signed2, long2ip($signed), long2ip($signed2));

?>

P.S.: If you're storing IPs in MySQL, there is NO POINT in converting to these unsigned values to place in an INT UNSIGNED column. Just use an INT SIGNED column, and directly use the result from ip2long. The only reason you should really need to force your ip2long result into its unsigned version is if you need to compare them with another source that uses unsigned (ie: some IP database lookup utils use unsigned).
sealbreaker at email dot com
03-Nov-2007 08:47
As a note : if you are using (PHP 4, PHP 5) and are looking to get the integer value of an IP address, i have found that the following works flawlessly for converting to and from IPv4 and it's integer equivalent. I must give credit elsewhere for this portion of the code (ip2long($ip) & 0x7FFFFFFF) + 0x80000000). I looked but was unable to find the comment where it was included.

$ip = "127.0.0.0"; // as an example

$integer_ip = (substr($ip, 0, 3) > 127) ? ((ip2long($ip) & 0x7FFFFFFF) + 0x80000000) : ip2long($ip);

echo $integer_ip; // integer value of IP address
echo long2ip($integer_ip); // convert to an IPv4 formatted address
-----------------------
Results are as follows:
-----------------------
2130706432
127.0.0.0
-----------------------
255.255.255.255 (converts to) 4294967295 (and back to) 255.255.255.255
209.65.0.0 (converts to) 3510697984 (and back to) 209.65.0.0
12.0.0.0 (converts to) 201326592 (and back to) 12.0.0.0
1.0.0.0 (converts to) 16777216 (and back to) 1.0.0.0
ir on ir id is at gm ai ld ot co m
21-Oct-2007 06:54
Keep in mind that storing IP addresses inside of your database as integers (rather than 15 character strings in decimal format, or 8 character strings in hex format) is hundreds of times faster.

Take the typical case of a MySQL database doing a search for an IP address on thousands (or millions!) of rows; you're either doing a string compare for each entry, or an integer equation. If you do your indexes correctly, your lookups should be literally 100x faster using an INT rather than a VARCHAR.

Also note that an integer doesn't need to be escaped when passed to a database. :)
andrew dot minerd at sellingsource dot com
21-Jul-2007 12:15
A somewhat more efficient alternative to convert the signed integer return by ip2long:

$float = ((ip2long($ip) & 0x7FFFFFFF) + 0x80000000);
one tiger one at gee mail dot comm
02-Apr-2007 02:49
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.

// 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;
 }
}
laacz at php dot net
16-Feb-2007 09:06
Just to save you some time.

Beware that octets in IP address are being treated as numbers. So, '10.0.0.11' is not equal to '10.0.0.011'. '011' is octal number (base 8), so it converts to '9'. You can even go further and see that '10.0.0.0xa' also works (equals to '10.0.0.16').

This is not PHP issue, though.
samb057 at gmail dot com
26-Dec-2006 08:37
Convert an ipv6 address to an base 10 integer

function ip2long6($ip)
    {
        if (substr_count($ip, '::'))
            {
                $ip = str_replace('::', str_repeat(':0000', 8 - substr_count($ip, ':')) . ':', $ip) ;
            }
           
        $ip = explode(':', $ip) ;
       
        $r_ip = '' ;
        foreach ($ip as $v)
            {
                $r_ip .= str_pad(base_convert($v, 16, 2), 16, 0, STR_PAD_LEFT) ;
            }
           
        return base_convert($r_ip, 2, 10) ;
    }
samb057 at gmail dot com
26-Dec-2006 08:20
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.

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.
Ian B
24-Dec-2006 12:22
NOTE: ip2long() should NOT be used for CIDR calculation.
Instead, you should use something like the following:

<?php
       
/* get the base and the bits from the ban in the database */
       
list($base, $bits) = explode('/', $CIDR);

       
/* now split it up into it's classes */
       
list($a, $b, $c, $d) = explode('.', $base);

       
/* now do some bit shfiting/switching to convert to ints */
       
$i = ($a << 24) + ($b << 16) + ($c << 8) + $d;
       
$mask = $bits == 0 ? 0 : (~0 << (32 - $bits));

       
/* here's our lowest int */
       
$low = $i & $mask;

       
/* here's our highest int */
       
$high = $i | (~$mask & 0xFFFFFFFF);

       
/* now split the ip were checking against up into classes */
       
list($a, $b, $c, $d) = explode('.', $iptocheck);

       
/* now convert the ip we're checking against to an int */
       
$check = ($a << 24) + ($b << 16) + ($c << 8) + $d;

       
/* if the ip is within the range, including
      highest/lowest values, then it's witin the CIDR range */
       
if ($check >= $low && $check <= $high)
            return
1;
       else
            return
0;
?>

This means that you should check to see if the IP
address is of the correct format each time.
jgwright
25-Oct-2006 07:13
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
*/

}

?>
dh06 at biztechwiz dot com
24-Oct-2006 04:59
I made one tiny change to Stephane's routine below when I had problems with spaces in an IP range.  I moved the trim function before the ip2long call.

Thanks Stephane!

Dirk.

<?php
function netMatch($network, $ip) {

  
$network=trim($network);
  
$ip = trim($ip);

  
$d = strpos($network,"-");
   if (
$d===false) {
      
$ip_arr = explode('/', $network);
 
       if (!
preg_match("@\d*\.\d*\.\d*\.\d*@",$ip_arr[0],$matches)){
          
$ip_arr[0].=".0";    // Alternate form 194.1.4/24
      
}

      
$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);
 
       return (
$ip_long & $mask) == ($network_long & $mask);
   }
   else {
      
$from = ip2long(trim(substr($network,0,$d)));
      
$to = ip2long(trim(substr($network,$d+1)));

      
$ip = ip2long($ip);
       return (
$ip>=$from and $ip<=$to);
   }
}
?>
stephane at deluca dot biz
01-Oct-2006 08:40
I propose a new function to match an IP against a network.
You can define the network in two different manner, most suited for handling data grabed from WHOIS records:
- 202.1.192.0-202.1.192.255: a range of IPs
- 200.36.161.0/24: a range of IP by using net masking
- 200.36.161/24: a shorten syntax similar to the above.

Sdl

<?php
function netMatch($network, $ip) {

   
$network=trim($network);
   
$ip = trim($ip);

   
$d = strpos($network,"-");
    if (
$d===false) {
       
$ip_arr = explode('/', $network);
   
        if (!
preg_match("@\d*\.\d*\.\d*\.\d*@",$ip_arr[0],$matches)){
           
$ip_arr[0].=".0";    // Alternate form 194.1.4/24
       
}

       
$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);
   
        return (
$ip_long & $mask) == ($network_long & $mask);
    }
    else {
       
$from = trim(ip2long(substr($network,0,$d)));
       
$to = trim(ip2long(substr($network,$d+1)));

       
$ip = ip2long($ip);
        return (
$ip>=$from and $ip<=$to);
    }
}
?>
legetz81 (AT) yahoo (dot) com
30-Mar-2006 02:15
Here is a script that will calculate host range and number of hosts with a given ip address CIDR notation (modified code which was posted by phl AT cyways.com):

$ip_addr_cidr = "192.168.37.215/27";
$ip_arr = explode('/', $ip_addr_cidr);

$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
Polarina
10-Mar-2006 02:37
Using the option that "paul at santasoft point com" mentioned will make IP Addresses larger than 2147483647 (Around 128.0.0.0) become 2147483647 and it wont go higher than that.
So it's not recommended to use his solution.
novikov at harizma dot lv
21-Dec-2005 03:57
Please not , that the results of ip2long on 64bit servers is different form 32bit.

Take a look here: http://bugs.php.net/bug.php?id=18816

It's a feature, that ip2long on 64bit always returned UNSIGNED result like after spitf("%u\n",ip2long());
greg at netops dot gvtc dot com
19-Dec-2005 11:43
Re:  m.ozarek's isIpMask() function

You might want to adjust your preg_match line from:
 
if(preg_match("/[0-9].[0-9].[0-9].[0-9]/",$mask)){

to

if(preg_match("/[0-9]++\.[0-9]++\.[0-9]++\.[0-9]++/",$mask)){

this should properly match up to 3 characters per octet and require that the periods be periods not just any character.  I tested against a handful of IPs, and it seemed to match fine, and did not allow a 7 digit number to be considered a mask to check.  This is probably more useful if doing a match against a regular IP than against the subnet mask, but its always good to have more layers of checking.

*note.. not all applications support the regex + metacharacter.
paul at santasoft point com
02-Nov-2005 03:03
to complement daevid at daevid dot com's note (11 Nov 2004) about the catch being that PHP returns negative values....

If you define your mysql column that stores the ip address in INT format as signed (not unsigned), then it'll work just fine. 

No need to do any conversion using sprintf and this way you'll keep the value and be able to use PHP's long2ip() function on the value and get an accurate result.

SO..  define your MySQL column as signed (leave the Attributes select box in phpMyAdmin blank) and you won't need to do any conversions or whatnot.
01-Sep-2005 07:47
# m.ozarek
#
# Check if given mask is correct. You can check the short format mask
# like 8,16,24 or long format like 255.255.255.0
#

function isIpMask($mask){
   
    $format = '';
    if(preg_match("/[0-9].[0-9].[0-9].[0-9]/",$mask)){
        $format = "long";
    }else{
        if($mask<=30){
            $format = "short";
        }else{
            return false;
        }
    }
    switch($format){
        case long;
            $mask = decbin(ip2long($mask));
        break;
        case short:
            $tmp = $mask;
            for($i=0; $i < $mask ;$i++){
                $tmp.= 1;
            }
            for($j=0; $j < (32 - $mask);$j++){
                $tmp.= 0;
            }
            $mask = $tmp;
        break;
    }
    if(strlen($mask) <= 32){
        for($i=0;$i<=32 ;$i++){
            $bit = substr($mask,$i,1);
            if(($bit - substr($mask,$i+1,1)) < 0){
                return false;
            }
        }
    }
    return true;
}
# EXAMPLE
# isIpMask("255.255.255.0") -> return true
# isIpMask("24") -> return true
# isIpMask("5.5.5.5") -> return false
ken at expitrans dot com
31-Aug-2005 10:31
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

?>
tristram at ccteam dot ru
26-Jul-2005 01:33
<?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];}
}
}
?>
cam at wecreate dot com
20-Jul-2005 04:37
A simple function to compare two IP addresses against a netmask. Useful if you're locking down a web app with an IP address, but can't force the IPs to be exactly the same.

function ipcompare ($ip1, $ip2, $mask) {
    $masked1 = ip2long($ip1) & ip2long($mask); // bitwise AND of $ip1 with the mask
    $masked2 = ip2long($ip2) & ip2long($mask); // bitwise AND of $ip2 with the mask
      if ($masked1 == $masked2) return true;
      else return false;
}

Examples:
ipcompare("192.168.1.63","192.168.1.65","255.255.255.0") // true
ipcompare("192.168.1.63","192.168.1.65","255.255.255.192") // false
Paragina Silviu
12-Jul-2005 03:41
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 ideea 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

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))
tomlove at gmail dot com
03-Jul-2005 11:09
A quick and efficient way to compare two IPs with a given mask:

<?
function ipmatch ($ip1, $ip2, $mask) {
  if ((
ip2long($ip1) & ~(pow(2, 32-$mask)-1)) == (ip2long($ip2) & ~(pow(2, 32-$mask)-1))) return true;
  else return
false;
}
?>

Here's an application of it that selects the best IP given the choice of a (possibly private or invalid) forwarded address or a (possibly proxy) apparent address:

<?
$a
= $_SERVER['HTTP_X_FORWARDED_FOR'];
if (
$a == '' || ipmatch($a, "10.0.0.0", 8) || ipmatch($a, "172.16.0.0", 12) || ipmatch($a, "192.168.0.0", 16) || ipmatch($a, "255.255.255.255", 32)) $ip = $_SERVER['REMOTE_ADDR'];
else
$ip = $a;
?>

Or use it to ban people:

<?
$banned_ip
= '135.23.12.3';
if (
ipmatch($ip, $banned_ip, 32)) die('BANNED!');
?>

The bitwise comparison the function uses can be done in SQL to do ban matches right in your database.
lawpoop at gmail dot com
06-Apr-2005 10:29
Here is a function that tells you if an ip address is in a CIDR range. However, the CIDR argument can be an array of CIDRs. This was created from other matchCIDR functions in the user notes.

function matchCIDR($addr, $cidr) {

        // $addr should be an ip address in the format '0.0.0.0'
        // $cidr should be a string in the format '100/8'
        //      or an array where each element is in the above format

        $output = false;

        if ( is_array($cidr) ) {

                foreach ( $cidr as $cidrlet ) {
                        if ( matchCIDR( $addr, $cidrlet) ) {
                                $output = true;
                        }
                }

        } else {

                list($ip, $mask) = explode('/', $cidr);

                $mask = 0xffffffff << (32 - $mask);

                $output = ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));

        }

        return $output;
}
mailNO at SPAMdapuzz dot com
13-Feb-2005 07:05
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
frank at vista dot com
27-Jan-2005 01:43
remixing mediator's function further:
<?php
function matchCIDR($addr, $cidr) {
   list($ip, $mask) = explode('/', $cidr);
   return (ip2long($addr) >> (32 - $mask) == ip2long($ip) >> (32 - mask));
}
?>
01-Dec-2004 02:06
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";
?>
daevid at daevid dot com
12-Nov-2004 03:30
This seems obvious in hindsight, but since nobody else posted it, it had me and two others scratching our heads. Thought I'd save someone else the trouble...

In mySQL, you can use the INET_ATON() function, but to save queries, you could use this function. However, the catch is that PHP returns negative values, so you have to use it like this:

$LongIP = sprintf('%u', ip2long($dotted_name));
DivineHawk
30-Oct-2004 07:38
For PHP5 I had to replace:
$mask_long= pow(2,32)-pow(2,(32-$ip_arr[1]));

-with-

$mask_long = 0xffffffff << (32 - $ip_arr[1]);

in dzver's IP_Match Function below.
mediator
02-Jun-2004 10:50
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));
}
?>
dzver
15-Apr-2004 12:12
Mix of rbsmith's function and pasted above url:

<?
// returns 1 if $ip is part of $network

function IP_Match($network, $ip) {
   
$ip_arr = explode("/",$network);
   
$network_long=ip2long($ip_arr[0]);

   
$mask_long= pow(2,32)-pow(2,(32-$ip_arr[1]));
   
$ip_long=ip2long($ip);
 
    if ((
$ip_long & $mask_long) == $network_long) {
        return
1;
    } else {
        return
0;
    }
}

// usage

$network="200.100.50.0/23";
$ip="200.100.51.55";
$ip2="200.100.52.2";

echo
IP_Match($network, $ip); //prints 1
echo IP_Match($network, $ip2); //prints 0
?>
mkr at binarywerks dot dk
12-Mar-2004 02:25
If you want to expand IP ranges, for instance: 111.111.111.0/24

You can get the function here - improved with more features and bug fixed:
http://binarywerks.dk/php_src/expand_CIDR.phps
theliberal at comcast dot net
09-Feb-2004 05:51
Doh!  That's what I get for copying the wrong code.

The correct code is:

<?php
function ip2base10($ip)
{
$parts=explode(".",$ip);
$base10=($parts[0]<<24)|($parts[1]<<16)|($parts[2]<<8)|($parts[3]);
return
$base10;
}
?>
sunfire at freemail dot hu
18-Jan-2004 07:31
The original function was writen by J.Adams <jna@retina.net> see this page. I found a mistake his code, and make it a little simpler.

function testip($range,$ip) {

.....

                $maskl = 0;

                for ($i = 0; $i < $regs[5]-1; $i++) {
                        $maskl += pow(2,(31-$i));
                }

                if (($maskl & $rangel) == ($maskl & $ipl)) {

.....
}