Prepare the HMAC key when it is added to the kernel, instead of preparing it implicitly for every packet. This significantly improves the performance of seg6_hmac_compute(). A microbenchmark on x86_64 shows seg6_hmac_compute() (with HMAC-SHA256) dropping from ~1978 cycles to ~1419 cycles, a 28% improvement. The size of 'struct seg6_hmac_info' increases by 128 bytes, but that should be fine, since there should not be a massive number of keys. As a side effect, invalid values for SEG6_ATTR_ALGID (i.e., values other than SEG6_HMAC_ALGO_SHA1 and SEG6_HMAC_ALGO_SHA256) now cause an error immediately when the key is added, rather than later when computing a HMAC value is attempted. This seems like the expected behavior anyway. Signed-off-by: Eric Biggers --- include/net/seg6_hmac.h | 8 ++++++++ net/ipv6/seg6.c | 13 +++++++++++++ net/ipv6/seg6_hmac.c | 9 ++++----- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/include/net/seg6_hmac.h b/include/net/seg6_hmac.h index 3fe4123dbbf0a..e9f41725933e4 100644 --- a/include/net/seg6_hmac.h +++ b/include/net/seg6_hmac.h @@ -7,10 +7,12 @@ */ #ifndef _NET_SEG6_HMAC_H #define _NET_SEG6_HMAC_H +#include +#include #include #include #include #include #include @@ -24,13 +26,19 @@ struct seg6_hmac_info { struct rhash_head node; struct rcu_head rcu; u32 hmackeyid; + /* The raw key, kept only so it can be returned back to userspace */ char secret[SEG6_HMAC_SECRET_LEN]; u8 slen; u8 alg_id; + /* The prepared key, which the calculations actually use */ + union { + struct hmac_sha1_key sha1; + struct hmac_sha256_key sha256; + } key; }; extern int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr, struct in6_addr *saddr, u8 *output); diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c index a5c4c629b788c..313acdf1b2158 100644 --- a/net/ipv6/seg6.c +++ b/net/ipv6/seg6.c @@ -211,10 +211,23 @@ static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info) memcpy(hinfo->secret, secret, slen); hinfo->slen = slen; hinfo->alg_id = algid; hinfo->hmackeyid = hmackeyid; + switch (algid) { + case SEG6_HMAC_ALGO_SHA1: + hmac_sha1_preparekey(&hinfo->key.sha1, secret, slen); + break; + case SEG6_HMAC_ALGO_SHA256: + hmac_sha256_preparekey(&hinfo->key.sha256, secret, slen); + break; + default: + kfree(hinfo); + err = -EINVAL; + goto out_unlock; + } + err = seg6_hmac_info_add(net, hmackeyid, hinfo); if (err) kfree(hinfo); out_unlock: diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c index 816aef69e0cea..5cdcb3011cb3e 100644 --- a/net/ipv6/seg6_hmac.c +++ b/net/ipv6/seg6_hmac.c @@ -146,23 +146,22 @@ int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr, off += 16; } switch (hinfo->alg_id) { case SEG6_HMAC_ALGO_SHA1: - hmac_sha1_usingrawkey(hinfo->secret, hinfo->slen, ring, plen, - output); + hmac_sha1(&hinfo->key.sha1, ring, plen, output); static_assert(SEG6_HMAC_FIELD_LEN > SHA1_DIGEST_SIZE); memset(&output[SHA1_DIGEST_SIZE], 0, SEG6_HMAC_FIELD_LEN - SHA1_DIGEST_SIZE); break; case SEG6_HMAC_ALGO_SHA256: - hmac_sha256_usingrawkey(hinfo->secret, hinfo->slen, ring, plen, - output); + hmac_sha256(&hinfo->key.sha256, ring, plen, output); static_assert(SEG6_HMAC_FIELD_LEN == SHA256_DIGEST_SIZE); break; default: - ret = -ENOENT; + WARN_ON_ONCE(1); + ret = -EINVAL; break; } local_unlock_nested_bh(&hmac_storage.bh_lock); local_bh_enable(); return ret; -- 2.50.1