OpenVPN 3 Core Library
Loading...
Searching...
No Matches
wintun.hpp
Go to the documentation of this file.
1#pragma once
2
8
9namespace openvpn::TunWin {
10
11class WintunClient : public TunClient
12{
14
15 public:
16 WintunClient(openvpn_io::io_context &io_context_arg,
17 ClientConfig *config_arg,
18 TunClientParent &parent_arg)
19 : io_context(io_context_arg),
20 config(config_arg),
21 parent(parent_arg),
22 state(new TunProp::State()),
23 frame(config_arg->frame)
24 {
25 }
26
27 // Inherited via TunClient
28 void tun_start(const OptionList &opt, TransportClient &transcli, CryptoDCSettings &) override
29 {
30 halt = false;
31 if (config->tun_persist)
32 tun_persist = config->tun_persist; // long-term persistent
33 else
34 tun_persist.reset(new TunPersist(false, TunWrapObjRetain::NO_RETAIN, nullptr)); // short-term
35
36 try
37 {
38
39 const IP::Addr server_addr = transcli.server_endpoint_addr();
40
41 // Check if persisted tun session matches properties of to-be-created session
42 if (tun_persist->use_persisted_tun(server_addr, config->tun_prop, opt))
43 {
44 state = tun_persist->state().state;
45 ring_buffer = tun_persist->state().adapter_state;
46 OPENVPN_LOG("TunPersist: reused tun context");
47 }
48 else
49 {
50 // notify parent
52
53 // close old TAP handle if persisted
54 tun_persist->close();
55
56 // parse pushed options
59 state.get(),
60 config->stats.get(),
61 server_addr,
62 config->tun_prop,
63 opt,
64 nullptr,
65 false);
66 OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl
67 << po->to_string());
68
69 // create new tun setup object
70 tun_setup = config->new_setup_obj(io_context);
71
73
74 // open/config TAP
75 HANDLE th;
76 {
77 std::ostringstream os;
78 auto os_print = Cleanup([&os]()
79 { OPENVPN_LOG_STRING(os.str()); });
80 th = tun_setup->establish(*po, Win::module_name(), config->stop, os, ring_buffer);
81 }
82
83 // create ASIO wrapper for HANDLE
84 TAPStream *ts = new TAPStream(io_context, th);
85
86 // persist tun settings state
87 if (tun_persist->persist_tun_state(ts, {state, ring_buffer}))
88 OPENVPN_LOG("TunPersist: saving tun context:" << std::endl
89 << tun_persist->options());
90
91 // enable tun_setup destructor
92 tun_persist->add_destructor(tun_setup);
93
94 // assert ownership over TAP device handle
95 tun_setup->confirm();
96 }
97
98 openvpn_io::post(io_context, [self = Ptr(this)]()
99 { self->read(); });
100
101 parent.tun_connected(); // signal that we are connected
102 }
103 catch (const std::exception &e)
104 {
105 stop();
107 const ExceptionCode *ec = dynamic_cast<const ExceptionCode *>(&e);
108 if (ec && ec->code_defined())
109 err = ec->code();
110 parent.tun_error(err, e.what());
111 }
112 }
113
114 void stop() override
115 {
116 if (!halt)
117 {
118 halt = true;
119
121 }
122 }
123
124 void set_disconnect() override
125 {
126 }
127
129 {
130 TUN_RING *receive_ring = ring_buffer->receive_ring();
131
132 ULONG head = receive_ring->head.load(std::memory_order_acquire);
133 if (head > WINTUN_RING_CAPACITY)
134 {
135 if (head == 0xFFFFFFFF)
136 parent.tun_error(Error::TUN_WRITE_ERROR, "invalid ring head/tail or bogus packet received");
137 return false;
138 }
139
140 ULONG tail = receive_ring->tail.load(std::memory_order_acquire);
141 if (tail >= WINTUN_RING_CAPACITY)
142 return false;
143
144 size_t aligned_packet_size = packet_align(sizeof(TUN_PACKET_HEADER) + buf.size());
145 ULONG buf_space = wrap(head - tail - WINTUN_PACKET_ALIGN);
146 if (aligned_packet_size > buf_space)
147 {
148 OPENVPN_LOG("ring is full");
149 return false;
150 }
151
152 // copy packet size and data into ring
153 TUN_PACKET *packet = (TUN_PACKET *)&receive_ring->data[tail];
154 if (!is_safe_conversion<decltype(packet->size)>(buf.size()))
155 return false;
156 packet->size = static_cast<decltype(packet->size)>(buf.size());
157 std::memcpy(packet->data, buf.data(), buf.size());
158
159 // move ring tail
160 receive_ring->tail.store(wrap(tail + static_cast<ULONG>(aligned_packet_size)), std::memory_order_release);
161 if (receive_ring->alertable.load(std::memory_order_acquire) != 0)
162 SetEvent(ring_buffer->receive_ring_tail_moved());
163
164 return true;
165 }
166
167 std::string tun_name() const override
168 {
169 return "wintun";
170 }
171
172 std::string vpn_ip4() const override
173 {
174 if (state->vpn_ip4_addr.specified())
175 return state->vpn_ip4_addr.to_string();
176 else
177 return "";
178 }
179
180 std::string vpn_ip6() const override
181 {
182 if (state->vpn_ip6_addr.specified())
183 return state->vpn_ip6_addr.to_string();
184 else
185 return "";
186 }
187
188 std::string vpn_gw4() const override
189 {
190 if (state->vpn_ip4_gw.specified())
191 return state->vpn_ip4_gw.to_string();
192 else
193 return "";
194 }
195
196 std::string vpn_gw6() const override
197 {
198 if (state->vpn_ip6_gw.specified())
199 return state->vpn_ip6_gw.to_string();
200 else
201 return "";
202 }
203
204 int vpn_mtu() const override
205 {
206 return state->mtu;
207 }
208
209 private:
210 void read()
211 {
212 TUN_RING *send_ring = ring_buffer->send_ring();
213
214 if (halt)
215 return;
216
217 ULONG head = send_ring->head.load(std::memory_order_acquire);
218 if (head >= WINTUN_RING_CAPACITY)
219 {
220 parent.tun_error(Error::TUN_ERROR, "ring head exceeds ring capacity");
221 return;
222 }
223
224 ULONG tail = send_ring->tail.load(std::memory_order_acquire);
225 if (tail >= WINTUN_RING_CAPACITY)
226 {
227 parent.tun_error(Error::TUN_ERROR, "ring tail exceeds ring capacity");
228 return;
229 }
230
231 while (true)
232 {
233 // tail has moved?
234 if (head == tail)
235 {
236 ring_buffer->send_tail_moved_asio_event().async_wait([self = Ptr(this)](const openvpn_io::error_code &error)
237 {
238 if (!error)
239 self->read();
240 else
241 {
242 if (!self->halt)
243 self->parent.tun_error(Error::TUN_ERROR, "error waiting on ring send tail moved");
244 } });
245 return;
246 }
247
248 // read buffer content
249 ULONG content_len = wrap(tail - head);
250 if (content_len < sizeof(TUN_PACKET_HEADER))
251 {
252 parent.tun_error(Error::TUN_ERROR, "incomplete packet header in send ring");
253 return;
254 }
255
256 TUN_PACKET *packet = (TUN_PACKET *)&send_ring->data[head];
257 if (packet->size > WINTUN_MAX_PACKET_SIZE)
258 {
259 parent.tun_error(Error::TUN_ERROR, "packet too big in send ring");
260 return;
261 }
262
263 size_t aligned_packet_size = packet_align(sizeof(TUN_PACKET_HEADER) + packet->size);
264 if (aligned_packet_size > content_len)
265 {
266 parent.tun_error(Error::TUN_ERROR, "incomplete packet in send ring");
267 return;
268 }
269
271
272 buf.write(packet->data, packet->size);
273
274 head = wrap(head + static_cast<ULONG>(aligned_packet_size));
275 send_ring->head.store(head, std::memory_order_release);
276
278
279 if (halt)
280 return;
281 }
282 }
283
285 {
286 uint32_t size;
287 };
288
290 {
291 uint32_t size;
293 };
294
295 size_t packet_align(size_t size)
296 {
297 return (size + (WINTUN_PACKET_ALIGN - 1)) & ~(WINTUN_PACKET_ALIGN - 1);
298 }
299
300 ULONG wrap(ULONG value)
301 {
302 return value & (WINTUN_RING_CAPACITY - 1);
303 }
304
305 openvpn_io::io_context &io_context;
306 TunPersist::Ptr tun_persist; // contains the TAP device HANDLE
311
313
315
316 bool halt = false;
317
319
321};
322} // namespace openvpn::TunWin
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1225
T * data()
Get a mutable pointer to the start of the array.
Definition buffer.hpp:1433
void write(const T *data, const size_t size)
Write data to the buffer.
Definition buffer.hpp:1546
Error::Type code() const
Definition excode.hpp:54
bool code_defined() const
Definition excode.hpp:63
size_t prepare(const unsigned int context, Buffer &buf) const
Definition frame.hpp:266
The smart pointer class.
Definition rc.hpp:119
void reset() noexcept
Points this RCPtr<T> to nullptr safely.
Definition rc.hpp:290
T * get() const noexcept
Returns the raw pointer to the object T, or nullptr.
Definition rc.hpp:321
std::string to_string() const
Definition capture.hpp:567
static void configure_builder(TunBuilderBase *tb, State *state, SessionStats *stats, const IP::Addr &server_addr, const Config &config, const OptionList &opt, const EmulateExcludeRouteFactory *eer_factory, const bool quiet)
Definition tunprop.hpp:90
WintunClient(openvpn_io::io_context &io_context_arg, ClientConfig *config_arg, TunClientParent &parent_arg)
Definition wintun.hpp:16
int vpn_mtu() const override
Definition wintun.hpp:204
ClientConfig::Ptr config
Definition wintun.hpp:307
TunWin::SetupBase::Ptr tun_setup
Definition wintun.hpp:310
openvpn_io::io_context & io_context
Definition wintun.hpp:305
std::string tun_name() const override
Definition wintun.hpp:167
void set_disconnect() override
Definition wintun.hpp:124
std::string vpn_ip6() const override
Definition wintun.hpp:180
RingBuffer::Ptr ring_buffer
Definition wintun.hpp:320
std::string vpn_gw6() const override
Definition wintun.hpp:196
bool tun_send(BufferAllocated &buf) override
Definition wintun.hpp:128
RCPtr< WintunClient > Ptr
Definition wintun.hpp:13
std::string vpn_ip4() const override
Definition wintun.hpp:172
TunClientParent & parent
Definition wintun.hpp:308
size_t packet_align(size_t size)
Definition wintun.hpp:295
std::string vpn_gw4() const override
Definition wintun.hpp:188
void tun_start(const OptionList &opt, TransportClient &transcli, CryptoDCSettings &) override
Definition wintun.hpp:28
TunProp::State::Ptr state
Definition wintun.hpp:309
TunPersist::Ptr tun_persist
Definition wintun.hpp:306
ULONG wrap(ULONG value)
Definition wintun.hpp:300
#define OPENVPN_LOG(args)
#define OPENVPN_LOG_STRING(str)
DNS utilities for Windows.
openvpn_io::windows::stream_handle TAPStream
TunPersistTemplate< ScopedTAPStream, TunPersistState< RingBuffer::Ptr > > TunPersist
std::wstring module_name()
Definition modname.hpp:29
bool is_safe_conversion(InT inVal)
Returns true if the given value can be contained by the out type.
CleanupType< F > Cleanup(F method) noexcept
Definition cleanup.hpp:43
#define WINTUN_PACKET_ALIGN
#define WINTUN_MAX_PACKET_SIZE
#define WINTUN_RING_CAPACITY
virtual IP::Addr server_endpoint_addr() const =0
virtual void tun_error(const Error::Type fatal_err, const std::string &err_text)=0
virtual void tun_connected()=0
virtual void tun_pre_tun_config()=0
virtual void tun_recv(BufferAllocated &buf)=0
std::atomic_ulong head
std::atomic_ulong tail
std::atomic_long alertable
UCHAR data[WINTUN_RING_CAPACITY+WINTUN_RING_TRAILING_BYTES+WINTUN_RING_FRAMING_SIZE]
UCHAR data[WINTUN_MAX_PACKET_SIZE]
Definition wintun.hpp:292