linux/net/dccp/ccid.c
<<
>>
Prefs
   1/*
   2 *  net/dccp/ccid.c
   3 *
   4 *  An implementation of the DCCP protocol
   5 *  Arnaldo Carvalho de Melo <acme@conectiva.com.br>
   6 *
   7 *  CCID infrastructure
   8 *
   9 *      This program is free software; you can redistribute it and/or modify it
  10 *      under the terms of the GNU General Public License version 2 as
  11 *      published by the Free Software Foundation.
  12 */
  13
  14#include "ccid.h"
  15#include "ccids/lib/tfrc.h"
  16
  17static struct ccid_operations *ccids[] = {
  18        &ccid2_ops,
  19#ifdef CONFIG_IP_DCCP_CCID3
  20        &ccid3_ops,
  21#endif
  22};
  23
  24static struct ccid_operations *ccid_by_number(const u8 id)
  25{
  26        int i;
  27
  28        for (i = 0; i < ARRAY_SIZE(ccids); i++)
  29                if (ccids[i]->ccid_id == id)
  30                        return ccids[i];
  31        return NULL;
  32}
  33
  34/* check that up to @array_len members in @ccid_array are supported */
  35bool ccid_support_check(u8 const *ccid_array, u8 array_len)
  36{
  37        while (array_len > 0)
  38                if (ccid_by_number(ccid_array[--array_len]) == NULL)
  39                        return false;
  40        return true;
  41}
  42
  43/**
  44 * ccid_get_builtin_ccids  -  Populate a list of built-in CCIDs
  45 * @ccid_array: pointer to copy into
  46 * @array_len: value to return length into
  47 * This function allocates memory - caller must see that it is freed after use.
  48 */
  49int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len)
  50{
  51        *ccid_array = kmalloc(ARRAY_SIZE(ccids), gfp_any());
  52        if (*ccid_array == NULL)
  53                return -ENOBUFS;
  54
  55        for (*array_len = 0; *array_len < ARRAY_SIZE(ccids); *array_len += 1)
  56                (*ccid_array)[*array_len] = ccids[*array_len]->ccid_id;
  57        return 0;
  58}
  59
  60int ccid_getsockopt_builtin_ccids(struct sock *sk, int len,
  61                                  char __user *optval, int __user *optlen)
  62{
  63        u8 *ccid_array, array_len;
  64        int err = 0;
  65
  66        if (len < ARRAY_SIZE(ccids))
  67                return -EINVAL;
  68
  69        if (ccid_get_builtin_ccids(&ccid_array, &array_len))
  70                return -ENOBUFS;
  71
  72        if (put_user(array_len, optlen) ||
  73            copy_to_user(optval, ccid_array, array_len))
  74                err = -EFAULT;
  75
  76        kfree(ccid_array);
  77        return err;
  78}
  79
  80static struct kmem_cache *ccid_kmem_cache_create(int obj_size, const char *fmt,...)
  81{
  82        struct kmem_cache *slab;
  83        char slab_name_fmt[32], *slab_name;
  84        va_list args;
  85
  86        va_start(args, fmt);
  87        vsnprintf(slab_name_fmt, sizeof(slab_name_fmt), fmt, args);
  88        va_end(args);
  89
  90        slab_name = kstrdup(slab_name_fmt, GFP_KERNEL);
  91        if (slab_name == NULL)
  92                return NULL;
  93        slab = kmem_cache_create(slab_name, sizeof(struct ccid) + obj_size, 0,
  94                                 SLAB_HWCACHE_ALIGN, NULL);
  95        if (slab == NULL)
  96                kfree(slab_name);
  97        return slab;
  98}
  99
 100static void ccid_kmem_cache_destroy(struct kmem_cache *slab)
 101{
 102        if (slab != NULL) {
 103                const char *name = kmem_cache_name(slab);
 104
 105                kmem_cache_destroy(slab);
 106                kfree(name);
 107        }
 108}
 109
 110static int ccid_activate(struct ccid_operations *ccid_ops)
 111{
 112        int err = -ENOBUFS;
 113
 114        ccid_ops->ccid_hc_rx_slab =
 115                        ccid_kmem_cache_create(ccid_ops->ccid_hc_rx_obj_size,
 116                                               "ccid%u_hc_rx_sock",
 117                                               ccid_ops->ccid_id);
 118        if (ccid_ops->ccid_hc_rx_slab == NULL)
 119                goto out;
 120
 121        ccid_ops->ccid_hc_tx_slab =
 122                        ccid_kmem_cache_create(ccid_ops->ccid_hc_tx_obj_size,
 123                                               "ccid%u_hc_tx_sock",
 124                                               ccid_ops->ccid_id);
 125        if (ccid_ops->ccid_hc_tx_slab == NULL)
 126                goto out_free_rx_slab;
 127
 128        pr_info("CCID: Activated CCID %d (%s)\n",
 129                ccid_ops->ccid_id, ccid_ops->ccid_name);
 130        err = 0;
 131out:
 132        return err;
 133out_free_rx_slab:
 134        ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
 135        ccid_ops->ccid_hc_rx_slab = NULL;
 136        goto out;
 137}
 138
 139static void ccid_deactivate(struct ccid_operations *ccid_ops)
 140{
 141        ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab);
 142        ccid_ops->ccid_hc_tx_slab = NULL;
 143        ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
 144        ccid_ops->ccid_hc_rx_slab = NULL;
 145
 146        pr_info("CCID: Deactivated CCID %d (%s)\n",
 147                ccid_ops->ccid_id, ccid_ops->ccid_name);
 148}
 149
 150struct ccid *ccid_new(const u8 id, struct sock *sk, bool rx)
 151{
 152        struct ccid_operations *ccid_ops = ccid_by_number(id);
 153        struct ccid *ccid = NULL;
 154
 155        if (ccid_ops == NULL)
 156                goto out;
 157
 158        ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab :
 159                                     ccid_ops->ccid_hc_tx_slab, gfp_any());
 160        if (ccid == NULL)
 161                goto out;
 162        ccid->ccid_ops = ccid_ops;
 163        if (rx) {
 164                memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size);
 165                if (ccid->ccid_ops->ccid_hc_rx_init != NULL &&
 166                    ccid->ccid_ops->ccid_hc_rx_init(ccid, sk) != 0)
 167                        goto out_free_ccid;
 168        } else {
 169                memset(ccid + 1, 0, ccid_ops->ccid_hc_tx_obj_size);
 170                if (ccid->ccid_ops->ccid_hc_tx_init != NULL &&
 171                    ccid->ccid_ops->ccid_hc_tx_init(ccid, sk) != 0)
 172                        goto out_free_ccid;
 173        }
 174out:
 175        return ccid;
 176out_free_ccid:
 177        kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab :
 178                        ccid_ops->ccid_hc_tx_slab, ccid);
 179        ccid = NULL;
 180        goto out;
 181}
 182
 183void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk)
 184{
 185        if (ccid != NULL) {
 186                if (ccid->ccid_ops->ccid_hc_rx_exit != NULL)
 187                        ccid->ccid_ops->ccid_hc_rx_exit(sk);
 188                kmem_cache_free(ccid->ccid_ops->ccid_hc_rx_slab, ccid);
 189        }
 190}
 191
 192void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk)
 193{
 194        if (ccid != NULL) {
 195                if (ccid->ccid_ops->ccid_hc_tx_exit != NULL)
 196                        ccid->ccid_ops->ccid_hc_tx_exit(sk);
 197                kmem_cache_free(ccid->ccid_ops->ccid_hc_tx_slab, ccid);
 198        }
 199}
 200
 201int __init ccid_initialize_builtins(void)
 202{
 203        int i, err = tfrc_lib_init();
 204
 205        if (err)
 206                return err;
 207
 208        for (i = 0; i < ARRAY_SIZE(ccids); i++) {
 209                err = ccid_activate(ccids[i]);
 210                if (err)
 211                        goto unwind_registrations;
 212        }
 213        return 0;
 214
 215unwind_registrations:
 216        while(--i >= 0)
 217                ccid_deactivate(ccids[i]);
 218        tfrc_lib_exit();
 219        return err;
 220}
 221
 222void ccid_cleanup_builtins(void)
 223{
 224        int i;
 225
 226        for (i = 0; i < ARRAY_SIZE(ccids); i++)
 227                ccid_deactivate(ccids[i]);
 228        tfrc_lib_exit();
 229}
 230