162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SR-IPv6 implementation -- HMAC functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: 662306a36Sopenharmony_ci * David Lebrun <david.lebrun@uclouvain.be> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/socket.h> 1362306a36Sopenharmony_ci#include <linux/sockios.h> 1462306a36Sopenharmony_ci#include <linux/net.h> 1562306a36Sopenharmony_ci#include <linux/netdevice.h> 1662306a36Sopenharmony_ci#include <linux/in6.h> 1762306a36Sopenharmony_ci#include <linux/icmpv6.h> 1862306a36Sopenharmony_ci#include <linux/mroute6.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/rhashtable.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/netfilter.h> 2362306a36Sopenharmony_ci#include <linux/netfilter_ipv6.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <net/sock.h> 2662306a36Sopenharmony_ci#include <net/snmp.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <net/ipv6.h> 2962306a36Sopenharmony_ci#include <net/protocol.h> 3062306a36Sopenharmony_ci#include <net/transp_v6.h> 3162306a36Sopenharmony_ci#include <net/rawv6.h> 3262306a36Sopenharmony_ci#include <net/ndisc.h> 3362306a36Sopenharmony_ci#include <net/ip6_route.h> 3462306a36Sopenharmony_ci#include <net/addrconf.h> 3562306a36Sopenharmony_ci#include <net/xfrm.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <crypto/hash.h> 3862306a36Sopenharmony_ci#include <net/seg6.h> 3962306a36Sopenharmony_ci#include <net/genetlink.h> 4062306a36Sopenharmony_ci#include <net/seg6_hmac.h> 4162306a36Sopenharmony_ci#include <linux/random.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic DEFINE_PER_CPU(char [SEG6_HMAC_RING_SIZE], hmac_ring); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int seg6_hmac_cmpfn(struct rhashtable_compare_arg *arg, const void *obj) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci const struct seg6_hmac_info *hinfo = obj; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return (hinfo->hmackeyid != *(__u32 *)arg->key); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic inline void seg6_hinfo_release(struct seg6_hmac_info *hinfo) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci kfree_rcu(hinfo, rcu); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void seg6_free_hi(void *ptr, void *arg) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct seg6_hmac_info *hinfo = (struct seg6_hmac_info *)ptr; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (hinfo) 6262306a36Sopenharmony_ci seg6_hinfo_release(hinfo); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic const struct rhashtable_params rht_params = { 6662306a36Sopenharmony_ci .head_offset = offsetof(struct seg6_hmac_info, node), 6762306a36Sopenharmony_ci .key_offset = offsetof(struct seg6_hmac_info, hmackeyid), 6862306a36Sopenharmony_ci .key_len = sizeof(u32), 6962306a36Sopenharmony_ci .automatic_shrinking = true, 7062306a36Sopenharmony_ci .obj_cmpfn = seg6_hmac_cmpfn, 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic struct seg6_hmac_algo hmac_algos[] = { 7462306a36Sopenharmony_ci { 7562306a36Sopenharmony_ci .alg_id = SEG6_HMAC_ALGO_SHA1, 7662306a36Sopenharmony_ci .name = "hmac(sha1)", 7762306a36Sopenharmony_ci }, 7862306a36Sopenharmony_ci { 7962306a36Sopenharmony_ci .alg_id = SEG6_HMAC_ALGO_SHA256, 8062306a36Sopenharmony_ci .name = "hmac(sha256)", 8162306a36Sopenharmony_ci }, 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic struct sr6_tlv_hmac *seg6_get_tlv_hmac(struct ipv6_sr_hdr *srh) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct sr6_tlv_hmac *tlv; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (srh->hdrlen < (srh->first_segment + 1) * 2 + 5) 8962306a36Sopenharmony_ci return NULL; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (!sr_has_hmac(srh)) 9262306a36Sopenharmony_ci return NULL; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci tlv = (struct sr6_tlv_hmac *) 9562306a36Sopenharmony_ci ((char *)srh + ((srh->hdrlen + 1) << 3) - 40); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (tlv->tlvhdr.type != SR6_TLV_HMAC || tlv->tlvhdr.len != 38) 9862306a36Sopenharmony_ci return NULL; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return tlv; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic struct seg6_hmac_algo *__hmac_get_algo(u8 alg_id) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct seg6_hmac_algo *algo; 10662306a36Sopenharmony_ci int i, alg_count; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci alg_count = ARRAY_SIZE(hmac_algos); 10962306a36Sopenharmony_ci for (i = 0; i < alg_count; i++) { 11062306a36Sopenharmony_ci algo = &hmac_algos[i]; 11162306a36Sopenharmony_ci if (algo->alg_id == alg_id) 11262306a36Sopenharmony_ci return algo; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return NULL; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int __do_hmac(struct seg6_hmac_info *hinfo, const char *text, u8 psize, 11962306a36Sopenharmony_ci u8 *output, int outlen) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct seg6_hmac_algo *algo; 12262306a36Sopenharmony_ci struct crypto_shash *tfm; 12362306a36Sopenharmony_ci struct shash_desc *shash; 12462306a36Sopenharmony_ci int ret, dgsize; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci algo = __hmac_get_algo(hinfo->alg_id); 12762306a36Sopenharmony_ci if (!algo) 12862306a36Sopenharmony_ci return -ENOENT; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci tfm = *this_cpu_ptr(algo->tfms); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci dgsize = crypto_shash_digestsize(tfm); 13362306a36Sopenharmony_ci if (dgsize > outlen) { 13462306a36Sopenharmony_ci pr_debug("sr-ipv6: __do_hmac: digest size too big (%d / %d)\n", 13562306a36Sopenharmony_ci dgsize, outlen); 13662306a36Sopenharmony_ci return -ENOMEM; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ret = crypto_shash_setkey(tfm, hinfo->secret, hinfo->slen); 14062306a36Sopenharmony_ci if (ret < 0) { 14162306a36Sopenharmony_ci pr_debug("sr-ipv6: crypto_shash_setkey failed: err %d\n", ret); 14262306a36Sopenharmony_ci goto failed; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci shash = *this_cpu_ptr(algo->shashs); 14662306a36Sopenharmony_ci shash->tfm = tfm; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ret = crypto_shash_digest(shash, text, psize, output); 14962306a36Sopenharmony_ci if (ret < 0) { 15062306a36Sopenharmony_ci pr_debug("sr-ipv6: crypto_shash_digest failed: err %d\n", ret); 15162306a36Sopenharmony_ci goto failed; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return dgsize; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cifailed: 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ciint seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr, 16162306a36Sopenharmony_ci struct in6_addr *saddr, u8 *output) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci __be32 hmackeyid = cpu_to_be32(hinfo->hmackeyid); 16462306a36Sopenharmony_ci u8 tmp_out[SEG6_HMAC_MAX_DIGESTSIZE]; 16562306a36Sopenharmony_ci int plen, i, dgsize, wrsize; 16662306a36Sopenharmony_ci char *ring, *off; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* a 160-byte buffer for digest output allows to store highest known 16962306a36Sopenharmony_ci * hash function (RadioGatun) with up to 1216 bits 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* saddr(16) + first_seg(1) + flags(1) + keyid(4) + seglist(16n) */ 17362306a36Sopenharmony_ci plen = 16 + 1 + 1 + 4 + (hdr->first_segment + 1) * 16; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* this limit allows for 14 segments */ 17662306a36Sopenharmony_ci if (plen >= SEG6_HMAC_RING_SIZE) 17762306a36Sopenharmony_ci return -EMSGSIZE; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Let's build the HMAC text on the ring buffer. The text is composed 18062306a36Sopenharmony_ci * as follows, in order: 18162306a36Sopenharmony_ci * 18262306a36Sopenharmony_ci * 1. Source IPv6 address (128 bits) 18362306a36Sopenharmony_ci * 2. first_segment value (8 bits) 18462306a36Sopenharmony_ci * 3. Flags (8 bits) 18562306a36Sopenharmony_ci * 4. HMAC Key ID (32 bits) 18662306a36Sopenharmony_ci * 5. All segments in the segments list (n * 128 bits) 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci local_bh_disable(); 19062306a36Sopenharmony_ci ring = this_cpu_ptr(hmac_ring); 19162306a36Sopenharmony_ci off = ring; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* source address */ 19462306a36Sopenharmony_ci memcpy(off, saddr, 16); 19562306a36Sopenharmony_ci off += 16; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* first_segment value */ 19862306a36Sopenharmony_ci *off++ = hdr->first_segment; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* flags */ 20162306a36Sopenharmony_ci *off++ = hdr->flags; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* HMAC Key ID */ 20462306a36Sopenharmony_ci memcpy(off, &hmackeyid, 4); 20562306a36Sopenharmony_ci off += 4; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* all segments in the list */ 20862306a36Sopenharmony_ci for (i = 0; i < hdr->first_segment + 1; i++) { 20962306a36Sopenharmony_ci memcpy(off, hdr->segments + i, 16); 21062306a36Sopenharmony_ci off += 16; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci dgsize = __do_hmac(hinfo, ring, plen, tmp_out, 21462306a36Sopenharmony_ci SEG6_HMAC_MAX_DIGESTSIZE); 21562306a36Sopenharmony_ci local_bh_enable(); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (dgsize < 0) 21862306a36Sopenharmony_ci return dgsize; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci wrsize = SEG6_HMAC_FIELD_LEN; 22162306a36Sopenharmony_ci if (wrsize > dgsize) 22262306a36Sopenharmony_ci wrsize = dgsize; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci memset(output, 0, SEG6_HMAC_FIELD_LEN); 22562306a36Sopenharmony_ci memcpy(output, tmp_out, wrsize); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ciEXPORT_SYMBOL(seg6_hmac_compute); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* checks if an incoming SR-enabled packet's HMAC status matches 23262306a36Sopenharmony_ci * the incoming policy. 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * called with rcu_read_lock() 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_cibool seg6_hmac_validate_skb(struct sk_buff *skb) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci u8 hmac_output[SEG6_HMAC_FIELD_LEN]; 23962306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 24062306a36Sopenharmony_ci struct seg6_hmac_info *hinfo; 24162306a36Sopenharmony_ci struct sr6_tlv_hmac *tlv; 24262306a36Sopenharmony_ci struct ipv6_sr_hdr *srh; 24362306a36Sopenharmony_ci struct inet6_dev *idev; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci idev = __in6_dev_get(skb->dev); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci srh = (struct ipv6_sr_hdr *)skb_transport_header(skb); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci tlv = seg6_get_tlv_hmac(srh); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* mandatory check but no tlv */ 25262306a36Sopenharmony_ci if (idev->cnf.seg6_require_hmac > 0 && !tlv) 25362306a36Sopenharmony_ci return false; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* no check */ 25662306a36Sopenharmony_ci if (idev->cnf.seg6_require_hmac < 0) 25762306a36Sopenharmony_ci return true; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* check only if present */ 26062306a36Sopenharmony_ci if (idev->cnf.seg6_require_hmac == 0 && !tlv) 26162306a36Sopenharmony_ci return true; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* now, seg6_require_hmac >= 0 && tlv */ 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci hinfo = seg6_hmac_info_lookup(net, be32_to_cpu(tlv->hmackeyid)); 26662306a36Sopenharmony_ci if (!hinfo) 26762306a36Sopenharmony_ci return false; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (seg6_hmac_compute(hinfo, srh, &ipv6_hdr(skb)->saddr, hmac_output)) 27062306a36Sopenharmony_ci return false; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (memcmp(hmac_output, tlv->hmac, SEG6_HMAC_FIELD_LEN) != 0) 27362306a36Sopenharmony_ci return false; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return true; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ciEXPORT_SYMBOL(seg6_hmac_validate_skb); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* called with rcu_read_lock() */ 28062306a36Sopenharmony_cistruct seg6_hmac_info *seg6_hmac_info_lookup(struct net *net, u32 key) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct seg6_pernet_data *sdata = seg6_pernet(net); 28362306a36Sopenharmony_ci struct seg6_hmac_info *hinfo; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci hinfo = rhashtable_lookup_fast(&sdata->hmac_infos, &key, rht_params); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return hinfo; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ciEXPORT_SYMBOL(seg6_hmac_info_lookup); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ciint seg6_hmac_info_add(struct net *net, u32 key, struct seg6_hmac_info *hinfo) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct seg6_pernet_data *sdata = seg6_pernet(net); 29462306a36Sopenharmony_ci int err; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci err = rhashtable_lookup_insert_fast(&sdata->hmac_infos, &hinfo->node, 29762306a36Sopenharmony_ci rht_params); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return err; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ciEXPORT_SYMBOL(seg6_hmac_info_add); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ciint seg6_hmac_info_del(struct net *net, u32 key) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct seg6_pernet_data *sdata = seg6_pernet(net); 30662306a36Sopenharmony_ci struct seg6_hmac_info *hinfo; 30762306a36Sopenharmony_ci int err = -ENOENT; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci hinfo = rhashtable_lookup_fast(&sdata->hmac_infos, &key, rht_params); 31062306a36Sopenharmony_ci if (!hinfo) 31162306a36Sopenharmony_ci goto out; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci err = rhashtable_remove_fast(&sdata->hmac_infos, &hinfo->node, 31462306a36Sopenharmony_ci rht_params); 31562306a36Sopenharmony_ci if (err) 31662306a36Sopenharmony_ci goto out; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci seg6_hinfo_release(hinfo); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ciout: 32162306a36Sopenharmony_ci return err; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ciEXPORT_SYMBOL(seg6_hmac_info_del); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ciint seg6_push_hmac(struct net *net, struct in6_addr *saddr, 32662306a36Sopenharmony_ci struct ipv6_sr_hdr *srh) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct seg6_hmac_info *hinfo; 32962306a36Sopenharmony_ci struct sr6_tlv_hmac *tlv; 33062306a36Sopenharmony_ci int err = -ENOENT; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci tlv = seg6_get_tlv_hmac(srh); 33362306a36Sopenharmony_ci if (!tlv) 33462306a36Sopenharmony_ci return -EINVAL; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci rcu_read_lock(); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci hinfo = seg6_hmac_info_lookup(net, be32_to_cpu(tlv->hmackeyid)); 33962306a36Sopenharmony_ci if (!hinfo) 34062306a36Sopenharmony_ci goto out; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci memset(tlv->hmac, 0, SEG6_HMAC_FIELD_LEN); 34362306a36Sopenharmony_ci err = seg6_hmac_compute(hinfo, srh, saddr, tlv->hmac); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ciout: 34662306a36Sopenharmony_ci rcu_read_unlock(); 34762306a36Sopenharmony_ci return err; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ciEXPORT_SYMBOL(seg6_push_hmac); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int seg6_hmac_init_algo(void) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct seg6_hmac_algo *algo; 35462306a36Sopenharmony_ci struct crypto_shash *tfm; 35562306a36Sopenharmony_ci struct shash_desc *shash; 35662306a36Sopenharmony_ci int i, alg_count, cpu; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci alg_count = ARRAY_SIZE(hmac_algos); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci for (i = 0; i < alg_count; i++) { 36162306a36Sopenharmony_ci struct crypto_shash **p_tfm; 36262306a36Sopenharmony_ci int shsize; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci algo = &hmac_algos[i]; 36562306a36Sopenharmony_ci algo->tfms = alloc_percpu(struct crypto_shash *); 36662306a36Sopenharmony_ci if (!algo->tfms) 36762306a36Sopenharmony_ci return -ENOMEM; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 37062306a36Sopenharmony_ci tfm = crypto_alloc_shash(algo->name, 0, 0); 37162306a36Sopenharmony_ci if (IS_ERR(tfm)) 37262306a36Sopenharmony_ci return PTR_ERR(tfm); 37362306a36Sopenharmony_ci p_tfm = per_cpu_ptr(algo->tfms, cpu); 37462306a36Sopenharmony_ci *p_tfm = tfm; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci p_tfm = raw_cpu_ptr(algo->tfms); 37862306a36Sopenharmony_ci tfm = *p_tfm; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci shsize = sizeof(*shash) + crypto_shash_descsize(tfm); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci algo->shashs = alloc_percpu(struct shash_desc *); 38362306a36Sopenharmony_ci if (!algo->shashs) 38462306a36Sopenharmony_ci return -ENOMEM; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 38762306a36Sopenharmony_ci shash = kzalloc_node(shsize, GFP_KERNEL, 38862306a36Sopenharmony_ci cpu_to_node(cpu)); 38962306a36Sopenharmony_ci if (!shash) 39062306a36Sopenharmony_ci return -ENOMEM; 39162306a36Sopenharmony_ci *per_cpu_ptr(algo->shashs, cpu) = shash; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ciint __init seg6_hmac_init(void) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci return seg6_hmac_init_algo(); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ciint __net_init seg6_hmac_net_init(struct net *net) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct seg6_pernet_data *sdata = seg6_pernet(net); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return rhashtable_init(&sdata->hmac_infos, &rht_params); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_civoid seg6_hmac_exit(void) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct seg6_hmac_algo *algo = NULL; 41362306a36Sopenharmony_ci int i, alg_count, cpu; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci alg_count = ARRAY_SIZE(hmac_algos); 41662306a36Sopenharmony_ci for (i = 0; i < alg_count; i++) { 41762306a36Sopenharmony_ci algo = &hmac_algos[i]; 41862306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 41962306a36Sopenharmony_ci struct crypto_shash *tfm; 42062306a36Sopenharmony_ci struct shash_desc *shash; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci shash = *per_cpu_ptr(algo->shashs, cpu); 42362306a36Sopenharmony_ci kfree(shash); 42462306a36Sopenharmony_ci tfm = *per_cpu_ptr(algo->tfms, cpu); 42562306a36Sopenharmony_ci crypto_free_shash(tfm); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci free_percpu(algo->tfms); 42862306a36Sopenharmony_ci free_percpu(algo->shashs); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ciEXPORT_SYMBOL(seg6_hmac_exit); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_civoid __net_exit seg6_hmac_net_exit(struct net *net) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct seg6_pernet_data *sdata = seg6_pernet(net); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci rhashtable_free_and_destroy(&sdata->hmac_infos, seg6_free_hi, NULL); 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ciEXPORT_SYMBOL(seg6_hmac_net_exit); 440