Reputation: 26680
My Winsock Delphi application should listen on all network interfaces for multicast UDP/IP stream. It listened normally until I tried it on another PC with different network adapters' priority order.
Then I started to research problem and found on some forums that INADDR_ANY
(or 0.0.0.0
) has different meaning in Windows and Linux:
0.0.0.1
for second one). Citation: "If this member specifies an IPv4 address of 0.0.0.0, the default IPv4 multicast interface is used" - without mentioning whether it is for listening or for sending.Could you confirm or deny this?
How to listen really on all interfaces?
Here is a little piece of my code:
TMulticastListener = class(TThread)
private
mreq: ip_mreq;
............
end;
constructor TMulticastListener.Create;
var err: Integer;
wData: WsaData;
reuse: Integer;
begin
inherited Create(true);
err := WSAStartup(MAKEWORD(2, 2), wData);
if err = SOCKET_ERROR then begin
// Tell the user that we could not find a usable Winsock DLL
perror('WSAStartup');
Exit;
end;
// create what looks like an ordinary UDP socket
fd := socket(AF_INET, SOCK_DGRAM, 0);
if fd = INVALID_SOCKET then begin
perror('socket');
Exit;
end;
reuse := 1;
// allow multiple sockets to use the same PORT number
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, Pointer(@reuse), SizeOf(reuse)) < 0) then begin
perror('Reusing ADDR failed');
Exit;
end;
// set up destination address
FillChar(addr, sizeof(addr), 0);
addr.sin_family := AF_INET;
addr.sin_addr.s_addr := htonl(INADDR_ANY); // N.B.: differs from sender
addr.sin_port := htons(HELLO_PORT);
// bind to receive address
if (bind(fd, addr, sizeof(addr)) < 0) then begin
perror('bind');
Exit;
end;
// use setsockopt() to request that the kernel join a multicast group
mreq.imr_multiaddr.s_addr := inet_addr(HELLO_GROUP);
mreq.imr_interface.s_addr := htonl(INADDR_ANY); //inet_addr('0.0.0.0');
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, @mreq, sizeof(mreq)) < 0) then begin
perror('setsockopt');
Exit;
end;
end;
Upvotes: 6
Views: 2580
Reputation: 224457
Windows and Linux actually behave the same regarding the use of INADDR_ANY
. The confusion here is because the two links you provide are being used in different contexts.
When using the bind
function to bind to an address/port, specifying INADDR_ANY
means that the socket will be able to receive packets on the given port from any interface. However, doing so does not set up anything regarding multicast.
In the context of the IP_ADD_MEMBERSHIP
call to setsockopt
, setting the interface to INADDR_ANY
will have the system join the given multicast group on the default network interface.
The Linux link you gave refers to bind
, while the Windows link refers to setsockopt
and IP_ADD_MEMBERSHIP
.
If you want to join the multicast group on all interfaces, you need to retrieve the list of interfaces on the system and join each one. On Windows, the GetAdaptersAddresses()
function will give you the list of interfaces.
On Linux, use the getifaddrs()
function.
Here's an example of how to use the GetAdaptersAddresses()
function in C:
struct iflist {
char name[50];
struct sockaddr_in sin;
int isloopback;
int ismulti;
int ifidx;
};
void getiflist(struct iflist *list, int *len)
{
IP_ADAPTER_ADDRESSES *head, *curr;
IP_ADAPTER_UNICAST_ADDRESS *uni;
char *buf;
int buflen, err, i;
buflen = 100000;
buf = calloc(buflen, 1);
head = (IP_ADAPTER_ADDRESSES *)buf;
if ((err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, head,
&buflen)) != ERROR_SUCCESS) {
char errbuf[300];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
0, errbuf, sizeof(errbuf), NULL);
printf("GetAdaptersAddresses failed: (%d) %s", err, errbuf);
free(buf);
return;
}
for (*len = 0, curr = head; curr; curr = curr->Next) {
if (curr->IfType == IF_TYPE_TUNNEL) continue;
for (uni = curr->FirstUnicastAddress; uni; uni = uni->Next) {
if (curr->OperStatus == IfOperStatusUp) {
memset(&list[*len], 0, sizeof(struct iflist));
strncpy(list[*len].name, (char *)curr->AdapterName,
sizeof(list[i].name) - 1);
memcpy(&list[*len].sin, uni->Address.lpSockaddr,
uni->Address.iSockaddrLength);
list[*len].isloopback =
(curr->IfType == IF_TYPE_SOFTWARE_LOOPBACK);
list[*len].ismulti =
((curr->Flags & IP_ADAPTER_NO_MULTICAST) == 0);
if (uni->Address.lpSockaddr->sa_family == AF_INET6) {
list[*len].ifidx = curr->Ipv6IfIndex;
} else {
list[*len].ifidx = curr->IfIndex;
}
(*len)++;
}
}
}
free(buf);
}
Upvotes: 6
Reputation: 1911
your source is completely oblivious of the fact that the internet protocol itself does not know anything about "ports" and "interfaces", the aforementioned statement ("listen on all interfaces") doesnt even make any sense, its completely made up but packets to broadcast addresses typically are routed onto multiple interfaces, more on this below:
0.0.0.0
is a special, reserved IPv4 address called "network identifier" - in fact IPv4 adresses which end with 0
typically are reserved - it is typically not usable except for broadcast and network purposes.
Operating systems usually reserve 0.0.0.0
for broadcasts within one single transport protocol
Now : these broadcast addresses always receive broadcasts for one single transport protocol via the default route which may point to multiple (or all) network interfaces. What you probably were reading about is something completely different : Multicast - thats yet another can of worms, it is possible to send singular packets to multiple, designated receivers - Microsoft Windows has a default multicast route and Linux typically has to be configured for multicast in order to work (AFAIK) - but you dont want that.
Conclusion : for your purposes, 0.0.0.0
is identical on Windows and Linux - its a broadcast address for your chosen transport protocol, there is no difference
Upvotes: -1