Skip to content

Commit

Permalink
Linux Tap Support (#335)
Browse files Browse the repository at this point in the history
  • Loading branch information
kayoub5 authored Oct 18, 2021
1 parent 0674075 commit 82167ba
Show file tree
Hide file tree
Showing 33 changed files with 1,029 additions and 407 deletions.
6 changes: 3 additions & 3 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ for:
build_script:
- dotnet build -c Release
test_script:
- bash scripts/test.sh --filter "TestCategory!=Performance&TestCategory!=RemotePcap&TestCategory!=WinDivert&TestCategory!=WinpkFilter&TestCategory!=WinTap"
- bash scripts/test.sh --filter "TestCategory!=Performance&TestCategory!=RemotePcap&TestCategory!=WinDivert&TestCategory!=WinpkFilter"

-
matrix:
Expand All @@ -42,7 +42,7 @@ for:
build_script:
- dotnet build -c Release
test_script:
- bash scripts/test.sh --filter "TestCategory!=RemotePcap&TestCategory!=WinDivert&TestCategory!=WinTap"
- bash scripts/test.sh --filter "TestCategory!=RemotePcap&TestCategory!=WinDivert"

-
matrix:
Expand All @@ -51,7 +51,7 @@ for:
install:
- sudo -E bash scripts/install-libpcap.sh
test_script:
- sudo -E bash scripts/test.sh
- sudo -E bash scripts/test.sh --filter "TestCategory!=Tunneling"

-
matrix:
Expand Down
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
steps:
- checkout
- run: sudo -E bash scripts/install-dotnet.sh
- run: sudo -E bash scripts/install-tap.sh
# Download and compile latest libpcap
- when:
condition: { equal: [ libpcap-script, << parameters.libpcap >> ] }
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/dotnet-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ jobs:
run: dotnet restore
- name: Install libpcap
run: sudo -E bash scripts/install-libpcap.sh
- name: Install tap
run: sudo -E bash scripts/install-tap.sh
- name: Build sharppcap assembly
run: dotnet build SharpPcap/SharpPcap.csproj
- name: Test
Expand Down
1 change: 1 addition & 0 deletions .semaphore/semaphore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ blocks:
- commands:
- checkout
- sudo -E bash scripts/install-libpcap.sh
- sudo -E bash scripts/install-tap.sh
- sudo -E bash scripts/install-dotnet.sh
- sudo -E bash scripts/test.sh --filter "TestCategory!=Performance"
name: Test
Expand Down
111 changes: 60 additions & 51 deletions SharpPcap/ARP.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,79 +111,88 @@ public PhysicalAddress Resolve(System.Net.IPAddress destIP,
{
throw new InvalidOperationException("Unable to find local mac address");
}
using (var device = new LibPcapLiveDevice(pcapInterface))
{
//open the device with 20ms timeout
device.Open(mode: DeviceModes.Promiscuous, read_timeout: 20);
return Resolve(device, destIP, localIP, localMAC, Timeout);
}
}

internal static PhysicalAddress Resolve(
ILiveDevice device,
System.Net.IPAddress destIP,
System.Net.IPAddress localIP,
PhysicalAddress localMAC,
TimeSpan timeout)
{
//Build a new ARP request packet
var request = BuildRequest(destIP, localMAC, localIP);

//create a "tcpdump" filter for allowing only arp replies to be read
String arpFilter = "arp and ether dst " + localMAC.ToString();

using (var device = new LibPcapLiveDevice(pcapInterface))
{
//open the device with 20ms timeout
device.Open(mode: DeviceModes.Promiscuous, read_timeout: 20);
//set the filter
device.Filter = arpFilter;

//set the filter
device.Filter = arpFilter;
// set a last request time that will trigger sending the
// arp request immediately
var lastRequestTime = DateTime.FromBinary(0);

// set a last request time that will trigger sending the
// arp request immediately
var lastRequestTime = DateTime.FromBinary(0);
var requestInterval = new TimeSpan(0, 0, 1);

var requestInterval = new TimeSpan(0, 0, 1);
PacketDotNet.ArpPacket arpPacket = null;

PacketDotNet.ArpPacket arpPacket = null;

// attempt to resolve the address with the current timeout
var timeoutDateTime = DateTime.Now + Timeout;
while (DateTime.Now < timeoutDateTime)
// attempt to resolve the address with the current timeout
var timeoutDateTime = DateTime.Now + timeout;
while (DateTime.Now < timeoutDateTime)
{
if (requestInterval < (DateTime.Now - lastRequestTime))
{
if (requestInterval < (DateTime.Now - lastRequestTime))
{
// inject the packet to the wire
device.SendPacket(request);
lastRequestTime = DateTime.Now;
}

//read the next packet from the network
var retval = device.GetNextPacket(out PacketCapture e);
if (retval != GetPacketStatus.PacketRead)
{
continue;
}
var reply = e.GetPacket();

// parse the packet
var packet = PacketDotNet.Packet.ParsePacket(reply.LinkLayerType, reply.Data);

// is this an arp packet?
arpPacket = packet.Extract<PacketDotNet.ArpPacket>();
if (arpPacket == null)
{
continue;
}
// inject the packet to the wire
device.SendPacket(request);
lastRequestTime = DateTime.Now;
}

//if this is the reply we're looking for, stop
if (arpPacket.SenderProtocolAddress.Equals(destIP))
{
break;
}
//read the next packet from the network
var retval = device.GetNextPacket(out PacketCapture e);
if (retval != GetPacketStatus.PacketRead)
{
continue;
}
var reply = e.GetPacket();

// the timeout happened
if (DateTime.Now >= timeoutDateTime)
// parse the packet
var packet = PacketDotNet.Packet.ParsePacket(reply.LinkLayerType, reply.Data);

// is this an arp packet?
arpPacket = packet.Extract<PacketDotNet.ArpPacket>();
if (arpPacket == null)
{
return null;
continue;
}
else

//if this is the reply we're looking for, stop
if (arpPacket.SenderProtocolAddress.Equals(destIP))
{
//return the resolved MAC address
return arpPacket.SenderHardwareAddress;
break;
}
}

// the timeout happened
if (DateTime.Now >= timeoutDateTime)
{
return null;
}
else
{
//return the resolved MAC address
return arpPacket.SenderHardwareAddress;
}
}

private PacketDotNet.Packet BuildRequest(System.Net.IPAddress destinationIP,

private static PacketDotNet.Packet BuildRequest(System.Net.IPAddress destinationIP,
PhysicalAddress localMac,
System.Net.IPAddress localIP)
{
Expand Down
8 changes: 5 additions & 3 deletions SharpPcap/BaseLiveDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,16 @@ protected void RaiseOnPacketArrival(PacketCapture capture)
public GetPacketStatus GetNextPacket(out PacketCapture e)
{
var sw = Stopwatch.StartNew();
do
var timeout = ReadTimeout - sw.Elapsed;
while (timeout.TotalMilliseconds > 0)
{
var status = GetUnfilteredPacket(out e, ReadTimeout - sw.Elapsed);
var status = GetUnfilteredPacket(out e, timeout);
if (FilterProgram?.Matches(e.Data) ?? true)
{
return status;
}
} while (sw.Elapsed < ReadTimeout);
timeout = ReadTimeout - sw.Elapsed;
}
e = default;
return GetPacketStatus.ReadTimeout;
}
Expand Down
13 changes: 12 additions & 1 deletion SharpPcap/LibPcap/PcapDeviceCaptureLoop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,18 @@ public virtual void StartCapture()
throw new DeviceNotReadyException("No delegates assigned to OnPacketArrival, no where for captured packets to go.");

var cancellationToken = threadCancellationTokenSource.Token;
captureThread = new Thread(() => this.CaptureThread(cancellationToken));
captureThread = new Thread(() =>
{
try
{
CaptureThread(cancellationToken);
}
catch
{
// a thread is not allowed to throw, otherwise it causes system crash
// most common case is misuse of API or concurent access to device
}
});
captureThread.Start();
}
}
Expand Down
3 changes: 1 addition & 2 deletions SharpPcap/LibPcap/PcapUnmanagedStructures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,9 @@ public struct sockaddr_in
// understand way

// pad the size of sockaddr_in out to 16 bytes
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
// Disable warnings around this unused field
#pragma warning disable 0169
private readonly byte[] pad;
private readonly ulong pad;
#pragma warning restore 0169
};

Expand Down
14 changes: 14 additions & 0 deletions SharpPcap/Tunneling/IPAddressConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Net;

namespace SharpPcap.Tunneling
{
/// <summary>
/// IP Address configuration of the tunnel interface
/// </summary>
public class IPAddressConfiguration
{
// property names based on UnicastIPAddressInformation
public IPAddress Address { get; set; }
public IPAddress IPv4Mask { get; set; }
}
}
15 changes: 15 additions & 0 deletions SharpPcap/Tunneling/ITunnelDriver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Net.NetworkInformation;

namespace SharpPcap.Tunneling
{
internal interface ITunnelDriver
{
bool IsTunnelInterface(NetworkInterface networkInterface);
FileStream Open(NetworkInterface networkInterface, IPAddressConfiguration address, DeviceConfiguration configuration);
Version GetVersion(NetworkInterface networkInterface, SafeFileHandle handle);
}

}
Loading

0 comments on commit 82167ba

Please sign in to comment.