[RFC PATCH v1 1/1] crypto: algif_compression - User-space interface for compression

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



crypto: algif_compression - User-space interface for compression

This patch adds af_alg plugin for compression algorithms of type scomp/acomp
registered to the kernel crypto layer.

The user needs to set operation (compression/decompression) as a control
message to sendmsg, identical to selecting the cipher operation type in case of
ciphers. Once a sendmsg call occurs, no further writes can be made to the
socket until all previous data has been processed and read. Therefore the
interface only supports one request at a time.

The interface is completely synchronous; all operations are carried out in
recvmsg and will complete prior to the system call returning.

The sendmsg and recvmsg interface supports directly reading/writing to 
user-space without additional copying, i.e., the kernel crypto interface will
receive the user-space address as its input/output SG list. The scomp interface
or crypto drivers may copy the data as required.

Signed-off-by: Abed Kamaluddin <akam...@xxxxxxxxxx>
Signed-off-by: Mahipal Challa <mahipal.cha...@xxxxxxxxxx>

---
 crypto/Kconfig              |  11 ++
 crypto/Makefile             |   1 +
 crypto/algif_compression.c  | 272 ++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/if_alg.h |   2 +
 4 files changed, 286 insertions(+)

diff --git a/crypto/Kconfig b/crypto/Kconfig
index f37e9cc..13b03ba 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -1741,6 +1741,17 @@ config CRYPTO_USER_API_AEAD
 	  This option enables the user-spaces interface for AEAD
 	  cipher algorithms.
 
+config CRYPTO_USER_API_COMPRESSION
+	tristate "User-space interface for compression algorithms"
+	depends on NET
+	select CRYPTO_ACOMP
+	select CRYPTO_USER_API
+	help
+	  This option enables the user-space interface for compression
+	  algorithms. Enable this option for access to compression algorithms
+	  of type scomp/acomp exported by the kernel crypto layer through
+	  AF_ALG interface.
+
 config CRYPTO_HASH_INFO
 	bool
 
diff --git a/crypto/Makefile b/crypto/Makefile
index 8a44057..1469e06 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -134,6 +134,7 @@ obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
 obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
 obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
 obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
+obj-$(CONFIG_CRYPTO_USER_API_COMPRESSION) += algif_compression.o
 obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
 obj-$(CONFIG_CRYPTO_USER_API_RNG) += algif_rng.o
 obj-$(CONFIG_CRYPTO_USER_API_AEAD) += algif_aead.o
diff --git a/crypto/algif_compression.c b/crypto/algif_compression.c
new file mode 100644
index 0000000..0ba6d1e
--- /dev/null
+++ b/crypto/algif_compression.c
@@ -0,0 +1,272 @@
+/*
+ * algif_compression: User-space interface for COMPRESSION algorithms
+ *
+ * This file provides user-space API support for compression algorithms
+ * registered through the kernel crypto layer.
+ *
+ * Copyright (C) 2017 Cavium, Inc.
+ *
+ * Original Authors: Abed Kamaluddin <akamaluddin@xxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <crypto/acompress.h>
+#include <crypto/if_alg.h>
+#include <linux/crypto.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/scatterlist.h>
+
+/* scomp scratch is currently 128KB */
+#define COMP_BUFFER_SIZE	65535
+
+struct comp_ctx {
+	struct af_alg_sgl tsgl;
+	struct af_alg_sgl rsgl;
+	struct af_alg_completion completion;
+	unsigned int clen;
+	unsigned int slen;
+	unsigned int dlen;
+	bool comp;
+	bool used;
+	struct acomp_req *acomp_req;
+};
+
+struct comp_tfm {
+	struct crypto_acomp *acomp;
+};
+
+static int comp_sendmsg(struct socket *sock, struct msghdr *msg,
+			size_t ignored)
+{
+	struct sock *sk = sock->sk;
+	struct alg_sock *ask = alg_sk(sk);
+	struct comp_ctx *ctx = ask->private;
+	struct af_alg_control con = {};
+	int limit = COMP_BUFFER_SIZE;
+	int len;
+	int err = -EINVAL;
+
+	if (msg->msg_controllen) {
+		err = af_alg_cmsg_send(msg, &con);
+		if (err)
+			return err;
+
+		switch (con.op) {
+		case ALG_OP_COMPRESS:
+			ctx->comp = 1;
+			break;
+
+		case ALG_OP_DECOMPRESS:
+			ctx->comp = 0;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+	}
+
+	lock_sock(sk);
+
+	/* One request at a time supported, data submitted for comp/decomp will
+	 * be processed at subsequent recvmsg
+	 */
+	if (ctx->used) {
+		err = -EAGAIN;
+		goto unlock;
+	}
+
+	len = msg_data_left(msg);
+
+	if (len > limit)
+		len = limit;
+
+	len = af_alg_make_sg(&ctx->tsgl, &msg->msg_iter, len);
+
+	if (len < 0) {
+		err = len;
+		goto unlock;
+	}
+
+	ctx->slen = len;
+	ctx->used = 1;
+
+unlock:
+	release_sock(sk);
+
+	return err ?: len;
+}
+
+static int comp_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+			int flags)
+{
+	struct sock *sk = sock->sk;
+	struct alg_sock *ask = alg_sk(sk);
+	struct comp_ctx *ctx = ask->private;
+	int rlen = ctx->dlen;
+	int err;
+
+	if (len > rlen)
+		len = rlen;
+
+	lock_sock(sk);
+
+	if (!ctx->used) {
+		err = -EAGAIN;
+		goto unlock;
+	}
+
+	len = af_alg_make_sg(&ctx->rsgl, &msg->msg_iter, len);
+	if (len < 0) {
+		err = len;
+		goto unlock;
+	}
+
+	acomp_request_set_params(ctx->acomp_req, ctx->tsgl.sg, ctx->rsgl.sg,
+				 ctx->slen, len);
+
+	/* Synchronous completion of comp/decomp requests */
+	err = af_alg_wait_for_completion(
+			ctx->comp ?
+				crypto_acomp_compress(ctx->acomp_req) :
+				crypto_acomp_decompress(ctx->acomp_req),
+				&ctx->completion);
+
+	/* Add acomp req wrapper for dlen */
+	len = (ctx->acomp_req)->dlen;
+
+	af_alg_free_sg(&ctx->tsgl);
+	af_alg_free_sg(&ctx->rsgl);
+
+unlock:
+	ctx->used = 0;
+	release_sock(sk);
+
+	return err ?: len;
+}
+
+static struct proto_ops algif_comp_ops = {
+	.family		=	PF_ALG,
+
+	.connect	=	sock_no_connect,
+	.socketpair	=	sock_no_socketpair,
+	.getname	=	sock_no_getname,
+	.ioctl		=	sock_no_ioctl,
+	.listen		=	sock_no_listen,
+	.shutdown	=	sock_no_shutdown,
+	.getsockopt	=	sock_no_getsockopt,
+	.mmap		=	sock_no_mmap,
+	.bind		=	sock_no_bind,
+	.setsockopt	=	sock_no_setsockopt,
+	.poll		=	sock_no_poll,
+
+	.release	=	af_alg_release,
+	.sendmsg	=	comp_sendmsg,
+	.recvmsg	=	comp_recvmsg,
+	.sendpage	=	sock_no_sendpage,
+	.accept		=	sock_no_accept,
+};
+
+static void *comp_bind(const char *name, u32 type, u32 mask)
+{
+	struct comp_tfm *tfm;
+	struct crypto_acomp *acomp;
+
+	tfm = kzalloc(sizeof(*tfm), GFP_KERNEL);
+	if (!tfm)
+		return ERR_PTR(-ENOMEM);
+
+	acomp = crypto_alloc_acomp(name, type, mask);
+	if (IS_ERR_OR_NULL(acomp)) {
+		kfree(tfm);
+		return ERR_PTR(-ENOMEM);
+	}
+	tfm->acomp = acomp;
+
+	return tfm;
+}
+
+static void comp_release(void *private)
+{
+	struct comp_tfm *tfm = private;
+
+	crypto_free_acomp(tfm->acomp);
+	kfree(tfm);
+}
+
+static void comp_sock_destruct(struct sock *sk)
+{
+	struct alg_sock *ask = alg_sk(sk);
+	struct comp_ctx *ctx = ask->private;
+
+	acomp_request_free(ctx->acomp_req);
+	sock_kfree_s(sk, ctx, ctx->clen);
+	af_alg_release_parent(sk);
+}
+
+static int comp_accept_parent(void *private, struct sock *sk)
+{
+	struct comp_ctx *ctx;
+	struct alg_sock *ask = alg_sk(sk);
+	struct comp_tfm *tfm = private;
+	struct crypto_acomp *acomp = tfm->acomp;
+	unsigned int len = sizeof(*ctx);
+
+	ctx = sock_kmalloc(sk, len, GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->used = 0;
+	ctx->clen = len;
+	ctx->dlen = COMP_BUFFER_SIZE;
+	ctx->slen = COMP_BUFFER_SIZE;
+
+	af_alg_init_completion(&ctx->completion);
+
+	ctx->acomp_req = acomp_request_alloc(acomp);
+	if (!ctx->acomp_req) {
+		sock_kfree_s(sk, ctx, ctx->clen);
+		return -ENOMEM;
+	}
+
+	acomp_request_set_callback(ctx->acomp_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+				   af_alg_complete, &ctx->completion);
+
+	ask->private = ctx;
+	sk->sk_destruct = comp_sock_destruct;
+
+	return 0;
+}
+
+static const struct af_alg_type algif_type_comp = {
+	.bind		=	comp_bind,
+	.release	=	comp_release,
+	.accept		=	comp_accept_parent,
+	.ops		=	&algif_comp_ops,
+	.name		=	"compression",
+	.owner		=	THIS_MODULE
+};
+
+static int __init algif_comp_init(void)
+{
+	return af_alg_register_type(&algif_type_comp);
+}
+
+static void __exit algif_comp_exit(void)
+{
+	int err = af_alg_unregister_type(&algif_type_comp);
+
+	BUG_ON(err);
+}
+
+module_init(algif_comp_init);
+module_exit(algif_comp_exit);
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/if_alg.h b/include/uapi/linux/if_alg.h
index f2acd2f..5cca9eb 100644
--- a/include/uapi/linux/if_alg.h
+++ b/include/uapi/linux/if_alg.h
@@ -38,5 +38,7 @@ struct af_alg_iv {
 /* Operations */
 #define ALG_OP_DECRYPT			0
 #define ALG_OP_ENCRYPT			1
+#define ALG_OP_DECOMPRESS		0
+#define ALG_OP_COMPRESS		1
 
 #endif	/* _LINUX_IF_ALG_H */
-- 
2.7.4




[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]

  Powered by Linux