mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-18 22:55:08 +08:00
Address PR review feedback
* Docs/MigrationGuide.md: drop the new 'Packet capture diagnostics hooks' section. * Stack/Opc.Ua.Core/Stack/Tcp/IFrameCaptureSink.cs -> Stack/Opc.Ua.Core/Stack/Bindings/IFrameCaptureSink.cs. * InProcessCaptureSource: drop sync-over-async (GetAwaiter().GetResult()) on the hot path. Frame and key-material persistence is now fully async via a bounded System.Threading.Channels queue drained by a single background writer task; the IFrameCaptureSink callbacks and OnTokenObserved enqueue non-blocking and never wait on I/O. * Models/Models.cs: split into one file per type (FormatKind, CaptureSourceKind, CaptureSessionState, StartCaptureRequest, CaptureNowRequest, StartReplayRequest, CaptureSessionInfo, NetworkInterfaceInfo). Reformat every <summary>...</summary> block to multi-line per repo convention. * Properties/AssemblyInfo.cs: drop duplicated InternalsVisibleTo (already declared in the csproj <ItemGroup>); keep [assembly: CLSCompliant(false)]. * Opc.Ua.Diagnostics.Pcap.csproj: trim the long marketing-style Description to a single sentence. Validated: UA.slnx build 0 errors; 49/49 pcap library tests pass on net10.0.
This commit is contained in:
@@ -1928,10 +1928,6 @@ TimeSpan elapsed = m_timeProvider.GetElapsedTime(startTimestamp);
|
||||
|
||||
### Subscriptions and Transports
|
||||
|
||||
#### Packet capture diagnostics hooks
|
||||
|
||||
**Not source-breaking.** Packet-capture diagnostics add the `Opc.Ua.Bindings.IFrameCaptureSink` interface and the opt-in `UaSCUaBinaryChannel.FrameCaptureSink` property for observing raw wire-level chunks. `TcpListenerChannel.OnTokenActivated` is a new public server-side event that mirrors the existing client-side `ISecureChannel.OnTokenActivated` event. Both additions are purely additive; existing code does not need to change.
|
||||
|
||||
#### Durable subscriptions and reshaped Subscription tree
|
||||
|
||||
**Source-breaking.** Durable subscription support reshapes the subscription tree on both the client and the server. On the client side, the new public surface in `Libraries/Opc.Ua.Client/Subscription/` includes `ISubscription`, `ISubscriptionManager`, `SubscriptionOptions`, and `MonitoredItemOptions` - these are the V2 options-based shapes; the classic `Opc.Ua.Client.Subscription` continues to ship alongside them. On the server side, the new public surface in `Libraries/Opc.Ua.Server/Subscription/...` includes `DataChangeMonitoredItemQueue`, `EventMonitoredItemQueue`, `IDataChangeMonitoredItemQueue`, `IMonitoredItemQueueFactory`, `ISubscriptionStore`, `IStoredSubscription`, `StoredSubscription`, and `StoredMonitoredItem`.
|
||||
|
||||
@@ -32,6 +32,7 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
@@ -196,21 +197,33 @@ namespace Opc.Ua.Diagnostics.Pcap.Capture.Sources
|
||||
/// Shared base class for the two in-process capture sources. Manages
|
||||
/// the pcap + keylog file writers and the IFrameCaptureSink observer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Captured frames and key-material snapshots are forwarded through a
|
||||
/// bounded <see cref="System.Threading.Channels.Channel{T}"/> to a
|
||||
/// single background writer task so the event handlers (which run on
|
||||
/// the OPC UA send / receive path) never block on I/O. Overflow uses
|
||||
/// <see cref="System.Threading.Channels.BoundedChannelFullMode.DropOldest"/>
|
||||
/// so we degrade gracefully under sustained load.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public abstract class InProcessCaptureSourceBase : ICaptureSource
|
||||
{
|
||||
private const string kPcapFileName = "capture.pcap";
|
||||
private const string kKeyLogJsonFileName = "keys.uakeys.json";
|
||||
private const string kKeyLogTextFileName = "keys.uakeys.txt";
|
||||
private const int kQueueCapacity = 4096;
|
||||
|
||||
private readonly ILoggerFactory m_loggerFactory;
|
||||
private readonly ConcurrentBag<Action> m_disposeActions = [];
|
||||
private readonly Lock m_lock = new();
|
||||
|
||||
private PcapFileWriter? m_pcapWriter;
|
||||
private UaKeyLogJsonWriter? m_jsonKeyWriter;
|
||||
private UaKeyLogTextWriter? m_textKeyWriter;
|
||||
private string? m_sessionFolder;
|
||||
private CaptureFrameSinkObserver? m_sink;
|
||||
private Channel<CaptureWorkItem>? m_queue;
|
||||
private Task? m_workerTask;
|
||||
private long m_frameCount;
|
||||
private long m_byteCount;
|
||||
private long m_maxBytes;
|
||||
@@ -284,6 +297,17 @@ namespace Opc.Ua.Diagnostics.Pcap.Capture.Sources
|
||||
m_textKeyWriter = new UaKeyLogTextWriter(textPath);
|
||||
m_sink = new CaptureFrameSinkObserver(this);
|
||||
|
||||
m_queue = Channel.CreateBounded<CaptureWorkItem>(
|
||||
new BoundedChannelOptions(kQueueCapacity)
|
||||
{
|
||||
SingleReader = true,
|
||||
SingleWriter = false,
|
||||
FullMode = BoundedChannelFullMode.DropOldest
|
||||
});
|
||||
m_workerTask = Task.Run(
|
||||
() => RunQueueWorkerAsync(m_queue.Reader),
|
||||
CancellationToken.None);
|
||||
|
||||
ct.ThrowIfCancellationRequested();
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
@@ -303,6 +327,21 @@ namespace Opc.Ua.Diagnostics.Pcap.Capture.Sources
|
||||
}
|
||||
m_disposeActions.Clear();
|
||||
|
||||
// Signal the worker to drain and exit.
|
||||
m_queue?.Writer.TryComplete();
|
||||
if (m_workerTask is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await m_workerTask.ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// best effort drain
|
||||
}
|
||||
m_workerTask = null;
|
||||
}
|
||||
|
||||
if (m_pcapWriter is not null)
|
||||
{
|
||||
await m_pcapWriter.DisposeAsync().ConfigureAwait(false);
|
||||
@@ -410,25 +449,19 @@ namespace Opc.Ua.Diagnostics.Pcap.Capture.Sources
|
||||
|
||||
/// <summary>
|
||||
/// Called by the derived in-process source whenever a token is
|
||||
/// activated on one of its tracked channels.
|
||||
/// activated on one of its tracked channels. The snapshot is
|
||||
/// enqueued for asynchronous persistence; this method never
|
||||
/// blocks on I/O.
|
||||
/// </summary>
|
||||
protected void OnTokenObserved(ChannelKeyMaterial material)
|
||||
{
|
||||
UaKeyLogJsonWriter? jsonWriter = m_jsonKeyWriter;
|
||||
UaKeyLogTextWriter? textWriter = m_textKeyWriter;
|
||||
if (jsonWriter is null || textWriter is null)
|
||||
ArgumentNullException.ThrowIfNull(material);
|
||||
Channel<CaptureWorkItem>? queue = m_queue;
|
||||
if (queue is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
jsonWriter.AppendAsync(material, CancellationToken.None).AsTask().GetAwaiter().GetResult();
|
||||
textWriter.AppendAsync(material, CancellationToken.None).AsTask().GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Failed to persist key material snapshot.");
|
||||
}
|
||||
queue.Writer.TryWrite(CaptureWorkItem.ForKey(material));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -476,8 +509,8 @@ namespace Opc.Ua.Diagnostics.Pcap.Capture.Sources
|
||||
|
||||
private void OnFrameCaptured(uint channelId, ReadOnlySpan<byte> chunk, bool sent)
|
||||
{
|
||||
PcapFileWriter? writer = m_pcapWriter;
|
||||
if (writer is null || chunk.IsEmpty)
|
||||
Channel<CaptureWorkItem>? queue = m_queue;
|
||||
if (queue is null || chunk.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -491,24 +524,99 @@ namespace Opc.Ua.Diagnostics.Pcap.Capture.Sources
|
||||
Interlocked.CompareExchange(ref m_state, StateStopped, StateRunning);
|
||||
return;
|
||||
}
|
||||
// Copy the chunk bytes; the underlying buffer is pooled and
|
||||
// only valid for the duration of this call.
|
||||
byte[] packet = LoopbackFrameBuilder.Build(
|
||||
fromClient: sent,
|
||||
channelId: channelId,
|
||||
chunkBytes: chunk);
|
||||
DateTimeOffset now = DateTimeOffset.UtcNow;
|
||||
queue.Writer.TryWrite(CaptureWorkItem.ForFrame(DateTimeOffset.UtcNow, packet));
|
||||
}
|
||||
|
||||
private async Task RunQueueWorkerAsync(ChannelReader<CaptureWorkItem> reader)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (m_lock)
|
||||
while (await reader.WaitToReadAsync().ConfigureAwait(false))
|
||||
{
|
||||
writer.WriteAsync(now, packet, CancellationToken.None)
|
||||
.AsTask()
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
while (reader.TryRead(out CaptureWorkItem item))
|
||||
{
|
||||
await ProcessWorkItemAsync(item).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Failed to write captured frame to pcap.");
|
||||
Logger.LogError(ex, "In-process capture queue worker terminated unexpectedly.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessWorkItemAsync(CaptureWorkItem item)
|
||||
{
|
||||
if (item.Packet is not null)
|
||||
{
|
||||
PcapFileWriter? writer = m_pcapWriter;
|
||||
if (writer is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await writer.WriteAsync(item.Timestamp, item.Packet, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Failed to write captured frame to pcap.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (item.KeyMaterial is not null)
|
||||
{
|
||||
UaKeyLogJsonWriter? jsonWriter = m_jsonKeyWriter;
|
||||
UaKeyLogTextWriter? textWriter = m_textKeyWriter;
|
||||
if (jsonWriter is null || textWriter is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await jsonWriter.AppendAsync(item.KeyMaterial, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
await textWriter.AppendAsync(item.KeyMaterial, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Failed to persist key material snapshot.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct CaptureWorkItem
|
||||
{
|
||||
private CaptureWorkItem(
|
||||
DateTimeOffset timestamp,
|
||||
byte[]? packet,
|
||||
ChannelKeyMaterial? keyMaterial)
|
||||
{
|
||||
Timestamp = timestamp;
|
||||
Packet = packet;
|
||||
KeyMaterial = keyMaterial;
|
||||
}
|
||||
|
||||
public DateTimeOffset Timestamp { get; }
|
||||
public byte[]? Packet { get; }
|
||||
public ChannelKeyMaterial? KeyMaterial { get; }
|
||||
|
||||
public static CaptureWorkItem ForFrame(DateTimeOffset timestamp, byte[] packet)
|
||||
{
|
||||
return new CaptureWorkItem(timestamp, packet, keyMaterial: null);
|
||||
}
|
||||
|
||||
public static CaptureWorkItem ForKey(ChannelKeyMaterial material)
|
||||
{
|
||||
return new CaptureWorkItem(DateTimeOffset.UtcNow, packet: null, material);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2026 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
namespace Opc.Ua.Diagnostics.Pcap.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Convenience request for <c>capture_now</c>.
|
||||
/// </summary>
|
||||
public sealed class CaptureNowRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The underlying capture parameters.
|
||||
/// </summary>
|
||||
public StartCaptureRequest Start { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// How long to capture for before returning. Capped at 60 s.
|
||||
/// </summary>
|
||||
public int DurationSeconds { get; init; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Format to return the captured data in.
|
||||
/// </summary>
|
||||
public FormatKind Format { get; init; } = FormatKind.ServiceTimeline;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2026 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace Opc.Ua.Diagnostics.Pcap.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Status / metadata describing a capture session.
|
||||
/// </summary>
|
||||
public sealed class CaptureSessionInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Session id (assigned by <c>start_capture</c>).
|
||||
/// </summary>
|
||||
public string SessionId { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The source kind.
|
||||
/// </summary>
|
||||
public CaptureSourceKind Source { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Current state.
|
||||
/// </summary>
|
||||
public CaptureSessionState State { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// UTC timestamp the session entered Running state.
|
||||
/// </summary>
|
||||
public DateTimeOffset? StartedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// UTC timestamp the session stopped.
|
||||
/// </summary>
|
||||
public DateTimeOffset? StoppedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Captured chunk count.
|
||||
/// </summary>
|
||||
public long FrameCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Captured byte count.
|
||||
/// </summary>
|
||||
public long ByteCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Folder on disk hosting the session's artifacts.
|
||||
/// </summary>
|
||||
public string SessionFolder { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Path to the pcap file (if any).
|
||||
/// </summary>
|
||||
public string? PcapFilePath { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to the keylog file (if any).
|
||||
/// </summary>
|
||||
public string? KeyLogFilePath { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Failure message (if state is Failed).
|
||||
/// </summary>
|
||||
public string? Error { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2026 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
namespace Opc.Ua.Diagnostics.Pcap.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// State a <c>CaptureSession</c> can be in.
|
||||
/// </summary>
|
||||
public enum CaptureSessionState
|
||||
{
|
||||
/// <summary>
|
||||
/// The session has been created but not yet started.
|
||||
/// </summary>
|
||||
Starting = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The session is actively capturing.
|
||||
/// </summary>
|
||||
Running = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The session is in the middle of being stopped.
|
||||
/// </summary>
|
||||
Stopping = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The session stopped cleanly.
|
||||
/// </summary>
|
||||
Completed = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The session terminated because of an error.
|
||||
/// </summary>
|
||||
Failed = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The session has been disposed.
|
||||
/// </summary>
|
||||
Disposed = 5
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2026 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
namespace Opc.Ua.Diagnostics.Pcap.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies a kind of capture source the
|
||||
/// <c>CaptureSessionManager</c> can create.
|
||||
/// </summary>
|
||||
public enum CaptureSourceKind
|
||||
{
|
||||
/// <summary>
|
||||
/// Live capture from a network interface via SharpPcap (requires
|
||||
/// libpcap / Npcap and usually elevated privileges).
|
||||
/// </summary>
|
||||
Nic = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Passive in-process tap that hooks the channel
|
||||
/// <see cref="Opc.Ua.Bindings.IFrameCaptureSink"/> on each new
|
||||
/// <see cref="Opc.Ua.ITransportChannel"/> created by an OPC UA
|
||||
/// client.
|
||||
/// </summary>
|
||||
InProcessClient = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Passive in-process tap that hooks every server-side
|
||||
/// <see cref="Opc.Ua.Bindings.TcpServerChannel"/> created by a
|
||||
/// hosted OPC UA server.
|
||||
/// </summary>
|
||||
InProcessServer = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Replay-only source that re-reads an existing pcap file plus
|
||||
/// optional keylog from disk.
|
||||
/// </summary>
|
||||
Replay = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2026 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
namespace Opc.Ua.Diagnostics.Pcap.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Output formats supported by the diagnostics pipeline.
|
||||
/// </summary>
|
||||
public enum FormatKind
|
||||
{
|
||||
/// <summary>
|
||||
/// libpcap (binary) - openable in Wireshark.
|
||||
/// </summary>
|
||||
Pcap = 0,
|
||||
|
||||
/// <summary>
|
||||
/// PcapNg (binary) - openable in Wireshark.
|
||||
/// </summary>
|
||||
PcapNg = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Per-frame JSON.
|
||||
/// </summary>
|
||||
Json = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Per-frame CSV.
|
||||
/// </summary>
|
||||
Csv = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable per-frame text.
|
||||
/// </summary>
|
||||
Text = 4,
|
||||
|
||||
/// <summary>
|
||||
/// OPC UA-specific decoded service-call timeline (text).
|
||||
/// </summary>
|
||||
ServiceTimeline = 5
|
||||
}
|
||||
}
|
||||
@@ -1,303 +0,0 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2026 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Opc.Ua.Diagnostics.Pcap.Replay;
|
||||
|
||||
namespace Opc.Ua.Diagnostics.Pcap.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Output formats supported by the diagnostics pipeline.
|
||||
/// </summary>
|
||||
public enum FormatKind
|
||||
{
|
||||
/// <summary>libpcap (binary) - openable in Wireshark.</summary>
|
||||
Pcap = 0,
|
||||
|
||||
/// <summary>PcapNg (binary) - openable in Wireshark.</summary>
|
||||
PcapNg = 1,
|
||||
|
||||
/// <summary>Per-frame JSON.</summary>
|
||||
Json = 2,
|
||||
|
||||
/// <summary>Per-frame CSV.</summary>
|
||||
Csv = 3,
|
||||
|
||||
/// <summary>Human-readable per-frame text.</summary>
|
||||
Text = 4,
|
||||
|
||||
/// <summary>
|
||||
/// OPC UA-specific decoded service-call timeline (text).
|
||||
/// </summary>
|
||||
ServiceTimeline = 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies a kind of capture source the
|
||||
/// <c>CaptureSessionManager</c> can create.
|
||||
/// </summary>
|
||||
public enum CaptureSourceKind
|
||||
{
|
||||
/// <summary>
|
||||
/// Live capture from a network interface via SharpPcap (requires
|
||||
/// libpcap / Npcap and usually elevated privileges).
|
||||
/// </summary>
|
||||
Nic = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Passive in-process tap that hooks the channel
|
||||
/// <see cref="Opc.Ua.Bindings.IFrameCaptureSink"/> on each new
|
||||
/// <see cref="Opc.Ua.ITransportChannel"/> created by an OPC UA
|
||||
/// client.
|
||||
/// </summary>
|
||||
InProcessClient = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Passive in-process tap that hooks every server-side
|
||||
/// <see cref="Opc.Ua.Bindings.TcpServerChannel"/> created by a
|
||||
/// hosted OPC UA server.
|
||||
/// </summary>
|
||||
InProcessServer = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Replay-only source that re-reads an existing pcap file plus
|
||||
/// optional keylog from disk.
|
||||
/// </summary>
|
||||
Replay = 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State a <c>CaptureSession</c> can be in.
|
||||
/// </summary>
|
||||
public enum CaptureSessionState
|
||||
{
|
||||
/// <summary>The session has been created but not yet started.</summary>
|
||||
Starting = 0,
|
||||
|
||||
/// <summary>The session is actively capturing.</summary>
|
||||
Running = 1,
|
||||
|
||||
/// <summary>The session is in the middle of being stopped.</summary>
|
||||
Stopping = 2,
|
||||
|
||||
/// <summary>The session stopped cleanly.</summary>
|
||||
Completed = 3,
|
||||
|
||||
/// <summary>The session terminated because of an error.</summary>
|
||||
Failed = 4,
|
||||
|
||||
/// <summary>The session has been disposed.</summary>
|
||||
Disposed = 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parameters for <c>start_capture</c>.
|
||||
/// </summary>
|
||||
public sealed class StartCaptureRequest
|
||||
{
|
||||
/// <summary>The kind of source to create.</summary>
|
||||
public CaptureSourceKind Source { get; init; } = CaptureSourceKind.InProcessClient;
|
||||
|
||||
/// <summary>
|
||||
/// Network interface name (required for
|
||||
/// <see cref="CaptureSourceKind.Nic"/>). Use
|
||||
/// <c>list_interfaces</c> to enumerate.
|
||||
/// </summary>
|
||||
public string? InterfaceName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional BPF filter expression for
|
||||
/// <see cref="CaptureSourceKind.Nic"/>.
|
||||
/// </summary>
|
||||
public string? BpfFilter { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to open the NIC in promiscuous mode. Defaults to true
|
||||
/// when capturing on a real NIC.
|
||||
/// </summary>
|
||||
public bool? Promiscuous { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to an existing pcap file (required for
|
||||
/// <see cref="CaptureSourceKind.Replay"/>).
|
||||
/// </summary>
|
||||
public string? PcapFilePath { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to an existing keylog file (optional for
|
||||
/// <see cref="CaptureSourceKind.Replay"/>; without it the source
|
||||
/// can only replay the raw bytes, not decode them).
|
||||
/// </summary>
|
||||
public string? KeyLogFilePath { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Hard byte cap for the capture; the session is stopped when
|
||||
/// reached. Default 50 MB.
|
||||
/// </summary>
|
||||
public long? MaxBytes { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Hard frame cap for the capture; the session is stopped when
|
||||
/// reached. Default unlimited.
|
||||
/// </summary>
|
||||
public long? MaxFrames { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Hard duration cap for the capture (seconds). Default 30 min.
|
||||
/// </summary>
|
||||
public int? MaxDurationSeconds { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Folder under which the session's pcap and keylog files are
|
||||
/// written. If <c>null</c> a per-session temp folder is created.
|
||||
/// </summary>
|
||||
public string? SessionFolder { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience request for <c>capture_now</c>.
|
||||
/// </summary>
|
||||
public sealed class CaptureNowRequest
|
||||
{
|
||||
/// <summary>The underlying capture parameters.</summary>
|
||||
public StartCaptureRequest Start { get; init; } = new();
|
||||
|
||||
/// <summary>How long to capture for before returning. Capped at 60 s.</summary>
|
||||
public int DurationSeconds { get; init; } = 10;
|
||||
|
||||
/// <summary>Format to return the captured data in.</summary>
|
||||
public FormatKind Format { get; init; } = FormatKind.ServiceTimeline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parameters for replaying a pcap recording.
|
||||
/// </summary>
|
||||
public sealed class StartReplayRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Path to the pcap file to replay.
|
||||
/// </summary>
|
||||
public string PcapFilePath { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Optional keylog file used by mock-client replay for offline decoding.
|
||||
/// </summary>
|
||||
public string? KeyLogFilePath { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Replay mode.
|
||||
/// </summary>
|
||||
public ReplayMode Mode { get; init; } = ReplayMode.MockServer;
|
||||
|
||||
/// <summary>
|
||||
/// Listen URI scheme for mock-server replay. Defaults to <c>opc.tcp</c>.
|
||||
/// </summary>
|
||||
public string? ListenScheme { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Listen port for mock-server replay. Defaults to an ephemeral port.
|
||||
/// </summary>
|
||||
public int? ListenPort { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Target endpoint URL for mock-client replay.
|
||||
/// </summary>
|
||||
public string? TargetEndpointUrl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Replay speed multiplier.
|
||||
/// </summary>
|
||||
public double Speed { get; init; } = 1.0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Status / metadata describing a capture session.
|
||||
/// </summary>
|
||||
public sealed class CaptureSessionInfo
|
||||
{
|
||||
/// <summary>Session id (assigned by <c>start_capture</c>).</summary>
|
||||
public string SessionId { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>The source kind.</summary>
|
||||
public CaptureSourceKind Source { get; init; }
|
||||
|
||||
/// <summary>Current state.</summary>
|
||||
public CaptureSessionState State { get; init; }
|
||||
|
||||
/// <summary>UTC timestamp the session entered Running state.</summary>
|
||||
public DateTimeOffset? StartedAt { get; init; }
|
||||
|
||||
/// <summary>UTC timestamp the session stopped.</summary>
|
||||
public DateTimeOffset? StoppedAt { get; init; }
|
||||
|
||||
/// <summary>Captured chunk count.</summary>
|
||||
public long FrameCount { get; init; }
|
||||
|
||||
/// <summary>Captured byte count.</summary>
|
||||
public long ByteCount { get; init; }
|
||||
|
||||
/// <summary>Folder on disk hosting the session's artifacts.</summary>
|
||||
public string SessionFolder { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>Path to the pcap file (if any).</summary>
|
||||
public string? PcapFilePath { get; init; }
|
||||
|
||||
/// <summary>Path to the keylog file (if any).</summary>
|
||||
public string? KeyLogFilePath { get; init; }
|
||||
|
||||
/// <summary>Failure message (if state is Failed).</summary>
|
||||
public string? Error { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a local network interface returned by
|
||||
/// <c>list_interfaces</c>.
|
||||
/// </summary>
|
||||
public sealed class NetworkInterfaceInfo
|
||||
{
|
||||
/// <summary>Device name as understood by libpcap (e.g. <c>\Device\NPF_{guid}</c>).</summary>
|
||||
public string Name { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>Friendly name (e.g. <c>Ethernet</c>).</summary>
|
||||
public string? FriendlyName { get; init; }
|
||||
|
||||
/// <summary>Free-form description.</summary>
|
||||
public string? Description { get; init; }
|
||||
|
||||
/// <summary>Bound IP addresses.</summary>
|
||||
public IReadOnlyList<string> Addresses { get; init; } = [];
|
||||
|
||||
/// <summary>The link-layer type (e.g. <c>Ethernet</c>).</summary>
|
||||
public string? LinkType { get; init; }
|
||||
|
||||
/// <summary>Whether this is the loopback interface.</summary>
|
||||
public bool IsLoopback { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2026 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Opc.Ua.Diagnostics.Pcap.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a local network interface returned by
|
||||
/// <c>list_interfaces</c>.
|
||||
/// </summary>
|
||||
public sealed class NetworkInterfaceInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Device name as understood by libpcap (e.g.
|
||||
/// <c>\Device\NPF_{guid}</c>).
|
||||
/// </summary>
|
||||
public string Name { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Friendly name (e.g. <c>Ethernet</c>).
|
||||
/// </summary>
|
||||
public string? FriendlyName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Free-form description.
|
||||
/// </summary>
|
||||
public string? Description { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Bound IP addresses.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> Addresses { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The link-layer type (e.g. <c>Ethernet</c>).
|
||||
/// </summary>
|
||||
public string? LinkType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is the loopback interface.
|
||||
/// </summary>
|
||||
public bool IsLoopback { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2026 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
namespace Opc.Ua.Diagnostics.Pcap.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters for <c>start_capture</c>.
|
||||
/// </summary>
|
||||
public sealed class StartCaptureRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The kind of source to create.
|
||||
/// </summary>
|
||||
public CaptureSourceKind Source { get; init; } = CaptureSourceKind.InProcessClient;
|
||||
|
||||
/// <summary>
|
||||
/// Network interface name (required for
|
||||
/// <see cref="CaptureSourceKind.Nic"/>). Use
|
||||
/// <c>list_interfaces</c> to enumerate.
|
||||
/// </summary>
|
||||
public string? InterfaceName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional BPF filter expression for
|
||||
/// <see cref="CaptureSourceKind.Nic"/>.
|
||||
/// </summary>
|
||||
public string? BpfFilter { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to open the NIC in promiscuous mode. Defaults to true
|
||||
/// when capturing on a real NIC.
|
||||
/// </summary>
|
||||
public bool? Promiscuous { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to an existing pcap file (required for
|
||||
/// <see cref="CaptureSourceKind.Replay"/>).
|
||||
/// </summary>
|
||||
public string? PcapFilePath { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to an existing keylog file (optional for
|
||||
/// <see cref="CaptureSourceKind.Replay"/>; without it the source
|
||||
/// can only replay the raw bytes, not decode them).
|
||||
/// </summary>
|
||||
public string? KeyLogFilePath { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Hard byte cap for the capture; the session is stopped when
|
||||
/// reached. Default 50 MB.
|
||||
/// </summary>
|
||||
public long? MaxBytes { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Hard frame cap for the capture; the session is stopped when
|
||||
/// reached. Default unlimited.
|
||||
/// </summary>
|
||||
public long? MaxFrames { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Hard duration cap for the capture (seconds). Default 30 min.
|
||||
/// </summary>
|
||||
public int? MaxDurationSeconds { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Folder under which the session's pcap and keylog files are
|
||||
/// written. If <c>null</c> a per-session temp folder is created.
|
||||
/// </summary>
|
||||
public string? SessionFolder { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/* ========================================================================
|
||||
* Copyright (c) 2005-2026 The OPC Foundation, Inc. All rights reserved.
|
||||
*
|
||||
* OPC Foundation MIT License 1.00
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* The complete license agreement can be found here:
|
||||
* http://opcfoundation.org/License/MIT/1.00/
|
||||
* ======================================================================*/
|
||||
|
||||
using Opc.Ua.Diagnostics.Pcap.Replay;
|
||||
|
||||
namespace Opc.Ua.Diagnostics.Pcap.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters for replaying a pcap recording.
|
||||
/// </summary>
|
||||
public sealed class StartReplayRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Path to the pcap file to replay.
|
||||
/// </summary>
|
||||
public string PcapFilePath { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Optional keylog file used by mock-client replay for offline
|
||||
/// decoding.
|
||||
/// </summary>
|
||||
public string? KeyLogFilePath { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Replay mode.
|
||||
/// </summary>
|
||||
public ReplayMode Mode { get; init; } = ReplayMode.MockServer;
|
||||
|
||||
/// <summary>
|
||||
/// Listen URI scheme for mock-server replay. Defaults to
|
||||
/// <c>opc.tcp</c>.
|
||||
/// </summary>
|
||||
public string? ListenScheme { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Listen port for mock-server replay. Defaults to an ephemeral
|
||||
/// port.
|
||||
/// </summary>
|
||||
public int? ListenPort { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Target endpoint URL for mock-client replay.
|
||||
/// </summary>
|
||||
public string? TargetEndpointUrl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Replay speed multiplier.
|
||||
/// </summary>
|
||||
public double Speed { get; init; } = 1.0;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<AssemblyName>$(AssemblyPrefix).Diagnostics.Pcap</AssemblyName>
|
||||
<PackageId>$(PackagePrefix).Opc.Ua.Diagnostics.Pcap</PackageId>
|
||||
<RootNamespace>Opc.Ua.Diagnostics.Pcap</RootNamespace>
|
||||
<Description>OPC UA packet capture, dissection and replay engine. Reuses the stack's UaSCUaBinaryChannel decoder to support every security profile the stack supports. Includes libpcap I/O, SharpPcap NIC capture, in-process taps, key-log writers, mock-server / mock-client replay and pcap/pcapng/json/csv/text/service-timeline formatters.</Description>
|
||||
<Description>OPC UA packet capture, dissection and replay engine.</Description>
|
||||
<IsPackable>true</IsPackable>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@@ -28,7 +28,5 @@
|
||||
* ======================================================================*/
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: CLSCompliant(false)]
|
||||
[assembly: InternalsVisibleTo("Opc.Ua.Diagnostics.Pcap.Tests")]
|
||||
|
||||
Reference in New Issue
Block a user