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

时间:2021-01-27 18:10:36

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
  2. socket0接收发送到224.2.2:5000的UDP流量,接口eth0
  3. socket1 receives UDP traffic sent to 224.2.2.2:5000 on interface eth1
  4. socket1在接口eth1上接收发送到224.2.2:5000的UDP流量

It seemed pretty straight forward until I realized that Linux merges all of that into the same traffic. For example, say there's only traffic on eth1 and there's no activity on 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. I found this link that explains this.

在我意识到Linux将所有这些都合并到相同的流量中之前,这似乎是非常直接的。例如,假设eth1上只有通信量,eth0上没有活动。当我第一次创建socket0时,它不会接收任何数据,但是一旦我创建了socket1(并加入多播组),那么socket0也将开始接收相同的数据。我找到了解释这个的链接。

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. I believe this specifies which interface joins the group but has nothing to do with from which interface your socket will receive from.

现在这对我来说是有意义的,因为我指定网络接口的唯一时刻是在加入多播组setsockopt(socket,IPPROTO_IP,IP_ADD_MEMBERSHIP,…)时使用ip_mreq.imr_interface.s_addr。我认为这指定了哪个接口加入组,但与您的套接字将从哪个接口接收没有任何关系。

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. 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.

到目前为止,我尝试的是将套接字绑定到多播地址和端口,它的行为与上面提到的类似。我已经尝试绑定到接口地址,但这在Linux上不起作用(尽管在Windows上似乎是这样),您不会接收到套接字上的任何通信。最后,我尝试了绑定到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,因为我们用的是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. 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.

我认为我在这里试图达到的目标是不同的。套接字应该从相同的多播组和端口接收数据(在上面的示例中是224.2.2:5000),但是只能从一个特定的接口接收数据。换句话说,两个接口都从同一个多播组接收数据(但是不同的网络,所以数据是不同的),我需要每个套接字只监听一个接口。

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.

我认为这个问题是关于同一个端口上的多个组,而不是来自不同接口的同一个组。除非有什么我看不到的东西可以帮助我。

1 个解决方案

#1


2  

Yes, you can do what you want on Linux, without root privileges:

是的,你可以在Linux上做你想做的事,没有root特权:

Bind to INADDR_ANY and set the IP_PKTINFO socket option. You then have to use recvmsg() to receive your multicast UDP packets and to scan for the IP_PKTINFO control message. This gives you some side band information of the received UDP packet:

绑定到INADDR_ANY并设置IP_PKTINFO套接字选项。然后必须使用recvmsg()接收多播UDP数据包,并扫描IP_PKTINFO控制消息。这将为您提供一些接收到的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. (You can turn this into an interface name using if_indextoname() or the other way round with if_nametoindex().

ipi_ifindex是接收数据包的接口索引。(可以使用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. 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 (e.g. receiving unicast and multicast).

UDP套接字的IP地址的Linux bind()语义基本上是无用的。它本质上只是一个目标地址过滤器。您几乎总是希望为UDP套接字绑定到INADDR_ANY,因为您要么不关心发送的是哪个地址,要么希望接收多个地址的包(例如接收单播和多播)。

#1


2  

Yes, you can do what you want on Linux, without root privileges:

是的,你可以在Linux上做你想做的事,没有root特权:

Bind to INADDR_ANY and set the IP_PKTINFO socket option. You then have to use recvmsg() to receive your multicast UDP packets and to scan for the IP_PKTINFO control message. This gives you some side band information of the received UDP packet:

绑定到INADDR_ANY并设置IP_PKTINFO套接字选项。然后必须使用recvmsg()接收多播UDP数据包,并扫描IP_PKTINFO控制消息。这将为您提供一些接收到的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. (You can turn this into an interface name using if_indextoname() or the other way round with if_nametoindex().

ipi_ifindex是接收数据包的接口索引。(可以使用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. 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 (e.g. receiving unicast and multicast).

UDP套接字的IP地址的Linux bind()语义基本上是无用的。它本质上只是一个目标地址过滤器。您几乎总是希望为UDP套接字绑定到INADDR_ANY,因为您要么不关心发送的是哪个地址,要么希望接收多个地址的包(例如接收单播和多播)。