HELP: IPsec reordering issue
Subsystems (4)
Hi, all:
I encountered a IPsec packets reordering issue, following is the setup and scenario
There is a IPSec IKEv1 tunnel between B & C
The traffic is UDP from C to A @ 40 mbps
Packets are coming in order at B but leaving out of order towards A If IPSec is disabled between B & C, there is no packet reordering.
The input and output of B is same physical interface but separated by two VLANs, and we have directed all our network interrupts to one core.
As per our analysis we are suspecting below is the root cause of the problem. All the packets which are out of order have got -EINPROGRESS error in below part of the code.
File: net/ipv4/esp4.c: function esp_input
.....
aead_request_set_callback(req, 0, esp_input_done, skb); aead_request_set_crypt(req, sg, sg, elen, iv); aead_request_set_assoc(req, asg, sizeof(*esph));
err = crypto_aead_decrypt(req);
if (err == -EINPROGRESS)
goto out; err = esp_input_done2(skb, err);
.....
Below is the place where the packets are either decrypted immediately or queue for later decryption.
static int ablk_decrypt(struct ablkcipher_request *req)
{
struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
struct async_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm);
if (!irq_fpu_usable()) {
struct ablkcipher_request *cryptd_req =
ablkcipher_request_ctx(req);
memcpy(cryptd_req, req, sizeof(*req));
ablkcipher_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
return crypto_ablkcipher_decrypt(cryptd_req);
} else {
struct blkcipher_desc desc;
desc.tfm = cryptd_ablkcipher_child(ctx->cryptd_tfm);
desc.info = req->info;
desc.flags = 0;
return crypto_blkcipher_crt(desc.tfm)->decrypt(
&desc, req->dst, req->src, req->nbytes);
}
}Now the problem scenario is, if a packet came for decryption and "irq_fpu_usable()" is not usable, then it will be queued for later decryption and crypto_aead_decrypt will be reuturned with "-EINPROGRESS" error. When the packets are in queue, if some more packets came and "irq_fpu_usable()" is usable then those packets will be decrypted before the queued packets and queued packets will get out of order.
And we've figure out a patch as the attached, the basic idea is just queue the packets if "irq_fpu_usable()" is not usable or if there are already few packets queued for decryption. Else decrypt the packets.
Could anybody tell if this is a appropriate fix? Or is this reordering thing a real probelm? 'cause I know the IPsec doesn't guarantee order at all. Appreciate it very much!
the best,
thank you
Quoted 1 line
From
eb0fe7074c61b4e57456c578db897928eb951db9Mon Sep 17 00:00:00 2001
From: Ming Liu <[email protected]>
Date: Thu, 31 Jul 2014 09:11:51 +0800
Subject: [PATCH] crypto: aesni-intel - avoid encrypt/decrypt re-ordering on
particular cpu
So far, the encrypt/decrypt are asynchronously processed in softirq and cryptd which would result in a implicit order of data, therefore leads IPSec stack also out of order while encapsulating/decapsulating packets.
Fix by letting encrypt/decrypt are processed only in one context for a particular period.
--- arch/x86/crypto/aesni-intel_glue.c | 32 ++++++++++++++++------------- crypto/cryptd.c | 42 ++++++++++++++++++++++++++++++++++++-- include/crypto/cryptd.h | 3 ++- 3 files changed, 60 insertions(+), 17 deletions(-)
arch/x86/crypto/aesni-intel_glue.c +18 -14
@@ -341,20 +341,22 @@ static int ablk_encrypt(struct ablkcipher_request *req) {struct crypto_ablkcipher *tfm =crypto_ablkcipher_reqtfm (req);struct async_aes_ctx *ctx =crypto_ablkcipher_ctx (tfm); +struct crypto_tfm *req_tfm =crypto_ablkcipher_tfm ( +crypto_ablkcipher_crt (&ctx->cryptd_tfm->base)->base); -if (!irq_fpu_usable ()) { -struct ablkcipher_request *cryptd_req = -ablkcipher_request_ctx (req); -memcpy (cryptd_req, req,sizeof (*req)); -ablkcipher_request_set_tfm (cryptd_req, &ctx->cryptd_tfm->base); -return crypto_ablkcipher_encrypt (cryptd_req); - }else { +if (irq_fpu_usable () && !cryptd_get_encrypt_nums (req_tfm)) {struct blkcipher_desc desc; desc.tfm =cryptd_ablkcipher_child (ctx->cryptd_tfm); desc.info = req->info; desc.flags =0 ;return crypto_blkcipher_crt (desc.tfm)->encrypt ( &desc, req->dst, req->src, req->nbytes); + }else { +struct ablkcipher_request *cryptd_req = +ablkcipher_request_ctx (req); +memcpy (cryptd_req, req,sizeof (*req)); + cryptd_req->base.tfm = req_tfm; +return crypto_ablkcipher_encrypt (cryptd_req); } }
@@ -362,20 +364,22 @@ static int ablk_decrypt(struct ablkcipher_request *req) {struct crypto_ablkcipher *tfm =crypto_ablkcipher_reqtfm (req);struct async_aes_ctx *ctx =crypto_ablkcipher_ctx (tfm); +struct crypto_tfm *req_tfm =crypto_ablkcipher_tfm ( +crypto_ablkcipher_crt (&ctx->cryptd_tfm->base)->base); -if (!irq_fpu_usable ()) { -struct ablkcipher_request *cryptd_req = -ablkcipher_request_ctx (req); -memcpy (cryptd_req, req,sizeof (*req)); -ablkcipher_request_set_tfm (cryptd_req, &ctx->cryptd_tfm->base); -return crypto_ablkcipher_decrypt (cryptd_req); - }else { +if (irq_fpu_usable () && !cryptd_get_decrypt_nums (req_tfm)) {struct blkcipher_desc desc; desc.tfm =cryptd_ablkcipher_child (ctx->cryptd_tfm); desc.info = req->info; desc.flags =0 ;return crypto_blkcipher_crt (desc.tfm)->decrypt ( &desc, req->dst, req->src, req->nbytes); + }else { +struct ablkcipher_request *cryptd_req = +ablkcipher_request_ctx (req); +memcpy (cryptd_req, req,sizeof (*req)); + cryptd_req->base.tfm = req_tfm; +return crypto_ablkcipher_decrypt (cryptd_req); } }
crypto/cryptd.c +40 -2
86 lines not on this page · raw message
include/crypto/cryptd.h +2 -1
@@ -41,5 +41,6 @@ struct cryptd_ahash *cryptd_alloc_ahash(const char *alg_name,struct crypto_shash *cryptd_ahash_child (struct cryptd_ahash *tfm);struct shash_desc *cryptd_shash_desc (struct ahash_request *req);void cryptd_free_ahash (struct cryptd_ahash *tfm); - +unsigned int cryptd_get_encrypt_nums (struct crypto_tfm *tfm); +unsigned int cryptd_get_decrypt_nums (struct crypto_tfm *tfm);#endif
Signature · 1 line
1.8.5.2.233.g932f7e4
Herbert Xu
maintainerQuoted 5 lines · Ming Liu
And we've figure out a patch as the attached, the basic idea is just queue the packets if "
irq_fpu_usable()" is not usable or if there are already few packets queued for decryption. Else decrypt the packets.
Yes your description makes sense and I will review your patch for inclusion.
Thanks,
Signature · 3 lines
Email: Herbert Xu <[email protected]> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
Quoted 7 lines · Herbert Xu
Quoted 4 lines · Ming Liu
And we've figure out a patch as the attached, the basic idea is just queue the packets if "
irq_fpu_usable()" is not usable or if there are already few packets queued for decryption. Else decrypt the packets.Yes your description makes sense and I will review your patch for inclusion.
Thanks, Herbert!
//Ming Liu
Quoted 2 lines
Thanks,
Quoted 7 lines · Herbert Xu
Quoted 4 lines · Ming Liu
And we've figure out a patch as the attached, the basic idea is just queue the packets if "
irq_fpu_usable()" is not usable or if there are already few packets queued for decryption. Else decrypt the packets.Yes your description makes sense and I will review your patch for inclusion.
Hi, Herbert:
Please review this attached patch instead, the original one has a problem causing the kernel crash.
the best,
thank you
Quoted 4 lines
Thanks,
From
0b3113eb2e690c3899382360f89281d6a64e8f3aMon Sep 17 00:00:00 2001
From: Ming Liu <[email protected]>
Date: Sun, 3 Aug 2014 16:23:39 +0800
Subject: [PATCH] crypto: aesni-intel- avoid encrypt/decrypt re-ordering with
debug
So far, the encrypt/decrypt are asynchronously processed in softirq and cryptd which would result in a implicit order of data, therefore leads IPSec stack also out of order while encapsulating/decapsulating packets.
Fix by letting encrypt/decrypt are processed only in one context for a particular period.
--- arch/x86/crypto/aesni-intel_glue.c | 32 ++++++++++++---------- crypto/cryptd.c | 55 ++++++++++++++++++++++++++++++++++++-- include/crypto/cryptd.h | 3 ++- 3 files changed, 73 insertions(+), 17 deletions(-)
arch/x86/crypto/aesni-intel_glue.c +18 -14
@@ -341,20 +341,22 @@ static int ablk_encrypt(struct ablkcipher_request *req) {struct crypto_ablkcipher *tfm =crypto_ablkcipher_reqtfm (req);struct async_aes_ctx *ctx =crypto_ablkcipher_ctx (tfm); +struct crypto_tfm *req_tfm =crypto_ablkcipher_tfm ( +crypto_ablkcipher_crt (&ctx->cryptd_tfm->base)->base); -if (!irq_fpu_usable ()) { -struct ablkcipher_request *cryptd_req = -ablkcipher_request_ctx (req); -memcpy (cryptd_req, req,sizeof (*req)); -ablkcipher_request_set_tfm (cryptd_req, &ctx->cryptd_tfm->base); -return crypto_ablkcipher_encrypt (cryptd_req); - }else { +if (irq_fpu_usable () && !cryptd_get_encrypt_nums (req_tfm)) {struct blkcipher_desc desc; desc.tfm =cryptd_ablkcipher_child (ctx->cryptd_tfm); desc.info = req->info; desc.flags =0 ;return crypto_blkcipher_crt (desc.tfm)->encrypt ( &desc, req->dst, req->src, req->nbytes); + }else { +struct ablkcipher_request *cryptd_req = +ablkcipher_request_ctx (req); +memcpy (cryptd_req, req,sizeof (*req)); + cryptd_req->base.tfm = req_tfm; +return crypto_ablkcipher_encrypt (cryptd_req); } }
@@ -362,20 +364,22 @@ static int ablk_decrypt(struct ablkcipher_request *req) {struct crypto_ablkcipher *tfm =crypto_ablkcipher_reqtfm (req);struct async_aes_ctx *ctx =crypto_ablkcipher_ctx (tfm); +struct crypto_tfm *req_tfm =crypto_ablkcipher_tfm ( +crypto_ablkcipher_crt (&ctx->cryptd_tfm->base)->base); -if (!irq_fpu_usable ()) { -struct ablkcipher_request *cryptd_req = -ablkcipher_request_ctx (req); -memcpy (cryptd_req, req,sizeof (*req)); -ablkcipher_request_set_tfm (cryptd_req, &ctx->cryptd_tfm->base); -return crypto_ablkcipher_decrypt (cryptd_req); - }else { +if (irq_fpu_usable () && !cryptd_get_decrypt_nums (req_tfm)) {struct blkcipher_desc desc; desc.tfm =cryptd_ablkcipher_child (ctx->cryptd_tfm); desc.info = req->info; desc.flags =0 ;return crypto_blkcipher_crt (desc.tfm)->decrypt ( &desc, req->dst, req->src, req->nbytes); + }else { +struct ablkcipher_request *cryptd_req = +ablkcipher_request_ctx (req); +memcpy (cryptd_req, req,sizeof (*req)); + cryptd_req->base.tfm = req_tfm; +return crypto_ablkcipher_decrypt (cryptd_req); } }
crypto/cryptd.c +53 -2
99 lines not on this page · raw message
include/crypto/cryptd.h +2 -1
@@ -41,5 +41,6 @@ struct cryptd_ahash *cryptd_alloc_ahash(const char *alg_name,struct crypto_shash *cryptd_ahash_child (struct cryptd_ahash *tfm);struct shash_desc *cryptd_shash_desc (struct ahash_request *req);void cryptd_free_ahash (struct cryptd_ahash *tfm); - +unsigned int cryptd_get_encrypt_nums (struct crypto_tfm *tfm); +unsigned int cryptd_get_decrypt_nums (struct crypto_tfm *tfm);#endif
Signature · 1 line
1.8.5.2.233.g932f7e4
Herbert Xu
maintainerQuoted 3 lines · Ming Liu
Please review this attached patch instead, the original one has a problem causing the kernel crash.
Thanks for the patch. I think it would better to enforce ordering for all operations rather than treat encryptions separately from decryptions. We could conceivably have more complex operations made up from both encryptions and decryptions that could then get out-of-order.
It would also make the code simpler.
Cheers,
Signature · 3 lines
Email: Herbert Xu <[email protected]> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
Hi, Herbert:
I've figured out a new patch for this issue reported by me previously, the basic idea is adding a cryptd_flush_queue function fixing it by being called from softirq to flush all previous queued elements before processing a new one, and it works very well so far per my test, would you please review it?
the best,
thank you
Quoted 9 lines · Herbert Xu
Quoted 2 lines · Ming Liu
Please review this attached patch instead, the original one has a problem causing the kernel crash.
Thanks for the patch. I think it would better to enforce ordering for all operations rather than treat encryptions separately from decryptions. We could conceivably have more complex operations made up from both encryptions and decryptions that could then get out-of-order.
Quoted 5 lines
It would also make the code simpler.
Cheers,
From
5e00cd925755015d4057ded1b24fd994e507a21eMon Sep 17 00:00:00 2001
From: Ming Liu <[email protected]>
Date: Wed, 12 Nov 2014 11:11:57 +0800
Subject: [PATCH] crypto: aesni-intel - avoid IPsec re-ordering
So far, the encryption/decryption are asynchronously processed in softirq and cryptd which would result in a implicit order of data, therefore leads IPSec stack also out of order while encapsulating or decapsulating packets.
Consider the following scenario:
DECRYPTION INBOUND
|
|
+-----Packet A
| |
| |
| Packet B
| |
(cryptd) | | (software interrupts)
| ......
| |
| |
| Packet B(decrypted)
| |
| |
+---> Packet A(decrypted)
|
|
DECRYPTION OUTBOUNDAdd cryptd_flush_queue function fixing it by being called from softirq to flush all previous queued elements before processing a new one. To prevent cryptd_flush_queue() being accessed from software interrupts, local_bh_disable/enable needs to be relocated in several places.
--- crypto/ablk_helper.c | 10 ++++- crypto/cryptd.c | 107 ++++++++++++++++++++++++++++++++++++++++-------- include/crypto/cryptd.h | 13 ++++++ 3 files changed, 111 insertions(+), 19 deletions(-)
crypto/ablk_helper.c +8 -2
@@ -70,16 +70,19 @@ int ablk_encrypt(struct ablkcipher_request *req) {struct crypto_ablkcipher *tfm =crypto_ablkcipher_reqtfm (req);struct async_helper_ctx *ctx =crypto_ablkcipher_ctx (tfm); +struct crypto_tfm *req_tfm =crypto_ablkcipher_tfm ( +crypto_ablkcipher_crt (&ctx->cryptd_tfm->base)->base);if (!may_use_simd ()) {struct ablkcipher_request *cryptd_req =ablkcipher_request_ctx (req); *cryptd_req = *req; -ablkcipher_request_set_tfm (cryptd_req, &ctx->cryptd_tfm->base); + cryptd_req->base.tfm = req_tfm;return crypto_ablkcipher_encrypt (cryptd_req); }else { +cryptd_flush_queue (req_tfm,CRYPTD_ENCRYPT );return __ablk_encrypt (req); } }
@@ -89,13 +92,15 @@ int ablk_decrypt(struct ablkcipher_request *req) {struct crypto_ablkcipher *tfm =crypto_ablkcipher_reqtfm (req);struct async_helper_ctx *ctx =crypto_ablkcipher_ctx (tfm); +struct crypto_tfm *req_tfm =crypto_ablkcipher_tfm ( +crypto_ablkcipher_crt (&ctx->cryptd_tfm->base)->base);if (!may_use_simd ()) {struct ablkcipher_request *cryptd_req =ablkcipher_request_ctx (req); *cryptd_req = *req; -ablkcipher_request_set_tfm (cryptd_req, &ctx->cryptd_tfm->base); + cryptd_req->base.tfm = req_tfm;return crypto_ablkcipher_decrypt (cryptd_req); }else {
@@ -105,6 +110,7 @@ int ablk_decrypt(struct ablkcipher_request *req) desc.info = req->info; desc.flags =0 ; +cryptd_flush_queue (req_tfm,CRYPTD_DECRYPT );return crypto_blkcipher_crt (desc.tfm)->decrypt ( &desc, req->dst, req->src, req->nbytes); }
crypto/cryptd.c +90 -17
187 lines not on this page · raw message
include/crypto/cryptd.h +13 -0
@@ -16,6 +16,18 @@#include <linux/kernel.h> #include <crypto/hash.h> +typedef enum { +CRYPTD_BLKCIPHER_ENCRYPT , +CRYPTD_BLKCIPHER_DECRYPT , +CRYPTD_HASH_INIT , +CRYPTD_HASH_UPDATE , +CRYPTD_HASH_FINAL , +CRYPTD_HASH_FINUP , +CRYPTD_HASH_DIGEST , +CRYPTD_AEAD_ENCRYPT , +CRYPTD_AEAD_DECRYPT , +}cryptd_type_t ; +struct cryptd_ablkcipher {struct crypto_ablkcipher base; };
@@ -48,6 +60,7 @@ struct cryptd_ahash *cryptd_alloc_ahash(const char *alg_name,struct crypto_shash *cryptd_ahash_child (struct cryptd_ahash *tfm);struct shash_desc *cryptd_shash_desc (struct ahash_request *req);void cryptd_free_ahash (struct cryptd_ahash *tfm); +void cryptd_flush_queue (struct crypto_tfm *tfm,cryptd_type_t type);struct cryptd_aead {struct crypto_aead base;
Signature · 1 line
1.8.4.1