1

When using accept() or getpeername(), the sockaddr_storage always has ss_family=AF_INET6:

struct sockaddr_storage address = {0};
socklen_t sockaddrlen = sizeof(address);
int client = accept(sock, (struct sockaddr*)(&address), &sockaddrlen);
if (client < 0) {
    perror("Unable to accept");
    exit(EXIT_FAILURE);
}
if( address.ss_family==AF_INET6  ){
    std::cout << "IPv6" << std::endl;
} else {
    std::cout << "IPv4" << std::endl;
}

I feel like its something to do with the creation:

s = socket(AF_INET6, SOCK_STREAM, 0);

or the bind

struct sockaddr_in6 addr;
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port   = htons(port);
addr.sin6_addr   = in6addr_any;

if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {

How can I get the ss_family corrected, or tell another way what kind of IP it is?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
jeff8j
  • 33
  • 5
  • Does this answer your question? [How to support both IPv4 and IPv6 connections](https://stackoverflow.com/questions/1618240/how-to-support-both-ipv4-and-ipv6-connections) – kaylum Dec 27 '19 at 20:52
  • 1
    You're going to need more sophisticated detection to see if the IPv6 address is an IPv4-mapped address like ::ffff:1.2.3.4 – David Schwartz Dec 28 '19 at 00:04
  • the posted code is C++, not C. please correct the question tags – user3629249 Dec 28 '19 at 00:44
  • @user3629249 the only part c++ is the cout the socket part is c and thats what I have been using to google "c socket question" if I google "c++ socket question" it shows things not as relevant so I think C is the correct tag for this so other people can find it. – jeff8j Dec 30 '19 at 14:46
  • any part of the code in the file being C++ results in the C++ compiler being used. It ALL has to be C. – user3629249 Dec 31 '19 at 04:29
  • @user3629249 Yea I guess since im using g++ instead of gcc still the relevant code and searches are more c in nature printf("IPv6"); printf("IPv4"); problem solved :) – jeff8j Jan 01 '20 at 05:58

1 Answers1

1

A dual-stack socket is an IPv6 socket, just with support for IPv4, so its IP addresses will always be AF_INET6 addresses. However, for IPv4 connections, those addresses will be IPv4-mapped IPv6 addresses.

Hybrid dual-stack IPv6/IPv4 implementations recognize a special class of addresses, the IPv4-mapped IPv6 addresses. These addresses are typically written with a 96-bit prefix in the standard IPv6 format, and the remaining 32 bits written in the customary dot-decimal notation of IPv4.

Addresses in this group consist of an 80-bit prefix of zeros, the next 16 bits are ones, and the remaining, least-significant 32 bits contain the IPv4 address. For example, ::ffff:192.0.2.128 represents the IPv4 address 192.0.2.128. Another format, called "IPv4-compatible IPv6 address", is ::192.0.2.128; however, this method is deprecated.

You need to detect that explicitly, see How to resolve IPv4 address from IPv4 mapped IPv6 address?, for example :

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

struct sockaddr_storage address = {0};
socklen_t sockaddrlen = sizeof(address);
int client = accept(sock, (struct sockaddr*)(&address), &sockaddrlen);
if (client < 0) {
    perror("Unable to accept");
    exit(EXIT_FAILURE);
}
if (address.ss_family == AF_INET6){
    struct sockaddr_in6 *addr = (struct sockaddr_in6*)(&address);
    if (IN6_IS_ADDR_V4MAPPED(&(addr->sin6_addr))) {
        std::cout << "IPv4 (mapped)" << std::endl;
    } else {
        std::cout << "IPv6" << std::endl;
    }
} else {
    std::cout << "IPv4" << std::endl;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770