在具有多个接口的linux主机上接收多播

时间:2018-01-17 20:18:47

标签: c++ c linux networking multicast

我有一台运行Ubuntu 16.04的主机通过主要有线网络接口连接到一个网络,并通过USB转以太网适配器连接到另一个网络。使用tcpdump,我能够验证两个网络接口上的传入多播数据包。但是,我的应用程序没有从辅助接口接收任何多播数据。如果我断开电缆到主接口,然后重新启动我的应用程序,那么我确实从辅助接口接收数据。只有两个接口都连接,应用程序才会从辅助接口接收。

2 个答案:

答案 0 :(得分:1)

我发现了类似的问题(带有USB转以太网适配器的Raspberry Pi Zero无法响应mDNS查询)。要确定您的问题是否相同,您的应用是否会在同时运行tcpdump时正确接收多播流量?运行带有--no-promiscuous-mode的tcpdump是否看不到多播流量?

如果你的回答都是肯定的,那么我找到的解决方法只是ip link set eth0 promisc on。我不知道它是否是硬件错误(我使用的是控创DM9601适配器,ID 0FE6:9700)或驱动程序错误,但无论哪种方式,启用混杂模式似乎都能为我修复多播接收。或者,您可以尝试更好的USB到以太网适配器。

答案 1 :(得分:0)

The ip_mreq stucture is passed as the option value for the IP_ADD_MEMBERSHIP socket option to join a multicast group. From the Multicast programming HOWTO from the The Linux Documentation Project:

The first member, imr_multiaddr, holds the group address you want to join. Remember that memberships are also associated with interfaces, not just groups. This is the reason you have to provide a value for the second member: imr_interface. This way, if you are in a multihomed host, you can join the same group in several interfaces. You can always fill this last member with the wildcard address (INADDR_ANY) and then the kernel will deal with the task of choosing the interface.

The IP_MULTICAT_IF socket option is also relevant on a multihomed host to set the outbound interface for multicast data sent via the socket. More information on these socket options, the ip_mreq structure, and the newer ip_mreqn stucture is found here.

For those using Boost on a multihomed host, you will need to use the native handle to join the group on specific interfaces. As of Boost 1.58 running on Ubuntu 16.04, the socket option abstraction ip::multiast::join_group() joins the group on an interface of the kernel's choosing and does not allow the developer to specify an interface. The socket option abstraction ip::multicast::outbound_interface() controls the outbound interface but does not affect which interface the socket receives on.

Here is a code sample to join a group on a specific interface based on the local interface IP address:

struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(discovery_ip);
mreq.imr_interface.s_addr = inet_addr(local_interface_ip);
if(setsockopt(socket_.native_handle(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))) {
    ... handle error ...
}