Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  SR-IPv6 implementation -- HMAC functions
0004  *
0005  *  Author:
0006  *  David Lebrun <david.lebrun@uclouvain.be>
0007  */
0008 
0009 #include <linux/errno.h>
0010 #include <linux/kernel.h>
0011 #include <linux/types.h>
0012 #include <linux/socket.h>
0013 #include <linux/sockios.h>
0014 #include <linux/net.h>
0015 #include <linux/netdevice.h>
0016 #include <linux/in6.h>
0017 #include <linux/icmpv6.h>
0018 #include <linux/mroute6.h>
0019 #include <linux/slab.h>
0020 #include <linux/rhashtable.h>
0021 
0022 #include <linux/netfilter.h>
0023 #include <linux/netfilter_ipv6.h>
0024 
0025 #include <net/sock.h>
0026 #include <net/snmp.h>
0027 
0028 #include <net/ipv6.h>
0029 #include <net/protocol.h>
0030 #include <net/transp_v6.h>
0031 #include <net/rawv6.h>
0032 #include <net/ndisc.h>
0033 #include <net/ip6_route.h>
0034 #include <net/addrconf.h>
0035 #include <net/xfrm.h>
0036 
0037 #include <crypto/hash.h>
0038 #include <net/seg6.h>
0039 #include <net/genetlink.h>
0040 #include <net/seg6_hmac.h>
0041 #include <linux/random.h>
0042 
0043 static DEFINE_PER_CPU(char [SEG6_HMAC_RING_SIZE], hmac_ring);
0044 
0045 static int seg6_hmac_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
0046 {
0047     const struct seg6_hmac_info *hinfo = obj;
0048 
0049     return (hinfo->hmackeyid != *(__u32 *)arg->key);
0050 }
0051 
0052 static inline void seg6_hinfo_release(struct seg6_hmac_info *hinfo)
0053 {
0054     kfree_rcu(hinfo, rcu);
0055 }
0056 
0057 static void seg6_free_hi(void *ptr, void *arg)
0058 {
0059     struct seg6_hmac_info *hinfo = (struct seg6_hmac_info *)ptr;
0060 
0061     if (hinfo)
0062         seg6_hinfo_release(hinfo);
0063 }
0064 
0065 static const struct rhashtable_params rht_params = {
0066     .head_offset        = offsetof(struct seg6_hmac_info, node),
0067     .key_offset     = offsetof(struct seg6_hmac_info, hmackeyid),
0068     .key_len        = sizeof(u32),
0069     .automatic_shrinking    = true,
0070     .obj_cmpfn      = seg6_hmac_cmpfn,
0071 };
0072 
0073 static struct seg6_hmac_algo hmac_algos[] = {
0074     {
0075         .alg_id = SEG6_HMAC_ALGO_SHA1,
0076         .name = "hmac(sha1)",
0077     },
0078     {
0079         .alg_id = SEG6_HMAC_ALGO_SHA256,
0080         .name = "hmac(sha256)",
0081     },
0082 };
0083 
0084 static struct sr6_tlv_hmac *seg6_get_tlv_hmac(struct ipv6_sr_hdr *srh)
0085 {
0086     struct sr6_tlv_hmac *tlv;
0087 
0088     if (srh->hdrlen < (srh->first_segment + 1) * 2 + 5)
0089         return NULL;
0090 
0091     if (!sr_has_hmac(srh))
0092         return NULL;
0093 
0094     tlv = (struct sr6_tlv_hmac *)
0095           ((char *)srh + ((srh->hdrlen + 1) << 3) - 40);
0096 
0097     if (tlv->tlvhdr.type != SR6_TLV_HMAC || tlv->tlvhdr.len != 38)
0098         return NULL;
0099 
0100     return tlv;
0101 }
0102 
0103 static struct seg6_hmac_algo *__hmac_get_algo(u8 alg_id)
0104 {
0105     struct seg6_hmac_algo *algo;
0106     int i, alg_count;
0107 
0108     alg_count = ARRAY_SIZE(hmac_algos);
0109     for (i = 0; i < alg_count; i++) {
0110         algo = &hmac_algos[i];
0111         if (algo->alg_id == alg_id)
0112             return algo;
0113     }
0114 
0115     return NULL;
0116 }
0117 
0118 static int __do_hmac(struct seg6_hmac_info *hinfo, const char *text, u8 psize,
0119              u8 *output, int outlen)
0120 {
0121     struct seg6_hmac_algo *algo;
0122     struct crypto_shash *tfm;
0123     struct shash_desc *shash;
0124     int ret, dgsize;
0125 
0126     algo = __hmac_get_algo(hinfo->alg_id);
0127     if (!algo)
0128         return -ENOENT;
0129 
0130     tfm = *this_cpu_ptr(algo->tfms);
0131 
0132     dgsize = crypto_shash_digestsize(tfm);
0133     if (dgsize > outlen) {
0134         pr_debug("sr-ipv6: __do_hmac: digest size too big (%d / %d)\n",
0135              dgsize, outlen);
0136         return -ENOMEM;
0137     }
0138 
0139     ret = crypto_shash_setkey(tfm, hinfo->secret, hinfo->slen);
0140     if (ret < 0) {
0141         pr_debug("sr-ipv6: crypto_shash_setkey failed: err %d\n", ret);
0142         goto failed;
0143     }
0144 
0145     shash = *this_cpu_ptr(algo->shashs);
0146     shash->tfm = tfm;
0147 
0148     ret = crypto_shash_digest(shash, text, psize, output);
0149     if (ret < 0) {
0150         pr_debug("sr-ipv6: crypto_shash_digest failed: err %d\n", ret);
0151         goto failed;
0152     }
0153 
0154     return dgsize;
0155 
0156 failed:
0157     return ret;
0158 }
0159 
0160 int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr,
0161               struct in6_addr *saddr, u8 *output)
0162 {
0163     __be32 hmackeyid = cpu_to_be32(hinfo->hmackeyid);
0164     u8 tmp_out[SEG6_HMAC_MAX_DIGESTSIZE];
0165     int plen, i, dgsize, wrsize;
0166     char *ring, *off;
0167 
0168     /* a 160-byte buffer for digest output allows to store highest known
0169      * hash function (RadioGatun) with up to 1216 bits
0170      */
0171 
0172     /* saddr(16) + first_seg(1) + flags(1) + keyid(4) + seglist(16n) */
0173     plen = 16 + 1 + 1 + 4 + (hdr->first_segment + 1) * 16;
0174 
0175     /* this limit allows for 14 segments */
0176     if (plen >= SEG6_HMAC_RING_SIZE)
0177         return -EMSGSIZE;
0178 
0179     /* Let's build the HMAC text on the ring buffer. The text is composed
0180      * as follows, in order:
0181      *
0182      * 1. Source IPv6 address (128 bits)
0183      * 2. first_segment value (8 bits)
0184      * 3. Flags (8 bits)
0185      * 4. HMAC Key ID (32 bits)
0186      * 5. All segments in the segments list (n * 128 bits)
0187      */
0188 
0189     local_bh_disable();
0190     ring = this_cpu_ptr(hmac_ring);
0191     off = ring;
0192 
0193     /* source address */
0194     memcpy(off, saddr, 16);
0195     off += 16;
0196 
0197     /* first_segment value */
0198     *off++ = hdr->first_segment;
0199 
0200     /* flags */
0201     *off++ = hdr->flags;
0202 
0203     /* HMAC Key ID */
0204     memcpy(off, &hmackeyid, 4);
0205     off += 4;
0206 
0207     /* all segments in the list */
0208     for (i = 0; i < hdr->first_segment + 1; i++) {
0209         memcpy(off, hdr->segments + i, 16);
0210         off += 16;
0211     }
0212 
0213     dgsize = __do_hmac(hinfo, ring, plen, tmp_out,
0214                SEG6_HMAC_MAX_DIGESTSIZE);
0215     local_bh_enable();
0216 
0217     if (dgsize < 0)
0218         return dgsize;
0219 
0220     wrsize = SEG6_HMAC_FIELD_LEN;
0221     if (wrsize > dgsize)
0222         wrsize = dgsize;
0223 
0224     memset(output, 0, SEG6_HMAC_FIELD_LEN);
0225     memcpy(output, tmp_out, wrsize);
0226 
0227     return 0;
0228 }
0229 EXPORT_SYMBOL(seg6_hmac_compute);
0230 
0231 /* checks if an incoming SR-enabled packet's HMAC status matches
0232  * the incoming policy.
0233  *
0234  * called with rcu_read_lock()
0235  */
0236 bool seg6_hmac_validate_skb(struct sk_buff *skb)
0237 {
0238     u8 hmac_output[SEG6_HMAC_FIELD_LEN];
0239     struct net *net = dev_net(skb->dev);
0240     struct seg6_hmac_info *hinfo;
0241     struct sr6_tlv_hmac *tlv;
0242     struct ipv6_sr_hdr *srh;
0243     struct inet6_dev *idev;
0244 
0245     idev = __in6_dev_get(skb->dev);
0246 
0247     srh = (struct ipv6_sr_hdr *)skb_transport_header(skb);
0248 
0249     tlv = seg6_get_tlv_hmac(srh);
0250 
0251     /* mandatory check but no tlv */
0252     if (idev->cnf.seg6_require_hmac > 0 && !tlv)
0253         return false;
0254 
0255     /* no check */
0256     if (idev->cnf.seg6_require_hmac < 0)
0257         return true;
0258 
0259     /* check only if present */
0260     if (idev->cnf.seg6_require_hmac == 0 && !tlv)
0261         return true;
0262 
0263     /* now, seg6_require_hmac >= 0 && tlv */
0264 
0265     hinfo = seg6_hmac_info_lookup(net, be32_to_cpu(tlv->hmackeyid));
0266     if (!hinfo)
0267         return false;
0268 
0269     if (seg6_hmac_compute(hinfo, srh, &ipv6_hdr(skb)->saddr, hmac_output))
0270         return false;
0271 
0272     if (memcmp(hmac_output, tlv->hmac, SEG6_HMAC_FIELD_LEN) != 0)
0273         return false;
0274 
0275     return true;
0276 }
0277 EXPORT_SYMBOL(seg6_hmac_validate_skb);
0278 
0279 /* called with rcu_read_lock() */
0280 struct seg6_hmac_info *seg6_hmac_info_lookup(struct net *net, u32 key)
0281 {
0282     struct seg6_pernet_data *sdata = seg6_pernet(net);
0283     struct seg6_hmac_info *hinfo;
0284 
0285     hinfo = rhashtable_lookup_fast(&sdata->hmac_infos, &key, rht_params);
0286 
0287     return hinfo;
0288 }
0289 EXPORT_SYMBOL(seg6_hmac_info_lookup);
0290 
0291 int seg6_hmac_info_add(struct net *net, u32 key, struct seg6_hmac_info *hinfo)
0292 {
0293     struct seg6_pernet_data *sdata = seg6_pernet(net);
0294     int err;
0295 
0296     err = rhashtable_lookup_insert_fast(&sdata->hmac_infos, &hinfo->node,
0297                         rht_params);
0298 
0299     return err;
0300 }
0301 EXPORT_SYMBOL(seg6_hmac_info_add);
0302 
0303 int seg6_hmac_info_del(struct net *net, u32 key)
0304 {
0305     struct seg6_pernet_data *sdata = seg6_pernet(net);
0306     struct seg6_hmac_info *hinfo;
0307     int err = -ENOENT;
0308 
0309     hinfo = rhashtable_lookup_fast(&sdata->hmac_infos, &key, rht_params);
0310     if (!hinfo)
0311         goto out;
0312 
0313     err = rhashtable_remove_fast(&sdata->hmac_infos, &hinfo->node,
0314                      rht_params);
0315     if (err)
0316         goto out;
0317 
0318     seg6_hinfo_release(hinfo);
0319 
0320 out:
0321     return err;
0322 }
0323 EXPORT_SYMBOL(seg6_hmac_info_del);
0324 
0325 int seg6_push_hmac(struct net *net, struct in6_addr *saddr,
0326            struct ipv6_sr_hdr *srh)
0327 {
0328     struct seg6_hmac_info *hinfo;
0329     struct sr6_tlv_hmac *tlv;
0330     int err = -ENOENT;
0331 
0332     tlv = seg6_get_tlv_hmac(srh);
0333     if (!tlv)
0334         return -EINVAL;
0335 
0336     rcu_read_lock();
0337 
0338     hinfo = seg6_hmac_info_lookup(net, be32_to_cpu(tlv->hmackeyid));
0339     if (!hinfo)
0340         goto out;
0341 
0342     memset(tlv->hmac, 0, SEG6_HMAC_FIELD_LEN);
0343     err = seg6_hmac_compute(hinfo, srh, saddr, tlv->hmac);
0344 
0345 out:
0346     rcu_read_unlock();
0347     return err;
0348 }
0349 EXPORT_SYMBOL(seg6_push_hmac);
0350 
0351 static int seg6_hmac_init_algo(void)
0352 {
0353     struct seg6_hmac_algo *algo;
0354     struct crypto_shash *tfm;
0355     struct shash_desc *shash;
0356     int i, alg_count, cpu;
0357 
0358     alg_count = ARRAY_SIZE(hmac_algos);
0359 
0360     for (i = 0; i < alg_count; i++) {
0361         struct crypto_shash **p_tfm;
0362         int shsize;
0363 
0364         algo = &hmac_algos[i];
0365         algo->tfms = alloc_percpu(struct crypto_shash *);
0366         if (!algo->tfms)
0367             return -ENOMEM;
0368 
0369         for_each_possible_cpu(cpu) {
0370             tfm = crypto_alloc_shash(algo->name, 0, 0);
0371             if (IS_ERR(tfm))
0372                 return PTR_ERR(tfm);
0373             p_tfm = per_cpu_ptr(algo->tfms, cpu);
0374             *p_tfm = tfm;
0375         }
0376 
0377         p_tfm = raw_cpu_ptr(algo->tfms);
0378         tfm = *p_tfm;
0379 
0380         shsize = sizeof(*shash) + crypto_shash_descsize(tfm);
0381 
0382         algo->shashs = alloc_percpu(struct shash_desc *);
0383         if (!algo->shashs)
0384             return -ENOMEM;
0385 
0386         for_each_possible_cpu(cpu) {
0387             shash = kzalloc_node(shsize, GFP_KERNEL,
0388                          cpu_to_node(cpu));
0389             if (!shash)
0390                 return -ENOMEM;
0391             *per_cpu_ptr(algo->shashs, cpu) = shash;
0392         }
0393     }
0394 
0395     return 0;
0396 }
0397 
0398 int __init seg6_hmac_init(void)
0399 {
0400     return seg6_hmac_init_algo();
0401 }
0402 
0403 int __net_init seg6_hmac_net_init(struct net *net)
0404 {
0405     struct seg6_pernet_data *sdata = seg6_pernet(net);
0406 
0407     return rhashtable_init(&sdata->hmac_infos, &rht_params);
0408 }
0409 
0410 void seg6_hmac_exit(void)
0411 {
0412     struct seg6_hmac_algo *algo = NULL;
0413     int i, alg_count, cpu;
0414 
0415     alg_count = ARRAY_SIZE(hmac_algos);
0416     for (i = 0; i < alg_count; i++) {
0417         algo = &hmac_algos[i];
0418         for_each_possible_cpu(cpu) {
0419             struct crypto_shash *tfm;
0420             struct shash_desc *shash;
0421 
0422             shash = *per_cpu_ptr(algo->shashs, cpu);
0423             kfree(shash);
0424             tfm = *per_cpu_ptr(algo->tfms, cpu);
0425             crypto_free_shash(tfm);
0426         }
0427         free_percpu(algo->tfms);
0428         free_percpu(algo->shashs);
0429     }
0430 }
0431 EXPORT_SYMBOL(seg6_hmac_exit);
0432 
0433 void __net_exit seg6_hmac_net_exit(struct net *net)
0434 {
0435     struct seg6_pernet_data *sdata = seg6_pernet(net);
0436 
0437     rhashtable_free_and_destroy(&sdata->hmac_infos, seg6_free_hi, NULL);
0438 }
0439 EXPORT_SYMBOL(seg6_hmac_net_exit);