15

I wanted to know how I can parse an IPv6 address in C and convert it to a 128 bit value?

So a hex address like 1:22:333:aaaa:b:c:d:e needs to be converted to its 128 bit equivalent binary. The problem is the IP address could be of the type ::2 and its variant since they are valid IPv6 address.

The input is from the keyboard and hence is in ASCII format.

Yu Hao
  • 115,525
  • 42
  • 225
  • 281
The Stig
  • 561
  • 2
  • 5
  • 15

7 Answers7

15

You can use POSIX inet_pton to convert a string to a struct in6_addr.

#include <arpa/inet.h>

  ...

const char *ip6str = "::2";
struct in6_addr result;

if (inet_pton(AF_INET6, ip6str, &result) == 1) // success!
{
    //successfully parsed string into "result"
}
else
{
    //failed, perhaps not a valid representation of IPv6?
}
dreamlax
  • 91,777
  • 29
  • 160
  • 208
  • You can pass anything big enough to hold the result, for example, an array of 8 `short` will do as well; just as long as the buffer is at least 128-bits in length. – dreamlax Jun 03 '10 at 01:07
  • 1
    I personally like this a bit more than `getaddrinfo()`, which I suggested below. Sadly, it seems that Windows only has `inet_pton()` starting with Vista. (And I'd almost bet that it doesn't parse according to RFC 2373, and only does your typical IPv6 address... anyone know?) – Thanatos Jun 03 '10 at 03:11
  • @Thanatos: [This link](http://msdn.microsoft.com/en-us/library/cc805844\(VS.85\).aspx) refers to [RFC 2553](http://www.ietf.org/rfc/rfc2553) which declares the function `inet_pton` as a function that converts IPv4 and IPv6 textual representations into binary form, and it says that it must accept IPv6 addresses in the representations described in section 2.2 of [RFC 2373](http://www.ietf.org/rfc/rfc2373). – dreamlax Jun 03 '10 at 03:20
  • Thanks Thanatos and dreamlax for the answers. I used them in my Linux enviroment and it worked perfectly.But the problem is my actual code where i wanted to incorporate this is a stripped down version of Linux and does not have all the libraries.Unfortunately the inet.h header file is not present in the enviroment so inet_pton is not supported... Looks like I might end up writing my own function as YeenFei pointed out.. Need some help as to how to go forward on this... Thanks !! Regards -TG – The Stig Jun 03 '10 at 18:54
  • 1
    Sometimes it's worth just trying this: declare the structs and function prototypes yourself, then just call the function in the hope that it's in the library (inet_pton part of glibc, not some other random library) despite your not having the header(s). – Bernd Jendrissek Feb 03 '11 at 14:14
10

getaddrinfo() can understand IPv6 addresses. Pass AF_INET6 to it in the hints, as well as AI_NUMERICHOST (to prevent a DNS lookup). Linux has it, Windows has it as of Windows XP.

Thanatos
  • 40,566
  • 14
  • 81
  • 139
4

You can use getaddrinfo() POSIX function. It is more flexible than inet_pton(), for example it automatically detects IPv4 and IPv6 address formats, it can resolve even hostnames (using DNS resolving) and port/service names (using /etc/services).

#include <sys/types.h>
#include <netdb.h>
#include <netdb.h>

....

const char *ip6str = "::2";

struct sockaddr_storage result;
socklen_t result_len;

struct addrinfo *res = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT | AI_NUMERICHOST | AI_NUMERICSERV;

rc = getaddrinfo(ip6str, NULL, &hints, &res);
if (rc != 0)
{
    fprintf(stderr, "Failure to parse host '%s': %s (%d)", ip6str, gai_strerror(rc), rc);
    return -1;
}

if (res == NULL)
{
    // Failure to resolve 'ip6str'
    fprintf(stderr, "No host found for '%s'", ip6str);
    return -1;
}

// We use the first returned entry
result_len = res->ai_addrlen;
memcpy(&result, res->ai_addr, res->ai_addrlen);

freeaddrinfo(res);

The IPv6 address is stored in the struct sockaddr_storage result variable.

if (result.ss_family == AF_INET6) // Ensure that we deal with IPv6
{
    struct sockaddr_in6 * sa6 = (struct sockaddr_in6 *) &result;
    struct in6_addr * in6 = &sa6->sin6_addr;
    in6->s6_addr[0]; // This is a first byte of the IPv6
    in6->s6_addr[15]; // This is a last byte of the IPv6
}
Ales Teska
  • 1,128
  • 1
  • 16
  • 36
2

To parse IPv6 in C, you need to build yourself a utility function, which tokenized string (colon for hex blocks, and forward-slash for subnet bits).

  1. Tokenize raw IPv6 string into smaller substring.
  2. Convert non-empty substring into hex blocks. (ASCII to decimal conversion)
  3. Expand hex block into 2-bytes by padding zero in front. (only leading zeroes get trimmed)
  4. Complete IPv6 should have 8 hex blocks, calculate missing hex-block(s). (zeroes grouping can happen only once)
  5. Reinsert missing hex-block. (use index of the empty substring)
YeenFei
  • 3,080
  • 17
  • 24
  • 1
    Good description if someone wants to learn IPv6 parsing. Buf it he just wants to actually parse addresses, the other answers are much simpler. – bortzmeyer Jun 03 '10 at 11:45
1

Rosetta has samples in several languages: https://rosettacode.org/wiki/Parse_an_IP_Address

1

In Windows, you can use WSAStringToAddress, which is available since Windows 2000.

Ken Bloom
  • 55,158
  • 13
  • 108
  • 167
0

if you can use boost, something like this should work:

#include<boost/asio.hpp>

using boost::asio::ip;

bool parseIpv6String(std::string ipv6_string, char* dest){
    try{
        address_v6 addr = address_v6::from_string(ipv6_string);
        memcpy(dest,addr.to_bytes().data(), 16);
    }catch(...){
        return false;
    }
    return true;
}

It is a bit more portable than for example POSIX specific functions.

Patryk
  • 1,381
  • 7
  • 21