| // SPDX-License-Identifier: GPL-2.0 | 
 | /* | 
 |  * NETLINK      Policy advertisement to userspace | 
 |  * | 
 |  * 		Authors:	Johannes Berg <johannes@sipsolutions.net> | 
 |  * | 
 |  * Copyright 2019 Intel Corporation | 
 |  */ | 
 |  | 
 | #include <linux/kernel.h> | 
 | #include <linux/errno.h> | 
 | #include <linux/types.h> | 
 | #include <net/netlink.h> | 
 |  | 
 | #define INITIAL_POLICIES_ALLOC	10 | 
 |  | 
 | struct netlink_policy_dump_state { | 
 | 	unsigned int policy_idx; | 
 | 	unsigned int attr_idx; | 
 | 	unsigned int n_alloc; | 
 | 	struct { | 
 | 		const struct nla_policy *policy; | 
 | 		unsigned int maxtype; | 
 | 	} policies[]; | 
 | }; | 
 |  | 
 | static int add_policy(struct netlink_policy_dump_state **statep, | 
 | 		      const struct nla_policy *policy, | 
 | 		      unsigned int maxtype) | 
 | { | 
 | 	struct netlink_policy_dump_state *state = *statep; | 
 | 	unsigned int n_alloc, i; | 
 |  | 
 | 	if (!policy || !maxtype) | 
 | 		return 0; | 
 |  | 
 | 	for (i = 0; i < state->n_alloc; i++) { | 
 | 		if (state->policies[i].policy == policy && | 
 | 		    state->policies[i].maxtype == maxtype) | 
 | 			return 0; | 
 |  | 
 | 		if (!state->policies[i].policy) { | 
 | 			state->policies[i].policy = policy; | 
 | 			state->policies[i].maxtype = maxtype; | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	n_alloc = state->n_alloc + INITIAL_POLICIES_ALLOC; | 
 | 	state = krealloc(state, struct_size(state, policies, n_alloc), | 
 | 			 GFP_KERNEL); | 
 | 	if (!state) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	memset(&state->policies[state->n_alloc], 0, | 
 | 	       flex_array_size(state, policies, n_alloc - state->n_alloc)); | 
 |  | 
 | 	state->policies[state->n_alloc].policy = policy; | 
 | 	state->policies[state->n_alloc].maxtype = maxtype; | 
 | 	state->n_alloc = n_alloc; | 
 | 	*statep = state; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /** | 
 |  * netlink_policy_dump_get_policy_idx - retrieve policy index | 
 |  * @state: the policy dump state | 
 |  * @policy: the policy to find | 
 |  * @maxtype: the policy's maxattr | 
 |  * | 
 |  * Returns: the index of the given policy in the dump state | 
 |  * | 
 |  * Call this to find a policy index when you've added multiple and e.g. | 
 |  * need to tell userspace which command has which policy (by index). | 
 |  * | 
 |  * Note: this will WARN and return 0 if the policy isn't found, which | 
 |  *	 means it wasn't added in the first place, which would be an | 
 |  *	 internal consistency bug. | 
 |  */ | 
 | int netlink_policy_dump_get_policy_idx(struct netlink_policy_dump_state *state, | 
 | 				       const struct nla_policy *policy, | 
 | 				       unsigned int maxtype) | 
 | { | 
 | 	unsigned int i; | 
 |  | 
 | 	if (WARN_ON(!policy || !maxtype)) | 
 |                 return 0; | 
 |  | 
 | 	for (i = 0; i < state->n_alloc; i++) { | 
 | 		if (state->policies[i].policy == policy && | 
 | 		    state->policies[i].maxtype == maxtype) | 
 | 			return i; | 
 | 	} | 
 |  | 
 | 	WARN_ON(1); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct netlink_policy_dump_state *alloc_state(void) | 
 | { | 
 | 	struct netlink_policy_dump_state *state; | 
 |  | 
 | 	state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC), | 
 | 			GFP_KERNEL); | 
 | 	if (!state) | 
 | 		return ERR_PTR(-ENOMEM); | 
 | 	state->n_alloc = INITIAL_POLICIES_ALLOC; | 
 |  | 
 | 	return state; | 
 | } | 
 |  | 
 | /** | 
 |  * netlink_policy_dump_add_policy - add a policy to the dump | 
 |  * @pstate: state to add to, may be reallocated, must be %NULL the first time | 
 |  * @policy: the new policy to add to the dump | 
 |  * @maxtype: the new policy's max attr type | 
 |  * | 
 |  * Returns: 0 on success, a negative error code otherwise. | 
 |  * | 
 |  * Call this to allocate a policy dump state, and to add policies to it. This | 
 |  * should be called from the dump start() callback. | 
 |  * | 
 |  * Note: on failures, any previously allocated state is freed. | 
 |  */ | 
 | int netlink_policy_dump_add_policy(struct netlink_policy_dump_state **pstate, | 
 | 				   const struct nla_policy *policy, | 
 | 				   unsigned int maxtype) | 
 | { | 
 | 	struct netlink_policy_dump_state *state = *pstate; | 
 | 	unsigned int policy_idx; | 
 | 	int err; | 
 |  | 
 | 	if (!state) { | 
 | 		state = alloc_state(); | 
 | 		if (IS_ERR(state)) | 
 | 			return PTR_ERR(state); | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * walk the policies and nested ones first, and build | 
 | 	 * a linear list of them. | 
 | 	 */ | 
 |  | 
 | 	err = add_policy(&state, policy, maxtype); | 
 | 	if (err) | 
 | 		return err; | 
 |  | 
 | 	for (policy_idx = 0; | 
 | 	     policy_idx < state->n_alloc && state->policies[policy_idx].policy; | 
 | 	     policy_idx++) { | 
 | 		const struct nla_policy *policy; | 
 | 		unsigned int type; | 
 |  | 
 | 		policy = state->policies[policy_idx].policy; | 
 |  | 
 | 		for (type = 0; | 
 | 		     type <= state->policies[policy_idx].maxtype; | 
 | 		     type++) { | 
 | 			switch (policy[type].type) { | 
 | 			case NLA_NESTED: | 
 | 			case NLA_NESTED_ARRAY: | 
 | 				err = add_policy(&state, | 
 | 						 policy[type].nested_policy, | 
 | 						 policy[type].len); | 
 | 				if (err) | 
 | 					return err; | 
 | 				break; | 
 | 			default: | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	*pstate = state; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static bool | 
 | netlink_policy_dump_finished(struct netlink_policy_dump_state *state) | 
 | { | 
 | 	return state->policy_idx >= state->n_alloc || | 
 | 	       !state->policies[state->policy_idx].policy; | 
 | } | 
 |  | 
 | /** | 
 |  * netlink_policy_dump_loop - dumping loop indicator | 
 |  * @state: the policy dump state | 
 |  * | 
 |  * Returns: %true if the dump continues, %false otherwise | 
 |  * | 
 |  * Note: this frees the dump state when finishing | 
 |  */ | 
 | bool netlink_policy_dump_loop(struct netlink_policy_dump_state *state) | 
 | { | 
 | 	return !netlink_policy_dump_finished(state); | 
 | } | 
 |  | 
 | int netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt) | 
 | { | 
 | 	/* nested + type */ | 
 | 	int common = 2 * nla_attr_size(sizeof(u32)); | 
 |  | 
 | 	switch (pt->type) { | 
 | 	case NLA_UNSPEC: | 
 | 	case NLA_REJECT: | 
 | 		/* these actually don't need any space */ | 
 | 		return 0; | 
 | 	case NLA_NESTED: | 
 | 	case NLA_NESTED_ARRAY: | 
 | 		/* common, policy idx, policy maxattr */ | 
 | 		return common + 2 * nla_attr_size(sizeof(u32)); | 
 | 	case NLA_U8: | 
 | 	case NLA_U16: | 
 | 	case NLA_U32: | 
 | 	case NLA_U64: | 
 | 	case NLA_MSECS: | 
 | 	case NLA_S8: | 
 | 	case NLA_S16: | 
 | 	case NLA_S32: | 
 | 	case NLA_S64: | 
 | 		/* maximum is common, u64 min/max with padding */ | 
 | 		return common + | 
 | 		       2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64))); | 
 | 	case NLA_BITFIELD32: | 
 | 		return common + nla_attr_size(sizeof(u32)); | 
 | 	case NLA_STRING: | 
 | 	case NLA_NUL_STRING: | 
 | 	case NLA_BINARY: | 
 | 		/* maximum is common, u32 min-length/max-length */ | 
 | 		return common + 2 * nla_attr_size(sizeof(u32)); | 
 | 	case NLA_FLAG: | 
 | 		return common; | 
 | 	} | 
 |  | 
 | 	/* this should then cause a warning later */ | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int | 
 | __netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state, | 
 | 				 struct sk_buff *skb, | 
 | 				 const struct nla_policy *pt, | 
 | 				 int nestattr) | 
 | { | 
 | 	int estimate = netlink_policy_dump_attr_size_estimate(pt); | 
 | 	enum netlink_attribute_type type; | 
 | 	struct nlattr *attr; | 
 |  | 
 | 	attr = nla_nest_start(skb, nestattr); | 
 | 	if (!attr) | 
 | 		return -ENOBUFS; | 
 |  | 
 | 	switch (pt->type) { | 
 | 	default: | 
 | 	case NLA_UNSPEC: | 
 | 	case NLA_REJECT: | 
 | 		/* skip - use NLA_MIN_LEN to advertise such */ | 
 | 		nla_nest_cancel(skb, attr); | 
 | 		return -ENODATA; | 
 | 	case NLA_NESTED: | 
 | 		type = NL_ATTR_TYPE_NESTED; | 
 | 		fallthrough; | 
 | 	case NLA_NESTED_ARRAY: | 
 | 		if (pt->type == NLA_NESTED_ARRAY) | 
 | 			type = NL_ATTR_TYPE_NESTED_ARRAY; | 
 | 		if (state && pt->nested_policy && pt->len && | 
 | 		    (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX, | 
 | 				 netlink_policy_dump_get_policy_idx(state, | 
 | 								    pt->nested_policy, | 
 | 								    pt->len)) || | 
 | 		     nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE, | 
 | 				 pt->len))) | 
 | 			goto nla_put_failure; | 
 | 		break; | 
 | 	case NLA_U8: | 
 | 	case NLA_U16: | 
 | 	case NLA_U32: | 
 | 	case NLA_U64: | 
 | 	case NLA_MSECS: { | 
 | 		struct netlink_range_validation range; | 
 |  | 
 | 		if (pt->type == NLA_U8) | 
 | 			type = NL_ATTR_TYPE_U8; | 
 | 		else if (pt->type == NLA_U16) | 
 | 			type = NL_ATTR_TYPE_U16; | 
 | 		else if (pt->type == NLA_U32) | 
 | 			type = NL_ATTR_TYPE_U32; | 
 | 		else | 
 | 			type = NL_ATTR_TYPE_U64; | 
 |  | 
 | 		if (pt->validation_type == NLA_VALIDATE_MASK) { | 
 | 			if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MASK, | 
 | 					      pt->mask, | 
 | 					      NL_POLICY_TYPE_ATTR_PAD)) | 
 | 				goto nla_put_failure; | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		nla_get_range_unsigned(pt, &range); | 
 |  | 
 | 		if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U, | 
 | 				      range.min, NL_POLICY_TYPE_ATTR_PAD) || | 
 | 		    nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_U, | 
 | 				      range.max, NL_POLICY_TYPE_ATTR_PAD)) | 
 | 			goto nla_put_failure; | 
 | 		break; | 
 | 	} | 
 | 	case NLA_S8: | 
 | 	case NLA_S16: | 
 | 	case NLA_S32: | 
 | 	case NLA_S64: { | 
 | 		struct netlink_range_validation_signed range; | 
 |  | 
 | 		if (pt->type == NLA_S8) | 
 | 			type = NL_ATTR_TYPE_S8; | 
 | 		else if (pt->type == NLA_S16) | 
 | 			type = NL_ATTR_TYPE_S16; | 
 | 		else if (pt->type == NLA_S32) | 
 | 			type = NL_ATTR_TYPE_S32; | 
 | 		else | 
 | 			type = NL_ATTR_TYPE_S64; | 
 |  | 
 | 		nla_get_range_signed(pt, &range); | 
 |  | 
 | 		if (nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_S, | 
 | 				range.min, NL_POLICY_TYPE_ATTR_PAD) || | 
 | 		    nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_S, | 
 | 				range.max, NL_POLICY_TYPE_ATTR_PAD)) | 
 | 			goto nla_put_failure; | 
 | 		break; | 
 | 	} | 
 | 	case NLA_BITFIELD32: | 
 | 		type = NL_ATTR_TYPE_BITFIELD32; | 
 | 		if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK, | 
 | 				pt->bitfield32_valid)) | 
 | 			goto nla_put_failure; | 
 | 		break; | 
 | 	case NLA_STRING: | 
 | 	case NLA_NUL_STRING: | 
 | 	case NLA_BINARY: | 
 | 		if (pt->type == NLA_STRING) | 
 | 			type = NL_ATTR_TYPE_STRING; | 
 | 		else if (pt->type == NLA_NUL_STRING) | 
 | 			type = NL_ATTR_TYPE_NUL_STRING; | 
 | 		else | 
 | 			type = NL_ATTR_TYPE_BINARY; | 
 |  | 
 | 		if (pt->validation_type == NLA_VALIDATE_RANGE || | 
 | 		    pt->validation_type == NLA_VALIDATE_RANGE_WARN_TOO_LONG) { | 
 | 			struct netlink_range_validation range; | 
 |  | 
 | 			nla_get_range_unsigned(pt, &range); | 
 |  | 
 | 			if (range.min && | 
 | 			    nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH, | 
 | 					range.min)) | 
 | 				goto nla_put_failure; | 
 |  | 
 | 			if (range.max < U16_MAX && | 
 | 			    nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, | 
 | 					range.max)) | 
 | 				goto nla_put_failure; | 
 | 		} else if (pt->len && | 
 | 			   nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, | 
 | 				       pt->len)) { | 
 | 			goto nla_put_failure; | 
 | 		} | 
 | 		break; | 
 | 	case NLA_FLAG: | 
 | 		type = NL_ATTR_TYPE_FLAG; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_TYPE, type)) | 
 | 		goto nla_put_failure; | 
 |  | 
 | 	nla_nest_end(skb, attr); | 
 | 	WARN_ON(attr->nla_len > estimate); | 
 |  | 
 | 	return 0; | 
 | nla_put_failure: | 
 | 	nla_nest_cancel(skb, attr); | 
 | 	return -ENOBUFS; | 
 | } | 
 |  | 
 | /** | 
 |  * netlink_policy_dump_write_attr - write a given attribute policy | 
 |  * @skb: the message skb to write to | 
 |  * @pt: the attribute's policy | 
 |  * @nestattr: the nested attribute ID to use | 
 |  * | 
 |  * Returns: 0 on success, an error code otherwise; -%ENODATA is | 
 |  *	    special, indicating that there's no policy data and | 
 |  *	    the attribute is generally rejected. | 
 |  */ | 
 | int netlink_policy_dump_write_attr(struct sk_buff *skb, | 
 | 				   const struct nla_policy *pt, | 
 | 				   int nestattr) | 
 | { | 
 | 	return __netlink_policy_dump_write_attr(NULL, skb, pt, nestattr); | 
 | } | 
 |  | 
 | /** | 
 |  * netlink_policy_dump_write - write current policy dump attributes | 
 |  * @skb: the message skb to write to | 
 |  * @state: the policy dump state | 
 |  * | 
 |  * Returns: 0 on success, an error code otherwise | 
 |  */ | 
 | int netlink_policy_dump_write(struct sk_buff *skb, | 
 | 			      struct netlink_policy_dump_state *state) | 
 | { | 
 | 	const struct nla_policy *pt; | 
 | 	struct nlattr *policy; | 
 | 	bool again; | 
 | 	int err; | 
 |  | 
 | send_attribute: | 
 | 	again = false; | 
 |  | 
 | 	pt = &state->policies[state->policy_idx].policy[state->attr_idx]; | 
 |  | 
 | 	policy = nla_nest_start(skb, state->policy_idx); | 
 | 	if (!policy) | 
 | 		return -ENOBUFS; | 
 |  | 
 | 	err = __netlink_policy_dump_write_attr(state, skb, pt, state->attr_idx); | 
 | 	if (err == -ENODATA) { | 
 | 		nla_nest_cancel(skb, policy); | 
 | 		again = true; | 
 | 		goto next; | 
 | 	} else if (err) { | 
 | 		goto nla_put_failure; | 
 | 	} | 
 |  | 
 | 	/* finish and move state to next attribute */ | 
 | 	nla_nest_end(skb, policy); | 
 |  | 
 | next: | 
 | 	state->attr_idx += 1; | 
 | 	if (state->attr_idx > state->policies[state->policy_idx].maxtype) { | 
 | 		state->attr_idx = 0; | 
 | 		state->policy_idx++; | 
 | 	} | 
 |  | 
 | 	if (again) { | 
 | 		if (netlink_policy_dump_finished(state)) | 
 | 			return -ENODATA; | 
 | 		goto send_attribute; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 |  | 
 | nla_put_failure: | 
 | 	nla_nest_cancel(skb, policy); | 
 | 	return -ENOBUFS; | 
 | } | 
 |  | 
 | /** | 
 |  * netlink_policy_dump_free - free policy dump state | 
 |  * @state: the policy dump state to free | 
 |  * | 
 |  * Call this from the done() method to ensure dump state is freed. | 
 |  */ | 
 | void netlink_policy_dump_free(struct netlink_policy_dump_state *state) | 
 | { | 
 | 	kfree(state); | 
 | } |