Development Resource Project
Enforce Coding Standards with PHP_CodeSniffer and Eclipse IDE on Ubuntu Linux
Symfony 2 Crash Course
Native Linux Space Warfare: Freespace 2
ENUMs, User Preferences, and the MySQL SET Datatype
Book Review: How to Implement Design Patterns in PHP

Updating MySQL and PHP to Store IPv6 Addresses

Saturday, 16 May 15, 12:08 pm
More and more people are using IPv6 on their systems, and this longer format requires a larger datatype for storage in MySQL. IPv4 addresses can be saved in a MySQL or MariaDB UNSIGNED column with the INET_ATON() function. If you put an IPv6 address into this function though, it will return NULL indicating unrecognised (you'll see this as the address 0.0.0.0 if you're displaying it with INET_NTOA()).

The ideal solution is to use the new INET6_ATON() which was introduced in MySQL 5.6.3 and MariaDB 10.0.12. This function will return the 128-bit integer corresponding to a given IPv6 address, or the 32-bit integer for an IPv4 address. A 128-bit integer equates to sixteen 8-bit bytes and thus can be stored in a BINARY(16) column. If you're storing IPv4 as well, a VARBINARY(16) column will use less storage space until IPv6 becomes predominant.

To convert an IPv6 address if you are running an older database version, use PHP's inet_pton() function, which works identically, taking either an IPv4 or an IPv6 address and returning the corresponding 32-bit or 128-bit integer as a string.

To get these columns in a human-readable form, use MySQL's INET6_NTOA() or PHP's inet_ntop().

Converting an UNSIGNED column to VARBINARY

To convert an existing column holding 32-bit IPv4 addresses as an UNSIGNED INTEGER type, first change to VARBINARY:
ALTER TABLE hitcounts CHANGE ip ip VARBINARY(16);
If you look at the contents of the altered column, while it may at first appear to hold the correctly converted old value, attempting to display it with INET6_NTOA() will fail:
MariaDB [mydatabase]> SELECT ip, INET6_NTOA(ip) FROM hitcounts LIMIT 5; +----------+----------------+ | ip | INET6_NTOA(ip) | +----------+----------------+ | 16816551 | NULL | | 16830938 | NULL | | 16831652 | NULL | | 16876553 | NULL | | 16883387 | NULL | +----------+----------------+ 5 ROWS IN SET (0.00 sec)
What's actually happened is that MySQL / MariaDB has converted the old integer values into their string representation and stored this ASCII string in the VARBINARY column. So we need to convert the stringified numbers into the actual number they represent:
UPDATE hitcounts SET ip = INET6_ATON(INET_NTOA(ip));
We can now get the human-readable IP addresses (also note the raw BINARY ip column is no longer readable):
MariaDB [mydatabase]> SELECT CONV(HEX(ip),16,10), INET6_NTOA(ip) FROM hitcounts LIMIT 5; +---------------------+----------------+ | CONV(HEX(ip),16,10) | INET6_NTOA(ip) | +---------------------+----------------+ | 16816551 | 1.0.153.167 | | 16830938 | 1.0.209.218 | | 16831652 | 1.0.212.164 | | 16876553 | 1.1.132.9 | | 16883387 | 1.1.158.187 | +---------------------+----------------+ 5 ROWS IN SET (0.00 sec)

Please enter your comment in the box below. Comments will be moderated before going live. Thanks for your feedback!

Cancel Post

/xkcd/ Exclusion Principle