Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/28.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
套接字选项IP_MULTICAST_IF,带静态多播路由,从多播切换到单播MAC寻址_C_Linux_Sockets_Multicast_Multicastsocket - Fatal编程技术网

套接字选项IP_MULTICAST_IF,带静态多播路由,从多播切换到单播MAC寻址

套接字选项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_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数据报是以多播以太网帧还是以单播以太网帧发送到网关

第二个问题:无论第一个问题的答案是什么,为什么套接字选项IP_MULTICAST_从多播切换到单播MAC寻址

Linux手册页(“man 7 ip”)不是很明确:

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