繁体   English   中英

Linux C++ IPv6 UDP 多播发送失败并出现错误 EADDRNOTAVAIL (99) 无法分配请求的地址

[英]Linux C++ IPv6 UDP Multicast sendto fails with errno EADDRNOTAVAIL (99) Cannot assign requested address

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

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

IPv4 的代码工作正常。

用于 IPv6 的代码在 sendto 中总是失败,返回 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 位标志(动态分配的多播地址)肯定应该正确设置。 但是,如果 T 位设置为 0(众所周知的多播地址),发送也可以。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM