Subject: multiple multicast listeners
To: None <tech-net@netbsd.org>
From: David Arnold <arnold@dstc.monash.edu.au>
List: tech-net
Date: 05/09/2001 20:14:52
i'm seeing a problem with some multicast code, and was wondering if
anyone could donate me clue ...
i have a process, listening on a multicast group. if i start a second
instance of the same program on the same machine, it seems that
multicast packets sent to that group are only delivered to one of the
listeners.
i *think* packets are being delivered to the process that *last sent*
a packet to the group (the processes send a multicast request, and
watch for replies, also multicast).
i've attached a code snippet, but in summary:
- both REUSEADDR and REUSEPORT are set
- the socket is bound to the multicast address and required port
- the socket is joined to INADDR_ANY:port
- loopback is set on.
turning loopback off, binding to INADDR_ANY:port (instead of to the
required class D address), and joining using the ex0 interface's IP
not INADDR_ANY (in the ip_mreq) don't seem to change the result.
tcpdump shows the packets as expected. ifmcstat shows the group
membership with the correct ethernet MAC address, but the refcount
always seems to be 1 (this seemed odd?)
i'm using 1.5 GENERIC on i386, with a `3Com 3c905-TX 10/100 Ethernet
(rev. 0x0)'.
i'm not seeing this on Solaris, Linux, FreeBSD, etc. i do have a
report of similar behaviour on OpenBSD.
could anyone suggest what might be causing this?
thanks ...
d
(apologies if this is inappropriate, i scanned the archive: it looked ok?)
-8<----
struct sockaddr_in name;
struct hostent *host;
struct ip_mreq request;
int value;
u_char loop;
/* Construct the socket address */
memset(&ep->name, 0, sizeof(ep->name));
ep->name.sin_family = AF_INET;
ep->name.sin_port = htons(ep->port);
/* See if the host name is in dot notation */
if ((ep->name.sin_addr.s_addr = inet_addr(ep->hostname)) == (uint32_t)-1) {
/* Check with the name service */
if (! (host = gethostbyname(ep->hostname))) {
ELVIN_ERROR_UNIX_GETHOSTBYNAME_FAILED(error,
ELVIN_SOCK_H_ERRNO,
ep->hostname);
return 0;
}
ep->name.sin_addr = *(struct in_addr *)host->h_addr;
}
/* Create a socket */
if ((ep->fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
ELVIN_ERROR_UNIX_SOCKET_FAILED(error, ELVIN_SOCK_ERRNO);
return 0;
}
/* Allow other programs to bind to the socket */
value = 1;
if (setsockopt(ep->fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) < 0) {
ELVIN_ERROR_UNIX_SETSOCKOPT_FAILED(error, ELVIN_SOCK_ERRNO);
return 0;
}
#if defined(HAVE_SO_REUSEPORT)
/* Allow multiple sockets to bind to this interface:port for multicast */
value = 1;
if (setsockopt(ep->fd, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value)) < 0) {
ELVIN_ERROR_UNIX_SETSOCKOPT_FAILED(error, ELVIN_SOCK_ERRNO);
return 0;
}
#endif
#if defined(HAVE_BROKEN_MCAST_BIND)
/* For some systems (ie. Irix 6.x) binding the multicast address to
* the socket succeeds, but then the OS doesn't deliver any packets.
* So we simply bind to the default interface as a means of setting
* the port number, and rely on the marshalling code to discard junk
* packets. */
/* Initialize the INADDR_ANY address */
memset(&name, 0, sizeof(name));
name.sin_family = AF_INET;
name.sin_port = htons(ep->port);
name.sin_addr.s_addr = INADDR_ANY;
/* Bind socket to the default systems interface */
if (bind(ep->fd, (struct sockaddr *)&name, sizeof(name)) < 0) {
ELVIN_ERROR_UNIX_BIND_FAILED(error, ELVIN_SOCK_ERRNO);
return 0;
}
#else
/* Try to bind the multicast address as the socket's interface
* to prevent stray unicast packets from being delivered (we have to
* bind to set the port number). On some systems this won't work so
* we fall back to a destination of INADDR_ANY if the first bind fails. */
if (bind(ep->fd, (struct sockaddr *)&ep->name, sizeof(ep->name)) < 0) {
/* Initialize the INADDR_ANY address */
memset(&name, 0, sizeof(name));
name.sin_family = AF_INET;
name.sin_port = htons(ep->port);
name.sin_addr.s_addr = INADDR_ANY;
/* Try to bind again */
if (bind(ep->fd, (struct sockaddr *)&name, sizeof(name)) < 0) {
ELVIN_ERROR_UNIX_BIND_FAILED(error, ELVIN_SOCK_ERRNO);
return 0;
}
}
#endif
/* Join the multicast group */
request.imr_multiaddr.s_addr = ep->name.sin_addr.s_addr;
request.imr_interface.s_addr = INADDR_ANY;
if (setsockopt(ep->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(ELVIN_MCAST_ADDR_TYPE)&request, sizeof(request)) < 0) {
ELVIN_ERROR_UNIX_SETSOCKOPT_FAILED(error, ELVIN_SOCK_ERRNO);
return 0;
}
/* Set the socket to loop packets back to us */
loop = 1;
/* Fails on older WinSock implementations (so don't check result) */
setsockopt(ep->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
-8<----