Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  net/dccp/ccid.c
0004  *
0005  *  An implementation of the DCCP protocol
0006  *  Arnaldo Carvalho de Melo <acme@conectiva.com.br>
0007  *
0008  *  CCID infrastructure
0009  */
0010 
0011 #include <linux/slab.h>
0012 
0013 #include "ccid.h"
0014 #include "ccids/lib/tfrc.h"
0015 
0016 static struct ccid_operations *ccids[] = {
0017     &ccid2_ops,
0018 #ifdef CONFIG_IP_DCCP_CCID3
0019     &ccid3_ops,
0020 #endif
0021 };
0022 
0023 static struct ccid_operations *ccid_by_number(const u8 id)
0024 {
0025     int i;
0026 
0027     for (i = 0; i < ARRAY_SIZE(ccids); i++)
0028         if (ccids[i]->ccid_id == id)
0029             return ccids[i];
0030     return NULL;
0031 }
0032 
0033 /* check that up to @array_len members in @ccid_array are supported */
0034 bool ccid_support_check(u8 const *ccid_array, u8 array_len)
0035 {
0036     while (array_len > 0)
0037         if (ccid_by_number(ccid_array[--array_len]) == NULL)
0038             return false;
0039     return true;
0040 }
0041 
0042 /**
0043  * ccid_get_builtin_ccids  -  Populate a list of built-in CCIDs
0044  * @ccid_array: pointer to copy into
0045  * @array_len: value to return length into
0046  *
0047  * This function allocates memory - caller must see that it is freed after use.
0048  */
0049 int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len)
0050 {
0051     *ccid_array = kmalloc(ARRAY_SIZE(ccids), gfp_any());
0052     if (*ccid_array == NULL)
0053         return -ENOBUFS;
0054 
0055     for (*array_len = 0; *array_len < ARRAY_SIZE(ccids); *array_len += 1)
0056         (*ccid_array)[*array_len] = ccids[*array_len]->ccid_id;
0057     return 0;
0058 }
0059 
0060 int ccid_getsockopt_builtin_ccids(struct sock *sk, int len,
0061                   char __user *optval, int __user *optlen)
0062 {
0063     u8 *ccid_array, array_len;
0064     int err = 0;
0065 
0066     if (ccid_get_builtin_ccids(&ccid_array, &array_len))
0067         return -ENOBUFS;
0068 
0069     if (put_user(array_len, optlen))
0070         err = -EFAULT;
0071     else if (len > 0 && copy_to_user(optval, ccid_array,
0072                      len > array_len ? array_len : len))
0073         err = -EFAULT;
0074 
0075     kfree(ccid_array);
0076     return err;
0077 }
0078 
0079 static __printf(3, 4) struct kmem_cache *ccid_kmem_cache_create(int obj_size, char *slab_name_fmt, const char *fmt,...)
0080 {
0081     struct kmem_cache *slab;
0082     va_list args;
0083 
0084     va_start(args, fmt);
0085     vsnprintf(slab_name_fmt, CCID_SLAB_NAME_LENGTH, fmt, args);
0086     va_end(args);
0087 
0088     slab = kmem_cache_create(slab_name_fmt, sizeof(struct ccid) + obj_size, 0,
0089                  SLAB_HWCACHE_ALIGN, NULL);
0090     return slab;
0091 }
0092 
0093 static void ccid_kmem_cache_destroy(struct kmem_cache *slab)
0094 {
0095     kmem_cache_destroy(slab);
0096 }
0097 
0098 static int __init ccid_activate(struct ccid_operations *ccid_ops)
0099 {
0100     int err = -ENOBUFS;
0101 
0102     ccid_ops->ccid_hc_rx_slab =
0103             ccid_kmem_cache_create(ccid_ops->ccid_hc_rx_obj_size,
0104                            ccid_ops->ccid_hc_rx_slab_name,
0105                            "ccid%u_hc_rx_sock",
0106                            ccid_ops->ccid_id);
0107     if (ccid_ops->ccid_hc_rx_slab == NULL)
0108         goto out;
0109 
0110     ccid_ops->ccid_hc_tx_slab =
0111             ccid_kmem_cache_create(ccid_ops->ccid_hc_tx_obj_size,
0112                            ccid_ops->ccid_hc_tx_slab_name,
0113                            "ccid%u_hc_tx_sock",
0114                            ccid_ops->ccid_id);
0115     if (ccid_ops->ccid_hc_tx_slab == NULL)
0116         goto out_free_rx_slab;
0117 
0118     pr_info("DCCP: Activated CCID %d (%s)\n",
0119         ccid_ops->ccid_id, ccid_ops->ccid_name);
0120     err = 0;
0121 out:
0122     return err;
0123 out_free_rx_slab:
0124     ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
0125     ccid_ops->ccid_hc_rx_slab = NULL;
0126     goto out;
0127 }
0128 
0129 static void ccid_deactivate(struct ccid_operations *ccid_ops)
0130 {
0131     ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab);
0132     ccid_ops->ccid_hc_tx_slab = NULL;
0133     ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
0134     ccid_ops->ccid_hc_rx_slab = NULL;
0135 
0136     pr_info("DCCP: Deactivated CCID %d (%s)\n",
0137         ccid_ops->ccid_id, ccid_ops->ccid_name);
0138 }
0139 
0140 struct ccid *ccid_new(const u8 id, struct sock *sk, bool rx)
0141 {
0142     struct ccid_operations *ccid_ops = ccid_by_number(id);
0143     struct ccid *ccid = NULL;
0144 
0145     if (ccid_ops == NULL)
0146         goto out;
0147 
0148     ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab :
0149                      ccid_ops->ccid_hc_tx_slab, gfp_any());
0150     if (ccid == NULL)
0151         goto out;
0152     ccid->ccid_ops = ccid_ops;
0153     if (rx) {
0154         memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size);
0155         if (ccid->ccid_ops->ccid_hc_rx_init != NULL &&
0156             ccid->ccid_ops->ccid_hc_rx_init(ccid, sk) != 0)
0157             goto out_free_ccid;
0158     } else {
0159         memset(ccid + 1, 0, ccid_ops->ccid_hc_tx_obj_size);
0160         if (ccid->ccid_ops->ccid_hc_tx_init != NULL &&
0161             ccid->ccid_ops->ccid_hc_tx_init(ccid, sk) != 0)
0162             goto out_free_ccid;
0163     }
0164 out:
0165     return ccid;
0166 out_free_ccid:
0167     kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab :
0168             ccid_ops->ccid_hc_tx_slab, ccid);
0169     ccid = NULL;
0170     goto out;
0171 }
0172 
0173 void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk)
0174 {
0175     if (ccid != NULL) {
0176         if (ccid->ccid_ops->ccid_hc_rx_exit != NULL)
0177             ccid->ccid_ops->ccid_hc_rx_exit(sk);
0178         kmem_cache_free(ccid->ccid_ops->ccid_hc_rx_slab, ccid);
0179     }
0180 }
0181 
0182 void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk)
0183 {
0184     if (ccid != NULL) {
0185         if (ccid->ccid_ops->ccid_hc_tx_exit != NULL)
0186             ccid->ccid_ops->ccid_hc_tx_exit(sk);
0187         kmem_cache_free(ccid->ccid_ops->ccid_hc_tx_slab, ccid);
0188     }
0189 }
0190 
0191 int __init ccid_initialize_builtins(void)
0192 {
0193     int i, err = tfrc_lib_init();
0194 
0195     if (err)
0196         return err;
0197 
0198     for (i = 0; i < ARRAY_SIZE(ccids); i++) {
0199         err = ccid_activate(ccids[i]);
0200         if (err)
0201             goto unwind_registrations;
0202     }
0203     return 0;
0204 
0205 unwind_registrations:
0206     while(--i >= 0)
0207         ccid_deactivate(ccids[i]);
0208     tfrc_lib_exit();
0209     return err;
0210 }
0211 
0212 void ccid_cleanup_builtins(void)
0213 {
0214     int i;
0215 
0216     for (i = 0; i < ARRAY_SIZE(ccids); i++)
0217         ccid_deactivate(ccids[i]);
0218     tfrc_lib_exit();
0219 }