如何在 C# 中使用 MLDv2(IPv6 组播)

发布于 2024-12-03 18:58:55 字数 548 浏览 1 评论 0原文


In IPv4, [Version 3 of IGMP adds support for "source filtering", that is, the ability for a system to report interest in receiving packets *only* from specific source addresses.][1]

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

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

谢谢!

In IPv4, [Version 3 of IGMP adds support for "source filtering", that is, the ability for a system to report interest in receiving packets *only* from specific source addresses.][1]

I am using IGMPv3 in a C# application to support this behaviour. Here is how I do it.

I am now in the process to add support to IPv6 in my application and I need to get the same behaviour as in IPv4. From what I've read, the equivalent protocol to IGMPv3 in IPv6 is MLDv2. Has someone any idea on how to implement this in C# with Socket?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

遮云壑 2024-12-10 18:58:55

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

如果 C# 运行时完全支持 IPv6,则必须尝试匹配 GROUP_REQGROUP_SOURCE_REQ 结构。 SSM 没有与 IPv4 API 相匹配的 IPv6 特定 API,因为开发人员最终放弃了 API 的愚蠢重复,并最终选择了一个超级集。

不幸的是,C# 很可能实现 ipv6_mreq 对于 AddMembershipAddSourceMembership 失败。该文档完全缺乏细节。

C# 中未定义所需的所有 SocketOptionName 值:

/* 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

The RFC3678 protocol independent API is only available in Vista+ which might explain the problem.

If C# runtime does fully support IPv6 you will have to try to match the GROUP_REQ or GROUP_SOURCE_REQ structures. There is no IPv6 specific API for SSM matching the IPv4 API because developers finally abandoned the inane duplication of APIs and finally settled on the one super set.

It is unfortunately likely that C# implements ipv6_mreq for AddMembership and AddSourceMembership fails. The documentation is completely absent on detail.

All the SocketOptionName values required are not defined in 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
懷念過去 2024-12-10 18:58:55

为了跟进 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*/);

To follow up on the answer of Steve-o, it is still possible to do source filtering in IPv6 in C# even if the System.Net.Sockets.SocketOptionName enumeration doesn't define the required options by casting the number directly.

(SocketOptionName) 45; //MCAST_JOIN_SOURCE_GROUP

The function SetSocketOption of the socket will let the call go to the "windows socket" even if the option is not recognized. The real struggle becomes the data structure itself that needs to be sent along side the option.
To set source filtering the data structure must be like this one: group_source_req. The previous struct is using a sockaddr_storage that is usually inside a union with sockaddr_in and sockaddr_in6. To replicate this behavior we can define the same structs like this:

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
}

You can now create a sockaddr_in6 by doing this:

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];
}

The bytes of the sockaddr_in6 can now be extracted by using the solution provided here and copied directly into a previously created 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];
}

Now that you have a sockaddr_storage you can assign it to group_source_req and extract the data of the group_source_req like we did earlier and use this as the value when you set the option.

socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName) 45, /*data extracted from group_source_req*/);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文