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/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.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
+ );
+ }
}
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()
{