Linux C ++ IPv6 UDP组播发送到errno EADDRNOTAVAIL(99)无法失败,无法分配请求的地址 [英] Linux C++ IPv6 UDP Multicast sendto fails with errno EADDRNOTAVAIL (99) Cannot assign requested address

查看:379
本文介绍了Linux C ++ IPv6 UDP组播发送到errno EADDRNOTAVAIL(99)无法失败,无法分配请求的地址的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

Part of test code below shows two parts, one for sending IPv6 multicast messages, the other for sending IPv4 multicast messages.

用于IPv4的代码可以正常工作.

Code for IPv4 works fine.

IPv6代码始终在sendto中失败,并以EADDRNOTAVAIL返回(99)无法分配请求的地址.

Code for IPv6 always fails in sendto, returning with EADDRNOTAVAIL (99) Cannot assign requested address.

...

  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

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

Network interface: The used network interface is a VLAN interface svlan1_260 with VLAN ID 260, created on physical network interface 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出了什么问题?

Is there any idea, what is going wrong for IPv6?

尽管我尝试了几次,但地址可能是错误的?

May be wrong address, although I tried several?

是否缺少套接字配置?

是否有系统设置不正确?

Any system settings, not correctly set?

谢谢

推荐答案

经过大量调试后发现问题的原因.

Found reason of the problem after much debugging.

上面的注释中提到的T位标志(动态分配的多播地址)必须正确设置.但是,如果T位设置为0(众所周知的多播地址),发送也可以.

The T-bit flag (Dynamically assigned multicast address), mentioned in comments above for sure should be correctly set. But sending also works, if T-bit is set to 0 (Well-known multicast address).

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

There's following reason for the failing sendto call. Short time before creating the socket and sending the message, on the respective network interface the MAC address has been changed via ioctl SIOCSIFHWADDR. In order to get the local IPv6 address automatically updated with the new MAC address the network interface was set down and up again via ioctl SIOCSIFFLAGS.

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

When socket was created and message sent, link up had not been finished. Therefore the sendto failed. The IPv6 messages in the tcpdump above belong to the network interface start up.

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

When waiting some seconds after network interface up, everything works fine.

这篇关于Linux C ++ IPv6 UDP组播发送到errno EADDRNOTAVAIL(99)无法失败,无法分配请求的地址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆