简体   繁体   English

C在所有接口上侦听多播,响应与收到的一样

[英]C listen to multicast on all interfaces, respond on same as recieved

I am trying to listen to multicast on all interfaces in system, but responds only on this on which I've received multicast packet. 我试图在系统中的所有接口上侦听多播,但只响应我收到组播数据包的情况。

What I've did is to create a socket for each of the interfaces and here the problems starts. 我所做的是为每个接口创建一个套接字,这里问题就出现了。

When I bind interface to INADDR_ANY it will receive packets for all interfaces and send on default one. 当我将接口绑定到INADDR_ANY ,它将接收所有接口的数据包并发送默认接口。 If I bind port to specific interface it will not receive multicast packet (but it will be able to send it on correct interface). 如果我将端口绑定到特定接口,它将不会接收多播数据包(但它将能够在正确的接口上发送)。

I've tried setting options like IP_ADD_MEMBERSHIP or IP_MULTICAST_IF but without success. 我尝试过设置IP_ADD_MEMBERSHIPIP_MULTICAST_IF等选项但没有成功。

I think the other options whould be to create one socket to receive on all ifs and senders sockets for all interfaces, but on this approach I have no idea on which ifs packet entered... 我认为其他选项可能是创建一个套接字来接收所有接口的所有ifs和发送器套接字,但是在这种方法中我不知道输入哪个ifs数据包...

Code samples (simplified, without error handling and stuff): 代码示例(简化,无错误处理和内容):

Creating socket: 创建套接字:

//here i am looping over all interfaces from getifaddrs
struct sockaddr_in *pAddr = (struct sockaddr_in *)tmp->ifa_addr;

sockets[i] = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
setsockopt(sockets[i], SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);

mreq.imr_multiaddr.s_addr=inet_addr(MDNS_ADDRESS);
mreq.imr_interface.s_addr=pAddr->sin_addr.s_addr;

setsockopt(sockets[i], IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
setsockopt(sockets[i], IPPROTO_IP, IP_MULTICAST_IF, &pAddr, sizeof(struct in_addr));

memset(&my_addr, 0, sizeof(struct sockaddr_in));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY; //or pAddr->sin_addr.s_addr;
my_addr.sin_port = htons(port);

bind(sockets[i], (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1);

Receiving and sending: 接收和发送:

recvfrom(sockfd, buf, MAXBUFLEN-1 , 0, (struct sockaddr *)&their_addr, &addr_len);

//do some magic and response (response should be a multicast too)

destination.sin_addr.s_addr = inet_addr(MULTICAST_ADDRESS);
destination.sin_family = AF_INET;
destination.sin_port = htons( port );
sendto(sockfd, buffer, len, 0, (struct sockaddr *)&destination, sizeof destination);

I would like to create something similar in work to mDNS so when packet entered on specific interface program should answer on the same if with some data about this if. 我想在mDNS的工作中创建类似的东西,所以当在特定的接口程序上输入的数据包应该回答相同的情况,如果有一些关于这个的数据if。 It should not send this on other ifs as it may not be relevant for them, but it should send it as multicast so any other host in same network will receive the respond. 它不应该在其他ifs上发送,因为它可能与它们无关,但它应该将其作为多播发送,因此同一网络中的任何其他主机都将收到响应。

You should only need one socket for this. 你应该只需要一个插座。

First bind to INADDR_ANY and your port of choice. 首先绑定到INADDR_ANY和您选择的端口。 Then call setsockopt with IP_ADD_MEMBERSHIP on each interface you want to receive multicast on. 然后在要接收多播的每个接口上使用IP_ADD_MEMBERSHIP调用setsockopt Finally, call setsockopt with IP_MULTICAST_IF on the interface you want to send multicast on. 最后,在要发送多播的接口上调用setsockoptIP_MULTICAST_IF Make sure to check for errors on each call. 确保检查每次通话时的错误。

int socket s;
struct sockaddr_in sin;
struct ip_mreq mreq;
struct in_addr out_addr;

bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr=htonl(INADDR_ANY);
sin.sin_port = htons(1044);   // or whatever port you listen on

if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
    perror("socket failed");
    exit(1);
}

if (bind(s, (struct sockaddr *)&sin, sizeof(sin))==-1) {
    perror("bind failed");
    exit(1);
}

// Do this in a loop for each interface
mreq.imr_multiaddr = inet_addr("230.4.4.1");     // your multicast address
mreq.imr_interface = inet_addr("192.168.1.1");   // your incoming interface IP
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) == -1) {
    perror("Error joining multicast group");
    exit(1);
}

out_addr.s_addr = inet_addr("192.168.1.1");   // your outgoing interface IP
if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&out_addr,sizeof(out_addr))== -1) {
    perror("Error setting outgoing interface");
    exit(1);
}

When using multicast, you should always bind to the INADDR_ANY address. 使用多播时,应始终绑定到INADDR_ANY地址。 Failure to do so breaks multicast on Linux systems. 如果不这样做会破坏Linux系统上的多播。

Initially, I accepted @dbush answer as it allowed me to get on right track. 最初,我接受了@dbush的回答,因为它允许我走上正轨。 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 这里找到了一些代码: 设置UDP套接字的源IP

I was able to do all of this with single socket and setting IP_PKTINFO. 我能够使用单个套接字并设置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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM