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