All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] netlink: expose policies to userspace
@ 2019-04-26 12:13 Johannes Berg
  2019-04-26 12:13 ` [PATCH 1/6] netlink: remove type-unsafe validation_data pointer Johannes Berg
                   ` (5 more replies)
  0 siblings, 6 replies; 13+ messages in thread
From: Johannes Berg @ 2019-04-26 12:13 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Pablo Neira Ayuso

Make some cleanups in policy validation, and add tools to expose
the policies to userspace.

This lets userspace discover what the appropriate data for an
element is.

Note that NLA_UNSPEC items are omitted as they're rejected in
strict validation mode so their policy entries should be changed
to NLA_MIN_LEN - this is just a representation change.

Eventually, I'd like to add a separation between input and output
policies (currently only the input policy is exposed), but that
needs some extra work to add "overrides".

johannes



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/6] netlink: remove type-unsafe validation_data pointer
  2019-04-26 12:13 [PATCH 0/6] netlink: expose policies to userspace Johannes Berg
@ 2019-04-26 12:13 ` Johannes Berg
  2019-04-26 12:13 ` [PATCH 2/6] netlink: extend policy range validation Johannes Berg
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Johannes Berg @ 2019-04-26 12:13 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Pablo Neira Ayuso, Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

In the netlink policy, we currently have a void *validation_data
that's pointing to different things:
 * a u32 value for bitfield32,
 * the netlink policy for nested/nested array
 * the string for NLA_REJECT

Remove the pointer and place appropriate type-safe items in the
union instead.

While at it, completely dissolve the pointer for the bitfield32
case and just put the value there directly.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/netlink.h | 55 ++++++++++++++++++++++++-------------------
 lib/nlattr.c          | 20 ++++++++--------
 net/sched/act_api.c   |  4 +---
 3 files changed, 42 insertions(+), 37 deletions(-)

diff --git a/include/net/netlink.h b/include/net/netlink.h
index 24dbf8bb695a..0379fdc3b610 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -217,7 +217,7 @@ enum nla_policy_validation {
  *    NLA_NESTED,
  *    NLA_NESTED_ARRAY     Length verification is done by checking len of
  *                         nested header (or empty); len field is used if
- *                         validation_data is also used, for the max attr
+ *                         nested_policy is also used, for the max attr
  *                         number in the nested policy.
  *    NLA_U8, NLA_U16,
  *    NLA_U32, NLA_U64,
@@ -235,27 +235,25 @@ enum nla_policy_validation {
  *    NLA_MIN_LEN          Minimum length of attribute payload
  *    All other            Minimum length of attribute payload
  *
- * Meaning of `validation_data' field:
+ * Meaning of validation union:
  *    NLA_BITFIELD32       This is a 32-bit bitmap/bitselector attribute and
- *                         validation data must point to a u32 value of valid
- *                         flags
- *    NLA_REJECT           This attribute is always rejected and validation data
+ *                         `bitfield32_valid' is the u32 value 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.
- *    NLA_NESTED           Points to a nested policy to validate, must also set
- *                         `len' to the max attribute number.
+ *    NLA_NESTED           `nested_policy' to a nested policy to validate, must
+ *                         also set `len' to the max attribute number. Use the
+ *                         provided NLA_POLICY_NESTED() macro.
  *                         Note that nla_parse() will validate, but of course not
  *                         parse, the nested sub-policies.
- *    NLA_NESTED_ARRAY     Points to a nested policy to validate, must also set
- *                         `len' to the max attribute number. The difference to
- *                         NLA_NESTED is the structure - NLA_NESTED has the
- *                         nested attributes directly inside, while an array has
- *                         the nested attributes at another level down and the
- *                         attributes directly in the nesting don't matter.
- *    All other            Unused - but note that it's a union
- *
- * Meaning of `min' and `max' fields, use via NLA_POLICY_MIN, NLA_POLICY_MAX
- * and NLA_POLICY_RANGE:
+ *    NLA_NESTED_ARRAY     `nested_policy' points to a nested policy to validate,
+ *                         must also set `len' to the max attribute number. Use
+ *                         the provided NLA_POLICY_NESTED_ARRAY() macro.
+ *                         The difference to NLA_NESTED is the structure:
+ *                         NLA_NESTED has the nested attributes directly inside
+ *                         while an array has the nested attributes at another
+ *                         level down and the attribute types directly in the
+ *                         nesting don't matter.
  *    NLA_U8,
  *    NLA_U16,
  *    NLA_U32,
@@ -263,14 +261,16 @@ enum nla_policy_validation {
  *    NLA_S8,
  *    NLA_S16,
  *    NLA_S32,
- *    NLA_S64              These are used depending on the validation_type
- *                         field, if that is min/max/range then the minimum,
- *                         maximum and both are used (respectively) to check
+ *    NLA_S64              The `min' and `max' fields are used depending on the
+ *                         validation_type field, if that is min/max/range then
+ *                         the min, max or both are used (respectively) to check
  *                         the value of the integer attribute.
  *                         Note that in the interest of code simplicity and
  *                         struct size both limits are s16, so you cannot
  *                         enforce a range that doesn't fall within the range
  *                         of s16 - do that as usual in the code instead.
+ *                         Use the NLA_POLICY_MIN(), NLA_POLICY_MAX() and
+ *                         NLA_POLICY_RANGE() macros.
  *    All other            Unused - but note that it's a union
  *
  * Meaning of `validate' field, use via NLA_POLICY_VALIDATE_FN:
@@ -281,11 +281,14 @@ enum nla_policy_validation {
  *    All other            Unused - but note that it's a union
  *
  * Example:
+ *
+ * static const u32 myvalidflags = 0xff231023;
+ *
  * static const struct nla_policy my_policy[ATTR_MAX+1] = {
  * 	[ATTR_FOO] = { .type = NLA_U16 },
  *	[ATTR_BAR] = { .type = NLA_STRING, .len = BARSIZ },
  *	[ATTR_BAZ] = { .type = NLA_EXACT_LEN, .len = sizeof(struct mystruct) },
- *	[ATTR_GOO] = { .type = NLA_BITFIELD32, .validation_data = &myvalidflags },
+ *	[ATTR_GOO] = NLA_POLICY_BITFIELD32(myvalidflags),
  * };
  */
 struct nla_policy {
@@ -293,7 +296,9 @@ struct nla_policy {
 	u8		validation_type;
 	u16		len;
 	union {
-		const void *validation_data;
+		const u32 bitfield32_valid;
+		const char *reject_message;
+		const struct nla_policy *nested_policy;
 		struct {
 			s16 min, max;
 		};
@@ -329,13 +334,15 @@ struct nla_policy {
 #define NLA_POLICY_ETH_ADDR_COMPAT	NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN)
 
 #define _NLA_POLICY_NESTED(maxattr, policy) \
-	{ .type = NLA_NESTED, .validation_data = policy, .len = maxattr }
+	{ .type = NLA_NESTED, .nested_policy = policy, .len = maxattr }
 #define _NLA_POLICY_NESTED_ARRAY(maxattr, policy) \
-	{ .type = NLA_NESTED_ARRAY, .validation_data = policy, .len = maxattr }
+	{ .type = NLA_NESTED_ARRAY, .nested_policy = policy, .len = maxattr }
 #define NLA_POLICY_NESTED(policy) \
 	_NLA_POLICY_NESTED(ARRAY_SIZE(policy) - 1, policy)
 #define NLA_POLICY_NESTED_ARRAY(policy) \
 	_NLA_POLICY_NESTED_ARRAY(ARRAY_SIZE(policy) - 1, policy)
+#define NLA_POLICY_BITFIELD32(valid) \
+	{ .type = NLA_BITFIELD32, .bitfield32_valid = valid }
 
 #define __NLA_ENSURE(condition) BUILD_BUG_ON_ZERO(!(condition))
 #define NLA_ENSURE_INT_TYPE(tp)				\
diff --git a/lib/nlattr.c b/lib/nlattr.c
index 29f6336e2422..c546db7c72dd 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -45,7 +45,7 @@ static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = {
 };
 
 static int validate_nla_bitfield32(const struct nlattr *nla,
-				   const u32 *valid_flags_mask)
+				   const u32 valid_flags_mask)
 {
 	const struct nla_bitfield32 *bf = nla_data(nla);
 
@@ -53,11 +53,11 @@ static int validate_nla_bitfield32(const struct nlattr *nla,
 		return -EINVAL;
 
 	/*disallow invalid bit selector */
-	if (bf->selector & ~*valid_flags_mask)
+	if (bf->selector & ~valid_flags_mask)
 		return -EINVAL;
 
 	/*disallow invalid bit values */
-	if (bf->value & ~*valid_flags_mask)
+	if (bf->value & ~valid_flags_mask)
 		return -EINVAL;
 
 	/*disallow valid bit values that are not selected*/
@@ -191,9 +191,9 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 		break;
 
 	case NLA_REJECT:
-		if (extack && pt->validation_data) {
+		if (extack && pt->reject_message) {
 			NL_SET_BAD_ATTR(extack, nla);
-			extack->_msg = pt->validation_data;
+			extack->_msg = pt->reject_message;
 			return -EINVAL;
 		}
 		err = -EINVAL;
@@ -208,7 +208,7 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 		if (attrlen != sizeof(struct nla_bitfield32))
 			goto out_err;
 
-		err = validate_nla_bitfield32(nla, pt->validation_data);
+		err = validate_nla_bitfield32(nla, pt->bitfield32_valid);
 		if (err)
 			goto out_err;
 		break;
@@ -253,9 +253,9 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 			break;
 		if (attrlen < NLA_HDRLEN)
 			goto out_err;
-		if (pt->validation_data) {
+		if (pt->nested_policy) {
 			err = __nla_validate(nla_data(nla), nla_len(nla), pt->len,
-					     pt->validation_data, validate,
+					     pt->nested_policy, validate,
 					     extack);
 			if (err < 0) {
 				/*
@@ -274,11 +274,11 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 			break;
 		if (attrlen < NLA_HDRLEN)
 			goto out_err;
-		if (pt->validation_data) {
+		if (pt->nested_policy) {
 			int err;
 
 			err = nla_validate_array(nla_data(nla), nla_len(nla),
-						 pt->len, pt->validation_data,
+						 pt->len, pt->nested_policy,
 						 extack, validate);
 			if (err < 0) {
 				/*
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 1c630da4be0d..8ea5e6e5ca60 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -1368,10 +1368,8 @@ static int tcf_action_add(struct net *net, struct nlattr *nla,
 	return ret;
 }
 
-static u32 tcaa_root_flags_allowed = TCA_FLAG_LARGE_DUMP_ON;
 static const struct nla_policy tcaa_policy[TCA_ROOT_MAX + 1] = {
-	[TCA_ROOT_FLAGS] = { .type = NLA_BITFIELD32,
-			     .validation_data = &tcaa_root_flags_allowed },
+	[TCA_ROOT_FLAGS] = NLA_POLICY_BITFIELD32(TCA_FLAG_LARGE_DUMP_ON),
 	[TCA_ROOT_TIME_DELTA]      = { .type = NLA_U32 },
 };
 
-- 
2.17.2


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/6] netlink: extend policy range validation
  2019-04-26 12:13 [PATCH 0/6] netlink: expose policies to userspace Johannes Berg
  2019-04-26 12:13 ` [PATCH 1/6] netlink: remove type-unsafe validation_data pointer Johannes Berg
@ 2019-04-26 12:13 ` Johannes Berg
  2019-04-30  2:49   ` David Miller
  2019-04-26 12:13 ` [PATCH 3/6] netlink: allow NLA_MSECS to have " Johannes Berg
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 13+ messages in thread
From: Johannes Berg @ 2019-04-26 12:13 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Pablo Neira Ayuso, Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

Using a pointer to a struct indicating the min/max values,
extend the ability to do range validation for arbitrary
values. Small values in the s16 range can be kept in the
policy directly.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/netlink.h |  45 +++++++++++++++++
 lib/nlattr.c          | 112 ++++++++++++++++++++++++++++++++++--------
 2 files changed, 136 insertions(+), 21 deletions(-)

diff --git a/include/net/netlink.h b/include/net/netlink.h
index 0379fdc3b610..66cc7591c000 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -189,11 +189,20 @@ enum {
 
 #define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
 
+struct netlink_range_validation {
+	u64 min, max;
+};
+
+struct netlink_range_validation_signed {
+	s64 min, max;
+};
+
 enum nla_policy_validation {
 	NLA_VALIDATE_NONE,
 	NLA_VALIDATE_RANGE,
 	NLA_VALIDATE_MIN,
 	NLA_VALIDATE_MAX,
+	NLA_VALIDATE_RANGE_PTR,
 	NLA_VALIDATE_FUNCTION,
 };
 
@@ -271,6 +280,22 @@ enum nla_policy_validation {
  *                         of s16 - do that as usual in the code instead.
  *                         Use the NLA_POLICY_MIN(), NLA_POLICY_MAX() and
  *                         NLA_POLICY_RANGE() macros.
+ *    NLA_U8,
+ *    NLA_U16,
+ *    NLA_U32,
+ *    NLA_U64              If the validation_type field instead is set to
+ *                         NLA_VALIDATE_RANGE_PTR, `range' must be a pointer
+ *                         to a struct netlink_range_validation that indicates
+ *                         the min/max values.
+ *                         Use NLA_POLICY_FULL_RANGE().
+ *    NLA_S8,
+ *    NLA_S16,
+ *    NLA_S32,
+ *    NLA_S64              If the validation_type field instead is set to
+ *                         NLA_VALIDATE_RANGE_PTR, `range_signed' must be a
+ *                         pointer to a struct netlink_range_validation_signed
+ *                         that indicates the min/max values.
+ *                         Use NLA_POLICY_FULL_RANGE_SIGNED().
  *    All other            Unused - but note that it's a union
  *
  * Meaning of `validate' field, use via NLA_POLICY_VALIDATE_FN:
@@ -299,6 +324,8 @@ struct nla_policy {
 		const u32 bitfield32_valid;
 		const char *reject_message;
 		const struct nla_policy *nested_policy;
+		struct netlink_range_validation *range;
+		struct netlink_range_validation_signed *range_signed;
 		struct {
 			s16 min, max;
 		};
@@ -345,6 +372,12 @@ struct nla_policy {
 	{ .type = NLA_BITFIELD32, .bitfield32_valid = valid }
 
 #define __NLA_ENSURE(condition) BUILD_BUG_ON_ZERO(!(condition))
+#define NLA_ENSURE_UINT_TYPE(tp)			\
+	(__NLA_ENSURE(tp == NLA_U8 || tp == NLA_U16 ||	\
+		      tp == NLA_U32 || tp == NLA_U64) + tp)
+#define NLA_ENSURE_SINT_TYPE(tp)			\
+	(__NLA_ENSURE(tp == NLA_S8 || tp == NLA_S16  ||	\
+		      tp == NLA_S32 || tp == NLA_S64) + tp)
 #define NLA_ENSURE_INT_TYPE(tp)				\
 	(__NLA_ENSURE(tp == NLA_S8 || tp == NLA_U8 ||	\
 		      tp == NLA_S16 || tp == NLA_U16 ||	\
@@ -363,6 +396,18 @@ struct nla_policy {
 	.max = _max					\
 }
 
+#define NLA_POLICY_FULL_RANGE(tp, _range) {		\
+	.type = NLA_ENSURE_UINT_TYPE(tp),		\
+	.validation_type = NLA_VALIDATE_RANGE_PTR,	\
+	.range = _range,				\
+}
+
+#define NLA_POLICY_FULL_RANGE_SIGNED(tp, _range) {	\
+	.type = NLA_ENSURE_SINT_TYPE(tp),		\
+	.validation_type = NLA_VALIDATE_RANGE_PTR,	\
+	.range = _range,				\
+}
+
 #define NLA_POLICY_MIN(tp, _min) {			\
 	.type = NLA_ENSURE_INT_TYPE(tp),		\
 	.validation_type = NLA_VALIDATE_MIN,		\
diff --git a/lib/nlattr.c b/lib/nlattr.c
index c546db7c72dd..b549b290d3fa 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -96,17 +96,33 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
 	return 0;
 }
 
-static int nla_validate_int_range(const struct nla_policy *pt,
-				  const struct nlattr *nla,
-				  struct netlink_ext_ack *extack)
+static int nla_validate_int_range_unsigned(const struct nla_policy *pt,
+					   const struct nlattr *nla,
+					   struct netlink_ext_ack *extack)
 {
-	bool validate_min, validate_max;
-	s64 value;
+	struct netlink_range_validation _range = {
+		.min = 0,
+		.max = U64_MAX,
+	}, *range = &_range;
+	u64 value;
 
-	validate_min = pt->validation_type == NLA_VALIDATE_RANGE ||
-		       pt->validation_type == NLA_VALIDATE_MIN;
-	validate_max = pt->validation_type == NLA_VALIDATE_RANGE ||
-		       pt->validation_type == NLA_VALIDATE_MAX;
+	WARN_ON_ONCE(pt->min < 0 || pt->max < 0);
+
+	switch (pt->validation_type) {
+	case NLA_VALIDATE_RANGE:
+		range->min = pt->min;
+		range->max = pt->max;
+		break;
+	case NLA_VALIDATE_RANGE_PTR:
+		range = pt->range;
+		break;
+	case NLA_VALIDATE_MIN:
+		range->min = pt->min;
+		break;
+	case NLA_VALIDATE_MAX:
+		range->max = pt->max;
+		break;
+	}
 
 	switch (pt->type) {
 	case NLA_U8:
@@ -118,6 +134,49 @@ static int nla_validate_int_range(const struct nla_policy *pt,
 	case NLA_U32:
 		value = nla_get_u32(nla);
 		break;
+	case NLA_U64:
+		value = nla_get_u64(nla);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (value < range->min || value > range->max) {
+		NL_SET_ERR_MSG_ATTR(extack, nla,
+				    "integer out of range");
+		return -ERANGE;
+	}
+
+	return 0;
+}
+
+static int nla_validate_int_range_signed(const struct nla_policy *pt,
+					 const struct nlattr *nla,
+					 struct netlink_ext_ack *extack)
+{
+	struct netlink_range_validation_signed _range = {
+		.min = S64_MIN,
+		.max = S64_MAX,
+	}, *range = &_range;
+	s64 value;
+
+	switch (pt->validation_type) {
+	case NLA_VALIDATE_RANGE:
+		range->min = pt->min;
+		range->max = pt->max;
+		break;
+	case NLA_VALIDATE_RANGE_PTR:
+		range = pt->range_signed;
+		break;
+	case NLA_VALIDATE_MIN:
+		range->min = pt->min;
+		break;
+	case NLA_VALIDATE_MAX:
+		range->max = pt->max;
+		break;
+	}
+
+	switch (pt->type) {
 	case NLA_S8:
 		value = nla_get_s8(nla);
 		break;
@@ -130,22 +189,11 @@ static int nla_validate_int_range(const struct nla_policy *pt,
 	case NLA_S64:
 		value = nla_get_s64(nla);
 		break;
-	case NLA_U64:
-		/* treat this one specially, since it may not fit into s64 */
-		if ((validate_min && nla_get_u64(nla) < pt->min) ||
-		    (validate_max && nla_get_u64(nla) > pt->max)) {
-			NL_SET_ERR_MSG_ATTR(extack, nla,
-					    "integer out of range");
-			return -ERANGE;
-		}
-		return 0;
 	default:
-		WARN_ON(1);
 		return -EINVAL;
 	}
 
-	if ((validate_min && value < pt->min) ||
-	    (validate_max && value > pt->max)) {
+	if (value < range->min || value > range->max) {
 		NL_SET_ERR_MSG_ATTR(extack, nla,
 				    "integer out of range");
 		return -ERANGE;
@@ -154,6 +202,27 @@ static int nla_validate_int_range(const struct nla_policy *pt,
 	return 0;
 }
 
+static int nla_validate_int_range(const struct nla_policy *pt,
+				  const struct nlattr *nla,
+				  struct netlink_ext_ack *extack)
+{
+	switch (pt->type) {
+	case NLA_U8:
+	case NLA_U16:
+	case NLA_U32:
+	case NLA_U64:
+		return nla_validate_int_range_unsigned(pt, nla, extack);
+	case NLA_S8:
+	case NLA_S16:
+	case NLA_S32:
+	case NLA_S64:
+		return nla_validate_int_range_signed(pt, nla, extack);
+	default:
+		WARN_ON(1);
+		return -EINVAL;
+	}
+}
+
 static int validate_nla(const struct nlattr *nla, int maxtype,
 			const struct nla_policy *policy, unsigned int validate,
 			struct netlink_ext_ack *extack)
@@ -317,6 +386,7 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 	case NLA_VALIDATE_NONE:
 		/* nothing to do */
 		break;
+	case NLA_VALIDATE_RANGE_PTR:
 	case NLA_VALIDATE_RANGE:
 	case NLA_VALIDATE_MIN:
 	case NLA_VALIDATE_MAX:
-- 
2.17.2


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/6] netlink: allow NLA_MSECS to have range validation
  2019-04-26 12:13 [PATCH 0/6] netlink: expose policies to userspace Johannes Berg
  2019-04-26 12:13 ` [PATCH 1/6] netlink: remove type-unsafe validation_data pointer Johannes Berg
  2019-04-26 12:13 ` [PATCH 2/6] netlink: extend policy range validation Johannes Berg
@ 2019-04-26 12:13 ` Johannes Berg
  2019-04-26 12:13 ` [PATCH 4/6] netlink: remove NLA_EXACT_LEN_WARN Johannes Berg
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Johannes Berg @ 2019-04-26 12:13 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Pablo Neira Ayuso, Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

Since NLA_MSECS is really equivalent to NLA_U64, allow
it to have range validation as well.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/netlink.h | 6 ++++--
 lib/nlattr.c          | 2 ++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/include/net/netlink.h b/include/net/netlink.h
index 66cc7591c000..2a75927d368a 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -374,7 +374,8 @@ struct nla_policy {
 #define __NLA_ENSURE(condition) BUILD_BUG_ON_ZERO(!(condition))
 #define NLA_ENSURE_UINT_TYPE(tp)			\
 	(__NLA_ENSURE(tp == NLA_U8 || tp == NLA_U16 ||	\
-		      tp == NLA_U32 || tp == NLA_U64) + tp)
+		      tp == NLA_U32 || tp == NLA_U64 ||	\
+		      tp == NLA_MSECS) + tp)
 #define NLA_ENSURE_SINT_TYPE(tp)			\
 	(__NLA_ENSURE(tp == NLA_S8 || tp == NLA_S16  ||	\
 		      tp == NLA_S32 || tp == NLA_S64) + tp)
@@ -382,7 +383,8 @@ struct nla_policy {
 	(__NLA_ENSURE(tp == NLA_S8 || tp == NLA_U8 ||	\
 		      tp == NLA_S16 || tp == NLA_U16 ||	\
 		      tp == NLA_S32 || tp == NLA_U32 ||	\
-		      tp == NLA_S64 || tp == NLA_U64) + tp)
+		      tp == NLA_S64 || tp == NLA_U64 ||	\
+		      tp == NLA_MSECS) + tp)
 #define NLA_ENSURE_NO_VALIDATION_PTR(tp)		\
 	(__NLA_ENSURE(tp != NLA_BITFIELD32 &&		\
 		      tp != NLA_REJECT &&		\
diff --git a/lib/nlattr.c b/lib/nlattr.c
index b549b290d3fa..c8789de96046 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -135,6 +135,7 @@ static int nla_validate_int_range_unsigned(const struct nla_policy *pt,
 		value = nla_get_u32(nla);
 		break;
 	case NLA_U64:
+	case NLA_MSECS:
 		value = nla_get_u64(nla);
 		break;
 	default:
@@ -211,6 +212,7 @@ static int nla_validate_int_range(const struct nla_policy *pt,
 	case NLA_U16:
 	case NLA_U32:
 	case NLA_U64:
+	case NLA_MSECS:
 		return nla_validate_int_range_unsigned(pt, nla, extack);
 	case NLA_S8:
 	case NLA_S16:
-- 
2.17.2


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/6] netlink: remove NLA_EXACT_LEN_WARN
  2019-04-26 12:13 [PATCH 0/6] netlink: expose policies to userspace Johannes Berg
                   ` (2 preceding siblings ...)
  2019-04-26 12:13 ` [PATCH 3/6] netlink: allow NLA_MSECS to have " Johannes Berg
@ 2019-04-26 12:13 ` Johannes Berg
  2019-04-26 12:13 ` [PATCH 5/6] netlink: factor out policy range helpers Johannes Berg
  2019-04-26 12:13 ` [PATCH 6/6] netlink: add infrastructure to expose policies to userspace Johannes Berg
  5 siblings, 0 replies; 13+ messages in thread
From: Johannes Berg @ 2019-04-26 12:13 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Pablo Neira Ayuso, Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

Use a validation type instead, so we can later expose
the NLA_* values to userspace for policy descriptions.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/netlink.h | 15 ++++++++-------
 lib/nlattr.c          | 16 ++++++++++------
 2 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/include/net/netlink.h b/include/net/netlink.h
index 2a75927d368a..0a02d25fb163 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -182,7 +182,6 @@ enum {
 	NLA_BITFIELD32,
 	NLA_REJECT,
 	NLA_EXACT_LEN,
-	NLA_EXACT_LEN_WARN,
 	NLA_MIN_LEN,
 	__NLA_TYPE_MAX,
 };
@@ -204,6 +203,7 @@ enum nla_policy_validation {
 	NLA_VALIDATE_MAX,
 	NLA_VALIDATE_RANGE_PTR,
 	NLA_VALIDATE_FUNCTION,
+	NLA_VALIDATE_WARN_TOO_LONG,
 };
 
 /**
@@ -237,10 +237,10 @@ enum nla_policy_validation {
  *                         just like "All other"
  *    NLA_BITFIELD32       Unused
  *    NLA_REJECT           Unused
- *    NLA_EXACT_LEN        Attribute must have exactly this length, otherwise
- *                         it is rejected.
- *    NLA_EXACT_LEN_WARN   Attribute should have exactly this length, a warning
- *                         is logged if it is longer, shorter is rejected.
+ *    NLA_EXACT_LEN        Attribute should have exactly this length, otherwise
+ *                         it is rejected or warned about, the latter happening
+ *                         if and only if the `validation_type' is set to
+ *                         NLA_VALIDATE_WARN_TOO_LONG.
  *    NLA_MIN_LEN          Minimum length of attribute payload
  *    All other            Minimum length of attribute payload
  *
@@ -353,8 +353,9 @@ struct nla_policy {
 };
 
 #define NLA_POLICY_EXACT_LEN(_len)	{ .type = NLA_EXACT_LEN, .len = _len }
-#define NLA_POLICY_EXACT_LEN_WARN(_len)	{ .type = NLA_EXACT_LEN_WARN, \
-					  .len = _len }
+#define NLA_POLICY_EXACT_LEN_WARN(_len) \
+	{ .type = NLA_EXACT_LEN, .len = _len, \
+	  .validation_type = NLA_VALIDATE_WARN_TOO_LONG, }
 #define NLA_POLICY_MIN_LEN(_len)	{ .type = NLA_MIN_LEN, .len = _len }
 
 #define NLA_POLICY_ETH_ADDR		NLA_POLICY_EXACT_LEN(ETH_ALEN)
diff --git a/lib/nlattr.c b/lib/nlattr.c
index c8789de96046..05761d2a74cc 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -245,7 +245,9 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 	BUG_ON(pt->type > NLA_TYPE_MAX);
 
 	if ((nla_attr_len[pt->type] && attrlen != nla_attr_len[pt->type]) ||
-	    (pt->type == NLA_EXACT_LEN_WARN && attrlen != pt->len)) {
+	    (pt->type == NLA_EXACT_LEN &&
+	     pt->validation_type == NLA_VALIDATE_WARN_TOO_LONG &&
+	     attrlen != pt->len)) {
 		pr_warn_ratelimited("netlink: '%s': attribute type %d has an invalid length.\n",
 				    current->comm, type);
 		if (validate & NL_VALIDATE_STRICT_ATTRS) {
@@ -256,11 +258,6 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 	}
 
 	switch (pt->type) {
-	case NLA_EXACT_LEN:
-		if (attrlen != pt->len)
-			goto out_err;
-		break;
-
 	case NLA_REJECT:
 		if (extack && pt->reject_message) {
 			NL_SET_BAD_ATTR(extack, nla);
@@ -373,6 +370,13 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
 			goto out_err;
 		break;
 
+	case NLA_EXACT_LEN:
+		if (pt->validation_type != NLA_VALIDATE_WARN_TOO_LONG) {
+			if (attrlen != pt->len)
+				goto out_err;
+			break;
+		}
+		/* fall through */
 	default:
 		if (pt->len)
 			minlen = pt->len;
-- 
2.17.2


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 5/6] netlink: factor out policy range helpers
  2019-04-26 12:13 [PATCH 0/6] netlink: expose policies to userspace Johannes Berg
                   ` (3 preceding siblings ...)
  2019-04-26 12:13 ` [PATCH 4/6] netlink: remove NLA_EXACT_LEN_WARN Johannes Berg
@ 2019-04-26 12:13 ` Johannes Berg
  2019-04-26 12:13 ` [PATCH 6/6] netlink: add infrastructure to expose policies to userspace Johannes Berg
  5 siblings, 0 replies; 13+ messages in thread
From: Johannes Berg @ 2019-04-26 12:13 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Pablo Neira Ayuso, Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

Add helpers to get the policy's signed/unsigned range
validation data.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/netlink.h |  5 +++
 lib/nlattr.c          | 95 +++++++++++++++++++++++++++++++++----------
 2 files changed, 79 insertions(+), 21 deletions(-)

diff --git a/include/net/netlink.h b/include/net/netlink.h
index 0a02d25fb163..aab0a30021b6 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -1875,4 +1875,9 @@ static inline bool nla_is_last(const struct nlattr *nla, int rem)
 	return nla->nla_len == rem;
 }
 
+void nla_get_range_unsigned(const struct nla_policy *pt,
+			    struct netlink_range_validation *range);
+void nla_get_range_signed(const struct nla_policy *pt,
+			  struct netlink_range_validation_signed *range);
+
 #endif
diff --git a/lib/nlattr.c b/lib/nlattr.c
index 05761d2a74cc..3db7a6984cb0 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -96,25 +96,39 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
 	return 0;
 }
 
-static int nla_validate_int_range_unsigned(const struct nla_policy *pt,
-					   const struct nlattr *nla,
-					   struct netlink_ext_ack *extack)
+void nla_get_range_unsigned(const struct nla_policy *pt,
+			    struct netlink_range_validation *range)
 {
-	struct netlink_range_validation _range = {
-		.min = 0,
-		.max = U64_MAX,
-	}, *range = &_range;
-	u64 value;
-
 	WARN_ON_ONCE(pt->min < 0 || pt->max < 0);
 
+	range->min = 0;
+
+	switch (pt->type) {
+	case NLA_U8:
+		range->max = U8_MAX;
+		break;
+	case NLA_U16:
+		range->max = U16_MAX;
+		break;
+	case NLA_U32:
+		range->max = U32_MAX;
+		break;
+	case NLA_U64:
+	case NLA_MSECS:
+		range->max = U64_MAX;
+		break;
+	default:
+		WARN_ON_ONCE(1);
+		return;
+	}
+
 	switch (pt->validation_type) {
 	case NLA_VALIDATE_RANGE:
 		range->min = pt->min;
 		range->max = pt->max;
 		break;
 	case NLA_VALIDATE_RANGE_PTR:
-		range = pt->range;
+		*range = *pt->range;
 		break;
 	case NLA_VALIDATE_MIN:
 		range->min = pt->min;
@@ -122,7 +136,17 @@ static int nla_validate_int_range_unsigned(const struct nla_policy *pt,
 	case NLA_VALIDATE_MAX:
 		range->max = pt->max;
 		break;
+	default:
+		break;
 	}
+}
+
+static int nla_validate_int_range_unsigned(const struct nla_policy *pt,
+					   const struct nlattr *nla,
+					   struct netlink_ext_ack *extack)
+{
+	struct netlink_range_validation range;
+	u64 value;
 
 	switch (pt->type) {
 	case NLA_U8:
@@ -142,7 +166,9 @@ static int nla_validate_int_range_unsigned(const struct nla_policy *pt,
 		return -EINVAL;
 	}
 
-	if (value < range->min || value > range->max) {
+	nla_get_range_unsigned(pt, &range);
+
+	if (value < range.min || value > range.max) {
 		NL_SET_ERR_MSG_ATTR(extack, nla,
 				    "integer out of range");
 		return -ERANGE;
@@ -151,15 +177,30 @@ static int nla_validate_int_range_unsigned(const struct nla_policy *pt,
 	return 0;
 }
 
-static int nla_validate_int_range_signed(const struct nla_policy *pt,
-					 const struct nlattr *nla,
-					 struct netlink_ext_ack *extack)
+void nla_get_range_signed(const struct nla_policy *pt,
+			  struct netlink_range_validation_signed *range)
 {
-	struct netlink_range_validation_signed _range = {
-		.min = S64_MIN,
-		.max = S64_MAX,
-	}, *range = &_range;
-	s64 value;
+	switch (pt->type) {
+	case NLA_S8:
+		range->min = S8_MIN;
+		range->max = S8_MAX;
+		break;
+	case NLA_S16:
+		range->min = S16_MIN;
+		range->max = S16_MAX;
+		break;
+	case NLA_S32:
+		range->min = S32_MIN;
+		range->max = S32_MAX;
+		break;
+	case NLA_S64:
+		range->min = S64_MIN;
+		range->max = S64_MAX;
+		break;
+	default:
+		WARN_ON_ONCE(1);
+		return;
+	}
 
 	switch (pt->validation_type) {
 	case NLA_VALIDATE_RANGE:
@@ -167,7 +208,7 @@ static int nla_validate_int_range_signed(const struct nla_policy *pt,
 		range->max = pt->max;
 		break;
 	case NLA_VALIDATE_RANGE_PTR:
-		range = pt->range_signed;
+		*range = *pt->range_signed;
 		break;
 	case NLA_VALIDATE_MIN:
 		range->min = pt->min;
@@ -175,7 +216,17 @@ static int nla_validate_int_range_signed(const struct nla_policy *pt,
 	case NLA_VALIDATE_MAX:
 		range->max = pt->max;
 		break;
+	default:
+		break;
 	}
+}
+
+static int nla_validate_int_range_signed(const struct nla_policy *pt,
+					 const struct nlattr *nla,
+					 struct netlink_ext_ack *extack)
+{
+	struct netlink_range_validation_signed range;
+	s64 value;
 
 	switch (pt->type) {
 	case NLA_S8:
@@ -194,7 +245,9 @@ static int nla_validate_int_range_signed(const struct nla_policy *pt,
 		return -EINVAL;
 	}
 
-	if (value < range->min || value > range->max) {
+	nla_get_range_signed(pt, &range);
+
+	if (value < range.min || value > range.max) {
 		NL_SET_ERR_MSG_ATTR(extack, nla,
 				    "integer out of range");
 		return -ERANGE;
-- 
2.17.2


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 6/6] netlink: add infrastructure to expose policies to userspace
  2019-04-26 12:13 [PATCH 0/6] netlink: expose policies to userspace Johannes Berg
                   ` (4 preceding siblings ...)
  2019-04-26 12:13 ` [PATCH 5/6] netlink: factor out policy range helpers Johannes Berg
@ 2019-04-26 12:13 ` Johannes Berg
  2019-04-26 18:21   ` Pablo Neira Ayuso
  5 siblings, 1 reply; 13+ messages in thread
From: Johannes Berg @ 2019-04-26 12:13 UTC (permalink / raw)
  To: netdev; +Cc: David Ahern, Pablo Neira Ayuso, Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

Add, and use in generic netlink, helpers to dump out a netlink
policy to userspace, including all the range validation data,
nested policies etc.

This lets userspace discover what the kernel understands.

For families/commands other than generic netlink, the helpers
need to be used directly in an appropriate command, or we can
add some infrastructure (a new netlink family) that those can
register their policies with for introspection. I'm not that
familiar with non-generic netlink, so that's left out for now.

The data exposed to userspace also includes min and max length
for binary/string data, I've done that instead of letting the
userspace tools figure out whether min/max is intended based
on the type so that we can extend this later in the kernel, we
might want to just use the range data for example.

Because of this, I opted to not directly expose the NLA_*
values, even if some of them are already exposed via BPF, as
with min/max length we don't need to have different types here
for NLA_BINARY/NLA_MIN_LEN/NLA_EXACT_LEN, we just make them
all NL_ATTR_TYPE_BINARY with min/max length optionally set.

Similarly, we don't really need NLA_MSECS, and perhaps can
remove it in the future - but not if we encode it into the
userspace API now. It gets mapped to NL_ATTR_TYPE_U64 here.

Note that the exposing here corresponds to the strict policy
interpretation, and NLA_UNSPEC items are omitted entirely.
To get those, change them to NLA_MIN_LEN which behaves in
exactly the same way, but is exposed.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/netlink.h          |   6 +
 include/uapi/linux/genetlink.h |   2 +
 include/uapi/linux/netlink.h   | 103 +++++++++++
 net/netlink/Makefile           |   2 +-
 net/netlink/genetlink.c        |  77 +++++++++
 net/netlink/policy.c           | 308 +++++++++++++++++++++++++++++++++
 6 files changed, 497 insertions(+), 1 deletion(-)
 create mode 100644 net/netlink/policy.c

diff --git a/include/net/netlink.h b/include/net/netlink.h
index aab0a30021b6..ca9dc2e6500e 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -1880,4 +1880,10 @@ void nla_get_range_unsigned(const struct nla_policy *pt,
 void nla_get_range_signed(const struct nla_policy *pt,
 			  struct netlink_range_validation_signed *range);
 
+int netlink_policy_dump_start(const struct nla_policy *policy,
+			      unsigned int maxtype,
+			      unsigned long *state);
+bool netlink_policy_dump_loop(unsigned long *state);
+int netlink_policy_dump_write(struct sk_buff *skb, unsigned long state);
+
 #endif
diff --git a/include/uapi/linux/genetlink.h b/include/uapi/linux/genetlink.h
index 877f7fa95466..9c0636ec2286 100644
--- a/include/uapi/linux/genetlink.h
+++ b/include/uapi/linux/genetlink.h
@@ -48,6 +48,7 @@ enum {
 	CTRL_CMD_NEWMCAST_GRP,
 	CTRL_CMD_DELMCAST_GRP,
 	CTRL_CMD_GETMCAST_GRP, /* unused */
+	CTRL_CMD_GETPOLICY,
 	__CTRL_CMD_MAX,
 };
 
@@ -62,6 +63,7 @@ enum {
 	CTRL_ATTR_MAXATTR,
 	CTRL_ATTR_OPS,
 	CTRL_ATTR_MCAST_GROUPS,
+	CTRL_ATTR_POLICY,
 	__CTRL_ATTR_MAX,
 };
 
diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
index 0a4d73317759..eac8a6a648ea 100644
--- a/include/uapi/linux/netlink.h
+++ b/include/uapi/linux/netlink.h
@@ -249,4 +249,107 @@ struct nla_bitfield32 {
 	__u32 selector;
 };
 
+/*
+ * policy descriptions - it's specific to each family how this is used
+ * Normally, it should be retrieved via a dump inside another attribute
+ * specifying where it applies.
+ */
+
+/**
+ * enum netlink_attribute_type - type of an attribute
+ * @NL_ATTR_TYPE_INVALID: unused
+ * @NL_ATTR_TYPE_FLAG: flag attribute (present/not present)
+ * @NL_ATTR_TYPE_U8: 8-bit unsigned attribute
+ * @NL_ATTR_TYPE_U16: 16-bit unsigned attribute
+ * @NL_ATTR_TYPE_U32: 32-bit unsigned attribute
+ * @NL_ATTR_TYPE_U64: 64-bit unsigned attribute
+ * @NL_ATTR_TYPE_S8: 8-bit signed attribute
+ * @NL_ATTR_TYPE_S16: 16-bit signed attribute
+ * @NL_ATTR_TYPE_S32: 32-bit signed attribute
+ * @NL_ATTR_TYPE_S64: 64-bit signed attribute
+ * @NL_ATTR_TYPE_BINARY: binary data, min/max length may be specified
+ * @NL_ATTR_TYPE_STRING: string, min/max length may be specified
+ * @NL_ATTR_TYPE_NUL_STRING: NUL-terminated string,
+ *	min/max length may be specified
+ * @NL_ATTR_TYPE_NESTED: nested, i.e. the content of this attribute
+ *	consists of sub-attributes. The nested policy and maxtype
+ *	inside may be specified.
+ * @NL_ATTR_TYPE_NESTED_ARRAY: nested array, i.e. the content of this
+ *	attribute contains sub-attributes whose type is irrelevant
+ *	(just used to separate the array entries) and each such array
+ *	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
+ */
+enum netlink_attribute_type {
+	NL_ATTR_TYPE_INVALID,
+
+	NL_ATTR_TYPE_FLAG,
+
+	NL_ATTR_TYPE_U8,
+	NL_ATTR_TYPE_U16,
+	NL_ATTR_TYPE_U32,
+	NL_ATTR_TYPE_U64,
+
+	NL_ATTR_TYPE_S8,
+	NL_ATTR_TYPE_S16,
+	NL_ATTR_TYPE_S32,
+	NL_ATTR_TYPE_S64,
+
+	NL_ATTR_TYPE_BINARY,
+	NL_ATTR_TYPE_STRING,
+	NL_ATTR_TYPE_NUL_STRING,
+
+	NL_ATTR_TYPE_NESTED,
+	NL_ATTR_TYPE_NESTED_ARRAY,
+
+	NL_ATTR_TYPE_BITFIELD32,
+};
+
+/**
+ * enum netlink_policy_type_attr - policy type attributes
+ * @NL_POLICY_TYPE_ATTR_UNSPEC: unused
+ * @NL_POLICY_TYPE_ATTR_TYPE: type of the attribute,
+ *	&enum netlink_attribute_type (U32)
+ * @NL_POLICY_TYPE_ATTR_MIN_VALUE_S: minimum value for signed
+ *	integers (S64)
+ * @NL_POLICY_TYPE_ATTR_MAX_VALUE_S: maximum value for signed
+ *	integers (S64)
+ * @NL_POLICY_TYPE_ATTR_MIN_VALUE_U: minimum value for unsigned
+ *	integers (U64)
+ * @NL_POLICY_TYPE_ATTR_MAX_VALUE_U: maximum value for unsigned
+ *	integers (U64)
+ * @NL_POLICY_TYPE_ATTR_MIN_LENGTH: minimum length for binary
+ *	attributes, no minimum if not given (U32)
+ * @NL_POLICY_TYPE_ATTR_MAX_LENGTH: maximum length for binary
+ *	attributes, no maximum if not given (U32)
+ * @NL_POLICY_TYPE_ATTR_POLICY_IDX: sub policy for nested and
+ *	nested array types (U32)
+ * @NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE: maximum sub policy
+ *	attribute for nested and nested array types, this can
+ *	in theory be < the size of the policy pointed to by
+ *	the index, if limited inside the nesting (U32)
+ * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the
+ *	bitfield32 type (U32)
+ * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment
+ */
+enum netlink_policy_type_attr {
+	NL_POLICY_TYPE_ATTR_UNSPEC,
+	NL_POLICY_TYPE_ATTR_TYPE,
+	NL_POLICY_TYPE_ATTR_MIN_VALUE_S,
+	NL_POLICY_TYPE_ATTR_MAX_VALUE_S,
+	NL_POLICY_TYPE_ATTR_MIN_VALUE_U,
+	NL_POLICY_TYPE_ATTR_MAX_VALUE_U,
+	NL_POLICY_TYPE_ATTR_MIN_LENGTH,
+	NL_POLICY_TYPE_ATTR_MAX_LENGTH,
+	NL_POLICY_TYPE_ATTR_POLICY_IDX,
+	NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
+	NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
+	NL_POLICY_TYPE_ATTR_PAD,
+
+	/* keep last */
+	__NL_POLICY_TYPE_ATTR_MAX,
+	NL_POLICY_TYPE_ATTR_MAX = __NL_POLICY_TYPE_ATTR_MAX - 1
+};
+
 #endif /* _UAPI__LINUX_NETLINK_H */
diff --git a/net/netlink/Makefile b/net/netlink/Makefile
index e837917f6c03..fd08054e69b4 100644
--- a/net/netlink/Makefile
+++ b/net/netlink/Makefile
@@ -2,7 +2,7 @@
 # Makefile for the netlink driver.
 #
 
-obj-y  				:= af_netlink.o genetlink.o
+obj-y  				:= af_netlink.o genetlink.o policy.o
 
 obj-$(CONFIG_NETLINK_DIAG)	+= netlink_diag.o
 netlink_diag-y			:= diag.o
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index 7bef05f2cfe7..65167aa82901 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -958,6 +958,79 @@ static int genl_ctrl_event(int event, const struct genl_family *family,
 	return 0;
 }
 
+static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	const struct genl_family *rt;
+	unsigned int fam_id = cb->args[0];
+	int err;
+
+	if (!fam_id) {
+		struct nlattr *tb[CTRL_ATTR_MAX + 1];
+
+		int err = genlmsg_parse(cb->nlh, &genl_ctrl, tb,
+					genl_ctrl.maxattr,
+					genl_ctrl.policy, cb->extack);
+		if (err)
+			return err;
+
+		if (!tb[CTRL_ATTR_FAMILY_ID] && !tb[CTRL_ATTR_FAMILY_NAME])
+			return -EINVAL;
+		if (tb[CTRL_ATTR_FAMILY_ID]) {
+			fam_id = nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
+		} else {
+			rt = genl_family_find_byname(
+				nla_data(tb[CTRL_ATTR_FAMILY_NAME]));
+			if (!rt)
+				return -ENOENT;
+			fam_id = rt->id;
+		}
+	}
+
+	rt = genl_family_find_byid(fam_id);
+	if (!rt)
+		return -ENOENT;
+
+	if (!rt->policy)
+		return -ENODATA;
+
+	err = netlink_policy_dump_start(rt->policy, rt->maxattr, &cb->args[1]);
+	if (err)
+		return err;
+
+	while (netlink_policy_dump_loop(&cb->args[1])) {
+		void *hdr;
+		struct nlattr *nest;
+
+		hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+				  cb->nlh->nlmsg_seq, &genl_ctrl,
+				  NLM_F_MULTI, CTRL_CMD_GETPOLICY);
+		if (!hdr)
+			goto nla_put_failure;
+
+		if (nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, rt->id))
+			goto nla_put_failure;
+
+		nest = nla_nest_start(skb, CTRL_ATTR_POLICY);
+		if (!nest)
+			goto nla_put_failure;
+
+		if (netlink_policy_dump_write(skb, cb->args[1]))
+			goto nla_put_failure;
+
+		nla_nest_end(skb, nest);
+
+		genlmsg_end(skb, hdr);
+		continue;
+
+nla_put_failure:
+		genlmsg_cancel(skb, hdr);
+		break;
+	}
+
+	cb->args[0] = fam_id;
+	return skb->len;
+}
+
 static const struct genl_ops genl_ctrl_ops[] = {
 	{
 		.cmd		= CTRL_CMD_GETFAMILY,
@@ -965,6 +1038,10 @@ static const struct genl_ops genl_ctrl_ops[] = {
 		.doit		= ctrl_getfamily,
 		.dumpit		= ctrl_dumpfamily,
 	},
+	{
+		.cmd		= CTRL_CMD_GETPOLICY,
+		.dumpit		= ctrl_dumppolicy,
+	},
 };
 
 static const struct genl_multicast_group genl_ctrl_groups[] = {
diff --git a/net/netlink/policy.c b/net/netlink/policy.c
new file mode 100644
index 000000000000..f6491853c797
--- /dev/null
+++ b/net/netlink/policy.c
@@ -0,0 +1,308 @@
+// 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 nl_policy_dump {
+	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 nl_policy_dump **statep,
+		      const struct nla_policy *policy,
+		      unsigned int maxtype)
+{
+	struct nl_policy_dump *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)
+			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;
+
+	state->policies[state->n_alloc].policy = policy;
+	state->policies[state->n_alloc].maxtype = maxtype;
+	state->n_alloc = n_alloc;
+	*statep = state;
+
+	return 0;
+}
+
+static unsigned int get_policy_idx(struct nl_policy_dump *state,
+				   const struct nla_policy *policy)
+{
+	unsigned int i;
+
+	for (i = 0; i < state->n_alloc; i++) {
+		if (state->policies[i].policy == policy)
+			return i;
+	}
+
+	WARN_ON_ONCE(1);
+	return -1;
+}
+
+int netlink_policy_dump_start(const struct nla_policy *policy,
+			      unsigned int maxtype,
+                              unsigned long *_state)
+{
+	struct nl_policy_dump *state;
+	unsigned int policy_idx;
+	int err;
+
+	/* also returns 0 if "*_state" is our ERR_PTR() end marker */
+	if (*_state)
+		return 0;
+
+	/*
+	 * walk the policies and nested ones first, and build
+	 * a linear list of them.
+	 */
+
+	state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC),
+			GFP_KERNEL);
+	if (!state)
+		return -ENOMEM;
+	state->n_alloc = INITIAL_POLICIES_ALLOC;
+
+	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;
+			}
+		}
+	}
+
+	*_state = (unsigned long)state;
+
+	return 0;
+}
+
+static bool netlink_policy_dump_finished(struct nl_policy_dump *state)
+{
+	return state->policy_idx >= state->n_alloc ||
+	       !state->policies[state->policy_idx].policy;
+}
+
+bool netlink_policy_dump_loop(unsigned long *_state)
+{
+	struct nl_policy_dump *state = (void *)*_state;
+
+	if (IS_ERR(state))
+		return false;
+
+	if (netlink_policy_dump_finished(state)) {
+		kfree(state);
+		/* store end marker instead of freed state */
+		*_state = (unsigned long)ERR_PTR(-ENOENT);
+		return false;
+	}
+
+	return true;
+}
+
+int netlink_policy_dump_write(struct sk_buff *skb, unsigned long _state)
+{
+	struct nl_policy_dump *state = (void *)_state;
+	const struct nla_policy *pt;
+	struct nlattr *policy, *attr;
+	enum netlink_attribute_type type;
+	bool again;
+
+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;
+
+	attr = nla_nest_start(skb, state->attr_idx);
+	if (!attr)
+		goto nla_put_failure;
+
+	switch (pt->type) {
+	default:
+	case NLA_UNSPEC:
+	case NLA_REJECT:
+		/* skip - use NLA_MIN_LEN to advertise such */
+		nla_nest_cancel(skb, policy);
+		again = true;
+		goto next;
+	case NLA_NESTED:
+		type = NL_ATTR_TYPE_NESTED;
+		/* fall through */
+	case NLA_NESTED_ARRAY:
+		if (pt->type == NLA_NESTED_ARRAY)
+			type = NL_ATTR_TYPE_NESTED_ARRAY;
+		if (pt->nested_policy && pt->len &&
+		    (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX,
+				 get_policy_idx(state, pt->nested_policy)) ||
+		     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;
+
+		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_EXACT_LEN:
+		type = NL_ATTR_TYPE_BINARY;
+		if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH, pt->len) ||
+		    nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, pt->len))
+			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->len && nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH,
+					   pt->len))
+			goto nla_put_failure;
+		break;
+	case NLA_MIN_LEN:
+		type = NL_ATTR_TYPE_BINARY;
+		if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_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;
+
+	/* finish and move state to next attribute */
+	nla_nest_end(skb, attr);
+	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;
+}
-- 
2.17.2


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 6/6] netlink: add infrastructure to expose policies to userspace
  2019-04-26 12:13 ` [PATCH 6/6] netlink: add infrastructure to expose policies to userspace Johannes Berg
@ 2019-04-26 18:21   ` Pablo Neira Ayuso
  2019-04-26 19:22     ` Johannes Berg
  0 siblings, 1 reply; 13+ messages in thread
From: Pablo Neira Ayuso @ 2019-04-26 18:21 UTC (permalink / raw)
  To: Johannes Berg; +Cc: netdev, David Ahern, Johannes Berg

On Fri, Apr 26, 2019 at 02:13:06PM +0200, Johannes Berg wrote:
> diff --git a/include/uapi/linux/genetlink.h b/include/uapi/linux/genetlink.h
> index 877f7fa95466..9c0636ec2286 100644
> --- a/include/uapi/linux/genetlink.h
> +++ b/include/uapi/linux/genetlink.h
> @@ -48,6 +48,7 @@ enum {
>  	CTRL_CMD_NEWMCAST_GRP,
>  	CTRL_CMD_DELMCAST_GRP,
>  	CTRL_CMD_GETMCAST_GRP, /* unused */
> +	CTRL_CMD_GETPOLICY,

It would be good to single entry point to request descriptions, ie.
have a NETLINK_DESC family for this. Thus, we could use the same
program to pull for policy/command descriptions without updating an
array that includes the command to get the policy _for each
subsystem_.

The program to inquire for policy/command descriptions would be very
much the same along time, no need for updates to include new command
type for each subsystem.

It would just spin over NETLINK_DESC discovering subsystems ID that we
support.

In genetlink, I understand this can be exception if you prefer so, ie.
I'll be fine with this CTRL_CMD_GETPOLICY if that makes it look nicer
in terms of integration with the existing infrastructure. But for
other netlink subsystems, NETLINK_DESC allows you to pull the
description for genetlink itself, not the internal subsystems.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 6/6] netlink: add infrastructure to expose policies to userspace
  2019-04-26 18:21   ` Pablo Neira Ayuso
@ 2019-04-26 19:22     ` Johannes Berg
  2019-04-27 11:25       ` Pablo Neira Ayuso
  0 siblings, 1 reply; 13+ messages in thread
From: Johannes Berg @ 2019-04-26 19:22 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netdev, David Ahern

On Fri, 2019-04-26 at 20:21 +0200, Pablo Neira Ayuso wrote:
> On Fri, Apr 26, 2019 at 02:13:06PM +0200, Johannes Berg wrote:
> > diff --git a/include/uapi/linux/genetlink.h b/include/uapi/linux/genetlink.h
> > index 877f7fa95466..9c0636ec2286 100644
> > --- a/include/uapi/linux/genetlink.h
> > +++ b/include/uapi/linux/genetlink.h
> > @@ -48,6 +48,7 @@ enum {
> >  	CTRL_CMD_NEWMCAST_GRP,
> >  	CTRL_CMD_DELMCAST_GRP,
> >  	CTRL_CMD_GETMCAST_GRP, /* unused */
> > +	CTRL_CMD_GETPOLICY,
> 
> It would be good to single entry point to request descriptions, ie.
> have a NETLINK_DESC family for this. Thus, we could use the same
> program to pull for policy/command descriptions without updating an
> array that includes the command to get the policy _for each
> subsystem_.
> 
> The program to inquire for policy/command descriptions would be very
> much the same along time, no need for updates to include new command
> type for each subsystem.
> 
> It would just spin over NETLINK_DESC discovering subsystems ID that we
> support.

No objection to that. The only problem I think is that there's no
natural point to "hang" the policy data, mostly it's just used in the
nla_parse() or nla_validate() calls in the code. Basically, it's code-
driven, not data-driven like generic netlink.

Take IFLA_AF_SPEC for example. To validate that, we end up calling into
validate_link_af() which is defined in IPv4 and IPv6, rather than having
the inet_af_policy/inet6_af_policy available and doing it in the caller
(also, validate_link_af() does some additional validation, though for
IFLA_INET_CONF that can actually now be expressed as a nested policy
inside inet_af_policy, I believe).

So to really generalize that you'd have change this - at least as far as
the netlink attribute validation is concerned, not the extra code - to
be data driven, rather than coded.

Then you could use and expose that data pretty easily.

> In genetlink, I understand this can be exception if you prefer so, ie.
> I'll be fine with this CTRL_CMD_GETPOLICY if that makes it look nicer
> in terms of integration with the existing infrastructure. But for
> other netlink subsystems, NETLINK_DESC allows you to pull the
> description for genetlink itself, not the internal subsystems.

I think genetlink it really makes more sense this way - the genetlink
family it basically the introspection point already: it lets you
discover which families there are, which commands they support,
multicast groups they have etc.

It's also easier to deal with in userspace because you already need to
deal with the genetlink family itself (to get your family ID), so
interacting with another NETLINK_DESC family would be annoying.

However, if we wanted to generalize that I guess we could make
NETLINK_DESC able to cover generic netlink as well, providing two entry
points to the same information? Very small amount of code, I guess.

johannes


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 6/6] netlink: add infrastructure to expose policies to userspace
  2019-04-26 19:22     ` Johannes Berg
@ 2019-04-27 11:25       ` Pablo Neira Ayuso
  2019-04-28 20:03         ` Johannes Berg
  0 siblings, 1 reply; 13+ messages in thread
From: Pablo Neira Ayuso @ 2019-04-27 11:25 UTC (permalink / raw)
  To: Johannes Berg; +Cc: netdev, David Ahern

On Fri, Apr 26, 2019 at 09:22:20PM +0200, Johannes Berg wrote:
> On Fri, 2019-04-26 at 20:21 +0200, Pablo Neira Ayuso wrote:
> > On Fri, Apr 26, 2019 at 02:13:06PM +0200, Johannes Berg wrote:
> > > diff --git a/include/uapi/linux/genetlink.h b/include/uapi/linux/genetlink.h
> > > index 877f7fa95466..9c0636ec2286 100644
> > > --- a/include/uapi/linux/genetlink.h
> > > +++ b/include/uapi/linux/genetlink.h
> > > @@ -48,6 +48,7 @@ enum {
> > >  	CTRL_CMD_NEWMCAST_GRP,
> > >  	CTRL_CMD_DELMCAST_GRP,
> > >  	CTRL_CMD_GETMCAST_GRP, /* unused */
> > > +	CTRL_CMD_GETPOLICY,
> > 
> > It would be good to single entry point to request descriptions, ie.
> > have a NETLINK_DESC family for this. Thus, we could use the same
> > program to pull for policy/command descriptions without updating an
> > array that includes the command to get the policy _for each
> > subsystem_.
> > 
> > The program to inquire for policy/command descriptions would be very
> > much the same along time, no need for updates to include new command
> > type for each subsystem.
> > 
> > It would just spin over NETLINK_DESC discovering subsystems ID that we
> > support.
> 
> No objection to that. The only problem I think is that there's no
> natural point to "hang" the policy data, mostly it's just used in the
> nla_parse() or nla_validate() calls in the code. Basically, it's code-
> driven, not data-driven like generic netlink.
> 
> Take IFLA_AF_SPEC for example. To validate that, we end up calling into
> validate_link_af() which is defined in IPv4 and IPv6, rather than having
> the inet_af_policy/inet6_af_policy available and doing it in the caller
> (also, validate_link_af() does some additional validation, though for
> IFLA_INET_CONF that can actually now be expressed as a nested policy
> inside inet_af_policy, I believe).
> 
> So to really generalize that you'd have change this - at least as far as
> the netlink attribute validation is concerned, not the extra code - to
> be data driven, rather than coded.
> 
> Then you could use and expose that data pretty easily.

I see, agreed, we would need to rework this to make data driven, so
this nest:

IFLA_AF_SPEC (nest)
  family = AF_INET6 (nest)
    IFLA_INET6_...

IFLA_AF_SPEC (nest)
  family = AF_INET4 (nest)
    IFLA_INET_...

needs a nla_policy definition for each family.

We can do this rework progressively, as we start exposing description
though the list of nla_policy structure for each subsystem.

> > In genetlink, I understand this can be exception if you prefer so, ie.
> > I'll be fine with this CTRL_CMD_GETPOLICY if that makes it look nicer
> > in terms of integration with the existing infrastructure. But for
> > other netlink subsystems, NETLINK_DESC allows you to pull the
> > description for genetlink itself, not the internal subsystems.
> 
> I think genetlink it really makes more sense this way - the genetlink
> family it basically the introspection point already: it lets you
> discover which families there are, which commands they support,
> multicast groups they have etc.
> 
> It's also easier to deal with in userspace because you already need to
> deal with the genetlink family itself (to get your family ID), so
> interacting with another NETLINK_DESC family would be annoying.
> 
> However, if we wanted to generalize that I guess we could make
> NETLINK_DESC able to cover generic netlink as well, providing two entry
> points to the same information? Very small amount of code, I guess.

Makes sense to me.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 6/6] netlink: add infrastructure to expose policies to userspace
  2019-04-27 11:25       ` Pablo Neira Ayuso
@ 2019-04-28 20:03         ` Johannes Berg
  0 siblings, 0 replies; 13+ messages in thread
From: Johannes Berg @ 2019-04-28 20:03 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netdev, David Ahern

On Sat, 2019-04-27 at 13:25 +0200, Pablo Neira Ayuso wrote:
> 
> > Take IFLA_AF_SPEC for example. To validate that, we end up calling into
> > validate_link_af() which is defined in IPv4 and IPv6, rather than having
> > the inet_af_policy/inet6_af_policy available and doing it in the caller
> > (also, validate_link_af() does some additional validation, though for
> > IFLA_INET_CONF that can actually now be expressed as a nested policy
> > inside inet_af_policy, I believe).
> > 
> > So to really generalize that you'd have change this - at least as far as
> > the netlink attribute validation is concerned, not the extra code - to
> > be data driven, rather than coded.
> > 
> > Then you could use and expose that data pretty easily.
> 
> I see, agreed, we would need to rework this to make data driven, so
> this nest:
> 
> IFLA_AF_SPEC (nest)
>   family = AF_INET6 (nest)
>     IFLA_INET6_...
> 
> IFLA_AF_SPEC (nest)
>   family = AF_INET4 (nest)
>     IFLA_INET_...
> 
> needs a nla_policy definition for each family.

Right.

> We can do this rework progressively, as we start exposing description
> though the list of nla_policy structure for each subsystem.

Sure, of course.

I'm just saying it's not easy right now to provide that, as all of this
is mostly code-driven today. You have
	rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, 0);

but the actual parsing happens WAY down in the code, multiple levels of
functions deep that are sometimes shared etc.

I'm not even sure how we could express this in the policy - right now we
only have
	[ILFA_AF_SPEC] = NLA_POLICY_NESTED(xyz_policy)
but that cannot capture the fact that the inner attributes depend on
another attribute or some other circumstance.

Oh, actually, it's not hard in this case because the AF is prescribed by
the type, so we'd need something like

struct nla_policy af_spec_policy[...] = {
	[AF_INET] = NLA_POLICY_NESTED(af_inet_policy),
	[AF_INET6] = NLA_POLICY_NESTED(af_inet6_policy),
	// .. also for MPLS
};
struct nla_policy ifla_policy[...] = {
	[IFLA_AF_SPEC] = NLA_POLICY_NESTED(af_spec_policy),
};

So it's actually easy to do and we'd just need to change the
rtnl_register() to something like rtnl_register_with_policy() or
something like that, and then we'd have all the data we want directly
available and would actually save quite a bit of code, including
possibly the whole validate_link_af() method pointer.

johannes


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/6] netlink: extend policy range validation
  2019-04-26 12:13 ` [PATCH 2/6] netlink: extend policy range validation Johannes Berg
@ 2019-04-30  2:49   ` David Miller
  2019-04-30  6:51     ` Johannes Berg
  0 siblings, 1 reply; 13+ messages in thread
From: David Miller @ 2019-04-30  2:49 UTC (permalink / raw)
  To: johannes; +Cc: netdev, dsa, pablo, johannes.berg

From: Johannes Berg <johannes@sipsolutions.net>
Date: Fri, 26 Apr 2019 14:13:02 +0200

>   *                         NLA_POLICY_RANGE() macros.
> + *    NLA_U8,
> + *    NLA_U16,
> + *    NLA_U32,
> + *    NLA_U64              If the validation_type field instead is set to
> + *                         NLA_VALIDATE_RANGE_PTR, `range' must be a pointer
> + *                         to a struct netlink_range_validation that indicates
> + *                         the min/max values.
> + *                         Use NLA_POLICY_FULL_RANGE().
> + *    NLA_S8,
> + *    NLA_S16,
> + *    NLA_S32,
> + *    NLA_S64              If the validation_type field instead is set to
> + *                         NLA_VALIDATE_RANGE_PTR, `range_signed' must be a
> + *                         pointer to a struct netlink_range_validation_signed
> + *                         that indicates the min/max values.
> + *                         Use NLA_POLICY_FULL_RANGE_SIGNED().

Documentation and datastructure says that "range_signed" member should be set
for signed ranges, however:

> +#define NLA_POLICY_FULL_RANGE(tp, _range) {		\
> +	.type = NLA_ENSURE_UINT_TYPE(tp),		\
> +	.validation_type = NLA_VALIDATE_RANGE_PTR,	\
> +	.range = _range,				\
> +}
> +
> +#define NLA_POLICY_FULL_RANGE_SIGNED(tp, _range) {	\
> +	.type = NLA_ENSURE_SINT_TYPE(tp),		\
> +	.validation_type = NLA_VALIDATE_RANGE_PTR,	\
> +	.range = _range,				\
> +}

The NLA_POLICY_FULL_RANGE_SIGNED macros sets 'range' not 'range_signed'.

Also, since range and range_signed are in a union however there is only one
NLA_VALIDATE_RANGE_PTR type, how does one differentiate between signed and
unsigned ranges exactly?

Thanks.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/6] netlink: extend policy range validation
  2019-04-30  2:49   ` David Miller
@ 2019-04-30  6:51     ` Johannes Berg
  0 siblings, 0 replies; 13+ messages in thread
From: Johannes Berg @ 2019-04-30  6:51 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, dsa, pablo

On Mon, 2019-04-29 at 22:49 -0400, David Miller wrote:
> From: Johannes Berg <johannes@sipsolutions.net>
> Date: Fri, 26 Apr 2019 14:13:02 +0200
> 
> >   *                         NLA_POLICY_RANGE() macros.
> > + *    NLA_U8,
> > + *    NLA_U16,
> > + *    NLA_U32,
> > + *    NLA_U64              If the validation_type field instead is set to
> > + *                         NLA_VALIDATE_RANGE_PTR, `range' must be a pointer
> > + *                         to a struct netlink_range_validation that indicates
> > + *                         the min/max values.
> > + *                         Use NLA_POLICY_FULL_RANGE().
> > + *    NLA_S8,
> > + *    NLA_S16,
> > + *    NLA_S32,
> > + *    NLA_S64              If the validation_type field instead is set to
> > + *                         NLA_VALIDATE_RANGE_PTR, `range_signed' must be a
> > + *                         pointer to a struct netlink_range_validation_signed
> > + *                         that indicates the min/max values.
> > + *                         Use NLA_POLICY_FULL_RANGE_SIGNED().
> 
> Documentation and datastructure says that "range_signed" member should be set
> for signed ranges, however:
> 
> > +#define NLA_POLICY_FULL_RANGE(tp, _range) {          \
> > +     .type = NLA_ENSURE_UINT_TYPE(tp),               \
> > +     .validation_type = NLA_VALIDATE_RANGE_PTR,      \
> > +     .range = _range,                                \
> > +}
> > +
> > +#define NLA_POLICY_FULL_RANGE_SIGNED(tp, _range) {   \
> > +     .type = NLA_ENSURE_SINT_TYPE(tp),               \
> > +     .validation_type = NLA_VALIDATE_RANGE_PTR,      \
> > +     .range = _range,                                \
> > +}
> 
> The NLA_POLICY_FULL_RANGE_SIGNED macros sets 'range' not 'range_signed'.

D'oh. Copy/paste error, and I must've missed the compiler warning that
should appear here on usage then. At least I'm pretty sure I tested that
with the policy exposition patch.

Will fix.

> Also, since range and range_signed are in a union however there is only one
> NLA_VALIDATE_RANGE_PTR type, how does one differentiate between signed and
> unsigned ranges exactly?

Based on the type - NLA_S* or NLA_U*. See the NLA_ENSURE_SINT_TYPE() and
NLA_ENSURE_UINT_TYPE() in the macros - that ensures you can only use
NLA_POLICY_FULL_RANGE_SIGNED() with NLA_S*, and NLA_POLICY_FULL_RANGE()
with NLA_U*.

johannes


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2019-04-30  6:51 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-26 12:13 [PATCH 0/6] netlink: expose policies to userspace Johannes Berg
2019-04-26 12:13 ` [PATCH 1/6] netlink: remove type-unsafe validation_data pointer Johannes Berg
2019-04-26 12:13 ` [PATCH 2/6] netlink: extend policy range validation Johannes Berg
2019-04-30  2:49   ` David Miller
2019-04-30  6:51     ` Johannes Berg
2019-04-26 12:13 ` [PATCH 3/6] netlink: allow NLA_MSECS to have " Johannes Berg
2019-04-26 12:13 ` [PATCH 4/6] netlink: remove NLA_EXACT_LEN_WARN Johannes Berg
2019-04-26 12:13 ` [PATCH 5/6] netlink: factor out policy range helpers Johannes Berg
2019-04-26 12:13 ` [PATCH 6/6] netlink: add infrastructure to expose policies to userspace Johannes Berg
2019-04-26 18:21   ` Pablo Neira Ayuso
2019-04-26 19:22     ` Johannes Berg
2019-04-27 11:25       ` Pablo Neira Ayuso
2019-04-28 20:03         ` Johannes Berg

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.