[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20220222105812.18668-2-shayd@nvidia.com>
Date: Tue, 22 Feb 2022 12:58:09 +0200
From: Shay Drory <shayd@...dia.com>
To: "David S . Miller" <davem@...emloft.net>,
Jakub Kicinski <kuba@...nel.org>
Cc: jiri@...dia.com, saeedm@...dia.com, parav@...dia.com,
netdev@...r.kernel.org, linux-kernel@...r.kernel.org,
Shay Drory <shayd@...dia.com>, Moshe Shemesh <moshe@...dia.com>
Subject: [PATCH net-next 1/4] net netlink: Introduce NLA_BITFIELD type
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@...dia.com>
Reviewed-by: Moshe Shemesh <moshe@...dia.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 --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;
--
2.21.3
Powered by blists - more mailing lists