套接字选项IP_MULTICAST_IF,带静态多播路由,从多播切换到单播MAC寻址
我想听听专家们对套接字选项IP_MULTICAST_IF(“设置多播接口”)与静态多播路由结合使用的建议 在LAN上,多播IP数据报通常在多播以太网帧(IP/MAC多播目的地址映射)中发送。在多宿主Linux系统(内核5.11)上,我注意到套接字选项套接字选项IP_MULTICAST_IF,带静态多播路由,从多播切换到单播MAC寻址,c,linux,sockets,multicast,multicastsocket,C,Linux,Sockets,Multicast,Multicastsocket,我想听听专家们对套接字选项IP_MULTICAST_IF(“设置多播接口”)与静态多播路由结合使用的建议 在LAN上,多播IP数据报通常在多播以太网帧(IP/MAC多播目的地址映射)中发送。在多宿主Linux系统(内核5.11)上,我注意到套接字选项IP\u MULTICAST\u IF修改了如下行为: 在没有静态路由的情况下,多播IP数据报总是在多播以太网帧中发送,如果有或没有IP\u多播 对于静态路由,如果不使用IP\u MULTICAST\u IF,则在多播以太网帧中发送多播IP数据报
IP\u MULTICAST\u IF
修改了如下行为:
- 在没有静态路由的情况下,多播IP数据报总是在多播以太网帧中发送,如果有或没有
IP\u多播
- 对于静态路由,如果不使用
,则在多播以太网帧中发送多播IP数据报IP\u MULTICAST\u IF
- 使用静态路由,使用
,多播IP数据报以单播以太网帧发送到网关IP\u MULTICAST\u IF
IP_多播_IF(自Linux 1.2起)
为多播套接字设置本地设备。setsockopt(2)的参数是ip_mreqn或(自Linux 3.5以来)
ip_mreq结构类似于ip_ADD_成员资格或in_addr结构。(内核决定哪种结构
正在根据optlen中传递的大小进行传递。)对于getsockopt(2),参数是in_addr结构。
下面是一个示例配置,用于在两个Linux虚拟机之间复制此配置
第一个系统,“vmubuntu”,在接口ens38上运行Wireshark的接收器:
ens33:00:0C:29:46:B7:CE 192.168.98.3/24->NAT,默认路由
ens38:00:50:56:39:F0:03 192.168.233.11/24->主机本地vmware
第二个系统,“vmfedora”,发送方系统:
ens33:00:0C:29:B6:33:8d192.168.98.2/24->NAT,默认路由
ens37:00:50:56:29:34:37 192.168.233.10/24->主机本地vmware
我们使用“vmubuntu”作为网关,在“vmfedora”上为多播流量声明一个静态路由。请注意,常规IP通信的默认路由在另一个接口上
$uname-a
Linux vmfedora 5.11.15-200.fc33.x86_64#1 SMP周五4月16日13:41:20 UTC 2021 x86_64 x86_64 GNU/Linux
$
$sudo路由添加-网络224.0.0.0网络掩码240.0.0.0 gw 192.168.233.11开发环境37
$
$route-n
内核IP路由表
目标网关Genmask标志度量参考使用Iface
0.0.0.0 192.168.98.1 0.0.0.0 UG 20100 0 ens33
192.168.98.0 0.0.0.0 255.255.0 U 100 0 0 ENS3
192.168.233.0.0.0.0.0 255.255.0 U 101 0 0 ens37
224.0.0.0 192.168.233.11 240.0.0.0 UG 0 0 ens37
让我们在路由范围内从“vmfedora”发送多播数据包到239.230.2.44:3044。我们将套接字绑定到本地地址192.168.233.10,这也是静态路由的传出接口。我们不使用socket选项IP\u MULTICAST\u IF
(下面的示例代码)
在“vmubuntu”上,Wireshark报告如下:
互联网协议版本4,Src:192.168.233.10,Dst:239.230.2.44
以太网II,Src:VMware_29:34:37(00:50:56:29:34:37),Dst:IPv4mcast_66:02:2c(01:00:5e:66:02:2c)
目的地:IPv4mcast_66:02:2c(01:00:5e:66:02:2c)
地址:IPv4mcast_66:02:2c(01:00:5e:66:02:2c)
.... ..0. .... .... .... .... = LG位:全局唯一地址(出厂默认)
.... ...1 .... .... .... .... = IG位:组地址(多播/广播)
来源:VMware_29:34:37(00:50:56:29:34:37)
地址:VMware_29:34:37(00:50:56:29:34:37)
.... ..0. .... .... .... .... = LG位:全局唯一地址(出厂默认)
.... ...0 .... .... .... .... = IG位:单个地址(单播)
类型:IPv4(0x0800)
我们可以看到,具有多播目的地地址的IP数据报在具有相应多播目的地地址的以太网帧中发送
现在,让我们在bind()
之后添加套接字选项IP\u MULTICAST\u IF
。Wireshark报道:
互联网协议版本4,Src:192.168.233.10,Dst:239.230.2.44
以太网II,Src:VMware_29:34:37(00:50:56:29:34:37),Dst:VMware_39:f0:03(00:50:56:39:f0:03)
目的地:VMware_39:f0:03(00:50:56:39:f0:03)
地址:VMware_39:f0:03(00:50:56:39:f0:03)
.... ..0. .... .... .... .... = LG位:全局唯一地址(出厂默认)
.... ...0 .... .... .... .... = IG位:单个地址(单播)
来源:VMware_29:34:37(00:50:56:29:34:37)
地址:VMware_29:34:37(00:50:56:29:34:37)
.... ..0. .... .... .... .... = LG位:全局唯一地址(出厂默认)
.... ...0 .... .... .... .... = IG位:单个地址(单播)
类型:IPv4(0x0800)
现在,我们看到具有多播目标地址的IP数据报在以太帧中发送,网关的单播MAC地址作为目标地址
唯一的区别是套接字选项IP\u MULTICAST\u IF
如果您想重现这个,请参阅下面的代码。它接受一个必需的命令行参数:套接字绑定到的本地接口的IP地址。第二个可选命令行参数是“-f”,用于强制套接字选项IP\u MULTICAST\u IF
(默认情况下未设置)
感谢您对此选项的解释
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main(int argc, char* argv[])
{
struct sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_port = htons(3044);
inet_pton(AF_INET, "239.230.2.44", &dest.sin_addr);
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = 0;
local.sin_addr.s_addr = 0;
int use_mcast_if = 0;
for (int arg = 1; arg < argc; ++arg) {
if (strcmp(argv[arg], "-f") == 0) {
use_mcast_if = 1;
}
else if (inet_pton(AF_INET, argv[arg], &local.sin_addr) != 1) {
fprintf(stderr, "invalid local address: %s\n", argv[arg]);
return EXIT_FAILURE;
}
}
const int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
perror("socket()");
return EXIT_FAILURE;
}
if (bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0) {
perror("bind()");
return EXIT_FAILURE;
}
if (use_mcast_if && setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &local.sin_addr, sizeof(local.sin_addr)) < 0) {
perror("setsockopt(IP_MULTICAST_IF)");
return EXIT_FAILURE;
}
const int data = 0x12345678;
for (int i = 0; i < 10; ++i) {
sendto(sock, &data, sizeof(data), 0, (struct sockaddr*)&dest, sizeof(dest));
}
close(sock);
return EXIT_SUCCESS;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
int main(int argc,char*argv[])
{
s