Как использовать 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*/);