1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27c657876SArnaldo Carvalho de Melo /*
37c657876SArnaldo Carvalho de Melo * net/dccp/ccid.c
47c657876SArnaldo Carvalho de Melo *
57c657876SArnaldo Carvalho de Melo * An implementation of the DCCP protocol
67c657876SArnaldo Carvalho de Melo * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
77c657876SArnaldo Carvalho de Melo *
87c657876SArnaldo Carvalho de Melo * CCID infrastructure
97c657876SArnaldo Carvalho de Melo */
107c657876SArnaldo Carvalho de Melo
115a0e3ad6STejun Heo #include <linux/slab.h>
125a0e3ad6STejun Heo
137c657876SArnaldo Carvalho de Melo #include "ccid.h"
14129fa447SGerrit Renker #include "ccids/lib/tfrc.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
ccid_by_number(const u8 id)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 */
ccid_support_check(u8 const * ccid_array,u8 array_len)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
462c53040fSBen Hutchings *
47ddebc973SGerrit Renker * This function allocates memory - caller must see that it is freed after use.
48ddebc973SGerrit Renker */
ccid_get_builtin_ccids(u8 ** ccid_array,u8 * array_len)49ddebc973SGerrit Renker int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len)
50ddebc973SGerrit Renker {
51ddebc973SGerrit Renker *ccid_array = kmalloc(ARRAY_SIZE(ccids), gfp_any());
52ddebc973SGerrit Renker if (*ccid_array == NULL)
53ddebc973SGerrit Renker return -ENOBUFS;
54ddebc973SGerrit Renker
55ddebc973SGerrit Renker for (*array_len = 0; *array_len < ARRAY_SIZE(ccids); *array_len += 1)
56ddebc973SGerrit Renker (*ccid_array)[*array_len] = ccids[*array_len]->ccid_id;
57ddebc973SGerrit Renker return 0;
58ddebc973SGerrit Renker }
59ddebc973SGerrit Renker
ccid_getsockopt_builtin_ccids(struct sock * sk,int len,char __user * optval,int __user * optlen)60ddebc973SGerrit Renker int ccid_getsockopt_builtin_ccids(struct sock *sk, int len,
61ddebc973SGerrit Renker char __user *optval, int __user *optlen)
62ddebc973SGerrit Renker {
63ddebc973SGerrit Renker u8 *ccid_array, array_len;
64ddebc973SGerrit Renker int err = 0;
65ddebc973SGerrit Renker
66ddebc973SGerrit Renker if (ccid_get_builtin_ccids(&ccid_array, &array_len))
67ddebc973SGerrit Renker return -ENOBUFS;
68ddebc973SGerrit Renker
6969a6a0b3SGerrit Renker if (put_user(array_len, optlen))
7069a6a0b3SGerrit Renker err = -EFAULT;
7169a6a0b3SGerrit Renker else if (len > 0 && copy_to_user(optval, ccid_array,
7269a6a0b3SGerrit Renker len > array_len ? array_len : len))
73ddebc973SGerrit Renker err = -EFAULT;
74ddebc973SGerrit Renker
75ddebc973SGerrit Renker kfree(ccid_array);
76ddebc973SGerrit Renker return err;
77ddebc973SGerrit Renker }
78ddebc973SGerrit Renker
ccid_kmem_cache_create(int obj_size,char * slab_name_fmt,const char * fmt,...)79*c7bb8688SAndrew Lunn static __printf(3, 4) struct kmem_cache *ccid_kmem_cache_create(int obj_size, char *slab_name_fmt, const char *fmt,...)
807c657876SArnaldo Carvalho de Melo {
81e18b890bSChristoph Lameter struct kmem_cache *slab;
8291f0ebf7SArnaldo Carvalho de Melo va_list args;
8391f0ebf7SArnaldo Carvalho de Melo
8491f0ebf7SArnaldo Carvalho de Melo va_start(args, fmt);
858ed030ddSGerrit Renker vsnprintf(slab_name_fmt, CCID_SLAB_NAME_LENGTH, fmt, args);
8691f0ebf7SArnaldo Carvalho de Melo va_end(args);
8791f0ebf7SArnaldo Carvalho de Melo
88de4ef86cSNeil Horman slab = kmem_cache_create(slab_name_fmt, sizeof(struct ccid) + obj_size, 0,
8920c2df83SPaul Mundt SLAB_HWCACHE_ALIGN, NULL);
9091f0ebf7SArnaldo Carvalho de Melo return slab;
9191f0ebf7SArnaldo Carvalho de Melo }
9291f0ebf7SArnaldo Carvalho de Melo
ccid_kmem_cache_destroy(struct kmem_cache * slab)93e18b890bSChristoph Lameter static void ccid_kmem_cache_destroy(struct kmem_cache *slab)
9491f0ebf7SArnaldo Carvalho de Melo {
9591f0ebf7SArnaldo Carvalho de Melo kmem_cache_destroy(slab);
9691f0ebf7SArnaldo Carvalho de Melo }
9791f0ebf7SArnaldo Carvalho de Melo
ccid_activate(struct ccid_operations * ccid_ops)98e500f488SFabian Frederick static int __init ccid_activate(struct ccid_operations *ccid_ops)
9991f0ebf7SArnaldo Carvalho de Melo {
10091f0ebf7SArnaldo Carvalho de Melo int err = -ENOBUFS;
10191f0ebf7SArnaldo Carvalho de Melo
10291f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_rx_slab =
10391f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_create(ccid_ops->ccid_hc_rx_obj_size,
104de4ef86cSNeil Horman ccid_ops->ccid_hc_rx_slab_name,
10584a97b0aSGerrit Renker "ccid%u_hc_rx_sock",
10684a97b0aSGerrit Renker ccid_ops->ccid_id);
10791f0ebf7SArnaldo Carvalho de Melo if (ccid_ops->ccid_hc_rx_slab == NULL)
10891f0ebf7SArnaldo Carvalho de Melo goto out;
10991f0ebf7SArnaldo Carvalho de Melo
11091f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_tx_slab =
11191f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_create(ccid_ops->ccid_hc_tx_obj_size,
112de4ef86cSNeil Horman ccid_ops->ccid_hc_tx_slab_name,
11384a97b0aSGerrit Renker "ccid%u_hc_tx_sock",
11484a97b0aSGerrit Renker ccid_ops->ccid_id);
11591f0ebf7SArnaldo Carvalho de Melo if (ccid_ops->ccid_hc_tx_slab == NULL)
11691f0ebf7SArnaldo Carvalho de Melo goto out_free_rx_slab;
1177c657876SArnaldo Carvalho de Melo
1181fd9d208SGerrit Renker pr_info("DCCP: Activated CCID %d (%s)\n",
11991f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_id, ccid_ops->ccid_name);
120ddebc973SGerrit Renker err = 0;
12191f0ebf7SArnaldo Carvalho de Melo out:
1227c657876SArnaldo Carvalho de Melo return err;
12391f0ebf7SArnaldo Carvalho de Melo out_free_rx_slab:
12491f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
12591f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_rx_slab = NULL;
12691f0ebf7SArnaldo Carvalho de Melo goto out;
1277c657876SArnaldo Carvalho de Melo }
1287c657876SArnaldo Carvalho de Melo
ccid_deactivate(struct ccid_operations * ccid_ops)129ddebc973SGerrit Renker static void ccid_deactivate(struct ccid_operations *ccid_ops)
1307c657876SArnaldo Carvalho de Melo {
13191f0ebf7SArnaldo Carvalho de Melo ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab);
13291f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_tx_slab = NULL;
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
1361fd9d208SGerrit Renker pr_info("DCCP: Deactivated CCID %d (%s)\n",
13791f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_id, ccid_ops->ccid_name);
1387c657876SArnaldo Carvalho de Melo }
1397c657876SArnaldo Carvalho de Melo
ccid_new(const u8 id,struct sock * sk,bool rx)140e5fd56caSGerrit Renker struct ccid *ccid_new(const u8 id, struct sock *sk, bool rx)
1417c657876SArnaldo Carvalho de Melo {
142ddebc973SGerrit Renker struct ccid_operations *ccid_ops = ccid_by_number(id);
14391f0ebf7SArnaldo Carvalho de Melo struct ccid *ccid = NULL;
1447c657876SArnaldo Carvalho de Melo
14591f0ebf7SArnaldo Carvalho de Melo if (ccid_ops == NULL)
146ddebc973SGerrit Renker goto out;
14791f0ebf7SArnaldo Carvalho de Melo
14891f0ebf7SArnaldo Carvalho de Melo ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab :
149e5fd56caSGerrit Renker ccid_ops->ccid_hc_tx_slab, gfp_any());
15091f0ebf7SArnaldo Carvalho de Melo if (ccid == NULL)
151ddebc973SGerrit Renker goto out;
15291f0ebf7SArnaldo Carvalho de Melo ccid->ccid_ops = ccid_ops;
15391f0ebf7SArnaldo Carvalho de Melo if (rx) {
15491f0ebf7SArnaldo Carvalho de Melo memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size);
15591f0ebf7SArnaldo Carvalho de Melo if (ccid->ccid_ops->ccid_hc_rx_init != NULL &&
15691f0ebf7SArnaldo Carvalho de Melo ccid->ccid_ops->ccid_hc_rx_init(ccid, sk) != 0)
15791f0ebf7SArnaldo Carvalho de Melo goto out_free_ccid;
15891f0ebf7SArnaldo Carvalho de Melo } else {
15991f0ebf7SArnaldo Carvalho de Melo memset(ccid + 1, 0, ccid_ops->ccid_hc_tx_obj_size);
16091f0ebf7SArnaldo Carvalho de Melo if (ccid->ccid_ops->ccid_hc_tx_init != NULL &&
16191f0ebf7SArnaldo Carvalho de Melo ccid->ccid_ops->ccid_hc_tx_init(ccid, sk) != 0)
16291f0ebf7SArnaldo Carvalho de Melo goto out_free_ccid;
16391f0ebf7SArnaldo Carvalho de Melo }
16491f0ebf7SArnaldo Carvalho de Melo out:
1657c657876SArnaldo Carvalho de Melo return ccid;
16691f0ebf7SArnaldo Carvalho de Melo out_free_ccid:
16791f0ebf7SArnaldo Carvalho de Melo kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab :
16891f0ebf7SArnaldo Carvalho de Melo ccid_ops->ccid_hc_tx_slab, ccid);
1697c657876SArnaldo Carvalho de Melo ccid = NULL;
1707c657876SArnaldo Carvalho de Melo goto out;
1717c657876SArnaldo Carvalho de Melo }
1727c657876SArnaldo Carvalho de Melo
ccid_hc_rx_delete(struct ccid * ccid,struct sock * sk)17391f0ebf7SArnaldo Carvalho de Melo void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk)
17491f0ebf7SArnaldo Carvalho de Melo {
175e5fd56caSGerrit Renker if (ccid != NULL) {
176e5fd56caSGerrit Renker if (ccid->ccid_ops->ccid_hc_rx_exit != NULL)
177e5fd56caSGerrit Renker ccid->ccid_ops->ccid_hc_rx_exit(sk);
178e5fd56caSGerrit Renker kmem_cache_free(ccid->ccid_ops->ccid_hc_rx_slab, ccid);
17991f0ebf7SArnaldo Carvalho de Melo }
180e5fd56caSGerrit Renker }
18191f0ebf7SArnaldo Carvalho de Melo
ccid_hc_tx_delete(struct ccid * ccid,struct sock * sk)18291f0ebf7SArnaldo Carvalho de Melo void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk)
18391f0ebf7SArnaldo Carvalho de Melo {
184e5fd56caSGerrit Renker if (ccid != NULL) {
185e5fd56caSGerrit Renker if (ccid->ccid_ops->ccid_hc_tx_exit != NULL)
186e5fd56caSGerrit Renker ccid->ccid_ops->ccid_hc_tx_exit(sk);
187e5fd56caSGerrit Renker kmem_cache_free(ccid->ccid_ops->ccid_hc_tx_slab, ccid);
18891f0ebf7SArnaldo Carvalho de Melo }
189e5fd56caSGerrit Renker }
190ddebc973SGerrit Renker
ccid_initialize_builtins(void)191ddebc973SGerrit Renker int __init ccid_initialize_builtins(void)
192ddebc973SGerrit Renker {
193129fa447SGerrit Renker int i, err = tfrc_lib_init();
194129fa447SGerrit Renker
195129fa447SGerrit Renker if (err)
196129fa447SGerrit Renker return err;
197ddebc973SGerrit Renker
198ddebc973SGerrit Renker for (i = 0; i < ARRAY_SIZE(ccids); i++) {
199ddebc973SGerrit Renker err = ccid_activate(ccids[i]);
200ddebc973SGerrit Renker if (err)
201ddebc973SGerrit Renker goto unwind_registrations;
202ddebc973SGerrit Renker }
203ddebc973SGerrit Renker return 0;
204ddebc973SGerrit Renker
205ddebc973SGerrit Renker unwind_registrations:
206ddebc973SGerrit Renker while(--i >= 0)
207ddebc973SGerrit Renker ccid_deactivate(ccids[i]);
208129fa447SGerrit Renker tfrc_lib_exit();
209ddebc973SGerrit Renker return err;
210ddebc973SGerrit Renker }
211ddebc973SGerrit Renker
ccid_cleanup_builtins(void)212ddebc973SGerrit Renker void ccid_cleanup_builtins(void)
213ddebc973SGerrit Renker {
214ddebc973SGerrit Renker int i;
215ddebc973SGerrit Renker
216ddebc973SGerrit Renker for (i = 0; i < ARRAY_SIZE(ccids); i++)
217ddebc973SGerrit Renker ccid_deactivate(ccids[i]);
218129fa447SGerrit Renker tfrc_lib_exit();
219ddebc973SGerrit Renker }
220