Skip to content

Commit

Permalink
improve handling of LLA in Quic (dotnet#90455)
Browse files Browse the repository at this point in the history
* QUIC LLA

* update

* linux

* line

* update

* Update src/libraries/Common/src/System/Net/IPEndPointExtensions.cs

Co-authored-by: Marie Píchová <11718369+ManickaP@users.noreply.github.com>

---------

Co-authored-by: Marie Píchová <11718369+ManickaP@users.noreply.github.com>
  • Loading branch information
wfurt and ManickaP authored Aug 14, 2023
1 parent bd4afce commit e079223
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 40 deletions.
4 changes: 3 additions & 1 deletion src/libraries/Common/src/System/Net/IPEndPointExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ public static IPAddress GetIPAddress(ReadOnlySpan<byte> socketAddressBuffer)
Span<byte> address = stackalloc byte[IPAddressParserStatics.IPv6AddressBytes];
uint scope;
SocketAddressPal.GetIPv6Address(socketAddressBuffer, address, out scope);
return new IPAddress(address, (long)scope);

// Clear scope if set for anything but Link Local address (always starts with fe80 first 10bits).
return new IPAddress(address, (address[0] == 0xFE && (address[1] & 0xC0) == 0x80) ? (long)scope : 0);
}
else if (family == AddressFamily.InterNetwork)
{
Expand Down
40 changes: 38 additions & 2 deletions src/libraries/Common/tests/System/Net/Configuration.Sockets.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,51 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;

namespace System.Net.Test.Common
{
public static partial class Configuration
{
public static partial class Sockets
{
public static Uri SocketServer => GetUriValue("DOTNET_TEST_NET_SOCKETS_SERVERURI", new Uri("http://" + DefaultAzureServer));
public static Uri SocketServer => GetUriValue("DOTNET_TEST_NET_SOCKETS_SERVERURI", new Uri("http://" + DefaultAzureServer));

public static string InvalidHost => GetValue("DOTNET_TEST_NET_SOCKETS_INVALIDSERVER", "notahostname.invalid.corp.microsoft.com");

public static IPAddress? LinkLocalAddress => GetIPv6LinkLocalAddress();

public static IEnumerable<object[]> LocalAddresses()
{
if (LinkLocalAddress != null)
{
yield return new[] { LinkLocalAddress };
}
if (Socket.OSSupportsIPv4)
{
yield return new[] { IPAddress.Loopback };
}
if (Socket.OSSupportsIPv6)
{
yield return new[] { IPAddress.IPv6Loopback };
}
}

public static string InvalidHost => GetValue("DOTNET_TEST_NET_SOCKETS_INVALIDSERVER", "notahostname.invalid.corp.microsoft.com");
private static IPAddress GetIPv6LinkLocalAddress() =>
NetworkInterface
.GetAllNetworkInterfaces()
.Where(i => !i.Description.StartsWith("PANGP Virtual Ethernet")) // This is a VPN adapter, but is reported as a regular Ethernet interface with
// a valid link-local address, but the link-local address doesn't actually work.
// So just manually filter it out.
.Where(i => !i.Name.Contains("Tailscale")) // Same as PANGP above.
.SelectMany(i => i.GetIPProperties().UnicastAddresses)
.Select(a => a.Address)
.Where(a => a.IsIPv6LinkLocal)
.FirstOrDefault();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

namespace System.Net.Http.Functional.Tests
{
using Configuration = System.Net.Test.Common.Configuration;

#if WINHTTPHANDLER_TEST
using HttpClientHandler = System.Net.Http.WinHttpClientHandler;
#endif
Expand Down Expand Up @@ -168,7 +170,7 @@ public async Task GetAsync_IPv6LinkLocalAddressUri_Success()
using HttpClientHandler handler = CreateHttpClientHandler(allowAllCertificates: true);
using HttpClient client = CreateHttpClient(handler);

var options = new GenericLoopbackOptions { Address = TestHelper.GetIPv6LinkLocalAddress() };
var options = new GenericLoopbackOptions { Address = Configuration.Sockets.LinkLocalAddress };
if (options.Address == null)
{
throw new SkipTestException("Unable to find valid IPv6 LL address.");
Expand Down
14 changes: 0 additions & 14 deletions src/libraries/Common/tests/System/Net/Http/TestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Net.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
Expand Down Expand Up @@ -104,18 +102,6 @@ public static Task WhenAllCompletedOrAnyFailedWithTimeout(int timeoutInMilliseco
public static Func<HttpRequestMessage, X509Certificate2, X509Chain, SslPolicyErrors, bool> AllowAllCertificates = (_, __, ___, ____) => true;
#endif

public static IPAddress GetIPv6LinkLocalAddress() =>
NetworkInterface
.GetAllNetworkInterfaces()
.Where(i => !i.Description.StartsWith("PANGP Virtual Ethernet")) // This is a VPN adapter, but is reported as a regular Ethernet interface with
// a valid link-local address, but the link-local address doesn't actually work.
// So just manually filter it out.
.Where(i => !i.Name.Contains("Tailscale")) // Same as PANGP above.
.SelectMany(i => i.GetIPProperties().UnicastAddresses)
.Select(a => a.Address)
.Where(a => a.IsIPv6LinkLocal)
.FirstOrDefault();

public static byte[] GenerateRandomContent(int size)
{
byte[] data = new byte[size];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Net.Test.Common;
using System.Threading.Tasks;

using Microsoft.DotNet.XUnitExtensions;
using Xunit;
using Xunit.Abstractions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,5 +304,28 @@ public async Task NetworkInterface_LoopbackInterfaceIndex_MatchesReceivedPackets
ipv6 ? NetworkInterface.IPv6LoopbackInterfaceIndex : NetworkInterface.LoopbackInterfaceIndex);
}
}

[ConditionalFact]
public void NetworkInterface_UnicastLLA_ScopeIdSet()
{
bool foundLla = false;
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
{
IPInterfaceProperties prop = nic.GetIPProperties();
foreach (UnicastIPAddressInformation info in prop.UnicastAddresses)
{
if (info.Address.IsIPv6LinkLocal)
{
foundLla = true;
Assert.NotEqual(0, info.Address.ScopeId);
}
}
}

if (!foundLla)
{
throw new SkipTestException("Did not find any LLA");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,23 @@

namespace System.Net.Quic.Tests
{
using Configuration = System.Net.Test.Common.Configuration;

[Collection(nameof(DisableParallelization))]
[ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported))]
public sealed class QuicConnectionTests : QuicTestBase
{
const int ExpectedErrorCode = 1234;
public static IEnumerable<object[]> LocalAddresses = Configuration.Sockets.LocalAddresses();

public QuicConnectionTests(ITestOutputHelper output) : base(output) { }

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task TestConnect(bool ipv6)
[MemberData(nameof(LocalAddresses))]
public async Task TestConnect(IPAddress address)
{
await using QuicListener listener = await CreateQuicListener(ipv6 ? IPAddress.IPv6Loopback : IPAddress.Loopback);
await using QuicListener listener = await CreateQuicListener(address);
Assert.Equal(address, listener.LocalEndPoint.Address);

var options = CreateQuicClientOptions(listener.LocalEndPoint);
ValueTask<QuicConnection> connectTask = CreateQuicConnection(options);
Expand All @@ -35,27 +38,19 @@ public async Task TestConnect(bool ipv6)
await using QuicConnection serverConnection = acceptTask.Result;
await using QuicConnection clientConnection = connectTask.Result;

IgnoreScopeIdIPEndpointComparer endPointComparer = new();
Assert.Equal(listener.LocalEndPoint, serverConnection.LocalEndPoint, endPointComparer);
Assert.Equal(listener.LocalEndPoint, clientConnection.RemoteEndPoint, endPointComparer);
Assert.Equal(clientConnection.LocalEndPoint, serverConnection.RemoteEndPoint, endPointComparer);
Assert.Equal(listener.LocalEndPoint, serverConnection.LocalEndPoint);
Assert.Equal(listener.LocalEndPoint, clientConnection.RemoteEndPoint);
if (PlatformDetection.IsWindows && address.IsIPv6LinkLocal)
{
// https://github.com/microsoft/msquic/issues/3813
Assert.Equal(clientConnection.LocalEndPoint, serverConnection.RemoteEndPoint);
}
Assert.Equal(ApplicationProtocol.ToString(), clientConnection.NegotiatedApplicationProtocol.ToString());
Assert.Equal(ApplicationProtocol.ToString(), serverConnection.NegotiatedApplicationProtocol.ToString());
Assert.Equal(options.ClientAuthenticationOptions.TargetHost, clientConnection.TargetHostName);
Assert.Equal(options.ClientAuthenticationOptions.TargetHost, serverConnection.TargetHostName);
}

private class IgnoreScopeIdIPEndpointComparer : IEqualityComparer<IPEndPoint>
{
public bool Equals(IPEndPoint x, IPEndPoint y)
{
byte[] xBytes = x.Address.GetAddressBytes();
byte[] yBytes = y.Address.GetAddressBytes();
return xBytes.AsSpan().SequenceEqual(yBytes) && x.Port == y.Port;
}
public int GetHashCode([DisallowNull] IPEndPoint obj) => obj.Port;
}

private static async Task<QuicStream> OpenAndUseStreamAsync(QuicConnection c)
{
QuicStream s = await c.OpenOutboundStreamAsync(QuicStreamType.Bidirectional);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<Compile Include="$(CommonTestPath)System\Net\Configuration.Certificates.Dynamic.cs" Link="TestCommon\System\Net\Configuration.Certificates.Dynamic.cs" />
<Compile Include="$(CommonTestPath)System\Net\Configuration.Http.cs" Link="Common\System\Net\Configuration.Http.cs" />
<Compile Include="$(CommonTestPath)System\Net\Configuration.Security.cs" Link="Common\System\Net\Configuration.Security.cs" />
<Compile Include="$(CommonTestPath)System\Net\Configuration.Sockets.cs" Link="Common\System\Net\Configuration.Sockets.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\PlatformSupport.cs" Link="TestCommon\System\Security\Cryptography\PlatformSupport.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\X509Certificates\CertificateAuthority.cs" Link="CommonTest\System\Security\Cryptography\X509Certificates\CertificateAuthority.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\X509Certificates\RevocationResponder.cs" Link="CommonTest\System\Security\Cryptography\X509Certificates\RevocationResponder.cs" />
Expand Down

0 comments on commit e079223

Please sign in to comment.