xref: /linux/net/dccp/ccid.c (revision cdd38c5f1ce4398ec58fec95904b75824daab7b5)
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