Using IN6ADDR_SETV4MAPPED sockets and dual stack

This is a continuation. Connecting an IPv4 Client to an IPv6 Server: Connection Failure . I am experimenting with dual stack sockets and trying to figure out what is useful for setockopt with IPV6_V6ONLY. In a related question, I was told that "setting IPV6_V6ONLY to 0 can be useful if you also bind the server to an IPv4 IPv4 address with an IPv4 mapping." I did this below and expected my server to accept connections from both IPv6 and the IPv4 client. But shockingly, when I launch my client with V4 and V6 socket, none of them can connect!

Can someone please tell me what I am doing wrong, or have I misunderstood the functionality of the IPv6 dual stack?

Server:

void ConvertToV4MappedAddressIfNeeded(PSOCKADDR pAddr) { // if v4 address, convert to v4 mapped v6 address if (AF_INET == pAddr->sa_family) { IN_ADDR In4addr; SCOPE_ID scope = INETADDR_SCOPE_ID(pAddr); USHORT port = INETADDR_PORT(pAddr); In4addr = *(IN_ADDR*)INETADDR_ADDRESS(pAddr); ZeroMemory(pAddr, sizeof(SOCKADDR_STORAGE)); IN6ADDR_SETV4MAPPED( (PSOCKADDR_IN6)pAddr, &In4addr, scope, port ); } } addrinfo* result, hints; memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; 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; ConvertToV4MappedAddressIfNeeded(result->ai_addr); if (bind(sock, result->ai_addr, 28/*result->ai_addrlen*/) == SOCKET_ERROR) return -1; if (listen(sock, SOMAXCONN) == SOCKET_ERROR) return -1; SOCKET sockClient = accept(sock, NULL, NULL); printf("Got one!\n"); 

Customer:

 addrinfo* result, *pCurrent, hints; char szIPAddress[INET6_ADDRSTRLEN]; memset(&hints, 0, sizeof hints); // Must do this! hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; const char* pszPort = "82"; if (getaddrinfo("powerhouse", "82", &hints, &result) != 0) return -1; SOCKET sock = socket(AF_INET, result->ai_socktype, result->ai_protocol); int nRet = connect(sock, result->ai_addr, result->ai_addrlen); 
+2
source share
1 answer

My C skills are a little rusty, so here is a counter example written in Python. My local IPv4 address is 37.77.56.75, so I will bind. I tried to focus on concepts as simple as possible.

This is the server side:

 #!/usr/bin/env python import socket # We bind to an IPv6 address, which contains an IPv6-mapped-IPv4-address, # port 5000 and we leave the flowinfo (an ID that identifies a flow, not used # a lot) and the scope-id (basically the interface, necessary if using # link-local addresses) host = '::ffff:37.77.56.75' port = 5000 flowinfo = 0 scopeid = 0 sockaddr = (host, port, flowinfo, scopeid) # Create an IPv6 socket, set IPV6_V6ONLY=0 and bind to the mapped address sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0) sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) sock.bind(sockaddr) # Listen and accept a connection sock.listen(0) conn = sock.accept() # Print the remote address print conn[1] 

Here we bind to the IPv6 address in the code, but the address is actually the IPv6 mapped IPv4 address, so we actually bind to the IPv4 address. This can be seen by looking at ie netstat:

 $ netstat -an | fgrep 5000 tcp4 0 0 37.77.56.75.5000 *.* LISTEN 

Then we can use the IPv4 client to connect to this server:

 #!/usr/bin/env python import socket # Connect to an IPv4 address on port 5000 host = '37.77.56.75' port = 5000 sockaddr = (host, port) # Create an IPv4 socket and connect sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) conn = sock.connect(sockaddr) 

And the server will show us who is connected using the IPv6 address view:

 ('::ffff:37.77.56.76', 50887, 0, 0) 

In this example, I connected from the IPv4 host 37.77.56.76 , and it selects port 50887 to connect.

In this example, we are only listening on an IPv4 address (using IPv6 sockets, but it is still an IPv4 address), so clients with IPv6 support will not be able to connect. A client with IPv4 and IPv6 could, of course, use IPv6 sockets with IPv6-mapped IPv4 addresses, but then it really would not use IPv6, but simply an IPv6 representation of an IPv4 connection.

A two-stack server must either:

  • listening to a wildcard address that will force the OS to accept connections on any address (both IPv4 and IPv6)
  • listen for both the IPv6 address and the IPv4 address (either by creating an IPv4 socket, or by creating an IPv6 socket, and listening to the IPv6-mapped-IPv4 address, as shown above).

Using a wildcard address is the easiest. Just use the server example above and replace the host name:

 # We bind to the wildcard IPv6 address, which will make the OS listen on both # IPv4 and IPv6 host = '::' port = 5000 flowinfo = 0 scopeid = 0 sockaddr = (host, port, flowinfo, scopeid) 

The "My Mac OS X" field shows this as:

 $ netstat -an | fgrep 5000 tcp46 0 0 *.5000 *.* LISTEN 

Pay attention to tcp46 , which indicates that it is listening on both address families. Unfortunately, on Linux, it only shows tcp6 even when listening on both families.

Now for the most complex example: listening to multiple sockets.

 #!/usr/bin/env python import select import socket # We bind to an IPv6 address, which contains an IPv6-mapped-IPv4-address sockaddr1 = ('::ffff:37.77.56.75', 5001, 0, 0) sock1 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0) sock1.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) sock1.bind(sockaddr1) sock1.listen(0) # And we bind to a real IPv6 address sockaddr2 = ('2a00:8640:1::224:36ff:feef:1d89', 5001, 0, 0) sock2 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0) sock2.bind(sockaddr2) sock2.listen(0) # Select sockets that become active sockets = [sock1, sock2] readable, writable, exceptional = select.select(sockets, [], sockets) for sock in readable: # Accept the connection conn = sock.accept() # Print the remote address print conn[1] 

When running this example, two sockets are displayed:

 $ netstat -an | fgrep 5000 tcp6 0 0 2a00:8640:1::224.5000 *.* LISTEN tcp4 0 0 37.77.56.75.5000 *.* LISTEN 

And now IPv6-only clients can connect to 2a00:8640:1::224:36ff:feef:1d89 , and IPv4-only clients can connect to 37.77.56.75 . Dual stack clients can choose which protocol they want to use.

+7
source

Source: https://habr.com/ru/post/944707/


All Articles