OpenVPN 3 Core Library
Loading...
Searching...
No Matches
ovpndcocli.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// Copyright (C) 2020-2022 Lev Stipakov <lev@openvpn.net>
9//
10// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
11//
12
13
14// tun/transport client for ovpn-dco
15
18
19class OvpnDcoClient : public Client,
20 public KoRekey::Receiver,
23{
24 friend class ClientConfig;
25 friend class GeNL;
26
27 OPENVPN_EXCEPTION(dcocli_error);
28
31
33 {
34 typedef std::unique_ptr<PacketFrom> SPtr;
36 };
37
38 public:
39 static bool available(TunBuilderBase *tb)
40 {
41 if (tb)
42 return tb->tun_builder_dco_available();
43 else
44 return GeNLImpl::available();
45 }
46
47 void tun_start(const OptionList &opt,
48 TransportClient &transcli,
49 CryptoDCSettings &dc_settings) override
50 {
51 // extract peer ID from pushed options
52 try
53 {
54 const Option *o = opt.get_ptr("peer-id");
55 if (o)
56 {
57 bool status = parse_number_validate<uint32_t>(
58 o->get(1, 16), 16, 0, OVPN_PEER_ID_UNDEF - 1, &peer_id);
59 if (!status)
60 OPENVPN_THROW(dcocli_error, "Parse/range issue with pushed peer-id");
61 }
62 else
63 {
64 OPENVPN_THROW(dcocli_error, "No peer-id pushed by server");
65 }
66 }
67 catch (const std::exception &e)
68 {
69 OPENVPN_THROW(dcocli_error, "Cannot extract peer-id: " << e.what());
70 }
71
72 // notify parent
73 tun_parent->tun_pre_tun_config();
74
75 // parse pushed options
77 TunBuilderBase *builder;
78
79 if (config->builder)
80 {
81 builder = config->builder;
82 }
83 else
84 {
85 po.reset(new TunBuilderCapture());
86 builder = po.get();
87 }
88
89 TunProp::configure_builder(builder,
90 state.get(),
91 config->transport.stats.get(),
93 config->tun.tun_prop,
94 opt,
95 nullptr,
96 false);
97
98 if (po)
99 OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl
100 << po->to_string());
101
102 if (config->builder)
103 {
104 config->builder->tun_builder_dco_establish();
105 }
106 else
107 {
108 ActionList::Ptr add_cmds = new ActionList();
109 remove_cmds.reset(new ActionListReversed());
110
111 std::vector<IP::Route> rtvec;
112
113 TUN_LINUX::tun_config(config->dev_name,
114 *po,
115 &rtvec,
116 *add_cmds,
117 *remove_cmds,
118 TunConfigFlags::ADD_BYPASS_ROUTES);
119
120 // execute commands to bring up interface
121 add_cmds->execute_log();
122 }
123
124 // Add a hook so ProtoContext will call back to
125 // rekey() on rekey ops.
126 dc_settings.set_factory(CryptoDCFactory::Ptr(new KoRekey::Factory(
127 dc_settings.factory(), this, config->transport.frame)));
128
129 // add peer in ovpn-dco
130 add_peer(peer_id,
131 state->vpn_ip4_gw.to_ipv4_zero(),
132 state->vpn_ip6_gw.to_ipv6_zero());
133 // signal that we are connected
134 tun_parent->tun_connected();
135 }
136
137 std::string tun_name() const override
138 {
139 return "ovpn-dco";
140 }
141
143 {
144 if (transport)
145 return transport->server_endpoint_addr();
146 else
147 return IP::Addr();
148 }
149
150 unsigned short server_endpoint_port() const override
151 {
152 if (transport)
153 return transport->server_endpoint_port();
154 else
155 return 0;
156 }
157
159 {
160 return transport->transport_protocol();
161 }
162
163 void transport_start() override
164 {
165 TransportClientFactory::Ptr transport_factory;
166
167 if (!config->transport.protocol.is_tcp())
168 {
169 UDPTransport::ClientConfig::Ptr udpconf = UDPTransport::ClientConfig::new_obj();
170 udpconf->remote_list = config->transport.remote_list;
171 udpconf->frame = config->transport.frame;
172 udpconf->stats = config->transport.stats;
173 udpconf->socket_protect = config->transport.socket_protect;
174 udpconf->server_addr_float = config->transport.server_addr_float;
175 transport_factory = udpconf;
176 }
177 else
178 {
179 TCPTransport::ClientConfig::Ptr tcpconf = TCPTransport::ClientConfig::new_obj();
180 tcpconf->remote_list = config->transport.remote_list;
181 tcpconf->frame = config->transport.frame;
182 tcpconf->stats = config->transport.stats;
183 tcpconf->socket_protect = config->transport.socket_protect;
184 transport_factory = tcpconf;
185 }
186
187 config->transport.stats->dco_configure(this);
188
189 transport = transport_factory->new_transport_client_obj(io_context, this);
190 transport->transport_start();
191 }
192
193 bool transport_send_const(const Buffer &buf) override
194 {
195 return transport->transport_send_const(buf);
196 }
197
198 bool transport_send(BufferAllocated &buf) override
199 {
200 OPENVPN_THROW(dcocli_error,
201 "Non-const send expected for data channel only, but "
202 "ovpndcocli is not expected to handle data packets");
203 }
204
205 void get_remote_sockaddr(struct sockaddr_storage &sa, socklen_t &salen)
206 {
207 memset(&sa, 0, sizeof(sa));
208
209 struct sockaddr_in *sa4 = (struct sockaddr_in *)&sa;
210 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&sa;
211
212 IP::Addr remote_addr = transport->server_endpoint_addr();
213 if (remote_addr.version() == IP::Addr::V4)
214 {
215 salen = sizeof(*sa4);
216 *sa4 = remote_addr.to_ipv4().to_sockaddr(transport->server_endpoint_port());
217 }
218 else
219 {
220 salen = sizeof(*sa6);
221 *sa6 = remote_addr.to_ipv6().to_sockaddr(transport->server_endpoint_port());
222 }
223 }
224
225 void del_peer(uint32_t peer_id)
226 {
227 TunBuilderBase *tb = config->builder;
228 if (tb)
229 {
230 tb->tun_builder_dco_del_peer(peer_id);
231 return;
232 }
233
234 genl->del_peer(peer_id);
235 }
236
237 void add_peer(uint32_t peer_id, IPv4::Addr ipv4, IPv6::Addr ipv6)
238 {
239 struct sockaddr_storage sa;
240 socklen_t salen;
241
242 get_remote_sockaddr(sa, salen);
243
244 TunBuilderBase *tb = config->builder;
245 if (tb)
246 {
247 tb->tun_builder_dco_new_peer(peer_id,
248 transport->native_handle(),
249 (struct sockaddr *)&sa,
250 salen,
251 ipv4,
252 ipv6);
253 return;
254 }
255
256 genl->new_peer(peer_id,
257 transport->native_handle(),
258 (struct sockaddr *)&sa,
259 salen,
260 ipv4,
261 ipv6);
262 }
263
264 void update_peer_stats(uint32_t peer_id, bool sync)
265 {
267
268 if (peer_id == OVPN_PEER_ID_UNDEF)
269 return;
270
271 TunBuilderBase *tb = config->builder;
272 if (tb)
273 {
274 tb->tun_builder_dco_get_peer(peer_id, sync);
275 queue_read_pipe(nullptr);
276 }
277 else
278 {
279 genl->get_peer(peer_id, sync);
280 }
281
282 last_delta = last_stats - old_stats;
283 }
284
285 void resolve_callback(const openvpn_io::error_code &error,
286 results_type results) override
287 {
288 }
289
290 void stop_() override
291 {
292 if (!halt)
293 {
294 /* update stats before deleting peer in kernelspace */
295 update_peer_stats(peer_id, true);
296
297 halt = true;
298
299 if (config->builder)
300 {
301 config->builder->tun_builder_teardown(true);
302 if (pipe)
303 pipe->close();
304 }
305 else
306 {
307 std::ostringstream os;
308 if (genl)
309 genl->stop();
310
311 int res = TunNetlink::iface_del(os, config->dev_name);
312 if (res != 0)
313 {
314 OPENVPN_LOG("ovpndcocli: error deleting iface ovpn:" << os.str());
315 }
316 }
317
318 if (transport)
319 transport->stop();
320 }
321 }
322
324 const KoRekey::Info &rkinfo) override
325 {
326 if (halt)
327 return;
328
329 if (config->builder)
330 rekey_impl_tb(rktype, rkinfo);
331 else
332 rekey_impl(rktype, rkinfo);
333 }
334
336 const KoRekey::Info &rkinfo)
337 {
338 KoRekey::OvpnDcoKey key(rktype, rkinfo);
339 auto kc = key();
340
341 switch (rktype)
342 {
343 case CryptoDCInstance::ACTIVATE_PRIMARY:
344 genl->new_key(OVPN_KEY_SLOT_PRIMARY, kc);
345
347 break;
348
349 case CryptoDCInstance::NEW_SECONDARY:
350
351 genl->new_key(OVPN_KEY_SLOT_SECONDARY, kc);
352 break;
353
354 case CryptoDCInstance::PRIMARY_SECONDARY_SWAP:
355
356 genl->swap_keys(peer_id);
357 break;
358
359 case CryptoDCInstance::DEACTIVATE_SECONDARY:
360
361 genl->del_key(peer_id, OVPN_KEY_SLOT_SECONDARY);
362 break;
363
364 case CryptoDCInstance::DEACTIVATE_ALL:
365 // TODO: deactivate all keys
366 OPENVPN_LOG("ovpndcocli: deactivate all keys");
367 break;
368
369 default:
370 OPENVPN_LOG("ovpndcocli: unknown rekey type: " << rktype);
371 break;
372 }
373 }
374
376 const KoRekey::Info &rkinfo)
377 {
378 KoRekey::OvpnDcoKey key(rktype, rkinfo);
379 auto kc = key();
380
381 TunBuilderBase *tb = config->builder;
382
383 switch (rktype)
384 {
385 case CryptoDCInstance::ACTIVATE_PRIMARY:
386 tb->tun_builder_dco_new_key(OVPN_KEY_SLOT_PRIMARY, kc);
387
389 break;
390
391 case CryptoDCInstance::NEW_SECONDARY:
392 tb->tun_builder_dco_new_key(OVPN_KEY_SLOT_SECONDARY, kc);
393 break;
394
395 case CryptoDCInstance::PRIMARY_SECONDARY_SWAP:
396 tb->tun_builder_dco_swap_keys(peer_id);
397 break;
398
399 case CryptoDCInstance::DEACTIVATE_SECONDARY:
400 tb->tun_builder_dco_del_key(peer_id, OVPN_KEY_SLOT_SECONDARY);
401 break;
402
403 case CryptoDCInstance::DEACTIVATE_ALL:
404 // TODO: deactivate all keys
405 OPENVPN_LOG("ovpndcocli: deactivate all keys");
406 break;
407
408 default:
409 OPENVPN_LOG("ovpndcocli: unknown rekey type: " << rktype);
410 break;
411 }
412 }
413
414 void transport_recv(BufferAllocated &buf) override
415 {
416 transport_parent->transport_recv(buf);
417 }
418
420 {
421 if (halt)
422 return false;
423
424 int8_t cmd = -1;
425 buf.read(&cmd, sizeof(cmd));
426
427 switch (cmd)
428 {
429 case OVPN_CMD_DEL_PEER:
430 {
431 uint32_t peer_id;
432 buf.read(&peer_id, sizeof(peer_id));
433 uint8_t reason;
434 buf.read(&reason, sizeof(reason));
435
436 std::ostringstream os;
437 Error::Type err;
438
439 switch (reason)
440 {
441 case OVPN_DEL_PEER_REASON_EXPIRED:
442 err = Error::TRANSPORT_ERROR;
443 os << "keepalive timeout";
444 break;
445
446 case OVPN_DEL_PEER_REASON_TRANSPORT_ERROR:
447 err = Error::TRANSPORT_ERROR;
448 os << "transport error";
449 break;
450
451 case OVPN_DEL_PEER_REASON_TEARDOWN:
452 err = Error::TRANSPORT_ERROR;
453 os << "peer deleted, id=" << peer_id << ", teardown";
454 break;
455
456 case OVPN_DEL_PEER_REASON_USERSPACE:
457 // volountary delete - do not stop client
458 OPENVPN_LOG("peer deleted, id=" << peer_id
459 << ", requested by userspace");
460 peer_id = OVPN_PEER_ID_UNDEF;
461 return true;
462
463 default:
464 err = Error::TUN_HALT;
465 os << "peer deleted, id=" << peer_id << ", reason=" << reason;
466 break;
467 }
468
469 stop_();
470 transport_parent->transport_error(err, os.str());
471 break;
472 }
473
474 case OVPN_CMD_GET_PEER:
475 {
476 struct OvpnDcoPeer peer;
477 buf.read(&peer, sizeof(peer));
478
479 last_stats = SessionStats::DCOTransportSource::Data(peer.transport.rx_bytes,
480 peer.transport.tx_bytes,
481 peer.vpn.rx_bytes,
482 peer.vpn.tx_bytes,
483 peer.transport.rx_pkts,
484 peer.transport.tx_pkts,
485 peer.vpn.rx_pkts,
486 peer.vpn.tx_pkts);
487
488 break;
489 }
490
491 case -1:
492 // consider all errors as fatal
493 stop_();
494 transport_parent->transport_error(Error::TUN_HALT, buf_to_string(buf));
495 return false;
496 break;
497
498 default:
499 OPENVPN_LOG("Unknown ovpn-dco cmd " << cmd);
500 break;
501 }
502
503 return true;
504 }
505
506 void transport_needs_send() override
507 {
508 transport_parent->transport_needs_send();
509 }
510
511 void transport_error(const Error::Type fatal_err,
512 const std::string &err_text) override
513 {
514 transport_parent->transport_error(fatal_err, err_text);
515 }
516
517 void proxy_error(const Error::Type fatal_err,
518 const std::string &err_text) override
519 {
520 transport_parent->proxy_error(fatal_err, err_text);
521 }
522
524 {
525 return transport_parent->transport_is_openvpn_protocol();
526 }
527
528 void transport_pre_resolve() override
529 {
530 transport_parent->transport_pre_resolve();
531 }
532
533 void transport_wait_proxy() override
534 {
535 transport_parent->transport_wait_proxy();
536 }
537
538 void transport_wait() override
539 {
540 transport_parent->transport_wait();
541 }
542
543 void transport_connecting() override
544 {
545 transport_parent->transport_connecting();
546 }
547
548 bool is_keepalive_enabled() const override
549 {
550 return transport_parent->is_keepalive_enabled();
551 }
552
553 void disable_keepalive(unsigned int &keepalive_ping,
554 unsigned int &keepalive_timeout) override
555 {
556 transport_parent->disable_keepalive(keepalive_ping, keepalive_timeout);
557 }
558
559 private:
560 OvpnDcoClient(openvpn_io::io_context &io_context_arg,
561 ClientConfig *config_arg,
562 TransportClientParent *parent_arg)
563 : Client(io_context_arg, config_arg, parent_arg)
564 {
565 TunBuilderBase *tb = config->builder;
566 if (tb)
567 {
568 tb->tun_builder_new();
569 // pipe fd which is used to communicate to kernel
570 int fd = tb->tun_builder_dco_enable(config->dev_name);
571 if (fd == -1)
572 {
573 stop_();
574 transport_parent->transport_error(Error::TUN_IFACE_CREATE,
575 "error creating ovpn-dco device");
576 return;
577 }
578
579 pipe.reset(new openvpn_io::posix::stream_descriptor(io_context, fd));
580 return;
581 }
582
583 std::ostringstream os;
584 int res = TunNetlink::iface_new(os, config->dev_name, "ovpn-dco");
585 if (res != 0)
586 {
587 stop_();
588 transport_parent->transport_error(Error::TUN_IFACE_CREATE, os.str());
589 return;
590 }
591
592 genl.reset(new GeNLImpl(
593 io_context_arg, if_nametoindex(config_arg->dev_name.c_str()), this));
594 }
595
597 {
598 // since userspace doesn't know anything about presense or
599 // absense of data channel traffic, ping should be handled in kernel
600 if (transport_parent->is_keepalive_enabled())
601 {
602 unsigned int keepalive_interval = 0;
603 unsigned int keepalive_timeout = 0;
604
605 // In addition to disabling userspace keepalive,
606 // this call also assigns keepalive values to provided arguments
607 // default keepalive values could be overwritten by config values,
608 // which in turn could be overwritten by pushed options
609 transport_parent->disable_keepalive(keepalive_interval,
610 keepalive_timeout);
611
612 // Allow overide of keepalive timeout
613 if (config->ping_restart_override)
614 keepalive_timeout = config->ping_restart_override;
615
616 if (config->builder)
617 {
618 config->builder->tun_builder_dco_set_peer(peer_id, keepalive_interval, keepalive_timeout);
619 }
620 else
621 {
622 // enable keepalive in kernel
623 genl->set_peer(peer_id, keepalive_interval, keepalive_timeout);
624 }
625 }
626 }
627
629 {
630 if (!pkt)
631 {
632 pkt = new PacketFrom();
633 }
634 // good enough values for control channel packets
635 pkt->buf.reset(512,
636 3072,
637 BufAllocFlags::GROW | BufAllocFlags::CONSTRUCT_ZERO | BufAllocFlags::DESTRUCT_ZERO);
638 pipe->async_read_some(
639 pkt->buf.mutable_buffer(),
640 [self = Ptr(this),
641 pkt = PacketFrom::SPtr(pkt)](const openvpn_io::error_code &error,
642 const size_t bytes_recvd) mutable
643 {
644 if (!error)
645 {
646 pkt->buf.set_size(bytes_recvd);
647 if (self->tun_read_handler(pkt->buf))
648 self->queue_read_pipe(pkt.release());
649 }
650 else
651 {
652 if (!self->halt)
653 {
654 OPENVPN_LOG("ovpn-dco pipe read error: " << error.message());
655 self->stop_();
656 self->transport_parent->transport_error(Error::TUN_HALT,
657 error.message());
658 }
659 }
660 });
661 }
662
664 {
665 if (halt)
666 {
667 /* retrieve the last stats update and erase it to avoid race conditions with other queries */
670 return delta;
671 }
672
673 update_peer_stats(peer_id, true);
674 return last_delta;
675 }
676
677 // used to communicate to kernel via privileged process
678 std::unique_ptr<openvpn_io::posix::stream_descriptor> pipe;
679
680 GeNLImpl::Ptr genl;
684};
std::string tun_name() const override
void del_peer(uint32_t peer_id)
bool transport_send(BufferAllocated &buf) override
void transport_recv(BufferAllocated &buf) override
void stop_() override
void rekey_impl_tb(const CryptoDCInstance::RekeyType rktype, const KoRekey::Info &rkinfo)
void transport_pre_resolve() override
void disable_keepalive(unsigned int &keepalive_ping, unsigned int &keepalive_timeout) override
TransportClient::Ptr transport
IP::Addr server_endpoint_addr() const override
void get_remote_sockaddr(struct sockaddr_storage &sa, socklen_t &salen)
void proxy_error(const Error::Type fatal_err, const std::string &err_text) override
void transport_error(const Error::Type fatal_err, const std::string &err_text) override
static bool available(TunBuilderBase *tb)
bool transport_is_openvpn_protocol() override
void rekey(const CryptoDCInstance::RekeyType rktype, const KoRekey::Info &rkinfo) override
void update_peer_stats(uint32_t peer_id, bool sync)
void transport_needs_send() override
void queue_read_pipe(PacketFrom *pkt)
void add_peer(uint32_t peer_id, IPv4::Addr ipv4, IPv6::Addr ipv6)
OvpnDcoClient(openvpn_io::io_context &io_context_arg, ClientConfig *config_arg, TransportClientParent *parent_arg)
void rekey_impl(const CryptoDCInstance::RekeyType rktype, const KoRekey::Info &rkinfo)
unsigned short server_endpoint_port() const override
std::unique_ptr< openvpn_io::posix::stream_descriptor > pipe
bool tun_read_handler(BufferAllocated &buf)
RCPtr< OvpnDcoClient > Ptr
void transport_connecting() override
SessionStats::DCOTransportSource::Data dco_transport_stats_delta() override
SessionStats::DCOTransportSource::Data last_delta
GeNLImpl::Ptr genl
void resolve_callback(const openvpn_io::error_code &error, results_type results) override
void transport_wait_proxy() override
friend class ClientConfig
bool is_keepalive_enabled() const override
void handle_keepalive()
void transport_wait() override
GeNL< OvpnDcoClient * > GeNLImpl
void transport_start() override
Protocol transport_protocol() const override
SessionStats::DCOTransportSource::Data last_stats
void tun_start(const OptionList &opt, TransportClient &transcli, CryptoDCSettings &dc_settings) override
bool transport_send_const(const Buffer &buf) override
OPENVPN_EXCEPTION(dcocli_error)
Private::ClientState * state
Definition ovpncli.hpp:760
const IPv4::Addr & to_ipv4() const
Definition ip.hpp:276
sockaddr_in to_sockaddr(const unsigned short port=0) const
Definition ipv4.hpp:87
Parses key information into format consumed by ovpn-dco.
const std::string * get_ptr(const size_t index, const size_t max_len) const
Definition options.hpp:212
The smart pointer class.
Definition rc.hpp:119
T * get() const noexcept
Returns the raw pointer to the object T, or nullptr.
Definition rc.hpp:321
TunBuilder methods, loosely based on the Android VpnService.Builder abstraction.
Definition base.hpp:42
virtual bool tun_builder_new()
Callback to construct a new TunBuilder. This function should be called first.
Definition base.hpp:50
#define OPENVPN_THROW(exc, stuff)
#define OPENVPN_LOG(args)
@ OVPN_KEY_SLOT_SECONDARY
Definition ovpn-dco.h:97
@ OVPN_KEY_SLOT_PRIMARY
Definition ovpn-dco.h:96
std::unique_ptr< PacketFrom > SPtr
remote_address ipv6
reroute_gw ipv4
std::ostringstream os
static const char config[]