Receiving Multicast on a Server with Multiple Interfaces (Linux)

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



Leave a reply



Submit