C listen to multicast on all interfaces, respond on same as recieved
Initially, I accepted @dbush answer as it allowed me to get on right track. For sake of completeness I post more detailed answer and as suggested by him I accepted my own answer.
Some of the code was found here: Setting the source IP for a UDP socket
I was able to do all of this with single socket and setting IP_PKTINFO.
Code samples (simplified, without error handling and stuff):
Creating socket:
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1) {
perror("socket");
exit(1);
}
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) < 0) {
perror("setsockopt");
exit(1);
}
optval2 = 1;
if(setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &optval2, sizeof(optval2)) < 0) {
perror("setsockopt");
exit(1);
}
memset(&my_addr, 0, sizeof(struct sockaddr_in));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY;
my_addr.sin_port = htons(5353);
if (bind(sockfd, (struct sockaddr *)&my_addr,
sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(1);
}
Receiving and responding:
char buf[MAXBUFLEN];
char cmsgbuf[MAXBUFLEN];
struct iovec iov[1];
iov[0].iov_base=buf;
iov[0].iov_len=sizeof(buf);
struct cmsghdr *cmsg;
struct msghdr message;
message.msg_name=&their_addr;
message.msg_namelen=sizeof(their_addr);
message.msg_iov=iov;
message.msg_iovlen=1;
message.msg_control=cmsgbuf;
message.msg_controllen=MAXBUFLEN;
if ((numbytes = recvmsg(sockfd, &message, 0)) == -1) {
perror("recvfrom");
exit(1);
}
for (cmsg = CMSG_FIRSTHDR(&message); cmsg != NULL; cmsg = CMSG_NXTHDR(&message, 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 = CMSG_DATA(cmsg);
addr = pi->ipi_spec_dst.s_addr;
}
//DO SOME MAGIC
//HERE WE ARE SETTING ADDR - INTERFACE WITH THIS ADDR WILL SEND MULTICAST
sock_opt_addr.s_addr = addr;
setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &sock_opt_addr, sizeof(sock_opt_addr));
sendto(sockfd, buffer, len, 0, (struct sockaddr *)&destination, sizeof destination);
How to Multicast (send) to first NIC?
Apparently setsockopt
and IP_MULTICAST_IF
don't work if wsock32.dll
is used instead of ws2_32.dll
. I thought I was doing it wrong when I kept getting 1.0.0.0
as the IP address even when it was something else that I've set with setsockopt. Funny thing is, before the call to IP_MULTICAST_IF
, it would return 0.0.0.0
, so setsockopt` did change something, just not correctly.
Someone else who had this same problem way back in 2004 - http://us.generation-nt.com/ip-multicast-problem-help-37595922.html. When we #include "winsock2.h"
we need to use ws2_32.dll
. However, with C++ Builder, it's impossible to use ws2_32.dll
when we use winsock2.h
- the RTL implicitly links in wsock32.dll and you can't link ws2_32.dll even if you explicitly specify #pragma comment(lib, "ws2_32.lib")
. Embarcadero really need to fix this! Someone in the RTL team must've decided it's clever to implicitly include wsock32.dll. The only 'clever' thing it did was users didn't have to include one line in their code - #pragma comment(lib, "wsock32.lib")
. While they're at that, they might as well include every single DLL files known to mankind.
Send UDP datagrams to multiple interfaces with multicast address
Packets will only ever go out one interface, whether they are unicast, multicast, or broadcast.
The correct way to handle this is as you've discovered, namely to set the IP_MULTICAST_IF
option on the socket before sending the packet.
Send packets from a specific interface in c
If the point of passing in the sockaddr to bind is to specify where to
bind the socket locally on your machine, could you just pass in the IP
address of a specific interface to bind your socket to it?
Binding to an address (via bind()
) and binding to an interface (via setsockopt()
) serve different, but overlapping purposes. Usually bind()
is what you want.
In particular, your question seems to assume that there is a 1:1 mapping between addresses and interfaces, but that it is not a safe assumption. One interface can have multiple addresses, and, at least in principle, one address can be served by multiple interfaces.
Related Topics
Gfortran: Compiling 32-Bit Executable in 64-Bit System
How to Link a Linux's Thread Tid and a Pthread_T "Thread Id"
How to Get Opencl Working on an Ati Radeon Card
Wmctrl: Moving a Fullscreen Window
U-Boot: Cannot Boot Linux Kernel Despite Kernel Being Less Than Maximum Bootm_Len
Page Fault in Interrupt Context
How Make /Var/Www Contents Editable by Ide
List of Available Wireless Networks with Golang (Under Linux)
Alpine Linux: Pmap Output Not Showing Rss
PHPmyadmin, Neginx Error.Log - Check Group Www-Data Has Read Access and Open_Basedir
Mount -T Cifs Works on One Version of Linux But Not Another