csocketswindows-subsystem-for-linuxmulticast

Multicast works on WSL1, but not WSL2


I have a small app that is running two threads.

One thread sends a UDP multicast packet (to group 239.0.0.1), and the other reads that same multicast packet.

When I run this on app on Windows using Visual Studio, it works - the packet is successfully sent and received. I see it on Wireshark. I also have an embedded target where this app ultimately runs, running VxWorks RTOS, and it works on VxWorks as well.

I tried it on Linux using WSL2. The sendto() is successful, but the recvfrom() fails. However I do see the packet on Wireshark, and there are no errors reported on Wireshark.

I downgraded to WSL1 and it works.

Is there something specific to WSL2 that might cause multicast to fail, but work on WSL1, Windows and VxWorks? If I switch to unicast, it works on WSL2.

Here are some code snippets:

Receive thread:

uint32_t  ip_addr_mgrp;
uint32_t  ip_addr_mifc;
inet_pton(AF_INET, (char*)"172.18.19.53", &ip_addr_mifc);
inet_pton(AF_INET, (char*)"239.0.0.1", &ip_addr_mgrp);

int fd = socket(AF_INET, SOCK_DGRAM, SOCKET_PROTOCOL);

struct sockaddr_in server_addr;

server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = ip_addr_mifc;
server_addr.sin_port = htons(45007);

bind(fd, (struct sockaddr*)&server_addr, sizeof(server_addr));

struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = ip_addr_mgrp;
mreq.imr_interface.s_addr = ip_addr_mifc;

setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq));

// code to set to non-blocking

// Loop for reading

// Call select()
// Call FD_ISSET()
// Call recvfrom()

TX thread:

uint32_t  ip_addr_mgrp;
uint32_t  ip_addr_mifc;
inet_pton(AF_INET, (char*)"172.18.19.53", &ip_addr_mifc);
inet_pton(AF_INET, (char*)"239.0.0.1", &ip_addr_mgrp);

int fd = socket(AF_INET, SOCK_DGRAM, SOCKET_PROTOCOL);

struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = ip_addr_mgrp;
mreq.imr_interface.s_addr = ip_addr_mifc;

setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq));

setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&mreq.imr_interface.s_addr, sizeof(struct in_addr));

char loop = 1;
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (char*)&loop, sizeof(loop));

// code to set to non-blocking

// Loop for sending

// Call sendto() to 237.0.0.1:45007

Solution

  • On Linux, if a socket is bound to a local IP address, it won't be able to receive multicast traffic even if the multicast group is joined.

    As mentioned in the comments, WSL1 is an API layer over the Windows syscalls while WSL2 is a full Linux virtual machine. This is why binding to a local interface works on WSL1 but not on WSL2.

    You can fix this by either binding the socket to INADDR_ANY or to the multicast address in question (239.0.0.1 in this case).