diff mbox

[net-next,v11,1/4] net netlink: Add new type NLA_BITFIELD_32

Message ID 1500860146-26970-2-git-send-email-jhs@emojatatu.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Jamal Hadi Salim July 24, 2017, 1:35 a.m. UTC
From: Jamal Hadi Salim <jhs@mojatatu.com>

Generic bitflags attribute content sent to the kernel by user.
With this type the user can either set or unset a flag in the
kernel.

The nla_value is a bitmap that defines the values being set
The nla_selector is a bitmask that defines which value is legit.

A check is made to ensure the rules that a kernel subsystem always
conforms to bitflags the kernel already knows about. i.e
if the user tries to set a bit flag that is not understood then
the _it will be rejected_.

In the most basic form, the user specifies the attribute policy as:
[ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data = &myvalidflags },

where myvalidflags is the bit mask of the flags the kernel understands.

If the user _does not_ provide myvalidflags then the attribute will
also be rejected.

Examples:
nla_value = 0x0, and nla_selector = 0x1
implies we are selecting bit 1 and we want to set its value to 0.

nla_value = 0x2, and nla_selector = 0x2
implies we are selecting bit 2 and we want to set its value to 1.

Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
 include/net/netlink.h        |  4 ++++
 include/uapi/linux/netlink.h | 17 +++++++++++++++++
 lib/nlattr.c                 | 21 +++++++++++++++++++++
 3 files changed, 42 insertions(+)

Comments

Jiri Pirko July 24, 2017, 11:14 a.m. UTC | #1
Mon, Jul 24, 2017 at 03:35:43AM CEST, jhs@mojatatu.com wrote:
>From: Jamal Hadi Salim <jhs@mojatatu.com>
>
>Generic bitflags attribute content sent to the kernel by user.
>With this type the user can either set or unset a flag in the
>kernel.
>
>The nla_value is a bitmap that defines the values being set
>The nla_selector is a bitmask that defines which value is legit.
>
>A check is made to ensure the rules that a kernel subsystem always
>conforms to bitflags the kernel already knows about. i.e
>if the user tries to set a bit flag that is not understood then
>the _it will be rejected_.
>
>In the most basic form, the user specifies the attribute policy as:
>[ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data = &myvalidflags },
>
>where myvalidflags is the bit mask of the flags the kernel understands.
>
>If the user _does not_ provide myvalidflags then the attribute will
>also be rejected.
>
>Examples:
>nla_value = 0x0, and nla_selector = 0x1
>implies we are selecting bit 1 and we want to set its value to 0.
>
>nla_value = 0x2, and nla_selector = 0x2
>implies we are selecting bit 2 and we want to set its value to 1.
>
>Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
>---
> include/net/netlink.h        |  4 ++++
> include/uapi/linux/netlink.h | 17 +++++++++++++++++
> lib/nlattr.c                 | 21 +++++++++++++++++++++
> 3 files changed, 42 insertions(+)
>
>diff --git a/include/net/netlink.h b/include/net/netlink.h
>index ef8e6c3..e33d1fb 100644
>--- a/include/net/netlink.h
>+++ b/include/net/netlink.h
>@@ -178,6 +178,7 @@ enum {
> 	NLA_S16,
> 	NLA_S32,
> 	NLA_S64,
>+	NLA_BITFIELD_32,
> 	__NLA_TYPE_MAX,
> };
> 
>@@ -206,6 +207,7 @@ enum {
>  *    NLA_MSECS            Leaving the length field zero will verify the
>  *                         given type fits, using it verifies minimum length
>  *                         just like "All other"
>+ *    NLA_BITFIELD_32      A 32-bit bitmap/bitselector attribute
>  *    All other            Minimum length of attribute payload
>  *
>  * Example:
>@@ -213,11 +215,13 @@ enum {
>  * 	[ATTR_FOO] = { .type = NLA_U16 },
>  *	[ATTR_BAR] = { .type = NLA_STRING, .len = BARSIZ },
>  *	[ATTR_BAZ] = { .len = sizeof(struct mystruct) },
>+ *	[ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data = &myvalidflags },
>  * };
>  */
> struct nla_policy {
> 	u16		type;
> 	u16		len;
>+	void            *validation_data;
> };
> 
> /**
>diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
>index f86127a..0ac05a6 100644
>--- a/include/uapi/linux/netlink.h
>+++ b/include/uapi/linux/netlink.h
>@@ -226,5 +226,22 @@ struct nlattr {
> #define NLA_ALIGN(len)		(((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
> #define NLA_HDRLEN		((int) NLA_ALIGN(sizeof(struct nlattr)))
> 
>+/* Generic 32 bitflags attribute content sent to the kernel.
>+ *
>+ * The nla_value is a bitmap that defines the values being set
>+ * The nla_selector is a bitmask that defines which value is legit
>+ *
>+ * Examples:
>+ *  nla_value = 0x0, and nla_selector = 0x1
>+ *  implies we are selecting bit 1 and we want to set its value to 0.
>+ *
>+ *  nla_value = 0x2, and nla_selector = 0x2
>+ *  implies we are selecting bit 2 and we want to set its value to 1.
>+ *
>+ */
>+struct nla_bitfield_32 {
>+	__u32 nla_value;
>+	__u32 nla_selector;

I would just have "value" and "selector" here. "nla_" prefix indicates
netlink attrubute, like in struct nlattr - nla_type, nla_len,
and that is wrong indication. 



>+};
> 
> #endif /* _UAPI__LINUX_NETLINK_H */
>diff --git a/lib/nlattr.c b/lib/nlattr.c
>index fb52435..c8aad7e 100644
>--- a/lib/nlattr.c
>+++ b/lib/nlattr.c
>@@ -27,6 +27,20 @@
> 	[NLA_S64]	= sizeof(s64),
> };
> 
>+static int validate_nla_bitfield_32(const struct nlattr *nla, void *valid_data)

This should be:
static int validate_nla_bitfield_32(const struct nlattr *nla, u32 *valid_flags_allowed)


Other than this 2 nits, this looks good.



>+{
>+	const struct nla_bitfield_32 *nbf = nla_data(nla);
>+	u32 *valid_flags_mask = valid_data;
>+
>+	if (!valid_data)
>+		return -EINVAL;
>+
>+	if (nbf->nla_value & ~*valid_flags_mask)
>+		return -EINVAL;
>+
>+	return 0;
>+}
>+
> static int validate_nla(const struct nlattr *nla, int maxtype,
> 			const struct nla_policy *policy)
> {
>@@ -46,6 +60,13 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
> 			return -ERANGE;
> 		break;
> 
>+	case NLA_BITFIELD_32:
>+		if (attrlen != sizeof(struct nla_bitfield_32))
>+			return -ERANGE;
>+
>+		return validate_nla_bitfield_32(nla, pt->validation_data);
>+		break;
>+
> 	case NLA_NUL_STRING:
> 		if (pt->len)
> 			minlen = min_t(int, attrlen, pt->len + 1);
>-- 
>1.9.1
>
Jiri Pirko July 24, 2017, 11:18 a.m. UTC | #2
Mon, Jul 24, 2017 at 03:35:43AM CEST, jhs@mojatatu.com wrote:
>From: Jamal Hadi Salim <jhs@mojatatu.com>
>
>Generic bitflags attribute content sent to the kernel by user.
>With this type the user can either set or unset a flag in the
>kernel.
>
>The nla_value is a bitmap that defines the values being set
>The nla_selector is a bitmask that defines which value is legit.
>
>A check is made to ensure the rules that a kernel subsystem always
>conforms to bitflags the kernel already knows about. i.e
>if the user tries to set a bit flag that is not understood then
>the _it will be rejected_.
>
>In the most basic form, the user specifies the attribute policy as:
>[ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data = &myvalidflags },
>
>where myvalidflags is the bit mask of the flags the kernel understands.
>
>If the user _does not_ provide myvalidflags then the attribute will
>also be rejected.
>
>Examples:
>nla_value = 0x0, and nla_selector = 0x1
>implies we are selecting bit 1 and we want to set its value to 0.
>
>nla_value = 0x2, and nla_selector = 0x2
>implies we are selecting bit 2 and we want to set its value to 1.

Oh, 2 more things:

[...]


>@@ -46,6 +60,13 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
> 			return -ERANGE;
> 		break;
> 
>+	case NLA_BITFIELD_32:

Now that I'm looking at it, perhaps just "NLA_BITFIELD32" looks nicer
and aligns with "NLA_U32" and others.



>+		if (attrlen != sizeof(struct nla_bitfield_32))
>+			return -ERANGE;
>+
>+		return validate_nla_bitfield_32(nla, pt->validation_data);
>+		break;

Remove the pointless "break" from here.
Jamal Hadi Salim July 25, 2017, 11:14 a.m. UTC | #3
On 17-07-24 07:14 AM, Jiri Pirko wrote:
> Mon, Jul 24, 2017 at 03:35:43AM CEST, jhs@mojatatu.com wrote:
>> From: Jamal Hadi Salim <jhs@mojatatu.com>
>>
>> Generic bitflags attribute content sent to the kernel by user.
>> With this type the user can either set or unset a flag in the
>> kernel.
>>
>> The nla_value is a bitmap that defines the values being set
>> The nla_selector is a bitmask that defines which value is legit.
>>
>> A check is made to ensure the rules that a kernel subsystem always
>> conforms to bitflags the kernel already knows about. i.e
>> if the user tries to set a bit flag that is not understood then
>> the _it will be rejected_.
>>
>> In the most basic form, the user specifies the attribute policy as:
>> [ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data = &myvalidflags },
>>
>> where myvalidflags is the bit mask of the flags the kernel understands.
>>
>> If the user _does not_ provide myvalidflags then the attribute will
>> also be rejected.
>>
>> Examples:
>> nla_value = 0x0, and nla_selector = 0x1
>> implies we are selecting bit 1 and we want to set its value to 0.
>>
>> nla_value = 0x2, and nla_selector = 0x2
>> implies we are selecting bit 2 and we want to set its value to 1.
>>
>> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
>> ---
>> include/net/netlink.h        |  4 ++++
>> include/uapi/linux/netlink.h | 17 +++++++++++++++++
>> lib/nlattr.c                 | 21 +++++++++++++++++++++
>> 3 files changed, 42 insertions(+)
>>
>> diff --git a/include/net/netlink.h b/include/net/netlink.h
>> index ef8e6c3..e33d1fb 100644
>> --- a/include/net/netlink.h
>> +++ b/include/net/netlink.h
>> @@ -178,6 +178,7 @@ enum {
>> 	NLA_S16,
>> 	NLA_S32,
>> 	NLA_S64,
>> +	NLA_BITFIELD_32,
>> 	__NLA_TYPE_MAX,
>> };
>>
>> @@ -206,6 +207,7 @@ enum {
>>   *    NLA_MSECS            Leaving the length field zero will verify the
>>   *                         given type fits, using it verifies minimum length
>>   *                         just like "All other"
>> + *    NLA_BITFIELD_32      A 32-bit bitmap/bitselector attribute
>>   *    All other            Minimum length of attribute payload
>>   *
>>   * Example:
>> @@ -213,11 +215,13 @@ enum {
>>   * 	[ATTR_FOO] = { .type = NLA_U16 },
>>   *	[ATTR_BAR] = { .type = NLA_STRING, .len = BARSIZ },
>>   *	[ATTR_BAZ] = { .len = sizeof(struct mystruct) },
>> + *	[ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data = &myvalidflags },
>>   * };
>>   */
>> struct nla_policy {
>> 	u16		type;
>> 	u16		len;
>> +	void            *validation_data;
>> };
>>
>> /**
>> diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
>> index f86127a..0ac05a6 100644
>> --- a/include/uapi/linux/netlink.h
>> +++ b/include/uapi/linux/netlink.h
>> @@ -226,5 +226,22 @@ struct nlattr {
>> #define NLA_ALIGN(len)		(((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
>> #define NLA_HDRLEN		((int) NLA_ALIGN(sizeof(struct nlattr)))
>>
>> +/* Generic 32 bitflags attribute content sent to the kernel.
>> + *
>> + * The nla_value is a bitmap that defines the values being set
>> + * The nla_selector is a bitmask that defines which value is legit
>> + *
>> + * Examples:
>> + *  nla_value = 0x0, and nla_selector = 0x1
>> + *  implies we are selecting bit 1 and we want to set its value to 0.
>> + *
>> + *  nla_value = 0x2, and nla_selector = 0x2
>> + *  implies we are selecting bit 2 and we want to set its value to 1.
>> + *
>> + */
>> +struct nla_bitfield_32 {
>> +	__u32 nla_value;
>> +	__u32 nla_selector;
> 
> I would just have "value" and "selector" here. "nla_" prefix indicates
> netlink attrubute, like in struct nlattr - nla_type, nla_len,
> and that is wrong indication.
> 

Sure ;->

> 
> 
>> +};
>>
>> #endif /* _UAPI__LINUX_NETLINK_H */
>> diff --git a/lib/nlattr.c b/lib/nlattr.c
>> index fb52435..c8aad7e 100644
>> --- a/lib/nlattr.c
>> +++ b/lib/nlattr.c
>> @@ -27,6 +27,20 @@
>> 	[NLA_S64]	= sizeof(s64),
>> };
>>
>> +static int validate_nla_bitfield_32(const struct nlattr *nla, void *valid_data)
> 
> This should be:
> static int validate_nla_bitfield_32(const struct nlattr *nla, u32 *valid_flags_allowed)
> 
> 
> Other than this 2 nits, this looks good.
> 
> 

Are you sure Jiri? ;->

cheers,
jamal
Jamal Hadi Salim July 25, 2017, 11:15 a.m. UTC | #4
On 17-07-24 07:18 AM, Jiri Pirko wrote:
> Mon, Jul 24, 2017 at 03:35:43AM CEST, jhs@mojatatu.com wrote:
>> From: Jamal Hadi Salim <jhs@mojatatu.com>
>>
>> Generic bitflags attribute content sent to the kernel by user.
>> With this type the user can either set or unset a flag in the
>> kernel.
>>
>> The nla_value is a bitmap that defines the values being set
>> The nla_selector is a bitmask that defines which value is legit.
>>
>> A check is made to ensure the rules that a kernel subsystem always
>> conforms to bitflags the kernel already knows about. i.e
>> if the user tries to set a bit flag that is not understood then
>> the _it will be rejected_.
>>
>> In the most basic form, the user specifies the attribute policy as:
>> [ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data = &myvalidflags },
>>
>> where myvalidflags is the bit mask of the flags the kernel understands.
>>
>> If the user _does not_ provide myvalidflags then the attribute will
>> also be rejected.
>>
>> Examples:
>> nla_value = 0x0, and nla_selector = 0x1
>> implies we are selecting bit 1 and we want to set its value to 0.
>>
>> nla_value = 0x2, and nla_selector = 0x2
>> implies we are selecting bit 2 and we want to set its value to 1.
> 
> Oh, 2 more things:
> 
> [...]
> 

;-> ok will do that next opportunity (probably at 30K feet).

cheers,
jamal
David Ahern July 25, 2017, 2:41 p.m. UTC | #5
On 7/23/17 7:35 PM, Jamal Hadi Salim wrote:
> In the most basic form, the user specifies the attribute policy as:
> [ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data = &myvalidflags },
> 
> where myvalidflags is the bit mask of the flags the kernel understands.
> 
> If the user _does not_ provide myvalidflags then the attribute will
> also be rejected.

No other netlink attribute has this requirement. Users of the attributes
are the only ones that know if a value is valid or not (e.g, attribute
passing a device index) and those are always checked in line.
Furthermore, you are locking this attribute into a static meaning of
what is a valid value when flags can be valid or invalid based on other
attributes passed in the request.
Jamal Hadi Salim July 28, 2017, 1:51 p.m. UTC | #6
On 17-07-25 10:41 AM, David Ahern wrote:
> On 7/23/17 7:35 PM, Jamal Hadi Salim wrote:
>> In the most basic form, the user specifies the attribute policy as:
>> [ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data = &myvalidflags },
>>
>> where myvalidflags is the bit mask of the flags the kernel understands.
>>
>> If the user _does not_ provide myvalidflags then the attribute will
>> also be rejected.
> 
> No other netlink attribute has this requirement. 

This is the first one where we have to inspect content. We add things
when we need them - as in this case.

> Users of the attributes
> are the only ones that know if a value is valid or not (e.g, attribute
> passing a device index) and those are always checked in line.

It doesnt make sense that every user of the API has to repeat that
validation code. Same principle as someone specifying that a type is
u32 and have the nla validation check it. At some point we never had
the u32 validation code. Then it was factored out because everyone
repeats the same boilerplate code.
I see this in the same spirit.

> Furthermore, you are locking this attribute into a static meaning of
> what is a valid value when flags can be valid or invalid based on other
> attributes passed in the request.
> 

That doesnt disqualify that i factored out common code that all users
of this nltype are going to cutnpaste.

On the dependency on bit presence topic: I had added an "extra
validation" ops - but it was distracting enough that i removed that
patch altogether.

cheers,
jamal
Jiri Pirko July 28, 2017, 2:08 p.m. UTC | #7
Fri, Jul 28, 2017 at 03:51:41PM CEST, jhs@mojatatu.com wrote:
>On 17-07-25 10:41 AM, David Ahern wrote:
>> On 7/23/17 7:35 PM, Jamal Hadi Salim wrote:
>> > In the most basic form, the user specifies the attribute policy as:
>> > [ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data = &myvalidflags },
>> > 
>> > where myvalidflags is the bit mask of the flags the kernel understands.
>> > 
>> > If the user _does not_ provide myvalidflags then the attribute will
>> > also be rejected.
>> 
>> No other netlink attribute has this requirement.
>
>This is the first one where we have to inspect content. We add things
>when we need them - as in this case.
>
>> Users of the attributes
>> are the only ones that know if a value is valid or not (e.g, attribute
>> passing a device index) and those are always checked in line.
>
>It doesnt make sense that every user of the API has to repeat that
>validation code. Same principle as someone specifying that a type is
>u32 and have the nla validation check it. At some point we never had
>the u32 validation code. Then it was factored out because everyone
>repeats the same boilerplate code.
>I see this in the same spirit.

Agreed.
David Ahern July 28, 2017, 2:19 p.m. UTC | #8
On 7/28/17 7:51 AM, Jamal Hadi Salim wrote:
> On 17-07-25 10:41 AM, David Ahern wrote:
>> On 7/23/17 7:35 PM, Jamal Hadi Salim wrote:
>>> In the most basic form, the user specifies the attribute policy as:
>>> [ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data =
>>> &myvalidflags },
>>>
>>> where myvalidflags is the bit mask of the flags the kernel understands.
>>>
>>> If the user _does not_ provide myvalidflags then the attribute will
>>> also be rejected.
>>
>> No other netlink attribute has this requirement. 
> 
> This is the first one where we have to inspect content. We add things
> when we need them - as in this case.

Sure, the validation is required. My argument is that the validation
should be done where other attributes are validated -- inline with its
use. Nothing about this new bitfield says it must have a generic
validation code.

> 
>> Users of the attributes
>> are the only ones that know if a value is valid or not (e.g, attribute
>> passing a device index) and those are always checked in line.
> 
> It doesnt make sense that every user of the API has to repeat that
> validation code. Same principle as someone specifying that a type is
> u32 and have the nla validation check it. At some point we never had
> the u32 validation code. Then it was factored out because everyone
> repeats the same boilerplate code.

Every user of an attribute that uses a device index must verify the
device index is valid. The same code is repeated over and over.

Now you are suggesting to have 1 attribute whose content is validated by
generic infra and the rest are validated inline by the code using it. I
believe it is wrong and going to lead to problems.
Jiri Pirko July 28, 2017, 2:55 p.m. UTC | #9
Fri, Jul 28, 2017 at 04:19:06PM CEST, dsahern@gmail.com wrote:
>On 7/28/17 7:51 AM, Jamal Hadi Salim wrote:
>> On 17-07-25 10:41 AM, David Ahern wrote:
>>> On 7/23/17 7:35 PM, Jamal Hadi Salim wrote:
>>>> In the most basic form, the user specifies the attribute policy as:
>>>> [ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data =
>>>> &myvalidflags },
>>>>
>>>> where myvalidflags is the bit mask of the flags the kernel understands.
>>>>
>>>> If the user _does not_ provide myvalidflags then the attribute will
>>>> also be rejected.
>>>
>>> No other netlink attribute has this requirement. 
>> 
>> This is the first one where we have to inspect content. We add things
>> when we need them - as in this case.
>
>Sure, the validation is required. My argument is that the validation
>should be done where other attributes are validated -- inline with its
>use. Nothing about this new bitfield says it must have a generic
>validation code.
>
>> 
>>> Users of the attributes
>>> are the only ones that know if a value is valid or not (e.g, attribute
>>> passing a device index) and those are always checked in line.
>> 
>> It doesnt make sense that every user of the API has to repeat that
>> validation code. Same principle as someone specifying that a type is
>> u32 and have the nla validation check it. At some point we never had
>> the u32 validation code. Then it was factored out because everyone
>> repeats the same boilerplate code.
>
>Every user of an attribute that uses a device index must verify the
>device index is valid. The same code is repeated over and over.

This is something different. You don't have NLA_IFINDEX. If you'd have it,
might make sense to do validation on Netlink level. Ofc this is highly
hypothetical. But in Jamal's case, there is indeed NLA_BITFIELD32 and
this attribute type itself assumes some format. Therefore the validation
on Netlink level makes sense here. At least that is how I feel it.


>
>Now you are suggesting to have 1 attribute whose content is validated by
>generic infra and the rest are validated inline by the code using it. I
>believe it is wrong and going to lead to problems.
Jamal Hadi Salim July 28, 2017, 3:04 p.m. UTC | #10
On 17-07-28 10:19 AM, David Ahern wrote:
> On 7/28/17 7:51 AM, Jamal Hadi Salim wrote:
>> On 17-07-25 10:41 AM, David Ahern wrote:
>>> On 7/23/17 7:35 PM, Jamal Hadi Salim wrote:
>>>> In the most basic form, the user specifies the attribute policy as:
>>>> [ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data =
>>>> &myvalidflags },
>>>>
>>>> where myvalidflags is the bit mask of the flags the kernel understands.
>>>>
>>>> If the user _does not_ provide myvalidflags then the attribute will
>>>> also be rejected.
>>>
>>> No other netlink attribute has this requirement.
>>
>> This is the first one where we have to inspect content. We add things
>> when we need them - as in this case.
> 
> Sure, the validation is required. My argument is that the validation
> should be done where other attributes are validated -- inline with its
> use. Nothing about this new bitfield says it must have a generic
> validation code.
>
Response below.

>>
>>> Users of the attributes
>>> are the only ones that know if a value is valid or not (e.g, attribute
>>> passing a device index) and those are always checked in line.
>>
>> It doesnt make sense that every user of the API has to repeat that
>> validation code. Same principle as someone specifying that a type is
>> u32 and have the nla validation check it. At some point we never had
>> the u32 validation code. Then it was factored out because everyone
>> repeats the same boilerplate code.
> 
> Every user of an attribute that uses a device index must verify the
> device index is valid. The same code is repeated over and over.
> 
> Now you are suggesting to have 1 attribute whose content is validated by
> generic infra and the rest are validated inline by the code using it. I
> believe it is wrong and going to lead to problems.
> 

Kernel side checking for device ifindex must know what a device
ifindex means.
That doesnt disqualify that the generic code checks that it
is of the same size as a signed 32b, etc. That is generic
stuff that can be factored out.

In this case:
Checking for whether bits selected are in the allowed range
that the kernel understands, that the bit value are set in
the right bit position, that the bits set in the correct bit
value position are also selected in the transaction.
That is generic code (which the content validation does).

Checking what bit 5 means - that is specific per kernel user
code. Checking that when bit 5 is selected it is only useful
if bit 3 is unselected - that is specific to the kernel code
consuming those bits.

cheers,
jamal
David Ahern July 28, 2017, 3:13 p.m. UTC | #11
On 7/28/17 9:04 AM, Jamal Hadi Salim wrote:
> 
> Kernel side checking for device ifindex must know what a device
> ifindex means.
> That doesnt disqualify that the generic code checks that it
> is of the same size as a signed 32b, etc. That is generic
> stuff that can be factored out.
> 
> In this case:
> Checking for whether bits selected are in the allowed range
> that the kernel understands, that the bit value are set in
> the right bit position, that the bits set in the correct bit
> value position are also selected in the transaction.
> That is generic code (which the content validation does).

Create a helper function then. It's the validation of attribute content
in 2 places that I object to. 1 attribute is validated in 1 place
(generic infra for this bitfield attribute), the others are validated in
line. Asymmetric validations is not a good design.
Jamal Hadi Salim July 28, 2017, 9:55 p.m. UTC | #12
On 17-07-28 11:13 AM, David Ahern wrote:
> On 7/28/17 9:04 AM, Jamal Hadi Salim wrote:
>>
>> Kernel side checking for device ifindex must know what a device
>> ifindex means.
>> That doesnt disqualify that the generic code checks that it
>> is of the same size as a signed 32b, etc. That is generic
>> stuff that can be factored out.
>>
>> In this case:
>> Checking for whether bits selected are in the allowed range
>> that the kernel understands, that the bit value are set in
>> the right bit position, that the bits set in the correct bit
>> value position are also selected in the transaction.
>> That is generic code (which the content validation does).
> 
> Create a helper function then. It's the validation of attribute content
> in 2 places that I object to. 1 attribute is validated in 1 place
> (generic infra for this bitfield attribute), the others are validated in
> line. Asymmetric validations is not a good design.
> 

What is not generic in what is being validated though?
Content validation is when you say "This bit means the sky is blue".
You have to know the meaning of the bit.
The generic validation is opaque; "this bit is not supposed to be here".
I would argue that "this is bit is not supposed to be here unless
this other bit is present" is had to generalize but would be infra
as well (why i provided the ops for it, but removed it so we could
move on).

cheers,
jamal
diff mbox

Patch

diff --git a/include/net/netlink.h b/include/net/netlink.h
index ef8e6c3..e33d1fb 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -178,6 +178,7 @@  enum {
 	NLA_S16,
 	NLA_S32,
 	NLA_S64,
+	NLA_BITFIELD_32,
 	__NLA_TYPE_MAX,
 };
 
@@ -206,6 +207,7 @@  enum {
  *    NLA_MSECS            Leaving the length field zero will verify the
  *                         given type fits, using it verifies minimum length
  *                         just like "All other"
+ *    NLA_BITFIELD_32      A 32-bit bitmap/bitselector attribute
  *    All other            Minimum length of attribute payload
  *
  * Example:
@@ -213,11 +215,13 @@  enum {
  * 	[ATTR_FOO] = { .type = NLA_U16 },
  *	[ATTR_BAR] = { .type = NLA_STRING, .len = BARSIZ },
  *	[ATTR_BAZ] = { .len = sizeof(struct mystruct) },
+ *	[ATTR_GOO] = { .type = NLA_BITFIELD_32, .validation_data = &myvalidflags },
  * };
  */
 struct nla_policy {
 	u16		type;
 	u16		len;
+	void            *validation_data;
 };
 
 /**
diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
index f86127a..0ac05a6 100644
--- a/include/uapi/linux/netlink.h
+++ b/include/uapi/linux/netlink.h
@@ -226,5 +226,22 @@  struct nlattr {
 #define NLA_ALIGN(len)		(((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
 #define NLA_HDRLEN		((int) NLA_ALIGN(sizeof(struct nlattr)))
 
+/* Generic 32 bitflags attribute content sent to the kernel.
+ *
+ * The nla_value is a bitmap that defines the values being set
+ * The nla_selector is a bitmask that defines which value is legit
+ *
+ * Examples:
+ *  nla_value = 0x0, and nla_selector = 0x1
+ *  implies we are selecting bit 1 and we want to set its value to 0.
+ *
+ *  nla_value = 0x2, and nla_selector = 0x2
+ *  implies we are selecting bit 2 and we want to set its value to 1.
+ *
+ */
+struct nla_bitfield_32 {
+	__u32 nla_value;
+	__u32 nla_selector;
+};
 
 #endif /* _UAPI__LINUX_NETLINK_H */
diff --git a/lib/nlattr.c b/lib/nlattr.c
index fb52435..c8aad7e 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -27,6 +27,20 @@ 
 	[NLA_S64]	= sizeof(s64),
 };
 
+static int validate_nla_bitfield_32(const struct nlattr *nla, void *valid_data)
+{
+	const struct nla_bitfield_32 *nbf = nla_data(nla);
+	u32 *valid_flags_mask = valid_data;
+
+	if (!valid_data)
+		return -EINVAL;
+
+	if (nbf->nla_value & ~*valid_flags_mask)
+		return -EINVAL;
+
+	return 0;
+}
+
 static int validate_nla(const struct nlattr *nla, int maxtype,
 			const struct nla_policy *policy)
 {
@@ -46,6 +60,13 @@  static int validate_nla(const struct nlattr *nla, int maxtype,
 			return -ERANGE;
 		break;
 
+	case NLA_BITFIELD_32:
+		if (attrlen != sizeof(struct nla_bitfield_32))
+			return -ERANGE;
+
+		return validate_nla_bitfield_32(nla, pt->validation_data);
+		break;
+
 	case NLA_NUL_STRING:
 		if (pt->len)
 			minlen = min_t(int, attrlen, pt->len + 1);