Reputation: 73
Docs on IP_ADD_MEMBERSHIP says:
IP_ADD_MEMBERSHIP (since Linux 1.2) Join a multicast group. Argument is an ip_mreqn structure.
struct ip_mreqn { struct in_addr imr_multiaddr; /* IP multicast group address */ struct in_addr imr_address; /* IP address of local interface */ int imr_ifindex; /* interface index */ }; imr_multiaddr contains the address of the multicast group the appli‐ cation wants to join or leave. It must be a valid multicast address (or setsockopt(2) fails with the error EINVAL). imr_address is the address of the local interface with which the system should join the multicast group; if it is equal to INADDR_ANY, an appropriate inter‐ face is chosen by the system. imr_ifindex is the interface index of the interface that should join/leave the imr_multiaddr group, or 0 to indicate any interface.
So I have an interface "eth0" with ip 192.168.1.5. I want to join this interface to multicast group 225.1.1.1. I'm a little bit confused on how to setup ip_mreqn structure properly? I found 2 possible ways:
1.
ip_mreqn group;
group.imr_multiaddr.s_addr = inet_addr("225.1.1.1");
group.imr_address.s_addr = inet_addr("192.168.1.5");
group.imr_ifindex = 0;
2.
ip_mreqn group;
group.imr_multiaddr.s_addr = inet_addr("225.1.1.1");
group.imr_address.s_addr = htonl(INADDR_ANY);
group.imr_ifindex = if_nametoindex("eth0");
And 3rd way is to use SO_BINDTODEVICE socket option.
My questions are.
1) What is correct way to join particular interface to multicast group?
2) What is functional difference between imr_address and imr_ifindex?
3) How option SO_BINDTODEVICE could be useful?
EDIT: I did some research.
Suppose that I have two network interfaces: eth0 with ip 192.168.1.5 and eth1 with ip 192.168.1.255 and I receive multicast on eth0 with ip 192.168.1.5.
These ways work properly (I get multicast messages on eth0):
group.imr_address.s_addr = inet_addr("192.168.1.5");
group.imr_ifindex = 0;
or
group.imr_address.s_addr = htonl(INADDR_ANY);
group.imr_ifindex = if_nametoindex("eth0");
or obviously
group.imr_address.s_addr = inet_addr("192.168.1.5");
group.imr_ifindex = if_nametoindex("eth0");
and even
group.imr_address.s_addr = inet_addr("192.168.1.255");
group.imr_ifindex = if_nametoindex("eth0");
And these ways do not (I don't get multicast messages on eth0):
group.imr_address.s_addr = inet_addr("192.168.1.5");
group.imr_ifindex = if_nametoindex("eth1");
and
group.imr_address.s_addr = inet_addr("192.168.1.255");
group.imr_ifindex = if_nametoindex("eth1");
Upvotes: 3
Views: 3726
Reputation: 53
ip_mreqn
is used to find network interface devices in the kernel source code at linux/net/ipv4/igmp.c
(linux 5.13), as follow.
static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr)
{
struct net_device *dev = NULL;
struct in_device *idev = NULL;
if (imr->imr_ifindex) {
idev = inetdev_by_index(net, imr->imr_ifindex);
return idev;
}
if (imr->imr_address.s_addr) {
dev = __ip_dev_find(net, imr->imr_address.s_addr, false);
if (!dev)
return NULL;
}
if (!dev) {
struct rtable *rt = ip_route_output(net,
imr->imr_multiaddr.s_addr,
0, 0, 0);
if (!IS_ERR(rt)) {
dev = rt->dst.dev;
ip_rt_put(rt);
}
}
if (dev) {
imr->imr_ifindex = dev->ifindex;
idev = __in_dev_get_rtnl(dev);
}
return idev;
}
imr->imr_ifindex
is first used to search for the device. If NOT found, imr->imr_address
is used.
Upvotes: 1
Reputation: 225344
I've always used the older struct ip_mreq
instead of struct ip_mreqn
, as both are supported. This struct doesn't have the index field, so it's less ambiguous as to what you need to set.
struct ip_mreq
{
/* IP multicast address of group. */
struct in_addr imr_multiaddr;
/* Local IP address of interface. */
struct in_addr imr_interface;
};
You would then set it like this:
struct ip_mreq group;
group.imr_multiaddr.s_addr = inet_addr("225.1.1.1");
group.imr_interface.s_addr = inet_addr("192.168.1.5");
Upvotes: 1