Search code examples
c++cnetwork-programmingmulticast

C++ receiving multicast on particular interface


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");

Solution

  • 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");