OpenVPN 3 Core Library
Loading...
Searching...
No Matches
gremlin.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12#ifndef OPENVPN_TRANSPORT_GREMLIN_H
13#define OPENVPN_TRANSPORT_GREMLIN_H
14
15#include <memory>
16#include <deque>
17#include <vector>
18#include <utility>
19#include <sstream>
20
21#include <openvpn/common/rc.hpp>
28
30
31OPENVPN_EXCEPTION(gremlin_error);
32
33struct DelayedQueue : public RC<thread_unsafe_refcount>
34{
35 public:
37
38 DelayedQueue(openvpn_io::io_context &io_context,
39 const unsigned int delay_ms)
40 : dur(Time::Duration::milliseconds(delay_ms)),
41 next_event(io_context)
42 {
43 }
44
45 template <class F>
46 void queue(F &&func_arg)
47 {
48 const bool empty = events.empty();
49 events.emplace_back(new Event<F>(Time::now() + dur, std::move(func_arg)));
50 if (empty)
51 set_timer();
52 }
53
54 size_t size() const
55 {
56 return events.size();
57 }
58
59 void stop()
60 {
61 next_event.cancel();
62 }
63
64 private:
65 struct EventBase
66 {
67 virtual void call() = 0;
68 virtual const Time &fire_time() = 0;
69 virtual ~EventBase() = default;
70 };
71
72 template <class F>
73 struct Event : public EventBase
74 {
75 public:
76 Event(Time fire_arg, F &&func_arg)
77 : fire(fire_arg),
78 func(std::move(func_arg))
79 {
80 }
81
82 void call() override
83 {
84 func();
85 }
86
87 const Time &fire_time() override
88 {
89 return fire;
90 }
91
92 private:
95 };
96
97 void set_timer()
98 {
99 if (events.empty())
100 return;
101 EventBase &ev = *events.front();
103 next_event.async_wait([self = Ptr(this)](const openvpn_io::error_code &error)
104 {
105 if (!error)
106 {
107 EventBase& ev = *self->events.front();
108 ev.call();
109 self->events.pop_front();
110 self->set_timer();
111 } });
112 }
113
114 Time::Duration dur;
116 std::deque<std::unique_ptr<EventBase>> events;
117};
118
119class Config : public RC<thread_unsafe_refcount>
120{
121 public:
123
124 Config(const std::string &config_str)
125 {
126 const std::vector<std::string> parms = string::split(config_str, ',');
127 if (parms.size() < 4)
128 throw gremlin_error("need at least 4 comma-separated values for send_delay_ms, recv_delay_ms, send_drop_prob, recv_drop_prob[, send_corrupt_prob]");
130 throw gremlin_error("send_delay_ms");
132 throw gremlin_error("recv_delay_ms");
134 throw gremlin_error("send_drop_probability");
136 throw gremlin_error("recv_drop_probability");
137 if (parms.size() >= 5 && !parse_number(string::trim_copy(parms[4]), send_corrupt_probability))
138 throw gremlin_error("send_corrupt_probability");
139 }
140
141 std::string to_string() const
142 {
143 std::ostringstream os;
144 os << '[' << send_delay_ms << ',' << recv_delay_ms << ',' << send_drop_probability << ',' << recv_drop_probability << ',' << send_corrupt_probability << ']';
145 return os.str();
146 }
147
148 unsigned int send_delay_ms = 0;
149 unsigned int recv_delay_ms = 0;
150 unsigned int send_drop_probability = 0;
151 unsigned int recv_drop_probability = 0;
152 // 1-in-N probability of flipping one bit in an outgoing data-channel
153 // packet (post-encryption). 0 disables corruption. Used to exercise
154 // server-side decrypt-fail-limit enforcement.
155 unsigned int send_corrupt_probability = 0;
156};
157
159{
160 public:
161 SendRecvQueue(openvpn_io::io_context &io_context,
162 const Config::Ptr &conf_arg,
163 const bool tcp_arg)
164 : conf(conf_arg),
165 send(new DelayedQueue(io_context, conf->send_delay_ms)),
166 recv(new DelayedQueue(io_context, conf->recv_delay_ms)),
167 tcp(tcp_arg)
168 {
169 }
170
171 template <class F>
172 void send_queue(F &&func_arg)
173 {
174 if (tcp || flip(conf->send_drop_probability))
175 send->queue(std::move(func_arg));
176 }
177
178 template <class F>
179 void recv_queue(F &&func_arg)
180 {
181 if (tcp || flip(conf->recv_drop_probability))
182 recv->queue(std::move(func_arg));
183 }
184
199 void maybe_corrupt_data(Buffer &buf, const size_t opcode_offset)
200 {
201 if (!conf->send_corrupt_probability || buf.size() <= opcode_offset + 1)
202 return;
203 const unsigned int opcode = static_cast<unsigned int>(buf.c_data()[opcode_offset]) >> 3;
204 constexpr unsigned int DATA_V1 = 6;
205 constexpr unsigned int DATA_V2 = 9;
206 if (opcode != DATA_V1 && opcode != DATA_V2)
207 return;
208 if (ri.randrange(conf->send_corrupt_probability) != 0)
209 return;
210 const size_t payload_start = opcode_offset + 1;
211 const size_t idx = payload_start + ri.randrange(buf.size() - payload_start);
212 buf.data()[idx] ^= 1u << ri.randrange(8);
213 }
214
215 size_t send_size() const
216 {
217 return send->size();
218 }
219
220 size_t recv_size() const
221 {
222 return recv->size();
223 }
224
225 void stop()
226 {
227 send->stop();
228 recv->stop();
229 }
230
231 private:
232 bool flip(const unsigned int prob)
233 {
234 if (prob)
235 return ri.randrange(prob) != 0;
236 else
237 return true;
238 }
239
244 bool tcp;
245};
246} // namespace openvpn::Gremlin
247
248#endif
std::size_t expires_at(const Time &t)
Definition asiotimer.hpp:64
const T * c_data() const
Returns a const pointer to the start of the buffer.
Definition buffer.hpp:1193
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1241
T * data()
Get a mutable pointer to the start of the array.
Definition buffer.hpp:1447
unsigned int send_delay_ms
Definition gremlin.hpp:148
std::string to_string() const
Definition gremlin.hpp:141
unsigned int recv_drop_probability
Definition gremlin.hpp:151
unsigned int send_corrupt_probability
Definition gremlin.hpp:155
unsigned int recv_delay_ms
Definition gremlin.hpp:149
Config(const std::string &config_str)
Definition gremlin.hpp:124
unsigned int send_drop_probability
Definition gremlin.hpp:150
RCPtr< Config > Ptr
Definition gremlin.hpp:122
SendRecvQueue(openvpn_io::io_context &io_context, const Config::Ptr &conf_arg, const bool tcp_arg)
Definition gremlin.hpp:161
void recv_queue(F &&func_arg)
Definition gremlin.hpp:179
bool flip(const unsigned int prob)
Definition gremlin.hpp:232
void send_queue(F &&func_arg)
Definition gremlin.hpp:172
void maybe_corrupt_data(Buffer &buf, const size_t opcode_offset)
Maybe flip one bit in an outgoing data-channel packet so the peer's AEAD/HMAC authentication fails.
Definition gremlin.hpp:199
The smart pointer class.
Definition rc.hpp:119
Reference count base class for objects tracked by RCPtr. Disallows copying and assignment.
Definition rc.hpp:908
T randrange(const T end)
Return a uniformly distributed random number in the range [0, end)
Definition randapi.hpp:117
static TimeType now()
Definition time.hpp:302
#define OPENVPN_EXCEPTION(C)
Definition exception.hpp:99
std::vector< T > split(const T &str, const typename T::value_type sep, const int maxsplit=-1)
Definition string.hpp:452
std::string trim_copy(const std::string &str)
Definition string.hpp:519
bool parse_number(const char *str, T &retval, const bool nondigit_term=false)
Definition number.hpp:35
const Time & fire_time() override
Definition gremlin.hpp:87
Event(Time fire_arg, F &&func_arg)
Definition gremlin.hpp:76
void queue(F &&func_arg)
Definition gremlin.hpp:46
DelayedQueue(openvpn_io::io_context &io_context, const unsigned int delay_ms)
Definition gremlin.hpp:38
RCPtr< DelayedQueue > Ptr
Definition gremlin.hpp:36
std::deque< std::unique_ptr< EventBase > > events
Definition gremlin.hpp:116