What is the right behavior of IP Multicast over UDP?
Imagine yourself following scenario:
When it is done in Windows, with Winsock, each socket becomes data only when both conditions are met:
Here is some Delphi code for that:
procedure Connect();
var err: Integer;
wData: WsaData;
reuse: Integer;
begin
FillChar(wData, SizeOf(wData), 0);
err := WSAStartup(MAKEWORD(2, 2), wData);
if err = SOCKET_ERROR then Exit;
_fd := socket(AF_INET, SOCK_DGRAM, 0);
if _fd = INVALID_SOCKET then Exit;
reuse := 1;
if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, Pointer(@reuse), SizeOf(reuse)) < 0) then Exit;
FillChar(_addr, sizeof(_addr), 0);
_addr.sin_family := AF_INET;
if (_listeningInterface = '0.0.0.0') or not _isIpAddress(_listeningInterface) then
_addr.sin_addr.s_addr := htonl(INADDR_ANY)
else
_addr.sin_addr.s_addr := inet_addr(PAnsiChar(_listeningInterface));
_addr.sin_port := htons(_port);
if (bind(_fd, _addr, sizeof(_addr)) < 0) then Exit;
if _isMulticast() then begin
_mreq.imr_multiaddr.s_addr := inet_addr(PAnsiChar(_multicastGroup));
if (_listeningInterface = '0.0.0.0') or not _isIpAddress(_listeningInterface) then
_mreq.imr_interface.s_addr := htonl(INADDR_ANY)
else
_mreq.imr_interface.s_addr := inet_addr(PAnsiChar(_listeningInterface));
// Joinig multicast group here. Note the _fd variable usage here.
if (setsockopt(_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, @_mreq, sizeof(_mreq)) < 0) then Exit;
end;
end;
When we try to do it from Keil library for microcontrollers, there is no function like setsockopt
for that. There is igmp_join
function that doesn't accept socket variable, but multicast group only.
As a result, we cannot specify correspondence of an UDP port and multicast group, and each UDP socket becomes data for all groups we joined to.
Which of two approaches is the right one: Keil's or Windows' ?
Both behaviors are correct.
Which UDP packets you receive on which socket is beyond the scope of the definition of IPv4 multicast.
The expectation that you only receive the traffic you joined the group for is often violated. Also, all operating systems implement this differently. Linux and Windows behave differently, and the small IP stacks in microcontrollers usually have even simpler behavior.
The baseline, the worst case you can expect is: You will receive random UDP packets on your UDP socket, even from groups you did not join. But it is usually not that bad in practice. Just as a guideline when thinking about it. The reason is: The OS may delay membership drops. The local routers will delay membership drops. The local routers will contain bugs.
You need to deal with receiving unexpected UDP packets anyway, since anyone in your network can forge UDP packets and send them to your socket in somebody elses name. Also anybody else can forge IGMP packets and "join you" to any multicast groups.
This means: In practice you always need to do application-level filtering and silently ignore unexpected UDP packets. On most IP stacks there is a (non-trivial) option to determine the destination address of a received UDP packet. This is most useful for splitting out the traffic for multiple multicast groups.
And since you now know the behavior of your IP stack you can for example save some memory and just use a single UDP socket to receive all UDP traffic and de-multiplex it on the application side. It depends also a bit on your application of course.
But yes, I understand your disappointment about IP multicast being so under-defined. I feel with you! :-)