Skip to content

Commit 82167ba

Browse files
authored
Linux Tap Support (#335)
1 parent 0674075 commit 82167ba

33 files changed

+1029
-407
lines changed

Diff for: .appveyor.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ for:
2929
build_script:
3030
- dotnet build -c Release
3131
test_script:
32-
- bash scripts/test.sh --filter "TestCategory!=Performance&TestCategory!=RemotePcap&TestCategory!=WinDivert&TestCategory!=WinpkFilter&TestCategory!=WinTap"
32+
- bash scripts/test.sh --filter "TestCategory!=Performance&TestCategory!=RemotePcap&TestCategory!=WinDivert&TestCategory!=WinpkFilter"
3333

3434
-
3535
matrix:
@@ -42,7 +42,7 @@ for:
4242
build_script:
4343
- dotnet build -c Release
4444
test_script:
45-
- bash scripts/test.sh --filter "TestCategory!=RemotePcap&TestCategory!=WinDivert&TestCategory!=WinTap"
45+
- bash scripts/test.sh --filter "TestCategory!=RemotePcap&TestCategory!=WinDivert"
4646

4747
-
4848
matrix:
@@ -51,7 +51,7 @@ for:
5151
install:
5252
- sudo -E bash scripts/install-libpcap.sh
5353
test_script:
54-
- sudo -E bash scripts/test.sh
54+
- sudo -E bash scripts/test.sh --filter "TestCategory!=Tunneling"
5555

5656
-
5757
matrix:

Diff for: .circleci/config.yml

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ jobs:
2525
steps:
2626
- checkout
2727
- run: sudo -E bash scripts/install-dotnet.sh
28+
- run: sudo -E bash scripts/install-tap.sh
2829
# Download and compile latest libpcap
2930
- when:
3031
condition: { equal: [ libpcap-script, << parameters.libpcap >> ] }

Diff for: .github/workflows/dotnet-core.yml

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ jobs:
2121
run: dotnet restore
2222
- name: Install libpcap
2323
run: sudo -E bash scripts/install-libpcap.sh
24+
- name: Install tap
25+
run: sudo -E bash scripts/install-tap.sh
2426
- name: Build sharppcap assembly
2527
run: dotnet build SharpPcap/SharpPcap.csproj
2628
- name: Test

Diff for: .semaphore/semaphore.yml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ blocks:
2020
- commands:
2121
- checkout
2222
- sudo -E bash scripts/install-libpcap.sh
23+
- sudo -E bash scripts/install-tap.sh
2324
- sudo -E bash scripts/install-dotnet.sh
2425
- sudo -E bash scripts/test.sh --filter "TestCategory!=Performance"
2526
name: Test

Diff for: SharpPcap/ARP.cs

+60-51
Original file line numberDiff line numberDiff line change
@@ -111,79 +111,88 @@ public PhysicalAddress Resolve(System.Net.IPAddress destIP,
111111
{
112112
throw new InvalidOperationException("Unable to find local mac address");
113113
}
114+
using (var device = new LibPcapLiveDevice(pcapInterface))
115+
{
116+
//open the device with 20ms timeout
117+
device.Open(mode: DeviceModes.Promiscuous, read_timeout: 20);
118+
return Resolve(device, destIP, localIP, localMAC, Timeout);
119+
}
120+
}
114121

122+
internal static PhysicalAddress Resolve(
123+
ILiveDevice device,
124+
System.Net.IPAddress destIP,
125+
System.Net.IPAddress localIP,
126+
PhysicalAddress localMAC,
127+
TimeSpan timeout)
128+
{
115129
//Build a new ARP request packet
116130
var request = BuildRequest(destIP, localMAC, localIP);
117131

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

121-
using (var device = new LibPcapLiveDevice(pcapInterface))
122-
{
123-
//open the device with 20ms timeout
124-
device.Open(mode: DeviceModes.Promiscuous, read_timeout: 20);
135+
//set the filter
136+
device.Filter = arpFilter;
125137

126-
//set the filter
127-
device.Filter = arpFilter;
138+
// set a last request time that will trigger sending the
139+
// arp request immediately
140+
var lastRequestTime = DateTime.FromBinary(0);
128141

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

133-
var requestInterval = new TimeSpan(0, 0, 1);
144+
PacketDotNet.ArpPacket arpPacket = null;
134145

135-
PacketDotNet.ArpPacket arpPacket = null;
136-
137-
// attempt to resolve the address with the current timeout
138-
var timeoutDateTime = DateTime.Now + Timeout;
139-
while (DateTime.Now < timeoutDateTime)
146+
// attempt to resolve the address with the current timeout
147+
var timeoutDateTime = DateTime.Now + timeout;
148+
while (DateTime.Now < timeoutDateTime)
149+
{
150+
if (requestInterval < (DateTime.Now - lastRequestTime))
140151
{
141-
if (requestInterval < (DateTime.Now - lastRequestTime))
142-
{
143-
// inject the packet to the wire
144-
device.SendPacket(request);
145-
lastRequestTime = DateTime.Now;
146-
}
147-
148-
//read the next packet from the network
149-
var retval = device.GetNextPacket(out PacketCapture e);
150-
if (retval != GetPacketStatus.PacketRead)
151-
{
152-
continue;
153-
}
154-
var reply = e.GetPacket();
155-
156-
// parse the packet
157-
var packet = PacketDotNet.Packet.ParsePacket(reply.LinkLayerType, reply.Data);
158-
159-
// is this an arp packet?
160-
arpPacket = packet.Extract<PacketDotNet.ArpPacket>();
161-
if (arpPacket == null)
162-
{
163-
continue;
164-
}
152+
// inject the packet to the wire
153+
device.SendPacket(request);
154+
lastRequestTime = DateTime.Now;
155+
}
165156

166-
//if this is the reply we're looking for, stop
167-
if (arpPacket.SenderProtocolAddress.Equals(destIP))
168-
{
169-
break;
170-
}
157+
//read the next packet from the network
158+
var retval = device.GetNextPacket(out PacketCapture e);
159+
if (retval != GetPacketStatus.PacketRead)
160+
{
161+
continue;
171162
}
163+
var reply = e.GetPacket();
172164

173-
// the timeout happened
174-
if (DateTime.Now >= timeoutDateTime)
165+
// parse the packet
166+
var packet = PacketDotNet.Packet.ParsePacket(reply.LinkLayerType, reply.Data);
167+
168+
// is this an arp packet?
169+
arpPacket = packet.Extract<PacketDotNet.ArpPacket>();
170+
if (arpPacket == null)
175171
{
176-
return null;
172+
continue;
177173
}
178-
else
174+
175+
//if this is the reply we're looking for, stop
176+
if (arpPacket.SenderProtocolAddress.Equals(destIP))
179177
{
180-
//return the resolved MAC address
181-
return arpPacket.SenderHardwareAddress;
178+
break;
182179
}
183180
}
181+
182+
// the timeout happened
183+
if (DateTime.Now >= timeoutDateTime)
184+
{
185+
return null;
186+
}
187+
else
188+
{
189+
//return the resolved MAC address
190+
return arpPacket.SenderHardwareAddress;
191+
}
184192
}
185193

186-
private PacketDotNet.Packet BuildRequest(System.Net.IPAddress destinationIP,
194+
195+
private static PacketDotNet.Packet BuildRequest(System.Net.IPAddress destinationIP,
187196
PhysicalAddress localMac,
188197
System.Net.IPAddress localIP)
189198
{

Diff for: SharpPcap/BaseLiveDevice.cs

+5-3
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,16 @@ protected void RaiseOnPacketArrival(PacketCapture capture)
8282
public GetPacketStatus GetNextPacket(out PacketCapture e)
8383
{
8484
var sw = Stopwatch.StartNew();
85-
do
85+
var timeout = ReadTimeout - sw.Elapsed;
86+
while (timeout.TotalMilliseconds > 0)
8687
{
87-
var status = GetUnfilteredPacket(out e, ReadTimeout - sw.Elapsed);
88+
var status = GetUnfilteredPacket(out e, timeout);
8889
if (FilterProgram?.Matches(e.Data) ?? true)
8990
{
9091
return status;
9192
}
92-
} while (sw.Elapsed < ReadTimeout);
93+
timeout = ReadTimeout - sw.Elapsed;
94+
}
9395
e = default;
9496
return GetPacketStatus.ReadTimeout;
9597
}

Diff for: SharpPcap/LibPcap/PcapDeviceCaptureLoop.cs

+12-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,18 @@ public virtual void StartCapture()
6666
throw new DeviceNotReadyException("No delegates assigned to OnPacketArrival, no where for captured packets to go.");
6767

6868
var cancellationToken = threadCancellationTokenSource.Token;
69-
captureThread = new Thread(() => this.CaptureThread(cancellationToken));
69+
captureThread = new Thread(() =>
70+
{
71+
try
72+
{
73+
CaptureThread(cancellationToken);
74+
}
75+
catch
76+
{
77+
// a thread is not allowed to throw, otherwise it causes system crash
78+
// most common case is misuse of API or concurent access to device
79+
}
80+
});
7081
captureThread.Start();
7182
}
7283
}

Diff for: SharpPcap/LibPcap/PcapUnmanagedStructures.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,9 @@ public struct sockaddr_in
9595
// understand way
9696

9797
// pad the size of sockaddr_in out to 16 bytes
98-
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
9998
// Disable warnings around this unused field
10099
#pragma warning disable 0169
101-
private readonly byte[] pad;
100+
private readonly ulong pad;
102101
#pragma warning restore 0169
103102
};
104103

Diff for: SharpPcap/Tunneling/IPAddressConfiguration.cs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Net;
2+
3+
namespace SharpPcap.Tunneling
4+
{
5+
/// <summary>
6+
/// IP Address configuration of the tunnel interface
7+
/// </summary>
8+
public class IPAddressConfiguration
9+
{
10+
// property names based on UnicastIPAddressInformation
11+
public IPAddress Address { get; set; }
12+
public IPAddress IPv4Mask { get; set; }
13+
}
14+
}

Diff for: SharpPcap/Tunneling/ITunnelDriver.cs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.Win32.SafeHandles;
2+
using System;
3+
using System.IO;
4+
using System.Net.NetworkInformation;
5+
6+
namespace SharpPcap.Tunneling
7+
{
8+
internal interface ITunnelDriver
9+
{
10+
bool IsTunnelInterface(NetworkInterface networkInterface);
11+
FileStream Open(NetworkInterface networkInterface, IPAddressConfiguration address, DeviceConfiguration configuration);
12+
Version GetVersion(NetworkInterface networkInterface, SafeFileHandle handle);
13+
}
14+
15+
}

0 commit comments

Comments
 (0)