user22602
user22602

Reputation: 329

C - choose interface for UDP/multicast socket

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.

Upvotes: 22

Views: 41164

Answers (4)

PLA
PLA

Reputation: 807

Going through your code step-by-step:

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 sends out packets using the default interface (for example eth0), not the one you specified (like eth1). So this has 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.

Upvotes: 15

dbush
dbush

Reputation: 223917

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.

Upvotes: 5

sina
sina

Reputation: 41

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.

Upvotes: 4

fkl
fkl

Reputation: 5535

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.

Upvotes: 0

Related Questions