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