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
V6ONLYalways enabled). Especially, WinXP does so.Then there is only one thing you can do:
- Use
getaddrinfo()withAI_PRIVATEfor getting all local socket addresses you can have - Use them all to create a listening socket and enable
V6ONLYimmediately, 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