Messages in this thread Patch in this message | | | From | Shay Drory <> | Subject | [PATCH net-next 1/4] net netlink: Introduce NLA_BITFIELD type | Date | Tue, 22 Feb 2022 12:58:09 +0200 |
| |
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 --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
| |