Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  1. socket0 receives UDP traffic sent to 224.2.2.2:5000 on interface eth0
  2. socket1 receives UDP traffic sent to 224.2.2.2:5000 on interface eth1

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.

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.

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.

I cannot use SO_BINDTODEVICE since it requires root privileges.

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.

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.

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.

like image 515
sycc90 Avatar asked Aug 28 '17 15:08

sycc90


People also ask

How do multicast sockets work?

Each multicast IP maps to a multicast ethernet address. When you use a socket to send to a specific multicast IP, the destination MAC address on the ethernet frame is set to the corresponding multicast MAC address for the multicast IP.

How do I get multicast packets?

Note: multicast uses UDP and are sent through switches and hubs. To receive a multicast message a host must be configured to receive on that multicast address. All hosts that are configured to receive packets on a particular address are part of a multicast group.

Does multicast use TCP or UDP?

Unicast uses TCP (Transmission Control Protocol) for communications while multicast communication uses UDP (User Datagram Protocol).

How do I join a multicast group?

To join a multicast group, a host sends a join message, using the Internet Group Management Protocol (IGMP), to its first-hop router. Groups are identified by a single class D IP address (in the range 224.0. 0.0 to 239.255. 255.255).


1 Answers

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

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:

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

As you said on Windows the same network functions have different semantics, especially for UDP and even more for multicast.

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

like image 151
Johannes Overmann Avatar answered Sep 27 '22 19:09

Johannes Overmann