diff mbox series

[v3,2/2] crypto: tools: Add cryptostat userspace

Message ID 1537351855-16618-3-git-send-email-clabbe@baylibre.com (mailing list archive)
State Accepted
Delegated to: Herbert Xu
Headers show
Series crypto: Implement a generic crypto statistics | expand

Commit Message

Corentin LABBE Sept. 19, 2018, 10:10 a.m. UTC
This patch adds an userspace tool for displaying kernel crypto API
statistics.

Signed-off-by: Corentin Labbe <clabbe@baylibre.com>
---
 tools/crypto/getstat.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 294 insertions(+)
 create mode 100644 tools/crypto/getstat.c

Comments

Ard Biesheuvel Sept. 28, 2018, 1:13 p.m. UTC | #1
On 19 September 2018 at 12:10, Corentin Labbe <clabbe@baylibre.com> wrote:
> This patch adds an userspace tool for displaying kernel crypto API
> statistics.
>
> Signed-off-by: Corentin Labbe <clabbe@baylibre.com>

How do I actually build this thing?

> ---
>  tools/crypto/getstat.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 294 insertions(+)
>  create mode 100644 tools/crypto/getstat.c
>
> diff --git a/tools/crypto/getstat.c b/tools/crypto/getstat.c
> new file mode 100644
> index 000000000000..24115173a483
> --- /dev/null
> +++ b/tools/crypto/getstat.c
> @@ -0,0 +1,294 @@
> +/* Heavily copied from libkcapi 2015 - 2017, Stephan Mueller <smueller@chronox.de> */
> +#include <errno.h>
> +#include <linux/cryptouser.h>
> +#include <linux/netlink.h>
> +#include <linux/rtnetlink.h>
> +#include <sys/types.h>
> +#include <sys/socket.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <time.h>
> +#include <unistd.h>
> +
> +#define CR_RTA(x)  ((struct rtattr *)(((char *)(x)) + NLMSG_ALIGN(sizeof(struct crypto_user_alg))))
> +
> +static int get_stat(const char *drivername)
> +{
> +       struct {
> +               struct nlmsghdr n;
> +               struct crypto_user_alg cru;
> +       } req;
> +       struct sockaddr_nl nl;
> +       int sd = 0, ret;
> +       socklen_t addr_len;
> +       struct iovec iov;
> +       struct msghdr msg;
> +       char buf[4096];
> +       struct nlmsghdr *res_n = (struct nlmsghdr *)buf;
> +       struct crypto_user_alg *cru_res = NULL;
> +       int res_len = 0;
> +       struct rtattr *tb[CRYPTOCFGA_MAX + 1];
> +       struct rtattr *rta;
> +       struct nlmsgerr *errmsg;
> +
> +       memset(&req, 0, sizeof(req));
> +       memset(&buf, 0, sizeof(buf));
> +       memset(&msg, 0, sizeof(msg));
> +
> +       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.cru));
> +       req.n.nlmsg_flags = NLM_F_REQUEST;
> +       req.n.nlmsg_type = CRYPTO_MSG_GETSTAT;
> +       req.n.nlmsg_seq = time(NULL);
> +
> +       strncpy(req.cru.cru_driver_name, drivername, strlen(drivername));
> +
> +       sd =  socket(AF_NETLINK, SOCK_RAW, NETLINK_CRYPTO);
> +       if (sd < 0) {
> +               fprintf(stderr, "Netlink error: cannot open netlink socket");
> +               return -errno;
> +       }
> +       memset(&nl, 0, sizeof(nl));
> +       nl.nl_family = AF_NETLINK;
> +       if (bind(sd, (struct sockaddr *)&nl, sizeof(nl)) < 0) {
> +               ret = -errno;
> +               fprintf(stderr, "Netlink error: cannot bind netlink socket");
> +               goto out;
> +       }
> +
> +       /* sanity check that netlink socket was successfully opened */
> +       addr_len = sizeof(nl);
> +       if (getsockname(sd, (struct sockaddr *)&nl, &addr_len) < 0) {
> +               ret = -errno;
> +               printf("Netlink error: cannot getsockname");
> +               goto out;
> +       }
> +       if (addr_len != sizeof(nl)) {
> +               ret = -errno;
> +               printf("Netlink error: wrong address length %d", addr_len);
> +               goto out;
> +       }
> +       if (nl.nl_family != AF_NETLINK) {
> +               ret = -errno;
> +               printf("Netlink error: wrong address family %d",
> +                               nl.nl_family);
> +               goto out;
> +       }
> +
> +       memset(&nl, 0, sizeof(nl));
> +       nl.nl_family = AF_NETLINK;
> +       iov.iov_base = (void *)&req.n;
> +       iov.iov_len = req.n.nlmsg_len;
> +       msg.msg_name = &nl;
> +       msg.msg_namelen = sizeof(nl);
> +       msg.msg_iov = &iov;
> +       msg.msg_iovlen = 1;
> +       if (sendmsg(sd, &msg, 0) < 0) {
> +               ret = -errno;
> +               printf("Netlink error: sendmsg failed");
> +               goto out;
> +       }
> +       memset(buf, 0, sizeof(buf));
> +       iov.iov_base = buf;
> +       while (1) {
> +               iov.iov_len = sizeof(buf);
> +               ret = recvmsg(sd, &msg, 0);
> +               if (ret < 0) {
> +                       if (errno == EINTR || errno == EAGAIN)
> +                               continue;
> +                       ret = -errno;
> +                       printf("Netlink error: netlink receive error");
> +                       goto out;
> +               }
> +               if (ret == 0) {
> +                       ret = -errno;
> +                       printf("Netlink error: no data");
> +                       goto out;
> +               }
> +               if (ret > sizeof(buf)) {
> +                       ret = -errno;
> +                       printf("Netlink error: received too much data");
> +                       goto out;
> +               }
> +               break;
> +       }
> +
> +       ret = -EFAULT;
> +       res_len = res_n->nlmsg_len;
> +       if (res_n->nlmsg_type == NLMSG_ERROR) {
> +               errmsg = NLMSG_DATA(res_n);
> +               fprintf(stderr, "Fail with %d\n", errmsg->error);
> +               ret = errmsg->error;
> +               goto out;
> +       }
> +
> +       if (res_n->nlmsg_type == CRYPTO_MSG_GETSTAT) {
> +               cru_res = NLMSG_DATA(res_n);
> +               res_len -= NLMSG_SPACE(sizeof(*cru_res));
> +       }
> +       if (res_len < 0) {
> +               printf("Netlink error: nlmsg len %d\n", res_len);
> +               goto out;
> +       }
> +
> +       if (!cru_res) {
> +               ret = -EFAULT;
> +               printf("Netlink error: no cru_res\n");
> +               goto out;
> +       }
> +
> +       rta = CR_RTA(cru_res);
> +       memset(tb, 0, sizeof(struct rtattr *) * (CRYPTOCFGA_MAX + 1));
> +       while (RTA_OK(rta, res_len)) {
> +               if ((rta->rta_type <= CRYPTOCFGA_MAX) && (!tb[rta->rta_type]))
> +                       tb[rta->rta_type] = rta;
> +               rta = RTA_NEXT(rta, res_len);
> +       }
> +       if (res_len) {
> +               printf("Netlink error: unprocessed data %d",
> +                               res_len);
> +               goto out;
> +       }
> +
> +       if (tb[CRYPTOCFGA_STAT_HASH]) {
> +               struct rtattr *rta = tb[CRYPTOCFGA_STAT_HASH];
> +               struct crypto_stat *rhash =
> +                       (struct crypto_stat *)RTA_DATA(rta);
> +               printf("%s\tHash\n\tHash: %u bytes: %llu\n\tErrors: %u\n",
> +                       drivername,
> +                       rhash->stat_hash_cnt, rhash->stat_hash_tlen,
> +                       rhash->stat_hash_err_cnt);
> +       } else if (tb[CRYPTOCFGA_STAT_COMPRESS]) {
> +               struct rtattr *rta = tb[CRYPTOCFGA_STAT_COMPRESS];
> +               struct crypto_stat *rblk =
> +                       (struct crypto_stat *)RTA_DATA(rta);
> +               printf("%s\tCompress\n\tCompress: %u bytes: %llu\n\tDecompress: %u bytes: %llu\n\tErrors: %u\n",
> +                       drivername,
> +                       rblk->stat_compress_cnt, rblk->stat_compress_tlen,
> +                       rblk->stat_decompress_cnt, rblk->stat_decompress_tlen,
> +                       rblk->stat_compress_err_cnt);
> +       } else if (tb[CRYPTOCFGA_STAT_ACOMP]) {
> +               struct rtattr *rta = tb[CRYPTOCFGA_STAT_ACOMP];
> +               struct crypto_stat *rcomp =
> +                       (struct crypto_stat *)RTA_DATA(rta);
> +               printf("%s\tACompress\n\tCompress: %u bytes: %llu\n\tDecompress: %u bytes: %llu\n\tErrors: %u\n",
> +                       drivername,
> +                       rcomp->stat_compress_cnt, rcomp->stat_compress_tlen,
> +                       rcomp->stat_decompress_cnt, rcomp->stat_decompress_tlen,
> +                       rcomp->stat_compress_err_cnt);
> +       } else if (tb[CRYPTOCFGA_STAT_AEAD]) {
> +               struct rtattr *rta = tb[CRYPTOCFGA_STAT_AEAD];
> +               struct crypto_stat *raead =
> +                       (struct crypto_stat *)RTA_DATA(rta);
> +               printf("%s\tAEAD\n\tEncrypt: %u bytes: %llu\n\tDecrypt: %u bytes: %llu\n\tErrors: %u\n",
> +                       drivername,
> +                       raead->stat_encrypt_cnt, raead->stat_encrypt_tlen,
> +                       raead->stat_decrypt_cnt, raead->stat_decrypt_tlen,
> +                       raead->stat_aead_err_cnt);
> +       } else if (tb[CRYPTOCFGA_STAT_BLKCIPHER]) {
> +               struct rtattr *rta = tb[CRYPTOCFGA_STAT_BLKCIPHER];
> +               struct crypto_stat *rblk =
> +                       (struct crypto_stat *)RTA_DATA(rta);
> +               printf("%s\tCipher\n\tEncrypt: %u bytes: %llu\n\tDecrypt: %u bytes: %llu\n\tErrors: %u\n",
> +                       drivername,
> +                       rblk->stat_encrypt_cnt, rblk->stat_encrypt_tlen,
> +                       rblk->stat_decrypt_cnt, rblk->stat_decrypt_tlen,
> +                       rblk->stat_cipher_err_cnt);
> +       } else if (tb[CRYPTOCFGA_STAT_AKCIPHER]) {
> +               struct rtattr *rta = tb[CRYPTOCFGA_STAT_AKCIPHER];
> +               struct crypto_stat *rblk =
> +                       (struct crypto_stat *)RTA_DATA(rta);
> +               printf("%s\tAkcipher\n\tEncrypt: %u bytes: %llu\n\tDecrypt: %u bytes: %llu\n\tSign: %u\n\tVerify: %u\n\tErrors: %u\n",
> +                       drivername,
> +                       rblk->stat_encrypt_cnt, rblk->stat_encrypt_tlen,
> +                       rblk->stat_decrypt_cnt, rblk->stat_decrypt_tlen,
> +                       rblk->stat_sign_cnt, rblk->stat_verify_cnt,
> +                       rblk->stat_akcipher_err_cnt);
> +       } else if (tb[CRYPTOCFGA_STAT_CIPHER]) {
> +               struct rtattr *rta = tb[CRYPTOCFGA_STAT_CIPHER];
> +               struct crypto_stat *rblk =
> +                       (struct crypto_stat *)RTA_DATA(rta);
> +               printf("%s\tcipher\n\tEncrypt: %u bytes: %llu\n\tDecrypt: %u bytes: %llu\n\tErrors: %u\n",
> +                       drivername,
> +                       rblk->stat_encrypt_cnt, rblk->stat_encrypt_tlen,
> +                       rblk->stat_decrypt_cnt, rblk->stat_decrypt_tlen,
> +                       rblk->stat_cipher_err_cnt);
> +       } else if (tb[CRYPTOCFGA_STAT_RNG]) {
> +               struct rtattr *rta = tb[CRYPTOCFGA_STAT_RNG];
> +               struct crypto_stat *rrng =
> +                       (struct crypto_stat *)RTA_DATA(rta);
> +               printf("%s\tRNG\n\tSeed: %u\n\tGenerate: %u bytes: %llu\n\tErrors: %u\n",
> +                       drivername,
> +                       rrng->stat_seed_cnt,
> +                       rrng->stat_generate_cnt, rrng->stat_generate_tlen,
> +                       rrng->stat_rng_err_cnt);
> +       } else if (tb[CRYPTOCFGA_STAT_KPP]) {
> +               struct rtattr *rta = tb[CRYPTOCFGA_STAT_KPP];
> +               struct crypto_stat *rkpp =
> +                       (struct crypto_stat *)RTA_DATA(rta);
> +               printf("%s\tKPP\n\tSetsecret: %u\n\tGenerate public key: %u\n\tCompute_shared_secret: %u\n\tErrors: %u\n",
> +                       drivername,
> +                       rkpp->stat_setsecret_cnt,
> +                       rkpp->stat_generate_public_key_cnt,
> +                       rkpp->stat_compute_shared_secret_cnt,
> +                       rkpp->stat_kpp_err_cnt);
> +       } else {
> +               fprintf(stderr, "%s is of an unknown algorithm\n", drivername);
> +       }
> +       ret = 0;
> +out:
> +       close(sd);
> +       return ret;
> +}
> +
> +int main(int argc, const char *argv[])
> +{
> +       char buf[4096];
> +       FILE *procfd;
> +       int i, lastspace;
> +       int ret;
> +
> +       procfd = fopen("/proc/crypto", "r");
> +       if (!procfd) {
> +               ret = errno;
> +               fprintf(stderr, "Cannot open /proc/crypto %s\n", strerror(errno));
> +               return ret;
> +       }
> +       if (argc > 1) {
> +               if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
> +                       printf("Usage: %s [-h|--help] display this help\n", argv[0]);
> +                       printf("Usage: %s display all crypto statistics\n", argv[0]);
> +                       printf("Usage: %s drivername1 drivername2 ... = display crypto statistics about drivername1 ...\n", argv[0]);
> +                       return 0;
> +               }
> +               for (i = 1; i < argc; i++) {
> +                       ret = get_stat(argv[i]);
> +                       if (ret) {
> +                               fprintf(stderr, "Failed with %s\n", strerror(-ret));
> +                               return ret;
> +                       }
> +               }
> +               return 0;
> +       }
> +
> +       while (fgets(buf, sizeof(buf), procfd)) {
> +               if (!strncmp(buf, "driver", 6)) {
> +                       lastspace = 0;
> +                       i = 0;
> +                       while (i < strlen(buf)) {
> +                               i++;
> +                               if (buf[i] == ' ')
> +                                       lastspace = i;
> +                       }
> +                       buf[strlen(buf) - 1] = '\0';
> +                       ret = get_stat(buf + lastspace + 1);
> +                       if (ret) {
> +                               fprintf(stderr, "Failed with %s\n", strerror(-ret));
> +                               goto out;
> +                       }
> +               }
> +       }
> +out:
> +       fclose(procfd);
> +       return ret;
> +}
> --
> 2.16.4
>
Ard Biesheuvel Sept. 28, 2018, 5:03 p.m. UTC | #2
On 28 September 2018 at 15:13, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> On 19 September 2018 at 12:10, Corentin Labbe <clabbe@baylibre.com> wrote:
>> This patch adds an userspace tool for displaying kernel crypto API
>> statistics.
>>
>> Signed-off-by: Corentin Labbe <clabbe@baylibre.com>
>
> How do I actually build this thing?
>

Never mind.
Corentin LABBE Oct. 1, 2018, 7:20 a.m. UTC | #3
On Fri, Sep 28, 2018 at 07:03:11PM +0200, Ard Biesheuvel wrote:
> On 28 September 2018 at 15:13, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
> > On 19 September 2018 at 12:10, Corentin Labbe <clabbe@baylibre.com> wrote:
> >> This patch adds an userspace tool for displaying kernel crypto API
> >> statistics.
> >>
> >> Signed-off-by: Corentin Labbe <clabbe@baylibre.com>
> >
> > How do I actually build this thing?
> >
> 
> Never mind.

Nope you are are right, this patchset clearly lack documentation.

I will send a subsequent patch with it.

Furthermore, since you seems to use cryptostat, what's your usage/opinion about it and what statistic it miss.

Regards
Ard Biesheuvel Oct. 1, 2018, 8:40 a.m. UTC | #4
On 1 October 2018 at 09:20, LABBE Corentin <clabbe@baylibre.com> wrote:
> On Fri, Sep 28, 2018 at 07:03:11PM +0200, Ard Biesheuvel wrote:
>> On 28 September 2018 at 15:13, Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:
>> > On 19 September 2018 at 12:10, Corentin Labbe <clabbe@baylibre.com> wrote:
>> >> This patch adds an userspace tool for displaying kernel crypto API
>> >> statistics.
>> >>
>> >> Signed-off-by: Corentin Labbe <clabbe@baylibre.com>
>> >
>> > How do I actually build this thing?
>> >
>>
>> Never mind.
>
> Nope you are are right, this patchset clearly lack documentation.
>
> I will send a subsequent patch with it.
>
> Furthermore, since you seems to use cryptostat, what's your usage/opinion about it and what statistic it miss.
>

To be honest, I mainly used it to check whether a certain cipher is
being used at all or not, i.e., to check whether the my mac80211
interface is really using the arc4 and ccmp ciphers it allocates, or
whether they are simply there as a fallback.
diff mbox series

Patch

diff --git a/tools/crypto/getstat.c b/tools/crypto/getstat.c
new file mode 100644
index 000000000000..24115173a483
--- /dev/null
+++ b/tools/crypto/getstat.c
@@ -0,0 +1,294 @@ 
+/* Heavily copied from libkcapi 2015 - 2017, Stephan Mueller <smueller@chronox.de> */
+#include <errno.h>
+#include <linux/cryptouser.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define CR_RTA(x)  ((struct rtattr *)(((char *)(x)) + NLMSG_ALIGN(sizeof(struct crypto_user_alg))))
+
+static int get_stat(const char *drivername)
+{
+	struct {
+		struct nlmsghdr n;
+		struct crypto_user_alg cru;
+	} req;
+	struct sockaddr_nl nl;
+	int sd = 0, ret;
+	socklen_t addr_len;
+	struct iovec iov;
+	struct msghdr msg;
+	char buf[4096];
+	struct nlmsghdr *res_n = (struct nlmsghdr *)buf;
+	struct crypto_user_alg *cru_res = NULL;
+	int res_len = 0;
+	struct rtattr *tb[CRYPTOCFGA_MAX + 1];
+	struct rtattr *rta;
+	struct nlmsgerr *errmsg;
+
+	memset(&req, 0, sizeof(req));
+	memset(&buf, 0, sizeof(buf));
+	memset(&msg, 0, sizeof(msg));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.cru));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = CRYPTO_MSG_GETSTAT;
+	req.n.nlmsg_seq = time(NULL);
+
+	strncpy(req.cru.cru_driver_name, drivername, strlen(drivername));
+
+	sd =  socket(AF_NETLINK, SOCK_RAW, NETLINK_CRYPTO);
+	if (sd < 0) {
+		fprintf(stderr, "Netlink error: cannot open netlink socket");
+		return -errno;
+	}
+	memset(&nl, 0, sizeof(nl));
+	nl.nl_family = AF_NETLINK;
+	if (bind(sd, (struct sockaddr *)&nl, sizeof(nl)) < 0) {
+		ret = -errno;
+		fprintf(stderr, "Netlink error: cannot bind netlink socket");
+		goto out;
+	}
+
+	/* sanity check that netlink socket was successfully opened */
+	addr_len = sizeof(nl);
+	if (getsockname(sd, (struct sockaddr *)&nl, &addr_len) < 0) {
+		ret = -errno;
+		printf("Netlink error: cannot getsockname");
+		goto out;
+	}
+	if (addr_len != sizeof(nl)) {
+		ret = -errno;
+		printf("Netlink error: wrong address length %d", addr_len);
+		goto out;
+	}
+	if (nl.nl_family != AF_NETLINK) {
+		ret = -errno;
+		printf("Netlink error: wrong address family %d",
+				nl.nl_family);
+		goto out;
+	}
+
+	memset(&nl, 0, sizeof(nl));
+	nl.nl_family = AF_NETLINK;
+	iov.iov_base = (void *)&req.n;
+	iov.iov_len = req.n.nlmsg_len;
+	msg.msg_name = &nl;
+	msg.msg_namelen = sizeof(nl);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	if (sendmsg(sd, &msg, 0) < 0) {
+		ret = -errno;
+		printf("Netlink error: sendmsg failed");
+		goto out;
+	}
+	memset(buf, 0, sizeof(buf));
+	iov.iov_base = buf;
+	while (1) {
+		iov.iov_len = sizeof(buf);
+		ret = recvmsg(sd, &msg, 0);
+		if (ret < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			ret = -errno;
+			printf("Netlink error: netlink receive error");
+			goto out;
+		}
+		if (ret == 0) {
+			ret = -errno;
+			printf("Netlink error: no data");
+			goto out;
+		}
+		if (ret > sizeof(buf)) {
+			ret = -errno;
+			printf("Netlink error: received too much data");
+			goto out;
+		}
+		break;
+	}
+
+	ret = -EFAULT;
+	res_len = res_n->nlmsg_len;
+	if (res_n->nlmsg_type == NLMSG_ERROR) {
+		errmsg = NLMSG_DATA(res_n);
+		fprintf(stderr, "Fail with %d\n", errmsg->error);
+		ret = errmsg->error;
+		goto out;
+	}
+
+	if (res_n->nlmsg_type == CRYPTO_MSG_GETSTAT) {
+		cru_res = NLMSG_DATA(res_n);
+		res_len -= NLMSG_SPACE(sizeof(*cru_res));
+	}
+	if (res_len < 0) {
+		printf("Netlink error: nlmsg len %d\n", res_len);
+		goto out;
+	}
+
+	if (!cru_res) {
+		ret = -EFAULT;
+		printf("Netlink error: no cru_res\n");
+		goto out;
+	}
+
+	rta = CR_RTA(cru_res);
+	memset(tb, 0, sizeof(struct rtattr *) * (CRYPTOCFGA_MAX + 1));
+	while (RTA_OK(rta, res_len)) {
+		if ((rta->rta_type <= CRYPTOCFGA_MAX) && (!tb[rta->rta_type]))
+			tb[rta->rta_type] = rta;
+		rta = RTA_NEXT(rta, res_len);
+	}
+	if (res_len) {
+		printf("Netlink error: unprocessed data %d",
+				res_len);
+		goto out;
+	}
+
+	if (tb[CRYPTOCFGA_STAT_HASH]) {
+		struct rtattr *rta = tb[CRYPTOCFGA_STAT_HASH];
+		struct crypto_stat *rhash =
+			(struct crypto_stat *)RTA_DATA(rta);
+		printf("%s\tHash\n\tHash: %u bytes: %llu\n\tErrors: %u\n",
+			drivername,
+			rhash->stat_hash_cnt, rhash->stat_hash_tlen,
+			rhash->stat_hash_err_cnt);
+	} else if (tb[CRYPTOCFGA_STAT_COMPRESS]) {
+		struct rtattr *rta = tb[CRYPTOCFGA_STAT_COMPRESS];
+		struct crypto_stat *rblk =
+			(struct crypto_stat *)RTA_DATA(rta);
+		printf("%s\tCompress\n\tCompress: %u bytes: %llu\n\tDecompress: %u bytes: %llu\n\tErrors: %u\n",
+			drivername,
+			rblk->stat_compress_cnt, rblk->stat_compress_tlen,
+			rblk->stat_decompress_cnt, rblk->stat_decompress_tlen,
+			rblk->stat_compress_err_cnt);
+	} else if (tb[CRYPTOCFGA_STAT_ACOMP]) {
+		struct rtattr *rta = tb[CRYPTOCFGA_STAT_ACOMP];
+		struct crypto_stat *rcomp =
+			(struct crypto_stat *)RTA_DATA(rta);
+		printf("%s\tACompress\n\tCompress: %u bytes: %llu\n\tDecompress: %u bytes: %llu\n\tErrors: %u\n",
+			drivername,
+			rcomp->stat_compress_cnt, rcomp->stat_compress_tlen,
+			rcomp->stat_decompress_cnt, rcomp->stat_decompress_tlen,
+			rcomp->stat_compress_err_cnt);
+	} else if (tb[CRYPTOCFGA_STAT_AEAD]) {
+		struct rtattr *rta = tb[CRYPTOCFGA_STAT_AEAD];
+		struct crypto_stat *raead =
+			(struct crypto_stat *)RTA_DATA(rta);
+		printf("%s\tAEAD\n\tEncrypt: %u bytes: %llu\n\tDecrypt: %u bytes: %llu\n\tErrors: %u\n",
+			drivername,
+			raead->stat_encrypt_cnt, raead->stat_encrypt_tlen,
+			raead->stat_decrypt_cnt, raead->stat_decrypt_tlen,
+			raead->stat_aead_err_cnt);
+	} else if (tb[CRYPTOCFGA_STAT_BLKCIPHER]) {
+		struct rtattr *rta = tb[CRYPTOCFGA_STAT_BLKCIPHER];
+		struct crypto_stat *rblk =
+			(struct crypto_stat *)RTA_DATA(rta);
+		printf("%s\tCipher\n\tEncrypt: %u bytes: %llu\n\tDecrypt: %u bytes: %llu\n\tErrors: %u\n",
+			drivername,
+			rblk->stat_encrypt_cnt, rblk->stat_encrypt_tlen,
+			rblk->stat_decrypt_cnt, rblk->stat_decrypt_tlen,
+			rblk->stat_cipher_err_cnt);
+	} else if (tb[CRYPTOCFGA_STAT_AKCIPHER]) {
+		struct rtattr *rta = tb[CRYPTOCFGA_STAT_AKCIPHER];
+		struct crypto_stat *rblk =
+			(struct crypto_stat *)RTA_DATA(rta);
+		printf("%s\tAkcipher\n\tEncrypt: %u bytes: %llu\n\tDecrypt: %u bytes: %llu\n\tSign: %u\n\tVerify: %u\n\tErrors: %u\n",
+			drivername,
+			rblk->stat_encrypt_cnt, rblk->stat_encrypt_tlen,
+			rblk->stat_decrypt_cnt, rblk->stat_decrypt_tlen,
+			rblk->stat_sign_cnt, rblk->stat_verify_cnt,
+			rblk->stat_akcipher_err_cnt);
+	} else if (tb[CRYPTOCFGA_STAT_CIPHER]) {
+		struct rtattr *rta = tb[CRYPTOCFGA_STAT_CIPHER];
+		struct crypto_stat *rblk =
+			(struct crypto_stat *)RTA_DATA(rta);
+		printf("%s\tcipher\n\tEncrypt: %u bytes: %llu\n\tDecrypt: %u bytes: %llu\n\tErrors: %u\n",
+			drivername,
+			rblk->stat_encrypt_cnt, rblk->stat_encrypt_tlen,
+			rblk->stat_decrypt_cnt, rblk->stat_decrypt_tlen,
+			rblk->stat_cipher_err_cnt);
+	} else if (tb[CRYPTOCFGA_STAT_RNG]) {
+		struct rtattr *rta = tb[CRYPTOCFGA_STAT_RNG];
+		struct crypto_stat *rrng =
+			(struct crypto_stat *)RTA_DATA(rta);
+		printf("%s\tRNG\n\tSeed: %u\n\tGenerate: %u bytes: %llu\n\tErrors: %u\n",
+			drivername,
+			rrng->stat_seed_cnt,
+			rrng->stat_generate_cnt, rrng->stat_generate_tlen,
+			rrng->stat_rng_err_cnt);
+	} else if (tb[CRYPTOCFGA_STAT_KPP]) {
+		struct rtattr *rta = tb[CRYPTOCFGA_STAT_KPP];
+		struct crypto_stat *rkpp =
+			(struct crypto_stat *)RTA_DATA(rta);
+		printf("%s\tKPP\n\tSetsecret: %u\n\tGenerate public key: %u\n\tCompute_shared_secret: %u\n\tErrors: %u\n",
+			drivername,
+			rkpp->stat_setsecret_cnt,
+			rkpp->stat_generate_public_key_cnt,
+			rkpp->stat_compute_shared_secret_cnt,
+			rkpp->stat_kpp_err_cnt);
+	} else {
+		fprintf(stderr, "%s is of an unknown algorithm\n", drivername);
+	}
+	ret = 0;
+out:
+	close(sd);
+	return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+	char buf[4096];
+	FILE *procfd;
+	int i, lastspace;
+	int ret;
+
+	procfd = fopen("/proc/crypto", "r");
+	if (!procfd) {
+		ret = errno;
+		fprintf(stderr, "Cannot open /proc/crypto %s\n", strerror(errno));
+		return ret;
+	}
+	if (argc > 1) {
+		if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
+			printf("Usage: %s [-h|--help] display this help\n", argv[0]);
+			printf("Usage: %s display all crypto statistics\n", argv[0]);
+			printf("Usage: %s drivername1 drivername2 ... = display crypto statistics about drivername1 ...\n", argv[0]);
+			return 0;
+		}
+		for (i = 1; i < argc; i++) {
+			ret = get_stat(argv[i]);
+			if (ret) {
+				fprintf(stderr, "Failed with %s\n", strerror(-ret));
+				return ret;
+			}
+		}
+		return 0;
+	}
+
+	while (fgets(buf, sizeof(buf), procfd)) {
+		if (!strncmp(buf, "driver", 6)) {
+			lastspace = 0;
+			i = 0;
+			while (i < strlen(buf)) {
+				i++;
+				if (buf[i] == ' ')
+					lastspace = i;
+			}
+			buf[strlen(buf) - 1] = '\0';
+			ret = get_stat(buf + lastspace + 1);
+			if (ret) {
+				fprintf(stderr, "Failed with %s\n", strerror(-ret));
+				goto out;
+			}
+		}
+	}
+out:
+	fclose(procfd);
+	return ret;
+}