21

Under IPv4 I have been parsing the string representation of IP addresses to Int32 and storing them as INT in the SQL Server.

Now, with IPv6 I'm trying to find out if there's a standard or accepted way to parse the string representation of IPv6 to two Int64 using C#?

Also how are people storing those values in the SQL Server - as two fields of BIGINT?

gotqn
  • 42,737
  • 46
  • 157
  • 243
Guy
  • 65,082
  • 97
  • 254
  • 325

5 Answers5

20

Just as an IPv4 address is really a 32 bit number, an IPv6 address is really a 128 bit number. There are different string representations of the addresses, but the actual address is the number, not the string.

So, you don't convert an IP address to a number, you parse a string representation of the address into the actual address.

Not even a decimal can hold a 128 bit number, so that leaves three obvious alternatives:

  • store the numeric value split into two bigint fields
  • store a string representation of the address in a varchar field
  • store the numeric value in a 16 byte binary field

Neither is as convenient as storing an IPv4 address in an int, so you have to consider their limitations against what you need to do with the addresses.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • 2
    @Bill: You are mistaken. There is nothing that says that you have to store the bytes of an IPv4 adress in an unsigned value. An Int32 has 32 bits just as an UInt32, so it works just fine for storing four bytes. – Guffa Oct 29 '09 at 22:20
  • Good thing @RBarryYoung didn't find this thread;-) http://stackoverflow.com/a/1385701/215068 IPv4 is 4 byte binary, not "really a 32 bit number". The four three-digit octets are just a convenient notation – EBarr Mar 30 '12 at 19:46
  • @EBarr: No, the IP number in version 4 *is* a 32 bit number, it's not a 4 byte number. Look at a description of the IP packet, and you see that the addresses are single units of 32 bits, they are not separated into four 8 bit values. – Guffa Mar 30 '12 at 20:04
  • 4
    Sorry, if my humor wasn't clear, i chuckled at contradictions between the 2 posts. 4-bytes or 32-bits seems like 2 sides of the same coin to me. Same ambiguity as to int or binary exists elsewhere: RFC 719 p.12 states "Source Address:32 bits" and not much else on the matter. Core Internet Protocols p.408 describes it as "actually a 32-bit binary number", wiki says "32-bit (four-byte) addresses". IP Addressing Fundamentals, p22 says "IP addresses are defined within a mathematical space that is 32 bits in length." 4-bytes or 32 bits? Yes please! – EBarr Mar 30 '12 at 20:58
  • 2
    @EBarr While an IP address is a 32 bit number (or 4 bytes) it is not always an int (due to machine endianness) which is why it is recommended that it be stored as an array of bytes. For example, if you were to convert the IP 192.168.1.1 to an int on an x86/x64 machine it would have the value 0xC0A80101. If you were to write that integer to a file (or push it out onto the network) it would be stored with the bits in the order of 0x0101A8C0. – Trisped Mar 16 '13 at 02:22
12

The simplest route is to get the framework to do this for you. Use IPAddress.Parse to parse the address, then IPAddress.GetAddressBytes to get the "number" as byte[].

Finally, divide the array into the first and second 8 bytes for conversion to two Int64s, e.g. by creating a MemoryStream over the byte array and then reading via a BinaryReader.

This avoids needing to understand all the available short cut representations for IPv6 addresses.

SetFreeByTruth
  • 819
  • 8
  • 23
Richard
  • 106,783
  • 21
  • 203
  • 265
4

If you are using SQL Server 2005, you can use the uniqueidentifier type. This type stores 16 bytes, which is perfect for an IPv6 ip address. You can convert between IPAddress and Guid by using the constructors and ToByteArray.

NoLifeKing
  • 386
  • 2
  • 8
anvilis
  • 231
  • 2
  • 4
  • 6
    The problem with the `uniqueidentifier` is that you cannot do range-searches. I.e. you cannot search for an IP-range that contains one specific IP. We tried this with a table of **3.000.000** rows of data. And instead of resulting in a 3 row-result, we got **~73.000** rows – NoLifeKing Feb 11 '11 at 10:05
3

I use the following method for converting an IP address to two UInt64s (C# 3.0).

/// <summary>
/// Converts an IP address to its UInt64[2] equivalent.
/// For an IPv4 address, the first element will be 0,
/// and the second will be a UInt32 representation of the four bytes.
/// For an IPv6 address, the first element will be a UInt64
/// representation of the first eight bytes, and the second will be the
/// last eight bytes.
/// </summary>
/// <param name="ipAddress">The IP address to convert.</param>
/// <returns></returns>
private static ulong[] ConvertIPAddressToUInt64Array(string ipAddress)
{
    byte[] addrBytes = System.Net.IPAddress.Parse(ipAddress).GetAddressBytes();
    if (System.BitConverter.IsLittleEndian)
    {
        //little-endian machines store multi-byte integers with the
        //least significant byte first. this is a problem, as integer
        //values are sent over the network in big-endian mode. reversing
        //the order of the bytes is a quick way to get the BitConverter
        //methods to convert the byte arrays in big-endian mode.
        System.Collections.Generic.List<byte> byteList = new System.Collections.Generic.List<byte>(addrBytes);
        byteList.Reverse();
        addrBytes = byteList.ToArray();
    }
    ulong[] addrWords = new ulong[2];
    if (addrBytes.Length > 8)
    {
        addrWords[0] = System.BitConverter.ToUInt64(addrBytes, 8);
        addrWords[1] = System.BitConverter.ToUInt64(addrBytes, 0);
    }
    else
    {
        addrWords[0] = 0;
        addrWords[1] = System.BitConverter.ToUInt32(addrBytes, 0);
    }
    return addrWords;
}

Make sure you cast your UInt64s to Int64s before you put them into the database, or you'll get an ArgumentException. When you get your values back out, you can cast them back to UInt64 to get the unsigned value.

I don't have a need to do the reverse (i.e. convert a UInt64[2] to an IP string) so I never built a method for it.

Billy Jo
  • 1,326
  • 19
  • 32
  • There's not many cases where you'd ever want to store 255.255.255.255 or FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF. – Gavin Oct 29 '09 at 21:44
  • 1
    -1: An Int32 works just fine for storing an IPv4 address. You are staring blindly at the max value of the integer, but that is irrelevant. 32 bits are 32 bits and they are obviously enough for representing the four bytes of an IPv4 address. – Guffa Oct 29 '09 at 22:23
  • My bad. I thought the CLR would throw an overflow error instead of "going around the corner" when trying to cast UInt32.MaxValue to an Int32. I've edited my answer to reflect that. – Billy Jo Oct 30 '09 at 16:24
  • @Guffa: Yes, but an IPv6 address has 128 bits... System.Net.IPAddress has to work with both. – Stefan Steiger Oct 17 '16 at 10:28
0
function encode_ip ($ip)
{
    return bin2hex(inet_pton($ip));
}

function decode_ip($ip)
{
    function hex2bin($temp) {
       $data="";
       for ($i=0; $i < strlen($temp); $i+=2) $data.=chr(hexdec(substr($temp,$i,2)));
       return $data;
    }
    return inet_ntop(hex2bin($ip));
}

-- max len row db
echo strlen(inet_pton('2001:db8:85a3::8a2e:370:7334'));

-- db row info
ip varchar(16)

-- sql binary save and read
save base
$bin_ip='0x'.bin2hex(inet_pton($data['ip_address']));

-- db read
select ip_address from users;

-- encode binary from db
echo inet_ntop($row['ip_address']);
animuson
  • 53,861
  • 28
  • 137
  • 147
Pandora
  • 11
  • 2