diff mbox series

[net-next,1/4] net netlink: Introduce NLA_BITFIELD type

Message ID 20220222105812.18668-2-shayd@nvidia.com (mailing list archive)
State Rejected
Delegated to: Netdev Maintainers
Headers show
Series devlink: Introduce cpu_affinity command | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 5629 this patch: 5629
netdev/cc_maintainers warning 4 maintainers not CCed: jacob.e.keller@intel.com chenli@uniontech.com thunder.leizhen@huawei.com maze@google.com
netdev/build_clang success Errors and warnings before: 836 this patch: 836
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 5784 this patch: 5784
netdev/checkpatch warning CHECK: No space is necessary after a cast WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns
netdev/kdoc fail Errors and warnings before: 18 this patch: 19
netdev/source_inline success Was 0 now: 0

Commit Message

Shay Drori Feb. 22, 2022, 10:58 a.m. UTC
Generic bitfield attribute content sent to the kernel by user.
With this netlink attr type the user can either set or unset a
bitmap in the kernel.

This attribute is an extension (dynamic array) of NLA_BITFIELD32,
and have similar checks and policies.

Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
---
 include/net/netlink.h        |  30 ++++++++
 include/uapi/linux/netlink.h |  10 +++
 lib/nlattr.c                 | 145 ++++++++++++++++++++++++++++++++++-
 net/netlink/policy.c         |   4 +
 4 files changed, 185 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/include/net/netlink.h b/include/net/netlink.h
index 7a2a9d3144ba..52a0bcccae36 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -180,6 +180,7 @@  enum {
 	NLA_S32,
 	NLA_S64,
 	NLA_BITFIELD32,
+	NLA_BITFIELD,
 	NLA_REJECT,
 	__NLA_TYPE_MAX,
 };
@@ -235,12 +236,16 @@  enum nla_policy_validation {
  *                         given type fits, using it verifies minimum length
  *                         just like "All other"
  *    NLA_BITFIELD32       Unused
+ *    NLA_BITFIELD         Maximum length of attribute payload
  *    NLA_REJECT           Unused
  *    All other            Minimum length of attribute payload
  *
  * Meaning of validation union:
  *    NLA_BITFIELD32       This is a 32-bit bitmap/bitselector attribute and
  *                         `bitfield32_valid' is the u32 value of valid flags
+ *    NLA_BITFIELD         This is a dynamic array of 32-bit bitmap/bitselector
+ *                         attribute and `arr_bitfield32_valid' is the u32
+ *                         values array of valid flags.
  *    NLA_REJECT           This attribute is always rejected and `reject_message'
  *                         may point to a string to report as the error instead
  *                         of the generic one in extended ACK.
@@ -318,6 +323,7 @@  struct nla_policy {
 	u16		len;
 	union {
 		const u32 bitfield32_valid;
+		const u32 *arr_bitfield32_valid;
 		const u32 mask;
 		const char *reject_message;
 		const struct nla_policy *nested_policy;
@@ -363,6 +369,8 @@  struct nla_policy {
 	_NLA_POLICY_NESTED_ARRAY(ARRAY_SIZE(policy) - 1, policy)
 #define NLA_POLICY_BITFIELD32(valid) \
 	{ .type = NLA_BITFIELD32, .bitfield32_valid = valid }
+#define NLA_POLICY_BITFIELD(valid, size) \
+	{ .type = NLA_BITFIELD, .arr_bitfield32_valid = valid, .len = size }
 
 #define __NLA_IS_UINT_TYPE(tp)						\
 	(tp == NLA_U8 || tp == NLA_U16 || tp == NLA_U32 || tp == NLA_U64)
@@ -1545,6 +1553,19 @@  static inline int nla_put_bitfield32(struct sk_buff *skb, int attrtype,
 	return nla_put(skb, attrtype, sizeof(tmp), &tmp);
 }
 
+/**
+ * nla_put_bitfield - Add a bitfield netlink attribute to a socket buffer
+ * @skb: socket buffer to add attribute to
+ * @attrtype: attribute type
+ * @bitfield: bitfield
+ */
+static inline int nla_put_bitfield(struct sk_buff *skb, int attrtype,
+				   const struct nla_bitfield *bitfield)
+{
+	return nla_put(skb, attrtype, bitfield->size * sizeof(struct nla_bitfield32)
+		       + sizeof(*bitfield), bitfield);
+}
+
 /**
  * nla_get_u32 - return payload of u32 attribute
  * @nla: u32 netlink attribute
@@ -1738,6 +1759,15 @@  static inline struct nla_bitfield32 nla_get_bitfield32(const struct nlattr *nla)
 	return tmp;
 }
 
+struct nla_bitfield *nla_bitfield_alloc(__u64 nbits);
+void nla_bitfield_free(struct nla_bitfield *bitfield);
+void nla_bitfield_to_bitmap(unsigned long *bitmap,
+			    struct nla_bitfield *bitfield);
+void nla_bitfield_from_bitmap(struct nla_bitfield *bitfield,
+			      unsigned long *bitmap, __u64 bitmap_nbits);
+bool nla_bitfield_len_is_valid(struct nla_bitfield *bitfield, size_t user_len);
+bool nla_bitfield_nbits_valid(struct nla_bitfield *bitfield, size_t nbits);
+
 /**
  * nla_memdup - duplicate attribute memory (kmemdup)
  * @src: netlink attribute to duplicate from
diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
index 4c0cde075c27..a11bb91e3386 100644
--- a/include/uapi/linux/netlink.h
+++ b/include/uapi/linux/netlink.h
@@ -252,6 +252,14 @@  struct nla_bitfield32 {
 	__u32 selector;
 };
 
+/* Generic bitmap attribute content sent to the kernel.
+ * The size is the number of elements in the array.
+ */
+struct nla_bitfield {
+	__u64 size;
+	struct nla_bitfield32 data[0];
+};
+
 /*
  * policy descriptions - it's specific to each family how this is used
  * Normally, it should be retrieved via a dump inside another attribute
@@ -283,6 +291,7 @@  struct nla_bitfield32 {
  *	entry has attributes again, the policy for those inner ones
  *	and the corresponding maxtype may be specified.
  * @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute
+ * @NL_ATTR_TYPE_BITFIELD: &struct nla_bitfield attribute
  */
 enum netlink_attribute_type {
 	NL_ATTR_TYPE_INVALID,
@@ -307,6 +316,7 @@  enum netlink_attribute_type {
 	NL_ATTR_TYPE_NESTED_ARRAY,
 
 	NL_ATTR_TYPE_BITFIELD32,
+	NL_ATTR_TYPE_BITFIELD,
 };
 
 /**
diff --git a/lib/nlattr.c b/lib/nlattr.c
index 86029ad5ead4..6d20bf38850b 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -58,11 +58,9 @@  static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype,
 				struct netlink_ext_ack *extack,
 				struct nlattr **tb, unsigned int depth);
 
-static int validate_nla_bitfield32(const struct nlattr *nla,
-				   const u32 valid_flags_mask)
+static int validate_bitfield32(const struct nla_bitfield32 *bf,
+			       const u32 valid_flags_mask)
 {
-	const struct nla_bitfield32 *bf = nla_data(nla);
-
 	if (!valid_flags_mask)
 		return -EINVAL;
 
@@ -81,6 +79,33 @@  static int validate_nla_bitfield32(const struct nlattr *nla,
 	return 0;
 }
 
+static int validate_nla_bitfield32(const struct nlattr *nla,
+				   const u32 valid_flags_mask)
+{
+	const struct nla_bitfield32 *bf = nla_data(nla);
+
+	return validate_bitfield32(bf, valid_flags_mask);
+}
+
+static int validate_nla_bitfield(const struct nlattr *nla,
+				 const u32 *valid_flags_masks,
+				 const u16 nbits)
+{
+	struct nla_bitfield *bf = nla_data(nla);
+	int err;
+	int i;
+
+	if (!nla_bitfield_len_is_valid(bf, nla_len(nla)) ||
+	    !nla_bitfield_nbits_valid(bf, nbits))
+		return -EINVAL;
+	for (i = 0; i < bf->size; i++) {
+		err = validate_bitfield32(&bf->data[i], valid_flags_masks[i]);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
 static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
 			      const struct nla_policy *policy,
 			      struct netlink_ext_ack *extack,
@@ -422,6 +447,12 @@  static int validate_nla(const struct nlattr *nla, int maxtype,
 			goto out_err;
 		break;
 
+	case NLA_BITFIELD:
+		err = validate_nla_bitfield(nla, pt->arr_bitfield32_valid, pt->len);
+		if (err)
+			goto out_err;
+		break;
+
 	case NLA_NUL_STRING:
 		if (pt->len)
 			minlen = min_t(int, attrlen, pt->len + 1);
@@ -839,6 +870,112 @@  int nla_strcmp(const struct nlattr *nla, const char *str)
 }
 EXPORT_SYMBOL(nla_strcmp);
 
+/**
+ * nla_bitfield_alloc - Alloc struct nla_bitfield
+ * @nbits: number of bits to accommodate
+ */
+struct nla_bitfield *nla_bitfield_alloc(__u64 nbits)
+{
+	struct nla_bitfield *bitfield;
+	size_t bitfield_size;
+	size_t bitfield_len;
+
+	bitfield_len = DIV_ROUND_UP(nbits, BITS_PER_TYPE(u32));
+	bitfield_size = bitfield_len * sizeof(struct nla_bitfield32) +
+		sizeof(*bitfield);
+	bitfield = kzalloc(bitfield_size, GFP_KERNEL);
+	if (bitfield)
+		bitfield->size = bitfield_len;
+	return bitfield;
+}
+EXPORT_SYMBOL(nla_bitfield_alloc);
+
+/**
+ * nla_bitfield_free - Free struct nla_bitfield
+ * @bitfield: the bitfield to free
+ */
+void nla_bitfield_free(struct nla_bitfield *bitfield)
+{
+	kfree(bitfield);
+}
+EXPORT_SYMBOL(nla_bitfield_free);
+
+/**
+ * nla_bitfield_to_bitmap - Convert bitfield to bitmap
+ * @bitmap: bitmap to copy to (dst)
+ * @bitfield: bitfield to be copied (src)
+ */
+void nla_bitfield_to_bitmap(unsigned long *bitmap,
+			    struct nla_bitfield *bitfield)
+{
+	int i, j;
+	u32 tmp;
+
+	for (i = 0; i < bitfield->size; i++) {
+		tmp = bitfield->data[i].value & bitfield->data[i].selector;
+		for (j = 0; j < BITS_PER_TYPE(u32); j++)
+			if (tmp & (1 << j))
+				set_bit(j + i * BITS_PER_TYPE(u32), bitmap);
+	}
+}
+EXPORT_SYMBOL(nla_bitfield_to_bitmap);
+
+/**
+ * nla_bitfield_from_bitmap - Convert bitmap to bitfield
+ * @bitfield: bitfield to copy to (dst)
+ * @bitmap: bitmap to be copied (src)
+ * @bitmap_nbits: len of bitmap
+ */
+void nla_bitfield_from_bitmap(struct nla_bitfield *bitfield,
+			      unsigned long *bitmap, __u64 bitmap_nbits)
+{
+	long size;
+	int i, j;
+
+	size = DIV_ROUND_UP(bitmap_nbits, BITS_PER_TYPE(u32));
+	for (i = 0; i < size; i++) {
+		for (j = 0; j < min_t(__u64, bitmap_nbits, BITS_PER_TYPE(u32)); j++)
+			if (test_bit(j + i * BITS_PER_TYPE(u32), bitmap))
+				bitfield->data[i].value |= 1 << j;
+		bitfield->data[i].selector = bitmap_nbits >= BITS_PER_TYPE(u32) ?
+			UINT_MAX : (1 << bitmap_nbits) - 1;
+		bitmap_nbits -= BITS_PER_TYPE(u32);
+	}
+}
+EXPORT_SYMBOL(nla_bitfield_from_bitmap);
+
+/**
+ * nla_bitfield_len_is_valid - validate the len of the bitfield
+ * @bitfield: bitfield to validate
+ * @user_len: len of the nla.
+ */
+bool nla_bitfield_len_is_valid(struct nla_bitfield *bitfield, size_t user_len)
+{
+	return !(user_len % sizeof(bitfield->data[0]) ||
+		 sizeof(bitfield->data[0]) * bitfield->size +
+		 sizeof(*bitfield) != user_len);
+}
+EXPORT_SYMBOL(nla_bitfield_len_is_valid);
+
+/**
+ * nla_bitfield_nbits_valid - validate the len of the bitfield vs a given nbits
+ * @bitfield: bitfield to validate
+ * @nbits: number of bits the user wants to use.
+ */
+bool nla_bitfield_nbits_valid(struct nla_bitfield *bitfield, size_t nbits)
+{
+	u32 *last_value = &bitfield->data[bitfield->size - 1].value;
+	u32 last_bit;
+
+	if (BITS_PER_TYPE(u32) * (bitfield->size - 1) > nbits)
+		return false;
+
+	nbits -= BITS_PER_TYPE(u32) * (bitfield->size - 1);
+	last_bit = find_last_bit((unsigned long *)last_value, BITS_PER_TYPE(u32));
+	return last_bit == BITS_PER_TYPE(u32) ? true : last_bit <= nbits - 1;
+}
+EXPORT_SYMBOL(nla_bitfield_nbits_valid);
+
 #ifdef CONFIG_NET
 /**
  * __nla_reserve - reserve room for attribute on the skb
diff --git a/net/netlink/policy.c b/net/netlink/policy.c
index 8d7c900e27f4..c9fffb3b8045 100644
--- a/net/netlink/policy.c
+++ b/net/netlink/policy.c
@@ -227,6 +227,7 @@  int netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt)
 	case NLA_STRING:
 	case NLA_NUL_STRING:
 	case NLA_BINARY:
+	case NLA_BITFIELD:
 		/* maximum is common, u32 min-length/max-length */
 		return common + 2 * nla_attr_size(sizeof(u32));
 	case NLA_FLAG:
@@ -338,11 +339,14 @@  __netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
 		break;
 	case NLA_STRING:
 	case NLA_NUL_STRING:
+	case NLA_BITFIELD:
 	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 if (pt->type == NLA_BITFIELD)
+			type = NL_ATTR_TYPE_BITFIELD;
 		else
 			type = NL_ATTR_TYPE_BINARY;