Porting IPv4 Application to Dualstack IPv4/IPv6

不打扰是莪最后的温柔 提交于 2019-12-08 01:59:14

问题


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


回答1:


There are 2 ways you can go:

  1. Just strip off this ::ffff: from the beginning, if present, and take it as indicator for IPv4.

  2. 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() with AI_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 call accept().

    Sounds more complicated than it really is...




回答2:


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().




回答3:


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!