1

I am using java.nio.channels.DatagramChannel to send and receive UDP multicast messages. The box, my program is running on can have multiple network interfaces.

I can specify network interface manually using socket option for outgoing datagrams:

NetworkInterface ni = NetworkInterface.getByName("eth0");
channel.setOption(StandardSocketOptions.IP_MULTICAST_IF, ni);

and passing network interface to join method for incomming datagrams:

MembershipKey key = channel.join(group, ni);

But I'd like my app to use the default interface based on routing tables. For outgoing data it is easy. I should not to specify IP_MULTICAST_IF or pass null as network interface. Java doc says the following:

"The initial/default value of this socket option may be null to indicate that outgoing interface will be selected by the operating system, typically based on the network routing tables."

But what is about incoming data. The method "join" always requires the network interface to be specified, and it does not allows me to pass null.

Ideally I'd like to join the multicast groups on exactly the same interface that is used for this multicast group for outgoing datagrams by default.

Is there any way to do it?

I am using Java 8 and Linux OS.

Sandro
  • 2,707
  • 2
  • 20
  • 30

2 Answers2

2

TL;DR I don't think it's possible to make it work the way you want it to. At least not in Java.

The thing about class D (multicast) addresses is that they are valid for pretty much any capable NIC. There is no coherence between sending and receiving, even for the same multicast group. I can send to 229.111.222.333 on one interface and join/listen to the same multicast group on another. Since UDP is session-less, this sort of setup is perfectly fine.

Even if the OS was able to select the proper interface, most of them can't and that's why you need to specify it. If the machine is acting as a multicast router (such as running mrouted) however, then that's an entirely different thing since its routing capabilities are typically controlled by /etc/mrouted.conf.

As an example, if I dump the routing table for my Windows box with two NICs (I know you are using Linux, but it won't typically show routing table entries for class D addresses. And I believe that's a hint ;-)), I got this (irrelevant entries omitted):

Network Destination        Netmask          Gateway       Interface  Metric
        224.0.0.0        240.0.0.0         On-link         127.0.0.1    131
        224.0.0.0        240.0.0.0         On-link       192.168.1.1    281
        224.0.0.0        240.0.0.0         On-link    172.89.123.123    281

As you can see, if I send or listen to, say, 224.123.123.123, the OS won't have a clue which interface to choose, they are all equally eligible. It might use the Metric value but in this case, using multicast on the loopback interface makes no sense. Class D addresses are odd birds, which is further supported by the "end address" of 240.0.0.0 above being labelled as Netmask, when it is clearly not.

"The initial/default value of this socket option may be null to indicate that outgoing interface will be selected by the operating system, typically based on the network routing tables."

I don't think that's an entirely accurate statement. Or it might be if you're on a multi-homed box and one of the interfaces has the MULTICAST flag set on it. But if you have several, then maybe you can use ip add route to add a specific route/NIC explicitly but I doubt it will work.

All multicast programming and commercial products I have done or used through the years over a multitude of operating systems, have all required me to specify which NIC I want the operation to be performed on.

Daniel
  • 4,033
  • 4
  • 24
  • 33
  • Not sure about Windows and other OS, but linux allows us to pass INADDR_ANY, when you joining mutlicast group. When I implemented this functionality on C it works perfectly. Old java.net.MulticastSocket has method join with one argument, I did not try it but it seems it should work correctly too. The most OS allow us to use multiple phisical and logical network interfaces and configure the default routes for different addresses. – Sandro Jan 14 '20 at 15:06
2

There isn't any explicit support in the DatagramChannel API for joining a multicast group on an interface chosen by the system but you can workaround it by using MulticastSocket::getNetworkInterface to obtain the placeholder NetworkInterface. So this should do what you want:

NetworkInterface ni;
try (MulticastSocket s = new MulticastSocket()) {
    ni = s.getNetworkInterface();
}

MembershipKey key = channel.group(join, ni);

The MulticastSocket is created solely to call the getNetworkInterface.

Alan Bateman
  • 5,283
  • 1
  • 20
  • 25
  • Great stuff. This might actually be the closest thing. I could not get this approach to work reliably several years ago for machines with many interfaces, but maybe it works better now. The only problem is that no group has been specified (yet) for `MulticastSocket`, so datagrams might ends up on an unintended network. – Daniel Jan 14 '20 at 16:14
  • This solution looks strange. But It works!!! Thank you very much!!! – Sandro Jan 14 '20 at 17:05
  • @Daniel we don't need to specify group for MulticastSocket, Multicast socket is automaticaly closed after try block is finished, We use it only to get the NetworkInterface object instance fo INADDR_ANY or 0.0.0.0... – Sandro Jan 14 '20 at 17:15