Skip to content

Commit

Permalink
Connection diagnostics and key set logging capabilities (#2279)
Browse files Browse the repository at this point in the history
* Connection diagnostics and key set logging capabilities
* Add TTL and retention settings for messages and writer groups
* Ensure complex types are loaded when synchronizing subscription state
  • Loading branch information
marcschier authored Jul 3, 2024
1 parent d55cd10 commit f8a0fea
Show file tree
Hide file tree
Showing 36 changed files with 833 additions and 150 deletions.
20 changes: 10 additions & 10 deletions docs/opc-publisher/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -862,23 +862,22 @@ This section lists the diagnostics APi provided by OPC Publisher providing
name.


<a name="resetallclients"></a>
#### ResetAllClients
<a name="getconnectiondiagnostic"></a>
#### GetConnectionDiagnostic
```
GET /v2/reset
GET /v2/diagnostics/connections
```


##### Description
Can be used to reset all established connections causing a full reconnect and recreate of all subscriptions.
Get connection diagnostic information for all connections. The first set of diagnostics are the diagnostics active for all connections, continue reading to get updates.


##### Responses

|HTTP Code|Description|Schema|
|---|---|---|
|**200**|The operation was successful.|No Content|
|**408**|The operation timed out.|[ProblemDetails](definitions.md#problemdetails)|
|**200**|The operation was successful.|[ConnectionDiagnosticModelIAsyncEnumerable](definitions.md#connectiondiagnosticmodeliasyncenumerable)|
|**500**|An unexpected error occurred|[ProblemDetails](definitions.md#problemdetails)|


Expand All @@ -888,22 +887,23 @@ Can be used to reset all established connections causing a full reconnect and re
* `application/x-msgpack`


<a name="settracemode"></a>
#### SetTraceMode
<a name="resetallclients"></a>
#### ResetAllClients
```
GET /v2/tracemode
GET /v2/reset
```


##### Description
Can be used to set trace mode for all established connections. Call within a minute to keep trace mode up or else trace mode will be disabled again after 1 minute. Enabling and resetting tracemode will cause a reconnect of the client.
Can be used to reset all established connections causing a full reconnect and recreate of all subscriptions.


##### Responses

|HTTP Code|Description|Schema|
|---|---|---|
|**200**|The operation was successful.|No Content|
|**408**|The operation timed out.|[ProblemDetails](definitions.md#problemdetails)|
|**500**|An unexpected error occurred|[ProblemDetails](definitions.md#problemdetails)|


Expand Down
5 changes: 5 additions & 0 deletions docs/opc-publisher/definitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ Condition handling options model
|**updateInterval** <br>*optional*|Time interval for sending pending interval updates in seconds.|integer (int32)|


<a name="connectiondiagnosticmodeliasyncenumerable"></a>
### ConnectionDiagnosticModelIAsyncEnumerable
*Type* : object


<a name="connectionmodel"></a>
### ConnectionModel
Connection model
Expand Down
Binary file added docs/opc-publisher/media/keyset.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/opc-publisher/media/keyset2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/opc-publisher/media/keyset3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 13 additions & 6 deletions docs/opc-publisher/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1166,21 +1166,24 @@
}
}
},
"/v2/tracemode": {
"/v2/diagnostics/connections": {
"get": {
"tags": [
"Diagnostics"
],
"summary": "SetTraceMode",
"description": "Can be used to set trace mode for all established connections. Call within a minute to keep trace mode up or else trace mode will be disabled again after 1 minute. Enabling and resetting tracemode will cause a reconnect of the client.",
"operationId": "SetTraceMode",
"summary": "GetConnectionDiagnostic",
"description": "Get connection diagnostic information for all connections. The first set of diagnostics are the diagnostics active for all connections, continue reading to get updates.",
"operationId": "GetConnectionDiagnostic",
"produces": [
"application/json",
"application/x-msgpack"
],
"responses": {
"200": {
"description": "The operation was successful."
"description": "The operation was successful.",
"schema": {
"$ref": "#/definitions/ConnectionDiagnosticModelIAsyncEnumerable"
}
},
"500": {
"description": "An unexpected error occurred",
Expand Down Expand Up @@ -4918,6 +4921,10 @@
},
"additionalProperties": false
},
"ConnectionDiagnosticModelIAsyncEnumerable": {
"type": "object",
"additionalProperties": false
},
"ConnectionModel": {
"description": "Connection model",
"type": "object",
Expand Down Expand Up @@ -9314,4 +9321,4 @@
"description": "\r\n\r\n This section contains the API to configure data set writers and writer\r\n groups inside OPC Publisher. It supersedes the configuration API.\r\n Applications should use one or the other, but not both at the same\r\n time.\r\n \r\n\r\n\r\n The method name for all transports other than HTTP (which uses the shown\r\n HTTP methods and resource uris) is the name of the subsection header.\r\n To use the version specific method append \"_V1\" or \"_V2\" to the method\r\n name.\r\n "
}
]
}
}
21 changes: 21 additions & 0 deletions docs/opc-publisher/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ In this document you find information about
- [IoT Hub Metrics](#iot-hub-metrics)
- [Use Azure IoT Explorer](#use-azure-iot-explorer)
- [Restart the module](#restart-the-module)
- [Analyzing network capture files](#analyzing-network-capture-files)
- [Limits and contributing factors](#limits-and-contributing-factors)
- [Debugging Discovery](#debugging-discovery)

Expand Down Expand Up @@ -166,6 +167,26 @@ iotedge list

You can also troubleshoot the OPC Publisher module remotely through the Azure Portal. Select the module inside the respective IoT Edge device in the IoT Edge blade and click on the Troubleshooting button to see logs and restart the module remotely.

## Analyzing network capture files

The issue might be between the OPC Publisher OPC UA client and the OPC UA server you are using. A network capture provides the definitive answer as to where the issue lies. To capture network traffic you can use [Wireshark](https://www.wireshark.org/) or [tshark](https://tshark.dev/setup/install/) (aka. command line wireshark) and capture a .pcap file for analysis. An example of how to capture network traces in a docker environment can be found [here](../../deploy/docker/with-pcap-capture.yaml). To analyze OPC UA traffic, you must load the .pcap or .pcapng file you captured with Wireshark.

Follow [these instructions](https://opcconnect.opcfoundation.org/2017/02/analyzing-opc-ua-communications-with-wireshark/#:~:text=Wireshark%20has%20a%20built-in%20filter%20for%20OPC%20UA%2C,fairly%20easy%20to%20capture%20and%20analyze%20the%20conversation.) to visualize the OPC UA traffic between OPC Publisher and your OPC UA server.

If your connection to the OPC UA server is encrypted (using security) you must use Wireshark 4.3 (not the stable version!). You will also need to capture the client and server keys. You can start OPC Publisher with the `-ksf` [command line argument](./commandline.md), providing an optional folder path that can be accessed after running OPC Publisher (volume mount). The folder is structured like this:

![Key Set Log Folder](./media/keyset.png)

The folder path starts with the port number, because the port number needs to be configured in the OPC UA protocol page in Wireshark.

![Wireshark configuration](./media/keyset2.png)

Find the connection you want to trace. You can open the `opcua_debug.log` file in one of the sub folders to identify the connection. The log file shows the remote and local endpoints as well as a summary of the connection configuration that was used to connect OPC Publisher. Once you have found the right folder, load the `opcua_debug.txt` file using the protocol page, then save and Wireshark will be able to decrypt traffic.

![Wireshark Analysis](./media/keyset3.png)

> IMPORTANT: While the keys that are logged are scoped to the connection and cannot be re-used, it still presents a security risk, therefore, ensure to clean up the logs when you are done, and do not use the feature in production.
## Limits and contributing factors

IoT Edge runs on various distributions of Linux and leverages Docker containers for all workloads, including the built-in IoT Edge modules `edgeAgent` and `edgeHub`. Docker has settings for the maximum host resources that each container can consume. These can be displayed by the Docker command "stats" (run "docker stats" from the terminal on the gateway host):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------

namespace Azure.IIoT.OpcUa.Publisher.Models
{
using System;
using System.Runtime.Serialization;

/// <summary>
/// Channel token. Can be used to decrypt encrypted
/// capture files.
/// </summary>
[DataContract]
public record class ChannelDiagnosticModel
{
/// <summary>
/// The id assigned to the channel that the token
/// belongs to.
/// </summary>
[DataMember(Name = "channelId", Order = 0)]
public required uint ChannelId { get; init; }

/// <summary>
/// The id assigned to the token.
/// </summary>
[DataMember(Name = "tokenId", Order = 1)]
public required uint TokenId { get; init; }

/// <summary>
/// When the token was created by the server
/// (refers to the server's clock).
/// </summary>
[DataMember(Name = "createdAt", Order = 2)]
public required DateTime CreatedAt { get; init; }

/// <summary>
/// The lifetime of the token
/// </summary>
[DataMember(Name = "lifetime", Order = 3)]
public required TimeSpan Lifetime { get; init; }

/// <summary>
/// Client keys
/// </summary>
[DataMember(Name = "client", Order = 4,
EmitDefaultValue = false)]
public ChannelKeyModel? Client { get; init; }

/// <summary>
/// Server keys
/// </summary>
[DataMember(Name = "aerver", Order = 5,
EmitDefaultValue = false)]
public ChannelKeyModel? Server { get; init; }
}
}
35 changes: 35 additions & 0 deletions src/Azure.IIoT.OpcUa.Publisher.Models/src/ChannelKeyModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------

namespace Azure.IIoT.OpcUa.Publisher.Models
{
using System.Collections.Generic;
using System.Runtime.Serialization;

/// <summary>
/// Channel token key model.
/// </summary>
[DataContract]
public record class ChannelKeyModel
{
/// <summary>
/// Iv
/// </summary>
[DataMember(Name = "iv", Order = 0)]
public required IReadOnlyList<byte> Iv { get; init; }

/// <summary>
/// Key
/// </summary>
[DataMember(Name = "key", Order = 1)]
public required IReadOnlyList<byte> Key { get; init; }

/// <summary>
/// Signature length
/// </summary>
[DataMember(Name = "sigLen", Order = 2)]
public required int SigLen { get; init; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------

namespace Azure.IIoT.OpcUa.Publisher.Models
{
using System;
using System.Runtime.Serialization;

/// <summary>
/// Connection / session diagnostics model
/// </summary>
[DataContract]
public record class ConnectionDiagnosticModel
{
/// <summary>
/// Timestamp of the diagnostic information
/// </summary>
[DataMember(Name = "timeStamp", Order = 0)]
public required DateTimeOffset TimeStamp { get; init; }

/// <summary>
/// The connection information specified by user.
/// </summary>
[DataMember(Name = "connection", Order = 1)]
public required ConnectionModel Connection { get; init; }

/// <summary>
/// Effective remote ip address used for the
/// connection if connected. Empty if disconnected.
/// </summary>
[DataMember(Name = "remoteIpAddress", Order = 2,
EmitDefaultValue = false)]
public string? RemoteIpAddress { get; init; }

/// <summary>
/// The effective remote port used when connected,
/// null if disconnected.
/// </summary>
[DataMember(Name = "remotePort", Order = 3,
EmitDefaultValue = false)]
public int? RemotePort { get; init; }

/// <summary>
/// Effective local ip address used for the connection
/// if connected. Empty if disconnected.
/// </summary>
[DataMember(Name = "localIpAddress", Order = 4,
EmitDefaultValue = false)]
public string? LocalIpAddress { get; init; }

/// <summary>
/// The effective local port used when connected,
/// null if disconnected.
/// </summary>
[DataMember(Name = "localPort", Order = 5,
EmitDefaultValue = false)]
public int? LocalPort { get; init; }

/// <summary>
/// Channel diagnostics
/// </summary>
[DataMember(Name = "channelDiagnostics", Order = 6,
EmitDefaultValue = false)]
public ChannelDiagnosticModel? ChannelDiagnostics { get; init; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

namespace Azure.IIoT.OpcUa.Publisher.Models
{
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,10 +400,42 @@ public sealed record class PublishedNodesEntryModel
EmitDefaultValue = false)]
public bool? DataSetFetchDisplayNames { get; set; }

/// <summary>
/// Default time to live for messages sent through
/// the writer group if the transport supports it.
/// </summary>
[DataMember(Name = "WriterGroupMessageTtlTimepan", Order = 49,
EmitDefaultValue = false)]
public TimeSpan? WriterGroupMessageTtlTimepan { get; set; }

/// <summary>
/// Default message retention setting for messages sent
/// through the writer group if the transport supports it.
/// </summary>
[DataMember(Name = "WriterGroupMessageRetention", Order = 50,
EmitDefaultValue = false)]
public bool? WriterGroupMessageRetention { get; set; }

/// <summary>
/// Message time to live for messages sent by the
/// writer if the transport supports it.
/// </summary>
[DataMember(Name = "MessageTtlTimespan", Order = 52,
EmitDefaultValue = false)]
public TimeSpan? MessageTtlTimespan { get; set; }

/// <summary>
/// Message retention setting for messages sent by
/// the writer if the transport supports it.
/// </summary>
[DataMember(Name = "MessageRetention", Order = 53,
EmitDefaultValue = false)]
public bool? MessageRetention { get; set; }

/// <summary>
/// The node to monitor in "ns=" syntax.
/// </summary>
[DataMember(Name = "NodeId", Order = 50,
[DataMember(Name = "NodeId", Order = 99,
EmitDefaultValue = false)]
public NodeIdModel? NodeId { get; set; }
}
Expand Down
Loading

0 comments on commit f8a0fea

Please sign in to comment.