From 9850ea41148346e1f22d6dcfe00d37350fc9617a Mon Sep 17 00:00:00 2001 From: smdn Date: Fri, 11 Apr 2025 20:10:33 +0900 Subject: [PATCH 1/2] add AddressTableEntry.Equals(IPAddress) overload that allows IPv4-mapped IPv6 addresses to be compared as IPv4 addresses --- .../AddressTableEntry.cs | 26 +++++- .../AddressTableEntry.cs | 88 +++++++++++++++++++ 2 files changed, 111 insertions(+), 3 deletions(-) diff --git a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressTables/AddressTableEntry.cs b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressTables/AddressTableEntry.cs index d1bd402..d39330d 100644 --- a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressTables/AddressTableEntry.cs +++ b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressTables/AddressTableEntry.cs @@ -73,11 +73,31 @@ public bool Equals(AddressTableEntry other) => DefaultEqualityComparer.Equals(this, other); public bool Equals(IPAddress? other) + => Equals(other, shouldConsiderIPv4MappedIPv6Address: false); + + /// + /// Indicates whether the member is equal to the IP address passed to the parameter. + /// + /// The to be compared with the . + /// + /// Specifies whether or not to be aware that the IP address to be an IPv4-mapped IPv6 address or not when comparing IP addresses. + /// + /// + /// if the is equal to the parameter; otherwise, . + /// + public bool Equals(IPAddress? other, bool shouldConsiderIPv4MappedIPv6Address) { - if (IPAddress is null) - return other is null; + if (other is null) + return IPAddress is null; + + if (shouldConsiderIPv4MappedIPv6Address) { + if (other.IsIPv4MappedToIPv6 && other.MapToIPv4().Equals(IPAddress)) + return true; + if (IPAddress is not null && IPAddress.IsIPv4MappedToIPv6 && other.Equals(IPAddress.MapToIPv4())) + return true; + } - return IPAddress.Equals(other); + return other.Equals(IPAddress); } public bool Equals(PhysicalAddress? other) diff --git a/tests/Smdn.Net.AddressResolution/Smdn.Net.AddressTables/AddressTableEntry.cs b/tests/Smdn.Net.AddressResolution/Smdn.Net.AddressTables/AddressTableEntry.cs index aa3401e..22d1b88 100644 --- a/tests/Smdn.Net.AddressResolution/Smdn.Net.AddressTables/AddressTableEntry.cs +++ b/tests/Smdn.Net.AddressResolution/Smdn.Net.AddressTables/AddressTableEntry.cs @@ -388,6 +388,94 @@ private static System.Collections.IEnumerable YieldTestCases_Equals_OfIPAddress( public void Equals_OfIPAddress(AddressTableEntry entry, IPAddress? ipAddress, bool expected) => Assert.That(entry.Equals(ipAddress), Is.EqualTo(expected)); + private static System.Collections.IEnumerable YieldTestCases_Equals_OfIPAddress_ShouldConsiderIPv4MappedIPv6Address() + { + var ipv4AddressEntry = new AddressTableEntry( + ipAddress: TestIPAddress, + physicalAddress: null, + isPermanent: true, + state: AddressTableEntryState.None, + interfaceId: null + ); + var ipv4MappedIPv6AddressEntry = new AddressTableEntry( + ipAddress: TestIPAddress.MapToIPv6(), + physicalAddress: null, + isPermanent: true, + state: AddressTableEntryState.None, + interfaceId: null + ); + IPAddress? nullIPAddress = null; + + foreach (var shouldConsiderIPv4MappedIPv6Address in new[] { true, false }) { + yield return new object?[] { + ipv4AddressEntry, + nullIPAddress, + shouldConsiderIPv4MappedIPv6Address, + false + }; + yield return new object?[] { + ipv4AddressEntry, + ipv4AddressEntry.IPAddress, + shouldConsiderIPv4MappedIPv6Address, + true + }; + yield return new object?[] { + ipv4AddressEntry, + ipv4AddressEntry.IPAddress!.MapToIPv6(), // compare with IPv4-mapped IPv6 address + shouldConsiderIPv4MappedIPv6Address, + shouldConsiderIPv4MappedIPv6Address + }; + } + + foreach (var shouldConsiderIPv4MappedIPv6Address in new[] { true, false }) { + yield return new object?[] { + ipv4MappedIPv6AddressEntry, + nullIPAddress, + shouldConsiderIPv4MappedIPv6Address, + false + }; + yield return new object?[] { + ipv4MappedIPv6AddressEntry, + ipv4MappedIPv6AddressEntry.IPAddress, + shouldConsiderIPv4MappedIPv6Address, + true + }; + yield return new object?[] { + ipv4MappedIPv6AddressEntry, + ipv4MappedIPv6AddressEntry.IPAddress!.MapToIPv4(), // compare with IPv4-mapped IPv6 address + shouldConsiderIPv4MappedIPv6Address, + shouldConsiderIPv4MappedIPv6Address + }; + } + + var nullIPAddressEntry = default(AddressTableEntry); + + foreach (var shouldConsiderIPv4MappedIPv6Address in new[] { true, false }) { + yield return new object?[] { + nullIPAddressEntry, + nullIPAddress, + shouldConsiderIPv4MappedIPv6Address, + true + }; + yield return new object?[] { + nullIPAddressEntry, + TestIPAddress, + shouldConsiderIPv4MappedIPv6Address, + false + }; + yield return new object?[] { + nullIPAddressEntry, + TestIPAddress.MapToIPv6(), + shouldConsiderIPv4MappedIPv6Address, + false + }; + } + } + + [TestCaseSource(nameof(YieldTestCases_Equals_OfIPAddress_ShouldConsiderIPv4MappedIPv6Address))] + public void Equals_OfIPAddress_ShouldConsiderIPv4MappedIPv6Address(AddressTableEntry entry, IPAddress? ipAddress, bool shouldConsiderIPv4MappedIPv6Address, bool expected) + => Assert.That(entry.Equals(ipAddress, shouldConsiderIPv4MappedIPv6Address), Is.EqualTo(expected)); + [Test] public void Equals_PhysicalAddressNull() { From e4f53e5ba926d0cb3591483a8f97e9b99129c5ac Mon Sep 17 00:00:00 2001 From: smdn Date: Fri, 11 Apr 2025 20:17:43 +0900 Subject: [PATCH 2/2] add property MacAddressResolver.ShouldResolveIPv4MappedIPv6Address to be able to resolve IPv4-mapped IPv6 addresses --- .../MacAddressResolver.AddressResolution.cs | 2 +- .../MacAddressResolver.cs | 8 +++++ .../MacAddressResolver.ResolveAsync.cs | 29 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.AddressResolution.cs b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.AddressResolution.cs index 1abfcfb..bc69f5c 100644 --- a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.AddressResolution.cs +++ b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.AddressResolution.cs @@ -33,7 +33,7 @@ CancellationToken cancellationToken if (!FilterAddressTableEntryForAddressResolution(entry)) return false; - if (!entry.Equals(ipAddress)) + if (!entry.Equals(ipAddress, shouldConsiderIPv4MappedIPv6Address: ShouldResolveIPv4MappedIPv6Address)) return false; // ignore the entry that is marked as invalidated diff --git a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.cs b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.cs index f795c03..635afd6 100644 --- a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.cs +++ b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.cs @@ -161,6 +161,14 @@ timeStampForFullScan is null || // not performed yet private const int DefaultParallelCountForRefreshInvalidatedAddresses = 3; private SemaphoreSlim partialScanSemaphore; + /// + /// Gets or sets a value indicating whether the address resolution to be aware or not to be aware that + /// the IP address is an IPv4-mapped IPv6 address when resolving IP address to MAC address. + /// + /// + /// + public bool ShouldResolveIPv4MappedIPv6Address { get; set; } + /// /// Initializes a new instance of the class. /// diff --git a/tests/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.ResolveAsync.cs b/tests/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.ResolveAsync.cs index 015e1ce..f4fe498 100644 --- a/tests/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.ResolveAsync.cs +++ b/tests/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.ResolveAsync.cs @@ -628,4 +628,33 @@ await resolver.ResolveMacAddressToIPAddressAsync(entryToResolve.PhysicalAddress! Is.EqualTo(expectedEntry.IPAddress) ); } + + [Test] + public async Task ResolveIPAddressToMacAddressAsync_ShouldResolveIPv4MappedIPv6Address( + [Values("127.0.0.1", "192.0.2.0")] string ipv4AddressString, + [Values] bool shouldResolveIPv4MappedIPv6Address + ) + { + using var resolver = CreateNullNetworkScannerMacAddressResolver( + new StaticAddressTable([ + new(IPAddress.Parse(ipv4AddressString), PhysicalAddress.Parse("00-00-5E-00-53-00"), false, AddressTableEntryState.Reachable, "wlan0"), + ]) + ); + + resolver.ShouldResolveIPv4MappedIPv6Address = shouldResolveIPv4MappedIPv6Address; + + Assert.That( + await resolver.ResolveIPAddressToMacAddressAsync(IPAddress.Parse(ipv4AddressString)), + Is.Not.Null + ); + + var ipv4MappedIPv6AddressString = $"::ffff:{ipv4AddressString}"; + + Assert.That( + await resolver.ResolveIPAddressToMacAddressAsync(IPAddress.Parse(ipv4MappedIPv6AddressString)), + shouldResolveIPv4MappedIPv6Address + ? Is.Not.Null + : Is.Null + ); + } }