Linux 多播数据包 运行 4 分钟后干燥
Linux Multicast-Packets run dry after 4 minutes
我尝试在 Linux 中处理多播数据包流。 266s - 278s(并不总是完全相同的时间段)接收正常,但之后就再也收不到包了。
这是我初始化多播的方式:
int arg = 1;
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(fd == -1) {
fprintf(stderr, "Error creating socket, %s\n", strerror(errno));
return;
}
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0) {
fprintf(stderr, "Failed to set `SO_REUSEADDR`, %s\n", strerror(errno));
return;
}
fcntl(fd, F_SETFL, O_NONBLOCK);
static struct ifreq intf;
strncpy(intf.ifr_name, cfg->ifname_buf, IF_NAMESIZE);
if(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (char*)&intf, sizeof(intf)) < 0) {
fprintf(stderr, "Failed to set `SO_BINDTODEVICE`, %s\n", strerror(errno));
return;
}
struct sockaddr_in sin;
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_port = htons(xxxxx);
sin.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");
if(bind(fd, (struct sockaddr*)&sin, sizeof(struct sockaddr)) < 0) {
fprintf(stderr, "Error on binding socket, %s\n", strerror(errno));
return;
}
ioctl(fd, SIOCGIFADDR, &intf);
struct ip_mreqn igmpv2_req;
memset(&igmpv2_req, 0, sizeof(struct ip_mreqn));
if(inet_pton(AF_INET, "xxx.xxx.xxx.xxx", &igmpv2_req.imr_multiaddr.s_addr)) {
memcpy(&igmpv2_req.imr_address, &cfg->ifaddr.sin_addr, sizeof(struct in_addr));
igmpv2_req.imr_ifindex = cfg->ifindex;
printf("Multiaddr: %s\n", inet_ntoa(igmpv2_req.imr_multiaddr));
printf("Interfaceaddr: %s\n", inet_ntoa(igmpv2_req.imr_address));
printf("Ifindex: %d\n", igmpv2_req.imr_ifindex);
if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &igmpv2_req, sizeof(struct ip_mreqn)) < 0) {
fprintf(stderr, "Failed to set `IP_ADD_MEMBERSHIP`: %s\n", strerror(errno));
return;
} else {
printf("Saved FD for igmp socket!\n");
cfg->socket_fd_igmp = fd;
}
} else {
fprintf(stderr, "Failed `inet_pton` igmp-multiaddr, %s\n", strerror(errno));
return;
}
正如我所说,它在 266 - 280 秒内工作正常。之后,没有收到任何数据包。我不知道这是因为我将接口添加到多播组的方式还是因为某些内核队列已满(我每秒接收多达 200k 个数据包)。
组播可不是小事。根据您的描述,会发生以下情况:
当你执行
setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &igmpv2_req, sizeof(struct ip_mreqn))
然后客户端发出该组的 igmp 加入消息。您的交换机启用了 igmp snooping,现在知道将该组的多播数据包转发到您主机的端口。
现在,在您观察到的时间(4-5 分钟,260 秒确实是许多交换机的默认值)之后,交换机中的此信息超时,因为主机不发送这些消息regularly/unsolicited.
您的网络上需要一个 多播路由器 ,它会定期查询主机,主机以 igmp 报告 响应它们的多播组正在收听,以使交换硬件上的 igmp 侦听表保持最新。
或者,您也可以尝试在多播发送方和接收方之间的所有设备上禁用 igmp 侦听,以便 所有 多播消息被淹没到 所有 端口(使它们从第 2 层的角度广播)。这会给二层网段带来相当高的负载,因此首选第一种方法。
一个丑陋的解决方法(我还没有测试过)可能是每 2-3 分钟删除并重新添加 setsockopt()
的成员资格。这应该强制一个 igmp 数据包,从而保持多播连接,但这不是它应该如何工作的。
我尝试在 Linux 中处理多播数据包流。 266s - 278s(并不总是完全相同的时间段)接收正常,但之后就再也收不到包了。
这是我初始化多播的方式:
int arg = 1;
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(fd == -1) {
fprintf(stderr, "Error creating socket, %s\n", strerror(errno));
return;
}
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0) {
fprintf(stderr, "Failed to set `SO_REUSEADDR`, %s\n", strerror(errno));
return;
}
fcntl(fd, F_SETFL, O_NONBLOCK);
static struct ifreq intf;
strncpy(intf.ifr_name, cfg->ifname_buf, IF_NAMESIZE);
if(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (char*)&intf, sizeof(intf)) < 0) {
fprintf(stderr, "Failed to set `SO_BINDTODEVICE`, %s\n", strerror(errno));
return;
}
struct sockaddr_in sin;
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_port = htons(xxxxx);
sin.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");
if(bind(fd, (struct sockaddr*)&sin, sizeof(struct sockaddr)) < 0) {
fprintf(stderr, "Error on binding socket, %s\n", strerror(errno));
return;
}
ioctl(fd, SIOCGIFADDR, &intf);
struct ip_mreqn igmpv2_req;
memset(&igmpv2_req, 0, sizeof(struct ip_mreqn));
if(inet_pton(AF_INET, "xxx.xxx.xxx.xxx", &igmpv2_req.imr_multiaddr.s_addr)) {
memcpy(&igmpv2_req.imr_address, &cfg->ifaddr.sin_addr, sizeof(struct in_addr));
igmpv2_req.imr_ifindex = cfg->ifindex;
printf("Multiaddr: %s\n", inet_ntoa(igmpv2_req.imr_multiaddr));
printf("Interfaceaddr: %s\n", inet_ntoa(igmpv2_req.imr_address));
printf("Ifindex: %d\n", igmpv2_req.imr_ifindex);
if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &igmpv2_req, sizeof(struct ip_mreqn)) < 0) {
fprintf(stderr, "Failed to set `IP_ADD_MEMBERSHIP`: %s\n", strerror(errno));
return;
} else {
printf("Saved FD for igmp socket!\n");
cfg->socket_fd_igmp = fd;
}
} else {
fprintf(stderr, "Failed `inet_pton` igmp-multiaddr, %s\n", strerror(errno));
return;
}
正如我所说,它在 266 - 280 秒内工作正常。之后,没有收到任何数据包。我不知道这是因为我将接口添加到多播组的方式还是因为某些内核队列已满(我每秒接收多达 200k 个数据包)。
组播可不是小事。根据您的描述,会发生以下情况:
当你执行
setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &igmpv2_req, sizeof(struct ip_mreqn))
然后客户端发出该组的 igmp 加入消息。您的交换机启用了 igmp snooping,现在知道将该组的多播数据包转发到您主机的端口。
现在,在您观察到的时间(4-5 分钟,260 秒确实是许多交换机的默认值)之后,交换机中的此信息超时,因为主机不发送这些消息regularly/unsolicited.
您的网络上需要一个 多播路由器 ,它会定期查询主机,主机以 igmp 报告 响应它们的多播组正在收听,以使交换硬件上的 igmp 侦听表保持最新。
或者,您也可以尝试在多播发送方和接收方之间的所有设备上禁用 igmp 侦听,以便 所有 多播消息被淹没到 所有 端口(使它们从第 2 层的角度广播)。这会给二层网段带来相当高的负载,因此首选第一种方法。
一个丑陋的解决方法(我还没有测试过)可能是每 2-3 分钟删除并重新添加 setsockopt()
的成员资格。这应该强制一个 igmp 数据包,从而保持多播连接,但这不是它应该如何工作的。