diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml
index ac50d1d7c00a..dd1bb96c312d 100644
--- a/Documentation/netlink/specs/ovpn.yaml
+++ b/Documentation/netlink/specs/ovpn.yaml
@@ -175,6 +175,68 @@ attribute-sets:
           will advertise the tx-id to be used on the link.
         checks:
           max: 0xFFFFFF
+      -
+        name: estats
+        type: nest
+        doc: Extended statistics, per-peer drop/event counters
+        nested-attributes: peer-estats
+  -
+    name: peer-estats
+    attributes:
+      -
+        name: rx-decrypt-errors
+        type: uint
+        doc: Number of packets dropped due to decryption failure
+      -
+        name: rx-replay-errors
+        type: uint
+        doc: Number of packets dropped by the replay protection check
+      -
+        name: rx-unknown-keyid
+        type: uint
+        doc: Number of packets dropped due to unknown key ID
+      -
+        name: rx-unsupported-proto
+        type: uint
+        doc: >-
+          Number of packets dropped due to unsupported or malformed inner
+          protocol
+      -
+        name: rx-rpf-errors
+        type: uint
+        doc: Number of packets dropped by the reverse path filtering check
+      -
+        name: tx-encrypt-errors
+        type: uint
+        doc: Number of packets dropped due to encryption failure
+      -
+        name: tx-iv-exhausted
+        type: uint
+        doc: Number of packets dropped due to packet ID (IV) exhaustion
+      -
+        name: tx-no-key
+        type: uint
+        doc: Number of packets dropped due to missing primary key
+      -
+        name: tx-no-transport
+        type: uint
+        doc: Number of packets dropped due to missing transport socket
+      -
+        name: tx-gso-errors
+        type: uint
+        doc: Number of packets dropped due to GSO segmentation failure
+      -
+        name: keepalive-rx
+        type: uint
+        doc: Number of keepalive packets received from this peer
+      -
+        name: keepalive-tx
+        type: uint
+        doc: Number of keepalive packets sent to this peer
+      -
+        name: float-count
+        type: uint
+        doc: Number of times the peer endpoint floated
   -
     name: peer-new-input
     subset-of: peer
diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
index 9a66d693039a..f9379d8c9c08 100644
--- a/drivers/net/ovpn/io.c
+++ b/drivers/net/ovpn/io.c
@@ -128,8 +128,10 @@ void ovpn_decrypt_post(void *data, int ret)
 	/* crypto is done, cleanup skb CB and its members */
 	kfree(ovpn_skb_cb(skb)->crypto_tmp);
 
-	if (unlikely(ret < 0))
+	if (unlikely(ret < 0)) {
+		atomic64_inc(&peer->estats.rx_decrypt_errors);
 		goto drop;
+	}
 
 	/* PID sits after the op */
 	pid = (__force __be32 *)(skb->data + OVPN_OPCODE_SIZE);
@@ -138,6 +140,7 @@ void ovpn_decrypt_post(void *data, int ret)
 		net_err_ratelimited("%s: PKT ID RX error for peer %u: %d\n",
 				    netdev_name(peer->ovpn->dev), peer->id,
 				    ret);
+		atomic64_inc(&peer->estats.rx_replay_errors);
 		goto drop;
 	}
 
@@ -165,6 +168,7 @@ void ovpn_decrypt_post(void *data, int ret)
 			net_info_ratelimited("%s: NULL packet received from peer %u\n",
 					     netdev_name(peer->ovpn->dev),
 					     peer->id);
+			atomic64_inc(&peer->estats.rx_unsupported_proto);
 			goto drop;
 		}
 
@@ -172,6 +176,7 @@ void ovpn_decrypt_post(void *data, int ret)
 			net_dbg_ratelimited("%s: ping received from peer %u\n",
 					    netdev_name(peer->ovpn->dev),
 					    peer->id);
+			atomic64_inc(&peer->estats.keepalive_rx);
 			/* we drop the packet, but this is not a failure */
 			consume_skb(skb);
 			goto drop_nocount;
@@ -179,6 +184,7 @@ void ovpn_decrypt_post(void *data, int ret)
 
 		net_info_ratelimited("%s: unsupported protocol received from peer %u\n",
 				     netdev_name(peer->ovpn->dev), peer->id);
+		atomic64_inc(&peer->estats.rx_unsupported_proto);
 		goto drop;
 	}
 	skb->protocol = proto;
@@ -193,6 +199,7 @@ void ovpn_decrypt_post(void *data, int ret)
 			net_dbg_ratelimited("%s: RPF dropped packet from peer %u, src: %pI4\n",
 					    netdev_name(peer->ovpn->dev),
 					    peer->id, &ip_hdr(skb)->saddr);
+		atomic64_inc(&peer->estats.rx_rpf_errors);
 		goto drop;
 	}
 
@@ -225,6 +232,7 @@ void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb)
 		net_info_ratelimited("%s: no available key for peer %u, key-id: %u\n",
 				     netdev_name(peer->ovpn->dev), peer->id,
 				     key_id);
+		atomic64_inc(&peer->estats.rx_unknown_keyid);
 		ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
 		kfree_skb(skb);
 		ovpn_peer_put(peer);
@@ -266,19 +274,24 @@ void ovpn_encrypt_post(void *data, int ret)
 			/* let userspace know so that a new key must be negotiated */
 			ovpn_nl_key_swap_notify(peer, ks->key_id);
 
+		atomic64_inc(&peer->estats.tx_iv_exhausted);
 		goto err;
 	}
 
-	if (unlikely(ret < 0))
+	if (unlikely(ret < 0)) {
+		atomic64_inc(&peer->estats.tx_encrypt_errors);
 		goto err;
+	}
 
 	skb_mark_not_on_list(skb);
 	orig_len = skb->len;
 
 	rcu_read_lock();
 	sock = rcu_dereference(peer->sock);
-	if (unlikely(!sock))
+	if (unlikely(!sock)) {
+		atomic64_inc(&peer->estats.tx_no_transport);
 		goto err_unlock;
+	}
 
 	switch (sock->sk->sk_protocol) {
 	case IPPROTO_UDP:
@@ -289,6 +302,7 @@ void ovpn_encrypt_post(void *data, int ret)
 		break;
 	default:
 		/* no transport configured yet */
+		atomic64_inc(&peer->estats.tx_no_transport);
 		goto err_unlock;
 	}
 
@@ -315,8 +329,10 @@ static bool ovpn_encrypt_one(struct ovpn_peer *peer, struct sk_buff *skb)
 
 	/* get primary key to be used for encrypting data */
 	ks = ovpn_crypto_key_slot_primary(&peer->crypto);
-	if (unlikely(!ks))
+	if (unlikely(!ks)) {
+		atomic64_inc(&peer->estats.tx_no_key);
 		return false;
+	}
 
 	/* take a reference to the peer because the crypto code may run async.
 	 * ovpn_encrypt_post() will release it upon completion
@@ -397,6 +413,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
 			ret = PTR_ERR(segments);
 			net_err_ratelimited("%s: cannot segment payload packet: %d\n",
 					    netdev_name(dev), ret);
+			atomic64_inc(&peer->estats.tx_gso_errors);
 			goto drop;
 		}
 
diff --git a/drivers/net/ovpn/netlink-gen.c b/drivers/net/ovpn/netlink-gen.c
index 92d2fdc17c2e..d427db6c563f 100644
--- a/drivers/net/ovpn/netlink-gen.c
+++ b/drivers/net/ovpn/netlink-gen.c
@@ -63,7 +63,7 @@ const struct nla_policy ovpn_keydir_nl_policy[OVPN_A_KEYDIR_NONCE_TAIL + 1] = {
 	[OVPN_A_KEYDIR_NONCE_TAIL] = NLA_POLICY_EXACT_LEN(OVPN_NONCE_TAIL_SIZE),
 };
 
-const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_TX_ID + 1] = {
+const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_ESTATS + 1] = {
 	[OVPN_A_PEER_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_id_range),
 	[OVPN_A_PEER_REMOTE_IPV4] = { .type = NLA_BE32, },
 	[OVPN_A_PEER_REMOTE_IPV6] = NLA_POLICY_EXACT_LEN(16),
@@ -88,12 +88,29 @@ const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_TX_ID + 1] = {
 	[OVPN_A_PEER_LINK_RX_PACKETS] = { .type = NLA_UINT, },
 	[OVPN_A_PEER_LINK_TX_PACKETS] = { .type = NLA_UINT, },
 	[OVPN_A_PEER_TX_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_tx_id_range),
+	[OVPN_A_PEER_ESTATS] = NLA_POLICY_NESTED(ovpn_peer_estats_nl_policy),
 };
 
 const struct nla_policy ovpn_peer_del_input_nl_policy[OVPN_A_PEER_ID + 1] = {
 	[OVPN_A_PEER_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_id_range),
 };
 
+const struct nla_policy ovpn_peer_estats_nl_policy[OVPN_A_PEER_ESTATS_FLOAT_COUNT + 1] = {
+	[OVPN_A_PEER_ESTATS_RX_DECRYPT_ERRORS] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_ESTATS_RX_REPLAY_ERRORS] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_ESTATS_RX_UNKNOWN_KEYID] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_ESTATS_RX_UNSUPPORTED_PROTO] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_ESTATS_RX_RPF_ERRORS] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_ESTATS_TX_ENCRYPT_ERRORS] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_ESTATS_TX_IV_EXHAUSTED] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_ESTATS_TX_NO_KEY] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_ESTATS_TX_NO_TRANSPORT] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_ESTATS_TX_GSO_ERRORS] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_ESTATS_KEEPALIVE_RX] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_ESTATS_KEEPALIVE_TX] = { .type = NLA_UINT, },
+	[OVPN_A_PEER_ESTATS_FLOAT_COUNT] = { .type = NLA_UINT, },
+};
+
 const struct nla_policy ovpn_peer_new_input_nl_policy[OVPN_A_PEER_TX_ID + 1] = {
 	[OVPN_A_PEER_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_id_range),
 	[OVPN_A_PEER_REMOTE_IPV4] = { .type = NLA_BE32, },
diff --git a/drivers/net/ovpn/netlink-gen.h b/drivers/net/ovpn/netlink-gen.h
index 67cd85f86173..197e4a992d69 100644
--- a/drivers/net/ovpn/netlink-gen.h
+++ b/drivers/net/ovpn/netlink-gen.h
@@ -18,8 +18,9 @@ extern const struct nla_policy ovpn_keyconf_del_input_nl_policy[OVPN_A_KEYCONF_S
 extern const struct nla_policy ovpn_keyconf_get_nl_policy[OVPN_A_KEYCONF_CIPHER_ALG + 1];
 extern const struct nla_policy ovpn_keyconf_swap_input_nl_policy[OVPN_A_KEYCONF_PEER_ID + 1];
 extern const struct nla_policy ovpn_keydir_nl_policy[OVPN_A_KEYDIR_NONCE_TAIL + 1];
-extern const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_TX_ID + 1];
+extern const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_ESTATS + 1];
 extern const struct nla_policy ovpn_peer_del_input_nl_policy[OVPN_A_PEER_ID + 1];
+extern const struct nla_policy ovpn_peer_estats_nl_policy[OVPN_A_PEER_ESTATS_FLOAT_COUNT + 1];
 extern const struct nla_policy ovpn_peer_new_input_nl_policy[OVPN_A_PEER_TX_ID + 1];
 extern const struct nla_policy ovpn_peer_set_input_nl_policy[OVPN_A_PEER_TX_ID + 1];
 
diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c
index 4dad85294198..9e03b4cefcee 100644
--- a/drivers/net/ovpn/netlink.c
+++ b/drivers/net/ovpn/netlink.c
@@ -551,9 +551,9 @@ static int ovpn_nl_send_peer(struct sk_buff *skb, const struct genl_info *info,
 			     int flags)
 {
 	const struct ovpn_bind *bind;
+	struct nlattr *attr, *estats;
 	struct ovpn_socket *sock;
 	int ret = -EMSGSIZE;
-	struct nlattr *attr;
 	__be16 local_port;
 	void *hdr;
 	int id;
@@ -654,6 +654,41 @@ static int ovpn_nl_send_peer(struct sk_buff *skb, const struct genl_info *info,
 			 atomic64_read(&peer->link_stats.tx.packets)))
 		goto err;
 
+	estats = nla_nest_start(skb, OVPN_A_PEER_ESTATS);
+	if (!estats)
+		goto err;
+
+	if (/* drop/event counters */
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_RX_DECRYPT_ERRORS,
+			 atomic64_read(&peer->estats.rx_decrypt_errors)) ||
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_RX_REPLAY_ERRORS,
+			 atomic64_read(&peer->estats.rx_replay_errors)) ||
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_RX_UNKNOWN_KEYID,
+			 atomic64_read(&peer->estats.rx_unknown_keyid)) ||
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_RX_UNSUPPORTED_PROTO,
+			 atomic64_read(&peer->estats.rx_unsupported_proto)) ||
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_RX_RPF_ERRORS,
+			 atomic64_read(&peer->estats.rx_rpf_errors)) ||
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_TX_ENCRYPT_ERRORS,
+			 atomic64_read(&peer->estats.tx_encrypt_errors)) ||
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_TX_IV_EXHAUSTED,
+			 atomic64_read(&peer->estats.tx_iv_exhausted)) ||
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_TX_NO_KEY,
+			 atomic64_read(&peer->estats.tx_no_key)) ||
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_TX_NO_TRANSPORT,
+			 atomic64_read(&peer->estats.tx_no_transport)) ||
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_TX_GSO_ERRORS,
+			 atomic64_read(&peer->estats.tx_gso_errors)) ||
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_KEEPALIVE_RX,
+			 atomic64_read(&peer->estats.keepalive_rx)) ||
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_KEEPALIVE_TX,
+			 atomic64_read(&peer->estats.keepalive_tx)) ||
+	    nla_put_uint(skb, OVPN_A_PEER_ESTATS_FLOAT_COUNT,
+			 atomic64_read(&peer->estats.floats)))
+		goto err;
+
+	nla_nest_end(skb, estats);
+
 	nla_nest_end(skb, attr);
 	genlmsg_end(skb, hdr);
 
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index b0519f9840d8..6235c3d8414a 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -77,6 +77,10 @@ static void ovpn_peer_keepalive_send(struct work_struct *work)
 	struct ovpn_peer *peer = container_of(work, struct ovpn_peer,
 					      keepalive_work);
 
+	/* count attempted keepalives: if the TX path fails afterwards,
+	 * keepalive_tx will include a transmission that was not sent
+	 */
+	atomic64_inc(&peer->estats.keepalive_tx);
 	local_bh_disable();
 	ovpn_xmit_special(peer, ovpn_keepalive_message,
 			  sizeof(ovpn_keepalive_message));
@@ -309,6 +313,7 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
 
 	spin_unlock_bh(&peer->lock);
 
+	atomic64_inc(&peer->estats.floats);
 	ovpn_nl_peer_float_notify(peer, &ss);
 
 	/* rehashing is required only in MP mode as P2P has one peer
diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h
index dfa5c0037e02..5667bad91c31 100644
--- a/drivers/net/ovpn/peer.h
+++ b/drivers/net/ovpn/peer.h
@@ -60,6 +60,7 @@
  * @rcu: used to free peer in an RCU safe way
  * @release_entry: entry for the socket release list
  * @keepalive_work: used to schedule keepalive sending
+ * @estats: per-peer drop/event counters
  */
 struct ovpn_peer {
 	struct ovpn_priv *ovpn;
@@ -114,6 +115,7 @@ struct ovpn_peer {
 	struct rcu_head rcu;
 	struct llist_node release_entry;
 	struct work_struct keepalive_work;
+	struct ovpn_peer_estats estats;
 };
 
 /**
diff --git a/drivers/net/ovpn/stats.h b/drivers/net/ovpn/stats.h
index 3a45b97c0056..8b900e0c1121 100644
--- a/drivers/net/ovpn/stats.h
+++ b/drivers/net/ovpn/stats.h
@@ -25,6 +25,39 @@ struct ovpn_peer_stats {
 	struct ovpn_peer_stat tx;
 };
 
+/**
+ * struct ovpn_peer_estats - per-peer drop/event counters
+ * @rx_decrypt_errors: packets dropped due to decryption/auth failure
+ * @rx_replay_errors: packets dropped by the replay protection check
+ * @rx_unknown_keyid: packets dropped due to unknown key ID
+ * @rx_unsupported_proto: packets dropped due to unsupported or malformed
+ *			  inner protocol
+ * @rx_rpf_errors: packets dropped by the reverse path filtering check
+ * @tx_encrypt_errors: packets dropped due to encryption failure
+ * @tx_iv_exhausted: packets dropped due to packet ID (IV) exhaustion
+ * @tx_no_key: packets dropped due to missing primary key
+ * @tx_no_transport: packets dropped due to missing transport socket
+ * @tx_gso_errors: packets dropped due to GSO segmentation failure
+ * @keepalive_rx: keepalive packets received from this peer
+ * @keepalive_tx: keepalive packets sent to this peer
+ * @floats: number of times the peer endpoint floated
+ */
+struct ovpn_peer_estats {
+	atomic64_t rx_decrypt_errors;
+	atomic64_t rx_replay_errors;
+	atomic64_t rx_unknown_keyid;
+	atomic64_t rx_unsupported_proto;
+	atomic64_t rx_rpf_errors;
+	atomic64_t tx_encrypt_errors;
+	atomic64_t tx_iv_exhausted;
+	atomic64_t tx_no_key;
+	atomic64_t tx_no_transport;
+	atomic64_t tx_gso_errors;
+	atomic64_t keepalive_rx;
+	atomic64_t keepalive_tx;
+	atomic64_t floats;
+};
+
 void ovpn_peer_stats_init(struct ovpn_peer_stats *ps);
 
 static inline void ovpn_peer_stats_increment(struct ovpn_peer_stat *stat,
diff --git a/include/uapi/linux/ovpn.h b/include/uapi/linux/ovpn.h
index 06690090a1a9..e5ed635c0373 100644
--- a/include/uapi/linux/ovpn.h
+++ b/include/uapi/linux/ovpn.h
@@ -56,11 +56,31 @@ enum {
 	OVPN_A_PEER_LINK_RX_PACKETS,
 	OVPN_A_PEER_LINK_TX_PACKETS,
 	OVPN_A_PEER_TX_ID,
+	OVPN_A_PEER_ESTATS,
 
 	__OVPN_A_PEER_MAX,
 	OVPN_A_PEER_MAX = (__OVPN_A_PEER_MAX - 1)
 };
 
+enum {
+	OVPN_A_PEER_ESTATS_RX_DECRYPT_ERRORS = 1,
+	OVPN_A_PEER_ESTATS_RX_REPLAY_ERRORS,
+	OVPN_A_PEER_ESTATS_RX_UNKNOWN_KEYID,
+	OVPN_A_PEER_ESTATS_RX_UNSUPPORTED_PROTO,
+	OVPN_A_PEER_ESTATS_RX_RPF_ERRORS,
+	OVPN_A_PEER_ESTATS_TX_ENCRYPT_ERRORS,
+	OVPN_A_PEER_ESTATS_TX_IV_EXHAUSTED,
+	OVPN_A_PEER_ESTATS_TX_NO_KEY,
+	OVPN_A_PEER_ESTATS_TX_NO_TRANSPORT,
+	OVPN_A_PEER_ESTATS_TX_GSO_ERRORS,
+	OVPN_A_PEER_ESTATS_KEEPALIVE_RX,
+	OVPN_A_PEER_ESTATS_KEEPALIVE_TX,
+	OVPN_A_PEER_ESTATS_FLOAT_COUNT,
+
+	__OVPN_A_PEER_ESTATS_MAX,
+	OVPN_A_PEER_ESTATS_MAX = (__OVPN_A_PEER_ESTATS_MAX - 1)
+};
+
 enum {
 	OVPN_A_KEYCONF_PEER_ID = 1,
 	OVPN_A_KEYCONF_SLOT,
diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c
index f4effa7580c0..ecac71db769a 100644
--- a/tools/testing/selftests/net/ovpn/ovpn-cli.c
+++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c
@@ -893,6 +893,66 @@ static int ovpn_handle_peer(struct nl_msg *msg, void (*arg)__always_unused)
 		fprintf(stderr, "\tLINK TX packets: %" PRIu64 "\n",
 			ovpn_nla_get_uint(pattrs[OVPN_A_PEER_LINK_TX_PACKETS]));
 
+	if (pattrs[OVPN_A_PEER_ESTATS]) {
+		struct nlattr *eattrs[OVPN_A_PEER_ESTATS_MAX + 1];
+
+		nla_parse(eattrs, OVPN_A_PEER_ESTATS_MAX,
+			  nla_data(pattrs[OVPN_A_PEER_ESTATS]),
+			  nla_len(pattrs[OVPN_A_PEER_ESTATS]), NULL);
+
+		if (eattrs[OVPN_A_PEER_ESTATS_RX_DECRYPT_ERRORS])
+			fprintf(stderr, "\tRX decrypt errors: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_RX_DECRYPT_ERRORS]));
+
+		if (eattrs[OVPN_A_PEER_ESTATS_RX_REPLAY_ERRORS])
+			fprintf(stderr, "\tRX replay errors: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_RX_REPLAY_ERRORS]));
+
+		if (eattrs[OVPN_A_PEER_ESTATS_RX_UNKNOWN_KEYID])
+			fprintf(stderr, "\tRX unknown key-id: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_RX_UNKNOWN_KEYID]));
+
+		if (eattrs[OVPN_A_PEER_ESTATS_RX_UNSUPPORTED_PROTO])
+			fprintf(stderr, "\tRX unsupported/malformed proto: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_RX_UNSUPPORTED_PROTO]));
+
+		if (eattrs[OVPN_A_PEER_ESTATS_RX_RPF_ERRORS])
+			fprintf(stderr, "\tRX RPF errors: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_RX_RPF_ERRORS]));
+
+		if (eattrs[OVPN_A_PEER_ESTATS_TX_ENCRYPT_ERRORS])
+			fprintf(stderr, "\tTX encrypt errors: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_TX_ENCRYPT_ERRORS]));
+
+		if (eattrs[OVPN_A_PEER_ESTATS_TX_IV_EXHAUSTED])
+			fprintf(stderr, "\tTX IV exhausted: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_TX_IV_EXHAUSTED]));
+
+		if (eattrs[OVPN_A_PEER_ESTATS_TX_NO_KEY])
+			fprintf(stderr, "\tTX no key: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_TX_NO_KEY]));
+
+		if (eattrs[OVPN_A_PEER_ESTATS_TX_NO_TRANSPORT])
+			fprintf(stderr, "\tTX no transport: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_TX_NO_TRANSPORT]));
+
+		if (eattrs[OVPN_A_PEER_ESTATS_TX_GSO_ERRORS])
+			fprintf(stderr, "\tTX GSO errors: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_TX_GSO_ERRORS]));
+
+		if (eattrs[OVPN_A_PEER_ESTATS_KEEPALIVE_RX])
+			fprintf(stderr, "\tKeepalive RX: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_KEEPALIVE_RX]));
+
+		if (eattrs[OVPN_A_PEER_ESTATS_KEEPALIVE_TX])
+			fprintf(stderr, "\tKeepalive TX: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_KEEPALIVE_TX]));
+
+		if (eattrs[OVPN_A_PEER_ESTATS_FLOAT_COUNT])
+			fprintf(stderr, "\tFloat count: %" PRIu64 "\n",
+				ovpn_nla_get_uint(eattrs[OVPN_A_PEER_ESTATS_FLOAT_COUNT]));
+	}
+
 	return NL_SKIP;
 }
 
