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
19
20class OvpnDcoClient : public Client,
21 public KoRekey::Receiver,
24{
25 friend class ClientConfig;
26 friend class GeNL;
27
28 OPENVPN_EXCEPTION(dcocli_error);
29
32
34 {
35 typedef std::unique_ptr<PacketFrom> SPtr;
37 };
38
39 public:
40 static bool available(TunBuilderBase *tb)
41 {
42 if (tb)
43 return tb->tun_builder_dco_available();
44 else
45 return GeNLImpl::available();
46 }
47
48 void tun_start(const OptionList &opt,
49 TransportClient &transcli,
50 CryptoDCSettings &dc_settings) override
51 {
52 // extract peer ID from pushed options
53 try
54 {
55 const Option *o = opt.get_ptr("peer-id");
56 if (o)
57 {
58 bool status = parse_number_validate<uint32_t>(
59 o->get(1, 16), 16, 0, OVPN_PEER_ID_UNDEF - 1, &peer_id);
60 if (!status)
61 OPENVPN_THROW(dcocli_error, "Parse/range issue with pushed peer-id");
62 }
63 else
64 {
65 OPENVPN_THROW(dcocli_error, "No peer-id pushed by server");
66 }
67 }
68 catch (const std::exception &e)
69 {
70 OPENVPN_THROW(dcocli_error, "Cannot extract peer-id: " << e.what());
71 }
72
73 tun_setup(opt);
74
75 // Add a hook so ProtoContext will call back to
76 // rekey() on rekey ops.
77 dc_settings.set_factory(CryptoDCFactory::Ptr(new KoRekey::Factory(
78 dc_settings.factory(), this, config->transport.frame)));
79
80 // add peer in ovpn-dco: in client mode we do not specify
81 // any peer VPN IP, because all traffic will go over the
82 // tunnel
83 add_peer(peer_id, IPv4::Addr(), IPv6::Addr());
84 // signal that we are connected
85 tun_parent->tun_connected();
86 }
87
88 std::string tun_name() const override
89 {
90 return OVPN_FAMILY_NAME;
91 }
92
94 {
95 if (transport)
96 return transport->server_endpoint_addr();
97 else
98 return IP::Addr();
99 }
100
101 unsigned short server_endpoint_port() const override
102 {
103 if (transport)
104 return transport->server_endpoint_port();
105 else
106 return 0;
107 }
108
110 {
111 return transport->transport_protocol();
112 }
113
114 void transport_start() override
115 {
116 TransportClientFactory::Ptr transport_factory;
117
118 if (!config->transport.protocol.is_tcp())
119 {
120 UDPTransport::ClientConfig::Ptr udpconf = UDPTransport::ClientConfig::new_obj();
121 udpconf->remote_list = config->transport.remote_list;
122 udpconf->frame = config->transport.frame;
123 udpconf->stats = config->transport.stats;
124 udpconf->socket_protect = config->transport.socket_protect;
125 udpconf->server_addr_float = config->transport.server_addr_float;
126 transport_factory = udpconf;
127 }
128 else
129 {
130 TCPTransport::ClientConfig::Ptr tcpconf = TCPTransport::ClientConfig::new_obj();
131 tcpconf->remote_list = config->transport.remote_list;
132 tcpconf->frame = config->transport.frame;
133 tcpconf->stats = config->transport.stats;
134 tcpconf->socket_protect = config->transport.socket_protect;
135 transport_factory = tcpconf;
136 }
137
138 config->transport.stats->dco_configure(this);
139
140 transport = transport_factory->new_transport_client_obj(io_context, this);
141 transport->transport_start();
142 }
143
144 bool transport_send_const(const Buffer &buf) override
145 {
146 return transport->transport_send_const(buf);
147 }
148
149 bool transport_send(BufferAllocated &buf) override
150 {
151 OPENVPN_THROW(dcocli_error,
152 "Non-const send expected for data channel only, but "
153 "ovpndcocli is not expected to handle data packets");
154 return true;
155 }
156
157 void get_remote_sockaddr(struct sockaddr_storage &sa, socklen_t &salen)
158 {
159 memset(&sa, 0, sizeof(sa));
160
161 struct sockaddr_in *sa4 = (struct sockaddr_in *)&sa;
162 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&sa;
163
164 IP::Addr remote_addr = transport->server_endpoint_addr();
165 if (remote_addr.version() == IP::Addr::V4)
166 {
167 salen = sizeof(*sa4);
168 *sa4 = remote_addr.to_ipv4().to_sockaddr(transport->server_endpoint_port());
169 }
170 else
171 {
172 salen = sizeof(*sa6);
173 *sa6 = remote_addr.to_ipv6().to_sockaddr(transport->server_endpoint_port());
174 }
175 }
176
177 void del_peer(uint32_t peer_id)
178 {
179 OPENVPN_LOG("Deleting DCO peer " << peer_id);
180
181 TunBuilderBase *tb = config->builder;
182 if (tb)
183 {
184 tb->tun_builder_dco_del_peer(peer_id);
185 return;
186 }
187
188 genl->del_peer(peer_id);
189 }
190
191 void add_peer(uint32_t peer_id, IPv4::Addr ipv4, IPv6::Addr ipv6)
192 {
193 struct sockaddr_storage sa;
194 socklen_t salen;
195
196 get_remote_sockaddr(sa, salen);
197
198 OPENVPN_LOG("Adding DCO peer " << peer_id << " remote "
199 << transport->server_endpoint_addr() << ":"
200 << transport->server_endpoint_port());
201
202 TunBuilderBase *tb = config->builder;
203 if (tb)
204 {
205 tb->tun_builder_dco_new_peer(peer_id,
206 transport->native_handle(),
207 (struct sockaddr *)&sa,
208 salen,
209 ipv4,
210 ipv6);
211 return;
212 }
213
214 genl->new_peer(peer_id,
215 transport->native_handle(),
216 (struct sockaddr *)&sa,
217 salen,
218 ipv4,
219 ipv6);
220 }
221
222 void update_peer_stats(uint32_t peer_id, bool sync)
223 {
225
226 OPENVPN_LOG("Updating stats for DCO peer " << peer_id);
227
228 if (peer_id == OVPN_PEER_ID_UNDEF)
229 return;
230
231 TunBuilderBase *tb = config->builder;
232 if (tb)
233 {
234 tb->tun_builder_dco_get_peer(peer_id, sync);
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 if (peer_id != this->peer_id)
407 {
408 OPENVPN_LOG("received DEL_PEER for " << peer_id << " while expecting " << this->peer_id);
409 break;
410 }
411
412 switch (reason)
413 {
415 err = Error::TRANSPORT_ERROR;
416 os << "keepalive timeout";
417 break;
418
420 err = Error::TRANSPORT_ERROR;
421 os << "transport error";
422 break;
423
425 err = Error::TRANSPORT_ERROR;
426 os << "peer deleted, id=" << peer_id << ", teardown";
427 break;
428
430 OPENVPN_LOG("peer deleted, id=" << peer_id
431 << ", requested by userspace");
432 return true;
433
434 default:
435 err = Error::TUN_HALT;
436 os << "peer deleted, id=" << peer_id << ", reason=" << reason;
437 break;
438 }
439
440 // peer is gone - avoid any other netlink call for it
441 this->peer_id = OVPN_PEER_ID_UNDEF;
442
443 stop_();
444 transport_parent->transport_error(err, os.str());
445 break;
446 }
447
449 {
450 struct OvpnDcoPeer peer;
451 buf.read(&peer, sizeof(peer));
452
453 last_stats = SessionStats::DCOTransportSource::Data(peer.transport.rx_bytes,
454 peer.transport.tx_bytes,
455 peer.vpn.rx_bytes,
456 peer.vpn.tx_bytes,
457 peer.transport.rx_pkts,
458 peer.transport.tx_pkts,
459 peer.vpn.rx_pkts,
460 peer.vpn.tx_pkts);
461
462 break;
463 }
464
465 case -1:
466 // consider all errors as fatal
467 stop_();
468 transport_parent->transport_error(Error::TUN_HALT, buf_to_string(buf));
469 return false;
470 break;
471
472 default:
473 OPENVPN_LOG("Unknown ovpn-dco cmd " << cmd);
474 break;
475 }
476
477 return true;
478 }
479
480 void transport_needs_send() override
481 {
482 transport_parent->transport_needs_send();
483 }
484
485 void transport_error(const Error::Type fatal_err,
486 const std::string &err_text) override
487 {
488 transport_parent->transport_error(fatal_err, err_text);
489 }
490
491 void proxy_error(const Error::Type fatal_err,
492 const std::string &err_text) override
493 {
494 transport_parent->proxy_error(fatal_err, err_text);
495 }
496
498 {
499 return transport_parent->transport_is_openvpn_protocol();
500 }
501
502 void transport_pre_resolve() override
503 {
504 transport_parent->transport_pre_resolve();
505 }
506
507 void transport_wait_proxy() override
508 {
509 transport_parent->transport_wait_proxy();
510 }
511
512 void transport_wait() override
513 {
514 transport_parent->transport_wait();
515 }
516
517 void transport_connecting() override
518 {
519 transport_parent->transport_connecting();
520 }
521
522 bool is_keepalive_enabled() const override
523 {
524 return transport_parent->is_keepalive_enabled();
525 }
526
527 void disable_keepalive(unsigned int &keepalive_ping,
528 unsigned int &keepalive_timeout) override
529 {
530 transport_parent->disable_keepalive(keepalive_ping, keepalive_timeout);
531 }
532
533 private:
534 OvpnDcoClient(openvpn_io::io_context &io_context_arg,
535 ClientConfig *config_arg,
536 TransportClientParent *parent_arg)
537 : Client(io_context_arg, config_arg, parent_arg)
538 {
539 TunBuilderBase *tb = config->builder;
540 if (tb)
541 {
542 tb->tun_builder_new();
543 // pipe fd which is used to communicate to kernel
544 int fd = tb->tun_builder_dco_enable(config->dev_name);
545 if (fd == -1)
546 {
547 stop_();
548 transport_parent->transport_error(Error::TUN_IFACE_CREATE,
549 "error creating ovpn-dco device");
550 return;
551 }
552
553 pipe.reset(new openvpn_io::posix::stream_descriptor(io_context, fd));
554 queue_read_pipe(nullptr);
555 return;
556 }
557
558 int res = -ENOENT;
559 const int max_units = 256;
560 // attempt creating the device using the provided name, in case
561 // of error append a counter starting at 1 and try again until 256.
562 for (int unit = 0; unit < max_units; ++unit)
563 {
564 std::string n = config->dev_name;
565 if (unit)
566 n += openvpn::to_string(unit);
567 if (n.length() >= IFNAMSIZ)
568 {
569 stop_();
570 transport_parent->transport_error(Error::TUN_IFACE_CREATE,
571 "DCO: ifname name too long: " + n);
572 return;
573 }
574
575
576 OPENVPN_LOG("DCO: attempting to open iface " + n);
577
578 std::ostringstream os;
579 res = TunNetlink::iface_new(os, n, OVPN_FAMILY_NAME);
580 if (res)
581 {
582 OPENVPN_LOG("DCO: couldn't open iface " + n + ": " << os.str());
583 continue;
584 }
585
586 // iface creation was successful
587 config->dev_name = n;
588 break;
589 }
590
591 // no successful creation. bail out
592 if (res != 0)
593 {
594 stop_();
595 transport_parent->transport_error(Error::TUN_IFACE_CREATE,
596 "DCO: failed to open iface " + config->dev_name
597 + " after trying " + openvpn::to_string(max_units)
598 + " units");
599 return;
600 }
601
602 genl.reset(new GeNLImpl(
603 io_context_arg, if_nametoindex(config_arg->dev_name.c_str()), this));
604 }
605
606 void tun_setup(const OptionList &opt)
607 {
608 // notify parent
609 tun_parent->tun_pre_tun_config();
610
611 // parse pushed options
613 TunBuilderBase *builder;
614
615 if (config->builder)
616 {
617 builder = config->builder;
618
619 // Reset any addresses/routes captured during a previous tun
620 // setup so that a reconfiguration -- e.g. triggered by a
621 // PUSH_UPDATE -- starts from a clean snapshot instead of
622 // accumulating duplicate entries (which break the re-establish
623 // and leave the interface unconfigured). For an active DCO
624 // session the builder keeps the ovpn-dco device, peer and keys
625 // intact across this reset.
626 builder->tun_builder_new();
627 }
628 else
629 {
630 po.reset(new TunBuilderCapture());
631 builder = po.get();
632 }
633
634 TunProp::configure_builder(builder,
635 state.get(),
636 config->transport.stats.get(),
638 config->tun.tun_prop,
639 opt,
640 nullptr,
641 false);
642
643 if (po)
644 OPENVPN_LOG("CAPTURED OPTIONS:\n"
645 << po->to_string());
646
647 if (config->builder)
648 {
649 config->builder->tun_builder_dco_establish();
650 }
651 else
652 {
653 if (remove_cmds)
654 remove_cmds->execute_log();
655
656 ActionList::Ptr add_cmds = new ActionList();
657 remove_cmds.reset(new ActionListReversed());
658
659 std::vector<IP::Route> rtvec;
660
661 TunNetlink::TunMethods::tun_config(config->dev_name,
662 *po,
663 &rtvec,
664 *add_cmds,
665 *remove_cmds,
666 TunConfigFlags::ADD_BYPASS_ROUTES);
667
668 // execute commands to bring up interface
669 add_cmds->execute_log();
670 }
671 }
672
674 {
675 // since userspace doesn't know anything about presense or
676 // absense of data channel traffic, ping should be handled in kernel
677 if (transport_parent->is_keepalive_enabled())
678 {
679 unsigned int keepalive_interval = 0;
680 unsigned int keepalive_timeout = 0;
681
682 // In addition to disabling userspace keepalive,
683 // this call also assigns keepalive values to provided arguments
684 // default keepalive values could be overwritten by config values,
685 // which in turn could be overwritten by pushed options
686 transport_parent->disable_keepalive(keepalive_interval,
687 keepalive_timeout);
688
689 // Allow overide of keepalive timeout
690 if (config->ping_restart_override)
691 keepalive_timeout = config->ping_restart_override;
692
693 if (config->builder)
694 {
695 config->builder->tun_builder_dco_set_peer(peer_id, keepalive_interval, keepalive_timeout);
696 }
697 else
698 {
699 OPENVPN_LOG("Setting DCO peer " << peer_id << " interval: " << keepalive_interval << " timeout: " << keepalive_timeout);
700
701 // enable keepalive in kernel
702 genl->set_peer(peer_id, keepalive_interval, keepalive_timeout);
703 }
704 }
705 }
706
708 {
709 if (!pkt)
710 {
711 pkt = new PacketFrom();
712 }
713 // good enough values for control channel packets
714 pkt->buf.reset(512,
715 3072,
716 BufAllocFlags::GROW | BufAllocFlags::CONSTRUCT_ZERO | BufAllocFlags::DESTRUCT_ZERO);
717 pipe->async_read_some(
718 pkt->buf.mutable_buffer(),
719 [self = Ptr(this),
720 pkt = PacketFrom::SPtr(pkt)](const openvpn_io::error_code &error,
721 const size_t bytes_recvd) mutable
722 {
723 if (!error)
724 {
725 pkt->buf.set_size(bytes_recvd);
726 if (self->tun_read_handler(pkt->buf))
727 self->queue_read_pipe(pkt.release());
728 }
729 else
730 {
731 if (!self->halt)
732 {
733 OPENVPN_LOG("ovpn-dco pipe read error: " << error.message());
734 self->stop_();
735 self->transport_parent->transport_error(Error::TUN_HALT,
736 error.message());
737 }
738 }
739 });
740 }
741
743 {
744 if (halt)
745 {
746 /* retrieve the last stats update and erase it to avoid race conditions with other queries */
749 return delta;
750 }
751
752 update_peer_stats(peer_id, true);
753 return last_delta;
754 }
755
756 // used to communicate to kernel via privileged process
757 std::unique_ptr<openvpn_io::posix::stream_descriptor> pipe;
758
759 GeNLImpl::Ptr genl;
763};
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:761
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: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
@ 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
static const char config[]