Notatki

Informacja:

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

Informacja:

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

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:

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.

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.

Be aware when you're running 64bit system, ip2long will result 64 bit integer which doesn't fit in MySQL INT, you can use BIGINT or INT UNSIGNED because on 64bit systems ip2long will never return negative integer. See also https://bugs.php.net/bug.php?id=54338

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):

//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);

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:

<?phpvar_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.

I wrote something on the different ways to get an IP and convert it using ip2long(), the different ways to store an IP or an IP range in MySQL, and the different ways to query for the IP(s). Maybe something useful for others?

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:

If you're speaking about efficiency, there's no absolute right / wrong as it all depends on the user's needs, specs and requirements.

But from what I'm coming from , this method served me well, I've used this to match 2 network ends of approx 2700++ something unique VRFs (Nortel Shasta) and yes, each VRF is a virtualized firewall that consists of at least 20 different network objects (some could have couple of hundreds). Not to mention that these , still have to be compared with another hundreds of contivity boxes and each box have a range of around 1-600 vpn peers (to add more complexity, each peer has around 1-50 network ranges).

So, if you do the math, the number of permutations that need to be calculated, is indeed quite complex. I could get the whole result somewhere in around 50-65 secs tops.

Had I used a php function to crunch those data the result would have been longer.

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.

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

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:

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.

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.

Thanks to sealbreaker for the conversion method. I'd seen <?php (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.

// 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).

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.

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. :)

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').

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]);

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

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.

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 HTML5. done

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.

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.