Linux C++IPv6 UDP多播发送到失败,错误号为EADDRNOTAVAIL(99)无法分配请求的地址



我正在尝试发送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

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

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消息属于网络接口启动。

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

最新更新