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