17c657876SArnaldo Carvalho de Melo /* 27c657876SArnaldo Carvalho de Melo * net/dccp/ccid.c 37c657876SArnaldo Carvalho de Melo * 47c657876SArnaldo Carvalho de Melo * An implementation of the DCCP protocol 57c657876SArnaldo Carvalho de Melo * Arnaldo Carvalho de Melo <acme@conectiva.com.br> 67c657876SArnaldo Carvalho de Melo * 77c657876SArnaldo Carvalho de Melo * CCID infrastructure 87c657876SArnaldo Carvalho de Melo * 97c657876SArnaldo Carvalho de Melo * This program is free software; you can redistribute it and/or modify it 107c657876SArnaldo Carvalho de Melo * under the terms of the GNU General Public License version 2 as 117c657876SArnaldo Carvalho de Melo * published by the Free Software Foundation. 127c657876SArnaldo Carvalho de Melo */ 137c657876SArnaldo Carvalho de Melo 147c657876SArnaldo Carvalho de Melo #include "ccid.h" 157c657876SArnaldo Carvalho de Melo 16ddebc973SGerrit Renker static struct ccid_operations *ccids[] = { 17ddebc973SGerrit Renker &ccid2_ops, 18ddebc973SGerrit Renker #ifdef CONFIG_IP_DCCP_CCID3 19ddebc973SGerrit Renker &ccid3_ops, 20ddebc973SGerrit Renker #endif 21ddebc973SGerrit Renker }; 22ddebc973SGerrit Renker 23ddebc973SGerrit Renker static struct ccid_operations *ccid_by_number(const u8 id) 24ddebc973SGerrit Renker { 25ddebc973SGerrit Renker int i; 26ddebc973SGerrit Renker 27ddebc973SGerrit Renker for (i = 0; i < ARRAY_SIZE(ccids); i++) 28ddebc973SGerrit Renker if (ccids[i]->ccid_id == id) 29ddebc973SGerrit Renker return ccids[i]; 30ddebc973SGerrit Renker return NULL; 31ddebc973SGerrit Renker } 32ddebc973SGerrit Renker 33ddebc973SGerrit Renker /* check that up to @array_len members in @ccid_array are supported */ 34ddebc973SGerrit Renker bool ccid_support_check(u8 const *ccid_array, u8 array_len) 35ddebc973SGerrit Renker { 36ddebc973SGerrit Renker while (array_len > 0) 37ddebc973SGerrit Renker if (ccid_by_number(ccid_array[--array_len]) == NULL) 38ddebc973SGerrit Renker return false; 39ddebc973SGerrit Renker return true; 40ddebc973SGerrit Renker } 41ddebc973SGerrit Renker 42ddebc973SGerrit Renker /** 43ddebc973SGerrit Renker * ccid_get_builtin_ccids - Populate a list of built-in CCIDs 44ddebc973SGerrit Renker * @ccid_array: pointer to copy into 45ddebc973SGerrit Renker * @array_len: value to return length into 46ddebc973SGerrit Renker * This function allocates memory - caller must see that it is freed after use. 47ddebc973SGerrit Renker */ 48ddebc973SGerrit Renker int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len) 49ddebc973SGerrit Renker { 50ddebc973SGerrit Renker *ccid_array = kmalloc(ARRAY_SIZE(ccids), gfp_any()); 51ddebc973SGerrit Renker if (*ccid_array == NULL) 52ddebc973SGerrit Renker return -ENOBUFS; 53ddebc973SGerrit Renker 54ddebc973SGerrit Renker for (*array_len = 0; *array_len < ARRAY_SIZE(ccids); *array_len += 1) 55ddebc973SGerrit Renker (*ccid_array)[*array_len] = ccids[*array_len]->ccid_id; 56ddebc973SGerrit Renker return 0; 57ddebc973SGerrit Renker } 58ddebc973SGerrit Renker 59ddebc973SGerrit Renker int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, 60ddebc973SGerrit Renker char __user *optval, int __user *optlen) 61ddebc973SGerrit Renker { 62ddebc973SGerrit Renker u8 *ccid_array, array_len; 63ddebc973SGerrit Renker int err = 0; 64ddebc973SGerrit Renker 65ddebc973SGerrit Renker if (len < ARRAY_SIZE(ccids)) 66ddebc973SGerrit Renker return -EINVAL; 67ddebc973SGerrit Renker 68ddebc973SGerrit Renker if (ccid_get_builtin_ccids(&ccid_array, &array_len)) 69ddebc973SGerrit Renker return -ENOBUFS; 70ddebc973SGerrit Renker 71ddebc973SGerrit Renker if (put_user(array_len, optlen) || 72ddebc973SGerrit Renker copy_to_user(optval, ccid_array, array_len)) 73ddebc973SGerrit Renker err = -EFAULT; 74ddebc973SGerrit Renker 75ddebc973SGerrit Renker kfree(ccid_array); 76ddebc973SGerrit Renker return err; 77ddebc973SGerrit Renker } 78ddebc973SGerrit Renker 79e18b890bSChristoph Lameter static struct kmem_cache *ccid_kmem_cache_create(int obj_size, const char *fmt,...) 807c657876SArnaldo Carvalho de Melo { 81e18b890bSChristoph Lameter struct kmem_cache *slab; 8291f0ebf7SArnaldo Carvalho de Melo char slab_name_fmt[32], *slab_name; 8391f0ebf7SArnaldo Carvalho de Melo va_list args; 8491f0ebf7SArnaldo Carvalho de Melo 8591f0ebf7SArnaldo Carvalho de Melo va_start(args, fmt); 8691f0ebf7SArnaldo Carvalho de Melo vsnprintf(slab_name_fmt, sizeof(slab_name_fmt), fmt, args); 8791f0ebf7SArnaldo Carvalho de Melo va_end(args); 8891f0ebf7SArnaldo Carvalho de Melo 8991f0ebf7SArnaldo Carvalho de Melo slab_name = kstrdup(slab_name_fmt, GFP_KERNEL); 9091f0ebf7SArnaldo Carvalho de Melo if (slab_name == NULL) 9191f0ebf7SArnaldo Carvalho de Melo return NULL; 9291f0ebf7SArnaldo Carvalho de Melo slab = kmem_cache_create(slab_name, sizeof(struct ccid) + obj_size, 0, 9320c2df83SPaul Mundt SLAB_HWCACHE_ALIGN, NULL); 9491f0ebf7SArnaldo Carvalho de Melo if (slab == NULL) 9591f0ebf7SArnaldo Carvalho de Melo kfree(slab_name); 9691f0ebf7SArnaldo Carvalho de Melo return slab; 9791f0ebf7SArnaldo Carvalho de Melo } 9891f0ebf7SArnaldo Carvalho de Melo 99e18b890bSChristoph Lameter static void ccid_kmem_cache_destroy(struct kmem_cache *slab) 10091f0ebf7SArnaldo Carvalho de Melo { 10191f0ebf7SArnaldo Carvalho de Melo if (slab != NULL) { 10291f0ebf7SArnaldo Carvalho de Melo const char *name = kmem_cache_name(slab); 10391f0ebf7SArnaldo Carvalho de Melo 10491f0ebf7SArnaldo Carvalho de Melo kmem_cache_destroy(slab); 10591f0ebf7SArnaldo Carvalho de Melo kfree(name); 10691f0ebf7SArnaldo Carvalho de Melo } 10791f0ebf7SArnaldo Carvalho de Melo } 10891f0ebf7SArnaldo Carvalho de Melo 109ddebc973SGerrit Renker static int ccid_activate(struct ccid_operations *ccid_ops) 11091f0ebf7SArnaldo Carvalho de Melo { 11191f0ebf7SArnaldo Carvalho de Melo int err = -ENOBUFS; 11291f0ebf7SArnaldo Carvalho de Melo 11391f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_rx_slab = 11491f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_create(ccid_ops->ccid_hc_rx_obj_size, 11584a97b0aSGerrit Renker "ccid%u_hc_rx_sock", 11684a97b0aSGerrit Renker ccid_ops->ccid_id); 11791f0ebf7SArnaldo Carvalho de Melo if (ccid_ops->ccid_hc_rx_slab == NULL) 11891f0ebf7SArnaldo Carvalho de Melo goto out; 11991f0ebf7SArnaldo Carvalho de Melo 12091f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_tx_slab = 12191f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_create(ccid_ops->ccid_hc_tx_obj_size, 12284a97b0aSGerrit Renker "ccid%u_hc_tx_sock", 12384a97b0aSGerrit Renker ccid_ops->ccid_id); 12491f0ebf7SArnaldo Carvalho de Melo if (ccid_ops->ccid_hc_tx_slab == NULL) 12591f0ebf7SArnaldo Carvalho de Melo goto out_free_rx_slab; 1267c657876SArnaldo Carvalho de Melo 127ddebc973SGerrit Renker pr_info("CCID: Activated CCID %d (%s)\n", 12891f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_id, ccid_ops->ccid_name); 129ddebc973SGerrit Renker err = 0; 13091f0ebf7SArnaldo Carvalho de Melo out: 1317c657876SArnaldo Carvalho de Melo return err; 13291f0ebf7SArnaldo Carvalho de Melo out_free_rx_slab: 13391f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab); 13491f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_rx_slab = NULL; 13591f0ebf7SArnaldo Carvalho de Melo goto out; 1367c657876SArnaldo Carvalho de Melo } 1377c657876SArnaldo Carvalho de Melo 138ddebc973SGerrit Renker static void ccid_deactivate(struct ccid_operations *ccid_ops) 1397c657876SArnaldo Carvalho de Melo { 14091f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab); 14191f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_tx_slab = NULL; 14291f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab); 14391f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_rx_slab = NULL; 14491f0ebf7SArnaldo Carvalho de Melo 145ddebc973SGerrit Renker pr_info("CCID: Deactivated CCID %d (%s)\n", 14691f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_id, ccid_ops->ccid_name); 1477c657876SArnaldo Carvalho de Melo } 1487c657876SArnaldo Carvalho de Melo 149*e5fd56caSGerrit Renker struct ccid *ccid_new(const u8 id, struct sock *sk, bool rx) 1507c657876SArnaldo Carvalho de Melo { 151ddebc973SGerrit Renker struct ccid_operations *ccid_ops = ccid_by_number(id); 15291f0ebf7SArnaldo Carvalho de Melo struct ccid *ccid = NULL; 1537c657876SArnaldo Carvalho de Melo 15491f0ebf7SArnaldo Carvalho de Melo if (ccid_ops == NULL) 155ddebc973SGerrit Renker goto out; 15691f0ebf7SArnaldo Carvalho de Melo 15791f0ebf7SArnaldo Carvalho de Melo ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab : 158*e5fd56caSGerrit Renker ccid_ops->ccid_hc_tx_slab, gfp_any()); 15991f0ebf7SArnaldo Carvalho de Melo if (ccid == NULL) 160ddebc973SGerrit Renker goto out; 16191f0ebf7SArnaldo Carvalho de Melo ccid->ccid_ops = ccid_ops; 16291f0ebf7SArnaldo Carvalho de Melo if (rx) { 16391f0ebf7SArnaldo Carvalho de Melo memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size); 16491f0ebf7SArnaldo Carvalho de Melo if (ccid->ccid_ops->ccid_hc_rx_init != NULL && 16591f0ebf7SArnaldo Carvalho de Melo ccid->ccid_ops->ccid_hc_rx_init(ccid, sk) != 0) 16691f0ebf7SArnaldo Carvalho de Melo goto out_free_ccid; 16791f0ebf7SArnaldo Carvalho de Melo } else { 16891f0ebf7SArnaldo Carvalho de Melo memset(ccid + 1, 0, ccid_ops->ccid_hc_tx_obj_size); 16991f0ebf7SArnaldo Carvalho de Melo if (ccid->ccid_ops->ccid_hc_tx_init != NULL && 17091f0ebf7SArnaldo Carvalho de Melo ccid->ccid_ops->ccid_hc_tx_init(ccid, sk) != 0) 17191f0ebf7SArnaldo Carvalho de Melo goto out_free_ccid; 17291f0ebf7SArnaldo Carvalho de Melo } 17391f0ebf7SArnaldo Carvalho de Melo out: 1747c657876SArnaldo Carvalho de Melo return ccid; 17591f0ebf7SArnaldo Carvalho de Melo out_free_ccid: 17691f0ebf7SArnaldo Carvalho de Melo kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab : 17791f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_tx_slab, ccid); 1787c657876SArnaldo Carvalho de Melo ccid = NULL; 1797c657876SArnaldo Carvalho de Melo goto out; 1807c657876SArnaldo Carvalho de Melo } 1817c657876SArnaldo Carvalho de Melo 18291f0ebf7SArnaldo Carvalho de Melo void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk) 18391f0ebf7SArnaldo Carvalho de Melo { 184*e5fd56caSGerrit Renker if (ccid != NULL) { 185*e5fd56caSGerrit Renker if (ccid->ccid_ops->ccid_hc_rx_exit != NULL) 186*e5fd56caSGerrit Renker ccid->ccid_ops->ccid_hc_rx_exit(sk); 187*e5fd56caSGerrit Renker kmem_cache_free(ccid->ccid_ops->ccid_hc_rx_slab, ccid); 18891f0ebf7SArnaldo Carvalho de Melo } 189*e5fd56caSGerrit Renker } 19091f0ebf7SArnaldo Carvalho de Melo 19191f0ebf7SArnaldo Carvalho de Melo void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk) 19291f0ebf7SArnaldo Carvalho de Melo { 193*e5fd56caSGerrit Renker if (ccid != NULL) { 194*e5fd56caSGerrit Renker if (ccid->ccid_ops->ccid_hc_tx_exit != NULL) 195*e5fd56caSGerrit Renker ccid->ccid_ops->ccid_hc_tx_exit(sk); 196*e5fd56caSGerrit Renker kmem_cache_free(ccid->ccid_ops->ccid_hc_tx_slab, ccid); 19791f0ebf7SArnaldo Carvalho de Melo } 198*e5fd56caSGerrit Renker } 199ddebc973SGerrit Renker 200ddebc973SGerrit Renker int __init ccid_initialize_builtins(void) 201ddebc973SGerrit Renker { 202ddebc973SGerrit Renker int i, err; 203ddebc973SGerrit Renker 204ddebc973SGerrit Renker for (i = 0; i < ARRAY_SIZE(ccids); i++) { 205ddebc973SGerrit Renker err = ccid_activate(ccids[i]); 206ddebc973SGerrit Renker if (err) 207ddebc973SGerrit Renker goto unwind_registrations; 208ddebc973SGerrit Renker } 209ddebc973SGerrit Renker return 0; 210ddebc973SGerrit Renker 211ddebc973SGerrit Renker unwind_registrations: 212ddebc973SGerrit Renker while(--i >= 0) 213ddebc973SGerrit Renker ccid_deactivate(ccids[i]); 214ddebc973SGerrit Renker return err; 215ddebc973SGerrit Renker } 216ddebc973SGerrit Renker 217ddebc973SGerrit Renker void ccid_cleanup_builtins(void) 218ddebc973SGerrit Renker { 219ddebc973SGerrit Renker int i; 220ddebc973SGerrit Renker 221ddebc973SGerrit Renker for (i = 0; i < ARRAY_SIZE(ccids); i++) 222ddebc973SGerrit Renker ccid_deactivate(ccids[i]); 223ddebc973SGerrit Renker } 224