netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes
@ 2024-10-07  9:49 Florian Westphal
  2024-10-07  9:49 ` [PATCH libnftnl 1/5] expr: add and use incomplete tag Florian Westphal
                   ` (5 more replies)
  0 siblings, 6 replies; 16+ messages in thread
From: Florian Westphal @ 2024-10-07  9:49 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

This extends linftnl/nftables to indicate incomplete expressions/sets.

When using old nft binary that cannot list a new expression, nft already
prints an error with the name of the unknown expression.

Extend libnftnl to also make an annotation when a known expression has
an unknown attribute included in the dump, then extend nftables to also
display this to the user.

Debug out out will include the [incomplete] tag for each affected
expression.

Nftables will append '"# Unknown features used (old nft version?)"'
comment to the rule resp. the set defintion.

I added new APIs because existing nftnl_expr_get() can't be re-used,
inserting a new common attribute like NFTNL_EXPR_COMPLETE will break ABI.

It would make sense to also add
nftnl_XXX_complete functions for table, chains, objects and flowtables so we
have coverage for all supported types in one go, but I think its better
to first check for feedback before doing this.

libnftnl:
Florian Westphal (3):
  expr: add and use incomplete tag
  sets: add and use incomplete tag
  libnftnl: add api to query dissection state

 include/data_reg.h      |  1 +
 include/expr.h          |  1 +
 include/libnftnl/expr.h |  2 ++
 include/libnftnl/set.h  |  1 +
 include/set.h           |  1 +
 src/expr.c              |  6 ++++++
 src/expr/bitwise.c      |  8 +++++---
 src/expr/byteorder.c    |  9 ++++++---
 src/expr/cmp.c          |  9 ++++++---
 src/expr/connlimit.c    |  9 ++++++---
 src/expr/counter.c      |  9 ++++++---
 src/expr/ct.c           |  9 ++++++---
 src/expr/data_reg.c     | 19 +++++++++++++------
 src/expr/dup.c          |  9 ++++++---
 src/expr/dynset.c       |  9 ++++++---
 src/expr/exthdr.c       |  8 +++++---
 src/expr/fib.c          |  9 ++++++---
 src/expr/flow_offload.c |  9 ++++++---
 src/expr/fwd.c          |  8 +++++---
 src/expr/hash.c         |  8 +++++---
 src/expr/immediate.c    |  8 +++++---
 src/expr/inner.c        |  8 +++++---
 src/expr/last.c         |  8 +++++---
 src/expr/limit.c        |  8 +++++---
 src/expr/log.c          |  8 +++++---
 src/expr/lookup.c       |  8 +++++---
 src/expr/masq.c         |  8 +++++---
 src/expr/match.c        |  8 +++++---
 src/expr/meta.c         |  6 ++++++
 src/expr/nat.c          |  8 +++++---
 src/expr/numgen.c       |  8 +++++---
 src/expr/objref.c       |  8 +++++---
 src/expr/osf.c          |  9 +++++----
 src/expr/payload.c      |  8 +++++---
 src/expr/queue.c        |  9 ++++++---
 src/expr/quota.c        |  8 +++++---
 src/expr/range.c        |  8 +++++---
 src/expr/redir.c        |  8 +++++---
 src/expr/reject.c       |  9 ++++++---
 src/expr/rt.c           |  9 ++++++---
 src/expr/socket.c       |  9 ++++++---
 src/expr/synproxy.c     | 16 ++++++++--------
 src/expr/target.c       |  9 ++++++---
 src/expr/tproxy.c       |  8 +++++---
 src/expr/tunnel.c       |  8 +++++---
 src/expr/xfrm.c         |  8 +++++---
 src/libnftnl.map        |  5 +++++
 src/rule.c              |  5 +++++
 src/set.c               |  6 ++++++
 src/set_elem.c          |  5 +++++
 50 files changed, 259 insertions(+), 126 deletions(-)

nft:
Florian Westphal (2):
      netlink: tell user if libnftnl detected unknown attributes/features
      sets: inform user when set definition contains unknown attributes

 include/netlink.h         |    1 +
 include/rule.h            |    2 ++
 src/netlink.c             |    3 +++
 src/netlink_delinearize.c |   24 ++++++++++++++++++++++++
 src/rule.c                |    5 +++++
 5 files changed, 35 insertions(+)
-- 
2.45.2


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

* [PATCH libnftnl 1/5] expr: add and use incomplete tag
  2024-10-07  9:49 [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes Florian Westphal
@ 2024-10-07  9:49 ` Florian Westphal
  2024-10-08 11:13   ` Pablo Neira Ayuso
  2024-10-07  9:49 ` [PATCH libnftnl 2/5] sets: " Florian Westphal
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: Florian Westphal @ 2024-10-07  9:49 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Extend netlink dump decoder functions to set
expr->incomplete marker if there are unrecognized attributes
set in the kernel dump.

This can be used by frontend tools to provide a warning to the user
that the rule dump might be incomplete.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/expr.h          |  1 +
 src/expr/bitwise.c      |  8 +++++---
 src/expr/byteorder.c    |  9 ++++++---
 src/expr/cmp.c          |  9 ++++++---
 src/expr/connlimit.c    |  9 ++++++---
 src/expr/counter.c      |  9 ++++++---
 src/expr/ct.c           |  9 ++++++---
 src/expr/data_reg.c     | 13 +++++++------
 src/expr/dup.c          |  9 ++++++---
 src/expr/dynset.c       |  9 ++++++---
 src/expr/exthdr.c       |  8 +++++---
 src/expr/fib.c          |  9 ++++++---
 src/expr/flow_offload.c |  9 ++++++---
 src/expr/fwd.c          |  8 +++++---
 src/expr/hash.c         |  8 +++++---
 src/expr/immediate.c    |  8 +++++---
 src/expr/inner.c        |  8 +++++---
 src/expr/last.c         |  8 +++++---
 src/expr/limit.c        |  8 +++++---
 src/expr/log.c          |  8 +++++---
 src/expr/lookup.c       |  8 +++++---
 src/expr/masq.c         |  8 +++++---
 src/expr/match.c        |  8 +++++---
 src/expr/meta.c         |  6 ++++++
 src/expr/nat.c          |  8 +++++---
 src/expr/numgen.c       |  8 +++++---
 src/expr/objref.c       |  8 +++++---
 src/expr/osf.c          |  9 +++++----
 src/expr/payload.c      |  8 +++++---
 src/expr/queue.c        |  9 ++++++---
 src/expr/quota.c        |  8 +++++---
 src/expr/range.c        |  8 +++++---
 src/expr/redir.c        |  8 +++++---
 src/expr/reject.c       |  9 ++++++---
 src/expr/rt.c           |  9 ++++++---
 src/expr/socket.c       |  9 ++++++---
 src/expr/synproxy.c     | 16 ++++++++--------
 src/expr/target.c       |  9 ++++++---
 src/expr/tproxy.c       |  8 +++++---
 src/expr/tunnel.c       |  8 +++++---
 src/expr/xfrm.c         |  8 +++++---
 41 files changed, 221 insertions(+), 126 deletions(-)

diff --git a/include/expr.h b/include/expr.h
index be45e954df5b..b1b724f1272f 100644
--- a/include/expr.h
+++ b/include/expr.h
@@ -6,6 +6,7 @@ struct expr_ops;
 struct nftnl_expr {
 	struct list_head	head;
 	uint32_t		flags;
+	bool			incomplete;
 	struct expr_ops		*ops;
 	uint8_t			data[];
 };
diff --git a/src/expr/bitwise.c b/src/expr/bitwise.c
index e99131a090ed..46346712e462 100644
--- a/src/expr/bitwise.c
+++ b/src/expr/bitwise.c
@@ -97,9 +97,6 @@ static int nftnl_expr_bitwise_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_BITWISE_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_BITWISE_SREG:
 	case NFTA_BITWISE_DREG:
@@ -114,6 +111,9 @@ static int nftnl_expr_bitwise_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_BITWISE_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -169,6 +169,8 @@ nftnl_expr_bitwise_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_bitwise_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_BITWISE_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_BITWISE_SREG]) {
 		bitwise->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_BITWISE_SREG]));
 		e->flags |= (1 << NFTNL_EXPR_BITWISE_SREG);
diff --git a/src/expr/byteorder.c b/src/expr/byteorder.c
index 383e80d57b44..43f16cd8aa66 100644
--- a/src/expr/byteorder.c
+++ b/src/expr/byteorder.c
@@ -86,9 +86,6 @@ static int nftnl_expr_byteorder_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_BYTEORDER_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_BYTEORDER_SREG:
 	case NFTA_BYTEORDER_DREG:
@@ -98,6 +95,9 @@ static int nftnl_expr_byteorder_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_BYTEORDER_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -141,6 +141,9 @@ nftnl_expr_byteorder_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_byteorder_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_BYTEORDER_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_BYTEORDER_SREG]) {
 		byteorder->sreg =
 			ntohl(mnl_attr_get_u32(tb[NFTA_BYTEORDER_SREG]));
diff --git a/src/expr/cmp.c b/src/expr/cmp.c
index d1f0f64a56b3..b04ddfd6f8e1 100644
--- a/src/expr/cmp.c
+++ b/src/expr/cmp.c
@@ -72,9 +72,6 @@ static int nftnl_expr_cmp_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_CMP_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_CMP_SREG:
 	case NFTA_CMP_OP:
@@ -85,6 +82,9 @@ static int nftnl_expr_cmp_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_CMP_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -119,6 +119,9 @@ nftnl_expr_cmp_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_cmp_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_CMP_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_CMP_SREG]) {
 		cmp->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_CMP_SREG]));
 		e->flags |= (1 << NFTA_CMP_SREG);
diff --git a/src/expr/connlimit.c b/src/expr/connlimit.c
index fcac8bf170ac..6974be6b2808 100644
--- a/src/expr/connlimit.c
+++ b/src/expr/connlimit.c
@@ -64,15 +64,15 @@ static int nftnl_expr_connlimit_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_CONNLIMIT_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_CONNLIMIT_COUNT:
 	case NFTA_CONNLIMIT_FLAGS:
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_CONNLIMIT_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -101,6 +101,9 @@ nftnl_expr_connlimit_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_connlimit_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_CONNLIMIT_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_CONNLIMIT_COUNT]) {
 		connlimit->count =
 			ntohl(mnl_attr_get_u32(tb[NFTA_CONNLIMIT_COUNT]));
diff --git a/src/expr/counter.c b/src/expr/counter.c
index cef911908981..56c1fd09ca1d 100644
--- a/src/expr/counter.c
+++ b/src/expr/counter.c
@@ -66,15 +66,15 @@ static int nftnl_expr_counter_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_COUNTER_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_COUNTER_BYTES:
 	case NFTA_COUNTER_PACKETS:
 		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_COUNTER_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -101,6 +101,9 @@ nftnl_expr_counter_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_counter_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_COUNTER_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_COUNTER_BYTES]) {
 		ctr->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_COUNTER_BYTES]));
 		e->flags |= (1 << NFTNL_EXPR_CTR_BYTES);
diff --git a/src/expr/ct.c b/src/expr/ct.c
index bea0522d8937..caeefced189d 100644
--- a/src/expr/ct.c
+++ b/src/expr/ct.c
@@ -82,9 +82,6 @@ static int nftnl_expr_ct_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_CT_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_CT_KEY:
 	case NFTA_CT_DREG:
@@ -96,6 +93,9 @@ static int nftnl_expr_ct_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_CT_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -126,6 +126,9 @@ nftnl_expr_ct_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_ct_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_CT_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_CT_KEY]) {
 		ct->key = ntohl(mnl_attr_get_u32(tb[NFTA_CT_KEY]));
 		e->flags |= (1 << NFTNL_EXPR_CT_KEY);
diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c
index d2ccf2e8dc68..149d52aea9b4 100644
--- a/src/expr/data_reg.c
+++ b/src/expr/data_reg.c
@@ -83,9 +83,6 @@ static int nftnl_data_parse_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_DATA_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_DATA_VALUE:
 		if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
@@ -95,7 +92,11 @@ static int nftnl_data_parse_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_DATA_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
+
 	tb[type] = attr;
 	return MNL_CB_OK;
 }
@@ -105,9 +106,6 @@ static int nftnl_verdict_parse_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_VERDICT_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_VERDICT_CODE:
 	case NFTA_VERDICT_CHAIN_ID:
@@ -118,6 +116,9 @@ static int nftnl_verdict_parse_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_VERDICT_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 	tb[type] = attr;
 	return MNL_CB_OK;
diff --git a/src/expr/dup.c b/src/expr/dup.c
index 28d686b1351b..c90a5c22cca7 100644
--- a/src/expr/dup.c
+++ b/src/expr/dup.c
@@ -62,15 +62,15 @@ static int nftnl_expr_dup_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_DUP_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch (type) {
 	case NFTA_DUP_SREG_ADDR:
 	case NFTA_DUP_SREG_DEV:
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_DUP_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -97,6 +97,9 @@ static int nftnl_expr_dup_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_dup_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_DUP_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_DUP_SREG_ADDR]) {
 		dup->sreg_addr = ntohl(mnl_attr_get_u32(tb[NFTA_DUP_SREG_ADDR]));
 		e->flags |= (1 << NFTNL_EXPR_DUP_SREG_ADDR);
diff --git a/src/expr/dynset.c b/src/expr/dynset.c
index 9d2bfe5e206b..b3a8a8bbc30b 100644
--- a/src/expr/dynset.c
+++ b/src/expr/dynset.c
@@ -118,9 +118,6 @@ static int nftnl_expr_dynset_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_DYNSET_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch (type) {
 	case NFTA_DYNSET_SREG_KEY:
 	case NFTA_DYNSET_SREG_DATA:
@@ -143,6 +140,9 @@ static int nftnl_expr_dynset_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_DYNSET_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -233,6 +233,9 @@ nftnl_expr_dynset_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_dynset_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_DYNSET_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_DYNSET_SREG_KEY]) {
 		dynset->sreg_key = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_KEY]));
 		e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_KEY);
diff --git a/src/expr/exthdr.c b/src/expr/exthdr.c
index 453902c23017..74709330f42c 100644
--- a/src/expr/exthdr.c
+++ b/src/expr/exthdr.c
@@ -107,9 +107,6 @@ static int nftnl_expr_exthdr_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_EXTHDR_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_EXTHDR_TYPE:
 		if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
@@ -124,6 +121,9 @@ static int nftnl_expr_exthdr_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_EXTHDR_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -160,6 +160,8 @@ nftnl_expr_exthdr_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_exthdr_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_EXTHDR_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_EXTHDR_DREG]) {
 		exthdr->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_DREG]));
 		e->flags |= (1 << NFTNL_EXPR_EXTHDR_DREG);
diff --git a/src/expr/fib.c b/src/expr/fib.c
index 20bc125aa3ad..d34162170001 100644
--- a/src/expr/fib.c
+++ b/src/expr/fib.c
@@ -72,9 +72,6 @@ static int nftnl_expr_fib_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_FIB_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch (type) {
 	case NFTA_FIB_RESULT:
 	case NFTA_FIB_DREG:
@@ -82,6 +79,9 @@ static int nftnl_expr_fib_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_FIB_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -111,6 +111,9 @@ nftnl_expr_fib_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_fib_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_FIB_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_FIB_RESULT]) {
 		fib->result = ntohl(mnl_attr_get_u32(tb[NFTA_FIB_RESULT]));
 		e->flags |= (1 << NFTNL_EXPR_FIB_RESULT);
diff --git a/src/expr/flow_offload.c b/src/expr/flow_offload.c
index 5f209a63fa96..9973e162847c 100644
--- a/src/expr/flow_offload.c
+++ b/src/expr/flow_offload.c
@@ -47,14 +47,14 @@ static int nftnl_expr_flow_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_FLOW_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_FLOW_TABLE_NAME:
 		if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_FLOW_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -79,6 +79,9 @@ static int nftnl_expr_flow_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_flow_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_FLOW_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_FLOW_TABLE_NAME]) {
 		flow->table_name =
 			strdup(mnl_attr_get_str(tb[NFTA_FLOW_TABLE_NAME]));
diff --git a/src/expr/fwd.c b/src/expr/fwd.c
index 04cb089a7146..2e789673ad1f 100644
--- a/src/expr/fwd.c
+++ b/src/expr/fwd.c
@@ -69,9 +69,6 @@ static int nftnl_expr_fwd_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_FWD_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch (type) {
 	case NFTA_FWD_SREG_DEV:
 	case NFTA_FWD_SREG_ADDR:
@@ -79,6 +76,9 @@ static int nftnl_expr_fwd_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_FWD_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -107,6 +107,8 @@ static int nftnl_expr_fwd_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_fwd_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_FWD_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_FWD_SREG_DEV]) {
 		fwd->sreg_dev = ntohl(mnl_attr_get_u32(tb[NFTA_FWD_SREG_DEV]));
 		e->flags |= (1 << NFTNL_EXPR_FWD_SREG_DEV);
diff --git a/src/expr/hash.c b/src/expr/hash.c
index eb44b2ea9bb6..d7ec7c438422 100644
--- a/src/expr/hash.c
+++ b/src/expr/hash.c
@@ -100,9 +100,6 @@ static int nftnl_expr_hash_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_HASH_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch (type) {
 	case NFTA_HASH_SREG:
 	case NFTA_HASH_DREG:
@@ -114,6 +111,9 @@ static int nftnl_expr_hash_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_HASH_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -151,6 +151,8 @@ nftnl_expr_hash_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_hash_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_HASH_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_HASH_SREG]) {
 		hash->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_HASH_SREG]));
 		e->flags |= (1 << NFTNL_EXPR_HASH_SREG);
diff --git a/src/expr/immediate.c b/src/expr/immediate.c
index ab1276a1772c..6100ca5e07a9 100644
--- a/src/expr/immediate.c
+++ b/src/expr/immediate.c
@@ -86,9 +86,6 @@ static int nftnl_expr_immediate_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_IMMEDIATE_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_IMMEDIATE_DREG:
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
@@ -98,6 +95,9 @@ static int nftnl_expr_immediate_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_IMMEDIATE_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -148,6 +148,8 @@ nftnl_expr_immediate_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_immediate_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_IMMEDIATE_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_IMMEDIATE_DREG]) {
 		imm->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_IMMEDIATE_DREG]));
 		e->flags |= (1 << NFTNL_EXPR_IMM_DREG);
diff --git a/src/expr/inner.c b/src/expr/inner.c
index 4f66e944ec91..f66fff2666f2 100644
--- a/src/expr/inner.c
+++ b/src/expr/inner.c
@@ -110,9 +110,6 @@ static int nftnl_inner_parse_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_INNER_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_INNER_NUM:
 	case NFTA_INNER_TYPE:
@@ -125,6 +122,9 @@ static int nftnl_inner_parse_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_INNER_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -144,6 +144,8 @@ nftnl_expr_inner_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (err < 0)
 		return err;
 
+	if (tb[NFTA_INNER_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_INNER_HDRSIZE]) {
 		inner->hdrsize =
 			ntohl(mnl_attr_get_u32(tb[NFTA_INNER_HDRSIZE]));
diff --git a/src/expr/last.c b/src/expr/last.c
index 8e5b88ebb96b..27d6a704cf45 100644
--- a/src/expr/last.c
+++ b/src/expr/last.c
@@ -62,9 +62,6 @@ static int nftnl_expr_last_cb(const struct nlattr *attr, void *data)
 	int type = mnl_attr_get_type(attr);
 	const struct nlattr **tb = data;
 
-	if (mnl_attr_type_valid(attr, NFTA_LAST_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_LAST_MSECS:
 		if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
@@ -74,6 +71,9 @@ static int nftnl_expr_last_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_LAST_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -100,6 +100,8 @@ nftnl_expr_last_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_last_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_LAST_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_LAST_MSECS]) {
 		last->msecs = be64toh(mnl_attr_get_u64(tb[NFTA_LAST_MSECS]));
 		e->flags |= (1 << NFTNL_EXPR_LAST_MSECS);
diff --git a/src/expr/limit.c b/src/expr/limit.c
index 5b821081eb20..d1e1a109bc2d 100644
--- a/src/expr/limit.c
+++ b/src/expr/limit.c
@@ -87,9 +87,6 @@ static int nftnl_expr_limit_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_LIMIT_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_LIMIT_RATE:
 	case NFTA_LIMIT_UNIT:
@@ -102,6 +99,9 @@ static int nftnl_expr_limit_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_LIMIT_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -134,6 +134,8 @@ nftnl_expr_limit_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_limit_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_LIMIT_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_LIMIT_RATE]) {
 		limit->rate = be64toh(mnl_attr_get_u64(tb[NFTA_LIMIT_RATE]));
 		e->flags |= (1 << NFTNL_EXPR_LIMIT_RATE);
diff --git a/src/expr/log.c b/src/expr/log.c
index 18ec2b64a5b9..7d0fde25e4fd 100644
--- a/src/expr/log.c
+++ b/src/expr/log.c
@@ -98,9 +98,6 @@ static int nftnl_expr_log_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_LOG_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_LOG_PREFIX:
 		if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
@@ -117,6 +114,9 @@ static int nftnl_expr_log_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_LOG_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -151,6 +151,8 @@ nftnl_expr_log_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_log_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_LOG_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_LOG_PREFIX]) {
 		if (log->prefix)
 			xfree(log->prefix);
diff --git a/src/expr/lookup.c b/src/expr/lookup.c
index 21a7fcef4041..65e56dae8af2 100644
--- a/src/expr/lookup.c
+++ b/src/expr/lookup.c
@@ -88,9 +88,6 @@ static int nftnl_expr_lookup_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_LOOKUP_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_LOOKUP_SREG:
 	case NFTA_LOOKUP_DREG:
@@ -103,6 +100,9 @@ static int nftnl_expr_lookup_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_LOOKUP_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -137,6 +137,8 @@ nftnl_expr_lookup_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_lookup_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_LOOKUP_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_LOOKUP_SREG]) {
 		lookup->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_LOOKUP_SREG]));
 		e->flags |= (1 << NFTNL_EXPR_LOOKUP_SREG);
diff --git a/src/expr/masq.c b/src/expr/masq.c
index e0565db66fe1..031f20544198 100644
--- a/src/expr/masq.c
+++ b/src/expr/masq.c
@@ -71,9 +71,6 @@ static int nftnl_expr_masq_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_MASQ_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch (type) {
 	case NFTA_MASQ_REG_PROTO_MIN:
 	case NFTA_MASQ_REG_PROTO_MAX:
@@ -81,6 +78,9 @@ static int nftnl_expr_masq_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_MASQ_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -111,6 +111,8 @@ nftnl_expr_masq_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_masq_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_MASQ_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_MASQ_FLAGS]) {
 		masq->flags = be32toh(mnl_attr_get_u32(tb[NFTA_MASQ_FLAGS]));
 		e->flags |= (1 << NFTNL_EXPR_MASQ_FLAGS);
diff --git a/src/expr/match.c b/src/expr/match.c
index 8c1bc74a1ce1..f499fd801670 100644
--- a/src/expr/match.c
+++ b/src/expr/match.c
@@ -84,9 +84,6 @@ static int nftnl_expr_match_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_MATCH_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_MATCH_NAME:
 		if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
@@ -100,6 +97,9 @@ static int nftnl_expr_match_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_MATCH_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -127,6 +127,8 @@ static int nftnl_expr_match_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_match_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_MATCH_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_MATCH_NAME]) {
 		snprintf(match->name, XT_EXTENSION_MAXNAMELEN, "%s",
 			 mnl_attr_get_str(tb[NFTA_MATCH_NAME]));
diff --git a/src/expr/meta.c b/src/expr/meta.c
index 136a450b6e97..858e6dc44e93 100644
--- a/src/expr/meta.c
+++ b/src/expr/meta.c
@@ -86,6 +86,9 @@ static int nftnl_expr_meta_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_META_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -114,6 +117,9 @@ nftnl_expr_meta_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_meta_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_META_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_META_KEY]) {
 		meta->key = ntohl(mnl_attr_get_u32(tb[NFTA_META_KEY]));
 		e->flags |= (1 << NFTNL_EXPR_META_KEY);
diff --git a/src/expr/nat.c b/src/expr/nat.c
index 1235ba45b694..0caaba1c0633 100644
--- a/src/expr/nat.c
+++ b/src/expr/nat.c
@@ -104,9 +104,6 @@ static int nftnl_expr_nat_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_NAT_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_NAT_TYPE:
 	case NFTA_NAT_FAMILY:
@@ -118,6 +115,9 @@ static int nftnl_expr_nat_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_NAT_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -133,6 +133,8 @@ nftnl_expr_nat_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_nat_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_NAT_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_NAT_TYPE]) {
 		nat->type = ntohl(mnl_attr_get_u32(tb[NFTA_NAT_TYPE]));
 		e->flags |= (1 << NFTNL_EXPR_NAT_TYPE);
diff --git a/src/expr/numgen.c b/src/expr/numgen.c
index c015b8847aa4..bbe82e30e1b3 100644
--- a/src/expr/numgen.c
+++ b/src/expr/numgen.c
@@ -80,9 +80,6 @@ static int nftnl_expr_ng_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_NG_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch (type) {
 	case NFTA_NG_DREG:
 	case NFTA_NG_MODULUS:
@@ -91,6 +88,9 @@ static int nftnl_expr_ng_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_NG_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -122,6 +122,8 @@ nftnl_expr_ng_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_ng_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_NG_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_NG_DREG]) {
 		ng->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_NG_DREG]));
 		e->flags |= (1 << NFTNL_EXPR_NG_DREG);
diff --git a/src/expr/objref.c b/src/expr/objref.c
index 00538057222b..8a820d549733 100644
--- a/src/expr/objref.c
+++ b/src/expr/objref.c
@@ -91,9 +91,6 @@ static int nftnl_expr_objref_cb(const struct nlattr *attr, void *data)
 	int type = mnl_attr_get_type(attr);
 	const struct nlattr **tb = data;
 
-	if (mnl_attr_type_valid(attr, NFTA_OBJREF_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_OBJREF_IMM_TYPE:
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
@@ -109,6 +106,9 @@ static int nftnl_expr_objref_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_OBJREF_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -143,6 +143,8 @@ static int nftnl_expr_objref_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_objref_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_OBJREF_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_OBJREF_IMM_TYPE]) {
 		objref->imm.type =
 			ntohl(mnl_attr_get_u32(tb[NFTA_OBJREF_IMM_TYPE]));
diff --git a/src/expr/osf.c b/src/expr/osf.c
index 293a81420a32..9b1197b4e0be 100644
--- a/src/expr/osf.c
+++ b/src/expr/osf.c
@@ -62,9 +62,6 @@ static int nftnl_expr_osf_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_OSF_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTNL_EXPR_OSF_DREG:
 	case NFTNL_EXPR_OSF_FLAGS:
@@ -76,7 +73,9 @@ static int nftnl_expr_osf_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
 			abi_breakage();
 		break;
-
+	default:
+		tb[NFTA_OSF_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -106,6 +105,8 @@ nftnl_expr_osf_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_osf_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_OSF_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_OSF_DREG]) {
 		osf->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_OSF_DREG]));
 		e->flags |= (1 << NFTNL_EXPR_OSF_DREG);
diff --git a/src/expr/payload.c b/src/expr/payload.c
index 35cd10c31b98..f040e9e89a11 100644
--- a/src/expr/payload.c
+++ b/src/expr/payload.c
@@ -110,9 +110,6 @@ static int nftnl_expr_payload_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_PAYLOAD_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_PAYLOAD_SREG:
 	case NFTA_PAYLOAD_DREG:
@@ -125,6 +122,9 @@ static int nftnl_expr_payload_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_PAYLOAD_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -166,6 +166,8 @@ nftnl_expr_payload_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_payload_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_PAYLOAD_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_PAYLOAD_SREG]) {
 		payload->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_SREG]));
 		e->flags |= (1 << NFTNL_EXPR_PAYLOAD_SREG);
diff --git a/src/expr/queue.c b/src/expr/queue.c
index 09220c4a1138..d14d2964339f 100644
--- a/src/expr/queue.c
+++ b/src/expr/queue.c
@@ -77,9 +77,6 @@ static int nftnl_expr_queue_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_QUEUE_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_QUEUE_NUM:
 	case NFTA_QUEUE_TOTAL:
@@ -91,6 +88,9 @@ static int nftnl_expr_queue_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_QUEUE_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -121,6 +121,9 @@ nftnl_expr_queue_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_queue_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_QUEUE_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_QUEUE_NUM]) {
 		queue->queuenum = ntohs(mnl_attr_get_u16(tb[NFTA_QUEUE_NUM]));
 		e->flags |= (1 << NFTNL_EXPR_QUEUE_NUM);
diff --git a/src/expr/quota.c b/src/expr/quota.c
index ddf232f9f3ac..e71feeed09f0 100644
--- a/src/expr/quota.c
+++ b/src/expr/quota.c
@@ -69,9 +69,6 @@ static int nftnl_expr_quota_cb(const struct nlattr *attr, void *data)
 	int type = mnl_attr_get_type(attr);
 	const struct nlattr **tb = data;
 
-	if (mnl_attr_type_valid(attr, NFTA_QUOTA_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_QUOTA_BYTES:
 	case NFTA_QUOTA_CONSUMED:
@@ -82,6 +79,9 @@ static int nftnl_expr_quota_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_QUOTA_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -110,6 +110,8 @@ nftnl_expr_quota_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_quota_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_QUOTA_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_QUOTA_BYTES]) {
 		quota->bytes = be64toh(mnl_attr_get_u64(tb[NFTA_QUOTA_BYTES]));
 		e->flags |= (1 << NFTNL_EXPR_QUOTA_BYTES);
diff --git a/src/expr/range.c b/src/expr/range.c
index 96bb140119b6..ac419cd181fe 100644
--- a/src/expr/range.c
+++ b/src/expr/range.c
@@ -74,9 +74,6 @@ static int nftnl_expr_range_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_RANGE_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_RANGE_SREG:
 	case NFTA_RANGE_OP:
@@ -88,6 +85,9 @@ static int nftnl_expr_range_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_RANGE_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -131,6 +131,8 @@ nftnl_expr_range_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_range_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_RANGE_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_RANGE_SREG]) {
 		range->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_RANGE_SREG]));
 		e->flags |= (1 << NFTA_RANGE_SREG);
diff --git a/src/expr/redir.c b/src/expr/redir.c
index 9971306130fb..b66326fad6ce 100644
--- a/src/expr/redir.c
+++ b/src/expr/redir.c
@@ -71,9 +71,6 @@ static int nftnl_expr_redir_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_REDIR_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch (type) {
 	case NFTA_REDIR_REG_PROTO_MIN:
 	case NFTA_REDIR_REG_PROTO_MAX:
@@ -81,6 +78,9 @@ static int nftnl_expr_redir_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_REDIR_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -111,6 +111,8 @@ nftnl_expr_redir_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_redir_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_REDIR_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_REDIR_REG_PROTO_MIN]) {
 		redir->sreg_proto_min =
 			ntohl(mnl_attr_get_u32(tb[NFTA_REDIR_REG_PROTO_MIN]));
diff --git a/src/expr/reject.c b/src/expr/reject.c
index 9090db3f697a..3a8487ebae63 100644
--- a/src/expr/reject.c
+++ b/src/expr/reject.c
@@ -64,9 +64,6 @@ static int nftnl_expr_reject_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_REJECT_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_REJECT_TYPE:
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
@@ -76,6 +73,9 @@ static int nftnl_expr_reject_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_REJECT_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -102,6 +102,9 @@ nftnl_expr_reject_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_reject_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_REJECT_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_REJECT_TYPE]) {
 		reject->type = ntohl(mnl_attr_get_u32(tb[NFTA_REJECT_TYPE]));
 		e->flags |= (1 << NFTNL_EXPR_REJECT_TYPE);
diff --git a/src/expr/rt.c b/src/expr/rt.c
index ff4fd03c8f1b..fee5594217b9 100644
--- a/src/expr/rt.c
+++ b/src/expr/rt.c
@@ -63,15 +63,15 @@ static int nftnl_expr_rt_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_RT_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch (type) {
 	case NFTA_RT_KEY:
 	case NFTA_RT_DREG:
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_RT_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -98,6 +98,9 @@ nftnl_expr_rt_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_rt_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_RT_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_RT_KEY]) {
 		rt->key = ntohl(mnl_attr_get_u32(tb[NFTA_RT_KEY]));
 		e->flags |= (1 << NFTNL_EXPR_RT_KEY);
diff --git a/src/expr/socket.c b/src/expr/socket.c
index 7a25cdf806d1..0e990cdf9b78 100644
--- a/src/expr/socket.c
+++ b/src/expr/socket.c
@@ -70,9 +70,6 @@ static int nftnl_expr_socket_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_SOCKET_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch (type) {
 	case NFTA_SOCKET_KEY:
 	case NFTA_SOCKET_DREG:
@@ -80,6 +77,9 @@ static int nftnl_expr_socket_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_SOCKET_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -108,6 +108,9 @@ nftnl_expr_socket_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_socket_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_SOCKET_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_SOCKET_KEY]) {
 		socket->key = ntohl(mnl_attr_get_u32(tb[NFTA_SOCKET_KEY]));
 		e->flags |= (1 << NFTNL_EXPR_SOCKET_KEY);
diff --git a/src/expr/synproxy.c b/src/expr/synproxy.c
index b5a1fef9f406..eb3c9b062999 100644
--- a/src/expr/synproxy.c
+++ b/src/expr/synproxy.c
@@ -60,24 +60,22 @@ static int nftnl_expr_synproxy_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_SYNPROXY_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
-	case NFTNL_EXPR_SYNPROXY_MSS:
+	case NFTA_SYNPROXY_MSS:
 		if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
 			abi_breakage();
 		break;
-
-	case NFTNL_EXPR_SYNPROXY_WSCALE:
+	case NFTA_SYNPROXY_WSCALE:
 		if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
 			abi_breakage();
 		break;
-
-	case NFTNL_EXPR_SYNPROXY_FLAGS:
+	case NFTA_SYNPROXY_FLAGS:
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_SYNPROXY_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -109,6 +107,8 @@ nftnl_expr_synproxy_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_synproxy_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_SYNPROXY_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_SYNPROXY_MSS]) {
 		synproxy->mss = ntohs(mnl_attr_get_u16(tb[NFTA_SYNPROXY_MSS]));
 		e->flags |= (1 << NFTNL_EXPR_SYNPROXY_MSS);
diff --git a/src/expr/target.c b/src/expr/target.c
index 8259a20a66cb..7b7795bcb6e3 100644
--- a/src/expr/target.c
+++ b/src/expr/target.c
@@ -84,9 +84,6 @@ static int nftnl_expr_target_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_TARGET_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_TARGET_NAME:
 		if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
@@ -100,6 +97,9 @@ static int nftnl_expr_target_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_TARGET_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -127,6 +127,9 @@ static int nftnl_expr_target_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_target_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_TARGET_UNSPEC])
+		e->incomplete = true;
+
 	if (tb[NFTA_TARGET_NAME]) {
 		snprintf(target->name, XT_EXTENSION_MAXNAMELEN, "%s",
 			 mnl_attr_get_str(tb[NFTA_TARGET_NAME]));
diff --git a/src/expr/tproxy.c b/src/expr/tproxy.c
index 9391ce880cd3..72aac9edea0b 100644
--- a/src/expr/tproxy.c
+++ b/src/expr/tproxy.c
@@ -72,9 +72,6 @@ static int nftnl_expr_tproxy_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_TPROXY_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_TPROXY_FAMILY:
 	case NFTA_TPROXY_REG_ADDR:
@@ -82,6 +79,9 @@ static int nftnl_expr_tproxy_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_TPROXY_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -97,6 +97,8 @@ nftnl_expr_tproxy_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_tproxy_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_TPROXY_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_TPROXY_FAMILY]) {
 		tproxy->family = ntohl(mnl_attr_get_u32(tb[NFTA_TPROXY_FAMILY]));
 		e->flags |= (1 << NFTNL_EXPR_TPROXY_FAMILY);
diff --git a/src/expr/tunnel.c b/src/expr/tunnel.c
index 861e56dd64c2..54c3f5886edf 100644
--- a/src/expr/tunnel.c
+++ b/src/expr/tunnel.c
@@ -62,15 +62,15 @@ static int nftnl_expr_tunnel_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_TUNNEL_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch(type) {
 	case NFTA_TUNNEL_KEY:
 	case NFTA_TUNNEL_DREG:
 		if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_TUNNEL_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -97,6 +97,8 @@ nftnl_expr_tunnel_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_tunnel_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_TUNNEL_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_TUNNEL_KEY]) {
 		tunnel->key = ntohl(mnl_attr_get_u32(tb[NFTA_TUNNEL_KEY]));
 		e->flags |= (1 << NFTNL_EXPR_TUNNEL_KEY);
diff --git a/src/expr/xfrm.c b/src/expr/xfrm.c
index 2585579c3b54..54d102638ef5 100644
--- a/src/expr/xfrm.c
+++ b/src/expr/xfrm.c
@@ -78,9 +78,6 @@ static int nftnl_expr_xfrm_cb(const struct nlattr *attr, void *data)
 	const struct nlattr **tb = data;
 	int type = mnl_attr_get_type(attr);
 
-	if (mnl_attr_type_valid(attr, NFTA_XFRM_MAX) < 0)
-		return MNL_CB_OK;
-
 	switch (type) {
 	case NFTA_XFRM_DREG:
 	case NFTA_XFRM_KEY:
@@ -92,6 +89,9 @@ static int nftnl_expr_xfrm_cb(const struct nlattr *attr, void *data)
 		if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
 			abi_breakage();
 		break;
+	default:
+		tb[NFTA_XFRM_UNSPEC] = attr;
+		return MNL_CB_OK;
 	}
 
 	tb[type] = attr;
@@ -122,6 +122,8 @@ nftnl_expr_xfrm_parse(struct nftnl_expr *e, struct nlattr *attr)
 	if (mnl_attr_parse_nested(attr, nftnl_expr_xfrm_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_XFRM_UNSPEC])
+		e->incomplete = true;
 	if (tb[NFTA_XFRM_KEY]) {
 		x->key = ntohl(mnl_attr_get_u32(tb[NFTA_XFRM_KEY]));
 		e->flags |= (1 << NFTNL_EXPR_XFRM_KEY);
-- 
2.45.2


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

* [PATCH libnftnl 2/5] sets: add and use incomplete tag
  2024-10-07  9:49 [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes Florian Westphal
  2024-10-07  9:49 ` [PATCH libnftnl 1/5] expr: add and use incomplete tag Florian Westphal
@ 2024-10-07  9:49 ` Florian Westphal
  2024-10-07  9:49 ` [PATCH libnftnl 3/5] libnftnl: add api to query dissection state Florian Westphal
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Florian Westphal @ 2024-10-07  9:49 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Like previous patch:

Extend data and set representation to indicate an incomplete
dissection.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/data_reg.h  | 1 +
 include/set.h       | 1 +
 src/expr/data_reg.c | 6 ++++++
 src/set_elem.c      | 5 +++++
 4 files changed, 13 insertions(+)

diff --git a/include/data_reg.h b/include/data_reg.h
index 946354dc9881..982e7a5bf2ea 100644
--- a/include/data_reg.h
+++ b/include/data_reg.h
@@ -27,6 +27,7 @@ union nftnl_data_reg {
 		const char	*chain;
 		uint32_t	chain_id;
 	};
+	bool incomplete;
 };
 
 int nftnl_data_reg_snprintf(char *buf, size_t size,
diff --git a/include/set.h b/include/set.h
index 55018b6b9ba9..ea84e511f1f9 100644
--- a/include/set.h
+++ b/include/set.h
@@ -34,6 +34,7 @@ struct nftnl_set {
 	uint32_t		gc_interval;
 	uint64_t		timeout;
 	struct list_head	expr_list;
+	bool			incomplete;
 };
 
 struct nftnl_set_list;
diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c
index 149d52aea9b4..dbd7b3660c13 100644
--- a/src/expr/data_reg.c
+++ b/src/expr/data_reg.c
@@ -135,6 +135,9 @@ nftnl_parse_verdict(union nftnl_data_reg *data, const struct nlattr *attr, int *
 	if (!tb[NFTA_VERDICT_CODE])
 		return -1;
 
+	if (tb[NFTA_VERDICT_UNSPEC])
+		data->incomplete = true;
+
 	data->verdict = ntohl(mnl_attr_get_u32(tb[NFTA_VERDICT_CODE]));
 
 	switch(data->verdict) {
@@ -193,6 +196,9 @@ int nftnl_parse_data(union nftnl_data_reg *data, struct nlattr *attr, int *type)
 	if (mnl_attr_parse_nested(attr, nftnl_data_parse_cb, tb) < 0)
 		return -1;
 
+	if (tb[NFTA_DATA_UNSPEC])
+		data->incomplete = true;
+
 	if (tb[NFTA_DATA_VALUE]) {
 		if (type)
 			*type = DATA_VALUE;
diff --git a/src/set_elem.c b/src/set_elem.c
index 9207a0dbd689..3531c0767e16 100644
--- a/src/set_elem.c
+++ b/src/set_elem.c
@@ -589,6 +589,11 @@ static int nftnl_set_elems_parse2(struct nftnl_set *s, const struct nlattr *nest
 		e->flags |= (1 << NFTNL_SET_ELEM_OBJREF);
 	}
 
+	if (!s->incomplete)
+		s->incomplete = e->key.incomplete ||
+				e->key_end.incomplete ||
+				e->data.incomplete;
+
 	/* Add this new element to this set */
 	list_add_tail(&e->head, &s->element_list);
 
-- 
2.45.2


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

* [PATCH libnftnl 3/5] libnftnl: add api to query dissection state
  2024-10-07  9:49 [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes Florian Westphal
  2024-10-07  9:49 ` [PATCH libnftnl 1/5] expr: add and use incomplete tag Florian Westphal
  2024-10-07  9:49 ` [PATCH libnftnl 2/5] sets: " Florian Westphal
@ 2024-10-07  9:49 ` Florian Westphal
  2024-10-07  9:49 ` [PATCH nft 4/5] netlink: tell user if libnftnl detected unknown attributes/features Florian Westphal
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Florian Westphal @ 2024-10-07  9:49 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Allow to check if the set / expression was decoded as-expected.

These two functions return false in case libnftl had to ignore
new attributes that it did not expect.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/libnftnl/expr.h | 2 ++
 include/libnftnl/set.h  | 1 +
 src/expr.c              | 6 ++++++
 src/libnftnl.map        | 5 +++++
 src/rule.c              | 5 +++++
 src/set.c               | 6 ++++++
 6 files changed, 25 insertions(+)

diff --git a/include/libnftnl/expr.h b/include/libnftnl/expr.h
index fba121062244..d938475394ec 100644
--- a/include/libnftnl/expr.h
+++ b/include/libnftnl/expr.h
@@ -47,6 +47,8 @@ int nftnl_expr_expr_foreach(const struct nftnl_expr *e,
 int nftnl_expr_snprintf(char *buf, size_t buflen, const struct nftnl_expr *expr, uint32_t type, uint32_t flags);
 int nftnl_expr_fprintf(FILE *fp, const struct nftnl_expr *expr, uint32_t type, uint32_t flags);
 
+bool nftnl_expr_complete(const struct nftnl_expr *expr);
+
 enum {
 	NFTNL_EXPR_PAYLOAD_DREG	= NFTNL_EXPR_BASE,
 	NFTNL_EXPR_PAYLOAD_BASE,
diff --git a/include/libnftnl/set.h b/include/libnftnl/set.h
index e2e5795aa9b4..2e624c3e7e66 100644
--- a/include/libnftnl/set.h
+++ b/include/libnftnl/set.h
@@ -171,6 +171,7 @@ void nftnl_set_elems_iter_destroy(struct nftnl_set_elems_iter *iter);
 int nftnl_set_elems_nlmsg_build_payload_iter(struct nlmsghdr *nlh,
 					   struct nftnl_set_elems_iter *iter);
 
+bool nftnl_set_complete(const struct nftnl_set *set);
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/src/expr.c b/src/expr.c
index 4e32189c6e8d..99078dcd058e 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -311,3 +311,9 @@ int nftnl_expr_fprintf(FILE *fp, const struct nftnl_expr *expr, uint32_t type,
 	return nftnl_fprintf(fp, expr, NFTNL_CMD_UNSPEC, type, flags,
 			     nftnl_expr_do_snprintf);
 }
+
+EXPORT_SYMBOL(nftnl_expr_complete);
+bool nftnl_expr_complete(const struct nftnl_expr *expr)
+{
+	return !expr->incomplete;
+}
diff --git a/src/libnftnl.map b/src/libnftnl.map
index 8fffff19eb2e..90eb4a92fca4 100644
--- a/src/libnftnl.map
+++ b/src/libnftnl.map
@@ -383,3 +383,8 @@ LIBNFTNL_16 {
 LIBNFTNL_17 {
   nftnl_set_elem_nlmsg_build;
 } LIBNFTNL_16;
+
+LIBNFTNL_18 {
+  nftnl_set_complete;
+  nftnl_expr_complete;
+} LIBNFTNL_17;
diff --git a/src/rule.c b/src/rule.c
index c22918a8f352..aa969ad5f876 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -582,6 +582,11 @@ static int nftnl_rule_snprintf_default(char *buf, size_t remain,
 					     type, flags);
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 
+		if (!nftnl_expr_complete(expr)) {
+			ret = snprintf(buf + offset, remain, "[incomplete]");
+			SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+		}
+
 		ret = snprintf(buf + offset, remain, "]");
 		SNPRINTF_BUFFER_SIZE(ret, remain, offset);
 	}
diff --git a/src/set.c b/src/set.c
index 75ad64e03850..40f5e1a955fd 100644
--- a/src/set.c
+++ b/src/set.c
@@ -1051,3 +1051,9 @@ int nftnl_set_lookup_id(struct nftnl_expr *e,
 	*set_id = nftnl_set_get_u32(s, NFTNL_SET_ID);
 	return 1;
 }
+
+EXPORT_SYMBOL(nftnl_set_complete);
+bool nftnl_set_complete(const struct nftnl_set *set)
+{
+	return !set->incomplete;
+}
-- 
2.45.2


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

* [PATCH nft 4/5] netlink: tell user if libnftnl detected unknown attributes/features
  2024-10-07  9:49 [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes Florian Westphal
                   ` (2 preceding siblings ...)
  2024-10-07  9:49 ` [PATCH libnftnl 3/5] libnftnl: add api to query dissection state Florian Westphal
@ 2024-10-07  9:49 ` Florian Westphal
  2024-10-07  9:49 ` [PATCH nft 5/5] sets: inform user when set definition contains unknown attributes Florian Westphal
  2024-10-16 17:07 ` [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes Phil Sutter
  5 siblings, 0 replies; 16+ messages in thread
From: Florian Westphal @ 2024-10-07  9:49 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

Add a warning in case libnftl failed to decode all attributes coming
from the kernel.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/netlink.h         |  1 +
 src/netlink_delinearize.c | 24 ++++++++++++++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/include/netlink.h b/include/netlink.h
index cf7ba3693885..66fd6b414a0b 100644
--- a/include/netlink.h
+++ b/include/netlink.h
@@ -42,6 +42,7 @@ struct netlink_parse_ctx {
 	struct netlink_ctx	*nlctx;
 	bool			inner;
 	uint8_t			inner_reg;
+	uint8_t			incomplete_exprs;
 };
 
 
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index e3d9cfbbede5..5c7c11352abf 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -1915,6 +1915,23 @@ static const struct expr_handler netlink_parsers[] = {
 	{ .name = "synproxy",	.parse = netlink_parse_synproxy },
 };
 
+static void netlink_incomplete_expr(struct netlink_parse_ctx *ctx)
+{
+	static const char incomplete[] = "# Unknown features used (old nft version?)";
+	struct stmt *stmt;
+	struct expr *e;
+
+	netlink_error(ctx, &ctx->rule->location, incomplete);
+
+	e = constant_expr_alloc(&ctx->rule->location, &string_type,
+				BYTEORDER_HOST_ENDIAN,
+				sizeof(incomplete) * BITS_PER_BYTE, incomplete);
+
+	__mpz_switch_byteorder(e->value, sizeof(incomplete));
+	stmt = expr_stmt_alloc(&ctx->rule->location, e);
+	rule_stmt_append(ctx->rule, stmt);
+}
+
 static int netlink_parse_expr(const struct nftnl_expr *nle,
 			      struct netlink_parse_ctx *ctx)
 {
@@ -1947,6 +1964,10 @@ static int netlink_parse_rule_expr(struct nftnl_expr *nle, void *arg)
 	err = netlink_parse_expr(nle, ctx);
 	if (err < 0)
 		return err;
+
+	if (!nftnl_expr_complete(nle))
+		ctx->incomplete_exprs++;
+
 	if (ctx->stmt != NULL) {
 		rule_stmt_append(ctx->rule, ctx->stmt);
 		ctx->stmt = NULL;
@@ -3508,6 +3529,9 @@ struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
 
 	nftnl_expr_foreach(nlr, netlink_parse_rule_expr, pctx);
 
+	if (pctx->incomplete_exprs)
+		netlink_incomplete_expr(pctx);
+
 	rule_parse_postprocess(pctx, pctx->rule);
 	netlink_release_registers(pctx);
 	return pctx->rule;
-- 
2.45.2


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

* [PATCH nft 5/5] sets: inform user when set definition contains unknown attributes
  2024-10-07  9:49 [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes Florian Westphal
                   ` (3 preceding siblings ...)
  2024-10-07  9:49 ` [PATCH nft 4/5] netlink: tell user if libnftnl detected unknown attributes/features Florian Westphal
@ 2024-10-07  9:49 ` Florian Westphal
  2024-10-16 17:07 ` [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes Phil Sutter
  5 siblings, 0 replies; 16+ messages in thread
From: Florian Westphal @ 2024-10-07  9:49 UTC (permalink / raw)
  To: netfilter-devel; +Cc: Florian Westphal

libnftnl detects when the kernel includes extra attributes that are not
recognized.  Expose this to the user.

This could happen when using an older release of libnftl/nftables
with a more recent kernel, where a raw user of the netlink interface
uses an extended/more recent feature set.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/rule.h | 2 ++
 src/netlink.c  | 3 +++
 src/rule.c     | 5 +++++
 3 files changed, 10 insertions(+)

diff --git a/include/rule.h b/include/rule.h
index 5b3e12b5d7dc..7cbd26897321 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -332,6 +332,7 @@ void rule_stmt_insert_at(struct rule *rule, struct stmt *nstmt,
  * @automerge:	merge adjacents and overlapping elements, if possible
  * @comment:	comment
  * @errors:	expr evaluation errors seen
+ * @incomplete: kernel set additional attributes unknown to this nft version
  * @desc.size:		count of set elements
  * @desc.field_len:	length of single concatenated fields, bytes
  * @desc.field_count:	count of concatenated fields
@@ -357,6 +358,7 @@ struct set {
 	bool			automerge;
 	bool			key_typeof_valid;
 	bool			errors;
+	bool			incomplete;
 	const char		*comment;
 	struct {
 		uint32_t	size;
diff --git a/src/netlink.c b/src/netlink.c
index 25ee3419772b..c057e1d04c28 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -1032,6 +1032,9 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
 	if (comment)
 		set->comment = xstrdup(comment);
 
+	if (!nftnl_set_complete(nls))
+		set->incomplete = true;
+
 	init_list_head(&set_parse_ctx.stmt_list);
 
 	if (nftnl_set_is_set(nls, NFTNL_SET_EXPR)) {
diff --git a/src/rule.c b/src/rule.c
index 9bc160ec0d88..e4fce143d8be 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -405,6 +405,11 @@ static void set_print_declaration(const struct set *set,
 			  set->comment,
 			  opts->stmt_separator);
 	}
+
+	if (set->incomplete)
+		nft_print(octx, "%s%s# Unknown features used (old nft version?)%s",
+			  opts->tab, opts->tab,
+			  opts->stmt_separator);
 }
 
 static void do_set_print(const struct set *set, struct print_fmt_options *opts,
-- 
2.45.2


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

* Re: [PATCH libnftnl 1/5] expr: add and use incomplete tag
  2024-10-07  9:49 ` [PATCH libnftnl 1/5] expr: add and use incomplete tag Florian Westphal
@ 2024-10-08 11:13   ` Pablo Neira Ayuso
  2024-10-08 12:17     ` Florian Westphal
  0 siblings, 1 reply; 16+ messages in thread
From: Pablo Neira Ayuso @ 2024-10-08 11:13 UTC (permalink / raw)
  To: Florian Westphal; +Cc: netfilter-devel

On Mon, Oct 07, 2024 at 11:49:34AM +0200, Florian Westphal wrote:
> Extend netlink dump decoder functions to set
> expr->incomplete marker if there are unrecognized attributes
> set in the kernel dump.
>
> This can be used by frontend tools to provide a warning to the user
> that the rule dump might be incomplete.

This is to handle old binary and new kernel scenario, correct?

I think it is hard to know if this attribute is fundamental to rise a
warning from libnftnl. It could be just an new attribute that can be
ignored by userspace or not? I think libnftables (higher layer) knows
better what to do in this case, if such new attribute is required or
not.

Or maybe this is a different issue?

> diff --git a/src/expr/bitwise.c b/src/expr/bitwise.c
> index e99131a090ed..46346712e462 100644
> --- a/src/expr/bitwise.c
> +++ b/src/expr/bitwise.c
> @@ -97,9 +97,6 @@ static int nftnl_expr_bitwise_cb(const struct nlattr *attr, void *data)
>  	const struct nlattr **tb = data;
>  	int type = mnl_attr_get_type(attr);

Why not simplify with:

	if (mnl_attr_type_valid(attr, NFTA_BITWISE_MAX) < 0) {
		tb[NFTA_BITWISE_UNSPEC] = attr;
		return MNL_CB_OK;
        }

I think it is intentional you are doing this at a later stage.

> -	if (mnl_attr_type_valid(attr, NFTA_BITWISE_MAX) < 0)
> -		return MNL_CB_OK;
> -
>  	switch(type) {
>  	case NFTA_BITWISE_SREG:
>  	case NFTA_BITWISE_DREG:

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

* Re: [PATCH libnftnl 1/5] expr: add and use incomplete tag
  2024-10-08 11:13   ` Pablo Neira Ayuso
@ 2024-10-08 12:17     ` Florian Westphal
  2024-10-08 14:43       ` Pablo Neira Ayuso
  0 siblings, 1 reply; 16+ messages in thread
From: Florian Westphal @ 2024-10-08 12:17 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: Florian Westphal, netfilter-devel

Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> On Mon, Oct 07, 2024 at 11:49:34AM +0200, Florian Westphal wrote:
> > Extend netlink dump decoder functions to set
> > expr->incomplete marker if there are unrecognized attributes
> > set in the kernel dump.
> >
> > This can be used by frontend tools to provide a warning to the user
> > that the rule dump might be incomplete.
> 
> This is to handle old binary and new kernel scenario, correct?

Yes, old binary is listing, newer binary added something old binary
can't understand.

> I think it is hard to know if this attribute is fundamental to rise a
> warning from libnftnl. It could be just an new attribute that can be
> ignored by userspace or not?

Yes, we can't know if its something harmless or not.

> I think libnftables (higher layer) knows
> better what to do in this case, if such new attribute is required or
> not.

Well, libnftables can't know that either.  libnfntl saw an netlink
attribute that it doesn't know about.

What that attibute is doing, if its harmless or important, we cannot
know.

> > diff --git a/src/expr/bitwise.c b/src/expr/bitwise.c
> > index e99131a090ed..46346712e462 100644
> > --- a/src/expr/bitwise.c
> > +++ b/src/expr/bitwise.c
> > @@ -97,9 +97,6 @@ static int nftnl_expr_bitwise_cb(const struct nlattr *attr, void *data)
> >  	const struct nlattr **tb = data;
> >  	int type = mnl_attr_get_type(attr);
> 
> Why not simplify with:
> 
> 	if (mnl_attr_type_valid(attr, NFTA_BITWISE_MAX) < 0) {
> 		tb[NFTA_BITWISE_UNSPEC] = attr;
> 		return MNL_CB_OK;
>         }

That would work too. I don't really get mnl_attr_type_valid().

All of the callbacks have a switch statement, so anything not handled
is 'unknown'.
But if you prefer the mnl_attr_type_valid() use then I can rewrite it.

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

* Re: [PATCH libnftnl 1/5] expr: add and use incomplete tag
  2024-10-08 12:17     ` Florian Westphal
@ 2024-10-08 14:43       ` Pablo Neira Ayuso
  2024-10-08 16:11         ` Florian Westphal
  0 siblings, 1 reply; 16+ messages in thread
From: Pablo Neira Ayuso @ 2024-10-08 14:43 UTC (permalink / raw)
  To: Florian Westphal; +Cc: netfilter-devel

On Tue, Oct 08, 2024 at 02:17:02PM +0200, Florian Westphal wrote:
> Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> > On Mon, Oct 07, 2024 at 11:49:34AM +0200, Florian Westphal wrote:
> > > Extend netlink dump decoder functions to set
> > > expr->incomplete marker if there are unrecognized attributes
> > > set in the kernel dump.
> > >
> > > This can be used by frontend tools to provide a warning to the user
> > > that the rule dump might be incomplete.
> > 
> > This is to handle old binary and new kernel scenario, correct?
> 
> Yes, old binary is listing, newer binary added something old binary
> can't understand.

I see.

> > I think it is hard to know if this attribute is fundamental to rise a
> > warning from libnftnl. It could be just an new attribute that can be
> > ignored by userspace or not?
> 
> Yes, we can't know if its something harmless or not.
> 
> > I think libnftables (higher layer) knows
> > better what to do in this case, if such new attribute is required or
> > not.
> 
> Well, libnftables can't know that either.  libnfntl saw an netlink
> attribute that it doesn't know about.

Indeed.

> What that attibute is doing, if its harmless or important, we cannot
> know.

Yes, this is an old binary, it does not know.

> > > diff --git a/src/expr/bitwise.c b/src/expr/bitwise.c
> > > index e99131a090ed..46346712e462 100644
> > > --- a/src/expr/bitwise.c
> > > +++ b/src/expr/bitwise.c
> > > @@ -97,9 +97,6 @@ static int nftnl_expr_bitwise_cb(const struct nlattr *attr, void *data)
> > >  	const struct nlattr **tb = data;
> > >  	int type = mnl_attr_get_type(attr);
> > 
> > Why not simplify with:
> > 
> > 	if (mnl_attr_type_valid(attr, NFTA_BITWISE_MAX) < 0) {
> > 		tb[NFTA_BITWISE_UNSPEC] = attr;
> > 		return MNL_CB_OK;
> >         }
> 
> That would work too. I don't really get mnl_attr_type_valid().

mnl_attr_type_valid() can go away if all switch() are audited, yes, it
is just defensive.

> All of the callbacks have a switch statement, so anything not handled
> is 'unknown'.
> But if you prefer the mnl_attr_type_valid() use then I can rewrite it.

There is also _PAD attributes that maybe trigger default case.

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

* Re: [PATCH libnftnl 1/5] expr: add and use incomplete tag
  2024-10-08 14:43       ` Pablo Neira Ayuso
@ 2024-10-08 16:11         ` Florian Westphal
  0 siblings, 0 replies; 16+ messages in thread
From: Florian Westphal @ 2024-10-08 16:11 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: Florian Westphal, netfilter-devel

Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> > That would work too. I don't really get mnl_attr_type_valid().
> 
> mnl_attr_type_valid() can go away if all switch() are audited, yes, it
> is just defensive.

> > All of the callbacks have a switch statement, so anything not handled
> > is 'unknown'.
> > But if you prefer the mnl_attr_type_valid() use then I can rewrite it.
> 
> There is also _PAD attributes that maybe trigger default case.

Right, I'll probably change it to only care about 'mnl_attr_type_valid
returns false'.

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

* Re: [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes
  2024-10-07  9:49 [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes Florian Westphal
                   ` (4 preceding siblings ...)
  2024-10-07  9:49 ` [PATCH nft 5/5] sets: inform user when set definition contains unknown attributes Florian Westphal
@ 2024-10-16 17:07 ` Phil Sutter
  2024-10-16 18:34   ` Pablo Neira Ayuso
  2024-10-16 19:28   ` Jan Engelhardt
  5 siblings, 2 replies; 16+ messages in thread
From: Phil Sutter @ 2024-10-16 17:07 UTC (permalink / raw)
  To: Florian Westphal; +Cc: netfilter-devel

On Mon, Oct 07, 2024 at 11:49:33AM +0200, Florian Westphal wrote:
[...]
> Extend libnftnl to also make an annotation when a known expression has
> an unknown attribute included in the dump, then extend nftables to also
> display this to the user.

We must be careful with this and LIBVERSION updates. I'm looking at
libnftnl-1.2.0 which gained support for NFTA_TABLE_OWNER,
NFTA_SOCKET_LEVEL, etc. but did not update LIBVERSION at all - OK,
that's probably a bug. But there is also libnftnl-1.1.9 with similar
additions (NFTA_{DYNSET,SET,SET_ELEM}_EXPRESSIONS) and a LIBVERSION
update in the compatible range (15:0:4 -> 16:0:5).

We may increase incomplete marker correctness by treating support for
any new attribute an incompatible update. Given that we often have
dependencies between libnftnl and nftables for other things, it may not
be too much of a downside though.

> Debug out out will include the [incomplete] tag for each affected
> expression.

Looking at the impact this series has for such situations, I want to
make the iptables-nft compat extension stuff depend on it for better
detection of incompatible rule content.

Thanks, Phil

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

* Re: [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes
  2024-10-16 17:07 ` [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes Phil Sutter
@ 2024-10-16 18:34   ` Pablo Neira Ayuso
  2024-10-16 19:04     ` Phil Sutter
  2024-10-16 19:28   ` Jan Engelhardt
  1 sibling, 1 reply; 16+ messages in thread
From: Pablo Neira Ayuso @ 2024-10-16 18:34 UTC (permalink / raw)
  To: Phil Sutter, Florian Westphal, netfilter-devel

On Wed, Oct 16, 2024 at 07:07:24PM +0200, Phil Sutter wrote:
> On Mon, Oct 07, 2024 at 11:49:33AM +0200, Florian Westphal wrote:
> [...]
> > Extend libnftnl to also make an annotation when a known expression has
> > an unknown attribute included in the dump, then extend nftables to also
> > display this to the user.
> 
> We must be careful with this and LIBVERSION updates. I'm looking at
> libnftnl-1.2.0 which gained support for NFTA_TABLE_OWNER,
> NFTA_SOCKET_LEVEL, etc. but did not update LIBVERSION at all - OK,
> that's probably a bug. But there is also libnftnl-1.1.9 with similar
> additions (NFTA_{DYNSET,SET,SET_ELEM}_EXPRESSIONS) and a LIBVERSION
> update in the compatible range (15:0:4 -> 16:0:5).

LIBVERSION talks about libnftnl API, not netlink attributes?
Probably 1.1.9 got any API update while 1.20 did not?

> We may increase incomplete marker correctness by treating support for
> any new attribute an incompatible update. Given that we often have
> dependencies between libnftnl and nftables for other things, it may not
> be too much of a downside though.

15:0:4 -> 16:0:5 means new API is available while older are still
supported, so old nftables can use this library binary safely.

You mean, we should reset age, considering c:0:a?

> > Debug out out will include the [incomplete] tag for each affected
> > expression.
> 
> Looking at the impact this series has for such situations, I want to
> make the iptables-nft compat extension stuff depend on it for better
> detection of incompatible rule content.
> 
> Thanks, Phil
> 

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

* Re: [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes
  2024-10-16 18:34   ` Pablo Neira Ayuso
@ 2024-10-16 19:04     ` Phil Sutter
  2024-10-16 19:41       ` Jan Engelhardt
  0 siblings, 1 reply; 16+ messages in thread
From: Phil Sutter @ 2024-10-16 19:04 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: Florian Westphal, netfilter-devel

On Wed, Oct 16, 2024 at 08:34:12PM +0200, Pablo Neira Ayuso wrote:
> On Wed, Oct 16, 2024 at 07:07:24PM +0200, Phil Sutter wrote:
> > On Mon, Oct 07, 2024 at 11:49:33AM +0200, Florian Westphal wrote:
> > [...]
> > > Extend libnftnl to also make an annotation when a known expression has
> > > an unknown attribute included in the dump, then extend nftables to also
> > > display this to the user.
> > 
> > We must be careful with this and LIBVERSION updates. I'm looking at
> > libnftnl-1.2.0 which gained support for NFTA_TABLE_OWNER,
> > NFTA_SOCKET_LEVEL, etc. but did not update LIBVERSION at all - OK,
> > that's probably a bug. But there is also libnftnl-1.1.9 with similar
> > additions (NFTA_{DYNSET,SET,SET_ELEM}_EXPRESSIONS) and a LIBVERSION
> > update in the compatible range (15:0:4 -> 16:0:5).
> 
> LIBVERSION talks about libnftnl API, not netlink attributes?
> Probably 1.1.9 got any API update while 1.20 did not?
> 
> > We may increase incomplete marker correctness by treating support for
> > any new attribute an incompatible update. Given that we often have
> > dependencies between libnftnl and nftables for other things, it may not
> > be too much of a downside though.
> 
> 15:0:4 -> 16:0:5 means new API is available while older are still
> supported, so old nftables can use this library binary safely.

Yes, and my concern is if one installs this newer libnftnl, behaviour of
incomplete marker will change despite the same old nftables package
being in place which doesn't handle the new kernel attribute.

> You mean, we should reset age, considering c:0:a?

I just realize that one may recompile nftables against a newer libnftnl
and achieve the same effect as the "compatible library update" described
above.

So the proposed incomplete marker merely indicates that libnftnl is
outdated compared to the running kernel (or the ruleset loaded into it).
If libnftnl does not signal "incomplete", nftables may still miss
important attributes.

The other way around should work though: If libnftnl indicates
"incomplete", user space is certainly outdated.

No longer incrementing library age value does not help here. Sorry for
the noise!

Cheers, Phil

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

* Re: [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes
  2024-10-16 17:07 ` [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes Phil Sutter
  2024-10-16 18:34   ` Pablo Neira Ayuso
@ 2024-10-16 19:28   ` Jan Engelhardt
  2024-10-16 20:05     ` Phil Sutter
  1 sibling, 1 reply; 16+ messages in thread
From: Jan Engelhardt @ 2024-10-16 19:28 UTC (permalink / raw)
  To: Phil Sutter; +Cc: Florian Westphal, netfilter-devel


On Wednesday 2024-10-16 19:07, Phil Sutter wrote:
>On Mon, Oct 07, 2024 at 11:49:33AM +0200, Florian Westphal wrote:
>[...]
>> Extend libnftnl to also make an annotation when a known expression has
>> an unknown attribute included in the dump, then extend nftables to also
>> display this to the user.
>
>We must be careful with this and LIBVERSION updates. I'm looking at
>libnftnl-1.2.0 which gained support for NFTA_TABLE_OWNER,
>NFTA_SOCKET_LEVEL, etc. but did not update LIBVERSION at all - OK,
>that's probably a bug. But there is also libnftnl-1.1.9 with similar
>additions (NFTA_{DYNSET,SET,SET_ELEM}_EXPRESSIONS) and a LIBVERSION
>update in the compatible range (15:0:4 -> 16:0:5).

From 1.1.8 to 1.1.9, there were a bunch of function additions:

+void nftnl_expr_add_expr(struct nftnl_expr *expr, uint32_t type, struct nftnl_expr *e);
+int nftnl_expr_expr_foreach(const struct nftnl_expr *e,
+                           int (*cb)(struct nftnl_expr *e, void *data),
+                           void *data);

No such modifications (of this kind, or any stronger kind) were made between
1.1.9 to 1.2.0, hence there was no LIBVERSION update.

Expanding the enum{} generally does not change the ABI unless the underlying
type changes (which it did not in this instance).

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

* Re: [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes
  2024-10-16 19:04     ` Phil Sutter
@ 2024-10-16 19:41       ` Jan Engelhardt
  0 siblings, 0 replies; 16+ messages in thread
From: Jan Engelhardt @ 2024-10-16 19:41 UTC (permalink / raw)
  To: Phil Sutter; +Cc: Pablo Neira Ayuso, Florian Westphal, netfilter-devel


On Wednesday 2024-10-16 21:04, Phil Sutter wrote:
>> 
>> > We may increase incomplete marker correctness by treating support for
>> > any new attribute an incompatible update. Given that we often have
>> > dependencies between libnftnl and nftables for other things, it may not
>> > be too much of a downside though.
>> 
>> 15:0:4 -> 16:0:5 means new API is available while older are still
>> supported, so old nftables can use this library binary safely.
>
>Yes, and my concern is if one installs this newer libnftnl, behaviour of
>incomplete marker will change despite the same old nftables package
>being in place which doesn't handle the new kernel attribute.

LIBVERSION is all about ABI/interface, not documented/observed behavior.

If, for example,
prime_factors@MATHLIB_v15(60000) yielded {2, 3, 5} but
prime_factors@MATHLIB_v16(60000) yields  {2,2,2,2,2, 3, 5,5,5,5},
perhaps one should devise a new function name to capture the new behavior.

If a library merely passes through data from another entity (another library,
or in this case, the kernel), the function should have been specified
(documented) to potentially output unrecognized objects, and require of
any function users to deal with the situation amicably.

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

* Re: [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes
  2024-10-16 19:28   ` Jan Engelhardt
@ 2024-10-16 20:05     ` Phil Sutter
  0 siblings, 0 replies; 16+ messages in thread
From: Phil Sutter @ 2024-10-16 20:05 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Florian Westphal, netfilter-devel

On Wed, Oct 16, 2024 at 09:28:46PM +0200, Jan Engelhardt wrote:
> 
> On Wednesday 2024-10-16 19:07, Phil Sutter wrote:
> >On Mon, Oct 07, 2024 at 11:49:33AM +0200, Florian Westphal wrote:
> >[...]
> >> Extend libnftnl to also make an annotation when a known expression has
> >> an unknown attribute included in the dump, then extend nftables to also
> >> display this to the user.
> >
> >We must be careful with this and LIBVERSION updates. I'm looking at
> >libnftnl-1.2.0 which gained support for NFTA_TABLE_OWNER,
> >NFTA_SOCKET_LEVEL, etc. but did not update LIBVERSION at all - OK,
> >that's probably a bug. But there is also libnftnl-1.1.9 with similar
> >additions (NFTA_{DYNSET,SET,SET_ELEM}_EXPRESSIONS) and a LIBVERSION
> >update in the compatible range (15:0:4 -> 16:0:5).
> 
> From 1.1.8 to 1.1.9, there were a bunch of function additions:
> 
> +void nftnl_expr_add_expr(struct nftnl_expr *expr, uint32_t type, struct nftnl_expr *e);
> +int nftnl_expr_expr_foreach(const struct nftnl_expr *e,
> +                           int (*cb)(struct nftnl_expr *e, void *data),
> +                           void *data);
> 
> No such modifications (of this kind, or any stronger kind) were made between
> 1.1.9 to 1.2.0, hence there was no LIBVERSION update.

Ah, you're right! No libnftnl.map update, so no newly exported symbols.
The ABI must be identical between the two and thus LIBVERSION remaining
the same is correct.

> Expanding the enum{} generally does not change the ABI unless the underlying
> type changes (which it did not in this instance).

I got confused by the added nftnl object attributes, but the data
structures are hidden for a reason and the getter/setter mechanism
allows for exactly these changes to happen under the surface.

Thanks for clarifying!

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

end of thread, other threads:[~2024-10-16 20:05 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-07  9:49 [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes Florian Westphal
2024-10-07  9:49 ` [PATCH libnftnl 1/5] expr: add and use incomplete tag Florian Westphal
2024-10-08 11:13   ` Pablo Neira Ayuso
2024-10-08 12:17     ` Florian Westphal
2024-10-08 14:43       ` Pablo Neira Ayuso
2024-10-08 16:11         ` Florian Westphal
2024-10-07  9:49 ` [PATCH libnftnl 2/5] sets: " Florian Westphal
2024-10-07  9:49 ` [PATCH libnftnl 3/5] libnftnl: add api to query dissection state Florian Westphal
2024-10-07  9:49 ` [PATCH nft 4/5] netlink: tell user if libnftnl detected unknown attributes/features Florian Westphal
2024-10-07  9:49 ` [PATCH nft 5/5] sets: inform user when set definition contains unknown attributes Florian Westphal
2024-10-16 17:07 ` [RFC libnftnl/nft 0/5] nftables: indicate presence of unsupported netlink attributes Phil Sutter
2024-10-16 18:34   ` Pablo Neira Ayuso
2024-10-16 19:04     ` Phil Sutter
2024-10-16 19:41       ` Jan Engelhardt
2024-10-16 19:28   ` Jan Engelhardt
2024-10-16 20:05     ` Phil Sutter

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).