Actually I'm porting an IPv4 server application to a dualstack IPv4/IPv6 application on Linux.
The basic function I have solved by using:
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_addr = in6addr_any;
...
bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
...
listen(sock, 5);
...
newsock = accept(syn->sock, (struct sockaddr *) &cli_addr, &clilen);
I can connect with IPv4 and IPv6 and use the connections. But when I want to get the IP with:
switch(data->sa_family) {
case AF_INET:
inet_ntop(AF_INET, &(((struct sockaddr_in*)data)->sin_addr), buffer, size);
break;
case AF_INET6:
inet_ntop(AF_INET6, &(((struct sockaddr_in6*)data)->sin6_addr), buffer, size);
break;
default:
buffer[0] = '?';
buffer[1] = 0;
}
I always get an IPv6 address as expected, or if it is a IPv4 connection something like ::ffff:127.0.0.1
What do I have to change, to display as a plain old IPv4 address in the form of 127.0.0.1 (without the ::ffff:-prefix)?
Thanks Teddy
There are 2 ways you can go:
Just strip off this
::ffff:
from the beginning, if present, and take it as indicator for IPv4.Use two distinct sockets. There are OS around there which do not support IPv4 and 6 via one socket (IOW, they have
V6ONLY
always enabled). Especially, WinXP does so.Then there is only one thing you can do:
- Use
getaddrinfo()
withAI_PRIVATE
for getting all local socket addresses you can have - Use them all to create a listening socket and enable
V6ONLY
immediately, if possible - Keep their descriptors in an array and use
select()
et al to determine which of them to callaccept()
.
Sounds more complicated than it really is...
- Use
You request that IPv4 mapped connections be presented as if they're actually IPv4 with code like the following:
int cli = accept( server, NULL, NULL );
int addrform = AF_INET;
setsockopt( cli, IPPROTO_IPV6, IPV6_ADDRFORM, &addrform, sizeof(addrform) );
This would need to be done on for each new client socket. It will fail for IPv6 connections; you can either ignore that or check first and only call it if you're working with an IPv4-mapped client.
Once that's done you can call getpeername()
to get the client address information in the preferred format rather than getting that as part of the call to accept()
.
There's a macro to help, but only half the battle:
if (IN6_IS_ADDR_V4MAPPED(&serv_addr.sin6_addr)) {
struct sockaddr_in tmpsa;
tmpsa.sin_family = AF_INET;
tmpsa.sin_port = 0;
tmpsa.sin_addr.s_addr = serv_addr.sin6_addr.s6_addr32[3];
/* process IPv4 address in tmpsa ... */
inet_ntop (AF_INET, &tmpsa.sin_addr, buffer, size);
} else {
/* process IPv6 address in serv_addr.. */
inet_ntop (AF_INET6, &serv_addr.sin6_addr, buffer, size);
}
Minor modifications may be required depending upon platform, here is use GNU standards.
来源:https://stackoverflow.com/questions/13949750/porting-ipv4-application-to-dualstack-ipv4-ipv6