I am writing a C program which multicasts a packet. The same socket used for multicasting is subscribed to the multicast group on all interfaces. This would mean that the sender would receive is own packet back. I expect to the socket to receive the message twice, once on loopback interface and another on eth0 interface. Here is the code for the same.
#define MULTICAST_PORT 1112
#define MULTICAST_GROUP "239.0.0.1"
int create_multi_receiver()
{
// Send is also through this socket
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in in_servaddr;
struct ip_mreq mreq;
// Bind UDP server
bzero(&in_servaddr, sizeof(in_servaddr));
in_servaddr.sin_family = AF_INET;
in_servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
in_servaddr.sin_port = htons(MULTICAST_PORT);
if (bind(fd, (struct sockaddr *)&in_servaddr, sizeof(in_servaddr)) == -1)
error_exit("bind error in UDP");
mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_GROUP);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
error_exit("setsockopt");
int opt = 1;
if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) < 0)
error_exit("setsockfd");
return fd;
}
struct msg *recv_multi_msg(int udpfd)
{
struct msg nmb_msg; /* space to msg into */
struct iovec vector[1]; /* file name from the child */
struct msghdr msg; /* full message */
struct cmsghdr *cmsg; /* control message with the fd */
/* set up the iovec for the file name */
vector[0].iov_base = &nmb_msg;
vector[0].iov_len = sizeof(nmb_msg);
/* the message we're expecting to receive */
memset(&msg, 0, sizeof(msg));
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = vector;
msg.msg_iovlen = 1;
/* overprovisioning buffer for now */
char cmbuf[128];
msg.msg_control = cmbuf;
msg.msg_controllen = sizeof(cmbuf);
printf("Receiving message..\n");
if (recvmsg(udpfd, &msg, 0) == -1)
{
perror("recvmsg multireceiver");
return NULL;
}
printf("received message..\n");
// TODO: Test this part, very high chance of error
for ( // iterate through all the control headers
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg))
{
// ignore the control headers that don't match what we want
if (cmsg->cmsg_level != IPPROTO_IP ||
cmsg->cmsg_type != IP_PKTINFO)
{
continue;
}
struct in_pktinfo pi;
memcpy(&pi, CMSG_DATA(cmsg), sizeof(pi));
printf("Destination IP: %s\n", inet_ntoa(pi.ipi_spec_dst));
// in_addr_t ip = (nmb_msg.mtype) >> 16;
// if (pi.ipi_spec_dst.s_addr != ip)
// return NULL;
// break;
}
return NULL;
// Do some stuff later
}
void send_multi_msg(int udpfd, int udsfd)
{
struct msg msg;
if (recv(udsfd, &msg, sizeof(msg), 0) > 0)
{
struct sockaddr_in addr;
int alen;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(MULTICAST_GROUP);
addr.sin_port = htons(MULTICAST_PORT);
alen = sizeof(addr);
printf("sending %s\n", msg.mtext);
if (sendto(udpfd, &msg, sizeof(msg), 0, (struct sockaddr *)&addr, alen) == -1)
perror("sendto in multicast");
}
}
However, I am receiving the packet only eth0. Why is this so? IP_MULTICAST_LOOP
is enabled by default.
I have tried checking StackOverflow but none of the questions have been answered satisfactorily.
When you send a multicast datagram with sendto
, it only sends the packet out on a single interface. So you only receive one packet because you're only sending one packet.
By default, the interface that sendto
will use for multicast will typically be the first non-loopback network interface on the system. You can control which interface is used by setting the IP_MULTICAST_IF
option on the sending socket. So if you want to send on multiple interfaces you'll need to call sendto
multiple times and set IP_MULTICAST_IF
before each send.
You've also used INADDR_ANY
as the interface to join the multicast group on. That will also typically be the first non-loopback interface. If you run your program and then while it's still up run netstat -ng
you can see which interface was joined.
If you want to listen for multicast on multiple interfaces, you need to set the IP_ADD_MEMBERSHIP
option for each interface.