0

How to get the IPv4 address from IPv4-mapped IPv6 addresses?

For e.g I have an IP address ::FFFF:129.144.52.38. from this, I need to extract 129.144.52.38. Is there any API for this purpose?

I can identify IPv6 or IPv4 address family by using the following function

int getaddrfamily(const char *addr)
{
    struct addrinfo hint, *info =0;
    memset(&hint, 0, sizeof(hint));
    hint.ai_family = AF_UNSPEC;
    // Uncomment this to disable DNS lookup
    //hint.ai_flags = AI_NUMERICHOST;
    int ret = getaddrinfo(addr, 0, &hint, &info);
    if (ret)
        return -1;
    int result = info->ai_family;
    freeaddrinfo(info);
    return result;
}

If I give a IPv4 mapped IPv6 address, then how it's possible to identify if it's a mapped adress? Is there any socket API to extract IPv4 from a mapped IPv6 address?

sarat
  • 10,512
  • 7
  • 43
  • 74

2 Answers2

4

Try something like this:

#ifndef IN6_IS_ADDR_V4MAPPED
#define IN6_IS_ADDR_V4MAPPED(a) \
       ((((a)->s6_words[0]) == 0) && \
        (((a)->s6_words[1]) == 0) && \
        (((a)->s6_words[2]) == 0) && \
        (((a)->s6_words[3]) == 0) && \
        (((a)->s6_words[4]) == 0) && \
        (((a)->s6_words[5]) == 0xFFFF))
#endif

unsigned long getIPv4addr(const char *addr)
{
    struct addrinfo hint, *info = 0;
    unsigned long result = INADDR_NONE;
    memset(&hint, 0, sizeof(hint));
    hint.ai_family = AF_UNSPEC;
    // Uncomment this to disable DNS lookup
    //hint.ai_flags = AI_NUMERICHOST;
    if (getaddrinfo(addr, 0, &hint, &info) == 0)
    {
        switch (info->ai_family)
        {
            case AF_INET:
            {
                struct sockaddr_in *addr = (struct sockaddr_in*)(info->ai_addr);
                result = addr->sin_addr.s_addr;
                break;
            }

            case AF_INET6:
            {
                struct sockaddr_in6 *addr = (struct sockaddr_in6*)(info->ai_addr);
                if (IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr))
                    result = ((in_addr*)(addr->sin6_addr.s6_addr+12))->s_addr;
                break;
            }
        }
        freeaddrinfo(info);
    }
    return result;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • There is some typo in the implementation of IN6_IS_ADDR_V4MAPPED (s6_words with an extra "s"). It should be 16 bits wide for each s6_word. – king flight Dec 16 '21 at 09:14
1

You simply extract the four last bytes from the IPv6 address, combine them into a single 32-bit number, and you have your IPv4 address.

Of course you need to check that it really is a IPv4-mapped address first.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • I'm not sure why this was downvoted, it's 100% correct and basic C. – Mahmoud Al-Qudsi Jul 20 '17 at 16:35
  • 3
    @MahmoudAl-Qudsi: Because it doesn't mention checking the first 10 bytes of IPv6 to be all `0`'s and then checking the following 2 bytes to be all `ff`'s. Only then someone can assume mapping of IPv4 into IPv6 space and extraction of the lower 4 bytes as IPv4. Otherwise what he suggested is a simple truncation of the higher 12 bytes of the IPv6 address, which makes no sense. – ahmd0 Mar 23 '18 at 07:20
  • 1
    @ahmd0 the question wasn't how to identify an IPv4-mapped IPv6 address, though. It was " _given_ such an address, how do you obtain the IPv4 from it. – Mahmoud Al-Qudsi Mar 23 '18 at 14:53
  • @MahmoudAl-Qudsi: You can't get an IPv4 from any arbitrary IPv6. The one I described above is a special case. – ahmd0 Mar 23 '18 at 16:05