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 tun_setup(opt);
73
74 // Add a hook so ProtoContext will call back to
75 // rekey() on rekey ops.
76 dc_settings.set_factory(CryptoDCFactory::Ptr(new KoRekey::Factory(
77 dc_settings.factory(), this, config->transport.frame)));
78
79 // add peer in ovpn-dco: in client mode we do not specify
80 // any peer VPN IP, because all traffic will go over the
81 // tunnel
82 add_peer(peer_id, IPv4::Addr(), IPv6::Addr());
83 // signal that we are connected
84 tun_parent->tun_connected();
85 }
86
87 std::string tun_name() const override
88 {
89 return OVPN_FAMILY_NAME;
90 }
91
93 {
94 if (transport)
95 return transport->server_endpoint_addr();
96 else
97 return IP::Addr();
98 }
99
100 unsigned short server_endpoint_port() const override
101 {
102 if (transport)
103 return transport->server_endpoint_port();
104 else
105 return 0;
106 }
107
109 {
110 return transport->transport_protocol();
111 }
112
113 void transport_start() override
114 {
115 TransportClientFactory::Ptr transport_factory;
116
117 if (!config->transport.protocol.is_tcp())
118 {
119 UDPTransport::ClientConfig::Ptr udpconf = UDPTransport::ClientConfig::new_obj();
120 udpconf->remote_list = config->transport.remote_list;
121 udpconf->frame = config->transport.frame;
122 udpconf->stats = config->transport.stats;
123 udpconf->socket_protect = config->transport.socket_protect;
124 udpconf->server_addr_float = config->transport.server_addr_float;
125 transport_factory = udpconf;
126 }
127 else
128 {
129 TCPTransport::ClientConfig::Ptr tcpconf = TCPTransport::ClientConfig::new_obj();
130 tcpconf->remote_list = config->transport.remote_list;
131 tcpconf->frame = config->transport.frame;
132 tcpconf->stats = config->transport.stats;
133 tcpconf->socket_protect = config->transport.socket_protect;
134 transport_factory = tcpconf;
135 }
136
137 config->transport.stats->dco_configure(this);
138
139 transport = transport_factory->new_transport_client_obj(io_context, this);
140 transport->transport_start();
141 }
142
143 bool transport_send_const(const Buffer &buf) override
144 {
145 return transport->transport_send_const(buf);
146 }
147
148 bool transport_send(BufferAllocated &buf) override
149 {
150 OPENVPN_THROW(dcocli_error,
151 "Non-const send expected for data channel only, but "
152 "ovpndcocli is not expected to handle data packets");
153 return true;
154 }
155
156 void get_remote_sockaddr(struct sockaddr_storage &sa, socklen_t &salen)
157 {
158 memset(&sa, 0, sizeof(sa));
159
160 struct sockaddr_in *sa4 = (struct sockaddr_in *)&sa;
161 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&sa;
162
163 IP::Addr remote_addr = transport->server_endpoint_addr();
164 if (remote_addr.version() == IP::Addr::V4)
165 {
166 salen = sizeof(*sa4);
167 *sa4 = remote_addr.to_ipv4().to_sockaddr(transport->server_endpoint_port());
168 }
169 else
170 {
171 salen = sizeof(*sa6);
172 *sa6 = remote_addr.to_ipv6().to_sockaddr(transport->server_endpoint_port());
173 }
174 }
175
176 void del_peer(uint32_t peer_id)
177 {
178 OPENVPN_LOG("Deleting DCO peer " << peer_id);
179
180 TunBuilderBase *tb = config->builder;
181 if (tb)
182 {
183 tb->tun_builder_dco_del_peer(peer_id);
184 return;
185 }
186
187 genl->del_peer(peer_id);
188 }
189
190 void add_peer(uint32_t peer_id, IPv4::Addr ipv4, IPv6::Addr ipv6)
191 {
192 struct sockaddr_storage sa;
193 socklen_t salen;
194
195 get_remote_sockaddr(sa, salen);
196
197 OPENVPN_LOG("Adding DCO peer " << peer_id << " remote "
198 << transport->server_endpoint_addr() << ":"
199 << transport->server_endpoint_port());
200
201 TunBuilderBase *tb = config->builder;
202 if (tb)
203 {
204 tb->tun_builder_dco_new_peer(peer_id,
205 transport->native_handle(),
206 (struct sockaddr *)&sa,
207 salen,
208 ipv4,
209 ipv6);
210 return;
211 }
212
213 genl->new_peer(peer_id,
214 transport->native_handle(),
215 (struct sockaddr *)&sa,
216 salen,
217 ipv4,
218 ipv6);
219 }
220
221 void update_peer_stats(uint32_t peer_id, bool sync)
222 {
224
225 OPENVPN_LOG("Updating stats for DCO peer " << peer_id);
226
227 if (peer_id == OVPN_PEER_ID_UNDEF)
228 return;
229
230 TunBuilderBase *tb = config->builder;
231 if (tb)
232 {
233 tb->tun_builder_dco_get_peer(peer_id, sync);
234 queue_read_pipe(nullptr);
235 }
236 else
237 {
238 genl->get_peer(peer_id, sync);
239 }
240
241 last_delta = last_stats - old_stats;
242 }
243
244 void resolve_callback(const openvpn_io::error_code &error,
245 results_type results) override
246 {
247 }
248
249 void stop_() override
250 {
251 if (!halt)
252 {
253 /* update stats before deleting peer in kernelspace */
254 update_peer_stats(peer_id, true);
255
256 halt = true;
257
258 if (config->builder)
259 {
260 config->builder->tun_builder_teardown(true);
261 if (pipe)
262 pipe->close();
263 }
264 else
265 {
266 std::ostringstream os;
267 if (genl)
268 genl->stop();
269
270 int res = TunNetlink::iface_del(os, config->dev_name);
271 if (res != 0)
272 {
273 OPENVPN_LOG("ovpndcocli: error deleting iface ovpn:" << os.str());
274 }
275 }
276
277 if (transport)
278 transport->stop();
279 }
280 }
281
282 void apply_push_update(const OptionList &opt, TransportClient & /* transcli */) override
283 {
284 tun_setup(opt);
285 tun_parent->tun_connected();
286 }
287
289 const KoRekey::Info &rkinfo) override
290 {
291 if (halt)
292 return;
293
294 if (config->builder)
295 rekey_impl_tb(rktype, rkinfo);
296 else
297 rekey_impl(rktype, rkinfo);
298 }
299
301 const KoRekey::Info &rkinfo)
302 {
303 KoRekey::OvpnDcoKey key(rktype, rkinfo);
304 auto kc = key();
305
306 switch (rktype)
307 {
308 case CryptoDCInstance::ACTIVATE_PRIMARY:
309 OPENVPN_LOG("Installing PRIMARY key for peer " << peer_id);
310 genl->new_key(OVPN_KEY_SLOT_PRIMARY, kc);
311
313 break;
314
315 case CryptoDCInstance::NEW_SECONDARY:
316 OPENVPN_LOG("Installing SECONDARY key for peer " << peer_id);
317 genl->new_key(OVPN_KEY_SLOT_SECONDARY, kc);
318 break;
319
320 case CryptoDCInstance::PRIMARY_SECONDARY_SWAP:
321 OPENVPN_LOG("Swapping keys for peer " << peer_id);
322 genl->swap_keys(peer_id);
323 break;
324
325 case CryptoDCInstance::DEACTIVATE_SECONDARY:
326 OPENVPN_LOG("Deleting SECONDARY key for peer " << peer_id);
327 genl->del_key(peer_id, OVPN_KEY_SLOT_SECONDARY);
328 break;
329
330 case CryptoDCInstance::DEACTIVATE_ALL:
331 OPENVPN_LOG("Deleting all keys for peer " << peer_id);
332 genl->del_key(peer_id, OVPN_KEY_SLOT_PRIMARY);
333 genl->del_key(peer_id, OVPN_KEY_SLOT_SECONDARY);
334 break;
335
336 default:
337 OPENVPN_LOG("ovpndcocli: unknown rekey type: " << rktype);
338 break;
339 }
340 }
341
343 const KoRekey::Info &rkinfo)
344 {
345 KoRekey::OvpnDcoKey key(rktype, rkinfo);
346 auto kc = key();
347
348 TunBuilderBase *tb = config->builder;
349
350 switch (rktype)
351 {
352 case CryptoDCInstance::ACTIVATE_PRIMARY:
353 tb->tun_builder_dco_new_key(OVPN_KEY_SLOT_PRIMARY, kc);
354
356 break;
357
358 case CryptoDCInstance::NEW_SECONDARY:
359 tb->tun_builder_dco_new_key(OVPN_KEY_SLOT_SECONDARY, kc);
360 break;
361
362 case CryptoDCInstance::PRIMARY_SECONDARY_SWAP:
363 tb->tun_builder_dco_swap_keys(peer_id);
364 break;
365
366 case CryptoDCInstance::DEACTIVATE_SECONDARY:
367 tb->tun_builder_dco_del_key(peer_id, OVPN_KEY_SLOT_SECONDARY);
368 break;
369
370 case CryptoDCInstance::DEACTIVATE_ALL:
371 // TODO: deactivate all keys
372 OPENVPN_LOG("ovpndcocli: deactivate all keys");
373 break;
374
375 default:
376 OPENVPN_LOG("ovpndcocli: unknown rekey type: " << rktype);
377 break;
378 }
379 }
380
381 void transport_recv(BufferAllocated &buf) override
382 {
383 transport_parent->transport_recv(buf);
384 }
385
387 {
388 if (halt)
389 return false;
390
391 int8_t cmd = -1;
392 buf.read(&cmd, sizeof(cmd));
393
394 switch (cmd)
395 {
397 {
398 uint32_t peer_id;
399 buf.read(&peer_id, sizeof(peer_id));
400 uint8_t reason;
401 buf.read(&reason, sizeof(reason));
402
403 std::ostringstream os;
404 Error::Type err;
405
406 switch (reason)
407 {
409 err = Error::TRANSPORT_ERROR;
410 os << "keepalive timeout";
411 break;
412
414 err = Error::TRANSPORT_ERROR;
415 os << "transport error";
416 break;
417
419 err = Error::TRANSPORT_ERROR;
420 os << "peer deleted, id=" << peer_id << ", teardown";
421 break;
422
424 // volountary delete - do not stop client
425 OPENVPN_LOG("peer deleted, id=" << peer_id
426 << ", requested by userspace");
427 peer_id = OVPN_PEER_ID_UNDEF;
428 return true;
429
430 default:
431 err = Error::TUN_HALT;
432 os << "peer deleted, id=" << peer_id << ", reason=" << reason;
433 break;
434 }
435
436 stop_();
437 transport_parent->transport_error(err, os.str());
438 break;
439 }
440
442 {
443 struct OvpnDcoPeer peer;
444 buf.read(&peer, sizeof(peer));
445
446 last_stats = SessionStats::DCOTransportSource::Data(peer.transport.rx_bytes,
447 peer.transport.tx_bytes,
448 peer.vpn.rx_bytes,
449 peer.vpn.tx_bytes,
450 peer.transport.rx_pkts,
451 peer.transport.tx_pkts,
452 peer.vpn.rx_pkts,
453 peer.vpn.tx_pkts);
454
455 break;
456 }
457
458 case -1:
459 // consider all errors as fatal
460 stop_();
461 transport_parent->transport_error(Error::TUN_HALT, buf_to_string(buf));
462 return false;
463 break;
464
465 default:
466 OPENVPN_LOG("Unknown ovpn-dco cmd " << cmd);
467 break;
468 }
469
470 return true;
471 }
472
473 void transport_needs_send() override
474 {
475 transport_parent->transport_needs_send();
476 }
477
478 void transport_error(const Error::Type fatal_err,
479 const std::string &err_text) override
480 {
481 transport_parent->transport_error(fatal_err, err_text);
482 }
483
484 void proxy_error(const Error::Type fatal_err,
485 const std::string &err_text) override
486 {
487 transport_parent->proxy_error(fatal_err, err_text);
488 }
489
491 {
492 return transport_parent->transport_is_openvpn_protocol();
493 }
494
495 void transport_pre_resolve() override
496 {
497 transport_parent->transport_pre_resolve();
498 }
499
500 void transport_wait_proxy() override
501 {
502 transport_parent->transport_wait_proxy();
503 }
504
505 void transport_wait() override
506 {
507 transport_parent->transport_wait();
508 }
509
510 void transport_connecting() override
511 {
512 transport_parent->transport_connecting();
513 }
514
515 bool is_keepalive_enabled() const override
516 {
517 return transport_parent->is_keepalive_enabled();
518 }
519
520 void disable_keepalive(unsigned int &keepalive_ping,
521 unsigned int &keepalive_timeout) override
522 {
523 transport_parent->disable_keepalive(keepalive_ping, keepalive_timeout);
524 }
525
526 private:
527 OvpnDcoClient(openvpn_io::io_context &io_context_arg,
528 ClientConfig *config_arg,
529 TransportClientParent *parent_arg)
530 : Client(io_context_arg, config_arg, parent_arg)
531 {
532 TunBuilderBase *tb = config->builder;
533 if (tb)
534 {
535 tb->tun_builder_new();
536 // pipe fd which is used to communicate to kernel
537 int fd = tb->tun_builder_dco_enable(config->dev_name);
538 if (fd == -1)
539 {
540 stop_();
541 transport_parent->transport_error(Error::TUN_IFACE_CREATE,
542 "error creating ovpn-dco device");
543 return;
544 }
545
546 pipe.reset(new openvpn_io::posix::stream_descriptor(io_context, fd));
547 return;
548 }
549
550 std::ostringstream os;
551 int res = TunNetlink::iface_new(os, config->dev_name, OVPN_FAMILY_NAME);
552 if (res != 0)
553 {
554 stop_();
555 transport_parent->transport_error(Error::TUN_IFACE_CREATE, os.str());
556 return;
557 }
558
559 genl.reset(new GeNLImpl(
560 io_context_arg, if_nametoindex(config_arg->dev_name.c_str()), this));
561 }
562
563 void tun_setup(const OptionList &opt)
564 {
565 // notify parent
566 tun_parent->tun_pre_tun_config();
567
568 // parse pushed options
570 TunBuilderBase *builder;
571
572 if (config->builder)
573 {
574 builder = config->builder;
575 }
576 else
577 {
578 po.reset(new TunBuilderCapture());
579 builder = po.get();
580 }
581
582 TunProp::configure_builder(builder,
583 state.get(),
584 config->transport.stats.get(),
586 config->tun.tun_prop,
587 opt,
588 nullptr,
589 false);
590
591 if (po)
592 OPENVPN_LOG("CAPTURED OPTIONS:\n"
593 << po->to_string());
594
595 if (config->builder)
596 {
597 config->builder->tun_builder_dco_establish();
598 }
599 else
600 {
601 if (remove_cmds)
602 remove_cmds->execute_log();
603
604 ActionList::Ptr add_cmds = new ActionList();
605 remove_cmds.reset(new ActionListReversed());
606
607 std::vector<IP::Route> rtvec;
608
609 TUN_LINUX::tun_config(config->dev_name,
610 *po,
611 &rtvec,
612 *add_cmds,
613 *remove_cmds,
614 TunConfigFlags::ADD_BYPASS_ROUTES);
615
616 // execute commands to bring up interface
617 add_cmds->execute_log();
618 }
619 }
620
622 {
623 // since userspace doesn't know anything about presense or
624 // absense of data channel traffic, ping should be handled in kernel
625 if (transport_parent->is_keepalive_enabled())
626 {
627 unsigned int keepalive_interval = 0;
628 unsigned int keepalive_timeout = 0;
629
630 // In addition to disabling userspace keepalive,
631 // this call also assigns keepalive values to provided arguments
632 // default keepalive values could be overwritten by config values,
633 // which in turn could be overwritten by pushed options
634 transport_parent->disable_keepalive(keepalive_interval,
635 keepalive_timeout);
636
637 // Allow overide of keepalive timeout
638 if (config->ping_restart_override)
639 keepalive_timeout = config->ping_restart_override;
640
641 if (config->builder)
642 {
643 config->builder->tun_builder_dco_set_peer(peer_id, keepalive_interval, keepalive_timeout);
644 }
645 else
646 {
647 OPENVPN_LOG("Setting DCO peer " << peer_id << " interval: " << keepalive_interval << " timeout: " << keepalive_timeout);
648
649 // enable keepalive in kernel
650 genl->set_peer(peer_id, keepalive_interval, keepalive_timeout);
651 }
652 }
653 }
654
656 {
657 if (!pkt)
658 {
659 pkt = new PacketFrom();
660 }
661 // good enough values for control channel packets
662 pkt->buf.reset(512,
663 3072,
664 BufAllocFlags::GROW | BufAllocFlags::CONSTRUCT_ZERO | BufAllocFlags::DESTRUCT_ZERO);
665 pipe->async_read_some(
666 pkt->buf.mutable_buffer(),
667 [self = Ptr(this),
668 pkt = PacketFrom::SPtr(pkt)](const openvpn_io::error_code &error,
669 const size_t bytes_recvd) mutable
670 {
671 if (!error)
672 {
673 pkt->buf.set_size(bytes_recvd);
674 if (self->tun_read_handler(pkt->buf))
675 self->queue_read_pipe(pkt.release());
676 }
677 else
678 {
679 if (!self->halt)
680 {
681 OPENVPN_LOG("ovpn-dco pipe read error: " << error.message());
682 self->stop_();
683 self->transport_parent->transport_error(Error::TUN_HALT,
684 error.message());
685 }
686 }
687 });
688 }
689
691 {
692 if (halt)
693 {
694 /* retrieve the last stats update and erase it to avoid race conditions with other queries */
697 return delta;
698 }
699
700 update_peer_stats(peer_id, true);
701 return last_delta;
702 }
703
704 // used to communicate to kernel via privileged process
705 std::unique_ptr<openvpn_io::posix::stream_descriptor> pipe;
706
707 GeNLImpl::Ptr genl;
711};
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 tun_setup(const OptionList &opt)
void apply_push_update(const OptionList &opt, TransportClient &) override
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:757
const IPv4::Addr & to_ipv4() const
Definition ip.hpp:276
sockaddr_in to_sockaddr(const unsigned short port=0) const
Definition ipv4.hpp:84
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:207
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
@ OVPN_CMD_PEER_GET
@ OVPN_CMD_PEER_DEL_NTF
#define OVPN_FAMILY_NAME
@ OVPN_DEL_PEER_REASON_EXPIRED
@ OVPN_DEL_PEER_REASON_TRANSPORT_ERROR
@ OVPN_DEL_PEER_REASON_USERSPACE
@ OVPN_DEL_PEER_REASON_TEARDOWN
std::unique_ptr< PacketFrom > SPtr
remote_address ipv6
reroute_gw ipv4
std::ostringstream os
static const char config[]