Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Connecting IPv4 client to IPv6 server: connection refused

I am experimenting with IPv6 sockets, particularly the "dual stack" capability offered on Windows Vista and later, and apparently on Unix by default. I am finding that when I bind my server to a specific IP address, or to the hostname resolution of my local machine, I cannot accept a connection from an IPv4 client. When I bind to INADDR_ANY however, I can.

Please consider the following code for my server. You can see that I follow Microsoft's advice of creating an IPv6 socket, then setting the IPV6_V6ONLY flag to zero:

addrinfo* result, *pCurrent, hints;

memset(&hints, 0, sizeof hints);    // Must do this!
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;     // We intend to use the addrinfo in a call to connect().  (I know it is ignored if we specify a server to connect to...)

int nRet = getaddrinfo("powerhouse", "82", &hints, &result);

SOCKET sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);

int no = 0;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) != 0)
    return -1;

if (bind(sock, result->ai_addr, result->ai_addrlen) ==  SOCKET_ERROR)
    return -1;

if (listen(sock, SOMAXCONN) == SOCKET_ERROR)
    return -1;

SOCKET sockClient = accept(sock, NULL, NULL);
                                 

Here is the code for my client. You can see I create an IPv4 socket and attempt to connect to my server:

addrinfo* result, *pCurrent, hints;

memset(&hints, 0, sizeof hints);    // Must do this!
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;

if (getaddrinfo("powerhouse", "82", &hints, &result) != 0)
    return -1;

SOCKET sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
int nRet = connect(sock, result->ai_addr, result->ai_addrlen);
                                  

The result from my connect call is always 10061: connection refused.

If I change my server code to bind to :: (or pass a NULL host to getaddrinfo() (same thing)), and change my client code to specify a NULL host in the getaddrinfo() call, then the V4 client can connect fine.

Can anyone explain why please? I have not read anything that we must specify a NULL host (hence use INADDR_ANY) if we want dual-socket behaviour. This can't be a requirement, because what I have a multihomed host and I want to accept IPv4 on only some of the available IPs?

EDIT 15/05/2013:

This is the relevant documentation which has gotten me confused as to why my code fails:

From Dual-Stack Sockets for IPv6 Winsock Applications

"Windows Vista and later offer the ability to create a single IPv6 socket which can handle both IPv6 and IPv4 traffic. For example, a TCP listening socket for IPv6 is created, put into dual stack mode, and bound to port 5001. This dual-stack socket can accept connections from IPv6 TCP clients connecting to port 5001 and from IPv4 TCP clients connecting to port 5001."

"By default, an IPv6 socket created on Windows Vista and later only operates over the IPv6 protocol. In order to make an IPv6 socket into a dual-stack socket, the setsockopt function must be called with the IPV6_V6ONLY socket option to set this value to zero before the socket is bound to an IP address. When the IPV6_V6ONLY socket option is set to zero, a socket created for the AF_INET6 address family can be used to send and receive packets to and from an IPv6 address or an IPv4 mapped address. (emphasis mine)"

like image 785
Wad Avatar asked May 10 '13 10:05

Wad


2 Answers

IPv4 and IPv6 are two separate protocols. Packets of one protocol cannot be handled using the other protocol. That is why the concept of Dual Stack exists: your system runs both the IPv4 and IPv6 protocol stacks, has both IPv4 and IPv6 addresses, etc.

Operating systems have a trick where you can have an IPv6 socket that listens on all IPv4 and IPv6 addresses. You still need to have both address families on the host, and it only works when you bind to the wildcard address. Once you bind that socket to a fixed address that doesn't work anymore and it will only work for the address that you have bound to.

So if you want to listen on all available addresses then setting IPV6_V6ONLY to 0 and listening on the wildcard address work. The IPv4 clients will be shown as using IPv6 addresses starting with ::ffff: with the last 32 bits containing the IPv4 address.

When you want to bind to specific addresses you will need sockets bound to each of the addresses you want to listen on. Then you need to use i.e. select(...) to monitor those sockets and to respond to those that become active because someone connects to them.

like image 61
Sander Steffann Avatar answered Nov 13 '22 15:11

Sander Steffann


This link http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch12lev1sec2.html gives more information about the IPv4 and IPv6 connection,

Most dual-stack hosts should use the following rules in dealing with listening sockets:

  • A listening IPv4 socket can accept incoming connections from only IPv4 clients.
  • If a server has a listening IPv6 socket that has bound the wildcard address and the IPV6_V6ONLY socket option (Section 7.8) is not set, that socket can accept incoming connections from either IPv4 clients or IPv6 clients. For a connection from an IPv4 client, the server's local address for the connection will be the corresponding IPv4-mapped IPv6 address.
  • If a server has a listening IPv6 socket that has bound an IPv6 address other than an IPv4-mapped IPv6 address, or has bound the wildcard address but has set the IPv6_V6ONLY socket option (Section 7.8), that socket can accept incoming connections from IPv6 clients only.
like image 39
Sathish Avatar answered Nov 13 '22 14:11

Sathish