-
Notifications
You must be signed in to change notification settings - Fork 273
/
Copy pathWinTapDriver.cs
151 lines (134 loc) · 5.58 KB
/
WinTapDriver.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// SPDX-FileCopyrightText: 2021 Ayoub Kaanich <[email protected]>
//
// SPDX-License-Identifier: MIT
using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
namespace SharpPcap.Tunneling.WinTap
{
/// <summary>
/// Driver to communicate with tap device file
/// </summary>
class WinTapDriver : ITunnelDriver
{
internal static readonly ITunnelDriver Instance = new WinTapDriver();
public bool IsTunnelInterface(NetworkInterface networkInterface)
{
return networkInterface.Description.StartsWith("TAP-Windows Adapter");
}
public FileStream Open(NetworkInterface networkInterface, IPAddressConfiguration address, DeviceConfiguration configuration)
{
var bufferSize = configuration.BufferSize ?? 4096;
var handle = CreateFile(@"\\.\Global\" + networkInterface.Id + ".tap",
WinFileAccess.GenericRead | WinFileAccess.GenericWrite,
0,
IntPtr.Zero,
WinFileCreation.OpenExisting,
WinFileAttributes.System | WinFileAttributes.Overlapped,
IntPtr.Zero
);
if (handle.IsInvalid)
{
throw new PcapException("Failed to open device");
}
if (address.Address != null)
{
ConfigureDhcp(
handle,
address.Address,
address.IPv4Mask,
IPAddress.Parse("0.0.0.0"),
TimeSpan.FromHours(24)
);
}
SetMediaStatus(handle, true);
return new FileStream(handle, FileAccess.ReadWrite, bufferSize, true);
}
public Version GetVersion(NetworkInterface networkInterface, SafeFileHandle handle)
{
Span<byte> inBuffer = stackalloc byte[0];
Span<byte> outBuffer = stackalloc byte[12];
TapControl(handle, TapIoControl.GetVersion, inBuffer, ref outBuffer);
var v = MemoryMarshal.Cast<byte, int>(outBuffer);
return new Version(v[0], v[1], v[2]);
}
internal static void ConfigureDhcp(SafeFileHandle handle, IPAddress ip, IPAddress mask, IPAddress server, TimeSpan lease_time)
{
var inBuffer = new byte[16];
ip.GetAddressBytes().CopyTo(inBuffer, 0);
mask.GetAddressBytes().CopyTo(inBuffer, 4);
server.GetAddressBytes().CopyTo(inBuffer, 8);
BitConverter.GetBytes((int)lease_time.TotalSeconds).CopyTo(inBuffer, 12);
Span<byte> outBuffer = stackalloc byte[1];
TapControl(handle, TapIoControl.ConfigDhcpMasq, inBuffer, ref outBuffer);
}
internal static void SetMediaStatus(SafeFileHandle handle, bool connected)
{
int value = connected ? 1 : 0;
Span<byte> inBuffer = stackalloc byte[4];
Span<byte> outBuffer = stackalloc byte[4];
#if NET8_0_OR_GREATER
MemoryMarshal.Write(inBuffer, in value);
#else
MemoryMarshal.Write(inBuffer, ref value);
#endif
TapControl(handle, TapIoControl.SetMediaStatus, inBuffer, ref outBuffer);
}
private static unsafe void TapControl(SafeFileHandle handle, TapIoControl code, Span<byte> inBuffer, ref Span<byte> outBuffer)
{
fixed (byte* inPtr = inBuffer, outPtr = outBuffer)
{
var controlCode = CTL_CODE(FILE_DEVICE_UNKNOWN, (uint)code, METHOD_BUFFERED, FILE_ANY_ACCESS);
var retval = DeviceIoControl(handle, controlCode,
new IntPtr(inPtr), inBuffer.Length,
new IntPtr(outPtr), outBuffer.Length,
out var returnedBytes,
IntPtr.Zero
);
outBuffer = outBuffer.Slice(0, returnedBytes);
if (!retval)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
private const uint METHOD_BUFFERED = 0;
private const uint FILE_ANY_ACCESS = 0;
private const uint FILE_DEVICE_UNKNOWN = 0x00000022;
/// <summary>
/// See https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/d4drvif/nf-d4drvif-ctl_code
/// </summary>
private static uint CTL_CODE(uint deviceType, uint function, uint method, uint access)
{
return ((deviceType << 16) | (access << 14) | (function << 2) | method);
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
internal static extern SafeFileHandle CreateFile(
string lpFileName,
WinFileAccess dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
WinFileCreation dwCreationDisposition,
WinFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool DeviceIoControl(
[In] SafeFileHandle hDevice,
[In] uint dwIoControlCode,
[In] IntPtr lpInBuffer,
[In] int nInBufferSize,
[Out] IntPtr lpOutBuffer,
[In] int nOutBufferSize,
out int lpBytesReturned,
[In] IntPtr lpOverlapped
);
}
}