162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * NETLINK      Policy advertisement to userspace
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * 		Authors:	Johannes Berg <johannes@sipsolutions.net>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright 2019 Intel Corporation
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci#include <net/netlink.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define INITIAL_POLICIES_ALLOC	10
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct netlink_policy_dump_state {
1862306a36Sopenharmony_ci	unsigned int policy_idx;
1962306a36Sopenharmony_ci	unsigned int attr_idx;
2062306a36Sopenharmony_ci	unsigned int n_alloc;
2162306a36Sopenharmony_ci	struct {
2262306a36Sopenharmony_ci		const struct nla_policy *policy;
2362306a36Sopenharmony_ci		unsigned int maxtype;
2462306a36Sopenharmony_ci	} policies[];
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int add_policy(struct netlink_policy_dump_state **statep,
2862306a36Sopenharmony_ci		      const struct nla_policy *policy,
2962306a36Sopenharmony_ci		      unsigned int maxtype)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct netlink_policy_dump_state *state = *statep;
3262306a36Sopenharmony_ci	unsigned int n_alloc, i;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (!policy || !maxtype)
3562306a36Sopenharmony_ci		return 0;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	for (i = 0; i < state->n_alloc; i++) {
3862306a36Sopenharmony_ci		if (state->policies[i].policy == policy &&
3962306a36Sopenharmony_ci		    state->policies[i].maxtype == maxtype)
4062306a36Sopenharmony_ci			return 0;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci		if (!state->policies[i].policy) {
4362306a36Sopenharmony_ci			state->policies[i].policy = policy;
4462306a36Sopenharmony_ci			state->policies[i].maxtype = maxtype;
4562306a36Sopenharmony_ci			return 0;
4662306a36Sopenharmony_ci		}
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	n_alloc = state->n_alloc + INITIAL_POLICIES_ALLOC;
5062306a36Sopenharmony_ci	state = krealloc(state, struct_size(state, policies, n_alloc),
5162306a36Sopenharmony_ci			 GFP_KERNEL);
5262306a36Sopenharmony_ci	if (!state)
5362306a36Sopenharmony_ci		return -ENOMEM;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	memset(&state->policies[state->n_alloc], 0,
5662306a36Sopenharmony_ci	       flex_array_size(state, policies, n_alloc - state->n_alloc));
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	state->policies[state->n_alloc].policy = policy;
5962306a36Sopenharmony_ci	state->policies[state->n_alloc].maxtype = maxtype;
6062306a36Sopenharmony_ci	state->n_alloc = n_alloc;
6162306a36Sopenharmony_ci	*statep = state;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return 0;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/**
6762306a36Sopenharmony_ci * netlink_policy_dump_get_policy_idx - retrieve policy index
6862306a36Sopenharmony_ci * @state: the policy dump state
6962306a36Sopenharmony_ci * @policy: the policy to find
7062306a36Sopenharmony_ci * @maxtype: the policy's maxattr
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * Returns: the index of the given policy in the dump state
7362306a36Sopenharmony_ci *
7462306a36Sopenharmony_ci * Call this to find a policy index when you've added multiple and e.g.
7562306a36Sopenharmony_ci * need to tell userspace which command has which policy (by index).
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci * Note: this will WARN and return 0 if the policy isn't found, which
7862306a36Sopenharmony_ci *	 means it wasn't added in the first place, which would be an
7962306a36Sopenharmony_ci *	 internal consistency bug.
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_ciint netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state,
8262306a36Sopenharmony_ci				       const struct nla_policy *policy,
8362306a36Sopenharmony_ci				       unsigned int maxtype)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	unsigned int i;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (WARN_ON(!policy || !maxtype))
8862306a36Sopenharmony_ci                return 0;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	for (i = 0; i < state->n_alloc; i++) {
9162306a36Sopenharmony_ci		if (state->policies[i].policy == policy &&
9262306a36Sopenharmony_ci		    state->policies[i].maxtype == maxtype)
9362306a36Sopenharmony_ci			return i;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	WARN_ON(1);
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic struct netlink_policy_dump_state *alloc_state(void)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct netlink_policy_dump_state *state;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC),
10562306a36Sopenharmony_ci			GFP_KERNEL);
10662306a36Sopenharmony_ci	if (!state)
10762306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
10862306a36Sopenharmony_ci	state->n_alloc = INITIAL_POLICIES_ALLOC;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return state;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/**
11462306a36Sopenharmony_ci * netlink_policy_dump_add_policy - add a policy to the dump
11562306a36Sopenharmony_ci * @pstate: state to add to, may be reallocated, must be %NULL the first time
11662306a36Sopenharmony_ci * @policy: the new policy to add to the dump
11762306a36Sopenharmony_ci * @maxtype: the new policy's max attr type
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci * Returns: 0 on success, a negative error code otherwise.
12062306a36Sopenharmony_ci *
12162306a36Sopenharmony_ci * Call this to allocate a policy dump state, and to add policies to it. This
12262306a36Sopenharmony_ci * should be called from the dump start() callback.
12362306a36Sopenharmony_ci *
12462306a36Sopenharmony_ci * Note: on failures, any previously allocated state is freed.
12562306a36Sopenharmony_ci */
12662306a36Sopenharmony_ciint netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate,
12762306a36Sopenharmony_ci				   const struct nla_policy *policy,
12862306a36Sopenharmony_ci				   unsigned int maxtype)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct netlink_policy_dump_state *state = *pstate;
13162306a36Sopenharmony_ci	unsigned int policy_idx;
13262306a36Sopenharmony_ci	int err;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (!state) {
13562306a36Sopenharmony_ci		state = alloc_state();
13662306a36Sopenharmony_ci		if (IS_ERR(state))
13762306a36Sopenharmony_ci			return PTR_ERR(state);
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/*
14162306a36Sopenharmony_ci	 * walk the policies and nested ones first, and build
14262306a36Sopenharmony_ci	 * a linear list of them.
14362306a36Sopenharmony_ci	 */
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	err = add_policy(&state, policy, maxtype);
14662306a36Sopenharmony_ci	if (err)
14762306a36Sopenharmony_ci		goto err_try_undo;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	for (policy_idx = 0;
15062306a36Sopenharmony_ci	     policy_idx < state->n_alloc && state->policies[policy_idx].policy;
15162306a36Sopenharmony_ci	     policy_idx++) {
15262306a36Sopenharmony_ci		const struct nla_policy *policy;
15362306a36Sopenharmony_ci		unsigned int type;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		policy = state->policies[policy_idx].policy;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		for (type = 0;
15862306a36Sopenharmony_ci		     type <= state->policies[policy_idx].maxtype;
15962306a36Sopenharmony_ci		     type++) {
16062306a36Sopenharmony_ci			switch (policy[type].type) {
16162306a36Sopenharmony_ci			case NLA_NESTED:
16262306a36Sopenharmony_ci			case NLA_NESTED_ARRAY:
16362306a36Sopenharmony_ci				err = add_policy(&state,
16462306a36Sopenharmony_ci						 policy[type].nested_policy,
16562306a36Sopenharmony_ci						 policy[type].len);
16662306a36Sopenharmony_ci				if (err)
16762306a36Sopenharmony_ci					goto err_try_undo;
16862306a36Sopenharmony_ci				break;
16962306a36Sopenharmony_ci			default:
17062306a36Sopenharmony_ci				break;
17162306a36Sopenharmony_ci			}
17262306a36Sopenharmony_ci		}
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	*pstate = state;
17662306a36Sopenharmony_ci	return 0;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cierr_try_undo:
17962306a36Sopenharmony_ci	/* Try to preserve reasonable unwind semantics - if we're starting from
18062306a36Sopenharmony_ci	 * scratch clean up fully, otherwise record what we got and caller will.
18162306a36Sopenharmony_ci	 */
18262306a36Sopenharmony_ci	if (!*pstate)
18362306a36Sopenharmony_ci		netlink_policy_dump_free(state);
18462306a36Sopenharmony_ci	else
18562306a36Sopenharmony_ci		*pstate = state;
18662306a36Sopenharmony_ci	return err;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic bool
19062306a36Sopenharmony_cinetlink_policy_dump_finished(struct netlink_policy_dump_state *state)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	return state->policy_idx >= state->n_alloc ||
19362306a36Sopenharmony_ci	       !state->policies[state->policy_idx].policy;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci/**
19762306a36Sopenharmony_ci * netlink_policy_dump_loop - dumping loop indicator
19862306a36Sopenharmony_ci * @state: the policy dump state
19962306a36Sopenharmony_ci *
20062306a36Sopenharmony_ci * Returns: %true if the dump continues, %false otherwise
20162306a36Sopenharmony_ci *
20262306a36Sopenharmony_ci * Note: this frees the dump state when finishing
20362306a36Sopenharmony_ci */
20462306a36Sopenharmony_cibool netlink_policy_dump_loop(struct netlink_policy_dump_state *state)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	return !netlink_policy_dump_finished(state);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ciint netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	/* nested + type */
21262306a36Sopenharmony_ci	int common = 2 * nla_attr_size(sizeof(u32));
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	switch (pt->type) {
21562306a36Sopenharmony_ci	case NLA_UNSPEC:
21662306a36Sopenharmony_ci	case NLA_REJECT:
21762306a36Sopenharmony_ci		/* these actually don't need any space */
21862306a36Sopenharmony_ci		return 0;
21962306a36Sopenharmony_ci	case NLA_NESTED:
22062306a36Sopenharmony_ci	case NLA_NESTED_ARRAY:
22162306a36Sopenharmony_ci		/* common, policy idx, policy maxattr */
22262306a36Sopenharmony_ci		return common + 2 * nla_attr_size(sizeof(u32));
22362306a36Sopenharmony_ci	case NLA_U8:
22462306a36Sopenharmony_ci	case NLA_U16:
22562306a36Sopenharmony_ci	case NLA_U32:
22662306a36Sopenharmony_ci	case NLA_U64:
22762306a36Sopenharmony_ci	case NLA_MSECS:
22862306a36Sopenharmony_ci	case NLA_S8:
22962306a36Sopenharmony_ci	case NLA_S16:
23062306a36Sopenharmony_ci	case NLA_S32:
23162306a36Sopenharmony_ci	case NLA_S64:
23262306a36Sopenharmony_ci		/* maximum is common, u64 min/max with padding */
23362306a36Sopenharmony_ci		return common +
23462306a36Sopenharmony_ci		       2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64)));
23562306a36Sopenharmony_ci	case NLA_BITFIELD32:
23662306a36Sopenharmony_ci		return common + nla_attr_size(sizeof(u32));
23762306a36Sopenharmony_ci	case NLA_STRING:
23862306a36Sopenharmony_ci	case NLA_NUL_STRING:
23962306a36Sopenharmony_ci	case NLA_BINARY:
24062306a36Sopenharmony_ci		/* maximum is common, u32 min-length/max-length */
24162306a36Sopenharmony_ci		return common + 2 * nla_attr_size(sizeof(u32));
24262306a36Sopenharmony_ci	case NLA_FLAG:
24362306a36Sopenharmony_ci		return common;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* this should then cause a warning later */
24762306a36Sopenharmony_ci	return 0;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic int
25162306a36Sopenharmony_ci__netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
25262306a36Sopenharmony_ci				 struct sk_buff *skb,
25362306a36Sopenharmony_ci				 const struct nla_policy *pt,
25462306a36Sopenharmony_ci				 int nestattr)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	int estimate = netlink_policy_dump_attr_size_estimate(pt);
25762306a36Sopenharmony_ci	enum netlink_attribute_type type;
25862306a36Sopenharmony_ci	struct nlattr *attr;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	attr = nla_nest_start(skb, nestattr);
26162306a36Sopenharmony_ci	if (!attr)
26262306a36Sopenharmony_ci		return -ENOBUFS;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	switch (pt->type) {
26562306a36Sopenharmony_ci	default:
26662306a36Sopenharmony_ci	case NLA_UNSPEC:
26762306a36Sopenharmony_ci	case NLA_REJECT:
26862306a36Sopenharmony_ci		/* skip - use NLA_MIN_LEN to advertise such */
26962306a36Sopenharmony_ci		nla_nest_cancel(skb, attr);
27062306a36Sopenharmony_ci		return -ENODATA;
27162306a36Sopenharmony_ci	case NLA_NESTED:
27262306a36Sopenharmony_ci		type = NL_ATTR_TYPE_NESTED;
27362306a36Sopenharmony_ci		fallthrough;
27462306a36Sopenharmony_ci	case NLA_NESTED_ARRAY:
27562306a36Sopenharmony_ci		if (pt->type == NLA_NESTED_ARRAY)
27662306a36Sopenharmony_ci			type = NL_ATTR_TYPE_NESTED_ARRAY;
27762306a36Sopenharmony_ci		if (state && pt->nested_policy && pt->len &&
27862306a36Sopenharmony_ci		    (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX,
27962306a36Sopenharmony_ci				 netlink_policy_dump_get_policy_idx(state,
28062306a36Sopenharmony_ci								    pt->nested_policy,
28162306a36Sopenharmony_ci								    pt->len)) ||
28262306a36Sopenharmony_ci		     nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
28362306a36Sopenharmony_ci				 pt->len)))
28462306a36Sopenharmony_ci			goto nla_put_failure;
28562306a36Sopenharmony_ci		break;
28662306a36Sopenharmony_ci	case NLA_U8:
28762306a36Sopenharmony_ci	case NLA_U16:
28862306a36Sopenharmony_ci	case NLA_U32:
28962306a36Sopenharmony_ci	case NLA_U64:
29062306a36Sopenharmony_ci	case NLA_MSECS: {
29162306a36Sopenharmony_ci		struct netlink_range_validation range;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		if (pt->type == NLA_U8)
29462306a36Sopenharmony_ci			type = NL_ATTR_TYPE_U8;
29562306a36Sopenharmony_ci		else if (pt->type == NLA_U16)
29662306a36Sopenharmony_ci			type = NL_ATTR_TYPE_U16;
29762306a36Sopenharmony_ci		else if (pt->type == NLA_U32)
29862306a36Sopenharmony_ci			type = NL_ATTR_TYPE_U32;
29962306a36Sopenharmony_ci		else
30062306a36Sopenharmony_ci			type = NL_ATTR_TYPE_U64;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		if (pt->validation_type == NLA_VALIDATE_MASK) {
30362306a36Sopenharmony_ci			if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MASK,
30462306a36Sopenharmony_ci					      pt->mask,
30562306a36Sopenharmony_ci					      NL_POLICY_TYPE_ATTR_PAD))
30662306a36Sopenharmony_ci				goto nla_put_failure;
30762306a36Sopenharmony_ci			break;
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		nla_get_range_unsigned(pt, &range);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U,
31362306a36Sopenharmony_ci				      range.min, NL_POLICY_TYPE_ATTR_PAD) ||
31462306a36Sopenharmony_ci		    nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_U,
31562306a36Sopenharmony_ci				      range.max, NL_POLICY_TYPE_ATTR_PAD))
31662306a36Sopenharmony_ci			goto nla_put_failure;
31762306a36Sopenharmony_ci		break;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci	case NLA_S8:
32062306a36Sopenharmony_ci	case NLA_S16:
32162306a36Sopenharmony_ci	case NLA_S32:
32262306a36Sopenharmony_ci	case NLA_S64: {
32362306a36Sopenharmony_ci		struct netlink_range_validation_signed range;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		if (pt->type == NLA_S8)
32662306a36Sopenharmony_ci			type = NL_ATTR_TYPE_S8;
32762306a36Sopenharmony_ci		else if (pt->type == NLA_S16)
32862306a36Sopenharmony_ci			type = NL_ATTR_TYPE_S16;
32962306a36Sopenharmony_ci		else if (pt->type == NLA_S32)
33062306a36Sopenharmony_ci			type = NL_ATTR_TYPE_S32;
33162306a36Sopenharmony_ci		else
33262306a36Sopenharmony_ci			type = NL_ATTR_TYPE_S64;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci		nla_get_range_signed(pt, &range);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		if (nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_S,
33762306a36Sopenharmony_ci				range.min, NL_POLICY_TYPE_ATTR_PAD) ||
33862306a36Sopenharmony_ci		    nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_S,
33962306a36Sopenharmony_ci				range.max, NL_POLICY_TYPE_ATTR_PAD))
34062306a36Sopenharmony_ci			goto nla_put_failure;
34162306a36Sopenharmony_ci		break;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci	case NLA_BITFIELD32:
34462306a36Sopenharmony_ci		type = NL_ATTR_TYPE_BITFIELD32;
34562306a36Sopenharmony_ci		if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
34662306a36Sopenharmony_ci				pt->bitfield32_valid))
34762306a36Sopenharmony_ci			goto nla_put_failure;
34862306a36Sopenharmony_ci		break;
34962306a36Sopenharmony_ci	case NLA_STRING:
35062306a36Sopenharmony_ci	case NLA_NUL_STRING:
35162306a36Sopenharmony_ci	case NLA_BINARY:
35262306a36Sopenharmony_ci		if (pt->type == NLA_STRING)
35362306a36Sopenharmony_ci			type = NL_ATTR_TYPE_STRING;
35462306a36Sopenharmony_ci		else if (pt->type == NLA_NUL_STRING)
35562306a36Sopenharmony_ci			type = NL_ATTR_TYPE_NUL_STRING;
35662306a36Sopenharmony_ci		else
35762306a36Sopenharmony_ci			type = NL_ATTR_TYPE_BINARY;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		if (pt->validation_type == NLA_VALIDATE_RANGE ||
36062306a36Sopenharmony_ci		    pt->validation_type == NLA_VALIDATE_RANGE_WARN_TOO_LONG) {
36162306a36Sopenharmony_ci			struct netlink_range_validation range;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci			nla_get_range_unsigned(pt, &range);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci			if (range.min &&
36662306a36Sopenharmony_ci			    nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH,
36762306a36Sopenharmony_ci					range.min))
36862306a36Sopenharmony_ci				goto nla_put_failure;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci			if (range.max < U16_MAX &&
37162306a36Sopenharmony_ci			    nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH,
37262306a36Sopenharmony_ci					range.max))
37362306a36Sopenharmony_ci				goto nla_put_failure;
37462306a36Sopenharmony_ci		} else if (pt->len &&
37562306a36Sopenharmony_ci			   nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH,
37662306a36Sopenharmony_ci				       pt->len)) {
37762306a36Sopenharmony_ci			goto nla_put_failure;
37862306a36Sopenharmony_ci		}
37962306a36Sopenharmony_ci		break;
38062306a36Sopenharmony_ci	case NLA_FLAG:
38162306a36Sopenharmony_ci		type = NL_ATTR_TYPE_FLAG;
38262306a36Sopenharmony_ci		break;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_TYPE, type))
38662306a36Sopenharmony_ci		goto nla_put_failure;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	nla_nest_end(skb, attr);
38962306a36Sopenharmony_ci	WARN_ON(attr->nla_len > estimate);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	return 0;
39262306a36Sopenharmony_cinla_put_failure:
39362306a36Sopenharmony_ci	nla_nest_cancel(skb, attr);
39462306a36Sopenharmony_ci	return -ENOBUFS;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/**
39862306a36Sopenharmony_ci * netlink_policy_dump_write_attr - write a given attribute policy
39962306a36Sopenharmony_ci * @skb: the message skb to write to
40062306a36Sopenharmony_ci * @pt: the attribute's policy
40162306a36Sopenharmony_ci * @nestattr: the nested attribute ID to use
40262306a36Sopenharmony_ci *
40362306a36Sopenharmony_ci * Returns: 0 on success, an error code otherwise; -%ENODATA is
40462306a36Sopenharmony_ci *	    special, indicating that there's no policy data and
40562306a36Sopenharmony_ci *	    the attribute is generally rejected.
40662306a36Sopenharmony_ci */
40762306a36Sopenharmony_ciint netlink_policy_dump_write_attr(struct sk_buff *skb,
40862306a36Sopenharmony_ci				   const struct nla_policy *pt,
40962306a36Sopenharmony_ci				   int nestattr)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	return __netlink_policy_dump_write_attr(NULL, skb, pt, nestattr);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/**
41562306a36Sopenharmony_ci * netlink_policy_dump_write - write current policy dump attributes
41662306a36Sopenharmony_ci * @skb: the message skb to write to
41762306a36Sopenharmony_ci * @state: the policy dump state
41862306a36Sopenharmony_ci *
41962306a36Sopenharmony_ci * Returns: 0 on success, an error code otherwise
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_ciint netlink_policy_dump_write(struct sk_buff *skb,
42262306a36Sopenharmony_ci			      struct netlink_policy_dump_state *state)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	const struct nla_policy *pt;
42562306a36Sopenharmony_ci	struct nlattr *policy;
42662306a36Sopenharmony_ci	bool again;
42762306a36Sopenharmony_ci	int err;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cisend_attribute:
43062306a36Sopenharmony_ci	again = false;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	pt = &state->policies[state->policy_idx].policy[state->attr_idx];
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	policy = nla_nest_start(skb, state->policy_idx);
43562306a36Sopenharmony_ci	if (!policy)
43662306a36Sopenharmony_ci		return -ENOBUFS;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	err = __netlink_policy_dump_write_attr(state, skb, pt, state->attr_idx);
43962306a36Sopenharmony_ci	if (err == -ENODATA) {
44062306a36Sopenharmony_ci		nla_nest_cancel(skb, policy);
44162306a36Sopenharmony_ci		again = true;
44262306a36Sopenharmony_ci		goto next;
44362306a36Sopenharmony_ci	} else if (err) {
44462306a36Sopenharmony_ci		goto nla_put_failure;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	/* finish and move state to next attribute */
44862306a36Sopenharmony_ci	nla_nest_end(skb, policy);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cinext:
45162306a36Sopenharmony_ci	state->attr_idx += 1;
45262306a36Sopenharmony_ci	if (state->attr_idx > state->policies[state->policy_idx].maxtype) {
45362306a36Sopenharmony_ci		state->attr_idx = 0;
45462306a36Sopenharmony_ci		state->policy_idx++;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (again) {
45862306a36Sopenharmony_ci		if (netlink_policy_dump_finished(state))
45962306a36Sopenharmony_ci			return -ENODATA;
46062306a36Sopenharmony_ci		goto send_attribute;
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	return 0;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cinla_put_failure:
46662306a36Sopenharmony_ci	nla_nest_cancel(skb, policy);
46762306a36Sopenharmony_ci	return -ENOBUFS;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci/**
47162306a36Sopenharmony_ci * netlink_policy_dump_free - free policy dump state
47262306a36Sopenharmony_ci * @state: the policy dump state to free
47362306a36Sopenharmony_ci *
47462306a36Sopenharmony_ci * Call this from the done() method to ensure dump state is freed.
47562306a36Sopenharmony_ci */
47662306a36Sopenharmony_civoid netlink_policy_dump_free(struct netlink_policy_dump_state *state)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	kfree(state);
47962306a36Sopenharmony_ci}
480