0

在 IPv4 中,[IGMP 版本 3 增加了对“源过滤”的支持,即系统能够报告对接收数据包的兴趣*仅*来自特定源地址。][1]

我在 C# 应用程序中使用 IGMPv3 来支持这种行为。这是我的做法。

我现在正在我的应用程序中添加对 IPv6 的支持,我需要获得与 IPv4 相同的行为。根据我的阅读,IPv6 中 IGMPv3 的等效协议是MLDv2。有人知道如何在 C# 中使用 Socket 实现这一点吗?

谢谢!

4

2 回答 2

1

RFC3678协议独立 API 仅在 Vista+ 中可用,这可能解释了该问题

如果 C# 运行时完全支持 IPv6,您将不得不尝试匹配GROUP_REQGROUP_SOURCE_REQ结构。没有针对 SSM 的 IPv6 特定 API 与 IPv4 API 相匹配,因为开发人员最终放弃了 API 的无用重复,并最终确定了一个超级集。

不幸的是,C# 很可能实现了ipv6_mreqAddMembershipAddSourceMembership失败了。该文档完全没有详细说明。

所有SocketOptionName需要的值都没有在 C# 中定义:

/* RFC 3678 */
#define MCAST_JOIN_GROUP       41
#define MCAST_LEAVE_GROUP      42
#define MCAST_BLOCK_SOURCE     43
#define MCAST_UNBLOCK_SOURCE   44
#define MCAST_JOIN_SOURCE_GROUP        45
#define MCAST_LEAVE_SOURCE_GROUP       46
#define MCAST_MSFILTER         47
于 2011-09-09T07:02:48.547 回答
0

为了跟进 Steve-o 的答案,即使 System.Net.Sockets.SocketOptionName 枚举没有通过直接转换数字来定义所需的选项,仍然可以在 C# 中在 IPv6 中进行源过滤。

(SocketOptionName) 45; //MCAST_JOIN_SOURCE_GROUP

套接字的函数 SetSocketOption 将让调用转到“windows 套接字”,即使该选项未被识别。真正的斗争变成了需要与选项一起发送的数据结构本身。要设置源过滤,数据结构必须是这样的:group_source_req。前一个结构使用sockaddr_storage,它通常位于与sockaddr_insockaddr_in6的联合中。为了复制这种行为,我们可以像这样定义相同的结构:

private unsafe struct sockaddr_storage
{
    public short ss_family;             //2
    private fixed byte __ss_pad1[6];    //6
    private Int64 __ss_align;           //8
    private fixed byte __ss_pad2[112];  //112
}
private unsafe struct sockaddr_in
{
    public ushort sin_family;       //2
    public ushort sin_port;         //2
    public fixed byte sin_addr[4];  //4
    private fixed byte sub_zero[8]; //8
 }
private unsafe struct sockaddr_in6
{
    public ushort sin6_family;       //2
    public ushort sin6_port;         //2
    public int sin6_flowinfo;        //4
    public fixed byte sin6_addr[16]; //16
    public uint sin6_scope_id;       //4
}
private struct group_source_req
{
    public uint gr_interface;           //4
    //Compiler add a padding here:      //4
    public sockaddr_storage gr_group;   //128
    public sockaddr_storage gr_source;  //128
}

您现在可以通过执行以下操作创建 sockaddr_in6:

sockaddr_in6 sockIn = new sockaddr_in6
{
    sin6_family = (ushort) endPoint.AddressFamily,
    sin6_port = (ushort)endPoint.Port,
    sin6_scope_id = 0
};
for (int i = 0; i < endPoint.Address.GetAddressBytes().Length; i++)
{
    sockIn.sin6_addr[i] = endPoint.Address.GetAddressBytes()[i];
}

现在可以使用此处提供的解决方案提取 sockaddr_in6 的字节,并将其直接复制到先前创建的 sockaddr_storage 中:

sockaddr_storage sock = new sockaddr_storage
{
    ss_family = (short)endPoint.AddressFamily
};
//[...]
byte[] sockInData = getBytes(sockIn);
byte* sockData = (byte*) &sock;
for (int i = 0; i < sockInData.Length; i++)
{
    sockData [i] = sockInData[i];
}

现在您有了一个 sockaddr_storage ,您可以将它分配给 group_source_req 并像我们之前所做的那样提取 group_source_req 的数据,并在您设置选项时将其用作值。

socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName) 45, /*data extracted from group_source_req*/);
于 2017-11-22T18:36:31.387 回答