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 queue_read_pipe(nullptr);
236 }
237 else
238 {
239 genl->get_peer(peer_id, sync);
240 }
241
242 last_delta = last_stats - old_stats;
243 }
244
245 void resolve_callback(const openvpn_io::error_code &error,
246 results_type results) override
247 {
248 }
249
250 void stop_() override
251 {
252 if (!halt)
253 {
254 /* update stats before deleting peer in kernelspace */
255 update_peer_stats(peer_id, true);
256
257 halt = true;
258
259 if (config->builder)
260 {
261 config->builder->tun_builder_teardown(true);
262 if (pipe)
263 pipe->close();
264 }
265 else
266 {
267 std::ostringstream os;
268 if (genl)
269 genl->stop();
270
271 int res = TunNetlink::iface_del(os, config->dev_name);
272 if (res != 0)
273 {
274 OPENVPN_LOG("ovpndcocli: error deleting iface ovpn:" << os.str());
275 }
276 }
277
278 if (transport)
279 transport->stop();
280 }
281 }
282
283 void apply_push_update(const OptionList &opt, TransportClient & /* transcli */) override
284 {
285 tun_setup(opt);
286 tun_parent->tun_connected();
287 }
288
290 const KoRekey::Info &rkinfo) override
291 {
292 if (halt)
293 return;
294
295 if (config->builder)
296 rekey_impl_tb(rktype, rkinfo);
297 else
298 rekey_impl(rktype, rkinfo);
299 }
300
302 const KoRekey::Info &rkinfo)
303 {
304 KoRekey::OvpnDcoKey key(rktype, rkinfo);
305 auto kc = key();
306
307 switch (rktype)
308 {
309 case CryptoDCInstance::ACTIVATE_PRIMARY:
310 OPENVPN_LOG("Installing PRIMARY key for peer " << peer_id);
311 genl->new_key(OVPN_KEY_SLOT_PRIMARY, kc);
312
314 break;
315
316 case CryptoDCInstance::NEW_SECONDARY:
317 OPENVPN_LOG("Installing SECONDARY key for peer " << peer_id);
318 genl->new_key(OVPN_KEY_SLOT_SECONDARY, kc);
319 break;
320
321 case CryptoDCInstance::PRIMARY_SECONDARY_SWAP:
322 OPENVPN_LOG("Swapping keys for peer " << peer_id);
323 genl->swap_keys(peer_id);
324 break;
325
326 case CryptoDCInstance::DEACTIVATE_SECONDARY:
327 OPENVPN_LOG("Deleting SECONDARY key for peer " << peer_id);
328 genl->del_key(peer_id, OVPN_KEY_SLOT_SECONDARY);
329 break;
330
331 case CryptoDCInstance::DEACTIVATE_ALL:
332 OPENVPN_LOG("Deleting all keys for peer " << peer_id);
333 genl->del_key(peer_id, OVPN_KEY_SLOT_PRIMARY);
334 genl->del_key(peer_id, OVPN_KEY_SLOT_SECONDARY);
335 break;
336
337 default:
338 OPENVPN_LOG("ovpndcocli: unknown rekey type: " << rktype);
339 break;
340 }
341 }
342
344 const KoRekey::Info &rkinfo)
345 {
346 KoRekey::OvpnDcoKey key(rktype, rkinfo);
347 auto kc = key();
348
349 TunBuilderBase *tb = config->builder;
350
351 switch (rktype)
352 {
353 case CryptoDCInstance::ACTIVATE_PRIMARY:
354 tb->tun_builder_dco_new_key(OVPN_KEY_SLOT_PRIMARY, kc);
355
357 break;
358
359 case CryptoDCInstance::NEW_SECONDARY:
360 tb->tun_builder_dco_new_key(OVPN_KEY_SLOT_SECONDARY, kc);
361 break;
362
363 case CryptoDCInstance::PRIMARY_SECONDARY_SWAP:
364 tb->tun_builder_dco_swap_keys(peer_id);
365 break;
366
367 case CryptoDCInstance::DEACTIVATE_SECONDARY:
368 tb->tun_builder_dco_del_key(peer_id, OVPN_KEY_SLOT_SECONDARY);
369 break;
370
371 case CryptoDCInstance::DEACTIVATE_ALL:
372 // TODO: deactivate all keys
373 OPENVPN_LOG("ovpndcocli: deactivate all keys");
374 break;
375
376 default:
377 OPENVPN_LOG("ovpndcocli: unknown rekey type: " << rktype);
378 break;
379 }
380 }
381
382 void transport_recv(BufferAllocated &buf) override
383 {
384 transport_parent->transport_recv(buf);
385 }
386
388 {
389 if (halt)
390 return false;
391
392 int8_t cmd = -1;
393 buf.read(&cmd, sizeof(cmd));
394
395 switch (cmd)
396 {
398 {
399 uint32_t peer_id;
400 buf.read(&peer_id, sizeof(peer_id));
401 uint8_t reason;
402 buf.read(&reason, sizeof(reason));
403
404 std::ostringstream os;
405 Error::Type err;
406
407 switch (reason)
408 {
410 err = Error::TRANSPORT_ERROR;
411 os << "keepalive timeout";
412 break;
413
415 err = Error::TRANSPORT_ERROR;
416 os << "transport error";
417 break;
418
420 err = Error::TRANSPORT_ERROR;
421 os << "peer deleted, id=" << peer_id << ", teardown";
422 break;
423
425 // volountary delete - do not stop client
426 OPENVPN_LOG("peer deleted, id=" << peer_id
427 << ", requested by userspace");
428 peer_id = OVPN_PEER_ID_UNDEF;
429 return true;
430
431 default:
432 err = Error::TUN_HALT;
433 os << "peer deleted, id=" << peer_id << ", reason=" << reason;
434 break;
435 }
436
437 stop_();
438 transport_parent->transport_error(err, os.str());
439 break;
440 }
441
443 {
444 struct OvpnDcoPeer peer;
445 buf.read(&peer, sizeof(peer));
446
447 last_stats = SessionStats::DCOTransportSource::Data(peer.transport.rx_bytes,
448 peer.transport.tx_bytes,
449 peer.vpn.rx_bytes,
450 peer.vpn.tx_bytes,
451 peer.transport.rx_pkts,
452 peer.transport.tx_pkts,
453 peer.vpn.rx_pkts,
454 peer.vpn.tx_pkts);
455
456 break;
457 }
458
459 case -1:
460 // consider all errors as fatal
461 stop_();
462 transport_parent->transport_error(Error::TUN_HALT, buf_to_string(buf));
463 return false;
464 break;
465
466 default:
467 OPENVPN_LOG("Unknown ovpn-dco cmd " << cmd);
468 break;
469 }
470
471 return true;
472 }
473
474 void transport_needs_send() override
475 {
476 transport_parent->transport_needs_send();
477 }
478
479 void transport_error(const Error::Type fatal_err,
480 const std::string &err_text) override
481 {
482 transport_parent->transport_error(fatal_err, err_text);
483 }
484
485 void proxy_error(const Error::Type fatal_err,
486 const std::string &err_text) override
487 {
488 transport_parent->proxy_error(fatal_err, err_text);
489 }
490
492 {
493 return transport_parent->transport_is_openvpn_protocol();
494 }
495
496 void transport_pre_resolve() override
497 {
498 transport_parent->transport_pre_resolve();
499 }
500
501 void transport_wait_proxy() override
502 {
503 transport_parent->transport_wait_proxy();
504 }
505
506 void transport_wait() override
507 {
508 transport_parent->transport_wait();
509 }
510
511 void transport_connecting() override
512 {
513 transport_parent->transport_connecting();
514 }
515
516 bool is_keepalive_enabled() const override
517 {
518 return transport_parent->is_keepalive_enabled();
519 }
520
521 void disable_keepalive(unsigned int &keepalive_ping,
522 unsigned int &keepalive_timeout) override
523 {
524 transport_parent->disable_keepalive(keepalive_ping, keepalive_timeout);
525 }
526
527 private:
528 OvpnDcoClient(openvpn_io::io_context &io_context_arg,
529 ClientConfig *config_arg,
530 TransportClientParent *parent_arg)
531 : Client(io_context_arg, config_arg, parent_arg)
532 {
533 TunBuilderBase *tb = config->builder;
534 if (tb)
535 {
536 tb->tun_builder_new();
537 // pipe fd which is used to communicate to kernel
538 int fd = tb->tun_builder_dco_enable(config->dev_name);
539 if (fd == -1)
540 {
541 stop_();
542 transport_parent->transport_error(Error::TUN_IFACE_CREATE,
543 "error creating ovpn-dco device");
544 return;
545 }
546
547 pipe.reset(new openvpn_io::posix::stream_descriptor(io_context, fd));
548 return;
549 }
550
551 int res = -ENOENT;
552 const int max_units = 256;
553 // attempt creating the device using the provided name, in case
554 // of error append a counter starting at 1 and try again until 256.
555 for (int unit = 0; unit < max_units; ++unit)
556 {
557 std::string n = config->dev_name;
558 if (unit)
559 n += openvpn::to_string(unit);
560 if (n.length() >= IFNAMSIZ)
561 {
562 stop_();
563 transport_parent->transport_error(Error::TUN_IFACE_CREATE,
564 "DCO: ifname name too long: " + n);
565 return;
566 }
567
568
569 OPENVPN_LOG("DCO: attempting to open iface " + n);
570
571 std::ostringstream os;
572 res = TunNetlink::iface_new(os, n, OVPN_FAMILY_NAME);
573 if (res)
574 {
575 OPENVPN_LOG("DCO: couldn't open iface " + n + ": " << os.str());
576 continue;
577 }
578
579 // iface creation was successful
580 config->dev_name = n;
581 break;
582 }
583
584 // no successful creation. bail out
585 if (res != 0)
586 {
587 stop_();
588 transport_parent->transport_error(Error::TUN_IFACE_CREATE,
589 "DCO: failed to open iface " + config->dev_name
590 + " after trying " + openvpn::to_string(max_units)
591 + " units");
592 return;
593 }
594
595 genl.reset(new GeNLImpl(
596 io_context_arg, if_nametoindex(config_arg->dev_name.c_str()), this));
597 }
598
599 void tun_setup(const OptionList &opt)
600 {
601 // notify parent
602 tun_parent->tun_pre_tun_config();
603
604 // parse pushed options
606 TunBuilderBase *builder;
607
608 if (config->builder)
609 {
610 builder = config->builder;
611 }
612 else
613 {
614 po.reset(new TunBuilderCapture());
615 builder = po.get();
616 }
617
618 TunProp::configure_builder(builder,
619 state.get(),
620 config->transport.stats.get(),
622 config->tun.tun_prop,
623 opt,
624 nullptr,
625 false);
626
627 if (po)
628 OPENVPN_LOG("CAPTURED OPTIONS:\n"
629 << po->to_string());
630
631 if (config->builder)
632 {
633 config->builder->tun_builder_dco_establish();
634 }
635 else
636 {
637 if (remove_cmds)
638 remove_cmds->execute_log();
639
640 ActionList::Ptr add_cmds = new ActionList();
641 remove_cmds.reset(new ActionListReversed());
642
643 std::vector<IP::Route> rtvec;
644
645 TunNetlink::TunMethods::tun_config(config->dev_name,
646 *po,
647 &rtvec,
648 *add_cmds,
649 *remove_cmds,
650 TunConfigFlags::ADD_BYPASS_ROUTES);
651
652 // execute commands to bring up interface
653 add_cmds->execute_log();
654 }
655 }
656
658 {
659 // since userspace doesn't know anything about presense or
660 // absense of data channel traffic, ping should be handled in kernel
661 if (transport_parent->is_keepalive_enabled())
662 {
663 unsigned int keepalive_interval = 0;
664 unsigned int keepalive_timeout = 0;
665
666 // In addition to disabling userspace keepalive,
667 // this call also assigns keepalive values to provided arguments
668 // default keepalive values could be overwritten by config values,
669 // which in turn could be overwritten by pushed options
670 transport_parent->disable_keepalive(keepalive_interval,
671 keepalive_timeout);
672
673 // Allow overide of keepalive timeout
674 if (config->ping_restart_override)
675 keepalive_timeout = config->ping_restart_override;
676
677 if (config->builder)
678 {
679 config->builder->tun_builder_dco_set_peer(peer_id, keepalive_interval, keepalive_timeout);
680 }
681 else
682 {
683 OPENVPN_LOG("Setting DCO peer " << peer_id << " interval: " << keepalive_interval << " timeout: " << keepalive_timeout);
684
685 // enable keepalive in kernel
686 genl->set_peer(peer_id, keepalive_interval, keepalive_timeout);
687 }
688 }
689 }
690
692 {
693 if (!pkt)
694 {
695 pkt = new PacketFrom();
696 }
697 // good enough values for control channel packets
698 pkt->buf.reset(512,
699 3072,
700 BufAllocFlags::GROW | BufAllocFlags::CONSTRUCT_ZERO | BufAllocFlags::DESTRUCT_ZERO);
701 pipe->async_read_some(
702 pkt->buf.mutable_buffer(),
703 [self = Ptr(this),
704 pkt = PacketFrom::SPtr(pkt)](const openvpn_io::error_code &error,
705 const size_t bytes_recvd) mutable
706 {
707 if (!error)
708 {
709 pkt->buf.set_size(bytes_recvd);
710 if (self->tun_read_handler(pkt->buf))
711 self->queue_read_pipe(pkt.release());
712 }
713 else
714 {
715 if (!self->halt)
716 {
717 OPENVPN_LOG("ovpn-dco pipe read error: " << error.message());
718 self->stop_();
719 self->transport_parent->transport_error(Error::TUN_HALT,
720 error.message());
721 }
722 }
723 });
724 }
725
727 {
728 if (halt)
729 {
730 /* retrieve the last stats update and erase it to avoid race conditions with other queries */
733 return delta;
734 }
735
736 update_peer_stats(peer_id, true);
737 return last_delta;
738 }
739
740 // used to communicate to kernel via privileged process
741 std::unique_ptr<openvpn_io::posix::stream_descriptor> pipe;
742
743 GeNLImpl::Ptr genl;
747};
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: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
#define OVPN_FAMILY_NAME
@ OVPN_CMD_PEER_GET
@ OVPN_CMD_PEER_DEL_NTF
@ 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[]