【问题标题】:Porting IPv4 Application to Dualstack IPv4/IPv6将 IPv4 应用程序移植到双栈 IPv4/IPv6
【发布时间】:2012-12-19 09:59:30
【问题描述】:

实际上,我正在将 IPv4 服务器应用程序移植到 Linux 上的双栈 IPv4/IPv6 应用程序。

我已经解决的基本功能:

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);

我可以连接 IPv4 和 IPv6 并使用这些连接。但是当我想通过以下方式获取 IP 时:

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;
}

我总是按预期获得 IPv6 地址,或者如果它是 IPv4 连接,例如 ::ffff:127.0.0.1

我必须更改什么,才能以 127.0.0.1 的形式显示为普通的旧 IPv4 地址(不带 ::ffff:-prefix)?

谢谢 泰迪熊

【问题讨论】:

    标签: ipv6


    【解决方案1】:

    有一个宏可以帮助,但只成功了一半:

      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);
      }
    

    可能需要根据平台进行细微修改,这里是使用 GNU 标准。

    【讨论】:

      【解决方案2】:

      有两种方法可供选择:

      1. 只要从开头去掉这个::ffff:(如果存在),并将其作为 IPv4 的指示符。

      2. 使用两个不同的套接字。那里的操作系统不支持通过一个套接字支持 IPv4 和 6(IOW,它们始终启用V6ONLY)。尤其是 WinXP 会这样做。

        那么你只能做一件事:

        • 使用getaddrinfo()AI_PRIVATE 获取您可以拥有的所有本地套接字地址
        • 如果可能的话,全部使用它们来创建一个监听套接字并立即启用V6ONLY
        • 将它们的描述符保存在一个数组中,并使用select() 等来确定要调用它们中的哪一个accept()

        听起来比实际复杂...

      【讨论】:

        【解决方案3】:

        您要求将 IPv4 映射连接呈现为就像它们实际上是 IPv4 一样,代码如下:

        int cli = accept( server, NULL, NULL );
        int addrform = AF_INET;
        setsockopt( cli, IPPROTO_IPV6, IPV6_ADDRFORM, &addrform, sizeof(addrform) );
        

        这需要对每个新的客户端套接字进行。 IPv6 连接将失败;您可以忽略它,也可以先检查,仅在使用 IPv4 映射客户端时才调用它。

        完成后,您可以调用getpeername() 以首选格式获取客户端地址信息,而不是在调用accept() 时获取该信息。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-06-10
          • 2020-01-12
          • 2011-01-17
          • 2014-03-07
          • 2016-01-12
          • 2023-03-06
          • 2019-06-09
          • 1970-01-01
          相关资源
          最近更新 更多