20

I am trying to modify a multicast listener / sender example to bind the UDP / multicast socket to a specific interface and not using the INADDR_ANY macro.

I possess the IPv4 address of the interface. I tried the following, but the socket does not receive any UDP (unicast, broadcast, multicast) packets.

struct sockaddr_in addr;
int fd, nbytes;
socklen_t  addrlen;
struct ip_mreq mreq;

// my_ipv4Addr equals current IP as String, e.g. "89.89.89.89"

// create what looks like an ordinary UDP socket */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("socket");
    exit(1);
}

// set up addresses
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
// [-]    addr.sin_addr.s_addr = htonl(INADDR_ANY); 
addr.sin_addr.s_addr = inet_addr(my_ipv4Addr); 
addr.sin_port = htons(port);

// bind socket
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
    perror("bind");
    exit(1);
}

// use setsockopt() to request that the kernel join a multicast group
mreq.imr_multiaddr.s_addr = inet_addr(group);
// [-]    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
mreq.imr_interface.s_addr = inet_addr(my_ipv4Addr);
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))< 0) {
    perror("setsockopt");
    exit(1);
}

Edit:

Let me explain the purpose of my program. I am writing a little tool, which will check, if a network supports broadcast/multicast. Therefore I own a system with two interfaces and send via Interface1 a multicast Packet and try to receive it with Interface2. But: The packet shall go through the network, not the loopack device.

The idea is to block multicast-loopback on thread1/interface1 with:

u_char loop = 0;
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));

And to listen on thread2/interface 2 interface-specific. Tcpdump shows, that the packets are arriving, but are dropped with my config above.

user22602
  • 309
  • 1
  • 2
  • 4
  • 3
    I think you will have to bind not to an IP but to a device with e.g. `setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 4)` – Gene Oct 01 '12 at 21:50
  • What happens if you let the kernel choose an interface for you? i.e. using `mreq.imr_interface.s_addr = htonl(INADDR_ANY);` – shinkou Oct 02 '12 at 00:44
  • @Gene Correct. Using `setsockopt( ... SO_BINDTODEVICE ...)` I can bind the UDP socket to an interface, but how do I explicitly do this for multicast? @shinkou Setting to INADDR_ANY (UDP and Multicast subscription) does not change the fact, that the socket does not receive packets. Packets were dropped as martian (seen in /var/log/syslog), until I set `net.ipv4.{all,default,interfaces}.rp_filter = 0`. Seems to be Linux related, as packets can be see via tcpdump on outgoing and incoming inteface. No idea now, though... – user22602 Oct 04 '12 at 00:13
  • What does your routing table look like? What does `netstat -ng` say when you run your listener? – Nikolai Fetissov Oct 04 '12 at 03:46
  • You may need to do `setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, ..)` to control which interface the packets are sent from... – Chris Dodd Oct 04 '12 at 03:46

4 Answers4

14

with
addr.sin_addr.s_addr=inet_addr(my_ipv4Addr);
bind(sockfd,(SA*)&addr,sizeof(addr));
you can only send out packets to the multicast group,
but you can't recv any packets, even those send out from `my_ipv4Addr'.

so addr.sin_addr.s_addr must be htonl(INADDR_ANY).

with
mreq.imr_interface.s_addr=inet_addr(my_ipv4Addr);
you can recv all packets from the multicast group,
but it send out packets with the default interface (maybe eth0),
not the one you specified (like eth1).
So this is no effect.

with
setsockopt(sockfd,SOL_SOCKET,SO_BINDTODEVICE,ETH1,strlen(ETH1));
you can send out packets through the interface ETH1,
but you can only recv packets send out from the ip associated with ETH1,
you can't recv any packets from other clients.

with
mreq.imr_interface.s_addr=inet_addr(my_ipv4Addr);
setsockopt(sockfd,IPPROTO_IP,IP_MULTICAST_IF,&mreq.imr_interface,sizeof(struct in_addr);
you can send out packets through the interface associated with my_ipv4addr,
also you can recv any packets from any clients in the multicast group.

PLA
  • 777
  • 8
  • 8
4

When binding a socket for receiving multicast traffic, if you bind to a local address, this prevents multicast packets from being received on non-Windows systems.

I first discovered this when I released version 3.6 of UFTP with the feature of binding to a specific address. Windows handles it just fine, but on Linux systems the multicast packets weren't received by the app. I had to remove the feature in the next release.

Note that you are allowed to bind directly to the multicast address:

addr.sin_addr.s_addr = inet_addr(group);

In this case, you'll receive traffic only for that multicast address on that socket. You still need to join the multicast group on a specific interface to receive however.

If you plan on receiving data from more than one multicast address on the same socket, then you need to bind to INADDR_ANY.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • This is answer is incorrect. I would review this related stackoverflow. http://stackoverflow.com/questions/10692956/what-does-it-mean-to-bind-a-multicast-udp-socket – f3xy Aug 10 '16 at 18:32
  • @f3xy It is correct. I just verified it on a RedHat 5 system. If you bind to `INADDR_ANY`, you'll receive multicast. If you bind to a specific interface, you won't. – dbush Aug 11 '16 at 02:30
  • The statement "When binding a socket for receiving multicast traffic, you should always bind to INADDR_ANY." Is incorrect. If you read the stackoverflow I cited. Which also cites UNIX Network Programming by Richard Stevens you will see the statement you are making is incorrect. Although it is possible to bind INADDR_ANY and receive multicast, it is a much better idea to bind on the multicast address and port you wish to receive. Perhaps you could edit your answer? I just don't want anyone being misled. – f3xy Aug 11 '16 at 15:34
  • 1
    @f3xy You're correct, binding to a multicast address does work. It's when you bind to a local interface that you can't receive multicast. I'll edit to reflect. – dbush Aug 11 '16 at 16:48
  • 1
    thank you very much for your understanding. UFTP looks pretty neat, I will have to check it out. – f3xy Aug 12 '16 at 14:47
  • Exactly. Binding to an IP address for a UDP socket on Linux just has a filtering function for the specified IP address, not a binding function. – Johannes Overmann Sep 20 '17 at 22:34
4
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY); 
//addr.sin_addr.s_addr = inet_addr(my_ipv4Addr); 
addr.sin_port = htons(port);

mreq.imr_multiaddr.s_addr = inet_addr(group);
// [-]    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
mreq.imr_interface.s_addr = inet_addr(my_ipv4Addr);

You just need to edit your code like mine.

sina
  • 41
  • 2
  • if you bind your socket to INADDR_ANY, it'll receive every packet sent to that specific port. I'd leave the bind address as my_IPV4Addr – user666412 Apr 25 '14 at 17:44
0

Either you simplified your code for the sake of understanding or i have missed something,

This is the struct

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 */
};

ip man page - IP_ADD_MEMBERSHIP

But you are referring

mreq.imr_interface.s_addr = inet_addr(my_ipv4Addr);

What is imr_interface? Does it compile?

In case you just wrote the name above for better readibility, have you tried filling the interface index i.e. imr_ifindex to the specific interface you want to attach to.

My guess is, if you leave the imrr_address and assign the interface index only, it should bind to that interface and receive packets. See if that helps.

fkl
  • 5,412
  • 4
  • 28
  • 68