18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  net/dccp/ccid.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  An implementation of the DCCP protocol
68c2ecf20Sopenharmony_ci *  Arnaldo Carvalho de Melo <acme@conectiva.com.br>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  CCID infrastructure
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "ccid.h"
148c2ecf20Sopenharmony_ci#include "ccids/lib/tfrc.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic struct ccid_operations *ccids[] = {
178c2ecf20Sopenharmony_ci	&ccid2_ops,
188c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_DCCP_CCID3
198c2ecf20Sopenharmony_ci	&ccid3_ops,
208c2ecf20Sopenharmony_ci#endif
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic struct ccid_operations *ccid_by_number(const u8 id)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	int i;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ccids); i++)
288c2ecf20Sopenharmony_ci		if (ccids[i]->ccid_id == id)
298c2ecf20Sopenharmony_ci			return ccids[i];
308c2ecf20Sopenharmony_ci	return NULL;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* check that up to @array_len members in @ccid_array are supported */
348c2ecf20Sopenharmony_cibool ccid_support_check(u8 const *ccid_array, u8 array_len)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	while (array_len > 0)
378c2ecf20Sopenharmony_ci		if (ccid_by_number(ccid_array[--array_len]) == NULL)
388c2ecf20Sopenharmony_ci			return false;
398c2ecf20Sopenharmony_ci	return true;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/**
438c2ecf20Sopenharmony_ci * ccid_get_builtin_ccids  -  Populate a list of built-in CCIDs
448c2ecf20Sopenharmony_ci * @ccid_array: pointer to copy into
458c2ecf20Sopenharmony_ci * @array_len: value to return length into
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * This function allocates memory - caller must see that it is freed after use.
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_ciint ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	*ccid_array = kmalloc(ARRAY_SIZE(ccids), gfp_any());
528c2ecf20Sopenharmony_ci	if (*ccid_array == NULL)
538c2ecf20Sopenharmony_ci		return -ENOBUFS;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	for (*array_len = 0; *array_len < ARRAY_SIZE(ccids); *array_len += 1)
568c2ecf20Sopenharmony_ci		(*ccid_array)[*array_len] = ccids[*array_len]->ccid_id;
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciint ccid_getsockopt_builtin_ccids(struct sock *sk, int len,
618c2ecf20Sopenharmony_ci				  char __user *optval, int __user *optlen)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	u8 *ccid_array, array_len;
648c2ecf20Sopenharmony_ci	int err = 0;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (ccid_get_builtin_ccids(&ccid_array, &array_len))
678c2ecf20Sopenharmony_ci		return -ENOBUFS;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (put_user(array_len, optlen))
708c2ecf20Sopenharmony_ci		err = -EFAULT;
718c2ecf20Sopenharmony_ci	else if (len > 0 && copy_to_user(optval, ccid_array,
728c2ecf20Sopenharmony_ci					 len > array_len ? array_len : len))
738c2ecf20Sopenharmony_ci		err = -EFAULT;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	kfree(ccid_array);
768c2ecf20Sopenharmony_ci	return err;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic struct kmem_cache *ccid_kmem_cache_create(int obj_size, char *slab_name_fmt, const char *fmt,...)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct kmem_cache *slab;
828c2ecf20Sopenharmony_ci	va_list args;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	va_start(args, fmt);
858c2ecf20Sopenharmony_ci	vsnprintf(slab_name_fmt, CCID_SLAB_NAME_LENGTH, fmt, args);
868c2ecf20Sopenharmony_ci	va_end(args);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	slab = kmem_cache_create(slab_name_fmt, sizeof(struct ccid) + obj_size, 0,
898c2ecf20Sopenharmony_ci				 SLAB_HWCACHE_ALIGN, NULL);
908c2ecf20Sopenharmony_ci	return slab;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void ccid_kmem_cache_destroy(struct kmem_cache *slab)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	kmem_cache_destroy(slab);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic int __init ccid_activate(struct ccid_operations *ccid_ops)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	int err = -ENOBUFS;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	ccid_ops->ccid_hc_rx_slab =
1038c2ecf20Sopenharmony_ci			ccid_kmem_cache_create(ccid_ops->ccid_hc_rx_obj_size,
1048c2ecf20Sopenharmony_ci					       ccid_ops->ccid_hc_rx_slab_name,
1058c2ecf20Sopenharmony_ci					       "ccid%u_hc_rx_sock",
1068c2ecf20Sopenharmony_ci					       ccid_ops->ccid_id);
1078c2ecf20Sopenharmony_ci	if (ccid_ops->ccid_hc_rx_slab == NULL)
1088c2ecf20Sopenharmony_ci		goto out;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	ccid_ops->ccid_hc_tx_slab =
1118c2ecf20Sopenharmony_ci			ccid_kmem_cache_create(ccid_ops->ccid_hc_tx_obj_size,
1128c2ecf20Sopenharmony_ci					       ccid_ops->ccid_hc_tx_slab_name,
1138c2ecf20Sopenharmony_ci					       "ccid%u_hc_tx_sock",
1148c2ecf20Sopenharmony_ci					       ccid_ops->ccid_id);
1158c2ecf20Sopenharmony_ci	if (ccid_ops->ccid_hc_tx_slab == NULL)
1168c2ecf20Sopenharmony_ci		goto out_free_rx_slab;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	pr_info("DCCP: Activated CCID %d (%s)\n",
1198c2ecf20Sopenharmony_ci		ccid_ops->ccid_id, ccid_ops->ccid_name);
1208c2ecf20Sopenharmony_ci	err = 0;
1218c2ecf20Sopenharmony_ciout:
1228c2ecf20Sopenharmony_ci	return err;
1238c2ecf20Sopenharmony_ciout_free_rx_slab:
1248c2ecf20Sopenharmony_ci	ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
1258c2ecf20Sopenharmony_ci	ccid_ops->ccid_hc_rx_slab = NULL;
1268c2ecf20Sopenharmony_ci	goto out;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic void ccid_deactivate(struct ccid_operations *ccid_ops)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab);
1328c2ecf20Sopenharmony_ci	ccid_ops->ccid_hc_tx_slab = NULL;
1338c2ecf20Sopenharmony_ci	ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
1348c2ecf20Sopenharmony_ci	ccid_ops->ccid_hc_rx_slab = NULL;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	pr_info("DCCP: Deactivated CCID %d (%s)\n",
1378c2ecf20Sopenharmony_ci		ccid_ops->ccid_id, ccid_ops->ccid_name);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistruct ccid *ccid_new(const u8 id, struct sock *sk, bool rx)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct ccid_operations *ccid_ops = ccid_by_number(id);
1438c2ecf20Sopenharmony_ci	struct ccid *ccid = NULL;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (ccid_ops == NULL)
1468c2ecf20Sopenharmony_ci		goto out;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab :
1498c2ecf20Sopenharmony_ci				     ccid_ops->ccid_hc_tx_slab, gfp_any());
1508c2ecf20Sopenharmony_ci	if (ccid == NULL)
1518c2ecf20Sopenharmony_ci		goto out;
1528c2ecf20Sopenharmony_ci	ccid->ccid_ops = ccid_ops;
1538c2ecf20Sopenharmony_ci	if (rx) {
1548c2ecf20Sopenharmony_ci		memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size);
1558c2ecf20Sopenharmony_ci		if (ccid->ccid_ops->ccid_hc_rx_init != NULL &&
1568c2ecf20Sopenharmony_ci		    ccid->ccid_ops->ccid_hc_rx_init(ccid, sk) != 0)
1578c2ecf20Sopenharmony_ci			goto out_free_ccid;
1588c2ecf20Sopenharmony_ci	} else {
1598c2ecf20Sopenharmony_ci		memset(ccid + 1, 0, ccid_ops->ccid_hc_tx_obj_size);
1608c2ecf20Sopenharmony_ci		if (ccid->ccid_ops->ccid_hc_tx_init != NULL &&
1618c2ecf20Sopenharmony_ci		    ccid->ccid_ops->ccid_hc_tx_init(ccid, sk) != 0)
1628c2ecf20Sopenharmony_ci			goto out_free_ccid;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ciout:
1658c2ecf20Sopenharmony_ci	return ccid;
1668c2ecf20Sopenharmony_ciout_free_ccid:
1678c2ecf20Sopenharmony_ci	kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab :
1688c2ecf20Sopenharmony_ci			ccid_ops->ccid_hc_tx_slab, ccid);
1698c2ecf20Sopenharmony_ci	ccid = NULL;
1708c2ecf20Sopenharmony_ci	goto out;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_civoid ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	if (ccid != NULL) {
1768c2ecf20Sopenharmony_ci		if (ccid->ccid_ops->ccid_hc_rx_exit != NULL)
1778c2ecf20Sopenharmony_ci			ccid->ccid_ops->ccid_hc_rx_exit(sk);
1788c2ecf20Sopenharmony_ci		kmem_cache_free(ccid->ccid_ops->ccid_hc_rx_slab, ccid);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_civoid ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	if (ccid != NULL) {
1858c2ecf20Sopenharmony_ci		if (ccid->ccid_ops->ccid_hc_tx_exit != NULL)
1868c2ecf20Sopenharmony_ci			ccid->ccid_ops->ccid_hc_tx_exit(sk);
1878c2ecf20Sopenharmony_ci		kmem_cache_free(ccid->ccid_ops->ccid_hc_tx_slab, ccid);
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ciint __init ccid_initialize_builtins(void)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	int i, err = tfrc_lib_init();
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (err)
1968c2ecf20Sopenharmony_ci		return err;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ccids); i++) {
1998c2ecf20Sopenharmony_ci		err = ccid_activate(ccids[i]);
2008c2ecf20Sopenharmony_ci		if (err)
2018c2ecf20Sopenharmony_ci			goto unwind_registrations;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci	return 0;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ciunwind_registrations:
2068c2ecf20Sopenharmony_ci	while(--i >= 0)
2078c2ecf20Sopenharmony_ci		ccid_deactivate(ccids[i]);
2088c2ecf20Sopenharmony_ci	tfrc_lib_exit();
2098c2ecf20Sopenharmony_ci	return err;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_civoid ccid_cleanup_builtins(void)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	int i;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ccids); i++)
2178c2ecf20Sopenharmony_ci		ccid_deactivate(ccids[i]);
2188c2ecf20Sopenharmony_ci	tfrc_lib_exit();
2198c2ecf20Sopenharmony_ci}
220