How to Specify a Local Bond Interface to Multicast Socket in Haskell

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

How to Multicast (send) to first NIC?

Apparently setsockopt and IP_MULTICAST_IF don't work if wsock32.dll is used instead of ws2_32.dll. I thought I was doing it wrong when I kept getting 1.0.0.0 as the IP address even when it was something else that I've set with setsockopt. Funny thing is, before the call to IP_MULTICAST_IF, it would return 0.0.0.0, so setsockopt` did change something, just not correctly.

Someone else who had this same problem way back in 2004 - http://us.generation-nt.com/ip-multicast-problem-help-37595922.html. When we #include "winsock2.h" we need to use ws2_32.dll. However, with C++ Builder, it's impossible to use ws2_32.dll when we use winsock2.h - the RTL implicitly links in wsock32.dll and you can't link ws2_32.dll even if you explicitly specify #pragma comment(lib, "ws2_32.lib"). Embarcadero really need to fix this! Someone in the RTL team must've decided it's clever to implicitly include wsock32.dll. The only 'clever' thing it did was users didn't have to include one line in their code - #pragma comment(lib, "wsock32.lib"). While they're at that, they might as well include every single DLL files known to mankind.

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.

Send packets from a specific interface in c

If the point of passing in the sockaddr to bind is to specify where to
bind the socket locally on your machine, could you just pass in the IP
address of a specific interface to bind your socket to it?

Binding to an address (via bind()) and binding to an interface (via setsockopt()) serve different, but overlapping purposes. Usually bind() is what you want.

In particular, your question seems to assume that there is a 1:1 mapping between addresses and interfaces, but that it is not a safe assumption. One interface can have multiple addresses, and, at least in principle, one address can be served by multiple interfaces.



Related Topics



Leave a reply



Submit