16

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
  • 119,891
  • 44
  • 235
  • 294
The Stig
  • 581
  • 2
  • 5
  • 15

8 Answers8

16

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
  • 93,976
  • 29
  • 161
  • 209
  • 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
  • 42,585
  • 14
  • 91
  • 146
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,198
  • 1
  • 17
  • 38
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,180
  • 18
  • 26
  • 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
  • 57,498
  • 14
  • 111
  • 168
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,421
  • 8
  • 21
0

I have created my own function to convert ipv6 string into ipv6 value in array without the help of any external library like "boost". It works for any format of ipv6 address.

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

#define PRINT_IPV6_STR            "[%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x]"
#define PRINT_IPV6_VAL(ipaddr)    ipaddr[0],ipaddr[1],ipaddr[2],ipaddr[3],ipaddr[4],ipaddr[5],ipaddr[6],ipaddr[7],ipaddr[8],ipaddr[9],ipaddr[10],ipaddr[11],ipaddr[12],ipaddr[13],ipaddr[14],ipaddr[15]

void getIpv6SubStr(const string inp, unsigned char* arr, unsigned char &dcolonIdx, unsigned char& arrIdxCnt)
{   
    unsigned char len=inp.size();

    unsigned char j=0;
    unsigned char k=0;
    string t_str;
    for(unsigned char i=0; i<=len-1; i++)
    {   
        if(((i!=0) && (inp[i] == ':') && (inp[i-1] == ':')) ||
           ((i==0) && (inp[i] == ':') && (inp[i+1] == ':')))
        {
            dcolonIdx = k;
            continue;
        }
        
        t_str[j++] = inp[i];
        t_str[j]='\0';
        
        if((inp[i] == ':') || (i==len-1))
        {
            unsigned short temp = stol (t_str,nullptr,16);
            arr[k++] = temp>>8;
            arr[k++] = temp;
            j=0;
            t_str.clear();
            continue;
        }
    }
    
    arrIdxCnt = k;
    
    printf("\n Input-ipv6-str[%s] Ipv6-substr" PRINT_IPV6_STR " dcolonIdx[%d] arrIdxCnt[%d]",
            inp.c_str(), PRINT_IPV6_VAL(arr), dcolonIdx,arrIdxCnt);
}

void convertIpv6StringToAddr(const string inp, unsigned char* arr)
{
    unsigned char substr_arr[16] = {};
    unsigned char dcolonIdx = 0xFF;
    unsigned char arrIdxCnt = 0;
    
    /* Get the rcvd ipv6 address */
    getIpv6SubStr(inp, substr_arr, dcolonIdx, arrIdxCnt);
    
    /* If doubleColon is not present in rcvd IPv6 address, then the parsed ipv6 address can be directly used */
    if(dcolonIdx == 0xFF)
    {
        memcpy(arr, substr_arr, 16);
        printf("\n Input-ipv6-str[%s] Converted-Ipv6-Addr-Val" PRINT_IPV6_STR,
                inp.c_str(), PRINT_IPV6_VAL(arr));
        return;
    }
    
    /** 
     * If doubleColon is present in rcvd IPv6 address, then we should repace it with required number of 0's 
     * For eg: 
     *  - if rcvd ipv6 address is "a1:a2:a3::a6:a7:a8" then the output ipv6 should be 
     *         "00a1:00a2:00a3:0000:0000:00a6:00a7:00a8"
     *  - if rcvd ipv6 address is "::124" then the output ipv6 should be 
     *         "0000:0000:0000:0000:0000:0000:0000:0124"
     */
    
    unsigned char idxZeros = 0;
    
    /**
     * if rcvd IPv6 address starts with doubleColon, 
     * find the number of indices which need to filled with Zeros. 
     * then from idx=0 fill zeros until the required idx, and then fill the rcvd ipv6 address
     * eg: "::124" => "0000:0000:0000:0000:0000:0000:0000:0124"
     */
    if(dcolonIdx == 0)
    {
        idxZeros = 16-arrIdxCnt;
        
        memcpy(&arr[idxZeros], substr_arr, arrIdxCnt);
        printf("\n Input-ipv6-str[%s] Converted-Ipv6-Addr-Val" PRINT_IPV6_STR,
                inp.c_str(), PRINT_IPV6_VAL(arr));
        return;
    }
    
    /**
     * if rcvd IPv6 address contians doubleColon inbetween,
     * find the number of indices which need to filled with Zeros and the position of doubleColon,
     * then from the position of doubleColon fill zeros until the required idx, and fill the rcvd 
     * ipv6 address in its required indices
     * eg: "a1:a2:a3::a6:a7:a8" => "00a1:00a2:00a3:0000:0000:00a6:00a7:00a8"
     */
    idxZeros = 16-arrIdxCnt;    
    memcpy(arr, substr_arr, dcolonIdx);
    memcpy(&arr[dcolonIdx+idxZeros], &substr_arr[dcolonIdx], arrIdxCnt-dcolonIdx);
    
    printf("\n Input-ipv6-str[%s] Converted-Ipv6-Addr-Val" PRINT_IPV6_STR,
            inp.c_str(), PRINT_IPV6_VAL(arr));
}

void test_ut(int i, string str)
{
    printf("\n%d.",i);
    unsigned char arr[16] = {};
    convertIpv6StringToAddr(str, arr);
    i++;
}

int main()
{
    test_ut(1, "a1a2:b3b4:c5c6:d7d8:0000:e5:6f:0001");
    test_ut(2, "a1");
    test_ut(3, "a1::");
    test_ut(4, "a1::1");
    test_ut(5, "a1a2:b3b4:c5c6:d7d8::e5");
    test_ut(6, "a1a2:b3b4:c5c6:d7d8::e5:6f:1");
    test_ut(7, "::e5:0:678");

    return 0;
}

output:

  1. Input-ipv6-str[a1a2:b3b4:c5c6:d7d8:0000:e5:6f:0001] Ipv6-substr[a1a2:b3b4:c5c6:d7d8:0000:00e5:006f:0001] dcolonIdx[255] arrIdxCnt[16] Input-ipv6-str[a1a2:b3b4:c5c6:d7d8:0000:e5:6f:0001] Converted-Ipv6-Addr-Val[a1a2:b3b4:c5c6:d7d8:0000:00e5:006f:0001]
  2. Input-ipv6-str[a1] Ipv6-substr[00a1:0000:0000:0000:0000:0000:0000:0000] dcolonIdx[255] arrIdxCnt[2] Input-ipv6-str[a1] Converted-Ipv6-Addr-Val[00a1:0000:0000:0000:0000:0000:0000:0000]
  3. Input-ipv6-str[a1::] Ipv6-substr[00a1:0000:0000:0000:0000:0000:0000:0000] dcolonIdx[2] arrIdxCnt[2] Input-ipv6-str[a1::] Converted-Ipv6-Addr-Val[00a1:0000:0000:0000:0000:0000:0000:0000]
  4. Input-ipv6-str[a1::1] Ipv6-substr[00a1:0001:0000:0000:0000:0000:0000:0000] dcolonIdx[2] arrIdxCnt[4] Input-ipv6-str[a1::1] Converted-Ipv6-Addr-Val[00a1:0000:0000:0000:0000:0000:0000:0001]
  5. Input-ipv6-str[a1a2:b3b4:c5c6:d7d8::e5] Ipv6-substr[a1a2:b3b4:c5c6:d7d8:00e5:0000:0000:0000] dcolonIdx[8] arrIdxCnt[10] Input-ipv6-str[a1a2:b3b4:c5c6:d7d8::e5] Converted-Ipv6-Addr-Val[a1a2:b3b4:c5c6:d7d8:0000:0000:0000:00e5]
  6. Input-ipv6-str[a1a2:b3b4:c5c6:d7d8::e5:6f:1] Ipv6-substr[a1a2:b3b4:c5c6:d7d8:00e5:006f:0001:0000] dcolonIdx[8] arrIdxCnt[14] Input-ipv6-str[a1a2:b3b4:c5c6:d7d8::e5:6f:1] Converted-Ipv6-Addr-Val[a1a2:b3b4:c5c6:d7d8:0000:00e5:006f:0001]
  7. Input-ipv6-str[::e5:0:678] Ipv6-substr[00e5:0000:0678:0000:0000:0000:0000:0000] dcolonIdx[0] arrIdxCnt[6] Input-ipv6-str[::e5:0:678] Converted-Ipv6-Addr-Val[0000:0000:0000:0000:0000:00e5:0000:0678]