Smdn.Net.AddressResolution version 1.0.0-preview4
Pre-release
Pre-release
·
199 commits
to main
since this release
Released package
Release notes
The full release notes are available at gist.
Change log
Change log in this release:
- 2023-03-15 update package version
- 2023-03-15 add APIs to mark unresolvable addresses as invalid and update them manually
- 2023-03-14 simplify log message format
API changes
API changes in this release:
diff --git a/doc/api-list/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution-net6.0.apilist.cs b/doc/api-list/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution-net6.0.apilist.cs
index 0fcaecd..8de9632 100644
--- a/doc/api-list/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution-net6.0.apilist.cs
+++ b/doc/api-list/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution-net6.0.apilist.cs
@@ -1,77 +1,88 @@
-// Smdn.Net.AddressResolution.dll (Smdn.Net.AddressResolution-1.0.0-preview3)
+// Smdn.Net.AddressResolution.dll (Smdn.Net.AddressResolution-1.0.0-preview4)
// Name: Smdn.Net.AddressResolution
// AssemblyVersion: 1.0.0.0
-// InformationalVersion: 1.0.0-preview3+b0d0efec19a7bb78ed612cabd93d8e805a1a32f8
+// InformationalVersion: 1.0.0-preview4+f17248a683dc95a5f8a1d3f3ec79fb49b8b2852f
// TargetFramework: .NETCoreApp,Version=v6.0
// Configuration: Release
// Referenced assemblies:
// Microsoft.Extensions.DependencyInjection.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// Microsoft.Extensions.Logging.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
+// System.Collections.Concurrent, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// System.ComponentModel, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// System.Diagnostics.Process, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// System.Linq, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// System.Memory, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
// System.Net.NetworkInformation, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// System.Net.Primitives, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// System.Runtime.InteropServices.RuntimeInformation, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
#nullable enable annotations
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Smdn.Net.AddressResolution;
namespace Smdn.Net {
public static class PhysicalAddressExtensions {
public static string ToMacAddressString(this PhysicalAddress hardwareAddress, char delimiter = ':') {}
}
}
namespace Smdn.Net.AddressResolution {
public interface IAddressResolver<TAddress, TResolvedAddress> {
+ void Invalidate(TResolvedAddress resolvedAddress);
ValueTask<TResolvedAddress> ResolveAsync(TAddress address, CancellationToken cancellationToken);
}
public abstract class MacAddressResolver :
IAddressResolver<IPAddress, PhysicalAddress>,
IAddressResolver<PhysicalAddress, IPAddress>,
IDisposable
{
protected static readonly PhysicalAddress AllZeroMacAddress; // = "000000000000"
public static MacAddressResolver Null { get; }
public static MacAddressResolver Create(MacAddressResolverOptions? options = null, IServiceProvider? serviceProvider = null) {}
protected MacAddressResolver(ILogger? logger = null) {}
+ public abstract bool HasInvalidated { get; }
protected ILogger? Logger { get; }
protected virtual void Dispose(bool disposing) {}
public void Dispose() {}
+ public void Invalidate(IPAddress resolvedIPAddress) {}
+ public void Invalidate(PhysicalAddress resolvedMacAddress) {}
+ protected abstract void InvalidateCore(IPAddress resolvedIPAddress);
+ protected abstract void InvalidateCore(PhysicalAddress resolvedMacAddress);
public ValueTask RefreshCacheAsync(CancellationToken cancellationToken = default) {}
protected virtual ValueTask RefreshCacheAsyncCore(CancellationToken cancellationToken) {}
+ public ValueTask RefreshInvalidatedCacheAsync(CancellationToken cancellationToken = default) {}
+ protected virtual ValueTask RefreshInvalidatedCacheAsyncCore(CancellationToken cancellationToken) {}
public ValueTask<PhysicalAddress?> ResolveIPAddressToMacAddressAsync(IPAddress ipAddress, CancellationToken cancellationToken = default) {}
protected abstract ValueTask<PhysicalAddress?> ResolveIPAddressToMacAddressAsyncCore(IPAddress ipAddress, CancellationToken cancellationToken);
public ValueTask<IPAddress?> ResolveMacAddressToIPAddressAsync(PhysicalAddress macAddress, CancellationToken cancellationToken = default) {}
protected abstract ValueTask<IPAddress?> ResolveMacAddressToIPAddressAsyncCore(PhysicalAddress macAddress, CancellationToken cancellationToken);
+ void IAddressResolver<IPAddress, PhysicalAddress>.Invalidate(PhysicalAddress resolvedAddress) {}
ValueTask<PhysicalAddress?> IAddressResolver<IPAddress, PhysicalAddress>.ResolveAsync(IPAddress address, CancellationToken cancellationToken) {}
+ void IAddressResolver<PhysicalAddress, IPAddress>.Invalidate(IPAddress resolvedAddress) {}
ValueTask<IPAddress?> IAddressResolver<PhysicalAddress, IPAddress>.ResolveAsync(PhysicalAddress address, CancellationToken cancellationToken) {}
protected void ThrowIfDisposed() {}
}
public sealed class MacAddressResolverOptions {
public static readonly MacAddressResolverOptions Default; // = "Smdn.Net.AddressResolution.MacAddressResolverOptions"
public MacAddressResolverOptions() {}
public string? NmapTargetSpecification { get; init; }
- public TimeSpan ProcfsArpScanInterval { get; init; }
+ public TimeSpan ProcfsArpFullScanInterval { get; init; }
}
}
// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.1.0.
// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution-netstandard2.0.apilist.cs b/doc/api-list/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution-netstandard2.0.apilist.cs
index 44fe7a4..f9d6a20 100644
--- a/doc/api-list/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution-netstandard2.0.apilist.cs
+++ b/doc/api-list/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution-netstandard2.0.apilist.cs
@@ -1,72 +1,82 @@
-// Smdn.Net.AddressResolution.dll (Smdn.Net.AddressResolution-1.0.0-preview3)
+// Smdn.Net.AddressResolution.dll (Smdn.Net.AddressResolution-1.0.0-preview4)
// Name: Smdn.Net.AddressResolution
// AssemblyVersion: 1.0.0.0
-// InformationalVersion: 1.0.0-preview3+b0d0efec19a7bb78ed612cabd93d8e805a1a32f8
+// InformationalVersion: 1.0.0-preview4+f17248a683dc95a5f8a1d3f3ec79fb49b8b2852f
// TargetFramework: .NETStandard,Version=v2.0
// Configuration: Release
// Referenced assemblies:
// Microsoft.Bcl.AsyncInterfaces, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
// Microsoft.Extensions.DependencyInjection.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// Microsoft.Extensions.Logging.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
// netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
#nullable enable annotations
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Smdn.Net.AddressResolution;
namespace Smdn.Net {
public static class PhysicalAddressExtensions {
public static string ToMacAddressString(this PhysicalAddress hardwareAddress, char delimiter = ':') {}
}
}
namespace Smdn.Net.AddressResolution {
public interface IAddressResolver<TAddress, TResolvedAddress> {
+ void Invalidate(TResolvedAddress resolvedAddress);
ValueTask<TResolvedAddress> ResolveAsync(TAddress address, CancellationToken cancellationToken);
}
public abstract class MacAddressResolver :
IAddressResolver<IPAddress, PhysicalAddress>,
IAddressResolver<PhysicalAddress, IPAddress>,
IDisposable
{
protected static readonly PhysicalAddress AllZeroMacAddress; // = "000000000000"
public static MacAddressResolver Null { get; }
public static MacAddressResolver Create(MacAddressResolverOptions? options = null, IServiceProvider? serviceProvider = null) {}
protected MacAddressResolver(ILogger? logger = null) {}
+ public abstract bool HasInvalidated { get; }
protected ILogger? Logger { get; }
protected virtual void Dispose(bool disposing) {}
public void Dispose() {}
+ public void Invalidate(IPAddress resolvedIPAddress) {}
+ public void Invalidate(PhysicalAddress resolvedMacAddress) {}
+ protected abstract void InvalidateCore(IPAddress resolvedIPAddress);
+ protected abstract void InvalidateCore(PhysicalAddress resolvedMacAddress);
public ValueTask RefreshCacheAsync(CancellationToken cancellationToken = default) {}
protected virtual ValueTask RefreshCacheAsyncCore(CancellationToken cancellationToken) {}
+ public ValueTask RefreshInvalidatedCacheAsync(CancellationToken cancellationToken = default) {}
+ protected virtual ValueTask RefreshInvalidatedCacheAsyncCore(CancellationToken cancellationToken) {}
public ValueTask<PhysicalAddress?> ResolveIPAddressToMacAddressAsync(IPAddress ipAddress, CancellationToken cancellationToken = default) {}
protected abstract ValueTask<PhysicalAddress?> ResolveIPAddressToMacAddressAsyncCore(IPAddress ipAddress, CancellationToken cancellationToken);
public ValueTask<IPAddress?> ResolveMacAddressToIPAddressAsync(PhysicalAddress macAddress, CancellationToken cancellationToken = default) {}
protected abstract ValueTask<IPAddress?> ResolveMacAddressToIPAddressAsyncCore(PhysicalAddress macAddress, CancellationToken cancellationToken);
+ void IAddressResolver<IPAddress, PhysicalAddress>.Invalidate(PhysicalAddress resolvedAddress) {}
ValueTask<PhysicalAddress?> IAddressResolver<IPAddress, PhysicalAddress>.ResolveAsync(IPAddress address, CancellationToken cancellationToken) {}
+ void IAddressResolver<PhysicalAddress, IPAddress>.Invalidate(IPAddress resolvedAddress) {}
ValueTask<IPAddress?> IAddressResolver<PhysicalAddress, IPAddress>.ResolveAsync(PhysicalAddress address, CancellationToken cancellationToken) {}
protected void ThrowIfDisposed() {}
}
public sealed class MacAddressResolverOptions {
public static readonly MacAddressResolverOptions Default; // = "Smdn.Net.AddressResolution.MacAddressResolverOptions"
public MacAddressResolverOptions() {}
public string? NmapTargetSpecification { get; init; }
- public TimeSpan ProcfsArpScanInterval { get; init; }
+ public TimeSpan ProcfsArpFullScanInterval { get; init; }
}
}
// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.1.0.
// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution-netstandard2.1.apilist.cs b/doc/api-list/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution-netstandard2.1.apilist.cs
index 7ac71b4..565bdda 100644
--- a/doc/api-list/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution-netstandard2.1.apilist.cs
+++ b/doc/api-list/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution-netstandard2.1.apilist.cs
@@ -1,70 +1,80 @@
-// Smdn.Net.AddressResolution.dll (Smdn.Net.AddressResolution-1.0.0-preview3)
+// Smdn.Net.AddressResolution.dll (Smdn.Net.AddressResolution-1.0.0-preview4)
// Name: Smdn.Net.AddressResolution
// AssemblyVersion: 1.0.0.0
-// InformationalVersion: 1.0.0-preview3+b0d0efec19a7bb78ed612cabd93d8e805a1a32f8
+// InformationalVersion: 1.0.0-preview4+f17248a683dc95a5f8a1d3f3ec79fb49b8b2852f
// TargetFramework: .NETStandard,Version=v2.1
// Configuration: Release
// Referenced assemblies:
// Microsoft.Extensions.DependencyInjection.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// Microsoft.Extensions.Logging.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
#nullable enable annotations
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Smdn.Net.AddressResolution;
namespace Smdn.Net {
public static class PhysicalAddressExtensions {
public static string ToMacAddressString(this PhysicalAddress hardwareAddress, char delimiter = ':') {}
}
}
namespace Smdn.Net.AddressResolution {
public interface IAddressResolver<TAddress, TResolvedAddress> {
+ void Invalidate(TResolvedAddress resolvedAddress);
ValueTask<TResolvedAddress> ResolveAsync(TAddress address, CancellationToken cancellationToken);
}
public abstract class MacAddressResolver :
IAddressResolver<IPAddress, PhysicalAddress>,
IAddressResolver<PhysicalAddress, IPAddress>,
IDisposable
{
protected static readonly PhysicalAddress AllZeroMacAddress; // = "000000000000"
public static MacAddressResolver Null { get; }
public static MacAddressResolver Create(MacAddressResolverOptions? options = null, IServiceProvider? serviceProvider = null) {}
protected MacAddressResolver(ILogger? logger = null) {}
+ public abstract bool HasInvalidated { get; }
protected ILogger? Logger { get; }
protected virtual void Dispose(bool disposing) {}
public void Dispose() {}
+ public void Invalidate(IPAddress resolvedIPAddress) {}
+ public void Invalidate(PhysicalAddress resolvedMacAddress) {}
+ protected abstract void InvalidateCore(IPAddress resolvedIPAddress);
+ protected abstract void InvalidateCore(PhysicalAddress resolvedMacAddress);
public ValueTask RefreshCacheAsync(CancellationToken cancellationToken = default) {}
protected virtual ValueTask RefreshCacheAsyncCore(CancellationToken cancellationToken) {}
+ public ValueTask RefreshInvalidatedCacheAsync(CancellationToken cancellationToken = default) {}
+ protected virtual ValueTask RefreshInvalidatedCacheAsyncCore(CancellationToken cancellationToken) {}
public ValueTask<PhysicalAddress?> ResolveIPAddressToMacAddressAsync(IPAddress ipAddress, CancellationToken cancellationToken = default) {}
protected abstract ValueTask<PhysicalAddress?> ResolveIPAddressToMacAddressAsyncCore(IPAddress ipAddress, CancellationToken cancellationToken);
public ValueTask<IPAddress?> ResolveMacAddressToIPAddressAsync(PhysicalAddress macAddress, CancellationToken cancellationToken = default) {}
protected abstract ValueTask<IPAddress?> ResolveMacAddressToIPAddressAsyncCore(PhysicalAddress macAddress, CancellationToken cancellationToken);
+ void IAddressResolver<IPAddress, PhysicalAddress>.Invalidate(PhysicalAddress resolvedAddress) {}
ValueTask<PhysicalAddress?> IAddressResolver<IPAddress, PhysicalAddress>.ResolveAsync(IPAddress address, CancellationToken cancellationToken) {}
+ void IAddressResolver<PhysicalAddress, IPAddress>.Invalidate(IPAddress resolvedAddress) {}
ValueTask<IPAddress?> IAddressResolver<PhysicalAddress, IPAddress>.ResolveAsync(PhysicalAddress address, CancellationToken cancellationToken) {}
protected void ThrowIfDisposed() {}
}
public sealed class MacAddressResolverOptions {
public static readonly MacAddressResolverOptions Default; // = "Smdn.Net.AddressResolution.MacAddressResolverOptions"
public MacAddressResolverOptions() {}
public string? NmapTargetSpecification { get; init; }
- public TimeSpan ProcfsArpScanInterval { get; init; }
+ public TimeSpan ProcfsArpFullScanInterval { get; init; }
}
}
// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.2.1.0.
// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.2.0.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
Full changes
Full changes in this release:
diff --git a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution.Arp/ProcfsArpMacAddressResolver.cs b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution.Arp/ProcfsArpMacAddressResolver.cs
index b5e05af..5693ea8 100644
--- a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution.Arp/ProcfsArpMacAddressResolver.cs
+++ b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution.Arp/ProcfsArpMacAddressResolver.cs
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2022 smdn <smdn@smdn.jp>
// SPDX-License-Identifier: MIT
using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
@@ -34,13 +36,31 @@ internal partial class ProcfsArpMacAddressResolver : MacAddressResolver {
);
}
+ private readonly struct None { }
+
+ private class ConcurrentSet<T> : ConcurrentDictionary<T, None>
+ where T : notnull
+ {
+ public ConcurrentSet()
+ {
+ }
+
+ public void Add(T key)
+ => AddOrUpdate(key: key, addValue: default, updateValueFactory: static (key, old) => default);
+ }
+
/*
* instance members
*/
- private DateTime lastArpScanAt = DateTime.MinValue;
- private readonly TimeSpan arpScanInterval;
+ private DateTime lastArpFullScanAt = DateTime.MinValue;
+ private readonly TimeSpan arpFullScanInterval;
+
+ private bool HasArpFullScanIntervalElapsed => lastArpFullScanAt + arpFullScanInterval <= DateTime.Now;
- private bool HasArpScanIntervalElapsed => lastArpScanAt + arpScanInterval <= DateTime.Now;
+ private readonly ConcurrentSet<IPAddress> invalidatedIPAddressSet = new();
+ private readonly ConcurrentSet<PhysicalAddress> invalidatedMacAddressSet = new();
+
+ public override bool HasInvalidated => !(invalidatedIPAddressSet.IsEmpty && invalidatedMacAddressSet.IsEmpty);
public ProcfsArpMacAddressResolver(
MacAddressResolverOptions options,
@@ -48,7 +68,7 @@ internal partial class ProcfsArpMacAddressResolver : MacAddressResolver {
)
: base(logger)
{
- arpScanInterval = options.ProcfsArpScanInterval;
+ arpFullScanInterval = options.ProcfsArpFullScanInterval;
}
protected override async ValueTask<PhysicalAddress?> ResolveIPAddressToMacAddressAsyncCore(
@@ -56,8 +76,8 @@ internal partial class ProcfsArpMacAddressResolver : MacAddressResolver {
CancellationToken cancellationToken
)
{
- if (HasArpScanIntervalElapsed)
- await ArpScanAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
+ if (HasArpFullScanIntervalElapsed)
+ await ArpFullScanAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
ArpTableEntry priorCandidate = default;
ArpTableEntry candidate = default;
@@ -67,6 +87,9 @@ internal partial class ProcfsArpMacAddressResolver : MacAddressResolver {
Logger,
cancellationToken
).ConfigureAwait(false)) {
+ if (invalidatedMacAddressSet.ContainsKey(entry.HardwareAddress!))
+ continue; // ignore the entry that is marked as invalidated
+
if (entry.IsPermanentOrComplete) {
// prefer permanent or complete entry
priorCandidate = entry;
@@ -88,8 +111,8 @@ internal partial class ProcfsArpMacAddressResolver : MacAddressResolver {
CancellationToken cancellationToken
)
{
- if (HasArpScanIntervalElapsed)
- await ArpScanAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
+ if (HasArpFullScanIntervalElapsed)
+ await ArpFullScanAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
ArpTableEntry priorCandidate = default;
ArpTableEntry candidate = default;
@@ -99,6 +122,9 @@ internal partial class ProcfsArpMacAddressResolver : MacAddressResolver {
Logger,
cancellationToken
).ConfigureAwait(false)) {
+ if (invalidatedIPAddressSet.ContainsKey(entry.IPAddress!))
+ continue; // ignore the entry that is marked as invalidated
+
if (entry.IsPermanentOrComplete) {
// prefer permanent or complete entry
priorCandidate = entry;
@@ -115,6 +141,12 @@ internal partial class ProcfsArpMacAddressResolver : MacAddressResolver {
: priorCandidate.IPAddress;
}
+ protected override void InvalidateCore(IPAddress resolvedIPAddress)
+ => invalidatedIPAddressSet.Add(resolvedIPAddress);
+
+ protected override void InvalidateCore(PhysicalAddress resolvedMacAddress)
+ => invalidatedMacAddressSet.Add(resolvedMacAddress);
+
protected override ValueTask RefreshCacheAsyncCore(
CancellationToken cancellationToken = default
)
@@ -124,21 +156,69 @@ internal partial class ProcfsArpMacAddressResolver : MacAddressResolver {
ValueTask.FromCanceled(cancellationToken)
#else
ValueTaskShim.FromCanceled(cancellationToken)
+#endif
+ : ArpFullScanAsync(cancellationToken: cancellationToken);
+
+ private async ValueTask ArpFullScanAsync(CancellationToken cancellationToken)
+ {
+ Logger?.LogDebug("Performing ARP full scan");
+
+ await ArpFullScanAsyncCore(cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ invalidatedIPAddressSet.Clear();
+ invalidatedMacAddressSet.Clear();
+
+ lastArpFullScanAt = DateTime.Now;
+ }
+
+ protected virtual ValueTask ArpFullScanAsyncCore(CancellationToken cancellationToken)
+ {
+ Logger?.LogWarning("ARP scan is not supported in this class.");
+
+ return default;
+ }
+
+ protected override ValueTask RefreshInvalidatedCacheAsyncCore(
+ CancellationToken cancellationToken = default
+ )
+ => cancellationToken.IsCancellationRequested
+ ?
+#if SYSTEM_THREADING_TASKS_VALUETASK_FROMCANCELED
+ ValueTask.FromCanceled(cancellationToken)
+#else
+ ValueTaskShim.FromCanceled(cancellationToken)
#endif
: ArpScanAsync(cancellationToken: cancellationToken);
private async ValueTask ArpScanAsync(CancellationToken cancellationToken)
{
- Logger?.LogDebug("[/proc/net/arp] Performing ARP scan");
+ Logger?.LogDebug("Performing ARP scan for invalidated targets.");
+
+ var invalidatedIPAddresses = invalidatedIPAddressSet.Keys;
+ var invalidatedMacAddresses = invalidatedMacAddressSet.Keys;
- await ArpScanAsyncCore(cancellationToken: cancellationToken).ConfigureAwait(false);
+ Logger?.LogTrace("Invalidated IP addresses: {InvalidatedIPAddresses}", string.Join(" ", invalidatedIPAddresses));
+ Logger?.LogTrace("Invalidated MAC addresses: {InvalidatedMACAddresses}", string.Join(" ", invalidatedMacAddresses));
- lastArpScanAt = DateTime.Now;
+ await ArpScanAsyncCore(
+ invalidatedIPAddresses: invalidatedIPAddresses,
+ invalidatedMacAddresses: invalidatedMacAddresses,
+ cancellationToken: cancellationToken
+ ).ConfigureAwait(false);
+
+ invalidatedIPAddressSet.Clear();
+ invalidatedMacAddressSet.Clear();
+
+ lastArpFullScanAt = DateTime.Now;
}
- protected virtual ValueTask ArpScanAsyncCore(CancellationToken cancellationToken)
+ protected virtual ValueTask ArpScanAsyncCore(
+ IEnumerable<IPAddress> invalidatedIPAddresses,
+ IEnumerable<PhysicalAddress> invalidatedMacAddresses,
+ CancellationToken cancellationToken
+ )
{
- Logger?.LogWarning($"[{nameof(ProcfsArpMacAddressResolver)}] ARP scan is not supported in this class.");
+ Logger?.LogWarning("ARP scan is not supported in this class.");
return default;
}
diff --git a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution.Arp/ProcfsArpNmapScanMacAddressResolver.cs b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution.Arp/ProcfsArpNmapScanMacAddressResolver.cs
index 5418064..983f739 100644
--- a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution.Arp/ProcfsArpNmapScanMacAddressResolver.cs
+++ b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution.Arp/ProcfsArpNmapScanMacAddressResolver.cs
@@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Net;
+using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
@@ -40,7 +42,45 @@ internal sealed class ProcfsArpNmapScanMacAddressResolver : ProcfsArpMacAddressR
?? throw new ArgumentException($"{nameof(options.NmapTargetSpecification)} must be specified with {nameof(MacAddressResolverOptions)}");
}
- protected override async ValueTask ArpScanAsyncCore(CancellationToken cancellationToken)
+ protected override ValueTask ArpFullScanAsyncCore(CancellationToken cancellationToken)
+ => NmapScanAsync(
+ nmapOptionTargetSpecification: nmapTargetSpecification,
+ logger: Logger,
+ cancellationToken: cancellationToken
+ );
+
+ protected override ValueTask ArpScanAsyncCore(
+ IEnumerable<IPAddress> invalidatedIPAddresses,
+ IEnumerable<PhysicalAddress> invalidatedMacAddresses,
+ CancellationToken cancellationToken
+ )
+ {
+ if (invalidatedMacAddresses.Any()) {
+ // perform full scan
+ return NmapScanAsync(
+ nmapOptionTargetSpecification: nmapTargetSpecification,
+ logger: Logger,
+ cancellationToken: cancellationToken
+ );
+ }
+
+ // perform scan for specific target IPs
+ var nmapOptionTargetSpecification = string.Join(" ", invalidatedIPAddresses);
+
+ return nmapOptionTargetSpecification.Length == 0
+ ? default // do nothing
+ : NmapScanAsync(
+ nmapOptionTargetSpecification: nmapOptionTargetSpecification,
+ logger: Logger,
+ cancellationToken: cancellationToken
+ );
+ }
+
+ private static async ValueTask NmapScanAsync(
+ string nmapOptionTargetSpecification,
+ ILogger? logger,
+ CancellationToken cancellationToken
+ )
{
// -sn: Ping Scan - disable port scan
// -n: Never do DNS resolution
@@ -51,13 +91,13 @@ internal sealed class ProcfsArpNmapScanMacAddressResolver : ProcfsArpMacAddressR
var nmapProcessStartInfo = new ProcessStartInfo() {
FileName = lazyPathToNmap.Value,
- Arguments = nmapOptions + nmapTargetSpecification,
+ Arguments = nmapOptions + nmapOptionTargetSpecification,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
};
- Logger?.LogDebug(
+ logger?.LogDebug(
"[nmap] {ProcessStartInfoFileName} {ProcessStartInfoArguments}",
nmapProcessStartInfo.FileName,
nmapProcessStartInfo.Arguments
@@ -76,7 +116,7 @@ internal sealed class ProcfsArpNmapScanMacAddressResolver : ProcfsArpMacAddressR
nmapProcess.WaitForExit(); // TODO: cacellation
#endif
- if (Logger is not null) {
+ if (logger is not null) {
const LogLevel logLevelForStandardOutput = LogLevel.Trace;
const LogLevel logLevelForStandardError = LogLevel.Error;
@@ -87,7 +127,7 @@ internal sealed class ProcfsArpNmapScanMacAddressResolver : ProcfsArpMacAddressR
}
foreach (var (stdio, logLevel) in EnumerateLogTarget(nmapProcess.StandardOutput, nmapProcess.StandardError)) {
- if (!Logger.IsEnabled(logLevel))
+ if (!logger.IsEnabled(logLevel))
continue;
for (; ;) {
@@ -96,13 +136,13 @@ internal sealed class ProcfsArpNmapScanMacAddressResolver : ProcfsArpMacAddressR
if (line is null)
break;
- Logger.Log(logLevel, "[nmap] {Line}", line);
+ logger.Log(logLevel, "[nmap] {Line}", line);
}
}
}
}
catch (Exception ex) {
- Logger?.LogError(ex, "[nmap] failed to perform ARP scanning");
+ logger?.LogError(ex, "[nmap] failed to perform ARP scanning");
}
}
}
diff --git a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution.csproj b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution.csproj
index afb36d6..c23de68 100644
--- a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution.csproj
+++ b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution.csproj
@@ -6,7 +6,7 @@ SPDX-License-Identifier: MIT
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net6.0</TargetFrameworks>
<VersionPrefix>1.0.0</VersionPrefix>
- <VersionSuffix>preview3</VersionSuffix>
+ <VersionSuffix>preview4</VersionSuffix>
<!-- <PackageValidationBaselineVersion>1.0.0</PackageValidationBaselineVersion> -->
<Nullable>enable</Nullable>
<NoWarn>CA1848</NoWarn> <!-- use the LoggerMessage delegates instead -->
diff --git a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/IAddressResolver.cs b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/IAddressResolver.cs
index 51cb4c3..b172e3d 100644
--- a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/IAddressResolver.cs
+++ b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/IAddressResolver.cs
@@ -8,4 +8,5 @@ namespace Smdn.Net.AddressResolution;
public interface IAddressResolver<TAddress, TResolvedAddress> {
/// <returns>An resolved address. <see langword="null"/> if address could not be resolved.</returns>
public ValueTask<TResolvedAddress?> ResolveAsync(TAddress address, CancellationToken cancellationToken);
+ public void Invalidate(TResolvedAddress resolvedAddress);
}
diff --git a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.cs b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.cs
index 33241a9..b9b01c5 100644
--- a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.cs
+++ b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolver.cs
@@ -38,6 +38,7 @@ public abstract class MacAddressResolver :
/*
* instance members
*/
+ public abstract bool HasInvalidated { get; }
protected ILogger? Logger { get; }
protected MacAddressResolver(
@@ -81,6 +82,11 @@ public abstract class MacAddressResolver :
cancellationToken: cancellationToken
);
+ void IAddressResolver<IPAddress, PhysicalAddress>.Invalidate(
+ PhysicalAddress resolvedAddress
+ )
+ => Invalidate(resolvedMacAddress: resolvedAddress);
+
#pragma warning disable SA1305
public ValueTask<PhysicalAddress?> ResolveIPAddressToMacAddressAsync(
IPAddress ipAddress,
@@ -100,8 +106,6 @@ public abstract class MacAddressResolver :
if (ipAddress is null)
throw new ArgumentNullException(nameof(ipAddress));
- // TODO: validate IP address
-
return ResolveAsync();
async ValueTask<PhysicalAddress?> ResolveAsync()
@@ -127,6 +131,20 @@ public abstract class MacAddressResolver :
);
#pragma warning restore SA1305
+ public void Invalidate(PhysicalAddress resolvedMacAddress)
+ {
+ if (resolvedMacAddress is null)
+ throw new ArgumentNullException(nameof(resolvedMacAddress));
+
+ ThrowIfDisposed();
+
+ Logger?.LogDebug("Invalidating {MacAddress}", resolvedMacAddress.ToMacAddressString());
+
+ InvalidateCore(resolvedMacAddress);
+ }
+
+ protected abstract void InvalidateCore(PhysicalAddress resolvedMacAddress);
+
/*
* PhysicalAddress -> IPAddress
*/
@@ -139,6 +157,11 @@ public abstract class MacAddressResolver :
cancellationToken: cancellationToken
);
+ void IAddressResolver<PhysicalAddress, IPAddress>.Invalidate(
+ IPAddress resolvedAddress
+ )
+ => Invalidate(resolvedIPAddress: resolvedAddress);
+
public ValueTask<IPAddress?> ResolveMacAddressToIPAddressAsync(
PhysicalAddress macAddress,
CancellationToken cancellationToken = default
@@ -163,8 +186,6 @@ public abstract class MacAddressResolver :
return ValueTaskShim.FromResult<IPAddress?>(null);
#endif
- // TODO: validate MAC address
-
return ResolveAsync();
async ValueTask<IPAddress?> ResolveAsync()
@@ -188,6 +209,20 @@ public abstract class MacAddressResolver :
CancellationToken cancellationToken
);
+ public void Invalidate(IPAddress resolvedIPAddress)
+ {
+ if (resolvedIPAddress is null)
+ throw new ArgumentNullException(nameof(resolvedIPAddress));
+
+ ThrowIfDisposed();
+
+ Logger?.LogDebug("Invalidating {IPAddress}", resolvedIPAddress);
+
+ InvalidateCore(resolvedIPAddress);
+ }
+
+ protected abstract void InvalidateCore(IPAddress resolvedIPAddress);
+
/*
* other virtual/abstract members
*/
@@ -217,4 +252,31 @@ public abstract class MacAddressResolver :
#else
default;
#endif
+
+ public ValueTask RefreshInvalidatedCacheAsync(
+ CancellationToken cancellationToken = default
+ )
+ {
+ if (cancellationToken.IsCancellationRequested)
+#if SYSTEM_THREADING_TASKS_VALUETASK_FROMCANCELED
+ return ValueTask.FromCanceled(cancellationToken);
+#else
+ return ValueTaskShim.FromCanceled(cancellationToken);
+#endif
+
+ ThrowIfDisposed();
+
+ return RefreshInvalidatedCacheAsyncCore(cancellationToken);
+ }
+
+ protected virtual ValueTask RefreshInvalidatedCacheAsyncCore(
+ CancellationToken cancellationToken
+ )
+ =>
+ // do nothing in this class
+#if SYSTEM_THREADING_TASKS_VALUETASK_COMPLETEDTASK
+ ValueTask.CompletedTask;
+#else
+ default;
+#endif
}
diff --git a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolverOptions.cs b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolverOptions.cs
index a4f2c46..9525810 100644
--- a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolverOptions.cs
+++ b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/MacAddressResolverOptions.cs
@@ -12,5 +12,5 @@ public sealed class MacAddressResolverOptions {
/// </summary>
public string? NmapTargetSpecification { get; init; }
- public TimeSpan ProcfsArpScanInterval { get; init; } = TimeSpan.FromMinutes(15.0);
+ public TimeSpan ProcfsArpFullScanInterval { get; init; } = TimeSpan.FromMinutes(15.0);
}
diff --git a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/NullMacAddressResolver.cs b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/NullMacAddressResolver.cs
index 2a3ee21..d78d3e9 100644
--- a/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/NullMacAddressResolver.cs
+++ b/src/Smdn.Net.AddressResolution/Smdn.Net.AddressResolution/NullMacAddressResolver.cs
@@ -6,6 +6,8 @@ using System.Threading.Tasks;
namespace Smdn.Net.AddressResolution;
internal sealed class NullMacAddressResolver : MacAddressResolver {
+ public override bool HasInvalidated => false;
+
internal NullMacAddressResolver()
: base(logger: null)
{
@@ -39,4 +41,14 @@ internal sealed class NullMacAddressResolver : MacAddressResolver {
#else
ValueTaskShim.FromResult<IPAddress?>(null);
#endif
+
+ protected override void InvalidateCore(IPAddress ipAddress)
+ {
+ // do nothing
+ }
+
+ protected override void InvalidateCore(PhysicalAddress macAddress)
+ {
+ // do nothing
+ }
}
Notes
Full Changelog: releases/Smdn.Net.AddressResolution-1.0.0-preview3...releases/Smdn.Net.AddressResolution-1.0.0-preview4