18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * NETLINK      Policy advertisement to userspace
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * 		Authors:	Johannes Berg <johannes@sipsolutions.net>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright 2019 Intel Corporation
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/types.h>
138c2ecf20Sopenharmony_ci#include <net/netlink.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define INITIAL_POLICIES_ALLOC	10
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistruct netlink_policy_dump_state {
188c2ecf20Sopenharmony_ci	unsigned int policy_idx;
198c2ecf20Sopenharmony_ci	unsigned int attr_idx;
208c2ecf20Sopenharmony_ci	unsigned int n_alloc;
218c2ecf20Sopenharmony_ci	struct {
228c2ecf20Sopenharmony_ci		const struct nla_policy *policy;
238c2ecf20Sopenharmony_ci		unsigned int maxtype;
248c2ecf20Sopenharmony_ci	} policies[];
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int add_policy(struct netlink_policy_dump_state **statep,
288c2ecf20Sopenharmony_ci		      const struct nla_policy *policy,
298c2ecf20Sopenharmony_ci		      unsigned int maxtype)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	struct netlink_policy_dump_state *state = *statep;
328c2ecf20Sopenharmony_ci	unsigned int n_alloc, i;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	if (!policy || !maxtype)
358c2ecf20Sopenharmony_ci		return 0;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	for (i = 0; i < state->n_alloc; i++) {
388c2ecf20Sopenharmony_ci		if (state->policies[i].policy == policy &&
398c2ecf20Sopenharmony_ci		    state->policies[i].maxtype == maxtype)
408c2ecf20Sopenharmony_ci			return 0;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci		if (!state->policies[i].policy) {
438c2ecf20Sopenharmony_ci			state->policies[i].policy = policy;
448c2ecf20Sopenharmony_ci			state->policies[i].maxtype = maxtype;
458c2ecf20Sopenharmony_ci			return 0;
468c2ecf20Sopenharmony_ci		}
478c2ecf20Sopenharmony_ci	}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	n_alloc = state->n_alloc + INITIAL_POLICIES_ALLOC;
508c2ecf20Sopenharmony_ci	state = krealloc(state, struct_size(state, policies, n_alloc),
518c2ecf20Sopenharmony_ci			 GFP_KERNEL);
528c2ecf20Sopenharmony_ci	if (!state)
538c2ecf20Sopenharmony_ci		return -ENOMEM;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	memset(&state->policies[state->n_alloc], 0,
568c2ecf20Sopenharmony_ci	       flex_array_size(state, policies, n_alloc - state->n_alloc));
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	state->policies[state->n_alloc].policy = policy;
598c2ecf20Sopenharmony_ci	state->policies[state->n_alloc].maxtype = maxtype;
608c2ecf20Sopenharmony_ci	state->n_alloc = n_alloc;
618c2ecf20Sopenharmony_ci	*statep = state;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return 0;
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/**
678c2ecf20Sopenharmony_ci * netlink_policy_dump_get_policy_idx - retrieve policy index
688c2ecf20Sopenharmony_ci * @state: the policy dump state
698c2ecf20Sopenharmony_ci * @policy: the policy to find
708c2ecf20Sopenharmony_ci * @maxtype: the policy's maxattr
718c2ecf20Sopenharmony_ci *
728c2ecf20Sopenharmony_ci * Returns: the index of the given policy in the dump state
738c2ecf20Sopenharmony_ci *
748c2ecf20Sopenharmony_ci * Call this to find a policy index when you've added multiple and e.g.
758c2ecf20Sopenharmony_ci * need to tell userspace which command has which policy (by index).
768c2ecf20Sopenharmony_ci *
778c2ecf20Sopenharmony_ci * Note: this will WARN and return 0 if the policy isn't found, which
788c2ecf20Sopenharmony_ci *	 means it wasn't added in the first place, which would be an
798c2ecf20Sopenharmony_ci *	 internal consistency bug.
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_ciint netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state,
828c2ecf20Sopenharmony_ci				       const struct nla_policy *policy,
838c2ecf20Sopenharmony_ci				       unsigned int maxtype)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	unsigned int i;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (WARN_ON(!policy || !maxtype))
888c2ecf20Sopenharmony_ci                return 0;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	for (i = 0; i < state->n_alloc; i++) {
918c2ecf20Sopenharmony_ci		if (state->policies[i].policy == policy &&
928c2ecf20Sopenharmony_ci		    state->policies[i].maxtype == maxtype)
938c2ecf20Sopenharmony_ci			return i;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	WARN_ON(1);
978c2ecf20Sopenharmony_ci	return 0;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic struct netlink_policy_dump_state *alloc_state(void)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct netlink_policy_dump_state *state;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC),
1058c2ecf20Sopenharmony_ci			GFP_KERNEL);
1068c2ecf20Sopenharmony_ci	if (!state)
1078c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1088c2ecf20Sopenharmony_ci	state->n_alloc = INITIAL_POLICIES_ALLOC;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return state;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/**
1148c2ecf20Sopenharmony_ci * netlink_policy_dump_add_policy - add a policy to the dump
1158c2ecf20Sopenharmony_ci * @pstate: state to add to, may be reallocated, must be %NULL the first time
1168c2ecf20Sopenharmony_ci * @policy: the new policy to add to the dump
1178c2ecf20Sopenharmony_ci * @maxtype: the new policy's max attr type
1188c2ecf20Sopenharmony_ci *
1198c2ecf20Sopenharmony_ci * Returns: 0 on success, a negative error code otherwise.
1208c2ecf20Sopenharmony_ci *
1218c2ecf20Sopenharmony_ci * Call this to allocate a policy dump state, and to add policies to it. This
1228c2ecf20Sopenharmony_ci * should be called from the dump start() callback.
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * Note: on failures, any previously allocated state is freed.
1258c2ecf20Sopenharmony_ci */
1268c2ecf20Sopenharmony_ciint netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate,
1278c2ecf20Sopenharmony_ci				   const struct nla_policy *policy,
1288c2ecf20Sopenharmony_ci				   unsigned int maxtype)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct netlink_policy_dump_state *state = *pstate;
1318c2ecf20Sopenharmony_ci	unsigned int policy_idx;
1328c2ecf20Sopenharmony_ci	int err;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (!state) {
1358c2ecf20Sopenharmony_ci		state = alloc_state();
1368c2ecf20Sopenharmony_ci		if (IS_ERR(state))
1378c2ecf20Sopenharmony_ci			return PTR_ERR(state);
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/*
1418c2ecf20Sopenharmony_ci	 * walk the policies and nested ones first, and build
1428c2ecf20Sopenharmony_ci	 * a linear list of them.
1438c2ecf20Sopenharmony_ci	 */
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	err = add_policy(&state, policy, maxtype);
1468c2ecf20Sopenharmony_ci	if (err)
1478c2ecf20Sopenharmony_ci		goto err_try_undo;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	for (policy_idx = 0;
1508c2ecf20Sopenharmony_ci	     policy_idx < state->n_alloc && state->policies[policy_idx].policy;
1518c2ecf20Sopenharmony_ci	     policy_idx++) {
1528c2ecf20Sopenharmony_ci		const struct nla_policy *policy;
1538c2ecf20Sopenharmony_ci		unsigned int type;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		policy = state->policies[policy_idx].policy;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		for (type = 0;
1588c2ecf20Sopenharmony_ci		     type <= state->policies[policy_idx].maxtype;
1598c2ecf20Sopenharmony_ci		     type++) {
1608c2ecf20Sopenharmony_ci			switch (policy[type].type) {
1618c2ecf20Sopenharmony_ci			case NLA_NESTED:
1628c2ecf20Sopenharmony_ci			case NLA_NESTED_ARRAY:
1638c2ecf20Sopenharmony_ci				err = add_policy(&state,
1648c2ecf20Sopenharmony_ci						 policy[type].nested_policy,
1658c2ecf20Sopenharmony_ci						 policy[type].len);
1668c2ecf20Sopenharmony_ci				if (err)
1678c2ecf20Sopenharmony_ci					goto err_try_undo;
1688c2ecf20Sopenharmony_ci				break;
1698c2ecf20Sopenharmony_ci			default:
1708c2ecf20Sopenharmony_ci				break;
1718c2ecf20Sopenharmony_ci			}
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	*pstate = state;
1768c2ecf20Sopenharmony_ci	return 0;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cierr_try_undo:
1798c2ecf20Sopenharmony_ci	/* Try to preserve reasonable unwind semantics - if we're starting from
1808c2ecf20Sopenharmony_ci	 * scratch clean up fully, otherwise record what we got and caller will.
1818c2ecf20Sopenharmony_ci	 */
1828c2ecf20Sopenharmony_ci	if (!*pstate)
1838c2ecf20Sopenharmony_ci		netlink_policy_dump_free(state);
1848c2ecf20Sopenharmony_ci	else
1858c2ecf20Sopenharmony_ci		*pstate = state;
1868c2ecf20Sopenharmony_ci	return err;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic bool
1908c2ecf20Sopenharmony_cinetlink_policy_dump_finished(struct netlink_policy_dump_state *state)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	return state->policy_idx >= state->n_alloc ||
1938c2ecf20Sopenharmony_ci	       !state->policies[state->policy_idx].policy;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/**
1978c2ecf20Sopenharmony_ci * netlink_policy_dump_loop - dumping loop indicator
1988c2ecf20Sopenharmony_ci * @state: the policy dump state
1998c2ecf20Sopenharmony_ci *
2008c2ecf20Sopenharmony_ci * Returns: %true if the dump continues, %false otherwise
2018c2ecf20Sopenharmony_ci *
2028c2ecf20Sopenharmony_ci * Note: this frees the dump state when finishing
2038c2ecf20Sopenharmony_ci */
2048c2ecf20Sopenharmony_cibool netlink_policy_dump_loop(struct netlink_policy_dump_state *state)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	return !netlink_policy_dump_finished(state);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ciint netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	/* nested + type */
2128c2ecf20Sopenharmony_ci	int common = 2 * nla_attr_size(sizeof(u32));
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	switch (pt->type) {
2158c2ecf20Sopenharmony_ci	case NLA_UNSPEC:
2168c2ecf20Sopenharmony_ci	case NLA_REJECT:
2178c2ecf20Sopenharmony_ci		/* these actually don't need any space */
2188c2ecf20Sopenharmony_ci		return 0;
2198c2ecf20Sopenharmony_ci	case NLA_NESTED:
2208c2ecf20Sopenharmony_ci	case NLA_NESTED_ARRAY:
2218c2ecf20Sopenharmony_ci		/* common, policy idx, policy maxattr */
2228c2ecf20Sopenharmony_ci		return common + 2 * nla_attr_size(sizeof(u32));
2238c2ecf20Sopenharmony_ci	case NLA_U8:
2248c2ecf20Sopenharmony_ci	case NLA_U16:
2258c2ecf20Sopenharmony_ci	case NLA_U32:
2268c2ecf20Sopenharmony_ci	case NLA_U64:
2278c2ecf20Sopenharmony_ci	case NLA_MSECS:
2288c2ecf20Sopenharmony_ci	case NLA_S8:
2298c2ecf20Sopenharmony_ci	case NLA_S16:
2308c2ecf20Sopenharmony_ci	case NLA_S32:
2318c2ecf20Sopenharmony_ci	case NLA_S64:
2328c2ecf20Sopenharmony_ci		/* maximum is common, u64 min/max with padding */
2338c2ecf20Sopenharmony_ci		return common +
2348c2ecf20Sopenharmony_ci		       2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64)));
2358c2ecf20Sopenharmony_ci	case NLA_BITFIELD32:
2368c2ecf20Sopenharmony_ci		return common + nla_attr_size(sizeof(u32));
2378c2ecf20Sopenharmony_ci	case NLA_STRING:
2388c2ecf20Sopenharmony_ci	case NLA_NUL_STRING:
2398c2ecf20Sopenharmony_ci	case NLA_BINARY:
2408c2ecf20Sopenharmony_ci		/* maximum is common, u32 min-length/max-length */
2418c2ecf20Sopenharmony_ci		return common + 2 * nla_attr_size(sizeof(u32));
2428c2ecf20Sopenharmony_ci	case NLA_FLAG:
2438c2ecf20Sopenharmony_ci		return common;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/* this should then cause a warning later */
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int
2518c2ecf20Sopenharmony_ci__netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
2528c2ecf20Sopenharmony_ci				 struct sk_buff *skb,
2538c2ecf20Sopenharmony_ci				 const struct nla_policy *pt,
2548c2ecf20Sopenharmony_ci				 int nestattr)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	int estimate = netlink_policy_dump_attr_size_estimate(pt);
2578c2ecf20Sopenharmony_ci	enum netlink_attribute_type type;
2588c2ecf20Sopenharmony_ci	struct nlattr *attr;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	attr = nla_nest_start(skb, nestattr);
2618c2ecf20Sopenharmony_ci	if (!attr)
2628c2ecf20Sopenharmony_ci		return -ENOBUFS;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	switch (pt->type) {
2658c2ecf20Sopenharmony_ci	default:
2668c2ecf20Sopenharmony_ci	case NLA_UNSPEC:
2678c2ecf20Sopenharmony_ci	case NLA_REJECT:
2688c2ecf20Sopenharmony_ci		/* skip - use NLA_MIN_LEN to advertise such */
2698c2ecf20Sopenharmony_ci		nla_nest_cancel(skb, attr);
2708c2ecf20Sopenharmony_ci		return -ENODATA;
2718c2ecf20Sopenharmony_ci	case NLA_NESTED:
2728c2ecf20Sopenharmony_ci		type = NL_ATTR_TYPE_NESTED;
2738c2ecf20Sopenharmony_ci		fallthrough;
2748c2ecf20Sopenharmony_ci	case NLA_NESTED_ARRAY:
2758c2ecf20Sopenharmony_ci		if (pt->type == NLA_NESTED_ARRAY)
2768c2ecf20Sopenharmony_ci			type = NL_ATTR_TYPE_NESTED_ARRAY;
2778c2ecf20Sopenharmony_ci		if (state && pt->nested_policy && pt->len &&
2788c2ecf20Sopenharmony_ci		    (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX,
2798c2ecf20Sopenharmony_ci				 netlink_policy_dump_get_policy_idx(state,
2808c2ecf20Sopenharmony_ci								    pt->nested_policy,
2818c2ecf20Sopenharmony_ci								    pt->len)) ||
2828c2ecf20Sopenharmony_ci		     nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
2838c2ecf20Sopenharmony_ci				 pt->len)))
2848c2ecf20Sopenharmony_ci			goto nla_put_failure;
2858c2ecf20Sopenharmony_ci		break;
2868c2ecf20Sopenharmony_ci	case NLA_U8:
2878c2ecf20Sopenharmony_ci	case NLA_U16:
2888c2ecf20Sopenharmony_ci	case NLA_U32:
2898c2ecf20Sopenharmony_ci	case NLA_U64:
2908c2ecf20Sopenharmony_ci	case NLA_MSECS: {
2918c2ecf20Sopenharmony_ci		struct netlink_range_validation range;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		if (pt->type == NLA_U8)
2948c2ecf20Sopenharmony_ci			type = NL_ATTR_TYPE_U8;
2958c2ecf20Sopenharmony_ci		else if (pt->type == NLA_U16)
2968c2ecf20Sopenharmony_ci			type = NL_ATTR_TYPE_U16;
2978c2ecf20Sopenharmony_ci		else if (pt->type == NLA_U32)
2988c2ecf20Sopenharmony_ci			type = NL_ATTR_TYPE_U32;
2998c2ecf20Sopenharmony_ci		else
3008c2ecf20Sopenharmony_ci			type = NL_ATTR_TYPE_U64;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		if (pt->validation_type == NLA_VALIDATE_MASK) {
3038c2ecf20Sopenharmony_ci			if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MASK,
3048c2ecf20Sopenharmony_ci					      pt->mask,
3058c2ecf20Sopenharmony_ci					      NL_POLICY_TYPE_ATTR_PAD))
3068c2ecf20Sopenharmony_ci				goto nla_put_failure;
3078c2ecf20Sopenharmony_ci			break;
3088c2ecf20Sopenharmony_ci		}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci		nla_get_range_unsigned(pt, &range);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci		if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U,
3138c2ecf20Sopenharmony_ci				      range.min, NL_POLICY_TYPE_ATTR_PAD) ||
3148c2ecf20Sopenharmony_ci		    nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_U,
3158c2ecf20Sopenharmony_ci				      range.max, NL_POLICY_TYPE_ATTR_PAD))
3168c2ecf20Sopenharmony_ci			goto nla_put_failure;
3178c2ecf20Sopenharmony_ci		break;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	case NLA_S8:
3208c2ecf20Sopenharmony_ci	case NLA_S16:
3218c2ecf20Sopenharmony_ci	case NLA_S32:
3228c2ecf20Sopenharmony_ci	case NLA_S64: {
3238c2ecf20Sopenharmony_ci		struct netlink_range_validation_signed range;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		if (pt->type == NLA_S8)
3268c2ecf20Sopenharmony_ci			type = NL_ATTR_TYPE_S8;
3278c2ecf20Sopenharmony_ci		else if (pt->type == NLA_S16)
3288c2ecf20Sopenharmony_ci			type = NL_ATTR_TYPE_S16;
3298c2ecf20Sopenharmony_ci		else if (pt->type == NLA_S32)
3308c2ecf20Sopenharmony_ci			type = NL_ATTR_TYPE_S32;
3318c2ecf20Sopenharmony_ci		else
3328c2ecf20Sopenharmony_ci			type = NL_ATTR_TYPE_S64;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci		nla_get_range_signed(pt, &range);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci		if (nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_S,
3378c2ecf20Sopenharmony_ci				range.min, NL_POLICY_TYPE_ATTR_PAD) ||
3388c2ecf20Sopenharmony_ci		    nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_S,
3398c2ecf20Sopenharmony_ci				range.max, NL_POLICY_TYPE_ATTR_PAD))
3408c2ecf20Sopenharmony_ci			goto nla_put_failure;
3418c2ecf20Sopenharmony_ci		break;
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci	case NLA_BITFIELD32:
3448c2ecf20Sopenharmony_ci		type = NL_ATTR_TYPE_BITFIELD32;
3458c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
3468c2ecf20Sopenharmony_ci				pt->bitfield32_valid))
3478c2ecf20Sopenharmony_ci			goto nla_put_failure;
3488c2ecf20Sopenharmony_ci		break;
3498c2ecf20Sopenharmony_ci	case NLA_STRING:
3508c2ecf20Sopenharmony_ci	case NLA_NUL_STRING:
3518c2ecf20Sopenharmony_ci	case NLA_BINARY:
3528c2ecf20Sopenharmony_ci		if (pt->type == NLA_STRING)
3538c2ecf20Sopenharmony_ci			type = NL_ATTR_TYPE_STRING;
3548c2ecf20Sopenharmony_ci		else if (pt->type == NLA_NUL_STRING)
3558c2ecf20Sopenharmony_ci			type = NL_ATTR_TYPE_NUL_STRING;
3568c2ecf20Sopenharmony_ci		else
3578c2ecf20Sopenharmony_ci			type = NL_ATTR_TYPE_BINARY;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		if (pt->validation_type == NLA_VALIDATE_RANGE ||
3608c2ecf20Sopenharmony_ci		    pt->validation_type == NLA_VALIDATE_RANGE_WARN_TOO_LONG) {
3618c2ecf20Sopenharmony_ci			struct netlink_range_validation range;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci			nla_get_range_unsigned(pt, &range);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci			if (range.min &&
3668c2ecf20Sopenharmony_ci			    nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH,
3678c2ecf20Sopenharmony_ci					range.min))
3688c2ecf20Sopenharmony_ci				goto nla_put_failure;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci			if (range.max < U16_MAX &&
3718c2ecf20Sopenharmony_ci			    nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH,
3728c2ecf20Sopenharmony_ci					range.max))
3738c2ecf20Sopenharmony_ci				goto nla_put_failure;
3748c2ecf20Sopenharmony_ci		} else if (pt->len &&
3758c2ecf20Sopenharmony_ci			   nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH,
3768c2ecf20Sopenharmony_ci				       pt->len)) {
3778c2ecf20Sopenharmony_ci			goto nla_put_failure;
3788c2ecf20Sopenharmony_ci		}
3798c2ecf20Sopenharmony_ci		break;
3808c2ecf20Sopenharmony_ci	case NLA_FLAG:
3818c2ecf20Sopenharmony_ci		type = NL_ATTR_TYPE_FLAG;
3828c2ecf20Sopenharmony_ci		break;
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_TYPE, type))
3868c2ecf20Sopenharmony_ci		goto nla_put_failure;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	nla_nest_end(skb, attr);
3898c2ecf20Sopenharmony_ci	WARN_ON(attr->nla_len > estimate);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return 0;
3928c2ecf20Sopenharmony_cinla_put_failure:
3938c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, attr);
3948c2ecf20Sopenharmony_ci	return -ENOBUFS;
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci/**
3988c2ecf20Sopenharmony_ci * netlink_policy_dump_write_attr - write a given attribute policy
3998c2ecf20Sopenharmony_ci * @skb: the message skb to write to
4008c2ecf20Sopenharmony_ci * @pt: the attribute's policy
4018c2ecf20Sopenharmony_ci * @nestattr: the nested attribute ID to use
4028c2ecf20Sopenharmony_ci *
4038c2ecf20Sopenharmony_ci * Returns: 0 on success, an error code otherwise; -%ENODATA is
4048c2ecf20Sopenharmony_ci *	    special, indicating that there's no policy data and
4058c2ecf20Sopenharmony_ci *	    the attribute is generally rejected.
4068c2ecf20Sopenharmony_ci */
4078c2ecf20Sopenharmony_ciint netlink_policy_dump_write_attr(struct sk_buff *skb,
4088c2ecf20Sopenharmony_ci				   const struct nla_policy *pt,
4098c2ecf20Sopenharmony_ci				   int nestattr)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	return __netlink_policy_dump_write_attr(NULL, skb, pt, nestattr);
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci/**
4158c2ecf20Sopenharmony_ci * netlink_policy_dump_write - write current policy dump attributes
4168c2ecf20Sopenharmony_ci * @skb: the message skb to write to
4178c2ecf20Sopenharmony_ci * @state: the policy dump state
4188c2ecf20Sopenharmony_ci *
4198c2ecf20Sopenharmony_ci * Returns: 0 on success, an error code otherwise
4208c2ecf20Sopenharmony_ci */
4218c2ecf20Sopenharmony_ciint netlink_policy_dump_write(struct sk_buff *skb,
4228c2ecf20Sopenharmony_ci			      struct netlink_policy_dump_state *state)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	const struct nla_policy *pt;
4258c2ecf20Sopenharmony_ci	struct nlattr *policy;
4268c2ecf20Sopenharmony_ci	bool again;
4278c2ecf20Sopenharmony_ci	int err;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cisend_attribute:
4308c2ecf20Sopenharmony_ci	again = false;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	pt = &state->policies[state->policy_idx].policy[state->attr_idx];
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	policy = nla_nest_start(skb, state->policy_idx);
4358c2ecf20Sopenharmony_ci	if (!policy)
4368c2ecf20Sopenharmony_ci		return -ENOBUFS;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	err = __netlink_policy_dump_write_attr(state, skb, pt, state->attr_idx);
4398c2ecf20Sopenharmony_ci	if (err == -ENODATA) {
4408c2ecf20Sopenharmony_ci		nla_nest_cancel(skb, policy);
4418c2ecf20Sopenharmony_ci		again = true;
4428c2ecf20Sopenharmony_ci		goto next;
4438c2ecf20Sopenharmony_ci	} else if (err) {
4448c2ecf20Sopenharmony_ci		goto nla_put_failure;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	/* finish and move state to next attribute */
4488c2ecf20Sopenharmony_ci	nla_nest_end(skb, policy);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cinext:
4518c2ecf20Sopenharmony_ci	state->attr_idx += 1;
4528c2ecf20Sopenharmony_ci	if (state->attr_idx > state->policies[state->policy_idx].maxtype) {
4538c2ecf20Sopenharmony_ci		state->attr_idx = 0;
4548c2ecf20Sopenharmony_ci		state->policy_idx++;
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (again) {
4588c2ecf20Sopenharmony_ci		if (netlink_policy_dump_finished(state))
4598c2ecf20Sopenharmony_ci			return -ENODATA;
4608c2ecf20Sopenharmony_ci		goto send_attribute;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	return 0;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cinla_put_failure:
4668c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, policy);
4678c2ecf20Sopenharmony_ci	return -ENOBUFS;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci/**
4718c2ecf20Sopenharmony_ci * netlink_policy_dump_free - free policy dump state
4728c2ecf20Sopenharmony_ci * @state: the policy dump state to free
4738c2ecf20Sopenharmony_ci *
4748c2ecf20Sopenharmony_ci * Call this from the done() method to ensure dump state is freed.
4758c2ecf20Sopenharmony_ci */
4768c2ecf20Sopenharmony_civoid netlink_policy_dump_free(struct netlink_policy_dump_state *state)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	kfree(state);
4798c2ecf20Sopenharmony_ci}
480