【问题标题】:Invalid argument in sendto when using interface name to set IP_MULTICAST_IF使用接口名称设置 IP_MULTICAST_IF 时 sendto 中的参数无效
【发布时间】:2012-06-12 09:33:22
【问题描述】:

我正在编写一个发送和接收多播数据包的小程序。我需要使用其名称(例如 eth0)而不是其地址来设置传出接口。因此我必须使用 struct ip_mreqn(而不是 struct in_addr),以便我可以使用它的 imr_ifindex 字段来设置接口索引(我可以使用接口的名称来获取)。

但是由于某种原因它不起作用。对 setsockopt() 的调用工作正常,但对 sendto() 的以下调用返回“无效参数”错误。当然,如果我用 in_addr 替换 ip_mreqn 并改用接口的地址,错误就会消失。

以下是我的代码:

sd = socket(AF_INET, SOCK_DGRAM, 0);

struct ip_mreqn addr;
addr.imr_ifindex = if_nametoindex("eth0");
setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr);

struct sockaddr_in sock_addr;
memset((char *) &sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_port = destination_port;
sock_addr.sin_addr.s_addr = destination_address;

char msg[] = "abc";

while (sendto(sd, msg, sizeof(msg), 0, reinterpret_cast<const sockaddr*>(&sock_addr),
  sizeof(sock_addr)) < 0)
    throw std::runtime_error(strerror(errno));

在设置IP_MULTICAST_IF时使用struct ip_mreqn有什么问题吗?有人知道吗?非常感谢帮助。谢谢。

请注意,目标端口和地址已经按网络字节顺序排列。

【问题讨论】:

  • 您对使用接口名称而不是 IP 地址的要求有信心吗?一个接口可以有数千个 IP 地址...
  • 它拥有多个 IP 地址这一事实是否会导致此问题?在那种情况下,内核不应该只选择第一个地址吗?至少我是这么假设的。
  • 你怎么知道它“正常”工作?检查setsockopt 的返回值。 if_nametoindex 是否返回正常值?

标签: sockets multicast sendto setsockopt


【解决方案1】:

来自tldp's multicast howto

通常,系统管理员会指定默认接口多播数据报应该从哪里发送。程序员可以覆盖它并使用这个选项为给定的套接字选择一个具体的传出接口。

struct in_addr interface_addr;
setsockopt (socket, IPPROTO_IP, IP_MULTICAST_IF, &interface_addr, sizeof(interface_addr));

在他们看来,仅仅一个接口的索引是不够的。

编辑:
您可能将IP_MULTICAST_IFIPV6_MULTICAST_IF 混淆了,后者使用无符号整数来表示接口编号(请参阅man 7 ipv6)。

编辑:
事实证明,给出的示例按预期工作。这是我的完整测试用例:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>


int main()
{
    int sd = socket(AF_INET, SOCK_DGRAM, 0);
    struct sockaddr_in sa = {0};
    struct ip_mreqn addr = {{0}};
    char msg[] = "abc";

    addr.imr_ifindex = if_nametoindex("eth0");
    setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr));

    sa.sin_family = AF_INET;
    sa.sin_port = 8653;

    inet_pton(AF_INET, "224.255.255.5", &sa.sin_addr);
    if (sendto(sd, msg, sizeof(msg), 0, (void*)&sa, sizeof(sa)) < 0) {
        perror("bugger");
        return 1;
    }
    return 0;
}

【讨论】:

  • man 7 ip 表示在 IP_MULTICAST_IF 的情况下 setsockopt 将 ip_mreqn 作为参数。查看内核代码here 我可以看到即使我只指定索引它也应该可以正常工作,因为它首先尝试使用索引来检索设备。
  • 确实,现在我已经实际运行了您的示例代码,它的工作方式与您一样。
  • 是的,我很惊讶您的代码 sn-p 确实有效。这意味着我在某处有其他东西导致错误。要去挖它,会回来的。非常感谢。
  • 知道了。发生这种情况是因为我从未初始化 ip_mreqn,这导致其余两个字段(imr_multiaddr 和 imr_address)出现垃圾。最终它在尝试将字段作为 IP 地址处理时遇到了错误,因此是“无效参数”。愚蠢的错误,吸取了教训。非常感谢您的帮助。
  • 它间接回答了我的问题,因此接受它作为答案。
【解决方案2】:

在答案的代码示例中,不要忘记在.sin_port中htons()目标端口

【讨论】:

  • 这应该是评论,而不是答案。请等到您有足够的声望后再发布 cmets,而不是在答案部分发垃圾邮件。
猜你喜欢
  • 1970-01-01
  • 2013-03-25
  • 2018-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-16
  • 2018-10-11
  • 2016-01-07
相关资源
最近更新 更多