【问题标题】:Linux C++ IPv6 UDP Multicast sendto fails with errno EADDRNOTAVAIL (99) Cannot assign requested addressLinux C++ IPv6 UDP Multicast sendto 失败并出现 errno EADDRNOTAVAIL (99) 无法分配请求的地址
【发布时间】:2020-04-02 23:38:25
【问题描述】:

我正在尝试发送 IPv6 UDP 多播消息。

下面的部分测试代码显示了两个部分,一个用于发送 IPv6 组播消息,另一个用于发送 IPv4 组播消息。

IPv4 代码运行良好。

发送到 IPv6 的代码总是失败,返回 EADDRNOTAVAIL (99) 无法分配请求的地址。

...

  if (ipV6Select)
  {
    // Create IPv6 DGRAM Socket
    int sock = socket(AF_INET6, SOCK_DGRAM, 0);
    if (sock < 0)
      throw OIP::OipException(OIP_SOCK_ACTION_FAILED("Cannot set socket option IPV6 socket, socket function failed with retVal " <<
                                                      sock << " (errno=" << strerror(errno) << " (" << errno << ")."));

    // Register multicast interface.
    int ifIdx{static_cast<int>(if_nametoindex("svlan1_260"))};
    std::cout << "Temp:ifIdx=" << ifIdx << std::endl;
    std::lock_guard<std::mutex> lockGuard(setSockoptMutex);
    retVal = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIdx, sizeof(ifIdx));
    if (retVal < 0)
       throw OIP::OipException(OIP_SOCK_ACTION_FAILED("Cannot set socket option IPV6_MULTICAST_IF, setsockopt function failed with retVal " <<
                                                      retVal << " (errno=" << strerror(errno) << " (" << errno << ")."));

    // Create IPv6 address.
    struct sockaddr_in6 sockAddrIpV6{};
    sockAddrIpV6.sin6_family = {AF_INET6};
    sockAddrIpV6.sin6_port = {htons(2020)};
    sockAddrIpV6.sin6_scope_id = {static_cast<uint32_t>(ifIdx)};
    inet_pton(AF_INET6, "FF02:0000:0000:0000:0000:0000:0000:00FE", &sockAddrIpV6.sin6_addr);

    std::cout << "IPV6 Send: family=" << sockAddrIpV6.sin6_family << ", sin6_port=" << sockAddrIpV6.sin6_port  << ", sin6_scope_id=" << sockAddrIpV6.sin6_scope_id
              << ", addr=FF02:0000:0000:0000:0000:0000:0000:00FE" << std::endl;


    // Send message to socket.
    retVal = sendto(sock,
                    sendMsgCharBufVect.data(),
                    sendMsgCharBufVect.size(),
                    0,
                    reinterpret_cast<struct sockaddr*>(&sockAddrIpV6),
                    sizeof(struct sockaddr_in6));
    if (retVal < 0)
      throw OIP::OipException(OIP_SOCK_ACTION_FAILED("IPV6 Socket send data (socket sendto function) failed with retVal " << retVal <<
                                                      " (errno=" << strerror(errno) << " (" << errno << "))."));
  }

  // Send IPV4 multicast message.
  else
  {
    // Create IPv4 DGRAM Socket
    std::string ipV4Str{"239.0.0.254"};
    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock < 0)
      throw OIP::OipException(OIP_SOCK_ACTION_FAILED("Cannot set socket option IPV4 socket, socket function failed with retVal " <<
                                                      sock << " (errno=" << strerror(errno) << " (" << errno << ")."));

    // Register multicast interface.
    struct ip_mreqn mreqn{};
    inet_aton(ipV4Str.c_str(), &mreqn.imr_multiaddr);
    mreqn.imr_ifindex        = {static_cast<int>(if_nametoindex("svlan1_260"))};
    retVal = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn));
    if (retVal < 0)
       throw OIP::OipException(OIP_SOCK_ACTION_FAILED("Cannot set socket option IP_MULTICAST_IF, setsockopt function failed with retVal " <<
                                                      retVal << " (errno=" << strerror(errno) << " (" << errno << ")."));

    // Create IPv4 address.
     struct sockaddr_in sockAddrIpV4{};
     sockAddrIpV4.sin_family = {AF_INET};
     sockAddrIpV4.sin_port = {htons(2020)};
     inet_aton(ipV4Str.c_str(), &sockAddrIpV4.sin_addr);

     std::cout << "IPV4 Send: family=" << sockAddrIpV4.sin_family << ", sin_port=" << sockAddrIpV4.sin_port  << ", addr=" << std::hex << sockAddrIpV4.sin_addr.s_addr<< std::dec <<std::endl;

     // Send message to socket.
     retVal = sendto(sock,
                     sendMsgCharBufVect.data(),
                     sendMsgCharBufVect.size(),
                     0,
                     reinterpret_cast<struct sockaddr*>(&sockAddrIpV4),
                     sizeof(struct sockaddr_in));
     if (retVal < 0)
       throw OIP::OipException(OIP_SOCK_ACTION_FAILED("IPV4 Socket send data (socket sendto function) failed with retVal " << retVal <<
                                                      " (errno=" << strerror(errno) << " (" << errno << "))."));
  }

...

tcpdump IPv4 显示带有请求地址的传出多播消息:

2020-04-02 23:30:19.384892 00:60:1d:7d:08:07 (oui Unknown) > 01:00:5e:00:00:fe (oui Unknown), ethertype 802.1Q (0x8100), length 90: vlan 260, p 0, ethertype IPv4, (tos 0x0, ttl 1, id 54916, offset 0, flags [none], proto UDP (17), length 72)
    100.5.81.1.38790 > 239.0.0.254.2020: UDP, length 44
        0x0000:  0100 5e00 00fe 0060 1d7d 0807 8100 0104
        0x0010:  0800 4500 0048 d684 0000 0111 3e1c 6405
        0x0020:  5101 ef00 00fe 9786 07e4 0034 a54a 4479

用于 IPV6 的 tcpdump 仅显示自动生成的 IPV6 多播消息:

2020-04-02 22:48:19.569203 00:60:1d:7d:08:07 (oui Unknown) > 33:33:00:00:00:16 (oui Unknown), ethertype 802.1Q (0x8100), length 174: vlan 260, p 0, ethertype IPv6, [|ip6]
        0x0000:  3333 0000 0016 0060 1d7d 0807 8100 0104
        0x0010:  86dd 6000 0000 0074 0001 0000 0000 0000
        0x0020:  0000 0000 0000 0000 0000 ff02 0000 0000
2020-04-02 22:48:20.049156 00:60:1d:7d:08:07 (oui Unknown) > 33:33:00:00:00:16 (oui Unknown), ethertype 802.1Q (0x8100), length 174: vlan 260, p 0, ethertype IPv6, [|ip6]
        0x0000:  3333 0000 0016 0060 1d7d 0807 8100 0104
        0x0010:  86dd 6000 0000 0074 0001 0000 0000 0000
        0x0020:  0000 0000 0000 0000 0000 ff02 0000 0000
2020-04-02 22:48:20.329171 00:60:1d:7d:08:07 (oui Unknown) > 33:33:ff:7d:08:07 (oui Unknown), ethertype 802.1Q (0x8100), length 90: vlan 260, p 0, ethertype IPv6, [|ip6]
        0x0000:  3333 ff7d 0807 0060 1d7d 0807 8100 0104
        0x0010:  86dd 6000 0000 0020 3aff 0000 0000 0000
        0x0020:  0000 0000 0000 0000 0000 ff02 0000 0000
root@MEC2-81-1-STDBY:/lib# 2020-04-02 22:48:21.359289 00:60:1d:7d:08:07 (oui Unknown) > 33:33:00:00:00:16 (oui Unknown), ethertype 802.1Q (0x8100), length 134: vlan 260, p 0, ethertype IPv6, [|ip6]
        0x0000:  3333 0000 0016 0060 1d7d 0807 8100 0104
        0x0010:  86dd 6000 0000 004c 0001 fe80 0000 0000
        0x0020:  0000 0260 1dff fe7d 0807 ff02 0000 0000
2020-04-02 22:48:21.389154 00:60:1d:7d:08:07 (oui Unknown) > 33:33:00:00:00:16 (oui Unknown), ethertype 802.1Q (0x8100), length 94: vlan 260, p 0, ethertype IPv6, [|ip6]
        0x0000:  3333 0000 0016 0060 1d7d 0807 8100 0104
        0x0010:  86dd 6000 0000 0024 0001 fe80 0000 0000
        0x0020:  0000 0260 1dff fe7d 0807 ff02 0000 0000
2020-04-02 22:48:22.159152 00:60:1d:7d:08:07 (oui Unknown) > 33:33:00:00:00:16 (oui Unknown), ethertype 802.1Q (0x8100), length 134: vlan 260, p 0, ethertype IPv6, [|ip6]
        0x0000:  3333 0000 0016 0060 1d7d 0807 8100 0104
        0x0010:  86dd 6000 0000 004c 0001 fe80 0000 0000
        0x0020:  0000 0260 1dff fe7d 0807 ff02 0000 0000
2020-04-02 22:48:22.319152 00:60:1d:7d:08:07 (oui Unknown) > 33:33:00:00:00:16 (oui Unknown), ethertype 802.1Q (0x8100), length 94: vlan 260, p 0, ethertype IPv6, [|ip6]
        0x0000:  3333 0000 0016 0060 1d7d 0807 8100 0104
        0x0010:  86dd 6000 0000 0024 0001 fe80 0000 0000
        0x0020:  0000 0260 1dff fe7d 0807 ff02 0000 0000

网络接口: 使用的网络接口是 VLAN ID 为 260 的 VLAN 接口 svlan1_260,在物理网络接口 eth2 上创建。

eth2      Link encap:Ethernet  HWaddr 00:00:00:00:81:01  
          UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1513  Metric:1
          RX packets:3684437 errors:0 dropped:0 overruns:0 frame:0
          TX packets:3410666 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:572562185 (546.0 MiB)  TX bytes:109620349 (104.5 MiB)
          Interrupt:32
svlan1_260 Link encap:Ethernet  HWaddr 00:60:1d:7d:08:07  
          inet addr:100.5.81.1  Bcast:100.5.255.255  Mask:255.255.0.0
          inet6 addr: fe80::260:1dff:fe7d:807/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:709 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:85982 (83.9 KiB)

问题:

有什么想法,IPv6 出了什么问题?

可能是地址错误,虽然我尝试了几个?

是否缺少任何套接字配置?

任何系统设置,没有正确设置?

谢谢

【问题讨论】:

  • 如果您不使用永久多播地址(自己制作),则应设置 T 位(ff12::fe)。您使用的地址属于 IANA 控制的永久 IPv6 多播范围:“动态 IPv6 多播地址可以由分配服务器或终端主机分配。无论分配机制如何,所有动态分配的 IPv6 多播地址必须将 T 位设置为 1。"
  • Ron Maupin,感谢您的评论。我将 T 位标志更改为 1(动态分配的多播地址)。不幸的是,同样的失败仍然发生。

标签: linux udp ipv6 sendto multicastsocket


【解决方案1】:

经过多次调试,找到了问题的原因。

上面的 cmets 中提到的 T 位标志(动态分配的多播地址)肯定应该正确设置。但是如果 T 位设置为 0(众所周知的多播地址),发送也可以。

sendto 调用失败的原因如下。 在创建套接字和发送消息之前不久,在相应的网络接口上,MAC 地址已通过 ioctl SIOCSIFHWADDR 更改。为了使用新的 MAC 地址自动更新本地 IPv6 地址,网络接口通过 ioctl SIOCSIFFLAGS 重新设置和重新设置。

创建套接字并发送消息时,链接尚未完成。因此 sendto 失败。上面 tcpdump 中的 IPv6 消息属于网络接口启动。

在网络接口启动后等待几秒钟,一切正常。

【讨论】:

    猜你喜欢
    • 2012-11-30
    • 2023-03-11
    • 2017-09-13
    • 2018-04-27
    • 2022-01-16
    • 2021-05-26
    • 2015-01-14
    • 2020-09-04
    • 2017-05-08
    相关资源
    最近更新 更多