views:

1496

answers:

5

Under IPv4 I have been parsing the string representation of IP addresses to Int32's and storing them as int's in 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's using C#? Also how are people storing those values in SQL Server? As 2 fields of big int?

A: 

Kind of a duplicate of this post:

http://stackoverflow.com/questions/420680/how-to-store-ipv6-compatible-address-in-a-relational-database

Also see this post for reference:

http://stackoverflow.com/questions/444966/working-with-ipv6-addresses-in-php

I'm not very familiar with this topic, so I'll be checking back to see what kind of answers show up here.

Cory Larson
I had already seen both those questions which deal with MySQL and PHP respectfully but I am interested in SQLServer and C# solutions.
Guy
+7  A: 

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
Thanks Guffa - I edit the post and replaced "convert" with "parse" because that more accurately describes what I'm doing.
Guy
@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
+3  A: 

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 fist 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 various short cut representations for IPv6 addresses available.

Richard
why 2 int64's instead of binary(128) field?
Henry
@Henry: That's what the Q specified.
Richard
A: 

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.

Bill Ayakatubby
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
-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
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.
Bill Ayakatubby
A: 

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.

anvilis