Как использовать MLDv2 в C# (IPv6 Multicast)


В версии IPv4 IGMP версии 3 добавлена ​​поддержка "фильтрации источников", то есть способность системы сообщать о заинтересованности в получении пакетов только с определенных адресов источника.

Я использую IGMPv3 в приложении C# для поддержки этого поведения. Вот как я это делаю.

Сейчас я нахожусь в процессе добавления поддержки IPv6 в моем приложении, и мне нужно получить то же поведение, что и в IPv4. Из того, что я прочитал, протоколом, эквивалентным IGMPv3 в IPv6, является MLDv2. Кто-нибудь есть идеи о том, как реализовать это в C# с Socket?

Спасибо!

2 ответа

Решение

API, независимый от протокола RFC3678, доступен только в Vista+, что может объяснить проблему.

Если среда выполнения C# полностью поддерживает IPv6, вам нужно будет попытаться сопоставить структуры GROUP_REQ или GROUP_SOURCE_REQ. Не существует специального API IPv6 для SSM, соответствующего API IPv4, потому что разработчики, наконец, отказались от бессмысленного дублирования API и в итоге остановились на одном суперсете.

К сожалению, вероятно, что C# реализует ipv6_mreq для AddMembership а также AddSourceMembership выходит из строя. Документация полностью отсутствует на деталях.

Все 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

Чтобы проконтролировать ответ Steve-o, все еще возможно выполнить фильтрацию исходного кода в IPv6 в C#, даже если перечисление System.Net.Sockets.SocketOptionName не определяет требуемые параметры путем непосредственного приведения числа.

(SocketOptionName) 45; //MCAST_JOIN_SOURCE_GROUP

Функция SetSocketOption сокета позволит вызову перейти к "сокету Windows", даже если опция не распознана. Настоящей борьбой становится сама структура данных, которую необходимо отправить вместе с опцией. Чтобы установить фильтрацию источника, структура данных должна быть такой: group_source_req. Предыдущая структура использует sockaddr_storage, который обычно находится внутри объединения с sockaddr_in и sockaddr_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*/);
Другие вопросы по тегам