Skip to content
This repository was archived by the owner on Feb 20, 2022. It is now read-only.

Commit e1c47c0

Browse files
committed
Added support for multiple listen ports
1 parent 4d8ab19 commit e1c47c0

File tree

4 files changed

+42
-17
lines changed

4 files changed

+42
-17
lines changed

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ MikroTik RouterOS has [built-in support for TZSP packet capture](https://wiki.mi
2222
# Quick start
2323

2424
1. Set up a TZSP packet stream to the server this app will be running on. Pick an arbitrary port number for the stream, for example 1234.
25-
1. Execute the app as `tzsp_packetstream_exporter --interface eth0 --port 1234` (see `--help` for more info).
25+
1. Execute the app as `tzsp_packetstream_exporter --interface eth0 --listen-port 1234` (see `--help` for more info).
2626
1. Navigate to http://hostname:9184/metrics to explore the available metrics.
2727
1. Register `hostname:9184` in your Prometheus configuration as a scrape target.
2828
1. If using Grafana, [install the template dashboard](https://grafana.com/grafana/dashboards/11609).
@@ -44,9 +44,7 @@ Only TCP and UDP are analyzed - other transport-level protocols are simply "unkn
4444
4545
# How do I analyze multiple parallel packet streams?
4646
47-
You could simply direct them at the same analyzer but this will lead to the results being merged.
48-
49-
If you want the results separated in Prometheus, run a separate instance of the analyzer, accepting packets and publishing results on individual ports (`--listen-port` and `--publish-port`, respectively).
47+
You can direct multiple TZSP streams to the same analyzer, either on the same port or separate ports (using multiple `--listen-port` options). The output metrics carry a label indicating the listen port the data arrived on.
5048

5149
# (Linux) On startup, I see "Failed to create directory ..." - what's wrong?
5250

TzspPacketStreamExporter/ExporterLogic.cs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Axinom.Toolkit;
22
using Prometheus;
33
using System;
4+
using System.Collections.Generic;
45
using System.IO;
56
using System.Linq;
67
using System.Net;
@@ -13,11 +14,13 @@ namespace TzspPacketStreamExporter
1314
sealed class ExporterLogic
1415
{
1516
public string ListenInterface { get; set; } = "";
16-
public int ListenPort { get; set; }
17+
public List<int> ListenPorts { get; set; } = new List<int>();
1718
public int PublishPort { get; set; } = Constants.DefaultPublishPort;
1819

1920
private MetricServer? _metricServer;
2021

22+
private string MakeTsharkFilterString() => $"(dst port {string.Join(" or dst port ", ListenPorts)}) and udp";
23+
2124
public async Task RunAsync(CancellationToken cancel)
2225
{
2326
await VerifyTshark(cancel);
@@ -46,6 +49,9 @@ void ConsumeStandardOutput(Stream stdout)
4649
// 1. Hex string of packet bytes (starting with either outer UDP header or inner TZSP header)
4750
// 2. A space character.
4851
// 3. Type of the data ("eth:ethertype:ip:data" - UDP header, "eth:ethertype:ip:udp:data" - TZSP header)
52+
// 4. A space character.
53+
// 5. The destination UDP port of the TZSP protocol ("udp.dstport") but ONLY if type of data is TZSP header.
54+
// If type of data is UDP header, we need to parse the port ourselves.
4955

5056
try
5157
{
@@ -64,8 +70,8 @@ void ConsumeStandardOutput(Stream stdout)
6470
string packetType;
6571

6672
var parts = line.Split(' ');
67-
if (parts.Length != 2)
68-
throw new NotSupportedException("Output line did not have expected 2 components.");
73+
if (parts.Length != 3)
74+
throw new NotSupportedException("Output line did not have expected number of components.");
6975

7076
// On some systems there are colons. On others there are not!
7177
// Language/version differences? Whatever, get rid of them.
@@ -82,7 +88,8 @@ void ConsumeStandardOutput(Stream stdout)
8288
}
8389
else if (packetType == "eth:ethertype:ip:udp:data")
8490
{
85-
ProcessTzspPacket(packetBytes);
91+
var listenPort = ushort.Parse(parts[2]);
92+
ProcessTzspPacket(packetBytes, listenPort);
8693
}
8794
else
8895
{
@@ -149,7 +156,7 @@ void ConsumeStandardError(Stream stderr)
149156
{
150157
ExecutablePath = Constants.TsharkExecutableName,
151158
ResultHeuristics = ExternalToolResultHeuristics.Linux,
152-
Arguments = @$"-i ""{ListenInterface}"" -f ""dst port {ListenPort} and udp"" -p -T fields -e data.data -e frame.protocols -Eseparator=/s -Q",
159+
Arguments = @$"-i ""{ListenInterface}"" -f ""{MakeTsharkFilterString()}"" -p -T fields -e data.data -e frame.protocols -e udp.dstport -Eseparator=/s -Q",
153160
StandardOutputConsumer = ConsumeStandardOutput,
154161
StandardErrorConsumer = ConsumeStandardError
155162
};
@@ -218,27 +225,35 @@ private static string DetermineIPv4AddressType(IPAddress address)
218225
219226
// Starts with UDP header because it is incomplete (did not fit in 1500 Ethernet frame).
220227
// Identified by "frame.protocols": "eth:ethertype:ip:data"
228+
// Given that UDP header is not parsed due to incomplete packet, we do not get "udp.dstport" value for this type of packet.
221229
// Inside is UDP multicast packet.
222230
const string IncompleteUdpPacketHex = "1535153505f50000010000010101005e28500100155d04eb0e0800450005da457340001f1198cbc0a87802ef2850019595138905c61d71000001e55e27e764000e2d69323334350000000030313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334";
223231
224232
// Seems to start with TZSP header because it is complete.
225233
// Identified by "frame.protocols": "eth:ethertype:ip:udp:data"
234+
// We get "udp.dstport" value for this because TShark can parse the full packet.
226235
// Inside is TCP packet.
227236
const string CompleteTcpPacketHex = "010000010100155d04eb0a00155d04eb0e080045000274655e40003f0661d0c0a87802c0a87902eba01451cf757cdc16f4d46680100480223300000101080acae8ac2922f45e949d2efd0c894fdee47ecc96c750f49ad526016ad8ff12a3a218d5d86dd9445876725583fba461df222d75e97e6983538f84bd6783d00a25e8dffd55b941ad2fb302b2aea6138dc84102b1bf6b3412fab8cf623b9f6c61884c5edd05a08b34538de70234fa8ffc3b92aefafde20cf89bdc5ad67bc637031296e117366c4c89f9338b2d2d1b2a69add863aaba70a2554cfc2cc7c363cbd5f9aced2f1839b9116c443f995f69020c4166b7fbd6595122567de919e0b4eeda60db097814c28a8007c91a66321c7373822a6e5883bf7ad93c64f21d18e1f779bc00f1d1c37b51ca446b307688a3e90acd58635117dd2a54411d715afe68d3ba68c48b2b40ddf5844826fbd0c9e4db973c3ee8541b12a85d2f19b72d818ae8e94e73158e500a1399300e69faf244912f8279839e8b2bfbbb44b2e8c53cd0ae8a44c31994ce2c2dfe3a97f82cdb895b5e02defc8e09f7494da93112e502c16f468488da52b40851ee9f491b7ad376d8d555d4635ecbacac74debe59e07fc926045100560608a7f4a7f10f22c486fa99dbcffd399aa9e50f87a4686723318d27838e7e8996257d3e168d60da135a74ee297127c41a0dd3a2b13b09d46d97fcf0257a79bb9ff6f9b683599096b40484dd75aca190b974326ab03b3e1dd23a0df7b486b3547cac0a00069a96ba9f1b9714c739a480add6ea5d12287ae46387dc170d8f6b8a3b758a411020fbaf3b93c302cc6882793e6cd750955135f8d9110fe6a07b70dbf0fa1d001b18af56ab735977dbdbf11948c86add199fd5f2b0e4d9505f492b504448505f6100b5";
228237
229238
*/
230239

231240
private static void ProcessTzspPacketWithUdpHeader(byte[] packet)
232241
{
242+
using var stream = new MemoryStream(packet);
243+
using var reader = new MultiEndianBinaryReader(stream, ByteOrder.BigEndian);
244+
233245
// source port (2)
234246
// destination port (2)
235247
// length (2)
236248
// checksum (2)
237249

238-
ProcessTzspPacket(packet.Skip(8).ToArray());
250+
stream.Position = 2;
251+
var listenPort = reader.ReadUInt16();
252+
253+
ProcessTzspPacket(packet.Skip(8).ToArray(), listenPort);
239254
}
240255

241-
private static void ProcessTzspPacket(byte[] packet)
256+
private static void ProcessTzspPacket(byte[] packet, ushort listenPort)
242257
{
243258
// TZSP header
244259
// version (1) == 1
@@ -341,8 +356,8 @@ private static void ProcessTzspPacket(byte[] packet)
341356
_ => "unknown"
342357
};
343358

344-
BytesBase.WithLabels(sourceAddressString, sourceAddressType, destinationAddressString, destinationAddressType, protocolName).Inc(totalPacketLengthIncludingIpHeader);
345-
PacketsBase.WithLabels(sourceAddressString, sourceAddressType, destinationAddressString, destinationAddressType, protocolName).Inc();
359+
BytesBase.WithLabels(sourceAddressString, sourceAddressType, destinationAddressString, destinationAddressType, protocolName, listenPort.ToString()).Inc(totalPacketLengthIncludingIpHeader);
360+
PacketsBase.WithLabels(sourceAddressString, sourceAddressType, destinationAddressString, destinationAddressType, protocolName, listenPort.ToString()).Inc();
346361
}
347362

348363
private static readonly Counter BytesBase = Metrics.CreateCounter("tzsp_observed_bytes_total", "Total number of bytes that have been observed in the captured packet stream.", new CounterConfiguration
@@ -353,7 +368,8 @@ private static void ProcessTzspPacket(byte[] packet)
353368
"from_type",
354369
"to",
355370
"to_type",
356-
"protocol"
371+
"protocol",
372+
"listen_port"
357373
}
358374
});
359375

@@ -365,7 +381,8 @@ private static void ProcessTzspPacket(byte[] packet)
365381
"from_type",
366382
"to",
367383
"to_type",
368-
"protocol"
384+
"protocol",
385+
"listen_port"
369386
}
370387
});
371388

TzspPacketStreamExporter/Program.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Diagnostics;
6+
using System.Linq;
67
using System.Threading;
78

89
namespace TzspPacketStreamExporter
@@ -70,11 +71,12 @@ private bool ParseArguments(string[] args)
7071
var options = new OptionSet
7172
{
7273
GetVersionString(),
74+
"Usage: --interface eth0 --listen-port 19345 --listen-port 19346",
7375
"",
7476
"General",
7577
{ "h|?|help", "Displays usage instructions.", val => showHelp = val != null },
7678
{ "interface=", "Name or number of the network interface (e.g. 1 or eth5 or \"Ethernet 3\"). Must match an entry in the 'tshark -D' list.", val => _logic.ListenInterface = val?.Trim('"') ?? "" },
77-
{ "listen-port|port=", "UDP port to listen on for an incoming TZSP packet stream.", (ushort val) => _logic.ListenPort = val },
79+
{ "listen-port|port=", "UDP port to listen on for an incoming TZSP packet stream. Use multiple times to listen on multiple ports.", (ushort val) => _logic.ListenPorts.Add(val) },
7880
{ "publish-port|publish=", $"TCP port to publish Prometheus metrics on. Defaults to {_logic.PublishPort}.", (ushort val) => _logic.PublishPort = val },
7981

8082
"",
@@ -101,6 +103,12 @@ private bool ParseArguments(string[] args)
101103
if (_logic.ListenInterface.Contains('"'))
102104
throw new OptionException("The network interface name must not contain the double quote character.", "interface");
103105

106+
if (_logic.ListenPorts.Count == 0)
107+
throw new OptionException("You must specify at least one port to listen on.", "listen-port");
108+
109+
if (_logic.ListenPorts.Count != _logic.ListenPorts.Distinct().Count())
110+
throw new OptionException("You have specified duplicate ports to listen on. Did you make a typo?", "listen-port");
111+
104112
if (string.IsNullOrWhiteSpace(_logic.ListenInterface))
105113
throw new OptionException("The network interface name must be specified.", "interface");
106114
}
@@ -134,7 +142,9 @@ private Program()
134142
// We default to displaying Info or higher but allow this to be reconfiured later, if the user wishes.
135143
_filteringLogListener = new FilteringLogListener(new ConsoleLogListener())
136144
{
145+
#if !DEBUG
137146
MinimumSeverity = LogEntrySeverity.Info
147+
#endif
138148
};
139149

140150
Log.Default.RegisterListener(_filteringLogListener);

TzspPacketStreamExporter/Properties/launchSettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"profiles": {
33
"TzspPacketStreamExporter": {
44
"commandName": "Project",
5-
"commandLineArgs": "--interface 1 --port 5429"
5+
"commandLineArgs": "--interface 1 --port 5429 --port 5430"
66
}
77
}
88
}

0 commit comments

Comments
 (0)