Skip to content

Commit

Permalink
Add USB packet capture
Browse files Browse the repository at this point in the history
  • Loading branch information
dorssel committed Mar 16, 2022
1 parent 5e60aaf commit 29f3ed4
Show file tree
Hide file tree
Showing 5 changed files with 551 additions and 2 deletions.
177 changes: 177 additions & 0 deletions UnitTests/PcapNg_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// SPDX-FileCopyrightText: 2022 Frans van Dorsselaer
//
// SPDX-License-Identifier: GPL-2.0-only

using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using UsbIpServer;
using static UsbIpServer.Interop.UsbIp;

namespace UnitTests
{
[TestClass]
sealed class PcapNg_Tests
{
static readonly string TemporaryPath = Path.GetTempFileName();

[ClassCleanup]
public static void ClassCleanup()
{
File.Delete(TemporaryPath);
}

sealed class MockConfiguration : IDisposable
{
public MockConfiguration(string? path)
{
Mock.Setup(m => m["usbipd:PcapNg:Path"]).Returns(path!);
}

readonly Mock<IConfiguration> Mock = new(MockBehavior.Strict);
public IConfiguration Object => Mock.Object;

bool IsDisposed;
public void Dispose()
{
if (!IsDisposed)
{
Mock.VerifyAll();
IsDisposed = true;
}
}
}

static readonly ILogger<PcapNg> MockLogger = new Mock<ILogger<PcapNg>>(MockBehavior.Loose).Object;

[TestMethod]
public void Constructor_Disabled()
{
using var mockConfiguration = new MockConfiguration(null);
{
using var _ = new PcapNg(mockConfiguration.Object, MockLogger);
}
}

[TestMethod]
public void Constructor_InvalidPath()
{
using var mockConfiguration = new MockConfiguration(@"<>?*\InvalidPath");
{
using var _ = new PcapNg(mockConfiguration.Object, MockLogger);
}
}

[TestMethod]
public void Constructor_Create()
{
File.Delete(TemporaryPath);
Assert.IsFalse(File.Exists(TemporaryPath));
using var mockConfiguration = new MockConfiguration(TemporaryPath);
{
using var _ = new PcapNg(mockConfiguration.Object, MockLogger);
}
Assert.AreNotEqual(0, new FileInfo(TemporaryPath).Length);
}

[TestMethod]
public void Constructor_Overwrite()
{
{
using var file = File.Create(TemporaryPath);
file.Write(new byte[1000]);
}
Assert.AreEqual(1000, new FileInfo(TemporaryPath).Length);
using var mockConfiguration = new MockConfiguration(TemporaryPath);
{
using var _ = new PcapNg(mockConfiguration.Object, MockLogger);
}
Assert.AreNotEqual(0, new FileInfo(TemporaryPath).Length);
Assert.IsTrue(new FileInfo(TemporaryPath).Length < 1000);
}

[TestMethod]
public void Dispose()
{
using var mockConfiguration = new MockConfiguration(TemporaryPath);
var pcapNg = new PcapNg(mockConfiguration.Object, MockLogger);
pcapNg.Dispose();
}

[TestMethod]
public void Dispose_Twice()
{
using var mockConfiguration = new MockConfiguration(TemporaryPath);
var pcapNg = new PcapNg(mockConfiguration.Object, MockLogger);
pcapNg.Dispose();
pcapNg.Dispose();
}

[TestMethod]
public void DumpPacket_Submit_Disabled()
{
using var mockConfiguration = new MockConfiguration(null);
using var pcapNg = new PcapNg(mockConfiguration.Object, MockLogger);
pcapNg.DumpPacket(new UsbIpHeaderBasic(), new UsbIpHeaderCmdSubmit(), new byte[10]);
}

[TestMethod]
public void DumpPacket_Submit_NoData_Disabled()
{
using var mockConfiguration = new MockConfiguration(null);
using var pcapNg = new PcapNg(mockConfiguration.Object, MockLogger);
pcapNg.DumpPacket(new UsbIpHeaderBasic(), new UsbIpHeaderCmdSubmit(), null);
}

[TestMethod]
public void DumpPacket_Reply_Disabled()
{
using var mockConfiguration = new MockConfiguration(null);
using var pcapNg = new PcapNg(mockConfiguration.Object, MockLogger);
pcapNg.DumpPacket(new UsbIpHeaderBasic(), new UsbIpHeaderCmdSubmit(), new UsbIpHeaderRetSubmit(), new byte[10]);
}

[TestMethod]
public void DumpPacket_Reply_NoData_Disabled()
{
using var mockConfiguration = new MockConfiguration(null);
using var pcapNg = new PcapNg(mockConfiguration.Object, MockLogger);
pcapNg.DumpPacket(new UsbIpHeaderBasic(), new UsbIpHeaderCmdSubmit(), new UsbIpHeaderRetSubmit(), null);
}

[TestMethod]
public void DumpPacket_Submit()
{
using var mockConfiguration = new MockConfiguration(TemporaryPath);
using var pcapNg = new PcapNg(mockConfiguration.Object, MockLogger);
pcapNg.DumpPacket(new UsbIpHeaderBasic(), new UsbIpHeaderCmdSubmit(), new byte[10]);
}

[TestMethod]
public void DumpPacket_Submit_NoData()
{
using var mockConfiguration = new MockConfiguration(TemporaryPath);
using var pcapNg = new PcapNg(mockConfiguration.Object, MockLogger);
pcapNg.DumpPacket(new UsbIpHeaderBasic(), new UsbIpHeaderCmdSubmit(), null);
}

[TestMethod]
public void DumpPacket_Reply()
{
using var mockConfiguration = new MockConfiguration(TemporaryPath);
using var pcapNg = new PcapNg(mockConfiguration.Object, MockLogger);
pcapNg.DumpPacket(new UsbIpHeaderBasic(), new UsbIpHeaderCmdSubmit(), new UsbIpHeaderRetSubmit(), new byte[10]);
}

[TestMethod]
public void DumpPacket_Reply_NoData()
{
using var mockConfiguration = new MockConfiguration(TemporaryPath);
using var pcapNg = new PcapNg(mockConfiguration.Object, MockLogger);
pcapNg.DumpPacket(new UsbIpHeaderBasic(), new UsbIpHeaderCmdSubmit(), new UsbIpHeaderRetSubmit(), null);
}
}
}
10 changes: 9 additions & 1 deletion UsbIpServer/AttachedClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ namespace UsbIpServer
{
sealed class AttachedClient
{
public AttachedClient(ILogger<AttachedClient> logger, ClientContext clientContext)
public AttachedClient(ILogger<AttachedClient> logger, ClientContext clientContext, PcapNg pcap)
{
Logger = logger;
ClientContext = clientContext;
Pcap = pcap;

var tcpClient = clientContext.TcpClient;
Stream = tcpClient.GetStream();
Expand All @@ -41,6 +42,7 @@ public AttachedClient(ILogger<AttachedClient> logger, ClientContext clientContex

readonly ILogger Logger;
readonly ClientContext ClientContext;
readonly PcapNg Pcap;
readonly NetworkStream Stream;
readonly Channel<byte[]> ReplyChannel = Channel.CreateUnbounded<byte[]>();
readonly DeviceFile Device;
Expand Down Expand Up @@ -103,6 +105,8 @@ async Task HandleSubmitIsochronousAsync(UsbIpHeaderBasic basic, UsbIpHeaderCmdSu

// Everything has been read and validated, now process...

Pcap.DumpPacket(basic, submit, packetDescriptors, basic.direction == UsbIpDir.USBIP_DIR_OUT ? buf : ReadOnlySpan<byte>.Empty);

// To support UNLINK, we must be able to abort the pipe that is used for this URB.
// We need the raw USB endpoint number, i.e. including the high bit for input pipes.
if (!PendingSubmits.TryAdd(basic.seqnum, basic.RawEndpoint()))
Expand Down Expand Up @@ -208,6 +212,7 @@ async Task HandleSubmitIsochronousAsync(UsbIpHeaderBasic basic, UsbIpHeaderCmdSu
}
}

Pcap.DumpPacket(basic, submit, header.ret_submit, basic.direction == UsbIpDir.USBIP_DIR_IN ? buf.AsSpan(0, header.ret_submit.actual_length) : ReadOnlySpan<byte>.Empty);
using var replyStream = new MemoryStream();
replyStream.Write(header.ToBytes());
if (basic.direction == UsbIpDir.USBIP_DIR_IN)
Expand Down Expand Up @@ -287,6 +292,8 @@ async Task HandleSubmitAsync(UsbIpHeaderBasic basic, UsbIpHeaderCmdSubmit submit
// This means multiple URBs can be outstanding awaiting completion.
// The pending URBs can be completed out of order, but for each endpoint the replies must be sent in order.

Pcap.DumpPacket(basic, submit, basic.direction == UsbIpDir.USBIP_DIR_OUT ? buf.AsSpan(payloadOffset) : ReadOnlySpan<byte>.Empty);

Task ioctl;
var pending = false;

Expand Down Expand Up @@ -426,6 +433,7 @@ async Task HandleSubmitAsync(UsbIpHeaderBasic basic, UsbIpHeaderCmdSubmit submit
}
Logger.Trace($"actual: {header.ret_submit.actual_length}, requested: {requestLength}");

Pcap.DumpPacket(basic, submit, header.ret_submit, basic.direction == UsbIpDir.USBIP_DIR_IN ? buf.AsSpan(payloadOffset, header.ret_submit.actual_length) : ReadOnlySpan<byte>.Empty);
using var replyStream = new MemoryStream();
replyStream.Write(header.ToBytes());
if (basic.direction == UsbIpDir.USBIP_DIR_IN)
Expand Down
1 change: 1 addition & 0 deletions UsbIpServer/CommandHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ async Task<ExitCode> ICommandHandlers.Server(string[] args, IConsole console, Ca
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Server>();
services.AddSingleton<PcapNg>();
services.AddScoped<ClientContext>();
services.AddScoped<ConnectedClient>();
services.AddScoped<AttachedClient>();
Expand Down
Loading

0 comments on commit 29f3ed4

Please sign in to comment.