@@ -133,4 +133,36 @@ int tst_alg_setup(const char *algtype, const char *algname,
int tst_alg_setup_reqfd(const char *algtype, const char *algname,
const uint8_t *key, unsigned int keylen);
+/** Specification of control data to send to an AF_ALG request socket */
+struct tst_alg_sendmsg_params {
+
+ /** If true, send ALG_SET_OP with ALG_OP_ENCRYPT */
+ bool encrypt;
+
+ /** If true, send ALG_SET_OP with ALG_OP_DECRYPT */
+ bool decrypt;
+
+ /** If ivlen != 0, send ALG_SET_IV */
+ const uint8_t *iv;
+ unsigned int ivlen;
+
+ /** If assoclen != 0, send ALG_SET_AEAD_ASSOCLEN */
+ unsigned int assoclen;
+
+ /* Value to use as msghdr::msg_flags */
+ uint32_t msg_flags;
+};
+
+/**
+ * Send some data to an AF_ALG request socket, including control data.
+ * @param reqfd An AF_ALG request socket
+ * @param data The data to send
+ * @param datalen The length of data in bytes
+ * @param params Specification of the control data to send
+ *
+ * On failure, tst_brk() is called with TBROK.
+ */
+void tst_alg_sendmsg(int reqfd, const void *data, size_t datalen,
+ const struct tst_alg_sendmsg_params *params);
+
#endif /* TST_AF_ALG_H */
@@ -146,3 +146,67 @@ int tst_alg_setup_reqfd(const char *algtype, const char *algname,
close(algfd);
return reqfd;
}
+
+void tst_alg_sendmsg(int reqfd, const void *data, size_t datalen,
+ const struct tst_alg_sendmsg_params *params)
+{
+ struct iovec iov = {
+ .iov_base = (void *)data,
+ .iov_len = datalen,
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_flags = params->msg_flags,
+ };
+ size_t controllen;
+ uint8_t *control;
+ struct cmsghdr *cmsg;
+ struct af_alg_iv *alg_iv;
+
+ if (params->encrypt && params->decrypt)
+ tst_brk(TBROK, "Both encrypt and decrypt are specified");
+
+ controllen = 0;
+ if (params->encrypt || params->decrypt)
+ controllen += CMSG_SPACE(sizeof(uint32_t));
+ if (params->ivlen)
+ controllen += CMSG_SPACE(sizeof(struct af_alg_iv) +
+ params->ivlen);
+ if (params->assoclen)
+ controllen += CMSG_SPACE(sizeof(uint32_t));
+
+ control = SAFE_MALLOC(controllen);
+ memset(control, 0, controllen);
+ msg.msg_control = control;
+ msg.msg_controllen = controllen;
+ cmsg = CMSG_FIRSTHDR(&msg);
+
+ if (params->encrypt || params->decrypt) {
+ cmsg->cmsg_level = SOL_ALG;
+ cmsg->cmsg_type = ALG_SET_OP;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
+ *(uint32_t *)CMSG_DATA(cmsg) =
+ params->encrypt ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT;
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ }
+ if (params->ivlen) {
+ cmsg->cmsg_level = SOL_ALG;
+ cmsg->cmsg_type = ALG_SET_IV;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct af_alg_iv) +
+ params->ivlen);
+ alg_iv = (struct af_alg_iv *)CMSG_DATA(cmsg);
+ alg_iv->ivlen = params->ivlen;
+ memcpy(alg_iv->iv, params->iv, params->ivlen);
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ }
+ if (params->assoclen) {
+ cmsg->cmsg_level = SOL_ALG;
+ cmsg->cmsg_type = ALG_SET_AEAD_ASSOCLEN;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
+ *(uint32_t *)CMSG_DATA(cmsg) = params->assoclen;
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ }
+
+ SAFE_SENDMSG(datalen, reqfd, &msg, 0);
+}