All of lore.kernel.org
 help / color / mirror / Atom feed
From: Hemant Agrawal <hemant.agrawal@nxp.com>
To: dev@dpdk.org, akhil.goyal@nxp.com
Cc: Hemant Agrawal <hemant.agrawal@nxp.com>
Subject: [dpdk-dev] [PATCH 7/8] crypto/dpaa_sec: add AES-GCM support for lookaside case
Date: Fri, 25 Oct 2019 14:03:35 +0530	[thread overview]
Message-ID: <20191025083336.24212-7-hemant.agrawal@nxp.com> (raw)
In-Reply-To: <20191025083336.24212-1-hemant.agrawal@nxp.com>

This patch add support for AES-128-GCM, when used in
proto lookaside mode.

Signed-off-by: Hemant Agrawal <hemant.agrawal@nxp.com>
---
 drivers/crypto/dpaa_sec/dpaa_sec.c | 296 ++++++++++++++++++++---------
 1 file changed, 207 insertions(+), 89 deletions(-)

diff --git a/drivers/crypto/dpaa_sec/dpaa_sec.c b/drivers/crypto/dpaa_sec/dpaa_sec.c
index 20b6e355a..e86a1e3b7 100644
--- a/drivers/crypto/dpaa_sec/dpaa_sec.c
+++ b/drivers/crypto/dpaa_sec/dpaa_sec.c
@@ -382,12 +382,14 @@ dpaa_sec_prep_ipsec_cdb(dpaa_sec_session *ses)
 	cipherdata.algtype = ses->cipher_key.alg;
 	cipherdata.algmode = ses->cipher_key.algmode;
 
-	authdata.key = (size_t)ses->auth_key.data;
-	authdata.keylen = ses->auth_key.length;
-	authdata.key_enc_flags = 0;
-	authdata.key_type = RTA_DATA_IMM;
-	authdata.algtype = ses->auth_key.alg;
-	authdata.algmode = ses->auth_key.algmode;
+	if (ses->auth_key.length) {
+		authdata.key = (size_t)ses->auth_key.data;
+		authdata.keylen = ses->auth_key.length;
+		authdata.key_enc_flags = 0;
+		authdata.key_type = RTA_DATA_IMM;
+		authdata.algtype = ses->auth_key.alg;
+		authdata.algmode = ses->auth_key.algmode;
+	}
 
 	cdb->sh_desc[0] = cipherdata.keylen;
 	cdb->sh_desc[1] = authdata.keylen;
@@ -2523,33 +2525,79 @@ dpaa_sec_sym_session_clear(struct rte_cryptodev *dev,
 
 #ifdef RTE_LIBRTE_SECURITY
 static int
-dpaa_sec_set_ipsec_session(__rte_unused struct rte_cryptodev *dev,
-			   struct rte_security_session_conf *conf,
-			   void *sess)
+dpaa_sec_ipsec_aead_init(struct rte_crypto_aead_xform *aead_xform,
+			dpaa_sec_session *session)
 {
-	struct dpaa_sec_dev_private *internals = dev->data->dev_private;
-	struct rte_security_ipsec_xform *ipsec_xform = &conf->ipsec;
-	struct rte_crypto_auth_xform *auth_xform = NULL;
-	struct rte_crypto_cipher_xform *cipher_xform = NULL;
-	dpaa_sec_session *session = (dpaa_sec_session *)sess;
-	uint32_t i;
-
 	PMD_INIT_FUNC_TRACE();
 
-	memset(session, 0, sizeof(dpaa_sec_session));
-	if (ipsec_xform->direction == RTE_SECURITY_IPSEC_SA_DIR_EGRESS) {
-		cipher_xform = &conf->crypto_xform->cipher;
-		if (conf->crypto_xform->next)
-			auth_xform = &conf->crypto_xform->next->auth;
-	} else {
-		auth_xform = &conf->crypto_xform->auth;
-		if (conf->crypto_xform->next)
-			cipher_xform = &conf->crypto_xform->next->cipher;
+	session->aead_key.data = rte_zmalloc(NULL, aead_xform->key.length,
+					       RTE_CACHE_LINE_SIZE);
+	if (session->aead_key.data == NULL && aead_xform->key.length > 0) {
+		DPAA_SEC_ERR("No Memory for aead key");
+		return -1;
 	}
-	session->proto_alg = conf->protocol;
-	session->ctxt = DPAA_SEC_IPSEC;
+	memcpy(session->aead_key.data, aead_xform->key.data,
+	       aead_xform->key.length);
+
+	session->digest_length = aead_xform->digest_length;
+	session->aead_key.length = aead_xform->key.length;
+
+	switch (aead_xform->algo) {
+	case RTE_CRYPTO_AEAD_AES_GCM:
+		switch (session->digest_length) {
+		case 8:
+			session->aead_key.alg = OP_PCL_IPSEC_AES_GCM8;
+			break;
+		case 12:
+			session->aead_key.alg = OP_PCL_IPSEC_AES_GCM12;
+			break;
+		case 16:
+			session->aead_key.alg = OP_PCL_IPSEC_AES_GCM16;
+			break;
+		default:
+			DPAA_SEC_ERR("Crypto: Undefined GCM digest %d",
+				     session->digest_length);
+			return -1;
+		}
+		session->aead_key.algmode = OP_ALG_AAI_GCM;
+		session->aead_alg = RTE_CRYPTO_AEAD_AES_GCM;
+		break;
+	case RTE_CRYPTO_AEAD_AES_CCM:
+		switch (session->digest_length) {
+		case 8:
+			session->aead_key.alg = OP_PCL_IPSEC_AES_CCM8;
+			break;
+		case 12:
+			session->aead_key.alg = OP_PCL_IPSEC_AES_CCM12;
+			break;
+		case 16:
+			session->aead_key.alg = OP_PCL_IPSEC_AES_CCM16;
+			break;
+		default:
+			DPAA_SEC_ERR("Crypto: Undefined CCM digest %d",
+				     session->digest_length);
+			return -1;
+		}
+		session->aead_key.algmode = OP_ALG_AAI_CCM;
+		session->aead_alg = RTE_CRYPTO_AEAD_AES_CCM;
+		break;
+	default:
+		DPAA_SEC_ERR("Crypto: Undefined AEAD specified %u",
+			      aead_xform->algo);
+		return -1;
+	}
+	session->dir = (aead_xform->op == RTE_CRYPTO_AEAD_OP_ENCRYPT) ?
+				DIR_ENC : DIR_DEC;
+
+	return 0;
+}
 
-	if (cipher_xform && cipher_xform->algo != RTE_CRYPTO_CIPHER_NULL) {
+static int
+dpaa_sec_ipsec_proto_init(struct rte_crypto_cipher_xform *cipher_xform,
+	struct rte_crypto_auth_xform *auth_xform,
+	dpaa_sec_session *session)
+{
+	if (cipher_xform) {
 		session->cipher_key.data = rte_zmalloc(NULL,
 						       cipher_xform->key.length,
 						       RTE_CACHE_LINE_SIZE);
@@ -2558,31 +2606,10 @@ dpaa_sec_set_ipsec_session(__rte_unused struct rte_cryptodev *dev,
 			DPAA_SEC_ERR("No Memory for cipher key");
 			return -ENOMEM;
 		}
+
+		session->cipher_key.length = cipher_xform->key.length;
 		memcpy(session->cipher_key.data, cipher_xform->key.data,
 				cipher_xform->key.length);
-		session->cipher_key.length = cipher_xform->key.length;
-
-		switch (cipher_xform->algo) {
-		case RTE_CRYPTO_CIPHER_NULL:
-			session->cipher_key.alg = OP_PCL_IPSEC_NULL;
-			break;
-		case RTE_CRYPTO_CIPHER_AES_CBC:
-			session->cipher_key.alg = OP_PCL_IPSEC_AES_CBC;
-			session->cipher_key.algmode = OP_ALG_AAI_CBC;
-			break;
-		case RTE_CRYPTO_CIPHER_3DES_CBC:
-			session->cipher_key.alg = OP_PCL_IPSEC_3DES;
-			session->cipher_key.algmode = OP_ALG_AAI_CBC;
-			break;
-		case RTE_CRYPTO_CIPHER_AES_CTR:
-			session->cipher_key.alg = OP_PCL_IPSEC_AES_CTR;
-			session->cipher_key.algmode = OP_ALG_AAI_CTR;
-			break;
-		default:
-			DPAA_SEC_ERR("Crypto: Unsupported Cipher alg %u",
-				cipher_xform->algo);
-			goto out;
-		}
 		session->cipher_alg = cipher_xform->algo;
 	} else {
 		session->cipher_key.data = NULL;
@@ -2590,54 +2617,18 @@ dpaa_sec_set_ipsec_session(__rte_unused struct rte_cryptodev *dev,
 		session->cipher_alg = RTE_CRYPTO_CIPHER_NULL;
 	}
 
-	if (auth_xform && auth_xform->algo != RTE_CRYPTO_AUTH_NULL) {
+	if (auth_xform) {
 		session->auth_key.data = rte_zmalloc(NULL,
 						auth_xform->key.length,
 						RTE_CACHE_LINE_SIZE);
 		if (session->auth_key.data == NULL &&
 				auth_xform->key.length > 0) {
 			DPAA_SEC_ERR("No Memory for auth key");
-			rte_free(session->cipher_key.data);
 			return -ENOMEM;
 		}
+		session->auth_key.length = auth_xform->key.length;
 		memcpy(session->auth_key.data, auth_xform->key.data,
 				auth_xform->key.length);
-		session->auth_key.length = auth_xform->key.length;
-
-		switch (auth_xform->algo) {
-		case RTE_CRYPTO_AUTH_NULL:
-			session->auth_key.alg = OP_PCL_IPSEC_HMAC_NULL;
-			session->digest_length = 0;
-			break;
-		case RTE_CRYPTO_AUTH_MD5_HMAC:
-			session->auth_key.alg = OP_PCL_IPSEC_HMAC_MD5_96;
-			session->auth_key.algmode = OP_ALG_AAI_HMAC;
-			break;
-		case RTE_CRYPTO_AUTH_SHA1_HMAC:
-			session->auth_key.alg = OP_PCL_IPSEC_HMAC_SHA1_96;
-			session->auth_key.algmode = OP_ALG_AAI_HMAC;
-			break;
-		case RTE_CRYPTO_AUTH_SHA224_HMAC:
-			session->auth_key.alg = OP_PCL_IPSEC_HMAC_SHA1_160;
-			session->auth_key.algmode = OP_ALG_AAI_HMAC;
-			break;
-		case RTE_CRYPTO_AUTH_SHA256_HMAC:
-			session->auth_key.alg = OP_PCL_IPSEC_HMAC_SHA2_256_128;
-			session->auth_key.algmode = OP_ALG_AAI_HMAC;
-			break;
-		case RTE_CRYPTO_AUTH_SHA384_HMAC:
-			session->auth_key.alg = OP_PCL_IPSEC_HMAC_SHA2_384_192;
-			session->auth_key.algmode = OP_ALG_AAI_HMAC;
-			break;
-		case RTE_CRYPTO_AUTH_SHA512_HMAC:
-			session->auth_key.alg = OP_PCL_IPSEC_HMAC_SHA2_512_256;
-			session->auth_key.algmode = OP_ALG_AAI_HMAC;
-			break;
-		default:
-			DPAA_SEC_ERR("Crypto: Unsupported auth alg %u",
-				auth_xform->algo);
-			goto out;
-		}
 		session->auth_alg = auth_xform->algo;
 	} else {
 		session->auth_key.data = NULL;
@@ -2645,6 +2636,133 @@ dpaa_sec_set_ipsec_session(__rte_unused struct rte_cryptodev *dev,
 		session->auth_alg = RTE_CRYPTO_AUTH_NULL;
 	}
 
+	switch (session->auth_alg) {
+	case RTE_CRYPTO_AUTH_SHA1_HMAC:
+		session->auth_key.alg = OP_PCL_IPSEC_HMAC_SHA1_96;
+		session->auth_key.algmode = OP_ALG_AAI_HMAC;
+		break;
+	case RTE_CRYPTO_AUTH_MD5_HMAC:
+		session->auth_key.alg = OP_PCL_IPSEC_HMAC_MD5_96;
+		session->auth_key.algmode = OP_ALG_AAI_HMAC;
+		break;
+	case RTE_CRYPTO_AUTH_SHA256_HMAC:
+		session->auth_key.alg = OP_PCL_IPSEC_HMAC_SHA2_256_128;
+		session->auth_key.algmode = OP_ALG_AAI_HMAC;
+		break;
+	case RTE_CRYPTO_AUTH_SHA384_HMAC:
+		session->auth_key.alg = OP_PCL_IPSEC_HMAC_SHA2_384_192;
+		session->auth_key.algmode = OP_ALG_AAI_HMAC;
+		break;
+	case RTE_CRYPTO_AUTH_SHA512_HMAC:
+		session->auth_key.alg = OP_PCL_IPSEC_HMAC_SHA2_512_256;
+		session->auth_key.algmode = OP_ALG_AAI_HMAC;
+		break;
+	case RTE_CRYPTO_AUTH_AES_CMAC:
+		session->auth_key.alg = OP_PCL_IPSEC_AES_CMAC_96;
+		break;
+	case RTE_CRYPTO_AUTH_NULL:
+		session->auth_key.alg = OP_PCL_IPSEC_HMAC_NULL;
+		break;
+	case RTE_CRYPTO_AUTH_SHA224_HMAC:
+	case RTE_CRYPTO_AUTH_AES_XCBC_MAC:
+	case RTE_CRYPTO_AUTH_SNOW3G_UIA2:
+	case RTE_CRYPTO_AUTH_SHA1:
+	case RTE_CRYPTO_AUTH_SHA256:
+	case RTE_CRYPTO_AUTH_SHA512:
+	case RTE_CRYPTO_AUTH_SHA224:
+	case RTE_CRYPTO_AUTH_SHA384:
+	case RTE_CRYPTO_AUTH_MD5:
+	case RTE_CRYPTO_AUTH_AES_GMAC:
+	case RTE_CRYPTO_AUTH_KASUMI_F9:
+	case RTE_CRYPTO_AUTH_AES_CBC_MAC:
+	case RTE_CRYPTO_AUTH_ZUC_EIA3:
+		DPAA_SEC_ERR("Crypto: Unsupported auth alg %u",
+			      session->auth_alg);
+		return -1;
+	default:
+		DPAA_SEC_ERR("Crypto: Undefined Auth specified %u",
+			      session->auth_alg);
+		return -1;
+	}
+
+	switch (session->cipher_alg) {
+	case RTE_CRYPTO_CIPHER_AES_CBC:
+		session->cipher_key.alg = OP_PCL_IPSEC_AES_CBC;
+		session->cipher_key.algmode = OP_ALG_AAI_CBC;
+		break;
+	case RTE_CRYPTO_CIPHER_3DES_CBC:
+		session->cipher_key.alg = OP_PCL_IPSEC_3DES;
+		session->cipher_key.algmode = OP_ALG_AAI_CBC;
+		break;
+	case RTE_CRYPTO_CIPHER_AES_CTR:
+		session->cipher_key.alg = OP_PCL_IPSEC_AES_CTR;
+		session->cipher_key.algmode = OP_ALG_AAI_CTR;
+		break;
+	case RTE_CRYPTO_CIPHER_NULL:
+		session->cipher_key.alg = OP_PCL_IPSEC_NULL;
+		break;
+	case RTE_CRYPTO_CIPHER_SNOW3G_UEA2:
+	case RTE_CRYPTO_CIPHER_ZUC_EEA3:
+	case RTE_CRYPTO_CIPHER_3DES_ECB:
+	case RTE_CRYPTO_CIPHER_AES_ECB:
+	case RTE_CRYPTO_CIPHER_KASUMI_F8:
+		DPAA_SEC_ERR("Crypto: Unsupported Cipher alg %u",
+			      session->cipher_alg);
+		return -1;
+	default:
+		DPAA_SEC_ERR("Crypto: Undefined Cipher specified %u",
+			      session->cipher_alg);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+dpaa_sec_set_ipsec_session(__rte_unused struct rte_cryptodev *dev,
+			   struct rte_security_session_conf *conf,
+			   void *sess)
+{
+	struct dpaa_sec_dev_private *internals = dev->data->dev_private;
+	struct rte_security_ipsec_xform *ipsec_xform = &conf->ipsec;
+	struct rte_crypto_auth_xform *auth_xform = NULL;
+	struct rte_crypto_cipher_xform *cipher_xform = NULL;
+	struct rte_crypto_aead_xform *aead_xform = NULL;
+	dpaa_sec_session *session = (dpaa_sec_session *)sess;
+	uint32_t i;
+	int ret;
+
+	PMD_INIT_FUNC_TRACE();
+
+	memset(session, 0, sizeof(dpaa_sec_session));
+	session->proto_alg = conf->protocol;
+	session->ctxt = DPAA_SEC_IPSEC;
+
+	if (conf->crypto_xform->type == RTE_CRYPTO_SYM_XFORM_CIPHER) {
+		cipher_xform = &conf->crypto_xform->cipher;
+		if (conf->crypto_xform->next)
+			auth_xform = &conf->crypto_xform->next->auth;
+		ret = dpaa_sec_ipsec_proto_init(cipher_xform, auth_xform,
+					session);
+	} else if (conf->crypto_xform->type == RTE_CRYPTO_SYM_XFORM_AUTH) {
+		auth_xform = &conf->crypto_xform->auth;
+		if (conf->crypto_xform->next)
+			cipher_xform = &conf->crypto_xform->next->cipher;
+		ret = dpaa_sec_ipsec_proto_init(cipher_xform, auth_xform,
+					session);
+	} else if (conf->crypto_xform->type == RTE_CRYPTO_SYM_XFORM_AEAD) {
+		aead_xform = &conf->crypto_xform->aead;
+		ret = dpaa_sec_ipsec_aead_init(aead_xform, session);
+	} else {
+		DPAA_SEC_ERR("XFORM not specified");
+		ret = -EINVAL;
+		goto out;
+	}
+	if (ret) {
+		DPAA_SEC_ERR("Failed to process xform");
+		goto out;
+	}
+
 	if (ipsec_xform->direction == RTE_SECURITY_IPSEC_SA_DIR_EGRESS) {
 		if (ipsec_xform->tunnel.type ==
 				RTE_SECURITY_IPSEC_TUNNEL_IPV4) {
-- 
2.17.1


  parent reply	other threads:[~2019-10-25  8:37 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-10-25  8:33 [dpdk-dev] [PATCH 1/8] crypto/dpaa_sec: fix to set PDCP capability flags Hemant Agrawal
2019-10-25  8:33 ` [dpdk-dev] [PATCH 2/8] crypto/dpaa2_sec: add check for the session validity Hemant Agrawal
2019-10-25  8:33 ` [dpdk-dev] [PATCH 3/8] crypto/dpaa_sec: " Hemant Agrawal
2019-10-25  8:33 ` [dpdk-dev] [PATCH 4/8] crypto/dpaa2_sec: adding NULL cipher or NULL auth Hemant Agrawal
2019-10-25  8:33 ` [dpdk-dev] [PATCH 5/8] crypto/dpaa_sec: " Hemant Agrawal
2019-10-25  8:33 ` [dpdk-dev] [PATCH 6/8] crypto/dpaa2_sec: add AES-GCM support for lookaside case Hemant Agrawal
2019-10-25  8:33 ` Hemant Agrawal [this message]
2019-10-25  8:33 ` [dpdk-dev] [PATCH 8/8] test/cryptodev: enable additional cases for dpaax Hemant Agrawal
2019-11-01 17:51 ` [dpdk-dev] [PATCH v2 01/13] crypto/dpaa_sec: fix to set PDCP capability flags Hemant Agrawal
2019-11-01 17:51   ` [dpdk-dev] [PATCH v2 02/13] crypto/dpaa2_sec: add check for the session validity Hemant Agrawal
2019-11-01 17:51   ` [dpdk-dev] [PATCH v2 03/13] crypto/dpaa_sec: " Hemant Agrawal
2019-11-01 17:51   ` [dpdk-dev] [PATCH v2 04/13] crypto/dpaa2_sec: adding NULL cipher or NULL auth Hemant Agrawal
2019-11-01 17:51   ` [dpdk-dev] [PATCH v2 05/13] crypto/dpaa_sec: " Hemant Agrawal
2019-11-01 17:51   ` [dpdk-dev] [PATCH v2 06/13] crypto/dpaa2_sec: add AES-GCM support for lookaside case Hemant Agrawal
2019-11-01 17:51   ` [dpdk-dev] [PATCH v2 07/13] crypto/dpaa_sec: " Hemant Agrawal
2019-11-01 17:51   ` [dpdk-dev] [PATCH v2 08/13] test/cryptodev: enable additional cases for dpaax Hemant Agrawal
2019-11-01 17:51   ` [dpdk-dev] [PATCH v2 09/13] crypto/dpaa2_sec: enable warning with truncated sha256 Hemant Agrawal
2019-11-01 17:51   ` [dpdk-dev] [PATCH v2 10/13] crypto/dpaa2_sec: remove unwanted context type check Hemant Agrawal
2019-11-01 17:51   ` [dpdk-dev] [PATCH v2 11/13] crypto/dpaa_sec: use macros in queue attach and detach Hemant Agrawal
2019-11-01 17:51   ` [dpdk-dev] [PATCH v2 12/13] crypto/dpaa2_sec: use aes-ctr initial counter as 1 Hemant Agrawal
2019-11-01 17:51   ` [dpdk-dev] [PATCH v2 13/13] crypto/dpaa_sec: enable ipsec aes-ctr to use nonce Hemant Agrawal
2019-11-06  5:17   ` [dpdk-dev] [PATCH v3 01/13] crypto/dpaa_sec: fix to set PDCP capability flags Hemant Agrawal
2019-11-06  5:17     ` [dpdk-dev] [PATCH v3 02/13] crypto/dpaa2_sec: add check for the session validity Hemant Agrawal
2019-11-06  5:17     ` [dpdk-dev] [PATCH v3 03/13] crypto/dpaa_sec: " Hemant Agrawal
2019-11-06  5:17     ` [dpdk-dev] [PATCH v3 04/13] crypto/dpaa2_sec: adding NULL cipher or NULL auth Hemant Agrawal
2019-11-06  5:17     ` [dpdk-dev] [PATCH v3 05/13] crypto/dpaa_sec: " Hemant Agrawal
2019-11-06  5:17     ` [dpdk-dev] [PATCH v3 06/13] crypto/dpaa2_sec: add AES-GCM support for lookaside case Hemant Agrawal
2019-11-06  5:17     ` [dpdk-dev] [PATCH v3 07/13] crypto/dpaa_sec: " Hemant Agrawal
2019-11-07  8:48       ` Hemant Agrawal
2019-11-07 10:07         ` Akhil Goyal
2019-11-06  5:17     ` [dpdk-dev] [PATCH v3 08/13] test/cryptodev: enable additional cases for dpaax Hemant Agrawal
2019-11-06  5:17     ` [dpdk-dev] [PATCH v3 09/13] crypto/dpaa2_sec: enable warning with truncated sha256 Hemant Agrawal
2019-11-06  5:17     ` [dpdk-dev] [PATCH v3 10/13] crypto/dpaa2_sec: remove unwanted context type check Hemant Agrawal
2019-11-06  5:17     ` [dpdk-dev] [PATCH v3 11/13] crypto/dpaa_sec: use macros in queue attach and detach Hemant Agrawal
2019-11-06  5:17     ` [dpdk-dev] [PATCH v3 12/13] crypto/dpaa2_sec: use aes-ctr initial counter as 1 Hemant Agrawal
2019-11-06  5:17     ` [dpdk-dev] [PATCH v3 13/13] crypto/dpaa_sec: enable ipsec aes-ctr to use nonce Hemant Agrawal
2019-11-06 13:09     ` [dpdk-dev] [PATCH v3 01/13] crypto/dpaa_sec: fix to set PDCP capability flags Akhil Goyal

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191025083336.24212-7-hemant.agrawal@nxp.com \
    --to=hemant.agrawal@nxp.com \
    --cc=akhil.goyal@nxp.com \
    --cc=dev@dpdk.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.