I'm trying to create a multicast testing tool for my local network. The only feature I don't yet have working is IPv6 SSM on Linux. I've used strace to try and debug and this is the error I'm receiving when I try to join the multicast group:
setsockopt(67, SOL_IPV6, MCAST_JOIN_SOURCE_GROUP, "\3\0\0\0\0\0\0\0\27\0\270\v\0\0\0\0\3778\0\0\0\0\0\0\0\0\0\0\207eC!"..., 264) = -1 EADDRNOTAVAIL (Cannot assign requested address)
Optval = 03000000000000001700B80B00000000FF38000000000000000000008765432100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001700000000000000FE8000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.
I am using interface 3, trying to connect to FF38::8765:4321 on port 3000 with FE80::1 as my source. This seems correct to me, any ideas what could be causing the error?
Edit: Here's my code to make the syscall:
[DllImport("libc", SetLastError = true)]
static extern int setsockopt(int sockfd, int level, int optname, void *optval, int size);
fixed(void* data = &joinData[0])
{
setsockopt((int)socket.Handle, 41, 46, data, joinData.Length);
}
and here's my code to populate optval:
byte[] joinBytes;
GroupSourceReqModel v6ssmJoin = new GroupSourceReqModel();
v6ssmJoin.gr_interface = (uint)selections.LocalIp.ScopeId;
SocketAddressIpv6InputModel mGroupIn = new SocketAddressIpv6InputModel
{
sin6_family = (ushort)selections.MulticastAddress.AddressFamily,
sin6_port = (ushort)selections.Port,
sin6_scope_id = 0
};
for (int i = 0; i < selections.MulticastAddress.GetAddressBytes().Length; i++)
{
mGroupIn.sin6_addr[i] = selections.MulticastAddress.GetAddressBytes()[i];
}
SocketAddressStorageModel groupSock = new SocketAddressStorageModel();
byte[] groupSockInData = ConvertToBytes(mGroupIn);
Console.WriteLine(Convert.ToHexString(groupSockInData));
byte* groupSockPointer = (byte*)&groupSock;
for (int i = 0; i < groupSockInData.Length; i++)
{
groupSockPointer[i] = groupSockInData[i]; // Now populate the groupSock object with the byte data of mGroupIn
}
v6ssmJoin.gsr_group = groupSock;
SocketAddressIpv6InputModel sGroupIn = new SocketAddressIpv6InputModel
{
sin6_family = (ushort)selections.SourceAddress.AddressFamily,
sin6_port = 0,
sin6_scope_id = 0
};
for (int i = 0; i < selections.SourceAddress.GetAddressBytes().Length; i++)
{
sGroupIn.sin6_addr[i] = selections.SourceAddress.GetAddressBytes()[i];
}
SocketAddressStorageModel sourceSock = new SocketAddressStorageModel
{
ss_family = (short)selections.SourceAddress.AddressFamily
};
byte[] sourceSockInData = ConvertToBytes(sGroupIn);
byte* sourceSockPointer = (byte*)&sourceSock;
for (int i = 0; i < sourceSockInData.Length; i++)
{
sourceSockPointer[i] = sourceSockInData[i];
}
v6ssmJoin.gsr_source = sourceSock;
joinBytes = ConvertToBytes(v6ssmJoin);
return joinBytes;
public static unsafe byte[] ConvertToBytes<T>(T value) where T : unmanaged
{
byte* pointer = (byte*)&value;
byte[] bytes = new byte[sizeof(T)];
for (int i = 0; i < sizeof(T); i++)
{
bytes[i] = pointer[i];
}
return bytes;
}
If it's relevant, I'm running on kernel version 5.4.0-122-generic.
Address family for IPv6 in Linux is 10, as seen in the kernel source code here which differs from the Windows value that C# uses. In addition, the port value is stored in big endian.