100 lines
2.7 KiB
C#
100 lines
2.7 KiB
C#
|
|
using SharpPcap;
|
||
|
|
using SharpPcap.LibPcap;
|
||
|
|
|
||
|
|
namespace gatuna_client;
|
||
|
|
|
||
|
|
sealed class TunnelLink : IDisposable
|
||
|
|
{
|
||
|
|
LibPcapLiveDevice _device;
|
||
|
|
readonly byte[] _ourMac = new byte[6];
|
||
|
|
|
||
|
|
public event Action<Frame, byte[]>? FrameReceived;
|
||
|
|
public event Action<string>? Log;
|
||
|
|
|
||
|
|
public TunnelLink(LibPcapLiveDevice device)
|
||
|
|
{
|
||
|
|
_device = device;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static LibPcapLiveDevice[] ListDevices()
|
||
|
|
{
|
||
|
|
return [.. LibPcapLiveDeviceList.Instance
|
||
|
|
.Where(d => !d.Loopback && d.MacAddress != null)];
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Open()
|
||
|
|
{
|
||
|
|
_device.Open(new DeviceConfiguration
|
||
|
|
{
|
||
|
|
Mode = DeviceModes.Promiscuous | DeviceModes.MaxResponsiveness,
|
||
|
|
ReadTimeout = 1000,
|
||
|
|
});
|
||
|
|
_device.Filter = $"ether proto 0x{Proto.EtherType:X4}";
|
||
|
|
|
||
|
|
if (_device.MacAddress?.GetAddressBytes() is { Length: 6 } mac)
|
||
|
|
{
|
||
|
|
Buffer.BlockCopy(mac, 0, _ourMac, 0, 6);
|
||
|
|
}
|
||
|
|
|
||
|
|
_device.OnPacketArrival += OnPacketArrival;
|
||
|
|
_device.StartCapture();
|
||
|
|
Log?.Invoke($"capture started on {_device.Name}");
|
||
|
|
}
|
||
|
|
|
||
|
|
void OnPacketArrival(object? sender, PacketCapture capture)
|
||
|
|
{
|
||
|
|
var raw = capture.GetPacket();
|
||
|
|
var data = raw.Data;
|
||
|
|
if (data.Length < Proto.EthHeaderLen + 6)
|
||
|
|
return;
|
||
|
|
var et = (ushort)(data[12] << 8 | data[13]);
|
||
|
|
if (et != Proto.EtherType)
|
||
|
|
return;
|
||
|
|
var srcMac = new byte[6];
|
||
|
|
Buffer.BlockCopy(data, 6, srcMac, 0, 6);
|
||
|
|
var payload = data.AsSpan(Proto.EthHeaderLen);
|
||
|
|
if (FrameCodec.Parse(payload) is { } frame)
|
||
|
|
FrameReceived?.Invoke(frame, srcMac);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void SendBroadcast(Frame frame)
|
||
|
|
{
|
||
|
|
SendRaw([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], FrameCodec.Encode(frame));
|
||
|
|
}
|
||
|
|
|
||
|
|
public void SendTo(byte[] dstMac, Frame frame)
|
||
|
|
{
|
||
|
|
SendRaw(dstMac, FrameCodec.Encode(frame));
|
||
|
|
}
|
||
|
|
|
||
|
|
void SendRaw(byte[] dstMac, byte[] payload)
|
||
|
|
{
|
||
|
|
var frame = new byte[Proto.EthHeaderLen + payload.Length];
|
||
|
|
Buffer.BlockCopy(dstMac, 0, frame, 0, 6);
|
||
|
|
Buffer.BlockCopy(_ourMac, 0, frame, 6, 6);
|
||
|
|
frame[12] = (byte)(Proto.EtherType >> 8);
|
||
|
|
frame[13] = (byte)(Proto.EtherType & 0xFF);
|
||
|
|
Buffer.BlockCopy(payload, 0, frame, 14, payload.Length);
|
||
|
|
try
|
||
|
|
{
|
||
|
|
_device.SendPacket(frame);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Log?.Invoke($"send failed: {ex.Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Dispose()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (_device.Started)
|
||
|
|
_device.StopCapture();
|
||
|
|
_device.OnPacketArrival -= OnPacketArrival;
|
||
|
|
_device.Close();
|
||
|
|
}
|
||
|
|
catch { }
|
||
|
|
}
|
||
|
|
}
|