Niranjan Upadhya Niranjan Upadhya - 21 days ago 7
C Question

IPV6 address to array of uint64_t is not working as expected

I am trying to convert an IPv6 address in string format to array of uint64_t data. For this i have written below program

typedef struct
{
union
{struct in6_addr sa;
uint64_t addr[2];
}u;
} ipv6_addr_t;

ipv6_addr_t rnc;
char rncs[100] = "2000::200a";
inet_pton(AF_INET6, rncs, &(rnc.u.sa));
printf("%u\", rnc.u.addr[0]);



Expected output is 1st 64 bits of the address which will be 2^61 =
2305843009213693952.


But when i execute the program i am getting output as 32 which is the
first byte of the address.


I am not understanding the reason behind it , please help. Thanks!

Answer Source

You have several problems here.

  • your method for printing the result is inherently flawed. The printf directive %u is not generically for printing unsigned integers, but rather specifically for printing values of type unsigned int. Although it is possible that your int64_t is the same type as unsigned int, that would be atypical. If they are not the same type, then the mismatch between directive and actual argument result in undefined behavior. @AndrewHenle explains in his answer how to print an int64_t via printf.

  • By using a union to map the bytes of your struct in6_addr onto an array of int64_t, you are exposing yourself to the details of the byte ordering of your implementation's representation of int64_t. That you can present a specific expected result at all indicates that you are assuming a particular representation (and evidently one that does not match your system).

  • You seem to think that fewer bytes are printed than should be, but even if you corrected your printf format per Andrew's answer, I'm inclined to think that the output would be the same. You observe that the value that is printed corresponds to the first byte of the address, but think about what the next several bytes are: all zeroes, until you get to the final two. Now consider a bit pattern consisting of one byte having the value 32 (decimal), followed by seven bytes with value zero. If you interpret that pattern as a 64-bit, unsigned, little-endian integer, its value is 32. That's the most likely thing for the corrected code to print on an Intel-based machine.

Evidently you want to produce int64_t values whose logical bit patterns match the address bits. You have instead produced values whose physical bit patterns match the address bits. This is to be expected when you use a union instead of arithmetic to convert between byte array and integer. I suggest using a loop instead:

struct in6_addr sa;
int64_t addr_ints[2] = { 0, 0 };
char rncs[100] = "2000::200a";
inet_pton(AF_INET6, rncs, &sa);

for (int i = 0; i < 16; i++) {
    addr_ints[i / 8] = addr_ints[i / 8] << 8 + sa.s6_addr[i];
}
printf( "%" PRIu64 "\n", addr_ints[ 0 ] );

That also avoids problems in the event that struct in6_addr is laid out differently than you seem to expect, as long as the layout conforms to POSIX.