receiving multicast on a server with multiple interfaces (linux)
You appear to be being stung by rp_filter
reverse-path filtering. This drops packets if they arrive on an interface that doesn't have a route for the source address.
You can disable it on a per-interface basis with the sysctl /proc/sys/net/ipv4/conf/<if>/rp_filter
.
How to receive multicast data on a multihomed server's non-default interface
caf's comment that this is a duplicate of receiving multicast on a server with multiple interfaces (linux) answered this! (And I post this as an answer for clarity.) Namely, an echo 0 > /proc/sys/net/ipv4/conf/eth1/rp_filter
resolves my issue.
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);
Multicast route to all interfaces
You can use route to add mutlicast routes to multiple devices.
sudo route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0
sudo route add -net 224.0.0.0 netmask 240.0.0.0 dev wlan0
This was reflected in the routing table and allowed our services operating on the separate NICs to work.
However, its not clear to me that you should do this.
The reason I am not sure if the is "valid" is that I tried to port from using route which is apparently deprecated/obsolete to using "ip route", eg
ip route add 224.0.0.0/4 dev eth0
ip route add 224.0.0.0/4 dev wlan0
However ip route won't let you add the second route. I have asked about how to properly use ip route without having to use explicit multicast group addresses, but if you are comfortable using route, you should be able to add the route to each device you need to use.
Receiving different multicast with same source address on different interfaces
The IP_MULTICAST_IF ioctl does not control incoming selection, it is setting the socket's default interface for its outgoing multicast packets.
IP_ADD_MEMBERSHIP is the only mechanism for configuring incoming multicast and the memberships it creates are for the entire host, the host does not tailor delivery to individual sockets based on their requested memberships. (You can observe the hosts memberships with netstat -gn
and the reference count is being used to determine when the host can stop observing, but not which sockets are receiving fan-out. If you have a matching membership from any socket, all sockets that made an applicable bind(2)
will start receiving that multicast, even if they have never used IP_ADD_MEMBERSHIP
.)
The usual method to differentiate these packets with out changing the system setup, is to receive them all on a socket using ancillary data to identify their interface. On Linux, this ancillary data setup is done with IP_PKTINFO
as described in ip(7)
.
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.
Can I (IGMP) join a stream on two NICs and answer (IGMP) queries on both NICs in Linux?
It is explained as clear as day here:
https://access.redhat.com/solutions/53031
And less clearly here:
https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
Considering a computer with 2 net interfaces, interfaceA and interfaceB.
Considering that Linux decides to use interfaceB to send packets to ip address X.
Considering a packet that is received on interfaceA from ip address X.
Linux will drop the packet.
Unless you run sysctl net.ipv4.conf.all.rp_filter=2
in a terminal or add that line to /etc/sysctl.conf
.
It enables receiving packets from an ip address on other interfaces than the one it uses to send packets to that ip address!
Related Topics
Git: Can't Push (Strange Config Issue)
Apache Httpd VS. Tomcat 7: Port 80 VS. Port 8080
Load Warning: Cannot Find Entry Symbol _Start
What Is Eof!! in The Bash Script
Finding Threading Bottlenecks and Optimizing for Wall-Time with Perf
How to Get Yesterday and Day Before Yesterday in Linux
How to Disable Non-Breaking Space with Altgr+Space
How to Connect to Amazon Linux Instance Using Remote Desktop from Windows 7
How to Send Signal from One Program to Another
Shared Volume in Docker Through Vagrant
Install Library in Home Directory
Checking If a Binary Compiled with "-Static"
Sudo User Not Using Same Node Version
What Is The Fastest Way to Display an Image in Qt on X11 Without Opengl
How to Measure Mispredictions for a Single Branch on Linux
Using "And" in Bash While Loop
Can Multiple .Gz Files Be Combined Such That They Extract into a Single File