Picking the right interface for multicast communications
At the recent hardware meetup, I was faced with an interesting problem: I was trying to communicate with my Wemos over multicast UDP in order to get it to automatically discover my PixelHub Server, but the multicast pings were going out over the wrong interface - I had both an ethernet cable plugged in and a WiFi hotspot running on my integrated wireless card.
The solution to this is not as simple you might think - you have to not only pick the right interface, but also the right version of the IP protocol. You also have to have some way to picking the correct interface in the first place.
Let's say you have a big beefy PC with a wireless card and 2 ethernet ports that are (for some magical reason) all in use at the same time, and you want to communicate with another device over your wireless card and not either of your ethernet ports.
I developed this on my linux laptop, but it should work just fine on other OSes.
To start, it's probably a good idea to list all of our network interfaces:
using System.Net.NetworkInformation;
// ...
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface nic in nics)
{
Console.WriteLine("Id: {0} - Description: {1}", nic.Id, nic.Description);
}
This (on my machine at least!) outputs something like this:
eth0 - eth0
lo - lo
wlan0 - wlan0
Your machine will probably output something different. Next, since you can't normally address this list of network interfaces directly by name, we need to write a method to do it for us:
public static NetworkInterface GetNetworkIndexByName4(string targetInterfaceName)
{
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface nic in nics)
{
if (nic.Id == targetInterfaceName)
return nic;
}
throw new Exception($"Error: Can't find network interface with the name {targetInterfaceName}.");
}
Pretty simple, right? We're not out the woods yet though - next we need to tell our UdpClient to talk on a specific network interface. Speaking of which, let's set up that UdpClient so that we can use it to do stuff with multicast:
using System.Net;
// ...
UdpClient client = new UdpClient(5050);
client.JoinMulticastGroup(IPAddress.Parse("239.62.148.30"));
With that out of the way, we can now deal with telling the UcpClient which network interface it should be talking on. This is actually quite tricky, since the UdpClient doesn't take a NetworkInterface directly. Let's define another helper method:
public static int GetIPv4Index(this NetworkInterface nic)
{
IPInterfaceProperties ipProps = nic.GetIPProperties();
IPv4InterfaceProperties ip4Props = ipProps.GetIPv4Properties();
return ip4Props.Index;
}
The above extension method gets the index of the IPv4 interface of the network interface. Since at the moment we are in the middle of a (frustratingly slow) transition from IPv4 and IPv6, each network interface must have both an IPv4 interface, for talking to other IPv4 hosts, and an IPv6 interface for talking to IPv6 hosts. In this example I'm using IPv4, since the Wemos I want to talk to doesn't support IPv6 :-(
Now that we have a way to get the index of a network interface, we need to translate it into something that the UdpClient understands:
int interfaceIndex = (int)IPAddress.HostToNetworkOrder(NetTools.GetNetworkIndexByName4(NetworkInterfaceName));
That complicated! Thankfully, we don't need to pick it apart completely - it just works :-)
Now that we have the interface index in the right format, all we have to do is tell the UdpClient about it. Again, this is also slightly overcomplicated:
beacon.Client.SetSocketOption(
SocketOptionLevel.IP,
SocketOptionName.MulticastInterface,
interfaceIndex
);
Make sure that you put this call before you join the multicast group. With that done, your UdpClient should finally be talking on the right interface!
Whew! That was quite the rabbit hole (I sent my regards to the rabbit :P). If you have any issues with getting it to work, I'm happy to help - just post a comment down below.