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