git: 8b630fa9ef6e - main - if_ovpn: implement OVPN_GET_PEER_STATS

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Wed, 14 Dec 2022 05:49:44 UTC
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=8b630fa9ef6e2eecc8425e62ea31194c4d0e410e

commit 8b630fa9ef6e2eecc8425e62ea31194c4d0e410e
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2022-11-27 11:58:40 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2022-12-14 05:48:58 +0000

    if_ovpn: implement OVPN_GET_PEER_STATS
    
    Allow userspace to retrieve per-peer traffic stats.
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D37604
---
 sys/net/if_ovpn.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 sys/net/if_ovpn.h |  1 +
 2 files changed, 58 insertions(+)

diff --git a/sys/net/if_ovpn.c b/sys/net/if_ovpn.c
index 6686315fbf01..2cfe55568348 100644
--- a/sys/net/if_ovpn.c
+++ b/sys/net/if_ovpn.c
@@ -1212,6 +1212,60 @@ error:
 	return (ret);
 }
 
+static int
+ovpn_get_peer_stats(struct ovpn_softc *sc, nvlist_t **nvl)
+{
+	struct ovpn_kpeer *peer;
+	nvlist_t *nvpeer = NULL;
+	int ret;
+
+	OVPN_RLOCK_TRACKER;
+
+	*nvl = nvlist_create(0);
+	if (*nvl == NULL)
+		return (ENOMEM);
+
+#define OVPN_PEER_COUNTER_OUT(name, in, out) \
+	do { \
+		ret = ovpn_add_counters(nvpeer, name, \
+		    peer->counters[offsetof(struct ovpn_peer_counters, in) / \
+		    sizeof(uint64_t)], \
+		    peer->counters[offsetof(struct ovpn_peer_counters, out) / \
+		    sizeof(uint64_t)]); \
+		if (ret != 0) \
+			goto error; \
+	} while(0)
+
+	OVPN_RLOCK(sc);
+	RB_FOREACH(peer, ovpn_kpeers, &sc->peers) {
+		nvpeer = nvlist_create(0);
+		if (nvpeer == NULL) {
+			OVPN_RUNLOCK(sc);
+			nvlist_destroy(*nvl);
+			*nvl = NULL;
+			return (ENOMEM);
+		}
+
+		nvlist_add_number(nvpeer, "peerid", peer->peerid);
+
+		OVPN_PEER_COUNTER_OUT("packets", pkt_in, pkt_out);
+		OVPN_PEER_COUNTER_OUT("bytes", bytes_in, bytes_out);
+
+		nvlist_append_nvlist_array(*nvl, "peers", nvpeer);
+		nvlist_destroy(nvpeer);
+	}
+#undef OVPN_PEER_COUNTER_OUT
+	OVPN_RUNLOCK(sc);
+
+	return (0);
+
+error:
+	nvlist_destroy(nvpeer);
+	nvlist_destroy(*nvl);
+	*nvl = NULL;
+	return (ret);
+}
+
 static int
 ovpn_poll_pkt(struct ovpn_softc *sc, nvlist_t **onvl)
 {
@@ -1266,6 +1320,9 @@ ovpn_ioctl_get(struct ifnet *ifp, struct ifdrv *ifd)
 	case OVPN_GET_STATS:
 		error = ovpn_get_stats(sc, &nvl);
 		break;
+	case OVPN_GET_PEER_STATS:
+		error = ovpn_get_peer_stats(sc, &nvl);
+		break;
 	case OVPN_POLL_PKT:
 		error = ovpn_poll_pkt(sc, &nvl);
 		break;
diff --git a/sys/net/if_ovpn.h b/sys/net/if_ovpn.h
index 5ea2ae6dd2ec..1c0299940c3e 100644
--- a/sys/net/if_ovpn.h
+++ b/sys/net/if_ovpn.h
@@ -66,5 +66,6 @@ enum ovpn_key_cipher {
 #define OVPN_POLL_PKT		_IO  ('D', 10)
 #define OVPN_GET_PKT		_IO  ('D', 11)
 #define OVPN_SET_IFMODE		_IO  ('D', 12)
+#define OVPN_GET_PEER_STATS	_IO  ('D', 13)
 
 #endif