14 March 2011

Converting inet_aton and inet_ntoa

At some point or another, you've probably stumbled upon the 'inet_aton'
and 'inet_ntoa' functions.  Perhaps you have used them in programming,
or maybe for a MySQL statement, etc.  Briefly, for those not familiar
with either function, 'inet_aton' takes an IP address (10.234.18.76) and
converts it into a packed address (183112268).  Alternatively, 'inet_ntoa'
takes a packed address (183112268) and converts it into an IP address
(10.234.18.76).  Both functions derive from the Berkeley socket API.
We normally just use either function and let it work its magic on the
supplied value as seen in the following MySQL examples:
        mysql> select inet_aton('10.234.18.76');
        +---------------------------+ 
        | inet_aton('10.234.18.76') |
        +---------------------------+
        |                 183112268 |
        +---------------------------+
        1 row in set (0.01 sec)

        mysql> select inet_ntoa(183112268);
        +----------------------+
        | inet_ntoa(183112268) |
        +----------------------+
        | 10.234.18.76         |
        +----------------------+
        1 row in set (0.00 sec)
While there's nothing wrong with using either function and I tend to
use them a bit, I also like to know what the "magic" is.  Starting with
'inet_aton', to convert an IP address to a packed address, we can use
the following formula:
        (octet0 * (256 ^ 3)) + (octet1 * (256 ^ 2)) + (octet2 * (256 ^ 1)) + octet3
Each octet (other than 3) is simply multiplied by the value of 256
raised to the Nth power, and subsequently added together.  Using this
logic, we can convert our IP address of '10.234.18.76' into our packed
address of '183112268':
        (octet0 * (256 ^ 3)) + (octet1 * (256 ^ 2)) + (octet2 * (256 ^ 1)) + octet3
        (10 * 16777216) + (234 * 65536) + (18 * 256) + 76
        (167772160) + (15335424) + (4608) + 76
        183112268
To reproduce 'inet_ntoa', we effectively reverse the above and start
with getting the value of octet0 (10) using '183112268' as our dividend:
        183112268 / (256 ^ 3)
        183112268 / (16777216)
        10.91
For each result, we discard the remainder and multiply that result by
256 power N.  Subtracting the newly derived result provides us with the
dividend for the next octet:
        183112268 - ((result - remainder) * (256 ^ 3))
        183112268 - ((10.91 - 0.91) * (16777216))
        183112268 - ((10) * (16777216))
        183112268 - (167772160)
        15340108
Our new dividend for octet1 is now '15340108'.  Following along:
        15340108 / 65536
        234.07
We now have octets 0 (10) and 1 (234).  Now for the dividend for octet2:
        15340108 - (234 * 65536)
        15340108 - (15335424)
        4684
Deriving octet2's value:
        4684 / 256
        18.29
Octet3's value is the remainder after subtraction:
        4684 - (256 * 18)
        4684 - (4608)
        76
After all of the above, we end up with our original IP address of
'10.234.18.76' from packed address '183112268'.