18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * NETLINK Generic Netlink Family 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Jamal Hadi Salim 68c2ecf20Sopenharmony_ci * Thomas Graf <tgraf@suug.ch> 78c2ecf20Sopenharmony_ci * Johannes Berg <johannes@sipsolutions.net> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <linux/socket.h> 168c2ecf20Sopenharmony_ci#include <linux/string.h> 178c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 188c2ecf20Sopenharmony_ci#include <linux/mutex.h> 198c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 208c2ecf20Sopenharmony_ci#include <linux/rwsem.h> 218c2ecf20Sopenharmony_ci#include <linux/idr.h> 228c2ecf20Sopenharmony_ci#include <net/sock.h> 238c2ecf20Sopenharmony_ci#include <net/genetlink.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(genl_mutex); /* serialization of message processing */ 268c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(cb_lock); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ciatomic_t genl_sk_destructing_cnt = ATOMIC_INIT(0); 298c2ecf20Sopenharmony_ciDECLARE_WAIT_QUEUE_HEAD(genl_sk_destructing_waitq); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_civoid genl_lock(void) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci mutex_lock(&genl_mutex); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(genl_lock); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_civoid genl_unlock(void) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci mutex_unlock(&genl_mutex); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(genl_unlock); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#ifdef CONFIG_LOCKDEP 448c2ecf20Sopenharmony_cibool lockdep_genl_is_held(void) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return lockdep_is_held(&genl_mutex); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lockdep_genl_is_held); 498c2ecf20Sopenharmony_ci#endif 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void genl_lock_all(void) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci down_write(&cb_lock); 548c2ecf20Sopenharmony_ci genl_lock(); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void genl_unlock_all(void) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci genl_unlock(); 608c2ecf20Sopenharmony_ci up_write(&cb_lock); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic DEFINE_IDR(genl_fam_idr); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * Bitmap of multicast groups that are currently in use. 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci * To avoid an allocation at boot of just one unsigned long, 698c2ecf20Sopenharmony_ci * declare it global instead. 708c2ecf20Sopenharmony_ci * Bit 0 is marked as already used since group 0 is invalid. 718c2ecf20Sopenharmony_ci * Bit 1 is marked as already used since the drop-monitor code 728c2ecf20Sopenharmony_ci * abuses the API and thinks it can statically use group 1. 738c2ecf20Sopenharmony_ci * That group will typically conflict with other groups that 748c2ecf20Sopenharmony_ci * any proper users use. 758c2ecf20Sopenharmony_ci * Bit 16 is marked as used since it's used for generic netlink 768c2ecf20Sopenharmony_ci * and the code no longer marks pre-reserved IDs as used. 778c2ecf20Sopenharmony_ci * Bit 17 is marked as already used since the VFS quota code 788c2ecf20Sopenharmony_ci * also abused this API and relied on family == group ID, we 798c2ecf20Sopenharmony_ci * cater to that by giving it a static family and group ID. 808c2ecf20Sopenharmony_ci * Bit 18 is marked as already used since the PMCRAID driver 818c2ecf20Sopenharmony_ci * did the same thing as the VFS quota code (maybe copied?) 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_cistatic unsigned long mc_group_start = 0x3 | BIT(GENL_ID_CTRL) | 848c2ecf20Sopenharmony_ci BIT(GENL_ID_VFS_DQUOT) | 858c2ecf20Sopenharmony_ci BIT(GENL_ID_PMCRAID); 868c2ecf20Sopenharmony_cistatic unsigned long *mc_groups = &mc_group_start; 878c2ecf20Sopenharmony_cistatic unsigned long mc_groups_longs = 1; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int genl_ctrl_event(int event, const struct genl_family *family, 908c2ecf20Sopenharmony_ci const struct genl_multicast_group *grp, 918c2ecf20Sopenharmony_ci int grp_id); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic const struct genl_family *genl_family_find_byid(unsigned int id) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci return idr_find(&genl_fam_idr, id); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic const struct genl_family *genl_family_find_byname(char *name) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci const struct genl_family *family; 1018c2ecf20Sopenharmony_ci unsigned int id; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci idr_for_each_entry(&genl_fam_idr, family, id) 1048c2ecf20Sopenharmony_ci if (strcmp(family->name, name) == 0) 1058c2ecf20Sopenharmony_ci return family; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return NULL; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int genl_get_cmd_cnt(const struct genl_family *family) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return family->n_ops + family->n_small_ops; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void genl_op_from_full(const struct genl_family *family, 1168c2ecf20Sopenharmony_ci unsigned int i, struct genl_ops *op) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci *op = family->ops[i]; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (!op->maxattr) 1218c2ecf20Sopenharmony_ci op->maxattr = family->maxattr; 1228c2ecf20Sopenharmony_ci if (!op->policy) 1238c2ecf20Sopenharmony_ci op->policy = family->policy; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int genl_get_cmd_full(u32 cmd, const struct genl_family *family, 1278c2ecf20Sopenharmony_ci struct genl_ops *op) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci int i; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci for (i = 0; i < family->n_ops; i++) 1328c2ecf20Sopenharmony_ci if (family->ops[i].cmd == cmd) { 1338c2ecf20Sopenharmony_ci genl_op_from_full(family, i, op); 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return -ENOENT; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void genl_op_from_small(const struct genl_family *family, 1418c2ecf20Sopenharmony_ci unsigned int i, struct genl_ops *op) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci memset(op, 0, sizeof(*op)); 1448c2ecf20Sopenharmony_ci op->doit = family->small_ops[i].doit; 1458c2ecf20Sopenharmony_ci op->dumpit = family->small_ops[i].dumpit; 1468c2ecf20Sopenharmony_ci op->cmd = family->small_ops[i].cmd; 1478c2ecf20Sopenharmony_ci op->internal_flags = family->small_ops[i].internal_flags; 1488c2ecf20Sopenharmony_ci op->flags = family->small_ops[i].flags; 1498c2ecf20Sopenharmony_ci op->validate = family->small_ops[i].validate; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci op->maxattr = family->maxattr; 1528c2ecf20Sopenharmony_ci op->policy = family->policy; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int genl_get_cmd_small(u32 cmd, const struct genl_family *family, 1568c2ecf20Sopenharmony_ci struct genl_ops *op) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci int i; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci for (i = 0; i < family->n_small_ops; i++) 1618c2ecf20Sopenharmony_ci if (family->small_ops[i].cmd == cmd) { 1628c2ecf20Sopenharmony_ci genl_op_from_small(family, i, op); 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return -ENOENT; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int genl_get_cmd(u32 cmd, const struct genl_family *family, 1708c2ecf20Sopenharmony_ci struct genl_ops *op) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci if (!genl_get_cmd_full(cmd, family, op)) 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci return genl_get_cmd_small(cmd, family, op); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void genl_get_cmd_by_index(unsigned int i, 1788c2ecf20Sopenharmony_ci const struct genl_family *family, 1798c2ecf20Sopenharmony_ci struct genl_ops *op) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci if (i < family->n_ops) 1828c2ecf20Sopenharmony_ci genl_op_from_full(family, i, op); 1838c2ecf20Sopenharmony_ci else if (i < family->n_ops + family->n_small_ops) 1848c2ecf20Sopenharmony_ci genl_op_from_small(family, i - family->n_ops, op); 1858c2ecf20Sopenharmony_ci else 1868c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int genl_allocate_reserve_groups(int n_groups, int *first_id) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci unsigned long *new_groups; 1928c2ecf20Sopenharmony_ci int start = 0; 1938c2ecf20Sopenharmony_ci int i; 1948c2ecf20Sopenharmony_ci int id; 1958c2ecf20Sopenharmony_ci bool fits; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci do { 1988c2ecf20Sopenharmony_ci if (start == 0) 1998c2ecf20Sopenharmony_ci id = find_first_zero_bit(mc_groups, 2008c2ecf20Sopenharmony_ci mc_groups_longs * 2018c2ecf20Sopenharmony_ci BITS_PER_LONG); 2028c2ecf20Sopenharmony_ci else 2038c2ecf20Sopenharmony_ci id = find_next_zero_bit(mc_groups, 2048c2ecf20Sopenharmony_ci mc_groups_longs * BITS_PER_LONG, 2058c2ecf20Sopenharmony_ci start); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci fits = true; 2088c2ecf20Sopenharmony_ci for (i = id; 2098c2ecf20Sopenharmony_ci i < min_t(int, id + n_groups, 2108c2ecf20Sopenharmony_ci mc_groups_longs * BITS_PER_LONG); 2118c2ecf20Sopenharmony_ci i++) { 2128c2ecf20Sopenharmony_ci if (test_bit(i, mc_groups)) { 2138c2ecf20Sopenharmony_ci start = i; 2148c2ecf20Sopenharmony_ci fits = false; 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (id + n_groups > mc_groups_longs * BITS_PER_LONG) { 2208c2ecf20Sopenharmony_ci unsigned long new_longs = mc_groups_longs + 2218c2ecf20Sopenharmony_ci BITS_TO_LONGS(n_groups); 2228c2ecf20Sopenharmony_ci size_t nlen = new_longs * sizeof(unsigned long); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (mc_groups == &mc_group_start) { 2258c2ecf20Sopenharmony_ci new_groups = kzalloc(nlen, GFP_KERNEL); 2268c2ecf20Sopenharmony_ci if (!new_groups) 2278c2ecf20Sopenharmony_ci return -ENOMEM; 2288c2ecf20Sopenharmony_ci mc_groups = new_groups; 2298c2ecf20Sopenharmony_ci *mc_groups = mc_group_start; 2308c2ecf20Sopenharmony_ci } else { 2318c2ecf20Sopenharmony_ci new_groups = krealloc(mc_groups, nlen, 2328c2ecf20Sopenharmony_ci GFP_KERNEL); 2338c2ecf20Sopenharmony_ci if (!new_groups) 2348c2ecf20Sopenharmony_ci return -ENOMEM; 2358c2ecf20Sopenharmony_ci mc_groups = new_groups; 2368c2ecf20Sopenharmony_ci for (i = 0; i < BITS_TO_LONGS(n_groups); i++) 2378c2ecf20Sopenharmony_ci mc_groups[mc_groups_longs + i] = 0; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci mc_groups_longs = new_longs; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } while (!fits); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci for (i = id; i < id + n_groups; i++) 2448c2ecf20Sopenharmony_ci set_bit(i, mc_groups); 2458c2ecf20Sopenharmony_ci *first_id = id; 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic struct genl_family genl_ctrl; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int genl_validate_assign_mc_groups(struct genl_family *family) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci int first_id; 2548c2ecf20Sopenharmony_ci int n_groups = family->n_mcgrps; 2558c2ecf20Sopenharmony_ci int err = 0, i; 2568c2ecf20Sopenharmony_ci bool groups_allocated = false; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (!n_groups) 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci for (i = 0; i < n_groups; i++) { 2628c2ecf20Sopenharmony_ci const struct genl_multicast_group *grp = &family->mcgrps[i]; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (WARN_ON(grp->name[0] == '\0')) 2658c2ecf20Sopenharmony_ci return -EINVAL; 2668c2ecf20Sopenharmony_ci if (WARN_ON(memchr(grp->name, '\0', GENL_NAMSIZ) == NULL)) 2678c2ecf20Sopenharmony_ci return -EINVAL; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* special-case our own group and hacks */ 2718c2ecf20Sopenharmony_ci if (family == &genl_ctrl) { 2728c2ecf20Sopenharmony_ci first_id = GENL_ID_CTRL; 2738c2ecf20Sopenharmony_ci BUG_ON(n_groups != 1); 2748c2ecf20Sopenharmony_ci } else if (strcmp(family->name, "NET_DM") == 0) { 2758c2ecf20Sopenharmony_ci first_id = 1; 2768c2ecf20Sopenharmony_ci BUG_ON(n_groups != 1); 2778c2ecf20Sopenharmony_ci } else if (family->id == GENL_ID_VFS_DQUOT) { 2788c2ecf20Sopenharmony_ci first_id = GENL_ID_VFS_DQUOT; 2798c2ecf20Sopenharmony_ci BUG_ON(n_groups != 1); 2808c2ecf20Sopenharmony_ci } else if (family->id == GENL_ID_PMCRAID) { 2818c2ecf20Sopenharmony_ci first_id = GENL_ID_PMCRAID; 2828c2ecf20Sopenharmony_ci BUG_ON(n_groups != 1); 2838c2ecf20Sopenharmony_ci } else { 2848c2ecf20Sopenharmony_ci groups_allocated = true; 2858c2ecf20Sopenharmony_ci err = genl_allocate_reserve_groups(n_groups, &first_id); 2868c2ecf20Sopenharmony_ci if (err) 2878c2ecf20Sopenharmony_ci return err; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci family->mcgrp_offset = first_id; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* if still initializing, can't and don't need to realloc bitmaps */ 2938c2ecf20Sopenharmony_ci if (!init_net.genl_sock) 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (family->netnsok) { 2978c2ecf20Sopenharmony_ci struct net *net; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci netlink_table_grab(); 3008c2ecf20Sopenharmony_ci rcu_read_lock(); 3018c2ecf20Sopenharmony_ci for_each_net_rcu(net) { 3028c2ecf20Sopenharmony_ci err = __netlink_change_ngroups(net->genl_sock, 3038c2ecf20Sopenharmony_ci mc_groups_longs * BITS_PER_LONG); 3048c2ecf20Sopenharmony_ci if (err) { 3058c2ecf20Sopenharmony_ci /* 3068c2ecf20Sopenharmony_ci * No need to roll back, can only fail if 3078c2ecf20Sopenharmony_ci * memory allocation fails and then the 3088c2ecf20Sopenharmony_ci * number of _possible_ groups has been 3098c2ecf20Sopenharmony_ci * increased on some sockets which is ok. 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_ci break; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci rcu_read_unlock(); 3158c2ecf20Sopenharmony_ci netlink_table_ungrab(); 3168c2ecf20Sopenharmony_ci } else { 3178c2ecf20Sopenharmony_ci err = netlink_change_ngroups(init_net.genl_sock, 3188c2ecf20Sopenharmony_ci mc_groups_longs * BITS_PER_LONG); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (groups_allocated && err) { 3228c2ecf20Sopenharmony_ci for (i = 0; i < family->n_mcgrps; i++) 3238c2ecf20Sopenharmony_ci clear_bit(family->mcgrp_offset + i, mc_groups); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return err; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void genl_unregister_mc_groups(const struct genl_family *family) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct net *net; 3328c2ecf20Sopenharmony_ci int i; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci netlink_table_grab(); 3358c2ecf20Sopenharmony_ci rcu_read_lock(); 3368c2ecf20Sopenharmony_ci for_each_net_rcu(net) { 3378c2ecf20Sopenharmony_ci for (i = 0; i < family->n_mcgrps; i++) 3388c2ecf20Sopenharmony_ci __netlink_clear_multicast_users( 3398c2ecf20Sopenharmony_ci net->genl_sock, family->mcgrp_offset + i); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci rcu_read_unlock(); 3428c2ecf20Sopenharmony_ci netlink_table_ungrab(); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci for (i = 0; i < family->n_mcgrps; i++) { 3458c2ecf20Sopenharmony_ci int grp_id = family->mcgrp_offset + i; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (grp_id != 1) 3488c2ecf20Sopenharmony_ci clear_bit(grp_id, mc_groups); 3498c2ecf20Sopenharmony_ci genl_ctrl_event(CTRL_CMD_DELMCAST_GRP, family, 3508c2ecf20Sopenharmony_ci &family->mcgrps[i], grp_id); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int genl_validate_ops(const struct genl_family *family) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci int i, j; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (WARN_ON(family->n_ops && !family->ops) || 3598c2ecf20Sopenharmony_ci WARN_ON(family->n_small_ops && !family->small_ops)) 3608c2ecf20Sopenharmony_ci return -EINVAL; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci for (i = 0; i < genl_get_cmd_cnt(family); i++) { 3638c2ecf20Sopenharmony_ci struct genl_ops op; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci genl_get_cmd_by_index(i, family, &op); 3668c2ecf20Sopenharmony_ci if (op.dumpit == NULL && op.doit == NULL) 3678c2ecf20Sopenharmony_ci return -EINVAL; 3688c2ecf20Sopenharmony_ci for (j = i + 1; j < genl_get_cmd_cnt(family); j++) { 3698c2ecf20Sopenharmony_ci struct genl_ops op2; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci genl_get_cmd_by_index(j, family, &op2); 3728c2ecf20Sopenharmony_ci if (op.cmd == op2.cmd) 3738c2ecf20Sopenharmony_ci return -EINVAL; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci/** 3818c2ecf20Sopenharmony_ci * genl_register_family - register a generic netlink family 3828c2ecf20Sopenharmony_ci * @family: generic netlink family 3838c2ecf20Sopenharmony_ci * 3848c2ecf20Sopenharmony_ci * Registers the specified family after validating it first. Only one 3858c2ecf20Sopenharmony_ci * family may be registered with the same family name or identifier. 3868c2ecf20Sopenharmony_ci * 3878c2ecf20Sopenharmony_ci * The family's ops, multicast groups and module pointer must already 3888c2ecf20Sopenharmony_ci * be assigned. 3898c2ecf20Sopenharmony_ci * 3908c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code. 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ciint genl_register_family(struct genl_family *family) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci int err, i; 3958c2ecf20Sopenharmony_ci int start = GENL_START_ALLOC, end = GENL_MAX_ID; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci err = genl_validate_ops(family); 3988c2ecf20Sopenharmony_ci if (err) 3998c2ecf20Sopenharmony_ci return err; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci genl_lock_all(); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (genl_family_find_byname(family->name)) { 4048c2ecf20Sopenharmony_ci err = -EEXIST; 4058c2ecf20Sopenharmony_ci goto errout_locked; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* 4098c2ecf20Sopenharmony_ci * Sadly, a few cases need to be special-cased 4108c2ecf20Sopenharmony_ci * due to them having previously abused the API 4118c2ecf20Sopenharmony_ci * and having used their family ID also as their 4128c2ecf20Sopenharmony_ci * multicast group ID, so we use reserved IDs 4138c2ecf20Sopenharmony_ci * for both to be sure we can do that mapping. 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ci if (family == &genl_ctrl) { 4168c2ecf20Sopenharmony_ci /* and this needs to be special for initial family lookups */ 4178c2ecf20Sopenharmony_ci start = end = GENL_ID_CTRL; 4188c2ecf20Sopenharmony_ci } else if (strcmp(family->name, "pmcraid") == 0) { 4198c2ecf20Sopenharmony_ci start = end = GENL_ID_PMCRAID; 4208c2ecf20Sopenharmony_ci } else if (strcmp(family->name, "VFS_DQUOT") == 0) { 4218c2ecf20Sopenharmony_ci start = end = GENL_ID_VFS_DQUOT; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci family->id = idr_alloc_cyclic(&genl_fam_idr, family, 4258c2ecf20Sopenharmony_ci start, end + 1, GFP_KERNEL); 4268c2ecf20Sopenharmony_ci if (family->id < 0) { 4278c2ecf20Sopenharmony_ci err = family->id; 4288c2ecf20Sopenharmony_ci goto errout_locked; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci err = genl_validate_assign_mc_groups(family); 4328c2ecf20Sopenharmony_ci if (err) 4338c2ecf20Sopenharmony_ci goto errout_remove; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci genl_unlock_all(); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* send all events */ 4388c2ecf20Sopenharmony_ci genl_ctrl_event(CTRL_CMD_NEWFAMILY, family, NULL, 0); 4398c2ecf20Sopenharmony_ci for (i = 0; i < family->n_mcgrps; i++) 4408c2ecf20Sopenharmony_ci genl_ctrl_event(CTRL_CMD_NEWMCAST_GRP, family, 4418c2ecf20Sopenharmony_ci &family->mcgrps[i], family->mcgrp_offset + i); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return 0; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cierrout_remove: 4468c2ecf20Sopenharmony_ci idr_remove(&genl_fam_idr, family->id); 4478c2ecf20Sopenharmony_cierrout_locked: 4488c2ecf20Sopenharmony_ci genl_unlock_all(); 4498c2ecf20Sopenharmony_ci return err; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(genl_register_family); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci/** 4548c2ecf20Sopenharmony_ci * genl_unregister_family - unregister generic netlink family 4558c2ecf20Sopenharmony_ci * @family: generic netlink family 4568c2ecf20Sopenharmony_ci * 4578c2ecf20Sopenharmony_ci * Unregisters the specified family. 4588c2ecf20Sopenharmony_ci * 4598c2ecf20Sopenharmony_ci * Returns 0 on success or a negative error code. 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_ciint genl_unregister_family(const struct genl_family *family) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci genl_lock_all(); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (!genl_family_find_byid(family->id)) { 4668c2ecf20Sopenharmony_ci genl_unlock_all(); 4678c2ecf20Sopenharmony_ci return -ENOENT; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci genl_unregister_mc_groups(family); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci idr_remove(&genl_fam_idr, family->id); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci up_write(&cb_lock); 4758c2ecf20Sopenharmony_ci wait_event(genl_sk_destructing_waitq, 4768c2ecf20Sopenharmony_ci atomic_read(&genl_sk_destructing_cnt) == 0); 4778c2ecf20Sopenharmony_ci genl_unlock(); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci genl_ctrl_event(CTRL_CMD_DELFAMILY, family, NULL, 0); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return 0; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(genl_unregister_family); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci/** 4868c2ecf20Sopenharmony_ci * genlmsg_put - Add generic netlink header to netlink message 4878c2ecf20Sopenharmony_ci * @skb: socket buffer holding the message 4888c2ecf20Sopenharmony_ci * @portid: netlink portid the message is addressed to 4898c2ecf20Sopenharmony_ci * @seq: sequence number (usually the one of the sender) 4908c2ecf20Sopenharmony_ci * @family: generic netlink family 4918c2ecf20Sopenharmony_ci * @flags: netlink message flags 4928c2ecf20Sopenharmony_ci * @cmd: generic netlink command 4938c2ecf20Sopenharmony_ci * 4948c2ecf20Sopenharmony_ci * Returns pointer to user specific header 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_civoid *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, 4978c2ecf20Sopenharmony_ci const struct genl_family *family, int flags, u8 cmd) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 5008c2ecf20Sopenharmony_ci struct genlmsghdr *hdr; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, family->id, GENL_HDRLEN + 5038c2ecf20Sopenharmony_ci family->hdrsize, flags); 5048c2ecf20Sopenharmony_ci if (nlh == NULL) 5058c2ecf20Sopenharmony_ci return NULL; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci hdr = nlmsg_data(nlh); 5088c2ecf20Sopenharmony_ci hdr->cmd = cmd; 5098c2ecf20Sopenharmony_ci hdr->version = family->version; 5108c2ecf20Sopenharmony_ci hdr->reserved = 0; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return (char *) hdr + GENL_HDRLEN; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(genlmsg_put); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic struct genl_dumpit_info *genl_dumpit_info_alloc(void) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci return kmalloc(sizeof(struct genl_dumpit_info), GFP_KERNEL); 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic void genl_dumpit_info_free(const struct genl_dumpit_info *info) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci kfree(info); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic struct nlattr ** 5278c2ecf20Sopenharmony_cigenl_family_rcv_msg_attrs_parse(const struct genl_family *family, 5288c2ecf20Sopenharmony_ci struct nlmsghdr *nlh, 5298c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack, 5308c2ecf20Sopenharmony_ci const struct genl_ops *ops, 5318c2ecf20Sopenharmony_ci int hdrlen, 5328c2ecf20Sopenharmony_ci enum genl_validate_flags no_strict_flag) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci enum netlink_validation validate = ops->validate & no_strict_flag ? 5358c2ecf20Sopenharmony_ci NL_VALIDATE_LIBERAL : 5368c2ecf20Sopenharmony_ci NL_VALIDATE_STRICT; 5378c2ecf20Sopenharmony_ci struct nlattr **attrbuf; 5388c2ecf20Sopenharmony_ci int err; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (!ops->maxattr) 5418c2ecf20Sopenharmony_ci return NULL; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci attrbuf = kmalloc_array(ops->maxattr + 1, 5448c2ecf20Sopenharmony_ci sizeof(struct nlattr *), GFP_KERNEL); 5458c2ecf20Sopenharmony_ci if (!attrbuf) 5468c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci err = __nlmsg_parse(nlh, hdrlen, attrbuf, ops->maxattr, ops->policy, 5498c2ecf20Sopenharmony_ci validate, extack); 5508c2ecf20Sopenharmony_ci if (err) { 5518c2ecf20Sopenharmony_ci kfree(attrbuf); 5528c2ecf20Sopenharmony_ci return ERR_PTR(err); 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci return attrbuf; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic void genl_family_rcv_msg_attrs_free(struct nlattr **attrbuf) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci kfree(attrbuf); 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistruct genl_start_context { 5638c2ecf20Sopenharmony_ci const struct genl_family *family; 5648c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 5658c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack; 5668c2ecf20Sopenharmony_ci const struct genl_ops *ops; 5678c2ecf20Sopenharmony_ci int hdrlen; 5688c2ecf20Sopenharmony_ci}; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic int genl_start(struct netlink_callback *cb) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci struct genl_start_context *ctx = cb->data; 5738c2ecf20Sopenharmony_ci const struct genl_ops *ops = ctx->ops; 5748c2ecf20Sopenharmony_ci struct genl_dumpit_info *info; 5758c2ecf20Sopenharmony_ci struct nlattr **attrs = NULL; 5768c2ecf20Sopenharmony_ci int rc = 0; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (ops->validate & GENL_DONT_VALIDATE_DUMP) 5798c2ecf20Sopenharmony_ci goto no_attrs; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (ctx->nlh->nlmsg_len < nlmsg_msg_size(ctx->hdrlen)) 5828c2ecf20Sopenharmony_ci return -EINVAL; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci attrs = genl_family_rcv_msg_attrs_parse(ctx->family, ctx->nlh, ctx->extack, 5858c2ecf20Sopenharmony_ci ops, ctx->hdrlen, 5868c2ecf20Sopenharmony_ci GENL_DONT_VALIDATE_DUMP_STRICT); 5878c2ecf20Sopenharmony_ci if (IS_ERR(attrs)) 5888c2ecf20Sopenharmony_ci return PTR_ERR(attrs); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cino_attrs: 5918c2ecf20Sopenharmony_ci info = genl_dumpit_info_alloc(); 5928c2ecf20Sopenharmony_ci if (!info) { 5938c2ecf20Sopenharmony_ci genl_family_rcv_msg_attrs_free(attrs); 5948c2ecf20Sopenharmony_ci return -ENOMEM; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci info->family = ctx->family; 5978c2ecf20Sopenharmony_ci info->op = *ops; 5988c2ecf20Sopenharmony_ci info->attrs = attrs; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci cb->data = info; 6018c2ecf20Sopenharmony_ci if (ops->start) { 6028c2ecf20Sopenharmony_ci if (!ctx->family->parallel_ops) 6038c2ecf20Sopenharmony_ci genl_lock(); 6048c2ecf20Sopenharmony_ci rc = ops->start(cb); 6058c2ecf20Sopenharmony_ci if (!ctx->family->parallel_ops) 6068c2ecf20Sopenharmony_ci genl_unlock(); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (rc) { 6108c2ecf20Sopenharmony_ci genl_family_rcv_msg_attrs_free(info->attrs); 6118c2ecf20Sopenharmony_ci genl_dumpit_info_free(info); 6128c2ecf20Sopenharmony_ci cb->data = NULL; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci return rc; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci const struct genl_ops *ops = &genl_dumpit_info(cb)->op; 6208c2ecf20Sopenharmony_ci int rc; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci genl_lock(); 6238c2ecf20Sopenharmony_ci rc = ops->dumpit(skb, cb); 6248c2ecf20Sopenharmony_ci genl_unlock(); 6258c2ecf20Sopenharmony_ci return rc; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic int genl_lock_done(struct netlink_callback *cb) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci const struct genl_dumpit_info *info = genl_dumpit_info(cb); 6318c2ecf20Sopenharmony_ci const struct genl_ops *ops = &info->op; 6328c2ecf20Sopenharmony_ci int rc = 0; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (ops->done) { 6358c2ecf20Sopenharmony_ci genl_lock(); 6368c2ecf20Sopenharmony_ci rc = ops->done(cb); 6378c2ecf20Sopenharmony_ci genl_unlock(); 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci genl_family_rcv_msg_attrs_free(info->attrs); 6408c2ecf20Sopenharmony_ci genl_dumpit_info_free(info); 6418c2ecf20Sopenharmony_ci return rc; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic int genl_parallel_done(struct netlink_callback *cb) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci const struct genl_dumpit_info *info = genl_dumpit_info(cb); 6478c2ecf20Sopenharmony_ci const struct genl_ops *ops = &info->op; 6488c2ecf20Sopenharmony_ci int rc = 0; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (ops->done) 6518c2ecf20Sopenharmony_ci rc = ops->done(cb); 6528c2ecf20Sopenharmony_ci genl_family_rcv_msg_attrs_free(info->attrs); 6538c2ecf20Sopenharmony_ci genl_dumpit_info_free(info); 6548c2ecf20Sopenharmony_ci return rc; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic int genl_family_rcv_msg_dumpit(const struct genl_family *family, 6588c2ecf20Sopenharmony_ci struct sk_buff *skb, 6598c2ecf20Sopenharmony_ci struct nlmsghdr *nlh, 6608c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack, 6618c2ecf20Sopenharmony_ci const struct genl_ops *ops, 6628c2ecf20Sopenharmony_ci int hdrlen, struct net *net) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci struct genl_start_context ctx; 6658c2ecf20Sopenharmony_ci int err; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (!ops->dumpit) 6688c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci ctx.family = family; 6718c2ecf20Sopenharmony_ci ctx.nlh = nlh; 6728c2ecf20Sopenharmony_ci ctx.extack = extack; 6738c2ecf20Sopenharmony_ci ctx.ops = ops; 6748c2ecf20Sopenharmony_ci ctx.hdrlen = hdrlen; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (!family->parallel_ops) { 6778c2ecf20Sopenharmony_ci struct netlink_dump_control c = { 6788c2ecf20Sopenharmony_ci .module = family->module, 6798c2ecf20Sopenharmony_ci .data = &ctx, 6808c2ecf20Sopenharmony_ci .start = genl_start, 6818c2ecf20Sopenharmony_ci .dump = genl_lock_dumpit, 6828c2ecf20Sopenharmony_ci .done = genl_lock_done, 6838c2ecf20Sopenharmony_ci }; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci genl_unlock(); 6868c2ecf20Sopenharmony_ci err = __netlink_dump_start(net->genl_sock, skb, nlh, &c); 6878c2ecf20Sopenharmony_ci genl_lock(); 6888c2ecf20Sopenharmony_ci } else { 6898c2ecf20Sopenharmony_ci struct netlink_dump_control c = { 6908c2ecf20Sopenharmony_ci .module = family->module, 6918c2ecf20Sopenharmony_ci .data = &ctx, 6928c2ecf20Sopenharmony_ci .start = genl_start, 6938c2ecf20Sopenharmony_ci .dump = ops->dumpit, 6948c2ecf20Sopenharmony_ci .done = genl_parallel_done, 6958c2ecf20Sopenharmony_ci }; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci err = __netlink_dump_start(net->genl_sock, skb, nlh, &c); 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci return err; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic int genl_family_rcv_msg_doit(const struct genl_family *family, 7048c2ecf20Sopenharmony_ci struct sk_buff *skb, 7058c2ecf20Sopenharmony_ci struct nlmsghdr *nlh, 7068c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack, 7078c2ecf20Sopenharmony_ci const struct genl_ops *ops, 7088c2ecf20Sopenharmony_ci int hdrlen, struct net *net) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct nlattr **attrbuf; 7118c2ecf20Sopenharmony_ci struct genl_info info; 7128c2ecf20Sopenharmony_ci int err; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (!ops->doit) 7158c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci attrbuf = genl_family_rcv_msg_attrs_parse(family, nlh, extack, 7188c2ecf20Sopenharmony_ci ops, hdrlen, 7198c2ecf20Sopenharmony_ci GENL_DONT_VALIDATE_STRICT); 7208c2ecf20Sopenharmony_ci if (IS_ERR(attrbuf)) 7218c2ecf20Sopenharmony_ci return PTR_ERR(attrbuf); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci info.snd_seq = nlh->nlmsg_seq; 7248c2ecf20Sopenharmony_ci info.snd_portid = NETLINK_CB(skb).portid; 7258c2ecf20Sopenharmony_ci info.nlhdr = nlh; 7268c2ecf20Sopenharmony_ci info.genlhdr = nlmsg_data(nlh); 7278c2ecf20Sopenharmony_ci info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; 7288c2ecf20Sopenharmony_ci info.attrs = attrbuf; 7298c2ecf20Sopenharmony_ci info.extack = extack; 7308c2ecf20Sopenharmony_ci genl_info_net_set(&info, net); 7318c2ecf20Sopenharmony_ci memset(&info.user_ptr, 0, sizeof(info.user_ptr)); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (family->pre_doit) { 7348c2ecf20Sopenharmony_ci err = family->pre_doit(ops, skb, &info); 7358c2ecf20Sopenharmony_ci if (err) 7368c2ecf20Sopenharmony_ci goto out; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci err = ops->doit(skb, &info); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (family->post_doit) 7428c2ecf20Sopenharmony_ci family->post_doit(ops, skb, &info); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ciout: 7458c2ecf20Sopenharmony_ci genl_family_rcv_msg_attrs_free(attrbuf); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci return err; 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic int genl_family_rcv_msg(const struct genl_family *family, 7518c2ecf20Sopenharmony_ci struct sk_buff *skb, 7528c2ecf20Sopenharmony_ci struct nlmsghdr *nlh, 7538c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 7568c2ecf20Sopenharmony_ci struct genlmsghdr *hdr = nlmsg_data(nlh); 7578c2ecf20Sopenharmony_ci struct genl_ops op; 7588c2ecf20Sopenharmony_ci int hdrlen; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /* this family doesn't exist in this netns */ 7618c2ecf20Sopenharmony_ci if (!family->netnsok && !net_eq(net, &init_net)) 7628c2ecf20Sopenharmony_ci return -ENOENT; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci hdrlen = GENL_HDRLEN + family->hdrsize; 7658c2ecf20Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) 7668c2ecf20Sopenharmony_ci return -EINVAL; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (genl_get_cmd(hdr->cmd, family, &op)) 7698c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci if ((op.flags & GENL_ADMIN_PERM) && 7728c2ecf20Sopenharmony_ci !netlink_capable(skb, CAP_NET_ADMIN)) 7738c2ecf20Sopenharmony_ci return -EPERM; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if ((op.flags & GENL_UNS_ADMIN_PERM) && 7768c2ecf20Sopenharmony_ci !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) 7778c2ecf20Sopenharmony_ci return -EPERM; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) 7808c2ecf20Sopenharmony_ci return genl_family_rcv_msg_dumpit(family, skb, nlh, extack, 7818c2ecf20Sopenharmony_ci &op, hdrlen, net); 7828c2ecf20Sopenharmony_ci else 7838c2ecf20Sopenharmony_ci return genl_family_rcv_msg_doit(family, skb, nlh, extack, 7848c2ecf20Sopenharmony_ci &op, hdrlen, net); 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, 7888c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci const struct genl_family *family; 7918c2ecf20Sopenharmony_ci int err; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci family = genl_family_find_byid(nlh->nlmsg_type); 7948c2ecf20Sopenharmony_ci if (family == NULL) 7958c2ecf20Sopenharmony_ci return -ENOENT; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (!family->parallel_ops) 7988c2ecf20Sopenharmony_ci genl_lock(); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci err = genl_family_rcv_msg(family, skb, nlh, extack); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (!family->parallel_ops) 8038c2ecf20Sopenharmony_ci genl_unlock(); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci return err; 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_cistatic void genl_rcv(struct sk_buff *skb) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci down_read(&cb_lock); 8118c2ecf20Sopenharmony_ci netlink_rcv_skb(skb, &genl_rcv_msg); 8128c2ecf20Sopenharmony_ci up_read(&cb_lock); 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci/************************************************************************** 8168c2ecf20Sopenharmony_ci * Controller 8178c2ecf20Sopenharmony_ci **************************************************************************/ 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic struct genl_family genl_ctrl; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic int ctrl_fill_info(const struct genl_family *family, u32 portid, u32 seq, 8228c2ecf20Sopenharmony_ci u32 flags, struct sk_buff *skb, u8 cmd) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci void *hdr; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd); 8278c2ecf20Sopenharmony_ci if (hdr == NULL) 8288c2ecf20Sopenharmony_ci return -1; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, family->name) || 8318c2ecf20Sopenharmony_ci nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id) || 8328c2ecf20Sopenharmony_ci nla_put_u32(skb, CTRL_ATTR_VERSION, family->version) || 8338c2ecf20Sopenharmony_ci nla_put_u32(skb, CTRL_ATTR_HDRSIZE, family->hdrsize) || 8348c2ecf20Sopenharmony_ci nla_put_u32(skb, CTRL_ATTR_MAXATTR, family->maxattr)) 8358c2ecf20Sopenharmony_ci goto nla_put_failure; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (genl_get_cmd_cnt(family)) { 8388c2ecf20Sopenharmony_ci struct nlattr *nla_ops; 8398c2ecf20Sopenharmony_ci int i; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci nla_ops = nla_nest_start_noflag(skb, CTRL_ATTR_OPS); 8428c2ecf20Sopenharmony_ci if (nla_ops == NULL) 8438c2ecf20Sopenharmony_ci goto nla_put_failure; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci for (i = 0; i < genl_get_cmd_cnt(family); i++) { 8468c2ecf20Sopenharmony_ci struct nlattr *nest; 8478c2ecf20Sopenharmony_ci struct genl_ops op; 8488c2ecf20Sopenharmony_ci u32 op_flags; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci genl_get_cmd_by_index(i, family, &op); 8518c2ecf20Sopenharmony_ci op_flags = op.flags; 8528c2ecf20Sopenharmony_ci if (op.dumpit) 8538c2ecf20Sopenharmony_ci op_flags |= GENL_CMD_CAP_DUMP; 8548c2ecf20Sopenharmony_ci if (op.doit) 8558c2ecf20Sopenharmony_ci op_flags |= GENL_CMD_CAP_DO; 8568c2ecf20Sopenharmony_ci if (op.policy) 8578c2ecf20Sopenharmony_ci op_flags |= GENL_CMD_CAP_HASPOL; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, i + 1); 8608c2ecf20Sopenharmony_ci if (nest == NULL) 8618c2ecf20Sopenharmony_ci goto nla_put_failure; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (nla_put_u32(skb, CTRL_ATTR_OP_ID, op.cmd) || 8648c2ecf20Sopenharmony_ci nla_put_u32(skb, CTRL_ATTR_OP_FLAGS, op_flags)) 8658c2ecf20Sopenharmony_ci goto nla_put_failure; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci nla_nest_end(skb, nla_ops); 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (family->n_mcgrps) { 8748c2ecf20Sopenharmony_ci struct nlattr *nla_grps; 8758c2ecf20Sopenharmony_ci int i; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci nla_grps = nla_nest_start_noflag(skb, CTRL_ATTR_MCAST_GROUPS); 8788c2ecf20Sopenharmony_ci if (nla_grps == NULL) 8798c2ecf20Sopenharmony_ci goto nla_put_failure; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci for (i = 0; i < family->n_mcgrps; i++) { 8828c2ecf20Sopenharmony_ci struct nlattr *nest; 8838c2ecf20Sopenharmony_ci const struct genl_multicast_group *grp; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci grp = &family->mcgrps[i]; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, i + 1); 8888c2ecf20Sopenharmony_ci if (nest == NULL) 8898c2ecf20Sopenharmony_ci goto nla_put_failure; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, 8928c2ecf20Sopenharmony_ci family->mcgrp_offset + i) || 8938c2ecf20Sopenharmony_ci nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME, 8948c2ecf20Sopenharmony_ci grp->name)) 8958c2ecf20Sopenharmony_ci goto nla_put_failure; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci nla_nest_end(skb, nla_grps); 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci genlmsg_end(skb, hdr); 9038c2ecf20Sopenharmony_ci return 0; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cinla_put_failure: 9068c2ecf20Sopenharmony_ci genlmsg_cancel(skb, hdr); 9078c2ecf20Sopenharmony_ci return -EMSGSIZE; 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic int ctrl_fill_mcgrp_info(const struct genl_family *family, 9118c2ecf20Sopenharmony_ci const struct genl_multicast_group *grp, 9128c2ecf20Sopenharmony_ci int grp_id, u32 portid, u32 seq, u32 flags, 9138c2ecf20Sopenharmony_ci struct sk_buff *skb, u8 cmd) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci void *hdr; 9168c2ecf20Sopenharmony_ci struct nlattr *nla_grps; 9178c2ecf20Sopenharmony_ci struct nlattr *nest; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci hdr = genlmsg_put(skb, portid, seq, &genl_ctrl, flags, cmd); 9208c2ecf20Sopenharmony_ci if (hdr == NULL) 9218c2ecf20Sopenharmony_ci return -1; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (nla_put_string(skb, CTRL_ATTR_FAMILY_NAME, family->name) || 9248c2ecf20Sopenharmony_ci nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, family->id)) 9258c2ecf20Sopenharmony_ci goto nla_put_failure; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci nla_grps = nla_nest_start_noflag(skb, CTRL_ATTR_MCAST_GROUPS); 9288c2ecf20Sopenharmony_ci if (nla_grps == NULL) 9298c2ecf20Sopenharmony_ci goto nla_put_failure; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci nest = nla_nest_start_noflag(skb, 1); 9328c2ecf20Sopenharmony_ci if (nest == NULL) 9338c2ecf20Sopenharmony_ci goto nla_put_failure; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci if (nla_put_u32(skb, CTRL_ATTR_MCAST_GRP_ID, grp_id) || 9368c2ecf20Sopenharmony_ci nla_put_string(skb, CTRL_ATTR_MCAST_GRP_NAME, 9378c2ecf20Sopenharmony_ci grp->name)) 9388c2ecf20Sopenharmony_ci goto nla_put_failure; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 9418c2ecf20Sopenharmony_ci nla_nest_end(skb, nla_grps); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci genlmsg_end(skb, hdr); 9448c2ecf20Sopenharmony_ci return 0; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cinla_put_failure: 9478c2ecf20Sopenharmony_ci genlmsg_cancel(skb, hdr); 9488c2ecf20Sopenharmony_ci return -EMSGSIZE; 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cistatic int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci int n = 0; 9548c2ecf20Sopenharmony_ci struct genl_family *rt; 9558c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 9568c2ecf20Sopenharmony_ci int fams_to_skip = cb->args[0]; 9578c2ecf20Sopenharmony_ci unsigned int id; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci idr_for_each_entry(&genl_fam_idr, rt, id) { 9608c2ecf20Sopenharmony_ci if (!rt->netnsok && !net_eq(net, &init_net)) 9618c2ecf20Sopenharmony_ci continue; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (n++ < fams_to_skip) 9648c2ecf20Sopenharmony_ci continue; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci if (ctrl_fill_info(rt, NETLINK_CB(cb->skb).portid, 9678c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 9688c2ecf20Sopenharmony_ci skb, CTRL_CMD_NEWFAMILY) < 0) { 9698c2ecf20Sopenharmony_ci n--; 9708c2ecf20Sopenharmony_ci break; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci cb->args[0] = n; 9758c2ecf20Sopenharmony_ci return skb->len; 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic struct sk_buff *ctrl_build_family_msg(const struct genl_family *family, 9798c2ecf20Sopenharmony_ci u32 portid, int seq, u8 cmd) 9808c2ecf20Sopenharmony_ci{ 9818c2ecf20Sopenharmony_ci struct sk_buff *skb; 9828c2ecf20Sopenharmony_ci int err; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 9858c2ecf20Sopenharmony_ci if (skb == NULL) 9868c2ecf20Sopenharmony_ci return ERR_PTR(-ENOBUFS); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci err = ctrl_fill_info(family, portid, seq, 0, skb, cmd); 9898c2ecf20Sopenharmony_ci if (err < 0) { 9908c2ecf20Sopenharmony_ci nlmsg_free(skb); 9918c2ecf20Sopenharmony_ci return ERR_PTR(err); 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci return skb; 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic struct sk_buff * 9988c2ecf20Sopenharmony_cictrl_build_mcgrp_msg(const struct genl_family *family, 9998c2ecf20Sopenharmony_ci const struct genl_multicast_group *grp, 10008c2ecf20Sopenharmony_ci int grp_id, u32 portid, int seq, u8 cmd) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci struct sk_buff *skb; 10038c2ecf20Sopenharmony_ci int err; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 10068c2ecf20Sopenharmony_ci if (skb == NULL) 10078c2ecf20Sopenharmony_ci return ERR_PTR(-ENOBUFS); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci err = ctrl_fill_mcgrp_info(family, grp, grp_id, portid, 10108c2ecf20Sopenharmony_ci seq, 0, skb, cmd); 10118c2ecf20Sopenharmony_ci if (err < 0) { 10128c2ecf20Sopenharmony_ci nlmsg_free(skb); 10138c2ecf20Sopenharmony_ci return ERR_PTR(err); 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci return skb; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic const struct nla_policy ctrl_policy_family[] = { 10208c2ecf20Sopenharmony_ci [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, 10218c2ecf20Sopenharmony_ci [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, 10228c2ecf20Sopenharmony_ci .len = GENL_NAMSIZ - 1 }, 10238c2ecf20Sopenharmony_ci}; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic int ctrl_getfamily(struct sk_buff *skb, struct genl_info *info) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci struct sk_buff *msg; 10288c2ecf20Sopenharmony_ci const struct genl_family *res = NULL; 10298c2ecf20Sopenharmony_ci int err = -EINVAL; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if (info->attrs[CTRL_ATTR_FAMILY_ID]) { 10328c2ecf20Sopenharmony_ci u16 id = nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]); 10338c2ecf20Sopenharmony_ci res = genl_family_find_byid(id); 10348c2ecf20Sopenharmony_ci err = -ENOENT; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci if (info->attrs[CTRL_ATTR_FAMILY_NAME]) { 10388c2ecf20Sopenharmony_ci char *name; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]); 10418c2ecf20Sopenharmony_ci res = genl_family_find_byname(name); 10428c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES 10438c2ecf20Sopenharmony_ci if (res == NULL) { 10448c2ecf20Sopenharmony_ci genl_unlock(); 10458c2ecf20Sopenharmony_ci up_read(&cb_lock); 10468c2ecf20Sopenharmony_ci request_module("net-pf-%d-proto-%d-family-%s", 10478c2ecf20Sopenharmony_ci PF_NETLINK, NETLINK_GENERIC, name); 10488c2ecf20Sopenharmony_ci down_read(&cb_lock); 10498c2ecf20Sopenharmony_ci genl_lock(); 10508c2ecf20Sopenharmony_ci res = genl_family_find_byname(name); 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci#endif 10538c2ecf20Sopenharmony_ci err = -ENOENT; 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci if (res == NULL) 10578c2ecf20Sopenharmony_ci return err; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (!res->netnsok && !net_eq(genl_info_net(info), &init_net)) { 10608c2ecf20Sopenharmony_ci /* family doesn't exist here */ 10618c2ecf20Sopenharmony_ci return -ENOENT; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci msg = ctrl_build_family_msg(res, info->snd_portid, info->snd_seq, 10658c2ecf20Sopenharmony_ci CTRL_CMD_NEWFAMILY); 10668c2ecf20Sopenharmony_ci if (IS_ERR(msg)) 10678c2ecf20Sopenharmony_ci return PTR_ERR(msg); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci return genlmsg_reply(msg, info); 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic int genl_ctrl_event(int event, const struct genl_family *family, 10738c2ecf20Sopenharmony_ci const struct genl_multicast_group *grp, 10748c2ecf20Sopenharmony_ci int grp_id) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci struct sk_buff *msg; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci /* genl is still initialising */ 10798c2ecf20Sopenharmony_ci if (!init_net.genl_sock) 10808c2ecf20Sopenharmony_ci return 0; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci switch (event) { 10838c2ecf20Sopenharmony_ci case CTRL_CMD_NEWFAMILY: 10848c2ecf20Sopenharmony_ci case CTRL_CMD_DELFAMILY: 10858c2ecf20Sopenharmony_ci WARN_ON(grp); 10868c2ecf20Sopenharmony_ci msg = ctrl_build_family_msg(family, 0, 0, event); 10878c2ecf20Sopenharmony_ci break; 10888c2ecf20Sopenharmony_ci case CTRL_CMD_NEWMCAST_GRP: 10898c2ecf20Sopenharmony_ci case CTRL_CMD_DELMCAST_GRP: 10908c2ecf20Sopenharmony_ci BUG_ON(!grp); 10918c2ecf20Sopenharmony_ci msg = ctrl_build_mcgrp_msg(family, grp, grp_id, 0, 0, event); 10928c2ecf20Sopenharmony_ci break; 10938c2ecf20Sopenharmony_ci default: 10948c2ecf20Sopenharmony_ci return -EINVAL; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci if (IS_ERR(msg)) 10988c2ecf20Sopenharmony_ci return PTR_ERR(msg); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci if (!family->netnsok) { 11018c2ecf20Sopenharmony_ci genlmsg_multicast_netns(&genl_ctrl, &init_net, msg, 0, 11028c2ecf20Sopenharmony_ci 0, GFP_KERNEL); 11038c2ecf20Sopenharmony_ci } else { 11048c2ecf20Sopenharmony_ci rcu_read_lock(); 11058c2ecf20Sopenharmony_ci genlmsg_multicast_allns(&genl_ctrl, msg, 0, 11068c2ecf20Sopenharmony_ci 0, GFP_ATOMIC); 11078c2ecf20Sopenharmony_ci rcu_read_unlock(); 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci return 0; 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistruct ctrl_dump_policy_ctx { 11148c2ecf20Sopenharmony_ci struct netlink_policy_dump_state *state; 11158c2ecf20Sopenharmony_ci const struct genl_family *rt; 11168c2ecf20Sopenharmony_ci unsigned int opidx; 11178c2ecf20Sopenharmony_ci u32 op; 11188c2ecf20Sopenharmony_ci u16 fam_id; 11198c2ecf20Sopenharmony_ci u8 policies:1, 11208c2ecf20Sopenharmony_ci single_op:1; 11218c2ecf20Sopenharmony_ci}; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_cistatic const struct nla_policy ctrl_policy_policy[] = { 11248c2ecf20Sopenharmony_ci [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, 11258c2ecf20Sopenharmony_ci [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_NUL_STRING, 11268c2ecf20Sopenharmony_ci .len = GENL_NAMSIZ - 1 }, 11278c2ecf20Sopenharmony_ci [CTRL_ATTR_OP] = { .type = NLA_U32 }, 11288c2ecf20Sopenharmony_ci}; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic int ctrl_dumppolicy_start(struct netlink_callback *cb) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci const struct genl_dumpit_info *info = genl_dumpit_info(cb); 11338c2ecf20Sopenharmony_ci struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; 11348c2ecf20Sopenharmony_ci struct nlattr **tb = info->attrs; 11358c2ecf20Sopenharmony_ci const struct genl_family *rt; 11368c2ecf20Sopenharmony_ci struct genl_ops op; 11378c2ecf20Sopenharmony_ci int err, i; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (!tb[CTRL_ATTR_FAMILY_ID] && !tb[CTRL_ATTR_FAMILY_NAME]) 11428c2ecf20Sopenharmony_ci return -EINVAL; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (tb[CTRL_ATTR_FAMILY_ID]) { 11458c2ecf20Sopenharmony_ci ctx->fam_id = nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]); 11468c2ecf20Sopenharmony_ci } else { 11478c2ecf20Sopenharmony_ci rt = genl_family_find_byname( 11488c2ecf20Sopenharmony_ci nla_data(tb[CTRL_ATTR_FAMILY_NAME])); 11498c2ecf20Sopenharmony_ci if (!rt) 11508c2ecf20Sopenharmony_ci return -ENOENT; 11518c2ecf20Sopenharmony_ci ctx->fam_id = rt->id; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci rt = genl_family_find_byid(ctx->fam_id); 11558c2ecf20Sopenharmony_ci if (!rt) 11568c2ecf20Sopenharmony_ci return -ENOENT; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci ctx->rt = rt; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci if (tb[CTRL_ATTR_OP]) { 11618c2ecf20Sopenharmony_ci ctx->single_op = true; 11628c2ecf20Sopenharmony_ci ctx->op = nla_get_u32(tb[CTRL_ATTR_OP]); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci err = genl_get_cmd(ctx->op, rt, &op); 11658c2ecf20Sopenharmony_ci if (err) { 11668c2ecf20Sopenharmony_ci NL_SET_BAD_ATTR(cb->extack, tb[CTRL_ATTR_OP]); 11678c2ecf20Sopenharmony_ci return err; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci if (!op.policy) 11718c2ecf20Sopenharmony_ci return -ENODATA; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci return netlink_policy_dump_add_policy(&ctx->state, op.policy, 11748c2ecf20Sopenharmony_ci op.maxattr); 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci for (i = 0; i < genl_get_cmd_cnt(rt); i++) { 11788c2ecf20Sopenharmony_ci genl_get_cmd_by_index(i, rt, &op); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci if (op.policy) { 11818c2ecf20Sopenharmony_ci err = netlink_policy_dump_add_policy(&ctx->state, 11828c2ecf20Sopenharmony_ci op.policy, 11838c2ecf20Sopenharmony_ci op.maxattr); 11848c2ecf20Sopenharmony_ci if (err) 11858c2ecf20Sopenharmony_ci goto err_free_state; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci if (!ctx->state) 11908c2ecf20Sopenharmony_ci return -ENODATA; 11918c2ecf20Sopenharmony_ci return 0; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cierr_free_state: 11948c2ecf20Sopenharmony_ci netlink_policy_dump_free(ctx->state); 11958c2ecf20Sopenharmony_ci return err; 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic void *ctrl_dumppolicy_prep(struct sk_buff *skb, 11998c2ecf20Sopenharmony_ci struct netlink_callback *cb) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; 12028c2ecf20Sopenharmony_ci void *hdr; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, 12058c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, &genl_ctrl, 12068c2ecf20Sopenharmony_ci NLM_F_MULTI, CTRL_CMD_GETPOLICY); 12078c2ecf20Sopenharmony_ci if (!hdr) 12088c2ecf20Sopenharmony_ci return NULL; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, ctx->fam_id)) 12118c2ecf20Sopenharmony_ci return NULL; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci return hdr; 12148c2ecf20Sopenharmony_ci} 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_cistatic int ctrl_dumppolicy_put_op(struct sk_buff *skb, 12178c2ecf20Sopenharmony_ci struct netlink_callback *cb, 12188c2ecf20Sopenharmony_ci struct genl_ops *op) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; 12218c2ecf20Sopenharmony_ci struct nlattr *nest_pol, *nest_op; 12228c2ecf20Sopenharmony_ci void *hdr; 12238c2ecf20Sopenharmony_ci int idx; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci /* skip if we have nothing to show */ 12268c2ecf20Sopenharmony_ci if (!op->policy) 12278c2ecf20Sopenharmony_ci return 0; 12288c2ecf20Sopenharmony_ci if (!op->doit && 12298c2ecf20Sopenharmony_ci (!op->dumpit || op->validate & GENL_DONT_VALIDATE_DUMP)) 12308c2ecf20Sopenharmony_ci return 0; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci hdr = ctrl_dumppolicy_prep(skb, cb); 12338c2ecf20Sopenharmony_ci if (!hdr) 12348c2ecf20Sopenharmony_ci return -ENOBUFS; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci nest_pol = nla_nest_start(skb, CTRL_ATTR_OP_POLICY); 12378c2ecf20Sopenharmony_ci if (!nest_pol) 12388c2ecf20Sopenharmony_ci goto err; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci nest_op = nla_nest_start(skb, op->cmd); 12418c2ecf20Sopenharmony_ci if (!nest_op) 12428c2ecf20Sopenharmony_ci goto err; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci /* for now both do/dump are always the same */ 12458c2ecf20Sopenharmony_ci idx = netlink_policy_dump_get_policy_idx(ctx->state, 12468c2ecf20Sopenharmony_ci op->policy, 12478c2ecf20Sopenharmony_ci op->maxattr); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci if (op->doit && nla_put_u32(skb, CTRL_ATTR_POLICY_DO, idx)) 12508c2ecf20Sopenharmony_ci goto err; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (op->dumpit && !(op->validate & GENL_DONT_VALIDATE_DUMP) && 12538c2ecf20Sopenharmony_ci nla_put_u32(skb, CTRL_ATTR_POLICY_DUMP, idx)) 12548c2ecf20Sopenharmony_ci goto err; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci nla_nest_end(skb, nest_op); 12578c2ecf20Sopenharmony_ci nla_nest_end(skb, nest_pol); 12588c2ecf20Sopenharmony_ci genlmsg_end(skb, hdr); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci return 0; 12618c2ecf20Sopenharmony_cierr: 12628c2ecf20Sopenharmony_ci genlmsg_cancel(skb, hdr); 12638c2ecf20Sopenharmony_ci return -ENOBUFS; 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cistatic int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb) 12678c2ecf20Sopenharmony_ci{ 12688c2ecf20Sopenharmony_ci struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; 12698c2ecf20Sopenharmony_ci void *hdr; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci if (!ctx->policies) { 12728c2ecf20Sopenharmony_ci while (ctx->opidx < genl_get_cmd_cnt(ctx->rt)) { 12738c2ecf20Sopenharmony_ci struct genl_ops op; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci if (ctx->single_op) { 12768c2ecf20Sopenharmony_ci int err; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci err = genl_get_cmd(ctx->op, ctx->rt, &op); 12798c2ecf20Sopenharmony_ci if (WARN_ON(err)) 12808c2ecf20Sopenharmony_ci return skb->len; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci /* break out of the loop after this one */ 12838c2ecf20Sopenharmony_ci ctx->opidx = genl_get_cmd_cnt(ctx->rt); 12848c2ecf20Sopenharmony_ci } else { 12858c2ecf20Sopenharmony_ci genl_get_cmd_by_index(ctx->opidx, ctx->rt, &op); 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci if (ctrl_dumppolicy_put_op(skb, cb, &op)) 12898c2ecf20Sopenharmony_ci return skb->len; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci ctx->opidx++; 12928c2ecf20Sopenharmony_ci } 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci /* completed with the per-op policy index list */ 12958c2ecf20Sopenharmony_ci ctx->policies = true; 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci while (netlink_policy_dump_loop(ctx->state)) { 12998c2ecf20Sopenharmony_ci struct nlattr *nest; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci hdr = ctrl_dumppolicy_prep(skb, cb); 13028c2ecf20Sopenharmony_ci if (!hdr) 13038c2ecf20Sopenharmony_ci goto nla_put_failure; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci nest = nla_nest_start(skb, CTRL_ATTR_POLICY); 13068c2ecf20Sopenharmony_ci if (!nest) 13078c2ecf20Sopenharmony_ci goto nla_put_failure; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci if (netlink_policy_dump_write(skb, ctx->state)) 13108c2ecf20Sopenharmony_ci goto nla_put_failure; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci nla_nest_end(skb, nest); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci genlmsg_end(skb, hdr); 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci return skb->len; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cinla_put_failure: 13208c2ecf20Sopenharmony_ci genlmsg_cancel(skb, hdr); 13218c2ecf20Sopenharmony_ci return skb->len; 13228c2ecf20Sopenharmony_ci} 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_cistatic int ctrl_dumppolicy_done(struct netlink_callback *cb) 13258c2ecf20Sopenharmony_ci{ 13268c2ecf20Sopenharmony_ci struct ctrl_dump_policy_ctx *ctx = (void *)cb->ctx; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci netlink_policy_dump_free(ctx->state); 13298c2ecf20Sopenharmony_ci return 0; 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_cistatic const struct genl_ops genl_ctrl_ops[] = { 13338c2ecf20Sopenharmony_ci { 13348c2ecf20Sopenharmony_ci .cmd = CTRL_CMD_GETFAMILY, 13358c2ecf20Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 13368c2ecf20Sopenharmony_ci .policy = ctrl_policy_family, 13378c2ecf20Sopenharmony_ci .maxattr = ARRAY_SIZE(ctrl_policy_family) - 1, 13388c2ecf20Sopenharmony_ci .doit = ctrl_getfamily, 13398c2ecf20Sopenharmony_ci .dumpit = ctrl_dumpfamily, 13408c2ecf20Sopenharmony_ci }, 13418c2ecf20Sopenharmony_ci { 13428c2ecf20Sopenharmony_ci .cmd = CTRL_CMD_GETPOLICY, 13438c2ecf20Sopenharmony_ci .policy = ctrl_policy_policy, 13448c2ecf20Sopenharmony_ci .maxattr = ARRAY_SIZE(ctrl_policy_policy) - 1, 13458c2ecf20Sopenharmony_ci .start = ctrl_dumppolicy_start, 13468c2ecf20Sopenharmony_ci .dumpit = ctrl_dumppolicy, 13478c2ecf20Sopenharmony_ci .done = ctrl_dumppolicy_done, 13488c2ecf20Sopenharmony_ci }, 13498c2ecf20Sopenharmony_ci}; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_cistatic const struct genl_multicast_group genl_ctrl_groups[] = { 13528c2ecf20Sopenharmony_ci { .name = "notify", }, 13538c2ecf20Sopenharmony_ci}; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_cistatic struct genl_family genl_ctrl __ro_after_init = { 13568c2ecf20Sopenharmony_ci .module = THIS_MODULE, 13578c2ecf20Sopenharmony_ci .ops = genl_ctrl_ops, 13588c2ecf20Sopenharmony_ci .n_ops = ARRAY_SIZE(genl_ctrl_ops), 13598c2ecf20Sopenharmony_ci .mcgrps = genl_ctrl_groups, 13608c2ecf20Sopenharmony_ci .n_mcgrps = ARRAY_SIZE(genl_ctrl_groups), 13618c2ecf20Sopenharmony_ci .id = GENL_ID_CTRL, 13628c2ecf20Sopenharmony_ci .name = "nlctrl", 13638c2ecf20Sopenharmony_ci .version = 0x2, 13648c2ecf20Sopenharmony_ci .netnsok = true, 13658c2ecf20Sopenharmony_ci}; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_cistatic int genl_bind(struct net *net, int group) 13688c2ecf20Sopenharmony_ci{ 13698c2ecf20Sopenharmony_ci const struct genl_family *family; 13708c2ecf20Sopenharmony_ci unsigned int id; 13718c2ecf20Sopenharmony_ci int ret = 0; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci genl_lock_all(); 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci idr_for_each_entry(&genl_fam_idr, family, id) { 13768c2ecf20Sopenharmony_ci const struct genl_multicast_group *grp; 13778c2ecf20Sopenharmony_ci int i; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci if (family->n_mcgrps == 0) 13808c2ecf20Sopenharmony_ci continue; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci i = group - family->mcgrp_offset; 13838c2ecf20Sopenharmony_ci if (i < 0 || i >= family->n_mcgrps) 13848c2ecf20Sopenharmony_ci continue; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci grp = &family->mcgrps[i]; 13878c2ecf20Sopenharmony_ci if ((grp->flags & GENL_UNS_ADMIN_PERM) && 13888c2ecf20Sopenharmony_ci !ns_capable(net->user_ns, CAP_NET_ADMIN)) 13898c2ecf20Sopenharmony_ci ret = -EPERM; 13908c2ecf20Sopenharmony_ci if (grp->cap_sys_admin && 13918c2ecf20Sopenharmony_ci !ns_capable(net->user_ns, CAP_SYS_ADMIN)) 13928c2ecf20Sopenharmony_ci ret = -EPERM; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci break; 13958c2ecf20Sopenharmony_ci } 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci genl_unlock_all(); 13988c2ecf20Sopenharmony_ci return ret; 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_cistatic int __net_init genl_pernet_init(struct net *net) 14028c2ecf20Sopenharmony_ci{ 14038c2ecf20Sopenharmony_ci struct netlink_kernel_cfg cfg = { 14048c2ecf20Sopenharmony_ci .input = genl_rcv, 14058c2ecf20Sopenharmony_ci .flags = NL_CFG_F_NONROOT_RECV, 14068c2ecf20Sopenharmony_ci .bind = genl_bind, 14078c2ecf20Sopenharmony_ci }; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci /* we'll bump the group number right afterwards */ 14108c2ecf20Sopenharmony_ci net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, &cfg); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci if (!net->genl_sock && net_eq(net, &init_net)) 14138c2ecf20Sopenharmony_ci panic("GENL: Cannot initialize generic netlink\n"); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci if (!net->genl_sock) 14168c2ecf20Sopenharmony_ci return -ENOMEM; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci return 0; 14198c2ecf20Sopenharmony_ci} 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_cistatic void __net_exit genl_pernet_exit(struct net *net) 14228c2ecf20Sopenharmony_ci{ 14238c2ecf20Sopenharmony_ci netlink_kernel_release(net->genl_sock); 14248c2ecf20Sopenharmony_ci net->genl_sock = NULL; 14258c2ecf20Sopenharmony_ci} 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_cistatic struct pernet_operations genl_pernet_ops = { 14288c2ecf20Sopenharmony_ci .init = genl_pernet_init, 14298c2ecf20Sopenharmony_ci .exit = genl_pernet_exit, 14308c2ecf20Sopenharmony_ci}; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_cistatic int __init genl_init(void) 14338c2ecf20Sopenharmony_ci{ 14348c2ecf20Sopenharmony_ci int err; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci err = genl_register_family(&genl_ctrl); 14378c2ecf20Sopenharmony_ci if (err < 0) 14388c2ecf20Sopenharmony_ci goto problem; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci err = register_pernet_subsys(&genl_pernet_ops); 14418c2ecf20Sopenharmony_ci if (err) 14428c2ecf20Sopenharmony_ci goto problem; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci return 0; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ciproblem: 14478c2ecf20Sopenharmony_ci panic("GENL: Cannot register controller: %d\n", err); 14488c2ecf20Sopenharmony_ci} 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_cicore_initcall(genl_init); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_cistatic int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group, 14538c2ecf20Sopenharmony_ci gfp_t flags) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci struct sk_buff *tmp; 14568c2ecf20Sopenharmony_ci struct net *net, *prev = NULL; 14578c2ecf20Sopenharmony_ci bool delivered = false; 14588c2ecf20Sopenharmony_ci int err; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci for_each_net_rcu(net) { 14618c2ecf20Sopenharmony_ci if (prev) { 14628c2ecf20Sopenharmony_ci tmp = skb_clone(skb, flags); 14638c2ecf20Sopenharmony_ci if (!tmp) { 14648c2ecf20Sopenharmony_ci err = -ENOMEM; 14658c2ecf20Sopenharmony_ci goto error; 14668c2ecf20Sopenharmony_ci } 14678c2ecf20Sopenharmony_ci err = nlmsg_multicast(prev->genl_sock, tmp, 14688c2ecf20Sopenharmony_ci portid, group, flags); 14698c2ecf20Sopenharmony_ci if (!err) 14708c2ecf20Sopenharmony_ci delivered = true; 14718c2ecf20Sopenharmony_ci else if (err != -ESRCH) 14728c2ecf20Sopenharmony_ci goto error; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci prev = net; 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci err = nlmsg_multicast(prev->genl_sock, skb, portid, group, flags); 14798c2ecf20Sopenharmony_ci if (!err) 14808c2ecf20Sopenharmony_ci delivered = true; 14818c2ecf20Sopenharmony_ci else if (err != -ESRCH) 14828c2ecf20Sopenharmony_ci return err; 14838c2ecf20Sopenharmony_ci return delivered ? 0 : -ESRCH; 14848c2ecf20Sopenharmony_ci error: 14858c2ecf20Sopenharmony_ci kfree_skb(skb); 14868c2ecf20Sopenharmony_ci return err; 14878c2ecf20Sopenharmony_ci} 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ciint genlmsg_multicast_allns(const struct genl_family *family, 14908c2ecf20Sopenharmony_ci struct sk_buff *skb, u32 portid, 14918c2ecf20Sopenharmony_ci unsigned int group, gfp_t flags) 14928c2ecf20Sopenharmony_ci{ 14938c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(group >= family->n_mcgrps)) 14948c2ecf20Sopenharmony_ci return -EINVAL; 14958c2ecf20Sopenharmony_ci group = family->mcgrp_offset + group; 14968c2ecf20Sopenharmony_ci return genlmsg_mcast(skb, portid, group, flags); 14978c2ecf20Sopenharmony_ci} 14988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(genlmsg_multicast_allns); 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_civoid genl_notify(const struct genl_family *family, struct sk_buff *skb, 15018c2ecf20Sopenharmony_ci struct genl_info *info, u32 group, gfp_t flags) 15028c2ecf20Sopenharmony_ci{ 15038c2ecf20Sopenharmony_ci struct net *net = genl_info_net(info); 15048c2ecf20Sopenharmony_ci struct sock *sk = net->genl_sock; 15058c2ecf20Sopenharmony_ci int report = 0; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci if (info->nlhdr) 15088c2ecf20Sopenharmony_ci report = nlmsg_report(info->nlhdr); 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(group >= family->n_mcgrps)) 15118c2ecf20Sopenharmony_ci return; 15128c2ecf20Sopenharmony_ci group = family->mcgrp_offset + group; 15138c2ecf20Sopenharmony_ci nlmsg_notify(sk, skb, info->snd_portid, group, report, flags); 15148c2ecf20Sopenharmony_ci} 15158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(genl_notify); 1516