Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit eee5e5f

Browse files
peponekarelz
authored andcommitted
Fix MulticasInterface SocketOption to work with IPv6 on Linux (#25544)
Fix MulticasInterface SocketOption to work with IPv6 on Linux Bug#25525
1 parent 36d4c2c commit eee5e5f

File tree

2 files changed

+97
-3
lines changed

2 files changed

+97
-3
lines changed

src/Native/Unix/System.Native/pal_networking.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,7 +1273,7 @@ static int8_t GetMulticastOptionName(int32_t multicastOption, int8_t isIPv6, int
12731273
return true;
12741274

12751275
case MulticastOption_MULTICAST_IF:
1276-
*optionName = IP_MULTICAST_IF;
1276+
*optionName = isIPv6 ? IPV6_MULTICAST_IF : IP_MULTICAST_IF;
12771277
return true;
12781278

12791279
default:
@@ -1372,7 +1372,7 @@ int32_t SystemNative_GetIPv6MulticastOption(intptr_t socket, int32_t multicastOp
13721372

13731373
struct ipv6_mreq opt;
13741374
socklen_t len = sizeof(opt);
1375-
int err = getsockopt(fd, IPPROTO_IP, optionName, &opt, &len);
1375+
int err = getsockopt(fd, IPPROTO_IPV6, optionName, &opt, &len);
13761376
if (err != 0)
13771377
{
13781378
return SystemNative_ConvertErrorPlatformToPal(errno);
@@ -1410,7 +1410,7 @@ int32_t SystemNative_SetIPv6MulticastOption(intptr_t socket, int32_t multicastOp
14101410

14111411
ConvertByteArrayToIn6Addr(&opt.ipv6mr_multiaddr, &option->Address.Address[0], NUM_BYTES_IN_IPV6_ADDRESS);
14121412

1413-
int err = setsockopt(fd, IPPROTO_IP, optionName, &opt, sizeof(opt));
1413+
int err = setsockopt(fd, IPPROTO_IPV6, optionName, &opt, sizeof(opt));
14141414
return err == 0 ? Error_SUCCESS : SystemNative_ConvertErrorPlatformToPal(errno);
14151415
}
14161416

@@ -1987,6 +1987,10 @@ static bool TryGetPlatformSocketOption(int32_t socketOptionName, int32_t socketO
19871987
*optName = IPV6_RECVPKTINFO;
19881988
return true;
19891989

1990+
case SocketOptionName_SO_IP_MULTICAST_IF:
1991+
*optName = IPV6_MULTICAST_IF;
1992+
return true;
1993+
19901994
default:
19911995
return false;
19921996
}

src/System.Net.Sockets/tests/FunctionalTests/SocketOptionNameTest.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,81 @@ public void MulticastInterface_Set_InvalidIndex_Throws()
150150
}
151151
}
152152

153+
[OuterLoop] // TODO: Issue #11345
154+
[Fact]
155+
public async Task MulticastInterface_Set_IPv6_AnyInterface_Succeeds()
156+
{
157+
if (PlatformDetection.IsFedora || PlatformDetection.IsRedHatFamily7 || PlatformDetection.IsOSX)
158+
{
159+
return; // [ActiveIssue(24008)]
160+
}
161+
162+
// On all platforms, index 0 means "any interface"
163+
await MulticastInterface_Set_IPv6_Helper(0);
164+
}
165+
166+
[OuterLoop] // TODO: Issue #11345
167+
[Fact]
168+
[PlatformSpecific(TestPlatforms.Windows)]
169+
[ActiveIssue(21327, TargetFrameworkMonikers.Uap)] // UWP Apps are forbidden to send network traffic to the local Computer.
170+
public async void MulticastInterface_Set_IPv6_Loopback_Succeeds()
171+
{
172+
// On Windows, we can apparently assume interface 1 is "loopback." On other platforms, this is not a
173+
// valid assumption. We could maybe use NetworkInterface.LoopbackInterfaceIndex to get the index, but
174+
// this would introduce a dependency on System.Net.NetworkInformation, which depends on System.Net.Sockets,
175+
// which is what we're testing here.... So for now, we'll just assume "loopback == 1" and run this on
176+
// Windows only.
177+
await MulticastInterface_Set_IPv6_Helper(1);
178+
}
179+
180+
private async Task MulticastInterface_Set_IPv6_Helper(int interfaceIndex)
181+
{
182+
IPAddress multicastAddress = IPAddress.Parse("ff11::1:1");
183+
string message = "hello";
184+
int port;
185+
186+
using (Socket receiveSocket = CreateBoundUdpIPv6Socket(out port),
187+
sendSocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp))
188+
{
189+
receiveSocket.ReceiveTimeout = 1000;
190+
receiveSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption(multicastAddress, interfaceIndex));
191+
192+
// https://github.com/Microsoft/BashOnWindows/issues/990
193+
if (!PlatformDetection.IsWindowsSubsystemForLinux)
194+
{
195+
sendSocket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.MulticastInterface, interfaceIndex);
196+
}
197+
198+
var receiveBuffer = new byte[1024];
199+
var receiveTask = receiveSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), SocketFlags.None);
200+
201+
for (int i = 0; i < TestSettings.UDPRedundancy; i++)
202+
{
203+
sendSocket.SendTo(Encoding.UTF8.GetBytes(message), new IPEndPoint(multicastAddress, port));
204+
}
205+
206+
var cts = new CancellationTokenSource();
207+
Assert.True(await Task.WhenAny(receiveTask, Task.Delay(20_000, cts.Token)) == receiveTask, "Waiting for received data timed out");
208+
cts.Cancel();
209+
210+
int bytesReceived = await receiveTask;
211+
string receivedMessage = Encoding.UTF8.GetString(receiveBuffer, 0, bytesReceived);
212+
213+
Assert.Equal(receivedMessage, message);
214+
}
215+
}
216+
217+
[Fact]
218+
public void MulticastInterface_Set_IPv6_InvalidIndex_Throws()
219+
{
220+
int interfaceIndex = 31415;
221+
using (Socket s = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp))
222+
{
223+
Assert.Throws<SocketException>(() =>
224+
s.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.MulticastInterface, interfaceIndex));
225+
}
226+
}
227+
153228
[OuterLoop] // TODO: Issue #11345
154229
[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // In WSL, the connect() call fails immediately.
155230
[InlineData(false)]
@@ -215,6 +290,21 @@ private static Socket CreateBoundUdpSocket(out int localPort)
215290
return receiveSocket;
216291
}
217292

293+
// Create an Udp Socket and binds it to an available port
294+
private static Socket CreateBoundUdpIPv6Socket(out int localPort)
295+
{
296+
Socket receiveSocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
297+
298+
// sending a message will bind the socket to an available port
299+
string sendMessage = "dummy message";
300+
int port = 54320;
301+
IPAddress multicastAddress = IPAddress.Parse("ff11::1:1");
302+
receiveSocket.SendTo(Encoding.UTF8.GetBytes(sendMessage), new IPEndPoint(multicastAddress, port));
303+
304+
localPort = (receiveSocket.LocalEndPoint as IPEndPoint).Port;
305+
return receiveSocket;
306+
}
307+
218308
[Theory]
219309
[InlineData(null, null, null, true)]
220310
[InlineData(null, null, false, true)]

0 commit comments

Comments
 (0)