Browse code

Read DCO traffic stats from the kernel

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)

Kristof Provost authored on 2022/12/06 01:41:00
Showing 7 changed files
... ...
@@ -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
 {
... ...
@@ -911,6 +911,13 @@ nla_put_failure:
911 911
     return ret;
912 912
 }
913 913
 
914
+int
915
+dco_get_peer_stats(dco_context_t *dco, struct multi_context *m)
916
+{
917
+    /* Not implemented. */
918
+    return 0;
919
+}
920
+
914 921
 bool
915 922
 dco_available(int msglevel)
916 923
 {
... ...
@@ -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;
... ...
@@ -61,5 +61,6 @@ enum ovpn_key_cipher {
61 61
 #define OVPN_POLL_PKT           _IO('D', 10)
62 62
 #define OVPN_GET_PKT            _IO('D', 11)
63 63
 #define OVPN_SET_IFMODE         _IO('D', 12)
64
+#define OVPN_GET_PEER_STATS     _IO('D', 13)
64 65
 
65 66
 #endif /* ifndef _NET_IF_OVPN_H_ */