简体   繁体   English

如何通过接口过滤组播接收套接字?

[英]How to filter a multicast receiving socket by interface?

I need to create two sockets listening on the same IP:port but on different interfaces: 我需要创建两个侦听同一IP:端口但在不同接口上的套接字:

  1. socket0 receives UDP traffic sent to 224.2.2.2:5000 on interface eth0 socket0接收发送到接口eth0上的224.2.2.2:5000的UDP流量
  2. socket1 receives UDP traffic sent to 224.2.2.2:5000 on interface eth1 socket1接收发送到接口eth1上的224.2.2.2:5000的UDP流量

It seemed pretty straight forward until I realized that Linux merges all of that into the same traffic. 直到我意识到Linux将所有这些合并到相同的流量中之前,它似乎非常直接。 For example, say there's only traffic on eth1 and there's no activity on eth0. 例如,假设eth1上只有流量,而且eth0没有活动。 When I first create socket0 it won't be receiving any data but as soon as I create socket1 (and join the multicast group) then socket0 will also start receiving the same data. 当我第一次创建socket0时,它不会接收任何数据,但是一旦我创建socket1(并加入多播组),socket0也将开始接收相同的数据。 I found this link that explains this. 我找到了解释这一点的链接

Now this actually makes sense to me because the only moment when I specify the network interface is when joining the multicast group setsockopt(socket,IPPROTO_IP,IP_ADD_MEMBERSHIP,...) with ip_mreq.imr_interface.s_addr . 现在这对我来说实际上是有道理的,因为我指定网络接口的唯一时刻是使用ip_mreq.imr_interface.s_addr加入多播组setsockopt(socket,IPPROTO_IP,IP_ADD_MEMBERSHIP,...) I believe this specifies which interface joins the group but has nothing to do with from which interface your socket will receive from. 我相信这指定哪个接口加入组但与套接字将从哪个接口接收无关。

What I tried so far is binding the sockets to the multicast address and port, which behaves like mentioned above. 到目前为止我尝试将套接字绑定到多播地址和端口,其行为如上所述。 I've tried binding to the interface address but that doesn't work on Linux (it seems to do so on Windows though), you don't receive any traffic on the socket. 我已经尝试绑定到接口地址,但这在Linux上不起作用(虽然它似乎在Windows上这样做),你在套接字上没有收到任何流量。 And finally, I've tried binding to INADDR_ANY but this isn't what I want since I will receive any other data sent to the port regardless of the destination IP, say unicast for example, and it will still not stop multicast data from other interfaces. 最后,我尝试绑定到INADDR_ANY但这不是我想要的,因为我将收到发送到端口的任何其他数据而不管目标IP,例如单播,并且它仍然不会停止来自其他的组播数据接口。

I cannot use SO_BINDTODEVICE since it requires root privileges. 我不能使用SO_BINDTODEVICE因为它需要root权限。

So what I want to know is if this is possible at all. 所以我想知道的是,这是否可行。 If it can't be done then that's fine, I'll take that as an answer and move on, I just haven't been able to find any way around it. 如果不能做到那就没关系,我会把它作为一个答案并继续前进,我只是无法找到任何方法。 Oh, and I've tagged the question as C because that's what we're using, but I'm thinking it really might not be specific to the language. 哦,我把这个问题标记为C因为这就是我们正在使用的东西,但我认为它真的可能不是特定于语言。

I haven't included the code for this because I believe it's more of a theoretical question rather than a problem with the source code. 我没有包含此代码,因为我认为它更多的是理论问题,而不是源代码的问题。 We've been working with sockets (multicast or otherwise) for a while now without any problems, it's just this is the first time we've had to deal with multiple interfaces. 我们一直在使用套接字(多播或其他)一段时间没有任何问题,这只是我们第一次必须处理多个接口。 But if you think it might help I can write some minimal working example. 但如果你认为它可能有所帮助,我可以写一些最小的工作示例。


Edit about the possible duplicate : 编辑 可能的重复

I think the usecase I'm trying to achieve here is different. 我认为我在这里尝试实现的用例是不同的。 The socket is supposed to receive data from the same multicast group and port (224.2.2.2:5000 in the example above) but only from one specific interface. 套接字应该从同一个组播组和端口(上例中的224.2.2.2:5000)接收数据,但只能从一个特定接口接收数据。 To put it another way, both interfaces are receiving data from the same multicast group (but different networks, so data is different) and I need each socket to only listen on one interface. 换句话说,两个接口都从同一个组播组接收数据(但不同的网络,因此数据不同),我需要每个套接字只能在一个接口上监听。

I think that question is about multiple groups on same port, rather than same group from different interfaces. 我认为这个问题是关于同一端口上的多个组,而不是来自不同接口的相同组。 Unless there's something I'm not seeing there that might actually help me with this. 除非有一些我没有看到的东西,实际上可能对我有所帮助。

Yes, you can do what you want on Linux, without root privileges: 是的,您可以在没有root权限的情况下在Linux上执行您想要的操作:

Bind to INADDR_ANY and set the IP_PKTINFO socket option. 绑定到INADDR_ANY并设置IP_PKTINFO套接字选项。 You then have to use recvmsg() to receive your multicast UDP packets and to scan for the IP_PKTINFO control message. 然后,您必须使用recvmsg()接收多播UDP数据包并扫描IP_PKTINFO控制消息。 This gives you some side band information of the received UDP packet: 这为您提供了收到的UDP数据包的一些边带信息:

struct in_pktinfo {
    unsigned int   ipi_ifindex;  /* Interface index */
    struct in_addr ipi_spec_dst; /* Local address */
    struct in_addr ipi_addr;     /* Header Destination address */
};

The ipi_ifindex is the interface index the packet was received on. ipi_ifindex是接收数据包的接口索引。 (You can turn this into an interface name using if_indextoname() or the other way round with if_nametoindex() . (您可以使用变成一个接口名称此if_indextoname()或其他方式轮if_nametoindex()

As you said on Windows the same network functions have different semantics, especially for UDP and even more for multicast. 正如您在Windows上所说的那样,相同的网络功能具有不同的语义,特别是对于UDP,甚至更多用于多播。

The Linux bind() semantics for the IP address for UDP sockets are mostly useless. UDP套接字的IP地址的Linux bind()语义大多没用。 It is essentially just a destination address filter. 它本质上只是一个目标地址过滤器。 You will almost always want to bind to INADDR_ANY for UDP sockets since you either do not care to which address a packet was sent or you want to receive packets for multiple addresses (eg receiving unicast and multicast). 您几乎总是希望绑定到INADDR_ANY以获取UDP套接字,因为您不关心数据包的发送地址,也不想接收多个地址的数据包(例如,接收单播和多播)。

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

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