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