When DCO is active userspace doesn't see all of the traffic, so when we
access these stats we must update them.
Retrieve kernel statistics every time we access the
link_(read|write)_bytes values.
Introduce a dco_(read|write)_bytes so that we don't clobber the existing
statistics, which still count control packets, sent or received directly
through the socket.
Signed-off-by: Kristof Provost <kprovost@netgate.com>
Acked-by: Antonio Quartulli <antonio@openvpn.net>
Message-Id: <20221205164103.9190-2-kprovost@netgate.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg25618.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
(cherry picked from commit ce2b459dabc29d071be28b8ddaa0512f8c8143ec)
| ... | ... |
@@ -226,6 +226,14 @@ void dco_install_iroute(struct multi_context *m, struct multi_instance *mi, |
| 226 | 226 |
void dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi); |
| 227 | 227 |
|
| 228 | 228 |
/** |
| 229 |
+ * Update traffic statistics for all peers |
|
| 230 |
+ * |
|
| 231 |
+ * @param dco DCO device context |
|
| 232 |
+ * @param m the server context |
|
| 233 |
+ **/ |
|
| 234 |
+int dco_get_peer_stats(dco_context_t *dco, struct multi_context *m); |
|
| 235 |
+ |
|
| 236 |
+/** |
|
| 229 | 237 |
* Retrieve the list of ciphers supported by the current platform |
| 230 | 238 |
* |
| 231 | 239 |
* @return list of colon-separated ciphers |
| ... | ... |
@@ -345,6 +353,12 @@ dco_delete_iroutes(struct multi_context *m, struct multi_instance *mi) |
| 345 | 345 |
{
|
| 346 | 346 |
} |
| 347 | 347 |
|
| 348 |
+static inline int |
|
| 349 |
+dco_get_peer_stats(dco_context_t *dco, struct multi_context *m) |
|
| 350 |
+{
|
|
| 351 |
+ return 0; |
|
| 352 |
+} |
|
| 353 |
+ |
|
| 348 | 354 |
static inline const char * |
| 349 | 355 |
dco_get_supported_ciphers() |
| 350 | 356 |
{
|
| ... | ... |
@@ -37,6 +37,7 @@ |
| 37 | 37 |
#include "dco.h" |
| 38 | 38 |
#include "tun.h" |
| 39 | 39 |
#include "crypto.h" |
| 40 |
+#include "multi.h" |
|
| 40 | 41 |
#include "ssl_common.h" |
| 41 | 42 |
|
| 42 | 43 |
static nvlist_t * |
| ... | ... |
@@ -641,6 +642,86 @@ dco_event_set(dco_context_t *dco, struct event_set *es, void *arg) |
| 641 | 641 |
nvlist_destroy(nvl); |
| 642 | 642 |
} |
| 643 | 643 |
|
| 644 |
+static void |
|
| 645 |
+dco_update_peer_stat(struct multi_context *m, uint32_t peerid, const nvlist_t *nvl) |
|
| 646 |
+{
|
|
| 647 |
+ struct hash_element *he; |
|
| 648 |
+ struct hash_iterator hi; |
|
| 649 |
+ |
|
| 650 |
+ hash_iterator_init(m->hash, &hi); |
|
| 651 |
+ |
|
| 652 |
+ while ((he = hash_iterator_next(&hi))) |
|
| 653 |
+ {
|
|
| 654 |
+ struct multi_instance *mi = (struct multi_instance *) he->value; |
|
| 655 |
+ |
|
| 656 |
+ if (mi->context.c2.tls_multi->peer_id != peerid) |
|
| 657 |
+ {
|
|
| 658 |
+ continue; |
|
| 659 |
+ } |
|
| 660 |
+ |
|
| 661 |
+ mi->context.c2.dco_read_bytes = nvlist_get_number(nvl, "in"); |
|
| 662 |
+ mi->context.c2.dco_write_bytes = nvlist_get_number(nvl, "out"); |
|
| 663 |
+ |
|
| 664 |
+ return; |
|
| 665 |
+ } |
|
| 666 |
+ |
|
| 667 |
+ msg(M_INFO, "Peer %d returned by kernel, but not found locally", peerid); |
|
| 668 |
+} |
|
| 669 |
+ |
|
| 670 |
+int |
|
| 671 |
+dco_get_peer_stats(dco_context_t *dco, struct multi_context *m) |
|
| 672 |
+{
|
|
| 673 |
+ |
|
| 674 |
+ struct ifdrv drv; |
|
| 675 |
+ uint8_t buf[4096]; |
|
| 676 |
+ nvlist_t *nvl; |
|
| 677 |
+ const nvlist_t *const *nvpeers; |
|
| 678 |
+ size_t npeers; |
|
| 679 |
+ int ret; |
|
| 680 |
+ |
|
| 681 |
+ if (!dco || !dco->open) |
|
| 682 |
+ {
|
|
| 683 |
+ return 0; |
|
| 684 |
+ } |
|
| 685 |
+ |
|
| 686 |
+ CLEAR(drv); |
|
| 687 |
+ snprintf(drv.ifd_name, IFNAMSIZ, "%s", dco->ifname); |
|
| 688 |
+ drv.ifd_cmd = OVPN_GET_PEER_STATS; |
|
| 689 |
+ drv.ifd_len = sizeof(buf); |
|
| 690 |
+ drv.ifd_data = buf; |
|
| 691 |
+ |
|
| 692 |
+ ret = ioctl(dco->fd, SIOCGDRVSPEC, &drv); |
|
| 693 |
+ if (ret) |
|
| 694 |
+ {
|
|
| 695 |
+ msg(M_WARN | M_ERRNO, "Failed to get peer stats"); |
|
| 696 |
+ return -EINVAL; |
|
| 697 |
+ } |
|
| 698 |
+ |
|
| 699 |
+ nvl = nvlist_unpack(buf, drv.ifd_len, 0); |
|
| 700 |
+ if (!nvl) |
|
| 701 |
+ {
|
|
| 702 |
+ msg(M_WARN, "Failed to unpack nvlist"); |
|
| 703 |
+ return -EINVAL; |
|
| 704 |
+ } |
|
| 705 |
+ |
|
| 706 |
+ if (!nvlist_exists_nvlist_array(nvl, "peers")) |
|
| 707 |
+ {
|
|
| 708 |
+ /* no peers */ |
|
| 709 |
+ return 0; |
|
| 710 |
+ } |
|
| 711 |
+ |
|
| 712 |
+ nvpeers = nvlist_get_nvlist_array(nvl, "peers", &npeers); |
|
| 713 |
+ for (size_t i = 0; i < npeers; i++) |
|
| 714 |
+ {
|
|
| 715 |
+ const nvlist_t *peer = nvpeers[i]; |
|
| 716 |
+ uint32_t peerid = nvlist_get_number(peer, "peerid"); |
|
| 717 |
+ |
|
| 718 |
+ dco_update_peer_stat(m, peerid, nvlist_get_nvlist(peer, "bytes")); |
|
| 719 |
+ } |
|
| 720 |
+ |
|
| 721 |
+ return 0; |
|
| 722 |
+} |
|
| 723 |
+ |
|
| 644 | 724 |
const char * |
| 645 | 725 |
dco_get_supported_ciphers() |
| 646 | 726 |
{
|
| ... | ... |
@@ -399,6 +399,13 @@ dco_do_write(dco_context_t *dco, int peer_id, struct buffer *buf) |
| 399 | 399 |
return 0; |
| 400 | 400 |
} |
| 401 | 401 |
|
| 402 |
+int |
|
| 403 |
+dco_get_peer_stats(dco_context_t *dco, struct multi_context *m) |
|
| 404 |
+{
|
|
| 405 |
+ /* Not implemented. */ |
|
| 406 |
+ return 0; |
|
| 407 |
+} |
|
| 408 |
+ |
|
| 402 | 409 |
void |
| 403 | 410 |
dco_event_set(dco_context_t *dco, struct event_set *es, void *arg) |
| 404 | 411 |
{
|
| ... | ... |
@@ -538,29 +538,31 @@ multi_del_iroutes(struct multi_context *m, |
| 538 | 538 |
} |
| 539 | 539 |
|
| 540 | 540 |
static void |
| 541 |
-setenv_stats(struct context *c) |
|
| 541 |
+setenv_stats(struct multi_context *m, struct context *c) |
|
| 542 | 542 |
{
|
| 543 |
- setenv_counter(c->c2.es, "bytes_received", c->c2.link_read_bytes); |
|
| 544 |
- setenv_counter(c->c2.es, "bytes_sent", c->c2.link_write_bytes); |
|
| 543 |
+ dco_get_peer_stats(&m->top.c1.tuntap->dco, m); |
|
| 544 |
+ |
|
| 545 |
+ setenv_counter(c->c2.es, "bytes_received", c->c2.link_read_bytes + c->c2.dco_read_bytes); |
|
| 546 |
+ setenv_counter(c->c2.es, "bytes_sent", c->c2.link_write_bytes + c->c2.dco_write_bytes); |
|
| 545 | 547 |
} |
| 546 | 548 |
|
| 547 | 549 |
static void |
| 548 |
-multi_client_disconnect_setenv(struct multi_instance *mi) |
|
| 550 |
+multi_client_disconnect_setenv(struct multi_context *m, struct multi_instance *mi) |
|
| 549 | 551 |
{
|
| 550 | 552 |
/* setenv client real IP address */ |
| 551 | 553 |
setenv_trusted(mi->context.c2.es, get_link_socket_info(&mi->context)); |
| 552 | 554 |
|
| 553 | 555 |
/* setenv stats */ |
| 554 |
- setenv_stats(&mi->context); |
|
| 556 |
+ setenv_stats(m, &mi->context); |
|
| 555 | 557 |
|
| 556 | 558 |
/* setenv connection duration */ |
| 557 | 559 |
setenv_long_long(mi->context.c2.es, "time_duration", now - mi->created); |
| 558 | 560 |
} |
| 559 | 561 |
|
| 560 | 562 |
static void |
| 561 |
-multi_client_disconnect_script(struct multi_instance *mi) |
|
| 563 |
+multi_client_disconnect_script(struct multi_context *m, struct multi_instance *mi) |
|
| 562 | 564 |
{
|
| 563 |
- multi_client_disconnect_setenv(mi); |
|
| 565 |
+ multi_client_disconnect_setenv(m, mi); |
|
| 564 | 566 |
|
| 565 | 567 |
if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT)) |
| 566 | 568 |
{
|
| ... | ... |
@@ -667,7 +669,7 @@ multi_close_instance(struct multi_context *m, |
| 667 | 667 |
|
| 668 | 668 |
if (mi->context.c2.tls_multi->multi_state >= CAS_CONNECT_DONE) |
| 669 | 669 |
{
|
| 670 |
- multi_client_disconnect_script(mi); |
|
| 670 |
+ multi_client_disconnect_script(m, mi); |
|
| 671 | 671 |
} |
| 672 | 672 |
|
| 673 | 673 |
close_context(&mi->context, SIGTERM, CC_GC_FREE); |
| ... | ... |
@@ -837,6 +839,8 @@ multi_print_status(struct multi_context *m, struct status_output *so, const int |
| 837 | 837 |
|
| 838 | 838 |
status_reset(so); |
| 839 | 839 |
|
| 840 |
+ dco_get_peer_stats(&m->top.c1.tuntap->dco, m); |
|
| 841 |
+ |
|
| 840 | 842 |
if (version == 1) |
| 841 | 843 |
{
|
| 842 | 844 |
/* |
| ... | ... |
@@ -856,8 +860,8 @@ multi_print_status(struct multi_context *m, struct status_output *so, const int |
| 856 | 856 |
status_printf(so, "%s,%s," counter_format "," counter_format ",%s", |
| 857 | 857 |
tls_common_name(mi->context.c2.tls_multi, false), |
| 858 | 858 |
mroute_addr_print(&mi->real, &gc), |
| 859 |
- mi->context.c2.link_read_bytes, |
|
| 860 |
- mi->context.c2.link_write_bytes, |
|
| 859 |
+ mi->context.c2.link_read_bytes + mi->context.c2.dco_read_bytes, |
|
| 860 |
+ mi->context.c2.link_write_bytes + mi->context.c2.dco_write_bytes, |
|
| 861 | 861 |
time_string(mi->created, 0, false, &gc)); |
| 862 | 862 |
} |
| 863 | 863 |
gc_free(&gc); |
| ... | ... |
@@ -932,8 +936,8 @@ multi_print_status(struct multi_context *m, struct status_output *so, const int |
| 932 | 932 |
sep, mroute_addr_print(&mi->real, &gc), |
| 933 | 933 |
sep, print_in_addr_t(mi->reporting_addr, IA_EMPTY_IF_UNDEF, &gc), |
| 934 | 934 |
sep, print_in6_addr(mi->reporting_addr_ipv6, IA_EMPTY_IF_UNDEF, &gc), |
| 935 |
- sep, mi->context.c2.link_read_bytes, |
|
| 936 |
- sep, mi->context.c2.link_write_bytes, |
|
| 935 |
+ sep, mi->context.c2.link_read_bytes + mi->context.c2.dco_read_bytes, |
|
| 936 |
+ sep, mi->context.c2.link_write_bytes + mi->context.c2.dco_write_bytes, |
|
| 937 | 937 |
sep, time_string(mi->created, 0, false, &gc), |
| 938 | 938 |
sep, (unsigned int)mi->created, |
| 939 | 939 |
sep, tls_username(mi->context.c2.tls_multi, false), |
| ... | ... |
@@ -2752,7 +2756,7 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi) |
| 2752 | 2752 |
* did not fail */ |
| 2753 | 2753 |
if (mi->context.c2.tls_multi->multi_state == CAS_PENDING_DEFERRED_PARTIAL) |
| 2754 | 2754 |
{
|
| 2755 |
- multi_client_disconnect_script(mi); |
|
| 2755 |
+ multi_client_disconnect_script(m, mi); |
|
| 2756 | 2756 |
} |
| 2757 | 2757 |
|
| 2758 | 2758 |
mi->context.c2.tls_multi->multi_state = CAS_FAILED; |
| ... | ... |
@@ -267,8 +267,10 @@ struct context_2 |
| 267 | 267 |
counter_type tun_read_bytes; |
| 268 | 268 |
counter_type tun_write_bytes; |
| 269 | 269 |
counter_type link_read_bytes; |
| 270 |
+ counter_type dco_read_bytes; |
|
| 270 | 271 |
counter_type link_read_bytes_auth; |
| 271 | 272 |
counter_type link_write_bytes; |
| 273 |
+ counter_type dco_write_bytes; |
|
| 272 | 274 |
#ifdef PACKET_TRUNCATION_CHECK |
| 273 | 275 |
counter_type n_trunc_tun_read; |
| 274 | 276 |
counter_type n_trunc_tun_write; |